poe-code 3.0.258 → 3.0.259-beta.1

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 (76) hide show
  1. package/dist/cli/commands/gaslight.js +84 -1
  2. package/dist/cli/commands/gaslight.js.map +1 -1
  3. package/dist/cli/poe-theme.d.ts +1 -0
  4. package/dist/cli/poe-theme.js +5 -0
  5. package/dist/cli/poe-theme.js.map +1 -0
  6. package/dist/cli/program.js +1 -0
  7. package/dist/cli/program.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +2753 -1123
  10. package/dist/index.js.map +4 -4
  11. package/dist/metafile.json +1 -1
  12. package/dist/sdk/gaslight.d.ts +1 -1
  13. package/dist/sdk/gaslight.js +1 -1
  14. package/dist/sdk/gaslight.js.map +1 -1
  15. package/package.json +3 -1
  16. package/packages/agent-gaslight/dist/config.d.ts +5 -1
  17. package/packages/agent-gaslight/dist/config.js +32 -11
  18. package/packages/agent-gaslight/dist/index.d.ts +3 -2
  19. package/packages/agent-gaslight/dist/index.js +2 -1
  20. package/packages/agent-gaslight/dist/ingest.d.ts +2 -0
  21. package/packages/agent-gaslight/dist/ingest.js +486 -0
  22. package/packages/agent-gaslight/dist/run.js +1 -1
  23. package/packages/agent-gaslight/dist/types.d.ts +41 -6
  24. package/packages/agent-harness/dist/loader/run.js +6 -21
  25. package/packages/agent-script/dist/cli.d.ts +2 -3
  26. package/packages/agent-script/dist/cli.js +70 -36
  27. package/packages/agent-script/dist/example-runner.d.ts +2 -3
  28. package/packages/agent-script/dist/example-runner.js +69 -56
  29. package/packages/agent-script/dist/interp/exceptions.js +1 -1
  30. package/packages/agent-script/dist/interp/globals/object-array.d.ts +1 -1
  31. package/packages/agent-script/dist/interp/globals/object-array.js +39 -20
  32. package/packages/agent-script/dist/interp/host-bridge.js +1 -1
  33. package/packages/agent-script/dist/interp/interpreter.js +83 -17
  34. package/packages/agent-script/dist/interp/methods/array.js +25 -2
  35. package/packages/agent-script/dist/interp/methods/regex.js +1 -1
  36. package/packages/agent-script/dist/interp/promise-tracker.d.ts +16 -0
  37. package/packages/agent-script/dist/interp/promise-tracker.js +58 -0
  38. package/packages/agent-script/dist/interp/promise.js +38 -7
  39. package/packages/agent-script/dist/interp/scope.d.ts +1 -0
  40. package/packages/agent-script/dist/interp/scope.js +3 -0
  41. package/packages/agent-script/dist/interp/values.js +2 -0
  42. package/packages/agent-script/dist/lint/index.d.ts +2 -0
  43. package/packages/agent-script/dist/lint/index.js +2 -0
  44. package/packages/agent-script/dist/lint/rules/AS-export-import-meta.d.ts +6 -1
  45. package/packages/agent-script/dist/lint/rules/AS-export-import-meta.js +33 -4
  46. package/packages/agent-script/dist/modules/agent.js +10 -1
  47. package/packages/agent-script/dist/modules/log.js +5 -1
  48. package/packages/agent-script/dist/modules/registry.js +9 -3
  49. package/packages/agent-script/dist/output-stream.d.ts +12 -0
  50. package/packages/agent-script/dist/output-stream.js +50 -0
  51. package/packages/agent-script/dist/parse/parser.d.ts +1 -1
  52. package/packages/agent-script/dist/parse/parser.js +151 -45
  53. package/packages/agent-script/dist/parse/tokenizer.js +26 -3
  54. package/packages/agent-script/dist/run.js +14 -3
  55. package/packages/agent-script/dist/runner/run-harness.js +28 -5
  56. package/packages/agent-traces/dist/collect.d.ts +4 -0
  57. package/packages/agent-traces/dist/collect.js +102 -0
  58. package/packages/agent-traces/dist/index.d.ts +4 -0
  59. package/packages/agent-traces/dist/index.js +3 -0
  60. package/packages/agent-traces/dist/jsonl.d.ts +2 -0
  61. package/packages/agent-traces/dist/jsonl.js +7 -0
  62. package/packages/agent-traces/dist/line-json.d.ts +4 -0
  63. package/packages/agent-traces/dist/line-json.js +40 -0
  64. package/packages/agent-traces/dist/readers/claude.d.ts +2 -0
  65. package/packages/agent-traces/dist/readers/claude.js +192 -0
  66. package/packages/agent-traces/dist/readers/codex.d.ts +2 -0
  67. package/packages/agent-traces/dist/readers/codex.js +266 -0
  68. package/packages/agent-traces/dist/readers/index.d.ts +5 -0
  69. package/packages/agent-traces/dist/readers/index.js +4 -0
  70. package/packages/agent-traces/dist/types.d.ts +84 -0
  71. package/packages/agent-traces/dist/types.js +1 -0
  72. package/packages/package-lint/dist/model.js +5 -1
  73. package/packages/package-lint/dist/source-imports.d.ts +11 -1
  74. package/packages/package-lint/dist/source-imports.js +30 -4
  75. package/packages/tiny-stdio-mcp-test-server/dist/cli.js +41 -0
  76. package/packages/tiny-stdio-mcp-test-server/dist/index.js +8 -0
