replicas-engine 0.1.8 → 0.1.10

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.
@@ -24,28 +24,30 @@ export class CodexManager {
24
24
  }
25
25
  }
26
26
  async sendMessage(message) {
27
+ console.log(`[CodexManager] sendMessage called with message length: ${message.length}`);
27
28
  if (!this.currentThread) {
28
29
  if (this.currentThreadId) {
29
- console.log(`Resuming thread ${this.currentThreadId}`);
30
+ console.log(`[CodexManager] Resuming existing thread: ${this.currentThreadId}`);
30
31
  this.currentThread = this.codex.resumeThread(this.currentThreadId, {
31
32
  workingDirectory: this.workingDirectory,
32
33
  skipGitRepoCheck: true,
33
34
  sandboxMode: 'danger-full-access',
34
35
  });
36
+ console.log(`[CodexManager] Thread resumed successfully`);
35
37
  }
36
38
  else {
37
- console.log('Starting new thread');
38
- this.currentThread = this.codex.startThread({
39
+ console.log('[CodexManager] No existing thread, starting new thread');
40
+ console.log(`[CodexManager] Working directory: ${this.workingDirectory}`);
41
+ const thread = this.codex.startThread({
39
42
  workingDirectory: this.workingDirectory,
40
43
  skipGitRepoCheck: true,
41
44
  sandboxMode: 'danger-full-access',
42
45
  });
43
- if (this.currentThread.id) {
44
- this.currentThreadId = this.currentThread.id;
45
- console.log(`Thread started: ${this.currentThreadId}`);
46
- }
46
+ this.currentThread = thread;
47
+ console.log(`[CodexManager] Thread object created, ID at creation: ${thread.id || 'null'}`);
47
48
  // prime the thread with system instructions if thread is new
48
49
  const workspaceName = process.env.WORKSPACE_NAME || 'workspace';
50
+ console.log(`[CodexManager] Workspace name from env: ${workspaceName}`);
49
51
  const workspaceBranchSegment = workspaceName
50
52
  .trim()
51
53
  .toLowerCase()
@@ -53,62 +55,115 @@ export class CodexManager {
53
55
  .replace(/^-+|-+$/g, '') || 'workspace';
54
56
  const uuid = randomUUID().slice(0, 8);
55
57
  const branchName = `replicas/${workspaceBranchSegment}-${uuid}`;
58
+ console.log(`[CodexManager] Generated branch name: ${branchName}`);
56
59
  const systemMessage = `<replicas_important_instructions>When completing solutions, push your changes to branch ${branchName} and to origin. Greet the user.</replicas_important_instructions>`;
57
- console.log('Priming thread with system instructions');
58
- await this.currentThread.run(systemMessage);
59
- console.log('Thread primed successfully');
60
+ console.log('[CodexManager] Starting thread priming with system instructions');
61
+ const primingStartTime = Date.now();
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
+ }
72
+ const primingDuration = Date.now() - primingStartTime;
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
+ }
60
83
  }
61
84
  }
85
+ else {
86
+ console.log(`[CodexManager] Using existing thread object for thread ID: ${this.currentThreadId}`);
87
+ }
88
+ console.log(`[CodexManager] Running user message on thread ${this.currentThreadId}`);
89
+ const messageStartTime = Date.now();
62
90
  await this.currentThread.run(message);
91
+ const messageDuration = Date.now() - messageStartTime;
92
+ console.log(`[CodexManager] User message run completed in ${messageDuration}ms`);
63
93
  }
64
94
  async getHistory() {
95
+ console.log('[CodexManager] getHistory called');
65
96
  if (!this.currentThreadId) {
97
+ console.log('[CodexManager] No active thread ID, returning empty history');
66
98
  return {
67
99
  thread_id: null,
68
100
  events: [],
69
101
  };
70
102
  }
103
+ console.log(`[CodexManager] Looking for session file for thread: ${this.currentThreadId}`);
71
104
  const sessionFile = await findSessionFile(this.currentThreadId);
72
105
  if (!sessionFile) {
73
- console.warn(`Session file not found for thread ${this.currentThreadId}`);
106
+ console.warn(`[CodexManager] WARNING: Session file not found for thread ${this.currentThreadId}`);
74
107
  return {
75
108
  thread_id: this.currentThreadId,
76
109
  events: [],
77
110
  };
78
111
  }
79
- console.log(`Reading session file: ${sessionFile}`);
112
+ console.log(`[CodexManager] Reading session file: ${sessionFile}`);
80
113
  const events = await readJSONL(sessionFile);
114
+ console.log(`[CodexManager] Read ${events.length} events from session file`);
81
115
  const filteredEvents = events.filter((event) => {
82
116
  const eventStr = JSON.stringify(event);
83
117
  return !eventStr.includes('<replicas_important_instructions>');
84
118
  });
119
+ const filteredCount = events.length - filteredEvents.length;
120
+ if (filteredCount > 0) {
121
+ console.log(`[CodexManager] Filtered out ${filteredCount} priming events`);
122
+ }
123
+ console.log(`[CodexManager] Returning ${filteredEvents.length} events`);
85
124
  return {
86
125
  thread_id: this.currentThreadId,
87
126
  events: filteredEvents,
88
127
  };
89
128
  }
90
129
  async getStatus() {
130
+ console.log('[CodexManager] getStatus called');
91
131
  let sessionFile = null;
92
132
  if (this.currentThreadId) {
133
+ console.log(`[CodexManager] Checking for session file for thread: ${this.currentThreadId}`);
93
134
  sessionFile = await findSessionFile(this.currentThreadId);
135
+ if (sessionFile) {
136
+ console.log(`[CodexManager] Session file found: ${sessionFile}`);
137
+ }
138
+ else {
139
+ console.log('[CodexManager] Session file not found');
140
+ }
94
141
  }
95
- return {
142
+ else {
143
+ console.log('[CodexManager] No active thread ID');
144
+ }
145
+ const status = {
96
146
  has_active_thread: this.currentThreadId !== null,
97
147
  thread_id: this.currentThreadId,
98
148
  session_file: sessionFile,
99
149
  working_directory: this.workingDirectory,
100
150
  };
151
+ console.log(`[CodexManager] Status: ${JSON.stringify(status)}`);
152
+ return status;
101
153
  }
102
154
  reset() {
103
- console.log('Resetting Codex thread');
155
+ console.log(`[CodexManager] Resetting thread (was: ${this.currentThreadId})`);
104
156
  this.currentThread = null;
105
157
  this.currentThreadId = null;
158
+ console.log('[CodexManager] Thread reset complete');
106
159
  }
107
160
  getThreadId() {
108
161
  return this.currentThreadId;
109
162
  }
110
163
  async getUpdates(since) {
164
+ console.log(`[CodexManager] getUpdates called with since: ${since}`);
111
165
  if (!this.currentThreadId) {
166
+ console.log('[CodexManager] No active thread, returning empty updates');
112
167
  return {
113
168
  events: [],
114
169
  isComplete: true,
@@ -116,20 +171,24 @@ export class CodexManager {
116
171
  }
117
172
  const sessionFile = await findSessionFile(this.currentThreadId);
118
173
  if (!sessionFile) {
174
+ console.log('[CodexManager] Session file not found, returning empty updates');
119
175
  return {
120
176
  events: [],
121
177
  isComplete: true,
122
178
  };
123
179
  }
124
180
  const allEvents = await readJSONL(sessionFile);
181
+ console.log(`[CodexManager] Read ${allEvents.length} total events`);
125
182
  // Filter events that occurred after the 'since' timestamp
126
183
  const filteredEvents = allEvents.filter((event) => {
127
184
  return event.timestamp > since;
128
185
  });
186
+ console.log(`[CodexManager] Found ${filteredEvents.length} events since ${since}`);
129
187
  // Check if thread is complete by looking for turn.completed or error events
130
188
  const isComplete = this.currentThread === null ||
131
189
  allEvents.some((event) => event.type === 'event_msg' &&
132
190
  (event.payload?.type === 'turn.completed' || event.payload?.type === 'error'));
191
+ console.log(`[CodexManager] Thread complete: ${isComplete}`);
133
192
  return {
134
193
  events: filteredEvents,
135
194
  isComplete,
@@ -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.8",
3
+ "version": "0.1.10",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",