@tracemarketplace/shared 0.0.6 → 0.0.9

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.
Files changed (82) hide show
  1. package/dist/chunker.d.ts.map +1 -1
  2. package/dist/chunker.js +14 -2
  3. package/dist/chunker.js.map +1 -1
  4. package/dist/extractor-claude-code.test.d.ts +2 -0
  5. package/dist/extractor-claude-code.test.d.ts.map +1 -0
  6. package/dist/extractor-claude-code.test.js +290 -0
  7. package/dist/extractor-claude-code.test.js.map +1 -0
  8. package/dist/extractor-codex.test.d.ts +2 -0
  9. package/dist/extractor-codex.test.d.ts.map +1 -0
  10. package/dist/extractor-codex.test.js +212 -0
  11. package/dist/extractor-codex.test.js.map +1 -0
  12. package/dist/extractor-cursor.test.d.ts +2 -0
  13. package/dist/extractor-cursor.test.d.ts.map +1 -0
  14. package/dist/extractor-cursor.test.js +120 -0
  15. package/dist/extractor-cursor.test.js.map +1 -0
  16. package/dist/extractors/claude-code.d.ts.map +1 -1
  17. package/dist/extractors/claude-code.js +172 -73
  18. package/dist/extractors/claude-code.js.map +1 -1
  19. package/dist/extractors/codex.d.ts.map +1 -1
  20. package/dist/extractors/codex.js +63 -35
  21. package/dist/extractors/codex.js.map +1 -1
  22. package/dist/extractors/common.d.ts +14 -0
  23. package/dist/extractors/common.d.ts.map +1 -0
  24. package/dist/extractors/common.js +100 -0
  25. package/dist/extractors/common.js.map +1 -0
  26. package/dist/extractors/cursor.d.ts.map +1 -1
  27. package/dist/extractors/cursor.js +205 -45
  28. package/dist/extractors/cursor.js.map +1 -1
  29. package/dist/hash.d.ts.map +1 -1
  30. package/dist/hash.js +35 -2
  31. package/dist/hash.js.map +1 -1
  32. package/dist/hash.test.js +29 -2
  33. package/dist/hash.test.js.map +1 -1
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +1 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/redact.d.ts +12 -0
  39. package/dist/redact.d.ts.map +1 -1
  40. package/dist/redact.js +120 -38
  41. package/dist/redact.js.map +1 -1
  42. package/dist/redact.test.d.ts +2 -0
  43. package/dist/redact.test.d.ts.map +1 -0
  44. package/dist/redact.test.js +96 -0
  45. package/dist/redact.test.js.map +1 -0
  46. package/dist/turn-actors.d.ts +3 -0
  47. package/dist/turn-actors.d.ts.map +1 -0
  48. package/dist/turn-actors.js +57 -0
  49. package/dist/turn-actors.js.map +1 -0
  50. package/dist/turn-actors.test.d.ts +2 -0
  51. package/dist/turn-actors.test.d.ts.map +1 -0
  52. package/dist/turn-actors.test.js +65 -0
  53. package/dist/turn-actors.test.js.map +1 -0
  54. package/dist/types.d.ts +5 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/utils.d.ts +1 -1
  57. package/dist/utils.d.ts.map +1 -1
  58. package/dist/utils.js +4 -0
  59. package/dist/utils.js.map +1 -1
  60. package/dist/validators.d.ts +24 -0
  61. package/dist/validators.d.ts.map +1 -1
  62. package/dist/validators.js +3 -0
  63. package/dist/validators.js.map +1 -1
  64. package/package.json +5 -1
  65. package/src/chunker.ts +17 -2
  66. package/src/extractor-claude-code.test.ts +326 -0
  67. package/src/extractor-codex.test.ts +225 -0
  68. package/src/extractor-cursor.test.ts +141 -0
  69. package/src/extractors/claude-code.ts +180 -69
  70. package/src/extractors/codex.ts +69 -38
  71. package/src/extractors/common.ts +139 -0
  72. package/src/extractors/cursor.ts +294 -52
  73. package/src/hash.test.ts +31 -2
  74. package/src/hash.ts +38 -3
  75. package/src/index.ts +1 -0
  76. package/src/redact.test.ts +100 -0
  77. package/src/redact.ts +175 -58
  78. package/src/turn-actors.test.ts +71 -0
  79. package/src/turn-actors.ts +71 -0
  80. package/src/types.ts +6 -0
  81. package/src/utils.ts +3 -1
  82. package/src/validators.ts +3 -0