@@ -0,0 +1,102 @@
1
+ import { promises as nodeFs } from "node:fs";
2
+ import os from "node:os";
3
+ import { traceReaders } from "./readers/index.js";
4
+ const DEFAULT_SOURCES = ["claude", "codex"];
5
+ function compareRecordsNewestFirst(first, second) {
6
+ const firstTime = first.timestamp ? new Date(first.timestamp).getTime() : 0;
7
+ const secondTime = second.timestamp ? new Date(second.timestamp).getTime() : 0;
8
+ return secondTime - firstTime;
9
+ }
10
+ function isInjectedContext(text) {
11
+ const trimmed = text.trim();
12
+ return (trimmed.startsWith("<codex_internal_context") ||
13
+ trimmed.startsWith("# AGENTS.md instructions") ||
14
+ trimmed.startsWith("## Prior conversation with Codex:") ||
15
+ trimmed.startsWith("<environment_context>") ||
16
+ trimmed.startsWith("<INSTRUCTIONS>") ||
17
+ trimmed.startsWith("<turn_aborted>") ||
18
+ trimmed.startsWith("<subagent_notification>") ||
19
+ trimmed.startsWith("Read this JSONL file of human prompts from coding-agent traces:"));
20
+ }
21
+ function removeContextTags(text) {
22
+ let result = text.trim();
23
+ while (result.startsWith("<ide_opened_file>")) {
24
+ const endIndex = result.indexOf("</ide_opened_file>");
25
+ if (endIndex === -1) {
26
+ return result;
27
+ }
28
+ result = result.slice(endIndex + "</ide_opened_file>".length).trim();
29
+ }
30
+ return result;
31
+ }
32
+ function recordKey(record) {
33
+ return [record.source, record.traceId, record.text].join("\u0000");
34
+ }
35
+ export async function collectHumanPromptsFromReaders(readers, options = {}) {
36
+ const sources = options.sources ?? DEFAULT_SOURCES;
37
+ const readerById = new Map(readers.map((reader) => [reader.id, reader]));
38
+ const fs = options.fs ?? nodeFs;
39
+ const homeDir = options.homeDir ?? os.homedir();
40
+ const cwd = options.cwd ?? process.cwd();
41
+ const records = [];
42
+ const seenRecords = new Set();
43
+ let traceCount = 0;
44
+ for (const source of sources) {
45
+ const reader = readerById.get(source);
46
+ if (!reader) {
47
+ throw new Error(`Unsupported trace source: ${source}`);
48
+ }
49
+ const references = await reader.discover({
50
+ cwd,
51
+ homeDir,
52
+ since: options.since,
53
+ allWorkspaces: options.allWorkspaces,
54
+ fs,
55
+ sqlite: options.sqlite
56
+ });
57
+ traceCount += references.length;
58
+ for (const reference of references) {
59
+ const trace = await reader.read(reference, { fs });
60
+ for (const turn of trace.turns) {
61
+ if (turn.role !== "human" || turn.text.trim().length === 0) {
62
+ continue;
63
+ }
64
+ if (options.since && turn.timestamp && turn.timestamp < options.since) {
65
+ continue;
66
+ }
67
+ const text = removeContextTags(turn.text);
68
+ if (text.length === 0) {
69
+ continue;
70
+ }
71
+ if (isInjectedContext(text)) {
72
+ continue;
73
+ }
74
+ const record = {
75
+ traceId: trace.id,
76
+ source: trace.source,
77
+ ...(trace.cwd ? { cwd: trace.cwd } : {}),
78
+ ...(trace.title ? { title: trace.title } : {}),
79
+ ...(turn.timestamp ? { timestamp: turn.timestamp.toISOString() } : {}),
80
+ text
81
+ };
82
+ const key = recordKey(record);
83
+ if (seenRecords.has(key)) {
84
+ continue;
85
+ }
86
+ seenRecords.add(key);
87
+ records.push(record);
88
+ }
89
+ }
90
+ }
91
+ records.sort(compareRecordsNewestFirst);
92
+ return {
93
+ records: options.limit === undefined ? records : records.slice(0, options.limit),
94
+ traceCount
95
+ };
96
+ }
97
+ export async function collectHumanPrompts(options = {}) {
98
+ return (await collectHumanPromptsWithStats(options)).records;
99
+ }
100
+ export async function collectHumanPromptsWithStats(options = {}) {
101
+ return await collectHumanPromptsFromReaders(traceReaders, options);
102
+ }
@@ -0,0 +1,4 @@
1
+ export { collectHumanPrompts, collectHumanPromptsFromReaders, collectHumanPromptsWithStats } from "./collect.js";
2
+ export { writeHumanPromptJsonl } from "./jsonl.js";
3
+ export { claudeTraceReader, codexTraceReader, traceReaders } from "./readers/index.js";
4
+ export type { AgentTraceFileSystem, AgentTraceSource, CollectHumanPromptsOptions, CollectHumanPromptsResult, HumanPromptRecord, NormalizedTrace, NormalizedTraceTurn, SqliteTraceDatabase, SqliteTraceDatabaseFactory, TraceDiscoverOptions, TraceReadOptions, TraceReader, TraceReference } from "./types.js";
@@ -0,0 +1,3 @@
1
+ export { collectHumanPrompts, collectHumanPromptsFromReaders, collectHumanPromptsWithStats } from "./collect.js";
2
+ export { writeHumanPromptJsonl } from "./jsonl.js";
3
+ export { claudeTraceReader, codexTraceReader, traceReaders } from "./readers/index.js";
@@ -0,0 +1,2 @@
1
+ import type { AgentTraceFileSystem, HumanPromptRecord } from "./types.js";
2
+ export declare function writeHumanPromptJsonl(records: HumanPromptRecord[], filePath: string, fs?: AgentTraceFileSystem): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import path from "node:path";
2
+ import { promises as nodeFs } from "node:fs";
3
+ export async function writeHumanPromptJsonl(records, filePath, fs = nodeFs) {
4
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
5
+ const content = records.map((record) => JSON.stringify(record)).join("\n");
6
+ await fs.writeFile(filePath, content.length === 0 ? "" : `${content}\n`, { encoding: "utf8" });
7
+ }
@@ -0,0 +1,4 @@
1
+ export declare function parseJsonLines(content: string): unknown[];
2
+ export declare function asRecord(value: unknown): Record<string, unknown> | undefined;
3
+ export declare function parseDate(value: unknown): Date | undefined;
4
+ export declare function newestDate(first: Date | undefined, second: Date | undefined): Date | undefined;
@@ -0,0 +1,40 @@
1
+ export function parseJsonLines(content) {
2
+ const values = [];
3
+ for (const rawLine of content.split("\n")) {
4
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
5
+ if (line.trim().length === 0) {
6
+ continue;
7
+ }
8
+ try {
9
+ values.push(JSON.parse(line));
10
+ }
11
+ catch {
12
+ // Agent trace files may contain partially written lines; keep scanning.
13
+ }
14
+ }
15
+ return values;
16
+ }
17
+ export function asRecord(value) {
18
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
19
+ return undefined;
20
+ }
21
+ return value;
22
+ }
23
+ export function parseDate(value) {
24
+ if (typeof value === "number" && Number.isFinite(value)) {
25
+ const milliseconds = value > 10_000_000_000 ? value : value * 1_000;
26
+ return new Date(milliseconds);
27
+ }
28
+ if (typeof value !== "string" || value.trim().length === 0) {
29
+ return undefined;
30
+ }
31
+ const date = new Date(value);
32
+ return Number.isNaN(date.getTime()) ? undefined : date;
33
+ }
34
+ export function newestDate(first, second) {
35
+ if (!first)
36
+ return second;
37
+ if (!second)
38
+ return first;
39
+ return first.getTime() >= second.getTime() ? first : second;
40
+ }
@@ -0,0 +1,2 @@
1
+ import type { TraceReader } from "../types.js";
2
+ export declare const claudeTraceReader: TraceReader;
@@ -0,0 +1,192 @@
1
+ import path from "node:path";
2
+ import { asRecord, newestDate, parseDate, parseJsonLines } from "../line-json.js";
3
+ function isMissingFile(error) {
4
+ return (typeof error === "object" &&
5
+ error !== null &&
6
+ "code" in error &&
7
+ error.code === "ENOENT");
8
+ }
9
+ function encodeClaudeProjectPath(cwd) {
10
+ return cwd.split(path.sep).join("-");
11
+ }
12
+ async function listJsonlFiles(fs, directory) {
13
+ let names;
14
+ try {
15
+ names = await fs.readdir(directory);
16
+ }
17
+ catch (error) {
18
+ if (isMissingFile(error)) {
19
+ return [];
20
+ }
21
+ throw error;
22
+ }
23
+ return names
24
+ .filter((name) => name.endsWith(".jsonl"))
25
+ .sort()
26
+ .map((name) => path.join(directory, name));
27
+ }
28
+ async function listProjectDirectories(fs, projectsRoot) {
29
+ let names;
30
+ try {
31
+ names = await fs.readdir(projectsRoot);
32
+ }
33
+ catch (error) {
34
+ if (isMissingFile(error)) {
35
+ return [];
36
+ }
37
+ throw error;
38
+ }
39
+ const directories = [];
40
+ for (const name of names.sort()) {
41
+ const candidate = path.join(projectsRoot, name);
42
+ try {
43
+ if ((await fs.stat(candidate)).isDirectory()) {
44
+ directories.push(candidate);
45
+ }
46
+ }
47
+ catch (error) {
48
+ if (!isMissingFile(error)) {
49
+ throw error;
50
+ }
51
+ }
52
+ }
53
+ return directories;
54
+ }
55
+ function fileId(filePath) {
56
+ const name = path.basename(filePath);
57
+ return name.endsWith(".jsonl") ? name.slice(0, -".jsonl".length) : name;
58
+ }
59
+ function textFromContent(value) {
60
+ if (typeof value === "string") {
61
+ return value.trim();
62
+ }
63
+ if (!Array.isArray(value)) {
64
+ return "";
65
+ }
66
+ const parts = [];
67
+ for (const item of value) {
68
+ if (typeof item === "string") {
69
+ parts.push(item);
70
+ continue;
71
+ }
72
+ const record = asRecord(item);
73
+ if (!record) {
74
+ continue;
75
+ }
76
+ const type = typeof record.type === "string" ? record.type : undefined;
77
+ if (type !== undefined && type !== "text" && type !== "input_text") {
78
+ continue;
79
+ }
80
+ if (typeof record.text === "string") {
81
+ parts.push(record.text);
82
+ }
83
+ }
84
+ return parts
85
+ .map((part) => part.trim())
86
+ .filter((part) => part.length > 0)
87
+ .join("\n");
88
+ }
89
+ function roleFromClaude(recordType, messageRole) {
90
+ if (messageRole === "user" || recordType === "user") {
91
+ return "human";
92
+ }
93
+ if (messageRole === "assistant" || recordType === "assistant") {
94
+ return "assistant";
95
+ }
96
+ if (recordType === "system") {
97
+ return "system";
98
+ }
99
+ return "tool";
100
+ }
101
+ function turnFromRecord(record) {
102
+ const message = asRecord(record.message);
103
+ if (!message) {
104
+ return undefined;
105
+ }
106
+ const text = textFromContent(message.content);
107
+ if (text.length === 0) {
108
+ return undefined;
109
+ }
110
+ return {
111
+ ...(typeof record.uuid === "string" ? { id: record.uuid } : {}),
112
+ role: roleFromClaude(record.type, message.role),
113
+ text,
114
+ ...(parseDate(record.timestamp) ? { timestamp: parseDate(record.timestamp) } : {}),
115
+ ...(typeof record.type === "string" ? { sourceKind: record.type } : {})
116
+ };
117
+ }
118
+ async function readTrace(filePath, fs) {
119
+ const records = parseJsonLines(await fs.readFile(filePath, "utf8"))
120
+ .map(asRecord)
121
+ .filter((record) => record !== undefined);
122
+ const turns = [];
123
+ let cwd;
124
+ let createdAt;
125
+ let updatedAt;
126
+ let title;
127
+ let sessionId;
128
+ for (const record of records) {
129
+ if (typeof record.cwd === "string" && cwd === undefined) {
130
+ cwd = record.cwd;
131
+ }
132
+ if (typeof record.sessionId === "string" && sessionId === undefined) {
133
+ sessionId = record.sessionId;
134
+ }
135
+ if (typeof record.aiTitle === "string" && title === undefined) {
136
+ title = record.aiTitle;
137
+ }
138
+ const timestamp = parseDate(record.timestamp);
139
+ createdAt = createdAt ?? timestamp;
140
+ updatedAt = newestDate(updatedAt, timestamp);
141
+ const turn = turnFromRecord(record);
142
+ if (turn) {
143
+ turns.push(turn);
144
+ }
145
+ }
146
+ return {
147
+ source: "claude",
148
+ id: sessionId ?? fileId(filePath),
149
+ path: filePath,
150
+ ...(cwd ? { cwd } : {}),
151
+ ...(title ? { title } : {}),
152
+ ...(createdAt ? { createdAt } : {}),
153
+ ...(updatedAt ? { updatedAt } : {}),
154
+ turns
155
+ };
156
+ }
157
+ export const claudeTraceReader = {
158
+ id: "claude",
159
+ defaultRoots(homeDir) {
160
+ return [path.join(homeDir, ".claude", "projects")];
161
+ },
162
+ async discover(options) {
163
+ const projectsRoot = path.join(options.homeDir, ".claude", "projects");
164
+ const directories = options.allWorkspaces || !options.cwd
165
+ ? await listProjectDirectories(options.fs, projectsRoot)
166
+ : [path.join(projectsRoot, encodeClaudeProjectPath(options.cwd))];
167
+ const references = [];
168
+ for (const directory of directories) {
169
+ for (const filePath of await listJsonlFiles(options.fs, directory)) {
170
+ const trace = await readTrace(filePath, options.fs);
171
+ if (options.since && trace.updatedAt && trace.updatedAt < options.since) {
172
+ continue;
173
+ }
174
+ references.push({
175
+ source: "claude",
176
+ id: trace.id,
177
+ path: filePath,
178
+ ...(trace.cwd ? { cwd: trace.cwd } : {}),
179
+ ...(trace.updatedAt ? { updatedAt: trace.updatedAt } : {}),
180
+ ...(trace.title ? { title: trace.title } : {})
181
+ });
182
+ }
183
+ }
184
+ return references;
185
+ },
186
+ async read(reference, options) {
187
+ if (!reference.path) {
188
+ throw new Error(`Claude trace ${reference.id} has no path.`);
189
+ }
190
+ return await readTrace(reference.path, options.fs);
191
+ }
192
+ };
@@ -0,0 +1,2 @@
1
+ import type { TraceReader } from "../types.js";
2
+ export declare const codexTraceReader: TraceReader;
@@ -0,0 +1,266 @@
1
+ import path from "node:path";
2
+ import { asRecord, newestDate, parseDate, parseJsonLines } from "../line-json.js";
3
+ function isMissingFile(error) {
4
+ return (typeof error === "object" &&
5
+ error !== null &&
6
+ "code" in error &&
7
+ error.code === "ENOENT");
8
+ }
9
+ async function importNodeSqlite() {
10
+ const emitWarning = process.emitWarning;
11
+ process.emitWarning = ((warning, ...args) => {
12
+ if (args[0] === "ExperimentalWarning" && String(warning).includes("SQLite")) {
13
+ return;
14
+ }
15
+ return emitWarning(warning, ...args);
16
+ });
17
+ try {
18
+ return await import("node:sqlite");
19
+ }
20
+ finally {
21
+ process.emitWarning = emitWarning;
22
+ }
23
+ }
24
+ async function defaultSqliteFactory(databasePath) {
25
+ const sqlite = await importNodeSqlite();
26
+ const db = new sqlite.DatabaseSync(databasePath, { readOnly: true });
27
+ return {
28
+ all(sql, params) {
29
+ const sqlParams = params;
30
+ return db.prepare(sql).all(...sqlParams);
31
+ },
32
+ close() {
33
+ db.close();
34
+ }
35
+ };
36
+ }
37
+ function dateFromRow(seconds, milliseconds) {
38
+ if (typeof milliseconds === "number" && Number.isFinite(milliseconds)) {
39
+ return new Date(milliseconds);
40
+ }
41
+ return parseDate(seconds);
42
+ }
43
+ function rowFromUnknown(value) {
44
+ const record = asRecord(value);
45
+ if (!record || typeof record.id !== "string") {
46
+ return undefined;
47
+ }
48
+ return {
49
+ id: record.id,
50
+ ...(typeof record.rollout_path === "string" ? { rollout_path: record.rollout_path } : {}),
51
+ ...(typeof record.created_at === "number" ? { created_at: record.created_at } : {}),
52
+ ...(typeof record.updated_at === "number" ? { updated_at: record.updated_at } : {}),
53
+ ...("created_at_ms" in record && typeof record.created_at_ms === "number"
54
+ ? { created_at_ms: record.created_at_ms }
55
+ : {}),
56
+ ...("updated_at_ms" in record && typeof record.updated_at_ms === "number"
57
+ ? { updated_at_ms: record.updated_at_ms }
58
+ : {}),
59
+ ...(typeof record.source === "string" ? { source: record.source } : {}),
60
+ ...(typeof record.model === "string" ? { model: record.model } : {}),
61
+ ...(typeof record.cwd === "string" ? { cwd: record.cwd } : {}),
62
+ ...(typeof record.title === "string" ? { title: record.title } : {}),
63
+ ...(typeof record.first_user_message === "string"
64
+ ? { first_user_message: record.first_user_message }
65
+ : {})
66
+ };
67
+ }
68
+ function textFromContent(value) {
69
+ if (typeof value === "string") {
70
+ return value.trim();
71
+ }
72
+ if (!Array.isArray(value)) {
73
+ return "";
74
+ }
75
+ const parts = [];
76
+ for (const item of value) {
77
+ if (typeof item === "string") {
78
+ parts.push(item);
79
+ continue;
80
+ }
81
+ const record = asRecord(item);
82
+ if (!record) {
83
+ continue;
84
+ }
85
+ const type = typeof record.type === "string" ? record.type : undefined;
86
+ if (type !== undefined && type !== "input_text" && type !== "output_text" && type !== "text") {
87
+ continue;
88
+ }
89
+ if (typeof record.text === "string") {
90
+ parts.push(record.text);
91
+ }
92
+ }
93
+ return parts
94
+ .map((part) => part.trim())
95
+ .filter((part) => part.length > 0)
96
+ .join("\n");
97
+ }
98
+ function textFromUserMessage(payload) {
99
+ if (typeof payload.message === "string") {
100
+ return payload.message.trim();
101
+ }
102
+ const textElements = textFromContent(payload.text_elements);
103
+ if (textElements.length > 0) {
104
+ return textElements;
105
+ }
106
+ return textFromContent(payload.message);
107
+ }
108
+ function turnFromRolloutRecord(record) {
109
+ const payload = asRecord(record.payload);
110
+ if (!payload || typeof payload.type !== "string") {
111
+ return undefined;
112
+ }
113
+ const timestamp = parseDate(record.timestamp);
114
+ if (payload.type === "user_message") {
115
+ const text = textFromUserMessage(payload);
116
+ return text.length === 0
117
+ ? undefined
118
+ : {
119
+ role: "human",
120
+ text,
121
+ ...(timestamp ? { timestamp } : {}),
122
+ sourceKind: "user_message"
123
+ };
124
+ }
125
+ if (payload.type === "message") {
126
+ const text = textFromContent(payload.content);
127
+ if (text.length === 0) {
128
+ return undefined;
129
+ }
130
+ const role = payload.role === "user" ? "human" : payload.role === "assistant" ? "assistant" : "tool";
131
+ return {
132
+ role,
133
+ text,
134
+ ...(timestamp ? { timestamp } : {}),
135
+ sourceKind: "message"
136
+ };
137
+ }
138
+ return undefined;
139
+ }
140
+ async function readRollout(filePath, fs) {
141
+ const content = await fs.readFile(filePath, "utf8");
142
+ return parseJsonLines(content)
143
+ .map(asRecord)
144
+ .filter((record) => record !== undefined)
145
+ .map(turnFromRolloutRecord)
146
+ .filter((turn) => turn !== undefined);
147
+ }
148
+ function referenceFromRow(row) {
149
+ const createdAt = dateFromRow(row.created_at, row.created_at_ms);
150
+ const updatedAt = dateFromRow(row.updated_at, row.updated_at_ms);
151
+ return {
152
+ source: "codex",
153
+ id: row.id,
154
+ ...(row.rollout_path ? { path: row.rollout_path } : {}),
155
+ ...(row.cwd ? { cwd: row.cwd } : {}),
156
+ ...(updatedAt ? { updatedAt } : {}),
157
+ ...(row.title ? { title: row.title } : {}),
158
+ metadata: {
159
+ ...(createdAt ? { createdAt } : {}),
160
+ ...(updatedAt ? { updatedAt } : {}),
161
+ ...(row.first_user_message ? { firstUserMessage: row.first_user_message } : {}),
162
+ ...(row.model ? { model: row.model } : {}),
163
+ ...(row.source ? { source: row.source } : {})
164
+ }
165
+ };
166
+ }
167
+ function fallbackTurn(reference) {
168
+ const firstUserMessage = reference.metadata?.firstUserMessage;
169
+ if (typeof firstUserMessage !== "string" || firstUserMessage.trim().length === 0) {
170
+ return [];
171
+ }
172
+ const createdAt = reference.metadata?.createdAt;
173
+ return [
174
+ {
175
+ role: "human",
176
+ text: firstUserMessage.trim(),
177
+ ...(createdAt instanceof Date ? { timestamp: createdAt } : {}),
178
+ sourceKind: "first_user_message"
179
+ }
180
+ ];
181
+ }
182
+ export const codexTraceReader = {
183
+ id: "codex",
184
+ defaultRoots(homeDir) {
185
+ return [path.join(homeDir, ".codex", "state_5.sqlite")];
186
+ },
187
+ async discover(options) {
188
+ const databasePath = path.join(options.homeDir, ".codex", "state_5.sqlite");
189
+ const sqlite = options.sqlite ?? defaultSqliteFactory;
190
+ let db;
191
+ try {
192
+ db = await sqlite(databasePath);
193
+ }
194
+ catch (error) {
195
+ if (isMissingFile(error)) {
196
+ return [];
197
+ }
198
+ throw error;
199
+ }
200
+ try {
201
+ const sinceSeconds = options.since ? Math.floor(options.since.getTime() / 1_000) : null;
202
+ const sql = [
203
+ "SELECT id, rollout_path, created_at, updated_at, created_at_ms, updated_at_ms, source, model, cwd, title, first_user_message",
204
+ "FROM threads",
205
+ options.allWorkspaces || !options.cwd
206
+ ? sinceSeconds === null
207
+ ? ""
208
+ : "WHERE updated_at >= ? OR updated_at_ms >= ?"
209
+ : "WHERE cwd = ? AND (? IS NULL OR updated_at >= ? OR updated_at_ms >= ?)",
210
+ "ORDER BY updated_at DESC"
211
+ ]
212
+ .filter((part) => part.length > 0)
213
+ .join(" ");
214
+ const params = options.allWorkspaces || !options.cwd
215
+ ? sinceSeconds === null
216
+ ? []
217
+ : [sinceSeconds, sinceSeconds * 1_000]
218
+ : [
219
+ options.cwd,
220
+ sinceSeconds,
221
+ sinceSeconds,
222
+ sinceSeconds === null ? null : sinceSeconds * 1_000
223
+ ];
224
+ const rows = (await db.all(sql, params))
225
+ .map(rowFromUnknown)
226
+ .filter((row) => row !== undefined);
227
+ return rows.map(referenceFromRow);
228
+ }
229
+ finally {
230
+ await db.close();
231
+ }
232
+ },
233
+ async read(reference, options) {
234
+ const createdAt = reference.metadata?.createdAt;
235
+ const updatedAt = reference.metadata?.updatedAt;
236
+ let turns = [];
237
+ if (reference.path) {
238
+ try {
239
+ turns = await readRollout(reference.path, options.fs);
240
+ }
241
+ catch (error) {
242
+ if (!isMissingFile(error)) {
243
+ throw error;
244
+ }
245
+ }
246
+ }
247
+ if (turns.length === 0) {
248
+ turns = fallbackTurn(reference);
249
+ }
250
+ const turnUpdatedAt = turns.reduce((latest, turn) => newestDate(latest, turn.timestamp), undefined);
251
+ return {
252
+ source: "codex",
253
+ id: reference.id,
254
+ ...(reference.path ? { path: reference.path } : {}),
255
+ ...(reference.cwd ? { cwd: reference.cwd } : {}),
256
+ ...(reference.title ? { title: reference.title } : {}),
257
+ ...(createdAt instanceof Date ? { createdAt } : {}),
258
+ ...(updatedAt instanceof Date
259
+ ? { updatedAt }
260
+ : turnUpdatedAt
261
+ ? { updatedAt: turnUpdatedAt }
262
+ : {}),
263
+ turns
264
+ };
265
+ }
266
+ };
@@ -0,0 +1,5 @@
1
+ import { claudeTraceReader } from "./claude.js";
2
+ import { codexTraceReader } from "./codex.js";
3
+ import type { TraceReader } from "../types.js";
4
+ export declare const traceReaders: TraceReader[];
5
+ export { claudeTraceReader, codexTraceReader };
@@ -0,0 +1,4 @@
1
+ import { claudeTraceReader } from "./claude.js";
2
+ import { codexTraceReader } from "./codex.js";
3
+ export const traceReaders = [claudeTraceReader, codexTraceReader];
4
+ export { claudeTraceReader, codexTraceReader };