@ai-devkit/agent-manager 0.16.0 → 0.17.0

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 (97) hide show
  1. package/.swcrc +27 -0
  2. package/dist/AgentManager.js +72 -74
  3. package/dist/AgentManager.js.map +1 -1
  4. package/dist/__tests__/AgentManager.test.js +564 -0
  5. package/dist/__tests__/AgentManager.test.js.map +1 -0
  6. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +2157 -0
  7. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -0
  8. package/dist/__tests__/adapters/CodexAdapter.test.js +1354 -0
  9. package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -0
  10. package/dist/__tests__/adapters/CopilotAdapter.test.js +500 -0
  11. package/dist/__tests__/adapters/CopilotAdapter.test.js.map +1 -0
  12. package/dist/__tests__/adapters/GeminiCliAdapter.test.js +1229 -0
  13. package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +1 -0
  14. package/dist/__tests__/adapters/OpenCodeAdapter.test.js +626 -0
  15. package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +1 -0
  16. package/dist/__tests__/terminal/TmuxManager.test.js +159 -0
  17. package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -0
  18. package/dist/__tests__/terminal/TtyWriter.test.js +162 -0
  19. package/dist/__tests__/terminal/TtyWriter.test.js.map +1 -0
  20. package/dist/__tests__/utils/AgentRegistry.test.js +282 -0
  21. package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -0
  22. package/dist/__tests__/utils/ClaudeSessionParser.test.js +312 -0
  23. package/dist/__tests__/utils/ClaudeSessionParser.test.js.map +1 -0
  24. package/dist/__tests__/utils/matching.test.js +324 -0
  25. package/dist/__tests__/utils/matching.test.js.map +1 -0
  26. package/dist/__tests__/utils/process.test.js +177 -0
  27. package/dist/__tests__/utils/process.test.js.map +1 -0
  28. package/dist/__tests__/utils/session.test.js +158 -0
  29. package/dist/__tests__/utils/session.test.js.map +1 -0
  30. package/dist/adapters/AgentAdapter.d.ts +1 -1
  31. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  32. package/dist/adapters/AgentAdapter.js +8 -7
  33. package/dist/adapters/AgentAdapter.js.map +1 -1
  34. package/dist/adapters/ClaudeCodeAdapter.js +66 -77
  35. package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
  36. package/dist/adapters/CodexAdapter.d.ts +9 -0
  37. package/dist/adapters/CodexAdapter.d.ts.map +1 -1
  38. package/dist/adapters/CodexAdapter.js +188 -141
  39. package/dist/adapters/CodexAdapter.js.map +1 -1
  40. package/dist/adapters/CopilotAdapter.d.ts +44 -0
  41. package/dist/adapters/CopilotAdapter.d.ts.map +1 -0
  42. package/dist/adapters/CopilotAdapter.js +314 -0
  43. package/dist/adapters/CopilotAdapter.js.map +1 -0
  44. package/dist/adapters/GeminiCliAdapter.d.ts +2 -0
  45. package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -1
  46. package/dist/adapters/GeminiCliAdapter.js +138 -156
  47. package/dist/adapters/GeminiCliAdapter.js.map +1 -1
  48. package/dist/adapters/OpenCodeAdapter.js +60 -69
  49. package/dist/adapters/OpenCodeAdapter.js.map +1 -1
  50. package/dist/adapters/index.d.ts +1 -0
  51. package/dist/adapters/index.d.ts.map +1 -1
  52. package/dist/adapters/index.js +2 -0
  53. package/dist/adapters/index.js.map +1 -1
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +2 -0
  57. package/dist/index.js.map +1 -1
  58. package/dist/terminal/TerminalFocusManager.js +62 -49
  59. package/dist/terminal/TerminalFocusManager.js.map +1 -1
  60. package/dist/terminal/TmuxManager.js +59 -34
  61. package/dist/terminal/TmuxManager.js.map +1 -1
  62. package/dist/terminal/TtyWriter.js +37 -16
  63. package/dist/terminal/TtyWriter.js.map +1 -1
  64. package/dist/terminal/index.js +1 -0
  65. package/dist/terminal/index.js.map +1 -1
  66. package/dist/utils/AgentRegistry.js +41 -32
  67. package/dist/utils/AgentRegistry.js.map +1 -1
  68. package/dist/utils/ClaudeSessionParser.js +46 -87
  69. package/dist/utils/ClaudeSessionParser.js.map +1 -1
  70. package/dist/utils/agents.d.ts +1 -1
  71. package/dist/utils/agents.d.ts.map +1 -1
  72. package/dist/utils/agents.js +22 -11
  73. package/dist/utils/agents.js.map +1 -1
  74. package/dist/utils/applescript.js +3 -6
  75. package/dist/utils/applescript.js.map +1 -1
  76. package/dist/utils/index.js +1 -0
  77. package/dist/utils/index.js.map +1 -1
  78. package/dist/utils/matching.js +21 -29
  79. package/dist/utils/matching.js.map +1 -1
  80. package/dist/utils/process.js +77 -56
  81. package/dist/utils/process.js.map +1 -1
  82. package/dist/utils/session.js +20 -34
  83. package/dist/utils/session.js.map +1 -1
  84. package/dist/utils/sortAgents.js +6 -8
  85. package/dist/utils/sortAgents.js.map +1 -1
  86. package/package.json +11 -7
  87. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +2 -0
  88. package/src/__tests__/adapters/CodexAdapter.test.ts +94 -0
  89. package/src/__tests__/adapters/CopilotAdapter.test.ts +411 -0
  90. package/src/__tests__/adapters/GeminiCliAdapter.test.ts +74 -1
  91. package/src/adapters/AgentAdapter.ts +1 -1
  92. package/src/adapters/CodexAdapter.ts +114 -6
  93. package/src/adapters/CopilotAdapter.ts +453 -0
  94. package/src/adapters/GeminiCliAdapter.ts +42 -3
  95. package/src/adapters/index.ts +1 -0
  96. package/src/index.ts +1 -0
  97. package/src/utils/agents.ts +1 -1
