replicas-engine 0.1.9 → 0.1.11
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/dist/services/codex-manager.js +39 -13
- package/dist/utils/jsonl-reader.js +34 -9
- package/package.json +1 -1
|
@@ -38,18 +38,13 @@ export class CodexManager {
|
|
|
38
38
|
else {
|
|
39
39
|
console.log('[CodexManager] No existing thread, starting new thread');
|
|
40
40
|
console.log(`[CodexManager] Working directory: ${this.workingDirectory}`);
|
|
41
|
-
|
|
41
|
+
const thread = this.codex.startThread({
|
|
42
42
|
workingDirectory: this.workingDirectory,
|
|
43
43
|
skipGitRepoCheck: true,
|
|
44
44
|
sandboxMode: 'danger-full-access',
|
|
45
45
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(`[CodexManager] New thread created with ID: ${this.currentThreadId}`);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
console.error('[CodexManager] ERROR: Thread created but no ID available');
|
|
52
|
-
}
|
|
46
|
+
this.currentThread = thread;
|
|
47
|
+
console.log(`[CodexManager] Thread object created, ID at creation: ${thread.id || 'null'}`);
|
|
53
48
|
// prime the thread with system instructions if thread is new
|
|
54
49
|
const workspaceName = process.env.WORKSPACE_NAME || 'workspace';
|
|
55
50
|
console.log(`[CodexManager] Workspace name from env: ${workspaceName}`);
|
|
@@ -64,9 +59,27 @@ export class CodexManager {
|
|
|
64
59
|
const systemMessage = `<replicas_important_instructions>When completing solutions, push your changes to branch ${branchName} and to origin. Greet the user.</replicas_important_instructions>`;
|
|
65
60
|
console.log('[CodexManager] Starting thread priming with system instructions');
|
|
66
61
|
const primingStartTime = Date.now();
|
|
67
|
-
|
|
62
|
+
// Use runStreamed to capture thread.started event with thread_id
|
|
63
|
+
const streamedTurn = await this.currentThread.runStreamed(systemMessage);
|
|
64
|
+
console.log('[CodexManager] Processing priming stream events...');
|
|
65
|
+
for await (const event of streamedTurn.events) {
|
|
66
|
+
console.log(`[CodexManager] Event: ${event.type}`);
|
|
67
|
+
if (event.type === 'thread.started') {
|
|
68
|
+
this.currentThreadId = event.thread_id;
|
|
69
|
+
console.log(`[CodexManager] ✓ Thread ID captured from thread.started event: ${this.currentThreadId}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
68
72
|
const primingDuration = Date.now() - primingStartTime;
|
|
69
73
|
console.log(`[CodexManager] Thread priming completed in ${primingDuration}ms`);
|
|
74
|
+
// Double-check thread ID is available
|
|
75
|
+
if (!this.currentThreadId && this.currentThread.id) {
|
|
76
|
+
this.currentThreadId = this.currentThread.id;
|
|
77
|
+
console.log(`[CodexManager] Thread ID fallback from thread.id property: ${this.currentThreadId}`);
|
|
78
|
+
}
|
|
79
|
+
if (!this.currentThreadId) {
|
|
80
|
+
console.error('[CodexManager] ERROR: Thread ID still not available after priming run');
|
|
81
|
+
console.error(`[CodexManager] thread.id = ${this.currentThread.id}`);
|
|
82
|
+
}
|
|
70
83
|
}
|
|
71
84
|
}
|
|
72
85
|
else {
|
|
@@ -99,13 +112,26 @@ export class CodexManager {
|
|
|
99
112
|
console.log(`[CodexManager] Reading session file: ${sessionFile}`);
|
|
100
113
|
const events = await readJSONL(sessionFile);
|
|
101
114
|
console.log(`[CodexManager] Read ${events.length} events from session file`);
|
|
102
|
-
|
|
115
|
+
// Filter out priming instruction events
|
|
116
|
+
let filteredEvents = events.filter((event) => {
|
|
103
117
|
const eventStr = JSON.stringify(event);
|
|
104
118
|
return !eventStr.includes('<replicas_important_instructions>');
|
|
105
119
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
// Find the first real user message (after priming)
|
|
121
|
+
const firstUserMessageIndex = filteredEvents.findIndex((event) => {
|
|
122
|
+
return event.type === 'response_item' &&
|
|
123
|
+
event.payload?.type === 'message' &&
|
|
124
|
+
event.payload?.role === 'user';
|
|
125
|
+
});
|
|
126
|
+
// If we found a user message, remove all events before it (priming response)
|
|
127
|
+
if (firstUserMessageIndex > 0) {
|
|
128
|
+
const beforeCount = filteredEvents.length;
|
|
129
|
+
filteredEvents = filteredEvents.slice(firstUserMessageIndex);
|
|
130
|
+
console.log(`[CodexManager] Filtered out ${beforeCount - filteredEvents.length} events before first user message`);
|
|
131
|
+
}
|
|
132
|
+
const totalFilteredCount = events.length - filteredEvents.length;
|
|
133
|
+
if (totalFilteredCount > 0) {
|
|
134
|
+
console.log(`[CodexManager] Total filtered: ${totalFilteredCount} events (priming + response)`);
|
|
109
135
|
}
|
|
110
136
|
console.log(`[CodexManager] Returning ${filteredEvents.length} events`);
|
|
111
137
|
return {
|
|
@@ -2,30 +2,36 @@ import { readFile, readdir, stat } from 'fs/promises';
|
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
4
|
export async function readJSONL(filePath) {
|
|
5
|
+
console.log(`[jsonl-reader] readJSONL called for: ${filePath}`);
|
|
5
6
|
try {
|
|
6
7
|
const content = await readFile(filePath, 'utf-8');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
console.log(`[jsonl-reader] File read successfully, content length: ${content.length} bytes`);
|
|
9
|
+
const lines = content.split('\n').filter((line) => line.trim());
|
|
10
|
+
console.log(`[jsonl-reader] Found ${lines.length} non-empty lines`);
|
|
11
|
+
const events = lines
|
|
12
|
+
.map((line, index) => {
|
|
11
13
|
try {
|
|
12
14
|
return JSON.parse(line);
|
|
13
15
|
}
|
|
14
16
|
catch (e) {
|
|
15
|
-
console.error(`Failed to parse
|
|
17
|
+
console.error(`[jsonl-reader] Failed to parse line ${index + 1}: ${line.substring(0, 100)}...`, e);
|
|
16
18
|
return null;
|
|
17
19
|
}
|
|
18
20
|
})
|
|
19
21
|
.filter((event) => event !== null);
|
|
22
|
+
console.log(`[jsonl-reader] Successfully parsed ${events.length} events`);
|
|
23
|
+
return events;
|
|
20
24
|
}
|
|
21
25
|
catch (error) {
|
|
22
|
-
console.error(`Failed to read JSONL file ${filePath}:`, error);
|
|
26
|
+
console.error(`[jsonl-reader] ERROR: Failed to read JSONL file ${filePath}:`, error);
|
|
23
27
|
return [];
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
// Sessions are stored in ~/.codex/sessions/YYYY/MM/DD/rollout-*-{threadId}.jsonl
|
|
27
31
|
export async function findSessionFile(threadId) {
|
|
32
|
+
console.log(`[jsonl-reader] findSessionFile called for threadId: ${threadId}`);
|
|
28
33
|
const sessionsDir = join(homedir(), '.codex', 'sessions');
|
|
34
|
+
console.log(`[jsonl-reader] Sessions directory: ${sessionsDir}`);
|
|
29
35
|
try {
|
|
30
36
|
// current date for searching
|
|
31
37
|
const now = new Date();
|
|
@@ -33,9 +39,13 @@ export async function findSessionFile(threadId) {
|
|
|
33
39
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
34
40
|
const day = String(now.getDate()).padStart(2, '0');
|
|
35
41
|
const todayDir = join(sessionsDir, String(year), month, day);
|
|
42
|
+
console.log(`[jsonl-reader] Searching today's directory: ${todayDir}`);
|
|
36
43
|
const file = await findFileInDirectory(todayDir, threadId);
|
|
37
|
-
if (file)
|
|
44
|
+
if (file) {
|
|
45
|
+
console.log(`[jsonl-reader] Found file in today's directory: ${file}`);
|
|
38
46
|
return file;
|
|
47
|
+
}
|
|
48
|
+
console.log(`[jsonl-reader] Not found in today's directory, searching previous 7 days`);
|
|
39
49
|
for (let daysAgo = 1; daysAgo <= 7; daysAgo++) {
|
|
40
50
|
const date = new Date(now);
|
|
41
51
|
date.setDate(date.getDate() - daysAgo);
|
|
@@ -43,32 +53,47 @@ export async function findSessionFile(threadId) {
|
|
|
43
53
|
const searchMonth = String(date.getMonth() + 1).padStart(2, '0');
|
|
44
54
|
const searchDay = String(date.getDate()).padStart(2, '0');
|
|
45
55
|
const searchDir = join(sessionsDir, String(searchYear), searchMonth, searchDay);
|
|
56
|
+
console.log(`[jsonl-reader] Searching ${daysAgo} day(s) ago: ${searchDir}`);
|
|
46
57
|
const file = await findFileInDirectory(searchDir, threadId);
|
|
47
|
-
if (file)
|
|
58
|
+
if (file) {
|
|
59
|
+
console.log(`[jsonl-reader] Found file ${daysAgo} day(s) ago: ${file}`);
|
|
48
60
|
return file;
|
|
61
|
+
}
|
|
49
62
|
}
|
|
63
|
+
console.log(`[jsonl-reader] Session file not found after searching 8 days`);
|
|
50
64
|
return null;
|
|
51
65
|
}
|
|
52
66
|
catch (error) {
|
|
53
|
-
console.error('
|
|
67
|
+
console.error('[jsonl-reader] ERROR finding session file:', error);
|
|
54
68
|
return null;
|
|
55
69
|
}
|
|
56
70
|
}
|
|
57
71
|
async function findFileInDirectory(directory, threadId) {
|
|
58
72
|
try {
|
|
73
|
+
console.log(`[jsonl-reader] Reading directory: ${directory}`);
|
|
59
74
|
const files = await readdir(directory);
|
|
75
|
+
console.log(`[jsonl-reader] Found ${files.length} files in directory`);
|
|
76
|
+
const jsonlFiles = files.filter(f => f.endsWith('.jsonl'));
|
|
77
|
+
console.log(`[jsonl-reader] ${jsonlFiles.length} JSONL files: ${jsonlFiles.join(', ')}`);
|
|
60
78
|
for (const file of files) {
|
|
61
79
|
if (file.endsWith('.jsonl') && file.includes(threadId)) {
|
|
62
80
|
const fullPath = join(directory, file);
|
|
81
|
+
console.log(`[jsonl-reader] Found matching file: ${file} -> ${fullPath}`);
|
|
63
82
|
const stats = await stat(fullPath);
|
|
64
83
|
if (stats.isFile()) {
|
|
84
|
+
console.log(`[jsonl-reader] Confirmed as regular file, returning: ${fullPath}`);
|
|
65
85
|
return fullPath;
|
|
66
86
|
}
|
|
87
|
+
else {
|
|
88
|
+
console.log(`[jsonl-reader] WARNING: Path exists but is not a regular file`);
|
|
89
|
+
}
|
|
67
90
|
}
|
|
68
91
|
}
|
|
92
|
+
console.log(`[jsonl-reader] No matching file found in ${directory}`);
|
|
69
93
|
return null;
|
|
70
94
|
}
|
|
71
95
|
catch (error) {
|
|
96
|
+
console.log(`[jsonl-reader] Directory read failed for ${directory}:`, error instanceof Error ? error.message : error);
|
|
72
97
|
return null;
|
|
73
98
|
}
|
|
74
99
|
}
|