@tracebench/adapter-cursor 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 Tracebench Contributors
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # @tracebench/adapter-cursor
2
+
3
+ Tracebench adapter for **Cursor Agent** session transcripts (JSONL).
4
+
5
+ ## Phase 1 (shipped): agent-transcripts JSONL
6
+
7
+ Cursor writes supplementary agent logs under:
8
+
9
+ ```
10
+ ~/.cursor/projects/<sanitized-project-path>/agent-transcripts/
11
+ <session-uuid>/<session-uuid>.jsonl # nested layout
12
+ <session-uuid>.jsonl # flat layout
13
+ <parent-uuid>/subagents/<subagent-uuid>.jsonl
14
+ ```
15
+
16
+ - **Default root:** `~/.cursor/projects` (override with `tracebench --cursor-dir <path>`)
17
+ - **Format version:** `2026-q1`
18
+ - **Harness id:** `cursor`
19
+
20
+ ### Limitations (JSONL only)
21
+
22
+ Cursor's JSONL export does not include:
23
+
24
+ - `tool_result` lines (tool outputs are not separate events)
25
+ - per-line timestamps (synthetic timestamps from file mtime)
26
+ - token usage / model id (cost analytics show zeros)
27
+
28
+ The UI still renders `tool_call` events (Read, Write, Bash, Task, etc.) with **input only**.
29
+
30
+ ## Phase 2 (planned): Composer SQLite (`state.vscdb`)
31
+
32
+ Full Composer / Ask history lives in Cursor's VS Code–style SQLite DBs. Paths by OS:
33
+
34
+ | OS | Global DB (`bubbleId`, `composerData`, …) |
35
+ |----|-------------------------------------------|
36
+ | macOS | `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb` |
37
+ | Linux | `~/.config/Cursor/User/globalStorage/state.vscdb` |
38
+ | Windows | `%APPDATA%\Cursor\User\globalStorage\state.vscdb` |
39
+
40
+ Per-workspace index (Cursor ≤2.6): `<User>/workspaceStorage/<id>/state.vscdb` + `workspace.json`.
41
+
42
+ **Read WAL consistently:** copy `state.vscdb`, `state.vscdb-wal`, and `state.vscdb-shm` together.
43
+
44
+ Phase 2 will:
45
+
46
+ 1. List composers via `composer.composerHeaders` (3.0+) or workspace `composer.composerData` (≤2.6).
47
+ 2. Load `cursorDiskKV` keys `composerData:{id}` and `bubbleId:{id}:{bubbleId}`.
48
+ 3. Normalize bubbles into `CanonicalEvent` (including tool results where present).
49
+ 4. Dedupe with JSONL sessions when `composerId` matches the transcript folder UUID.
50
+
51
+ Helpers exported for Phase 2: `defaultCursorUserDataDir()`, `defaultCursorGlobalDbPath()` in `src/paths.ts`.
52
+
53
+ ## SSH / WSL
54
+
55
+ Chat data stays on the machine running the Cursor UI (not the remote SSH host). Use the data dir for whichever Cursor install you actually run.
@@ -0,0 +1,11 @@
1
+ import { defaultCursorProjectsRoot } from './paths.js';
2
+ export interface DiscoveredCursorSession {
3
+ session_id: string;
4
+ file_path: string;
5
+ encoded_project_dir: string;
6
+ size: number;
7
+ mtime_ms: number;
8
+ }
9
+ export { defaultCursorProjectsRoot as defaultProjectsRoot };
10
+ export declare function discoverSessions(root?: string): DiscoveredCursorSession[];
11
+ //# sourceMappingURL=discover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,OAAO,EAAE,yBAAyB,IAAI,mBAAmB,EAAE,CAAC;AAsC5D,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,uBAAuB,EAAE,CAiBzE"}
@@ -0,0 +1,65 @@
1
+ // Discover Cursor agent transcripts on disk.
2
+ //
3
+ // Layout:
4
+ // ~/.cursor/projects/<sanitized-path>/agent-transcripts/
5
+ // <session-uuid>.jsonl (flat)
6
+ // <session-uuid>/<session-uuid>.jsonl (nested)
7
+ // <parent-uuid>/subagents/<subagent-uuid>.jsonl
8
+ import { readdirSync, statSync } from 'node:fs';
9
+ import { join, basename } from 'node:path';
10
+ import { defaultCursorProjectsRoot } from './paths.js';
11
+ export { defaultCursorProjectsRoot as defaultProjectsRoot };
12
+ function walkJsonl(dir, encodedProjectDir, out) {
13
+ let entries;
14
+ try {
15
+ entries = readdirSync(dir, { withFileTypes: true });
16
+ }
17
+ catch {
18
+ return;
19
+ }
20
+ for (const ent of entries) {
21
+ const name = String(ent.name);
22
+ const p = join(dir, name);
23
+ if (ent.isDirectory()) {
24
+ walkJsonl(p, encodedProjectDir, out);
25
+ continue;
26
+ }
27
+ if (!name.endsWith('.jsonl'))
28
+ continue;
29
+ let st;
30
+ try {
31
+ st = statSync(p);
32
+ }
33
+ catch {
34
+ continue;
35
+ }
36
+ const sessionId = basename(name, '.jsonl');
37
+ out.push({
38
+ session_id: sessionId,
39
+ file_path: p,
40
+ encoded_project_dir: encodedProjectDir,
41
+ size: st.size,
42
+ mtime_ms: st.mtimeMs,
43
+ });
44
+ }
45
+ }
46
+ export function discoverSessions(root) {
47
+ const base = root ?? defaultCursorProjectsRoot();
48
+ const out = [];
49
+ let projectDirs;
50
+ try {
51
+ projectDirs = readdirSync(base, { withFileTypes: true })
52
+ .filter((d) => d.isDirectory())
53
+ .map((d) => d.name);
54
+ }
55
+ catch {
56
+ return out;
57
+ }
58
+ for (const dir of projectDirs) {
59
+ const transcriptsDir = join(base, dir, 'agent-transcripts');
60
+ walkJsonl(transcriptsDir, dir, out);
61
+ }
62
+ out.sort((a, b) => b.mtime_ms - a.mtime_ms);
63
+ return out;
64
+ }
65
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,UAAU;AACV,2DAA2D;AAC3D,0DAA0D;AAC1D,4DAA4D;AAC5D,oDAAoD;AAEpD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAUvD,OAAO,EAAE,yBAAyB,IAAI,mBAAmB,EAAE,CAAC;AAE5D,SAAS,SAAS,CAChB,GAAW,EACX,iBAAyB,EACzB,GAA8B;IAE9B,IAAI,OAAmC,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAA+B,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,SAAS,CAAC,CAAC,EAAE,iBAAiB,EAAE,GAAG,CAAC,CAAC;YACrC,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,SAAS;QACvC,IAAI,EAAE,CAAC;QACP,IAAI,CAAC;YACH,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC3C,GAAG,CAAC,IAAI,CAAC;YACP,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,CAAC;YACZ,mBAAmB,EAAE,iBAAiB;YACtC,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,QAAQ,EAAE,EAAE,CAAC,OAAO;SACrB,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAa;IAC5C,MAAM,IAAI,GAAG,IAAI,IAAI,yBAAyB,EAAE,CAAC;IACjD,MAAM,GAAG,GAA8B,EAAE,CAAC;IAC1C,IAAI,WAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,WAAW,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aACrD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;aAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,mBAAmB,CAAC,CAAC;QAC5D,SAAS,CAAC,cAAc,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare const HARNESS_NAME: "cursor";
2
+ export declare const FORMAT_VERSION = "2026-q1";
3
+ export { defaultCursorProjectsRoot, defaultCursorUserDataDir, defaultCursorGlobalDbPath, decodeProjectPath, } from './paths.js';
4
+ export { defaultProjectsRoot, discoverSessions, type DiscoveredCursorSession } from './discover.js';
5
+ export { parseSession, streamSession, type RawCursorEvent } from './parse.js';
6
+ export { loadSession, normalizeSession, parseTranscriptPath, type NormalizeResult, } from './normalize.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,EAAG,QAAiB,CAAC;AAC9C,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export const HARNESS_NAME = 'cursor';
2
+ export const FORMAT_VERSION = '2026-q1';
3
+ export { defaultCursorProjectsRoot, defaultCursorUserDataDir, defaultCursorGlobalDbPath, decodeProjectPath, } from './paths.js';
4
+ export { defaultProjectsRoot, discoverSessions } from './discover.js';
5
+ export { parseSession, streamSession } from './parse.js';
6
+ export { loadSession, normalizeSession, parseTranscriptPath, } from './normalize.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,QAAiB,CAAC;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AAExC,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAgC,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,aAAa,EAAuB,MAAM,YAAY,CAAC;AAC9E,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,mBAAmB,GAEpB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { CanonicalEvent, Session } from '@tracebench/core';
2
+ import type { RawCursorEvent } from './parse.js';
3
+ interface NormalizeOptions {
4
+ rawPath: string;
5
+ sessionId?: string;
6
+ formatVersion: string;
7
+ /** File mtime (ms) — used for synthetic per-line timestamps. */
8
+ fileMtimeMs?: number;
9
+ encodedProjectDir?: string;
10
+ }
11
+ export interface NormalizeResult {
12
+ session: Session;
13
+ events: CanonicalEvent[];
14
+ }
15
+ export declare function parseTranscriptPath(rawPath: string): {
16
+ encoded_project_dir: string | null;
17
+ subagent: boolean;
18
+ parent_session_id: string | null;
19
+ };
20
+ export declare function normalizeSession(raws: RawCursorEvent[], opts: NormalizeOptions): NormalizeResult;
21
+ export declare function loadSession(filePath: string, opts?: {
22
+ formatVersion?: string;
23
+ encodedProjectDir?: string;
24
+ }): Promise<NormalizeResult>;
25
+ export {};
26
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAwB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AA6CD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG;IACpD,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,CAYA;AAWD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EAAE,EACtB,IAAI,EAAE,gBAAgB,GACrB,eAAe,CA4MjB;AAKD,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO,GAChE,OAAO,CAAC,eAAe,CAAC,CAiB1B"}
@@ -0,0 +1,284 @@
1
+ // Convert Cursor agent-transcript JSONL into canonical Tracebench events.
2
+ //
3
+ // Cursor writes supplementary logs under ~/.cursor/projects/.../agent-transcripts/.
4
+ // Format is Anthropic-like (role + message.content blocks) but:
5
+ // - no per-line timestamps (synthetic timestamps from file mtime)
6
+ // - tool_use blocks have no id field
7
+ // - no tool_result lines — tool outputs are not exported separately
8
+ //
9
+ // Turn boundaries: each user text message starts a new turn.
10
+ import { decodeProjectPath } from './paths.js';
11
+ function emptyTool() {
12
+ return { name: null, input: null, output: null, status: null, error_message: null };
13
+ }
14
+ const emptyTokens = {
15
+ input: null,
16
+ output: null,
17
+ cache_read: null,
18
+ cache_creation: null,
19
+ reasoning: null,
20
+ };
21
+ function toString(content) {
22
+ if (typeof content === 'string')
23
+ return content;
24
+ if (Array.isArray(content)) {
25
+ const parts = content
26
+ .map((b) => {
27
+ if (typeof b === 'string')
28
+ return b;
29
+ if (b && typeof b === 'object' && 'text' in b && typeof b.text === 'string') {
30
+ return b.text;
31
+ }
32
+ return '';
33
+ })
34
+ .filter((s) => s.length > 0);
35
+ return parts.length > 0 ? parts.join('\n') : null;
36
+ }
37
+ return null;
38
+ }
39
+ function stripUserQuery(text) {
40
+ if (text == null)
41
+ return null;
42
+ const stripped = text.replace(/<\/?user_query>/gi, '').trim();
43
+ return stripped.length > 0 ? stripped : null;
44
+ }
45
+ function deriveTitle(content) {
46
+ const s = stripUserQuery(toString(content));
47
+ if (!s)
48
+ return null;
49
+ const single = s.replace(/\s+/g, ' ').trim();
50
+ if (!single)
51
+ return null;
52
+ return single.length > 120 ? single.slice(0, 117) + '...' : single;
53
+ }
54
+ export function parseTranscriptPath(rawPath) {
55
+ const dirMatch = rawPath.match(/\/projects\/([^/]+)\/agent-transcripts\//);
56
+ const encoded = dirMatch ? dirMatch[1] : null;
57
+ const subMatch = /\/agent-transcripts\/([^/]+)\/subagents\/([^/]+)\.jsonl$/i.exec(rawPath);
58
+ if (subMatch) {
59
+ return {
60
+ encoded_project_dir: encoded,
61
+ subagent: true,
62
+ parent_session_id: subMatch[1],
63
+ };
64
+ }
65
+ return { encoded_project_dir: encoded, subagent: false, parent_session_id: null };
66
+ }
67
+ function syntheticTimestamps(lineCount, endMs) {
68
+ if (lineCount === 0)
69
+ return [];
70
+ const span = Math.max(lineCount - 1, 0) * 1000;
71
+ const startMs = endMs - span;
72
+ return Array.from({ length: lineCount }, (_, i) => new Date(startMs + i * 1000).toISOString());
73
+ }
74
+ export function normalizeSession(raws, opts) {
75
+ const pathInfo = parseTranscriptPath(opts.rawPath);
76
+ const encodedDir = opts.encodedProjectDir ?? pathInfo.encoded_project_dir ?? 'unknown';
77
+ const projectPath = decodeProjectPath(encodedDir);
78
+ let resolvedSessionId = opts.sessionId ?? null;
79
+ if (!resolvedSessionId) {
80
+ resolvedSessionId =
81
+ opts.rawPath.split('/').pop()?.replace(/\.jsonl$/, '') ?? 'unknown';
82
+ }
83
+ const endMs = opts.fileMtimeMs ?? Date.now();
84
+ const timestamps = syntheticTimestamps(raws.length, endMs);
85
+ const firstTs = timestamps[0] ?? new Date(endMs).toISOString();
86
+ const lastTs = timestamps[timestamps.length - 1] ?? firstTs;
87
+ let firstUser;
88
+ for (const r of raws) {
89
+ if (r.role === 'user' && !firstUser)
90
+ firstUser = r;
91
+ }
92
+ const titleSource = firstUser
93
+ ? (firstUser.message?.content ?? null)
94
+ : null;
95
+ const title = deriveTitle(titleSource);
96
+ const source = {
97
+ harness: 'cursor',
98
+ format_version: opts.formatVersion,
99
+ raw_path: opts.rawPath,
100
+ };
101
+ const sessionMeta = {
102
+ encoded_project_dir: encodedDir,
103
+ };
104
+ if (pathInfo.subagent) {
105
+ sessionMeta.subagent = true;
106
+ if (pathInfo.parent_session_id)
107
+ sessionMeta.parent_session_id = pathInfo.parent_session_id;
108
+ }
109
+ const events = [];
110
+ let turnIndex = 0;
111
+ let currentTurnId = `${resolvedSessionId}::t0`;
112
+ function newTurn() {
113
+ turnIndex += 1;
114
+ currentTurnId = `${resolvedSessionId}::t${turnIndex}`;
115
+ return currentTurnId;
116
+ }
117
+ function baseMeta(extra = {}) {
118
+ return { ...sessionMeta, ...extra };
119
+ }
120
+ for (let lineIndex = 0; lineIndex < raws.length; lineIndex++) {
121
+ const r = raws[lineIndex];
122
+ const ts = timestamps[lineIndex] ?? lastTs;
123
+ const role = typeof r.role === 'string' ? r.role : 'unknown';
124
+ if (role === 'user') {
125
+ newTurn();
126
+ const content = r.message?.content;
127
+ events.push({
128
+ event_id: `cursor:${resolvedSessionId}:u:${lineIndex}`,
129
+ session_id: resolvedSessionId,
130
+ turn_id: currentTurnId,
131
+ parent_event_id: null,
132
+ timestamp: ts,
133
+ source,
134
+ role: 'user',
135
+ event_type: 'message',
136
+ model: null,
137
+ tokens: { ...emptyTokens },
138
+ cost_usd: null,
139
+ cost_method: null,
140
+ duration_ms: null,
141
+ content: stripUserQuery(toString(content)),
142
+ tool: emptyTool(),
143
+ metadata: baseMeta(),
144
+ raw: r,
145
+ });
146
+ continue;
147
+ }
148
+ if (role === 'assistant') {
149
+ const msg = r.message ?? {};
150
+ const blocks = Array.isArray(msg.content) ? msg.content : [];
151
+ if (blocks.length === 0) {
152
+ events.push({
153
+ event_id: `cursor:${resolvedSessionId}:a:${lineIndex}:0`,
154
+ session_id: resolvedSessionId,
155
+ turn_id: currentTurnId,
156
+ parent_event_id: null,
157
+ timestamp: ts,
158
+ source,
159
+ role: 'assistant',
160
+ event_type: 'message',
161
+ model: null,
162
+ tokens: { ...emptyTokens },
163
+ cost_usd: null,
164
+ cost_method: null,
165
+ duration_ms: null,
166
+ content: null,
167
+ tool: emptyTool(),
168
+ metadata: baseMeta({ empty: true }),
169
+ raw: r,
170
+ });
171
+ continue;
172
+ }
173
+ for (let bi = 0; bi < blocks.length; bi++) {
174
+ const block = blocks[bi];
175
+ const blockType = typeof block.type === 'string' ? block.type : '';
176
+ const eventId = `cursor:${resolvedSessionId}:${lineIndex}:${bi}`;
177
+ let evtType = 'message';
178
+ let toolInfo = emptyTool();
179
+ let contentField = null;
180
+ if (blockType === 'text') {
181
+ evtType = 'message';
182
+ contentField = typeof block.text === 'string' ? block.text : null;
183
+ }
184
+ else if (blockType === 'tool_use') {
185
+ evtType = 'tool_call';
186
+ toolInfo = {
187
+ name: typeof block.name === 'string' ? block.name : null,
188
+ input: block.input ?? null,
189
+ output: null,
190
+ status: null,
191
+ error_message: null,
192
+ };
193
+ }
194
+ else if (blockType === 'thinking') {
195
+ evtType = 'thinking';
196
+ contentField =
197
+ typeof block.thinking === 'string'
198
+ ? block.thinking
199
+ : typeof block.text === 'string'
200
+ ? block.text
201
+ : null;
202
+ }
203
+ else {
204
+ evtType = 'meta';
205
+ contentField = block;
206
+ }
207
+ events.push({
208
+ event_id: eventId,
209
+ session_id: resolvedSessionId,
210
+ turn_id: currentTurnId,
211
+ parent_event_id: null,
212
+ timestamp: ts,
213
+ source,
214
+ role: 'assistant',
215
+ event_type: evtType,
216
+ model: null,
217
+ tokens: { ...emptyTokens },
218
+ cost_usd: null,
219
+ cost_method: null,
220
+ duration_ms: null,
221
+ content: contentField,
222
+ tool: toolInfo,
223
+ metadata: baseMeta(blockType && blockType !== 'text' ? { block_type: blockType } : {}),
224
+ raw: bi === 0 ? r : { _ref_line: lineIndex },
225
+ });
226
+ }
227
+ continue;
228
+ }
229
+ events.push({
230
+ event_id: `cursor:${resolvedSessionId}:meta:${lineIndex}`,
231
+ session_id: resolvedSessionId,
232
+ turn_id: currentTurnId,
233
+ parent_event_id: null,
234
+ timestamp: ts,
235
+ source,
236
+ role: 'system',
237
+ event_type: 'meta',
238
+ model: null,
239
+ tokens: { ...emptyTokens },
240
+ cost_usd: null,
241
+ cost_method: null,
242
+ duration_ms: null,
243
+ content: null,
244
+ tool: emptyTool(),
245
+ metadata: baseMeta({ kind: role }),
246
+ raw: r,
247
+ });
248
+ }
249
+ const session = {
250
+ session_id: resolvedSessionId,
251
+ harness: 'cursor',
252
+ project_path: projectPath,
253
+ title,
254
+ started_at: firstTs,
255
+ ended_at: lastTs,
256
+ model: null,
257
+ raw_path: opts.rawPath,
258
+ format_version: opts.formatVersion,
259
+ mtime_ms: 0,
260
+ };
261
+ return { session, events };
262
+ }
263
+ import { parseSession } from './parse.js';
264
+ import { promises as fs } from 'node:fs';
265
+ export async function loadSession(filePath, opts = {}) {
266
+ const raws = await parseSession(filePath);
267
+ let fileMtimeMs;
268
+ try {
269
+ const st = await fs.stat(filePath);
270
+ fileMtimeMs = st.mtimeMs;
271
+ }
272
+ catch {
273
+ // ignore
274
+ }
275
+ const sessionId = filePath.split('/').pop()?.replace(/\.jsonl$/, '');
276
+ return normalizeSession(raws, {
277
+ rawPath: filePath,
278
+ sessionId,
279
+ formatVersion: opts.formatVersion ?? '2026-q1',
280
+ fileMtimeMs,
281
+ encodedProjectDir: opts.encodedProjectDir,
282
+ });
283
+ }
284
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type RawCursorEvent = Record<string, unknown> & {
2
+ role?: string;
3
+ message?: unknown;
4
+ };
5
+ export declare function streamSession(filePath: string): AsyncIterable<RawCursorEvent>;
6
+ export declare function parseSession(filePath: string): Promise<RawCursorEvent[]>;
7
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAuB,aAAa,CAClC,QAAQ,EAAE,MAAM,GACf,aAAa,CAAC,cAAc,CAAC,CAW/B;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAI9E"}
package/dist/parse.js ADDED
@@ -0,0 +1,24 @@
1
+ // Stream a Cursor agent-transcript JSONL file line by line.
2
+ import { createReadStream } from 'node:fs';
3
+ import { createInterface } from 'node:readline';
4
+ export async function* streamSession(filePath) {
5
+ const stream = createReadStream(filePath, { encoding: 'utf8' });
6
+ const rl = createInterface({ input: stream, crlfDelay: Infinity });
7
+ for await (const line of rl) {
8
+ if (!line.trim())
9
+ continue;
10
+ try {
11
+ yield JSON.parse(line);
12
+ }
13
+ catch {
14
+ continue;
15
+ }
16
+ }
17
+ }
18
+ export async function parseSession(filePath) {
19
+ const out = [];
20
+ for await (const ev of streamSession(filePath))
21
+ out.push(ev);
22
+ return out;
23
+ }
24
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAOhD,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,aAAa,CAClC,QAAgB;IAEhB,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAChE,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;IACnE,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,EAAE,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,IAAI,KAAK,EAAE,MAAM,EAAE,IAAI,aAAa,CAAC,QAAQ,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare function defaultCursorProjectsRoot(): string;
2
+ /** VS Code–style Cursor `User/` data directory (global + workspace DBs). */
3
+ export declare function defaultCursorUserDataDir(): string;
4
+ /** Global Composer DB — Phase 2 reads this (not used by JSONL adapter yet). */
5
+ export declare function defaultCursorGlobalDbPath(): string;
6
+ /**
7
+ * Decode sanitized project dir name back to an absolute path (best-effort).
8
+ * Cursor encodes `/Users/me/proj` → `Users-me-proj` (leading slash stripped).
9
+ */
10
+ export declare function decodeProjectPath(encoded: string): string;
11
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAKA,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,4EAA4E;AAC5E,wBAAgB,wBAAwB,IAAI,MAAM,CAYjD;AAED,+EAA+E;AAC/E,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAYzD"}
package/dist/paths.js ADDED
@@ -0,0 +1,42 @@
1
+ // Cursor on-disk path helpers (JSONL roots + future Composer SQLite locations).
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ export function defaultCursorProjectsRoot() {
5
+ return join(homedir(), '.cursor', 'projects');
6
+ }
7
+ /** VS Code–style Cursor `User/` data directory (global + workspace DBs). */
8
+ export function defaultCursorUserDataDir() {
9
+ const home = homedir();
10
+ switch (process.platform) {
11
+ case 'darwin':
12
+ return join(home, 'Library', 'Application Support', 'Cursor', 'User');
13
+ case 'win32': {
14
+ const appData = process.env.APPDATA ?? join(home, 'AppData', 'Roaming');
15
+ return join(appData, 'Cursor', 'User');
16
+ }
17
+ default:
18
+ return join(home, '.config', 'Cursor', 'User');
19
+ }
20
+ }
21
+ /** Global Composer DB — Phase 2 reads this (not used by JSONL adapter yet). */
22
+ export function defaultCursorGlobalDbPath() {
23
+ return join(defaultCursorUserDataDir(), 'globalStorage', 'state.vscdb');
24
+ }
25
+ /**
26
+ * Decode sanitized project dir name back to an absolute path (best-effort).
27
+ * Cursor encodes `/Users/me/proj` → `Users-me-proj` (leading slash stripped).
28
+ */
29
+ export function decodeProjectPath(encoded) {
30
+ if (encoded.startsWith('Users-')) {
31
+ return '/Users/' + encoded.slice('Users-'.length).replace(/-/g, '/');
32
+ }
33
+ if (encoded.startsWith('home-')) {
34
+ return '/' + encoded.replace(/-/g, '/');
35
+ }
36
+ if (/^[A-Za-z]-/.test(encoded)) {
37
+ const drive = encoded[0].toUpperCase();
38
+ return drive + ':\\' + encoded.slice(2).replace(/-/g, '\\');
39
+ }
40
+ return encoded;
41
+ }
42
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,UAAU,yBAAyB;IACvC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,wBAAwB;IACtC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACxE,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;YACxE,OAAO,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACzC,CAAC;QACD;YACE,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,yBAAyB;IACvC,OAAO,IAAI,CAAC,wBAAwB,EAAE,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;AAC1E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,OAAO,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,OAAO,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;QACxC,OAAO,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,3 @@
1
+ {"role":"user","message":{"content":[{"type":"text","text":"<user_query>\nRead the README and summarize it\n</user_query>"}]}}
2
+ {"role":"assistant","message":{"content":[{"type":"text","text":"I'll read the README first."},{"type":"tool_use","name":"Read","input":{"path":"/Users/me/code/fixture/README.md"}}]}}
3
+ {"role":"assistant","message":{"content":[{"type":"text","text":"The README describes a local-first session viewer."}]}}
@@ -0,0 +1,2 @@
1
+ {"role":"user","message":{"content":[{"type":"text","text":"<user_query>\nExplore the repo structure\n</user_query>"}]}}
2
+ {"role":"assistant","message":{"content":[{"type":"text","text":"Launching a subagent."},{"type":"tool_use","name":"Task","input":{"description":"Explore repo","prompt":"List top-level dirs"}}]}}