@@ -8,8 +8,7 @@
8
8
  * 4. Setting resolvedCwd from session_meta first line
9
9
  * 5. Matching sessions to processes via shared matchProcessesToSessions()
10
10
  * 6. Extracting summary from last event entry in session JSONL
11
- */
12
- import * as fs from 'fs';
11
+ */ import * as fs from 'fs';
13
12
  import * as path from 'path';
14
13
  import { AgentStatus } from './AgentAdapter.js';
15
14
  import { listAgentProcesses, enrichProcesses } from '../utils/process.js';
@@ -19,11 +18,10 @@ import { AgentRegistry } from '../utils/AgentRegistry.js';
19
18
  export class CodexAdapter {
20
19
  type = 'codex';
21
20
  static IDLE_THRESHOLD_MINUTES = 5;
22
- /** Include session files around process start day to recover long-lived processes. */
23
- static PROCESS_START_DAY_WINDOW_DAYS = 1;
21
+ /** Include session files around process start day to recover long-lived processes. */ static PROCESS_START_DAY_WINDOW_DAYS = 1;
24
22
  codexSessionsDir;
25
23
  registry;
26
- constructor(registry = AgentRegistry.default()) {
24
+ constructor(registry = AgentRegistry.default()){
27
25
  const homeDir = process.env.HOME || process.env.USERPROFILE || '';
28
26
  this.codexSessionsDir = path.join(homeDir, '.codex', 'sessions');
29
27
  this.registry = registry;
@@ -33,48 +31,62 @@ export class CodexAdapter {
33
31
  }
34
32
  /**
35
33
  * Detect running Codex agents
36
- */
37
- async detectAgents() {
34
+ */ async detectAgents() {
38
35
  const processes = enrichProcesses(listAgentProcesses('codex'));
39
- if (processes.length === 0)
40
- return [];
36
+ if (processes.length === 0) return [];
41
37
  const { cachedAgents, remaining } = this.tryRegistryCache(processes);
42
- if (remaining.length === 0)
43
- return cachedAgents;
44
- const { sessions, contentCache } = this.discoverSessions(remaining);
38
+ if (remaining.length === 0) return cachedAgents;
39
+ const { direct, fallback } = this.tryResumeMatching(remaining);
40
+ const directResult = this.mapDirectMatches(direct);
41
+ const { sessions, contentCache } = this.discoverSessions(fallback);
45
42
  if (sessions.length === 0) {
46
- return [...cachedAgents, ...remaining.map((p) => this.mapProcessOnlyAgent(p))];
43
+ return [
44
+ ...cachedAgents,
45
+ ...directResult.agents,
46
+ ...directResult.failedProcesses.map((p)=>this.mapProcessOnlyAgent(p)),
47
+ ...fallback.map((p)=>this.mapProcessOnlyAgent(p))
48
+ ];
47
49
  }
48
- const matches = matchProcessesToSessions(remaining, sessions);
49
- const matchedPids = new Set(matches.map((m) => m.process.pid));
50
- const agents = [];
51
- for (const match of matches) {
50
+ const matches = matchProcessesToSessions(fallback, sessions);
51
+ const matchedPids = new Set([
52
+ ...directResult.agents.map((a)=>a.pid),
53
+ ...matches.map((m)=>m.process.pid)
54
+ ]);
55
+ const agents = [
56
+ ...directResult.agents
57
+ ];
58
+ for (const match of matches){
52
59
  const cachedContent = contentCache.get(match.session.filePath);
53
60
  const sessionData = this.parseSession(cachedContent, match.session.filePath);
54
61
  if (sessionData) {
55
62
  agents.push(this.mapSessionToAgent(sessionData, match.process, match.session.filePath));
56
- }
57
- else {
63
+ } else {
58
64
  matchedPids.delete(match.process.pid);
59
65
  }
60
66
  }
61
- for (const proc of remaining) {
67
+ for (const proc of fallback){
62
68
  if (!matchedPids.has(proc.pid)) {
63
69
  agents.push(this.mapProcessOnlyAgent(proc));
64
70
  }
65
71
  }
66
- return [...cachedAgents, ...agents];
72
+ for (const proc of directResult.failedProcesses){
73
+ agents.push(this.mapProcessOnlyAgent(proc));
74
+ }
75
+ return [
76
+ ...cachedAgents,
77
+ ...agents
78
+ ];
67
79
  }
68
80
  tryRegistryCache(processes) {
69
81
  const cachedAgents = [];
70
82
  const remaining = [];
71
- const byPid = new Map(this.registry.list().map((e) => [e.pid, e]));
72
- for (const proc of processes) {
83
+ const byPid = new Map(this.registry.list().map((e)=>[
84
+ e.pid,
85
+ e
86
+ ]));
87
+ for (const proc of processes){
73
88
  const entry = byPid.get(proc.pid);
74
- if (!entry ||
75
- entry.type !== this.type ||
76
- !entry.sessionFilePath ||
77
- !fs.existsSync(entry.sessionFilePath)) {
89
+ if (!entry || entry.type !== this.type || !entry.sessionFilePath || !fs.existsSync(entry.sessionFilePath)) {
78
90
  remaining.push(proc);
79
91
  continue;
80
92
  }
@@ -86,7 +98,84 @@ export class CodexAdapter {
86
98
  }
87
99
  cachedAgents.push(this.mapSessionToAgent(sessionData, proc, entry.sessionFilePath));
88
100
  }
89
- return { cachedAgents, remaining };
101
+ return {
102
+ cachedAgents,
103
+ remaining
104
+ };
105
+ }
106
+ /**
107
+ * Match processes via `codex resume <uuid>` in their command line.
108
+ * Resumed sessions predate the process, so birth-time proximity cannot
109
+ * reliably pair them with the running PID.
110
+ */ tryResumeMatching(processes) {
111
+ const direct = [];
112
+ const fallback = [];
113
+ for (const proc of processes){
114
+ const sessionId = this.extractResumeSessionId(proc.command);
115
+ if (!sessionId) {
116
+ fallback.push(proc);
117
+ continue;
118
+ }
119
+ const sessionFile = this.findSessionFileById(sessionId);
120
+ if (!sessionFile) {
121
+ fallback.push(proc);
122
+ continue;
123
+ }
124
+ direct.push({
125
+ process: proc,
126
+ sessionFile
127
+ });
128
+ }
129
+ return {
130
+ direct,
131
+ fallback
132
+ };
133
+ }
134
+ extractResumeSessionId(command) {
135
+ const match = command.match(/(?:^|\s)resume\s+([0-9a-f-]{36})(?:\s|$)/i);
136
+ return match?.[1] ?? null;
137
+ }
138
+ findSessionFileById(sessionId) {
139
+ for (const filePath of this.collectAllSessionFiles()){
140
+ if (!path.basename(filePath).includes(sessionId)) continue;
141
+ const content = safeReadFile(filePath);
142
+ const firstLine = content?.split('\n')[0]?.trim();
143
+ if (!firstLine) continue;
144
+ try {
145
+ const parsed = JSON.parse(firstLine);
146
+ if (parsed.type !== 'session_meta' || parsed.payload?.id !== sessionId) {
147
+ continue;
148
+ }
149
+ const stat = safeStat(filePath);
150
+ if (!stat) continue;
151
+ return {
152
+ sessionId,
153
+ filePath,
154
+ projectDir: path.dirname(filePath),
155
+ birthtimeMs: stat.birthtimeMs,
156
+ resolvedCwd: parsed.payload.cwd || ''
157
+ };
158
+ } catch {
159
+ continue;
160
+ }
161
+ }
162
+ return null;
163
+ }
164
+ mapDirectMatches(matches) {
165
+ const agents = [];
166
+ const failedProcesses = [];
167
+ for (const match of matches){
168
+ const sessionData = this.parseSession(undefined, match.sessionFile.filePath);
169
+ if (sessionData) {
170
+ agents.push(this.mapSessionToAgent(sessionData, match.process, match.sessionFile.filePath));
171
+ } else {
172
+ failedProcesses.push(match.process);
173
+ }
174
+ }
175
+ return {
176
+ agents,
177
+ failedProcesses
178
+ };
90
179
  }
91
180
  /**
92
181
  * Discover session files for the given processes.
@@ -95,18 +184,18 @@ export class CodexAdapter {
95
184
  * to scan (±1 day window), then batches stat calls across all directories.
96
185
  * Reads each file once and caches content for later parsing by parseSession().
97
186
  * Sets resolvedCwd from session_meta first line.
98
- */
99
- discoverSessions(processes) {
100
- const empty = { sessions: [], contentCache: new Map() };
101
- if (!fs.existsSync(this.codexSessionsDir))
102
- return empty;
187
+ */ discoverSessions(processes) {
188
+ const empty = {
189
+ sessions: [],
190
+ contentCache: new Map()
191
+ };
192
+ if (!fs.existsSync(this.codexSessionsDir)) return empty;
103
193
  const dateDirs = this.getDateDirs(processes);
104
- if (dateDirs.length === 0)
105
- return empty;
194
+ if (dateDirs.length === 0) return empty;
106
195
  const files = batchGetSessionFileBirthtimes(dateDirs);
107
196
  const contentCache = new Map();
108
197
  // Read each file once: extract CWD for matching, cache content for later parsing
109
- for (const file of files) {
198
+ for (const file of files){
110
199
  try {
111
200
  const content = fs.readFileSync(file.filePath, 'utf-8');
112
201
  contentCache.set(file.filePath, content);
@@ -117,37 +206,37 @@ export class CodexAdapter {
117
206
  file.resolvedCwd = parsed.payload?.cwd || '';
118
207
  }
119
208
  }
120
- }
121
- catch {
122
- // Skip unreadable files
209
+ } catch {
210
+ // Skip unreadable files
123
211
  }
124
212
  }
125
- return { sessions: files, contentCache };
213
+ return {
214
+ sessions: files,
215
+ contentCache
216
+ };
126
217
  }
127
218
  /**
128
219
  * Determine which date directories to scan based on process start times.
129
220
  * Returns only directories that actually exist.
130
- */
131
- getDateDirs(processes) {
221
+ */ getDateDirs(processes) {
132
222
  const dayKeys = new Set();
133
223
  const window = CodexAdapter.PROCESS_START_DAY_WINDOW_DAYS;
134
- for (const proc of processes) {
224
+ for (const proc of processes){
135
225
  const startTime = proc.startTime || new Date();
136
- for (let offset = -window; offset <= window; offset++) {
226
+ for(let offset = -window; offset <= window; offset++){
137
227
  const day = new Date(startTime.getTime());
138
228
  day.setDate(day.getDate() + offset);
139
229
  dayKeys.add(this.toSessionDayKey(day));
140
230
  }
141
231
  }
142
232
  const dirs = [];
143
- for (const dayKey of dayKeys) {
233
+ for (const dayKey of dayKeys){
144
234
  const dayDir = path.join(this.codexSessionsDir, dayKey);
145
235
  try {
146
236
  if (fs.statSync(dayDir).isDirectory()) {
147
237
  dirs.push(dayDir);
148
238
  }
149
- }
150
- catch {
239
+ } catch {
151
240
  continue;
152
241
  }
153
242
  }
@@ -162,56 +251,47 @@ export class CodexAdapter {
162
251
  /**
163
252
  * Parse session file content into CodexSession.
164
253
  * Uses cached content if available, otherwise reads from disk.
165
- */
166
- parseSession(cachedContent, filePath) {
254
+ */ parseSession(cachedContent, filePath) {
167
255
  let content;
168
256
  if (cachedContent !== undefined) {
169
257
  content = cachedContent;
170
- }
171
- else {
258
+ } else {
172
259
  try {
173
260
  content = fs.readFileSync(filePath, 'utf-8');
174
- }
175
- catch {
261
+ } catch {
176
262
  return null;
177
263
  }
178
264
  }
179
265
  const allLines = content.trim().split('\n');
180
- if (!allLines[0])
181
- return null;
266
+ if (!allLines[0]) return null;
182
267
  let metaEntry;
183
268
  try {
184
269
  metaEntry = JSON.parse(allLines[0]);
185
- }
186
- catch {
270
+ } catch {
187
271
  return null;
188
272
  }
189
273
  if (metaEntry.type !== 'session_meta' || !metaEntry.payload?.id) {
190
274
  return null;
191
275
  }
192
276
  const entries = [];
193
- for (const line of allLines) {
277
+ for (const line of allLines){
194
278
  try {
195
279
  entries.push(JSON.parse(line));
196
- }
197
- catch {
280
+ } catch {
198
281
  continue;
199
282
  }
200
283
  }
201
284
  const lastEntry = this.findLastEventEntry(entries);
202
285
  const lastPayloadType = lastEntry?.payload?.type;
203
- const lastActive = this.parseTimestamp(lastEntry?.timestamp) ||
204
- this.parseTimestamp(metaEntry.payload.timestamp) ||
205
- fs.statSync(filePath).mtime;
206
- const sessionStart = this.parseTimestamp(metaEntry.payload.timestamp) ||
207
- lastActive;
286
+ const lastActive = this.parseTimestamp(lastEntry?.timestamp) || this.parseTimestamp(metaEntry.payload.timestamp) || fs.statSync(filePath).mtime;
287
+ const sessionStart = this.parseTimestamp(metaEntry.payload.timestamp) || lastActive;
208
288
  return {
209
289
  sessionId: metaEntry.payload.id,
210
290
  projectPath: metaEntry.payload.cwd || '',
211
291
  summary: this.extractSummary(entries),
212
292
  sessionStart,
213
293
  lastActive,
214
- lastPayloadType,
294
+ lastPayloadType
215
295
  };
216
296
  }
217
297
  mapSessionToAgent(session, processInfo, filePath) {
@@ -224,7 +304,7 @@ export class CodexAdapter {
224
304
  projectPath: session.projectPath || processInfo.cwd || '',
225
305
  sessionId: session.sessionId,
226
306
  lastActive: session.lastActive,
227
- sessionFilePath: filePath,
307
+ sessionFilePath: filePath
228
308
  };
229
309
  }
230
310
  mapProcessOnlyAgent(processInfo) {
@@ -236,11 +316,11 @@ export class CodexAdapter {
236
316
  pid: processInfo.pid,
237
317
  projectPath: processInfo.cwd || '',
238
318
  sessionId: `pid-${processInfo.pid}`,
239
- lastActive: new Date(),
319
+ lastActive: new Date()
240
320
  };
241
321
  }
242
322
  findLastEventEntry(entries) {
243
- for (let i = entries.length - 1; i >= 0; i--) {
323
+ for(let i = entries.length - 1; i >= 0; i--){
244
324
  const entry = entries[i];
245
325
  if (entry && typeof entry.type === 'string') {
246
326
  return entry;
@@ -249,8 +329,7 @@ export class CodexAdapter {
249
329
  return undefined;
250
330
  }
251
331
  parseTimestamp(value) {
252
- if (!value)
253
- return null;
332
+ if (!value) return null;
254
333
  const timestamp = new Date(value);
255
334
  return Number.isNaN(timestamp.getTime()) ? null : timestamp;
256
335
  }
@@ -260,15 +339,13 @@ export class CodexAdapter {
260
339
  if (diffMinutes > CodexAdapter.IDLE_THRESHOLD_MINUTES) {
261
340
  return AgentStatus.IDLE;
262
341
  }
263
- if (session.lastPayloadType === 'agent_message' ||
264
- session.lastPayloadType === 'task_complete' ||
265
- session.lastPayloadType === 'turn_aborted') {
342
+ if (session.lastPayloadType === 'agent_message' || session.lastPayloadType === 'task_complete' || session.lastPayloadType === 'turn_aborted') {
266
343
  return AgentStatus.WAITING;
267
344
  }
268
345
  return AgentStatus.RUNNING;
269
346
  }
270
347
  extractSummary(entries) {
271
- for (let i = entries.length - 1; i >= 0; i--) {
348
+ for(let i = entries.length - 1; i >= 0; i--){
272
349
  const message = entries[i]?.payload?.message;
273
350
  if (typeof message === 'string' && message.trim().length > 0) {
274
351
  return this.truncate(message.trim(), 120);
@@ -277,8 +354,7 @@ export class CodexAdapter {
277
354
  return 'Codex session active';
278
355
  }
279
356
  truncate(value, maxLength) {
280
- if (value.length <= maxLength)
281
- return value;
357
+ if (value.length <= maxLength) return value;
282
358
  return `${value.slice(0, maxLength - 3)}...`;
283
359
  }
284
360
  isCodexExecutable(command) {
@@ -290,62 +366,50 @@ export class CodexAdapter {
290
366
  * Read the full conversation from a Codex session JSONL file.
291
367
  *
292
368
  * Codex entries use payload.type to indicate message role and payload.message for content.
293
- */
294
- getConversation(sessionFilePath, options) {
369
+ */ getConversation(sessionFilePath, options) {
295
370
  const verbose = options?.verbose ?? false;
296
371
  const content = safeReadFile(sessionFilePath);
297
- if (content === undefined)
298
- return [];
372
+ if (content === undefined) return [];
299
373
  const lines = content.trim().split('\n');
300
374
  const messages = [];
301
- for (const line of lines) {
375
+ for (const line of lines){
302
376
  let entry;
303
377
  try {
304
378
  entry = JSON.parse(line);
305
- }
306
- catch {
379
+ } catch {
307
380
  continue;
308
381
  }
309
- if (entry.type === 'session_meta')
310
- continue;
382
+ if (entry.type === 'session_meta') continue;
311
383
  const payloadType = entry.payload?.type;
312
- if (!payloadType)
313
- continue;
384
+ if (!payloadType) continue;
314
385
  let role;
315
386
  if (payloadType === 'user_message') {
316
387
  role = 'user';
317
- }
318
- else if (payloadType === 'agent_message' || payloadType === 'task_complete') {
388
+ } else if (payloadType === 'agent_message' || payloadType === 'task_complete') {
319
389
  role = 'assistant';
320
- }
321
- else if (verbose) {
390
+ } else if (verbose) {
322
391
  role = 'system';
323
- }
324
- else {
392
+ } else {
325
393
  continue;
326
394
  }
327
395
  const text = entry.payload?.message?.trim();
328
- if (!text)
329
- continue;
396
+ if (!text) continue;
330
397
  messages.push({
331
398
  role,
332
399
  content: text,
333
- timestamp: entry.timestamp,
400
+ timestamp: entry.timestamp
334
401
  });
335
402
  }
336
403
  return messages;
337
404
  }
338
405
  async listSessions(opts) {
339
- if (!isDirectory(this.codexSessionsDir))
340
- return [];
406
+ if (!isDirectory(this.codexSessionsDir)) return [];
341
407
  const files = this.collectAllSessionFiles();
342
408
  const summaries = [];
343
- for (const filePath of files) {
409
+ for (const filePath of files){
344
410
  const summary = this.fileToSessionSummary(filePath);
345
- if (!summary)
346
- continue;
347
- if (opts?.cwd !== undefined && summary.cwd !== opts.cwd)
348
- continue;
411
+ if (!summary) continue;
412
+ if (opts?.cwd !== undefined && summary.cwd !== opts.cwd) continue;
349
413
  summaries.push(summary);
350
414
  }
351
415
  return summaries;
@@ -354,24 +418,19 @@ export class CodexAdapter {
354
418
  * Walk every YYYY/MM/DD directory under `codexSessionsDir` and return
355
419
  * absolute paths of `.jsonl` files. Tolerates malformed layouts
356
420
  * (skips entries that aren't directories at the expected depth).
357
- */
358
- collectAllSessionFiles() {
421
+ */ collectAllSessionFiles() {
359
422
  const out = [];
360
- for (const yearEntry of safeReaddir(this.codexSessionsDir)) {
423
+ for (const yearEntry of safeReaddir(this.codexSessionsDir)){
361
424
  const yearDir = path.join(this.codexSessionsDir, yearEntry);
362
- if (!isDirectory(yearDir))
363
- continue;
364
- for (const monthEntry of safeReaddir(yearDir)) {
425
+ if (!isDirectory(yearDir)) continue;
426
+ for (const monthEntry of safeReaddir(yearDir)){
365
427
  const monthDir = path.join(yearDir, monthEntry);
366
- if (!isDirectory(monthDir))
367
- continue;
368
- for (const dayEntry of safeReaddir(monthDir)) {
428
+ if (!isDirectory(monthDir)) continue;
429
+ for (const dayEntry of safeReaddir(monthDir)){
369
430
  const dayDir = path.join(monthDir, dayEntry);
370
- if (!isDirectory(dayDir))
371
- continue;
372
- for (const fileEntry of safeReaddir(dayDir)) {
373
- if (!fileEntry.endsWith('.jsonl'))
374
- continue;
431
+ if (!isDirectory(dayDir)) continue;
432
+ for (const fileEntry of safeReaddir(dayDir)){
433
+ if (!fileEntry.endsWith('.jsonl')) continue;
375
434
  out.push(path.join(dayDir, fileEntry));
376
435
  }
377
436
  }
@@ -383,19 +442,15 @@ export class CodexAdapter {
383
442
  * Read a Codex session JSONL file and produce a {@link SessionSummary}.
384
443
  * Returns null when the file is unreadable, has no `session_meta`, or
385
444
  * lacks a session id.
386
- */
387
- fileToSessionSummary(filePath) {
445
+ */ fileToSessionSummary(filePath) {
388
446
  const content = safeReadFile(filePath);
389
- if (content === undefined)
390
- return null;
447
+ if (content === undefined) return null;
391
448
  const allLines = content.trim().split('\n');
392
- if (!allLines[0])
393
- return null;
449
+ if (!allLines[0]) return null;
394
450
  let metaEntry;
395
451
  try {
396
452
  metaEntry = JSON.parse(allLines[0]);
397
- }
398
- catch {
453
+ } catch {
399
454
  return null;
400
455
  }
401
456
  if (metaEntry.type !== 'session_meta' || !metaEntry.payload?.id) {
@@ -403,30 +458,21 @@ export class CodexAdapter {
403
458
  }
404
459
  let firstUserMessage = '';
405
460
  let lastTimestamp = null;
406
- for (let i = 1; i < allLines.length; i++) {
461
+ for(let i = 1; i < allLines.length; i++){
407
462
  let entry;
408
463
  try {
409
464
  entry = JSON.parse(allLines[i]);
410
- }
411
- catch {
465
+ } catch {
412
466
  continue;
413
467
  }
414
468
  const ts = this.parseTimestamp(entry.timestamp);
415
- if (ts)
416
- lastTimestamp = ts;
417
- if (!firstUserMessage &&
418
- entry.payload?.type === 'user_message' &&
419
- typeof entry.payload.message === 'string' &&
420
- entry.payload.message.trim().length > 0) {
469
+ if (ts) lastTimestamp = ts;
470
+ if (!firstUserMessage && entry.payload?.type === 'user_message' && typeof entry.payload.message === 'string' && entry.payload.message.trim().length > 0) {
421
471
  firstUserMessage = entry.payload.message.trim();
422
472
  }
423
473
  }
424
474
  const stat = safeStat(filePath);
425
- const startedAt = this.parseTimestamp(metaEntry.payload.timestamp) ||
426
- lastTimestamp ||
427
- stat?.birthtime ||
428
- stat?.mtime ||
429
- new Date();
475
+ const startedAt = this.parseTimestamp(metaEntry.payload.timestamp) || lastTimestamp || stat?.birthtime || stat?.mtime || new Date();
430
476
  const lastActive = lastTimestamp || startedAt;
431
477
  return {
432
478
  type: 'codex',
@@ -435,8 +481,9 @@ export class CodexAdapter {
435
481
  firstUserMessage,
436
482
  lastActive,
437
483
  startedAt,
438
- sessionFilePath: filePath,
484
+ sessionFilePath: filePath
439
485
  };
440
486
  }
441
487
  }
488
+
442
489
  //# sourceMappingURL=CodexAdapter.js.map