@ai-devkit/agent-manager 0.16.0 → 0.18.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.
- package/.swcrc +27 -0
- package/dist/AgentManager.js +72 -74
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +564 -0
- package/dist/__tests__/AgentManager.test.js.map +1 -0
- package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +2157 -0
- package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -0
- package/dist/__tests__/adapters/CodexAdapter.test.js +1419 -0
- package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -0
- package/dist/__tests__/adapters/CopilotAdapter.test.js +500 -0
- package/dist/__tests__/adapters/CopilotAdapter.test.js.map +1 -0
- package/dist/__tests__/adapters/GeminiCliAdapter.test.js +1229 -0
- package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +1 -0
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js +626 -0
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +1 -0
- package/dist/__tests__/adapters/PiAdapter.test.js +590 -0
- package/dist/__tests__/adapters/PiAdapter.test.js.map +1 -0
- package/dist/__tests__/terminal/TmuxManager.test.js +159 -0
- package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -0
- package/dist/__tests__/terminal/TtyWriter.test.js +162 -0
- package/dist/__tests__/terminal/TtyWriter.test.js.map +1 -0
- package/dist/__tests__/utils/AgentRegistry.test.js +282 -0
- package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -0
- package/dist/__tests__/utils/ClaudeSessionParser.test.js +312 -0
- package/dist/__tests__/utils/ClaudeSessionParser.test.js.map +1 -0
- package/dist/__tests__/utils/matching.test.js +324 -0
- package/dist/__tests__/utils/matching.test.js.map +1 -0
- package/dist/__tests__/utils/process.test.js +177 -0
- package/dist/__tests__/utils/process.test.js.map +1 -0
- package/dist/__tests__/utils/session.test.js +158 -0
- package/dist/__tests__/utils/session.test.js.map +1 -0
- package/dist/adapters/AgentAdapter.d.ts +1 -1
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/AgentAdapter.js +8 -7
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/adapters/ClaudeCodeAdapter.js +66 -77
- package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.d.ts +13 -0
- package/dist/adapters/CodexAdapter.d.ts.map +1 -1
- package/dist/adapters/CodexAdapter.js +225 -141
- package/dist/adapters/CodexAdapter.js.map +1 -1
- package/dist/adapters/CopilotAdapter.d.ts +44 -0
- package/dist/adapters/CopilotAdapter.d.ts.map +1 -0
- package/dist/adapters/CopilotAdapter.js +314 -0
- package/dist/adapters/CopilotAdapter.js.map +1 -0
- package/dist/adapters/GeminiCliAdapter.d.ts +2 -0
- package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -1
- package/dist/adapters/GeminiCliAdapter.js +138 -156
- package/dist/adapters/GeminiCliAdapter.js.map +1 -1
- package/dist/adapters/OpenCodeAdapter.js +60 -69
- package/dist/adapters/OpenCodeAdapter.js.map +1 -1
- package/dist/adapters/PiAdapter.d.ts +62 -0
- package/dist/adapters/PiAdapter.d.ts.map +1 -0
- package/dist/adapters/PiAdapter.js +450 -0
- package/dist/adapters/PiAdapter.js.map +1 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/terminal/TerminalFocusManager.js +62 -49
- package/dist/terminal/TerminalFocusManager.js.map +1 -1
- package/dist/terminal/TmuxManager.js +59 -34
- package/dist/terminal/TmuxManager.js.map +1 -1
- package/dist/terminal/TtyWriter.js +37 -16
- package/dist/terminal/TtyWriter.js.map +1 -1
- package/dist/terminal/index.js +1 -0
- package/dist/terminal/index.js.map +1 -1
- package/dist/utils/AgentRegistry.js +41 -32
- package/dist/utils/AgentRegistry.js.map +1 -1
- package/dist/utils/ClaudeSessionParser.js +46 -87
- package/dist/utils/ClaudeSessionParser.js.map +1 -1
- package/dist/utils/agents.d.ts +1 -1
- package/dist/utils/agents.d.ts.map +1 -1
- package/dist/utils/agents.js +22 -11
- package/dist/utils/agents.js.map +1 -1
- package/dist/utils/applescript.js +3 -6
- package/dist/utils/applescript.js.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/matching.js +21 -29
- package/dist/utils/matching.js.map +1 -1
- package/dist/utils/process.js +77 -56
- package/dist/utils/process.js.map +1 -1
- package/dist/utils/session.js +20 -34
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/sortAgents.js +6 -8
- package/dist/utils/sortAgents.js.map +1 -1
- package/package.json +11 -7
- package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +2 -0
- package/src/__tests__/adapters/CodexAdapter.test.ts +145 -0
- package/src/__tests__/adapters/CopilotAdapter.test.ts +411 -0
- package/src/__tests__/adapters/GeminiCliAdapter.test.ts +74 -1
- package/src/__tests__/adapters/PiAdapter.test.ts +435 -0
- package/src/adapters/AgentAdapter.ts +1 -1
- package/src/adapters/CodexAdapter.ts +164 -6
- package/src/adapters/CopilotAdapter.ts +453 -0
- package/src/adapters/GeminiCliAdapter.ts +42 -3
- package/src/adapters/PiAdapter.ts +597 -0
- package/src/adapters/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/utils/agents.ts +1 -1
package/dist/utils/sortAgents.js
CHANGED
|
@@ -3,7 +3,7 @@ const STATUS_PRIORITY = {
|
|
|
3
3
|
[AgentStatus.WAITING]: 0,
|
|
4
4
|
[AgentStatus.RUNNING]: 1,
|
|
5
5
|
[AgentStatus.IDLE]: 2,
|
|
6
|
-
[AgentStatus.UNKNOWN]: 3
|
|
6
|
+
[AgentStatus.UNKNOWN]: 3
|
|
7
7
|
};
|
|
8
8
|
function compareByName(a, b) {
|
|
9
9
|
return a.name.localeCompare(b.name);
|
|
@@ -11,25 +11,22 @@ function compareByName(a, b) {
|
|
|
11
11
|
function compareByStatus(a, b) {
|
|
12
12
|
const pa = STATUS_PRIORITY[a.status] ?? 999;
|
|
13
13
|
const pb = STATUS_PRIORITY[b.status] ?? 999;
|
|
14
|
-
if (pa !== pb)
|
|
15
|
-
return pa - pb;
|
|
14
|
+
if (pa !== pb) return pa - pb;
|
|
16
15
|
return compareByName(a, b);
|
|
17
16
|
}
|
|
18
17
|
function compareByPid(a, b) {
|
|
19
|
-
if (a.pid !== b.pid)
|
|
20
|
-
return a.pid - b.pid;
|
|
18
|
+
if (a.pid !== b.pid) return a.pid - b.pid;
|
|
21
19
|
return compareByName(a, b);
|
|
22
20
|
}
|
|
23
21
|
function compareByLastActive(a, b) {
|
|
24
22
|
const ta = a.lastActive instanceof Date ? a.lastActive.getTime() : new Date(a.lastActive).getTime();
|
|
25
23
|
const tb = b.lastActive instanceof Date ? b.lastActive.getTime() : new Date(b.lastActive).getTime();
|
|
26
|
-
if (tb !== ta)
|
|
27
|
-
return tb - ta; // most recent first
|
|
24
|
+
if (tb !== ta) return tb - ta; // most recent first
|
|
28
25
|
return compareByName(a, b);
|
|
29
26
|
}
|
|
30
27
|
export function sortAgents(agents, by) {
|
|
31
28
|
const copy = agents.slice();
|
|
32
|
-
switch
|
|
29
|
+
switch(by){
|
|
33
30
|
case 'name':
|
|
34
31
|
copy.sort(compareByName);
|
|
35
32
|
break;
|
|
@@ -45,4 +42,5 @@ export function sortAgents(agents, by) {
|
|
|
45
42
|
}
|
|
46
43
|
return copy;
|
|
47
44
|
}
|
|
45
|
+
|
|
48
46
|
//# sourceMappingURL=sortAgents.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../../src/utils/sortAgents.ts"],"sourcesContent":["import { AgentStatus, type AgentInfo } from '../adapters/AgentAdapter.js';\n\nexport type AgentSortKey = 'name' | 'status' | 'pid' | 'lastActive';\n\nconst STATUS_PRIORITY: Record<AgentStatus, number> = {\n [AgentStatus.WAITING]: 0,\n [AgentStatus.RUNNING]: 1,\n [AgentStatus.IDLE]: 2,\n [AgentStatus.UNKNOWN]: 3,\n};\n\nfunction compareByName(a: AgentInfo, b: AgentInfo): number {\n return a.name.localeCompare(b.name);\n}\n\nfunction compareByStatus(a: AgentInfo, b: AgentInfo): number {\n const pa = STATUS_PRIORITY[a.status] ?? 999;\n const pb = STATUS_PRIORITY[b.status] ?? 999;\n if (pa !== pb) return pa - pb;\n return compareByName(a, b);\n}\n\nfunction compareByPid(a: AgentInfo, b: AgentInfo): number {\n if (a.pid !== b.pid) return a.pid - b.pid;\n return compareByName(a, b);\n}\n\nfunction compareByLastActive(a: AgentInfo, b: AgentInfo): number {\n const ta = a.lastActive instanceof Date ? a.lastActive.getTime() : new Date(a.lastActive).getTime();\n const tb = b.lastActive instanceof Date ? b.lastActive.getTime() : new Date(b.lastActive).getTime();\n if (tb !== ta) return tb - ta; // most recent first\n return compareByName(a, b);\n}\n\nexport function sortAgents(agents: AgentInfo[], by: AgentSortKey): AgentInfo[] {\n const copy = agents.slice();\n switch (by) {\n case 'name':\n copy.sort(compareByName);\n break;\n case 'status':\n copy.sort(compareByStatus);\n break;\n case 'pid':\n copy.sort(compareByPid);\n break;\n case 'lastActive':\n copy.sort(compareByLastActive);\n break;\n }\n return copy;\n}\n"],"names":["AgentStatus","STATUS_PRIORITY","WAITING","RUNNING","IDLE","UNKNOWN","compareByName","a","b","name","localeCompare","compareByStatus","pa","status","pb","compareByPid","pid","compareByLastActive","ta","lastActive","Date","getTime","tb","sortAgents","agents","by","copy","slice","sort"],"mappings":"AAAA,SAASA,WAAW,QAAwB,8BAA8B;AAI1E,MAAMC,kBAA+C;IACjD,CAACD,YAAYE,OAAO,CAAC,EAAE;IACvB,CAACF,YAAYG,OAAO,CAAC,EAAE;IACvB,CAACH,YAAYI,IAAI,CAAC,EAAE;IACpB,CAACJ,YAAYK,OAAO,CAAC,EAAE;AAC3B;AAEA,SAASC,cAAcC,CAAY,EAAEC,CAAY;IAC7C,OAAOD,EAAEE,IAAI,CAACC,aAAa,CAACF,EAAEC,IAAI;AACtC;AAEA,SAASE,gBAAgBJ,CAAY,EAAEC,CAAY;IAC/C,MAAMI,KAAKX,eAAe,CAACM,EAAEM,MAAM,CAAC,IAAI;IACxC,MAAMC,KAAKb,eAAe,CAACO,EAAEK,MAAM,CAAC,IAAI;IACxC,IAAID,OAAOE,IAAI,OAAOF,KAAKE;IAC3B,OAAOR,cAAcC,GAAGC;AAC5B;AAEA,SAASO,aAAaR,CAAY,EAAEC,CAAY;IAC5C,IAAID,EAAES,GAAG,KAAKR,EAAEQ,GAAG,EAAE,OAAOT,EAAES,GAAG,GAAGR,EAAEQ,GAAG;IACzC,OAAOV,cAAcC,GAAGC;AAC5B;AAEA,SAASS,oBAAoBV,CAAY,EAAEC,CAAY;IACnD,MAAMU,KAAKX,EAAEY,UAAU,YAAYC,OAAOb,EAAEY,UAAU,CAACE,OAAO,KAAK,IAAID,KAAKb,EAAEY,UAAU,EAAEE,OAAO;IACjG,MAAMC,KAAKd,EAAEW,UAAU,YAAYC,OAAOZ,EAAEW,UAAU,CAACE,OAAO,KAAK,IAAID,KAAKZ,EAAEW,UAAU,EAAEE,OAAO;IACjG,IAAIC,OAAOJ,IAAI,OAAOI,KAAKJ,IAAI,oBAAoB;IACnD,OAAOZ,cAAcC,GAAGC;AAC5B;AAEA,OAAO,SAASe,WAAWC,MAAmB,EAAEC,EAAgB;IAC5D,MAAMC,OAAOF,OAAOG,KAAK;IACzB,OAAQF;QACJ,KAAK;YACDC,KAAKE,IAAI,CAACtB;YACV;QACJ,KAAK;YACDoB,KAAKE,IAAI,CAACjB;YACV;QACJ,KAAK;YACDe,KAAKE,IAAI,CAACb;YACV;QACJ,KAAK;YACDW,KAAKE,IAAI,CAACX;YACV;IACR;IACA,OAAOS;AACX"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-devkit/agent-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Standalone agent detection and management utilities for AI DevKit",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "tsc",
|
|
15
|
+
"build": "swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
|
|
16
|
+
"dev": "swc src -d dist --strip-leading-paths --watch",
|
|
16
17
|
"test": "vitest run",
|
|
17
18
|
"test:watch": "vitest",
|
|
18
19
|
"test:coverage": "vitest run --coverage",
|
|
@@ -29,17 +30,20 @@
|
|
|
29
30
|
"author": "",
|
|
30
31
|
"license": "MIT",
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"better-sqlite3": "^12.6.2"
|
|
33
|
+
"better-sqlite3": "^12.6.2",
|
|
34
|
+
"uuid": "14.0.0"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|
|
37
|
+
"@swc/cli": "^0.8.1",
|
|
38
|
+
"@swc/core": "^1.10.0",
|
|
35
39
|
"@types/better-sqlite3": "^7.6.11",
|
|
36
40
|
"@types/node": "^20.11.5",
|
|
37
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
38
|
-
"@typescript-eslint/parser": "^
|
|
39
|
-
"@vitest/coverage-v8": "^
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
|
42
|
+
"@typescript-eslint/parser": "^8.60.1",
|
|
43
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
40
44
|
"eslint": "^8.56.0",
|
|
41
45
|
"typescript": "^5.5.0",
|
|
42
|
-
"vitest": "^
|
|
46
|
+
"vitest": "^4.1.8"
|
|
43
47
|
},
|
|
44
48
|
"engines": {
|
|
45
49
|
"node": ">=20.20.0"
|
|
@@ -758,6 +758,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
758
758
|
|
|
759
759
|
beforeEach(() => {
|
|
760
760
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
|
|
761
|
+
(adapter as any).projectsDir = path.join(tmpDir, 'projects');
|
|
761
762
|
});
|
|
762
763
|
|
|
763
764
|
afterEach(() => {
|
|
@@ -1073,6 +1074,7 @@ describe('ClaudeCodeAdapter', () => {
|
|
|
1073
1074
|
|
|
1074
1075
|
beforeEach(() => {
|
|
1075
1076
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
|
|
1077
|
+
(adapter as any).projectsDir = path.join(tmpDir, 'projects');
|
|
1076
1078
|
});
|
|
1077
1079
|
|
|
1078
1080
|
afterEach(() => {
|
|
@@ -248,6 +248,151 @@ describe('CodexAdapter', () => {
|
|
|
248
248
|
|
|
249
249
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
250
250
|
});
|
|
251
|
+
|
|
252
|
+
it('should match resumed sessions by id even when the JSONL predates the process', async () => {
|
|
253
|
+
const sessionId = '019eabed-4079-7071-9531-b853ddd9914e';
|
|
254
|
+
const processes: ProcessInfo[] = [
|
|
255
|
+
{
|
|
256
|
+
pid: 88018,
|
|
257
|
+
command: `codex resume ${sessionId}`,
|
|
258
|
+
cwd: '/repo-a',
|
|
259
|
+
tty: 'ttys001',
|
|
260
|
+
startTime: new Date('2026-06-10T12:00:00.000Z'),
|
|
261
|
+
},
|
|
262
|
+
];
|
|
263
|
+
mockedListAgentProcesses.mockReturnValue(processes);
|
|
264
|
+
mockedEnrichProcesses.mockReturnValue(processes);
|
|
265
|
+
|
|
266
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-resume-'));
|
|
267
|
+
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
268
|
+
const dateDir = path.join(sessionsDir, '2026', '06', '09');
|
|
269
|
+
fs.mkdirSync(dateDir, { recursive: true });
|
|
270
|
+
|
|
271
|
+
const recentTs = new Date().toISOString();
|
|
272
|
+
const sessionFile = path.join(dateDir, `rollout-2026-06-09T12-28-33-${sessionId}.jsonl`);
|
|
273
|
+
fs.writeFileSync(sessionFile, [
|
|
274
|
+
JSON.stringify({ type: 'session_meta', payload: { id: sessionId, timestamp: recentTs, cwd: '/repo-a' } }),
|
|
275
|
+
JSON.stringify({ type: 'event', timestamp: recentTs, payload: { type: 'agent_message', message: 'resumed codex conversation' } }),
|
|
276
|
+
].join('\n'));
|
|
277
|
+
|
|
278
|
+
(adapter as any).codexSessionsDir = sessionsDir;
|
|
279
|
+
mockedBatchGetSessionFileBirthtimes.mockReturnValue([]);
|
|
280
|
+
const collectAllSpy = vi.spyOn(adapter as any, 'collectAllSessionFiles');
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const agents = await adapter.detectAgents();
|
|
284
|
+
|
|
285
|
+
expect(mockedBatchGetSessionFileBirthtimes).not.toHaveBeenCalled();
|
|
286
|
+
expect(mockedMatchProcessesToSessions).not.toHaveBeenCalled();
|
|
287
|
+
expect(collectAllSpy).not.toHaveBeenCalled();
|
|
288
|
+
expect(agents).toHaveLength(1);
|
|
289
|
+
expect(agents[0]).toMatchObject({
|
|
290
|
+
type: 'codex',
|
|
291
|
+
pid: 88018,
|
|
292
|
+
sessionId,
|
|
293
|
+
projectPath: '/repo-a',
|
|
294
|
+
sessionFilePath: sessionFile,
|
|
295
|
+
});
|
|
296
|
+
expect(agents[0].summary).toBe('resumed codex conversation');
|
|
297
|
+
} finally {
|
|
298
|
+
collectAllSpy.mockRestore();
|
|
299
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should fall back to all session files for non-time-sortable resume ids', async () => {
|
|
304
|
+
const sessionId = 'aaaaaaaa-bbbb-4ccc-dddd-eeeeeeeeeeee';
|
|
305
|
+
const processes: ProcessInfo[] = [
|
|
306
|
+
{
|
|
307
|
+
pid: 88020,
|
|
308
|
+
command: `codex resume ${sessionId}`,
|
|
309
|
+
cwd: '/repo-a',
|
|
310
|
+
tty: 'ttys001',
|
|
311
|
+
startTime: new Date('2026-06-10T12:00:00.000Z'),
|
|
312
|
+
},
|
|
313
|
+
];
|
|
314
|
+
mockedListAgentProcesses.mockReturnValue(processes);
|
|
315
|
+
mockedEnrichProcesses.mockReturnValue(processes);
|
|
316
|
+
|
|
317
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-resume-v4-'));
|
|
318
|
+
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
319
|
+
const dateDir = path.join(sessionsDir, '2026', '01', '02');
|
|
320
|
+
fs.mkdirSync(dateDir, { recursive: true });
|
|
321
|
+
|
|
322
|
+
const recentTs = new Date().toISOString();
|
|
323
|
+
const sessionFile = path.join(dateDir, `${sessionId}.jsonl`);
|
|
324
|
+
fs.writeFileSync(sessionFile, [
|
|
325
|
+
JSON.stringify({ type: 'session_meta', payload: { id: sessionId, timestamp: recentTs, cwd: '/repo-a' } }),
|
|
326
|
+
JSON.stringify({ type: 'event', timestamp: recentTs, payload: { type: 'agent_message', message: 'legacy id conversation' } }),
|
|
327
|
+
].join('\n'));
|
|
328
|
+
|
|
329
|
+
(adapter as any).codexSessionsDir = sessionsDir;
|
|
330
|
+
mockedBatchGetSessionFileBirthtimes.mockReturnValue([]);
|
|
331
|
+
const collectAllSpy = vi.spyOn(adapter as any, 'collectAllSessionFiles');
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
const agents = await adapter.detectAgents();
|
|
335
|
+
|
|
336
|
+
expect(collectAllSpy).toHaveBeenCalledOnce();
|
|
337
|
+
expect(agents).toHaveLength(1);
|
|
338
|
+
expect(agents[0]).toMatchObject({
|
|
339
|
+
pid: 88020,
|
|
340
|
+
sessionId,
|
|
341
|
+
sessionFilePath: sessionFile,
|
|
342
|
+
});
|
|
343
|
+
} finally {
|
|
344
|
+
collectAllSpy.mockRestore();
|
|
345
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('should fall back to process-only when a resumed session becomes unreadable after direct matching', async () => {
|
|
350
|
+
const sessionId = '019eabed-4079-7071-9531-b853ddd9914e';
|
|
351
|
+
const processes: ProcessInfo[] = [
|
|
352
|
+
{
|
|
353
|
+
pid: 88019,
|
|
354
|
+
command: `codex resume ${sessionId}`,
|
|
355
|
+
cwd: '/repo-a',
|
|
356
|
+
tty: 'ttys001',
|
|
357
|
+
startTime: new Date('2026-06-10T12:00:00.000Z'),
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
mockedListAgentProcesses.mockReturnValue(processes);
|
|
361
|
+
mockedEnrichProcesses.mockReturnValue(processes);
|
|
362
|
+
|
|
363
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-resume-gone-'));
|
|
364
|
+
const sessionsDir = path.join(tmpDir, 'sessions');
|
|
365
|
+
const dateDir = path.join(sessionsDir, '2026', '06', '09');
|
|
366
|
+
fs.mkdirSync(dateDir, { recursive: true });
|
|
367
|
+
|
|
368
|
+
const sessionFile = path.join(dateDir, `rollout-2026-06-09T12-28-33-${sessionId}.jsonl`);
|
|
369
|
+
|
|
370
|
+
(adapter as any).codexSessionsDir = sessionsDir;
|
|
371
|
+
const findSpy = vi.spyOn(adapter as any, 'findSessionFileById').mockReturnValue({
|
|
372
|
+
sessionId,
|
|
373
|
+
filePath: sessionFile,
|
|
374
|
+
projectDir: dateDir,
|
|
375
|
+
birthtimeMs: Date.now(),
|
|
376
|
+
resolvedCwd: '/repo-a',
|
|
377
|
+
});
|
|
378
|
+
mockedBatchGetSessionFileBirthtimes.mockReturnValue([]);
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
const agents = await adapter.detectAgents();
|
|
382
|
+
|
|
383
|
+
expect(agents).toHaveLength(1);
|
|
384
|
+
expect(agents[0]).toMatchObject({
|
|
385
|
+
type: 'codex',
|
|
386
|
+
pid: 88019,
|
|
387
|
+
sessionId: 'pid-88019',
|
|
388
|
+
projectPath: '/repo-a',
|
|
389
|
+
});
|
|
390
|
+
expect(agents[0].sessionFilePath).toBeUndefined();
|
|
391
|
+
} finally {
|
|
392
|
+
findSpy.mockRestore();
|
|
393
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
394
|
+
}
|
|
395
|
+
});
|
|
251
396
|
});
|
|
252
397
|
|
|
253
398
|
describe('detectAgents — registry cache short-circuit', () => {
|