@@ -0,0 +1,120 @@
1
+ import { mkdtempSync, rmSync } from "fs";
2
+ import { join } from "path";
3
+ import { tmpdir } from "os";
4
+ import Database from "better-sqlite3";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import { extractCursor } from "./extractors/cursor.js";
7
+ const tempDirs = [];
8
+ function makeCursorDb(sessionId, headers, values) {
9
+ const dir = mkdtempSync(join(tmpdir(), "tracemp-cursor-"));
10
+ tempDirs.push(dir);
11
+ const dbPath = join(dir, "state.vscdb");
12
+ const db = new Database(dbPath);
13
+ db.exec("CREATE TABLE cursorDiskKV (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
14
+ db.prepare("INSERT INTO cursorDiskKV (key, value) VALUES (?, ?)").run(`composerData:${sessionId}`, JSON.stringify({
15
+ _v: 14,
16
+ createdAt: Date.parse("2026-03-21T00:00:00.000Z"),
17
+ fullConversationHeadersOnly: headers,
18
+ }));
19
+ const insert = db.prepare("INSERT INTO cursorDiskKV (key, value) VALUES (?, ?)");
20
+ for (const [key, value] of values) {
21
+ insert.run(key, JSON.stringify(value));
22
+ }
23
+ db.close();
24
+ return dbPath;
25
+ }
26
+ afterEach(() => {
27
+ while (tempDirs.length > 0) {
28
+ rmSync(tempDirs.pop(), { force: true, recursive: true });
29
+ }
30
+ });
31
+ describe("extractCursor", () => {
32
+ it("supports the modern bubbleId:<session>:<bubble> storage format", async () => {
33
+ const sessionId = "cursor-modern";
34
+ const dbPath = makeCursorDb(sessionId, [
35
+ { bubbleId: "user-1", type: 1 },
36
+ { bubbleId: "assistant-1", type: 2 },
37
+ ], [
38
+ [
39
+ `bubbleId:${sessionId}:user-1`,
40
+ {
41
+ type: 1,
42
+ text: "Help me debug the repo",
43
+ createdAt: "2026-03-21T00:00:01.000Z",
44
+ tokenCount: { inputTokens: 0, outputTokens: 0 },
45
+ },
46
+ ],
47
+ [
48
+ `bubbleId:${sessionId}:assistant-1`,
49
+ {
50
+ type: 2,
51
+ text: "I will inspect src/main.ts",
52
+ thinking: { text: "Start with the entrypoint." },
53
+ createdAt: "2026-03-21T00:00:02.000Z",
54
+ modelName: "gpt-5.4",
55
+ tokenCount: { inputTokens: 200, outputTokens: 80 },
56
+ relevantFiles: [{ path: "src/main.ts" }],
57
+ workspaceUris: ["file:///Users/test/project"],
58
+ },
59
+ ],
60
+ ]);
61
+ const trace = await extractCursor(dbPath, sessionId, "test@example.com");
62
+ expect(trace.turn_count).toBe(2);
63
+ expect(trace.source_version).toBe("14");
64
+ expect(trace.total_input_tokens).toBe(200);
65
+ expect(trace.total_output_tokens).toBe(80);
66
+ expect(trace.has_thinking_blocks).toBe(true);
67
+ expect(trace.turns[0]).toMatchObject({
68
+ role: "user",
69
+ content: [{ type: "text", text: "Help me debug the repo" }],
70
+ });
71
+ expect(trace.turns[1].role).toBe("assistant");
72
+ expect(trace.turns[1].model).toBe("gpt-5.4");
73
+ expect(trace.turns[1].content.map((block) => block.type)).toEqual([
74
+ "thinking",
75
+ "text",
76
+ ]);
77
+ expect(trace.turns[1].usage).toMatchObject({
78
+ input_tokens: 200,
79
+ output_tokens: 80,
80
+ });
81
+ expect(trace.env_state?.open_files_in_editor).toEqual(expect.arrayContaining(["src/main.ts", "/Users/test/project"]));
82
+ });
83
+ it("falls back to the legacy agentKv:blob:<bubble> storage format", async () => {
84
+ const sessionId = "cursor-legacy";
85
+ const dbPath = makeCursorDb(sessionId, [
86
+ { bubbleId: "user-1", type: 1 },
87
+ { bubbleId: "assistant-1", type: 2 },
88
+ ], [
89
+ [
90
+ `agentKv:blob:user-1`,
91
+ {
92
+ role: "user",
93
+ type: "user",
94
+ text: "Open app.py",
95
+ createdAt: "2026-03-21T01:00:01.000Z",
96
+ },
97
+ ],
98
+ [
99
+ `agentKv:blob:assistant-1`,
100
+ {
101
+ role: "assistant",
102
+ message: "Opened the file.",
103
+ createdAt: "2026-03-21T01:00:02.000Z",
104
+ usage: { promptTokens: 55, completionTokens: 21 },
105
+ context: { openFiles: [{ path: "/tmp/app.py" }] },
106
+ },
107
+ ],
108
+ ]);
109
+ const trace = await extractCursor(dbPath, sessionId, "test@example.com");
110
+ expect(trace.turn_count).toBe(2);
111
+ expect(trace.total_input_tokens).toBe(55);
112
+ expect(trace.total_output_tokens).toBe(21);
113
+ expect(trace.turns[1]).toMatchObject({
114
+ role: "assistant",
115
+ content: [{ type: "text", text: "Opened the file." }],
116
+ });
117
+ expect(trace.env_state?.open_files_in_editor).toEqual(["/tmp/app.py"]);
118
+ });
119
+ });
120
+ //# sourceMappingURL=extractor-cursor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractor-cursor.test.js","sourceRoot":"","sources":["../src/extractor-cursor.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/extractors/claude-code.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,aAAa,CAAC;AAErB,wBAAsB,iBAAiB,CACrC,eAAe,EAAE,MAAM,EACvB,WAAW,SAAY,GACtB,OAAO,CAAC,eAAe,CAAC,CAmK1B"}
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/extractors/claude-code.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,aAAa,CAAC;AAGrB,wBAAsB,iBAAiB,CACrC,eAAe,EAAE,MAAM,EACvB,WAAW,SAAY,GACtB,OAAO,CAAC,eAAe,CAAC,CAiR1B"}
@@ -2,6 +2,7 @@ import { createReadStream } from "fs";
2
2
  import { createInterface } from "readline";
3
3
  import { randomUUID } from "crypto";
4
4
  import { hashString, computeContentHash } from "../hash.js";
5
+ import { deriveTurnActors } from "../turn-actors.js";
5
6
  export async function extractClaudeCode(sessionFilePath, submittedBy = "unknown") {
6
7
  const lines = [];
7
8
  const rl = createInterface({
@@ -17,95 +18,193 @@ export async function extractClaudeCode(sessionFilePath, submittedBy = "unknown"
17
18
  catch { }
18
19
  }
19
20
  const sessionId = sessionFilePath.split("/").pop()?.replace(".jsonl", "") ?? randomUUID();
21
+ // Build uuid→entry map for parent lookups
22
+ const uuidMap = new Map();
23
+ for (const line of lines) {
24
+ if (line.uuid)
25
+ uuidMap.set(line.uuid, line);
26
+ }
27
+ // Filter to lines that carry message content (skip progress, snapshots, etc.)
28
+ const SKIP_TYPES = new Set([
29
+ "progress",
30
+ "file-history-snapshot",
31
+ "system",
32
+ "last-prompt",
33
+ ]);
34
+ const entries = lines.filter((l) => !SKIP_TYPES.has(l.type) && l.message);
35
+ // Claude Code splits a single logical turn into multiple chained JSONL entries:
36
+ //
37
+ // 1. Assistant turns: thinking → tool_use → text each parent the next
38
+ // (assistant→assistant parentage). Merge into one turn.
39
+ //
40
+ // 2. User tool_results for parallel tool calls: each result is a separate entry
41
+ // whose parentUuid points to a different entry in the same assistant chain.
42
+ // Merge into one user turn.
43
+ // For assistant entries: walk up until the parent is NOT an assistant entry.
44
+ function getAssistantRoot(entry) {
45
+ let cur = entry;
46
+ while (cur.message?.role === "assistant") {
47
+ const parent = uuidMap.get(cur.parentUuid);
48
+ if (!parent?.message || parent.message.role !== "assistant")
49
+ break;
50
+ cur = parent;
51
+ }
52
+ return cur;
53
+ }
54
+ // For a user entry: return the uuid of the assistant group root that it responds to
55
+ // (null if its parent is not an assistant).
56
+ function parentAssistantRootUuid(entry) {
57
+ const parent = uuidMap.get(entry.parentUuid);
58
+ if (!parent?.message || parent.message.role !== "assistant")
59
+ return null;
60
+ return getAssistantRoot(parent).uuid ?? null;
61
+ }
62
+ // Group entries in file order.
63
+ // Key: root uuid for assistant groups; entry uuid for user groups.
64
+ const groupMap = new Map();
65
+ const rootOrder = [];
66
+ for (const entry of entries) {
67
+ const role = entry.message?.role;
68
+ if (role === "assistant") {
69
+ const root = getAssistantRoot(entry);
70
+ const rootUuid = root.uuid ?? randomUUID();
71
+ if (!groupMap.has(rootUuid)) {
72
+ groupMap.set(rootUuid, [root]);
73
+ rootOrder.push(rootUuid);
74
+ }
75
+ else if (entry !== root) {
76
+ groupMap.get(rootUuid).push(entry);
77
+ }
78
+ }
79
+ else {
80
+ // User entry: merge with the previous user group if they share the same
81
+ // parent assistant root (i.e., parallel tool results for the same tool call batch).
82
+ const myParentRoot = parentAssistantRootUuid(entry);
83
+ const lastKey = rootOrder[rootOrder.length - 1];
84
+ const lastGroup = lastKey ? groupMap.get(lastKey) : undefined;
85
+ const lastRole = lastGroup?.[0]?.message?.role;
86
+ if (myParentRoot &&
87
+ lastRole === "user" &&
88
+ parentAssistantRootUuid(lastGroup[0]) === myParentRoot) {
89
+ lastGroup.push(entry);
90
+ }
91
+ else {
92
+ const groupKey = entry.uuid ?? randomUUID();
93
+ groupMap.set(groupKey, [entry]);
94
+ rootOrder.push(groupKey);
95
+ }
96
+ }
97
+ }
20
98
  const turns = [];
21
99
  let totalInputTokens = 0;
22
100
  let totalOutputTokens = 0;
23
101
  let totalCacheReadTokens = 0;
24
102
  let gitBranch = null;
25
103
  let cwdHash = null;
26
- for (const line of lines) {
27
- if (!line.message)
28
- continue;
29
- const { role, content, usage, model } = line.message;
30
- const tokenUsage = usage
31
- ? {
32
- input_tokens: usage.input_tokens ?? 0,
33
- output_tokens: usage.output_tokens ?? 0,
34
- cache_read_input_tokens: usage.cache_read_input_tokens ?? null,
35
- cache_creation_input_tokens: usage.cache_creation_input_tokens ?? null,
36
- reasoning_tokens: null,
37
- }
38
- : null;
39
- if (tokenUsage) {
40
- totalInputTokens += tokenUsage.input_tokens;
41
- totalOutputTokens += tokenUsage.output_tokens;
42
- totalCacheReadTokens += tokenUsage.cache_read_input_tokens ?? 0;
43
- }
44
- if (line.gitBranch)
45
- gitBranch = line.gitBranch;
46
- if (line.cwd && !cwdHash)
47
- cwdHash = hashString(line.cwd);
104
+ for (const rootUuid of rootOrder) {
105
+ const group = groupMap.get(rootUuid);
106
+ const root = group[0];
107
+ if (root.gitBranch)
108
+ gitBranch = root.gitBranch;
109
+ if (root.cwd && !cwdHash)
110
+ cwdHash = hashString(root.cwd);
111
+ const role = root.message.role === "assistant" ? "assistant" : "user";
112
+ // Collect content blocks from all entries in the group
48
113
  const contentBlocks = [];
49
- const rawContent = Array.isArray(content)
50
- ? content
51
- : typeof content === "string"
52
- ? [{ type: "text", text: content }]
53
- : [];
54
- for (const block of rawContent) {
55
- if (!block || !block.type)
56
- continue;
57
- if (block.type === "file-history-snapshot")
58
- continue;
59
- if (block.type === "text") {
60
- contentBlocks.push({ type: "text", text: block.text ?? "" });
61
- }
62
- else if (block.type === "thinking") {
63
- contentBlocks.push({
64
- type: "thinking",
65
- text: block.thinking ?? block.text ?? "",
66
- });
67
- }
68
- else if (block.type === "tool_use") {
69
- contentBlocks.push({
70
- type: "tool_use",
71
- tool_call_id: block.id ?? randomUUID(),
72
- tool_name: block.name ?? "",
73
- tool_input: block.input ?? {},
74
- });
75
- }
76
- else if (block.type === "tool_result") {
77
- const resultContent = Array.isArray(block.content)
78
- ? block.content.map((c) => c.text ?? "").join("\n")
79
- : block.content ?? null;
80
- contentBlocks.push({
81
- type: "tool_result",
82
- tool_call_id: block.tool_use_id ?? "",
83
- is_error: block.is_error ?? false,
84
- result_content: resultContent,
85
- exit_code: null,
86
- });
114
+ for (const entry of group) {
115
+ const raw = entry.message.content;
116
+ const rawContent = Array.isArray(raw)
117
+ ? raw
118
+ : typeof raw === "string"
119
+ ? [{ type: "text", text: raw }]
120
+ : [];
121
+ for (const block of rawContent) {
122
+ if (!block || !block.type)
123
+ continue;
124
+ if (block.type === "file-history-snapshot")
125
+ continue;
126
+ if (block.type === "text") {
127
+ contentBlocks.push({ type: "text", text: block.text ?? "" });
128
+ }
129
+ else if (block.type === "thinking") {
130
+ contentBlocks.push({
131
+ type: "thinking",
132
+ text: block.thinking ?? block.text ?? "",
133
+ });
134
+ }
135
+ else if (block.type === "tool_use") {
136
+ contentBlocks.push({
137
+ type: "tool_use",
138
+ tool_call_id: block.id ?? randomUUID(),
139
+ tool_name: block.name ?? "",
140
+ tool_input: block.input ?? {},
141
+ });
142
+ }
143
+ else if (block.type === "tool_result") {
144
+ const resultContent = Array.isArray(block.content)
145
+ ? block.content.map((c) => c.text ?? "").join("\n")
146
+ : block.content ?? null;
147
+ contentBlocks.push({
148
+ type: "tool_result",
149
+ tool_call_id: block.tool_use_id ?? "",
150
+ is_error: block.is_error ?? false,
151
+ result_content: resultContent,
152
+ exit_code: null,
153
+ });
154
+ }
87
155
  }
88
156
  }
89
157
  if (contentBlocks.length === 0)
90
158
  continue;
159
+ // Token accounting for assistant turns:
160
+ // - input/cache tokens come from the root entry only (same API call repeated on each chain entry)
161
+ // - output tokens must be summed across all chain entries (each has its own generated content)
162
+ let tokenUsage = null;
163
+ if (role === "assistant") {
164
+ const rootUsage = root.message.usage;
165
+ if (rootUsage) {
166
+ const inputTokens = rootUsage.input_tokens ?? 0;
167
+ const cacheCreate = rootUsage.cache_creation_input_tokens ?? 0;
168
+ const cacheRead = rootUsage.cache_read_input_tokens ?? 0;
169
+ // Sum output tokens across all chain entries
170
+ const outputTokens = group.reduce((sum, entry) => {
171
+ return sum + (entry.message.usage?.output_tokens ?? 0);
172
+ }, 0);
173
+ tokenUsage = {
174
+ input_tokens: inputTokens + cacheCreate + cacheRead,
175
+ output_tokens: outputTokens,
176
+ cache_read_input_tokens: cacheRead,
177
+ cache_creation_input_tokens: cacheCreate,
178
+ reasoning_tokens: null,
179
+ };
180
+ totalInputTokens += inputTokens + cacheCreate + cacheRead;
181
+ totalOutputTokens += outputTokens;
182
+ totalCacheReadTokens += cacheRead;
183
+ }
184
+ }
91
185
  turns.push({
92
- turn_id: line.uuid ?? randomUUID(),
93
- parent_turn_id: line.parentUuid ?? null,
94
- role: role === "assistant" ? "assistant" : "user",
95
- timestamp: line.timestamp ?? null,
186
+ turn_id: root.uuid ?? randomUUID(),
187
+ parent_turn_id: root.parentUuid ?? null,
188
+ role,
189
+ timestamp: root.timestamp ?? null,
96
190
  content: contentBlocks,
97
- model: model ?? null,
191
+ model: root.message.model ?? null,
98
192
  usage: tokenUsage,
99
193
  source_metadata: {
100
- uuid: line.uuid,
101
- parentUuid: line.parentUuid,
102
- gitBranch: line.gitBranch,
194
+ uuid: root.uuid,
195
+ parentUuid: root.parentUuid,
196
+ gitBranch: root.gitBranch,
103
197
  },
104
198
  });
105
199
  }
106
- const startedAt = turns[0]?.timestamp ?? new Date().toISOString();
107
- const endedAt = turns[turns.length - 1]?.timestamp ?? new Date().toISOString();
108
- const allBlocks = turns.flatMap((t) => t.content);
200
+ const turnActors = deriveTurnActors(turns);
201
+ const normalizedTurns = turns.map((turn) => ({
202
+ ...turn,
203
+ actor: turnActors[turn.turn_id],
204
+ }));
205
+ const startedAt = normalizedTurns[0]?.timestamp ?? new Date().toISOString();
206
+ const endedAt = normalizedTurns[normalizedTurns.length - 1]?.timestamp ?? new Date().toISOString();
207
+ const allBlocks = normalizedTurns.flatMap((t) => t.content);
109
208
  const toolCallCount = allBlocks.filter((b) => b.type === "tool_use").length;
110
209
  const hasFileChanges = allBlocks.some((b) => b.type === "tool_use" &&
111
210
  (b.tool_name?.toLowerCase().includes("write") ||
@@ -127,8 +226,8 @@ export async function extractClaudeCode(sessionFilePath, submittedBy = "unknown"
127
226
  working_language: null,
128
227
  started_at: startedAt,
129
228
  ended_at: endedAt,
130
- turns,
131
- turn_count: turns.length,
229
+ turns: normalizedTurns,
230
+ turn_count: normalizedTurns.length,
132
231
  tool_call_count: toolCallCount,
133
232
  has_tool_calls: toolCallCount > 0,
134
233
  has_thinking_blocks: hasThinking,
@@ -1 +1 @@
1
- {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/extractors/claude-code.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/extractors/claude-code.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/extractors/codex.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,aAAa,CAAC;AAErB,wBAAsB,YAAY,CAChC,iBAAiB,EAAE,MAAM,EACzB,WAAW,SAAY,GACtB,OAAO,CAAC,eAAe,CAAC,CAoM1B"}
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/extractors/codex.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,aAAa,CAAC;AAkBrB,wBAAsB,YAAY,CAChC,iBAAiB,EAAE,MAAM,EACzB,WAAW,SAAY,GACtB,OAAO,CAAC,eAAe,CAAC,CAmN1B"}
@@ -1,5 +1,15 @@
1
1
  import { randomUUID } from "crypto";
2
2
  import { hashString, computeContentHash } from "../hash.js";
3
+ import { collectTraceMetrics, createPassiveEnvState, extractTextFragments, pushUniqueTextBlock, } from "./common.js";
4
+ function toNumber(value) {
5
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
6
+ }
7
+ function extractExitCode(output) {
8
+ if (!output)
9
+ return null;
10
+ const match = output.match(/Process exited with code (\d+)/);
11
+ return match ? Number.parseInt(match[1], 10) : null;
12
+ }
3
13
  export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
4
14
  const events = [];
5
15
  for (const line of rolloutFileBuffer.toString("utf-8").split("\n")) {
@@ -21,6 +31,10 @@ export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
21
31
  let currentBlocks = [];
22
32
  let taskTimestamp = null;
23
33
  let currentModel = null;
34
+ let currentTaskLastTimestamp = null;
35
+ let totalInputTokens = null;
36
+ let totalOutputTokens = null;
37
+ let totalCacheReadTokens = null;
24
38
  for (const event of events) {
25
39
  const type = event["type"];
26
40
  const payload = (event["payload"] ?? {});
@@ -31,6 +45,7 @@ export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
31
45
  currentUserMessage = null;
32
46
  currentBlocks = [];
33
47
  taskTimestamp = timestamp;
48
+ currentTaskLastTimestamp = timestamp;
34
49
  continue;
35
50
  }
36
51
  if (type === "event_msg" && payload["type"] === "task_complete") {
@@ -49,16 +64,14 @@ export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
49
64
  }
50
65
  const lastMsg = payload["last_agent_message"];
51
66
  if (lastMsg) {
52
- const alreadyCaptured = currentBlocks.some(b => b.type === "text" && b.text === lastMsg);
53
- if (!alreadyCaptured)
54
- currentBlocks.push({ type: "text", text: lastMsg });
67
+ pushUniqueTextBlock(currentBlocks, "text", lastMsg);
55
68
  }
56
69
  if (currentBlocks.length > 0) {
57
70
  turns.push({
58
71
  turn_id: currentTurnId ?? randomUUID(),
59
72
  parent_turn_id: currentUserMessage ? "user_" + currentTurnId : null,
60
73
  role: "assistant",
61
- timestamp: taskTimestamp,
74
+ timestamp: currentTaskLastTimestamp ?? timestamp ?? taskTimestamp,
62
75
  content: currentBlocks,
63
76
  model: currentModel,
64
77
  usage: null,
@@ -72,10 +85,13 @@ export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
72
85
  currentBlocks = [];
73
86
  taskTimestamp = null;
74
87
  currentModel = null;
88
+ currentTaskLastTimestamp = null;
75
89
  continue;
76
90
  }
77
91
  if (!inTask)
78
92
  continue;
93
+ if (timestamp)
94
+ currentTaskLastTimestamp = timestamp;
79
95
  if (type === "turn_context") {
80
96
  currentModel = payload["model"] ?? null;
81
97
  continue;
@@ -86,17 +102,43 @@ export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
86
102
  currentUserMessage = payload["message"] ?? "";
87
103
  break;
88
104
  case "agent_reasoning":
89
- if (payload["text"])
90
- currentBlocks.push({ type: "thinking", text: payload["text"] });
105
+ pushUniqueTextBlock(currentBlocks, "thinking", payload["text"]);
91
106
  break;
92
107
  case "agent_message":
93
- if (payload["message"])
94
- currentBlocks.push({ type: "text", text: payload["message"] });
108
+ pushUniqueTextBlock(currentBlocks, "text", payload["message"]);
95
109
  break;
110
+ case "token_count": {
111
+ const info = (payload["info"] ?? {});
112
+ const usage = (info["total_token_usage"] ?? {});
113
+ totalInputTokens = toNumber(usage["input_tokens"]) ?? totalInputTokens;
114
+ totalOutputTokens = toNumber(usage["output_tokens"]) ?? totalOutputTokens;
115
+ totalCacheReadTokens =
116
+ toNumber(usage["cached_input_tokens"]) ?? totalCacheReadTokens;
117
+ break;
118
+ }
96
119
  }
97
120
  }
98
121
  else if (type === "response_item") {
99
122
  switch (payload["type"]) {
123
+ case "message": {
124
+ const role = payload["role"];
125
+ const texts = extractTextFragments(payload["content"]);
126
+ if (role === "assistant") {
127
+ for (const text of texts) {
128
+ pushUniqueTextBlock(currentBlocks, "text", text);
129
+ }
130
+ }
131
+ else if (role === "user" && !currentUserMessage) {
132
+ currentUserMessage = texts.join("\n\n") || currentUserMessage;
133
+ }
134
+ break;
135
+ }
136
+ case "reasoning": {
137
+ const reasoningText = extractTextFragments(payload["content"]).join("\n\n") ||
138
+ extractTextFragments(payload["summary"]).join("\n\n");
139
+ pushUniqueTextBlock(currentBlocks, "thinking", reasoningText);
140
+ break;
141
+ }
100
142
  case "function_call": {
101
143
  const callId = payload["call_id"] ?? randomUUID();
102
144
  let toolInput = {};
@@ -116,15 +158,13 @@ export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
116
158
  }
117
159
  case "function_call_output": {
118
160
  const output = payload["output"];
119
- const isError = typeof output === "string"
120
- && output.includes("Process exited with code")
121
- && !output.includes("code 0");
161
+ const exitCode = extractExitCode(output);
122
162
  currentBlocks.push({
123
163
  type: "tool_result",
124
164
  tool_call_id: payload["call_id"] ?? "",
125
- is_error: isError,
165
+ is_error: exitCode !== null ? exitCode !== 0 : false,
126
166
  result_content: output ?? null,
127
- exit_code: null,
167
+ exit_code: exitCode,
128
168
  });
129
169
  break;
130
170
  }
@@ -141,11 +181,7 @@ export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
141
181
  }
142
182
  }
143
183
  }
144
- const allBlocks = turns.flatMap(t => t.content);
145
- const toolCallCount = allBlocks.filter(b => b.type === "tool_use").length;
146
- const hasFileChanges = allBlocks.some(b => b.type === "tool_use" && ["write_file", "file_change", "create_file"].includes(b.tool_name));
147
- const hasShellCommands = allBlocks.some(b => b.type === "tool_use" && ["exec_command", "bash", "shell"].includes(b.tool_name));
148
- const hasThinking = allBlocks.some(b => b.type === "thinking");
184
+ const metrics = collectTraceMetrics(turns);
149
185
  const startedAt = sessionMetaPayload["timestamp"] ?? events[0]?.["timestamp"] ?? new Date().toISOString();
150
186
  const endedAt = events[events.length - 1]?.["timestamp"] ?? new Date().toISOString();
151
187
  const partialTrace = {
@@ -163,24 +199,16 @@ export async function extractCodex(rolloutFileBuffer, submittedBy = "unknown") {
163
199
  ended_at: endedAt,
164
200
  turns,
165
201
  turn_count: turns.length,
166
- tool_call_count: toolCallCount,
167
- has_tool_calls: toolCallCount > 0,
168
- has_thinking_blocks: hasThinking,
169
- has_file_changes: hasFileChanges,
170
- has_shell_commands: hasShellCommands,
171
- total_input_tokens: null,
172
- total_output_tokens: null,
173
- total_cache_read_tokens: null,
202
+ tool_call_count: metrics.toolCallCount,
203
+ has_tool_calls: metrics.toolCallCount > 0,
204
+ has_thinking_blocks: metrics.hasThinkingBlocks,
205
+ has_file_changes: metrics.hasFileChanges,
206
+ has_shell_commands: metrics.hasShellCommands,
207
+ total_input_tokens: totalInputTokens,
208
+ total_output_tokens: totalOutputTokens,
209
+ total_cache_read_tokens: totalCacheReadTokens,
174
210
  content_fidelity: "full",
175
- env_state: {
176
- git_branch: null,
177
- inferred_file_tree: null,
178
- inferred_changed_files: null,
179
- inferred_error_files: null,
180
- shell_exit_codes: null,
181
- open_files_in_editor: null,
182
- extraction_method: "passive",
183
- },
211
+ env_state: createPassiveEnvState(),
184
212
  score: null,
185
213
  raw_r2_key: "",
186
214
  normalized_r2_key: "",