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.
@@ -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
- this.currentThread = this.codex.startThread({
41
+ const thread = this.codex.startThread({
42
42
  workingDirectory: this.workingDirectory,
43
43
  skipGitRepoCheck: true,
44
44
  sandboxMode: 'danger-full-access',
45
45
  });
46
- if (this.currentThread.id) {
47
- this.currentThreadId = this.currentThread.id;
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
- await this.currentThread.run(systemMessage);
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
- const filteredEvents = events.filter((event) => {
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
- const filteredCount = events.length - filteredEvents.length;
107
- if (filteredCount > 0) {
108
- console.log(`[CodexManager] Filtered out ${filteredCount} priming events`);
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
- return content
8
- .split('\n')
9
- .filter((line) => line.trim())
10
- .map((line) => {
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 JSONL line: ${line}`, e);
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('Error finding session file:', 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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",