@ai-devkit/agent-manager 0.33.0 → 0.33.1
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/AgentManager.d.ts +11 -2
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +37 -6
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +212 -5
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js +1 -0
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +1 -1
- package/dist/__tests__/{adapters → providers/copilot}/CopilotAdapter.test.js +19 -14
- package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -0
- package/dist/__tests__/{adapters → providers/gemini}/GeminiCliAdapter.test.js +37 -29
- package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -0
- package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js +164 -0
- package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js.map +1 -0
- package/dist/__tests__/runtime/HerdrAgentRuntime.test.js +37 -1
- package/dist/__tests__/runtime/HerdrAgentRuntime.test.js.map +1 -1
- package/dist/__tests__/utils/AgentRegistry.test.js +20 -13
- package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
- package/dist/adapters/OpenCodeAdapter.d.ts +1 -0
- package/dist/adapters/OpenCodeAdapter.d.ts.map +1 -1
- package/dist/adapters/OpenCodeAdapter.js +7 -4
- package/dist/adapters/OpenCodeAdapter.js.map +1 -1
- package/dist/adapters/index.d.ts +2 -2
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +2 -2
- package/dist/adapters/index.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/{adapters → providers/copilot}/CopilotAdapter.d.ts +11 -25
- package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -0
- package/dist/providers/copilot/CopilotAdapter.js +108 -0
- package/dist/providers/copilot/CopilotAdapter.js.map +1 -0
- package/dist/providers/copilot/CopilotAgentMapper.d.ts +9 -0
- package/dist/providers/copilot/CopilotAgentMapper.d.ts.map +1 -0
- package/dist/providers/copilot/CopilotAgentMapper.js +37 -0
- package/dist/providers/copilot/CopilotAgentMapper.js.map +1 -0
- package/dist/providers/copilot/CopilotSessionLocator.d.ts +19 -0
- package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -0
- package/dist/providers/copilot/CopilotSessionLocator.js +41 -0
- package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -0
- package/dist/providers/copilot/CopilotSessionParser.d.ts +30 -0
- package/dist/providers/copilot/CopilotSessionParser.d.ts.map +1 -0
- package/dist/providers/copilot/CopilotSessionParser.js +181 -0
- package/dist/providers/copilot/CopilotSessionParser.js.map +1 -0
- package/dist/providers/gemini/GeminiAgentMapper.d.ts +9 -0
- package/dist/providers/gemini/GeminiAgentMapper.d.ts.map +1 -0
- package/dist/providers/gemini/GeminiAgentMapper.js +37 -0
- package/dist/providers/gemini/GeminiAgentMapper.js.map +1 -0
- package/dist/providers/gemini/GeminiCliAdapter.d.ts +52 -0
- package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -0
- package/dist/providers/gemini/GeminiCliAdapter.js +188 -0
- package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -0
- package/dist/providers/gemini/GeminiSessionLocator.d.ts +30 -0
- package/dist/providers/gemini/GeminiSessionLocator.d.ts.map +1 -0
- package/dist/providers/gemini/GeminiSessionLocator.js +142 -0
- package/dist/providers/gemini/GeminiSessionLocator.js.map +1 -0
- package/dist/providers/gemini/GeminiSessionParser.d.ts +94 -0
- package/dist/providers/gemini/GeminiSessionParser.d.ts.map +1 -0
- package/dist/providers/gemini/GeminiSessionParser.js +166 -0
- package/dist/providers/gemini/GeminiSessionParser.js.map +1 -0
- package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts +18 -0
- package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -0
- package/dist/runtime/herdr/HerdrAgentDiscovery.js +183 -0
- package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -0
- package/dist/runtime/herdr/HerdrAgentRuntime.d.ts +5 -0
- package/dist/runtime/herdr/HerdrAgentRuntime.d.ts.map +1 -1
- package/dist/runtime/herdr/HerdrAgentRuntime.js +26 -8
- package/dist/runtime/herdr/HerdrAgentRuntime.js.map +1 -1
- package/dist/runtime/herdr/HerdrResponseParsers.d.ts +2 -0
- package/dist/runtime/herdr/HerdrResponseParsers.d.ts.map +1 -1
- package/dist/runtime/herdr/HerdrResponseParsers.js +12 -1
- package/dist/runtime/herdr/HerdrResponseParsers.js.map +1 -1
- package/dist/utils/AgentRegistry.d.ts.map +1 -1
- package/dist/utils/AgentRegistry.js +5 -5
- package/dist/utils/AgentRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +67 -5
- package/src/__tests__/AgentManager.test.ts +231 -5
- package/src/__tests__/adapters/OpenCodeAdapter.test.ts +1 -0
- package/src/__tests__/{adapters → providers/copilot}/CopilotAdapter.test.ts +13 -16
- package/src/__tests__/{adapters → providers/gemini}/GeminiCliAdapter.test.ts +39 -31
- package/src/__tests__/runtime/HerdrAgentDiscovery.test.ts +131 -0
- package/src/__tests__/runtime/HerdrAgentRuntime.test.ts +20 -1
- package/src/__tests__/utils/AgentRegistry.test.ts +20 -10
- package/src/adapters/OpenCodeAdapter.ts +8 -4
- package/src/adapters/index.ts +2 -2
- package/src/index.ts +10 -3
- package/src/providers/copilot/CopilotAdapter.ts +145 -0
- package/src/providers/copilot/CopilotAgentMapper.ts +36 -0
- package/src/providers/copilot/CopilotSessionLocator.ts +57 -0
- package/src/providers/copilot/CopilotSessionParser.ts +289 -0
- package/src/providers/gemini/GeminiAgentMapper.ts +36 -0
- package/src/providers/gemini/GeminiCliAdapter.ts +266 -0
- package/src/providers/gemini/GeminiSessionLocator.ts +183 -0
- package/src/providers/gemini/GeminiSessionParser.ts +271 -0
- package/src/runtime/herdr/HerdrAgentDiscovery.ts +178 -0
- package/src/runtime/herdr/HerdrAgentRuntime.ts +33 -7
- package/src/runtime/herdr/HerdrResponseParsers.ts +11 -1
- package/src/utils/AgentRegistry.ts +8 -6
- package/dist/__tests__/adapters/CopilotAdapter.test.js.map +0 -1
- package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +0 -1
- package/dist/adapters/CopilotAdapter.d.ts.map +0 -1
- package/dist/adapters/CopilotAdapter.js +0 -329
- package/dist/adapters/CopilotAdapter.js.map +0 -1
- package/dist/adapters/GeminiCliAdapter.d.ts +0 -98
- package/dist/adapters/GeminiCliAdapter.d.ts.map +0 -1
- package/dist/adapters/GeminiCliAdapter.js +0 -513
- package/dist/adapters/GeminiCliAdapter.js.map +0 -1
- package/src/adapters/CopilotAdapter.ts +0 -483
- package/src/adapters/GeminiCliAdapter.ts +0 -699
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
} from "../adapters/AgentAdapter.js";
|
|
17
17
|
import { AgentStatus } from "../adapters/AgentAdapter.js";
|
|
18
18
|
import { AgentRegistry, type RegistryEntry } from "../utils/AgentRegistry.js";
|
|
19
|
+
import type { HerdrAgentPane } from "../runtime/herdr/HerdrAgentDiscovery.js";
|
|
19
20
|
|
|
20
21
|
// Mock adapter for testing
|
|
21
22
|
class MockAdapter implements AgentAdapter {
|
|
@@ -360,6 +361,15 @@ describe("AgentManager", () => {
|
|
|
360
361
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
361
362
|
});
|
|
362
363
|
|
|
364
|
+
function useHerdrPanes(panes: readonly HerdrAgentPane[]): ReturnType<typeof vi.fn> {
|
|
365
|
+
const fetchHerdrPanes = vi.fn().mockResolvedValue(panes);
|
|
366
|
+
scopedManager = new AgentManager(registry, undefined, {
|
|
367
|
+
runtimeProvider: "herdr",
|
|
368
|
+
fetchHerdrAgentPanes: fetchHerdrPanes,
|
|
369
|
+
});
|
|
370
|
+
return fetchHerdrPanes;
|
|
371
|
+
}
|
|
372
|
+
|
|
363
373
|
it("persists every detected agent to the registry", async () => {
|
|
364
374
|
scopedManager.registerAdapter(
|
|
365
375
|
new MockAdapter("claude", [
|
|
@@ -390,6 +400,223 @@ describe("AgentManager", () => {
|
|
|
390
400
|
expect(entries[0].startedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
391
401
|
});
|
|
392
402
|
|
|
403
|
+
it("persists a Herdr runtime ref for manually discovered agents matched by session id", async () => {
|
|
404
|
+
useHerdrPanes([
|
|
405
|
+
{
|
|
406
|
+
agent: "codex",
|
|
407
|
+
agentSessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
|
|
408
|
+
cwd: "/cwd/ai-devkit",
|
|
409
|
+
paneId: "w31:p1",
|
|
410
|
+
workspaceId: "w31",
|
|
411
|
+
tabId: "w31:t1",
|
|
412
|
+
},
|
|
413
|
+
]);
|
|
414
|
+
scopedManager.registerAdapter(
|
|
415
|
+
new MockAdapter("codex", [
|
|
416
|
+
createMockAgent({
|
|
417
|
+
name: "ai-devkit-44290",
|
|
418
|
+
type: "codex",
|
|
419
|
+
pid: process.pid,
|
|
420
|
+
sessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
|
|
421
|
+
sessionFilePath: "/path/codex.jsonl",
|
|
422
|
+
projectPath: "/cwd/ai-devkit",
|
|
423
|
+
}),
|
|
424
|
+
]),
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
await scopedManager.listAgents();
|
|
428
|
+
|
|
429
|
+
expect(registry.list()[0]).toMatchObject({
|
|
430
|
+
name: "ai-devkit-44290",
|
|
431
|
+
type: "codex",
|
|
432
|
+
pid: process.pid,
|
|
433
|
+
runtime: "herdr",
|
|
434
|
+
runtimeRef: {
|
|
435
|
+
session: "default",
|
|
436
|
+
paneId: "w31:p1",
|
|
437
|
+
workspaceId: "w31",
|
|
438
|
+
tabId: "w31:t1",
|
|
439
|
+
agentName: "ai-devkit-44290",
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it("upgrades an existing tmux fallback row when Herdr later matches by session id", async () => {
|
|
445
|
+
useHerdrPanes([
|
|
446
|
+
{
|
|
447
|
+
agent: "codex",
|
|
448
|
+
agentSessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
|
|
449
|
+
cwd: "/cwd/ai-devkit",
|
|
450
|
+
paneId: "w31:p1",
|
|
451
|
+
},
|
|
452
|
+
]);
|
|
453
|
+
registry.register({
|
|
454
|
+
name: "ai-devkit-44290",
|
|
455
|
+
type: "codex",
|
|
456
|
+
pid: process.pid,
|
|
457
|
+
runtime: "tmux",
|
|
458
|
+
runtimeRef: null,
|
|
459
|
+
cwd: "/cwd/ai-devkit",
|
|
460
|
+
startedAt: "2026-09-14T00:00:00.000Z",
|
|
461
|
+
sessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
|
|
462
|
+
sessionFilePath: "/path/codex.jsonl",
|
|
463
|
+
});
|
|
464
|
+
scopedManager.registerAdapter(
|
|
465
|
+
new MockAdapter("codex", [
|
|
466
|
+
createMockAgent({
|
|
467
|
+
name: "ai-devkit-44290",
|
|
468
|
+
type: "codex",
|
|
469
|
+
pid: process.pid,
|
|
470
|
+
sessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
|
|
471
|
+
sessionFilePath: "/path/codex.jsonl",
|
|
472
|
+
projectPath: "/cwd/ai-devkit",
|
|
473
|
+
}),
|
|
474
|
+
]),
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
await scopedManager.listAgents();
|
|
478
|
+
|
|
479
|
+
expect(registry.lookup("ai-devkit-44290")).toMatchObject({
|
|
480
|
+
runtime: "herdr",
|
|
481
|
+
runtimeRef: {
|
|
482
|
+
session: "default",
|
|
483
|
+
paneId: "w31:p1",
|
|
484
|
+
agentName: "ai-devkit-44290",
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it("upgrades an existing tmux fallback row when Herdr matches by foreground pid", async () => {
|
|
490
|
+
useHerdrPanes([
|
|
491
|
+
{
|
|
492
|
+
agent: "codex",
|
|
493
|
+
cwd: "/cwd/ai-devkit",
|
|
494
|
+
paneId: "w31:p1",
|
|
495
|
+
foregroundPids: [44290],
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
agent: "codex",
|
|
499
|
+
cwd: "/cwd/ai-devkit",
|
|
500
|
+
paneId: "w32:p1",
|
|
501
|
+
foregroundPids: [process.pid],
|
|
502
|
+
},
|
|
503
|
+
]);
|
|
504
|
+
registry.register({
|
|
505
|
+
name: "ai-devkit-64904",
|
|
506
|
+
type: "codex",
|
|
507
|
+
pid: process.pid,
|
|
508
|
+
runtime: "tmux",
|
|
509
|
+
runtimeRef: null,
|
|
510
|
+
cwd: "/cwd/ai-devkit",
|
|
511
|
+
startedAt: "2026-09-14T00:00:00.000Z",
|
|
512
|
+
sessionId: `pid-${process.pid}`,
|
|
513
|
+
sessionFilePath: "",
|
|
514
|
+
});
|
|
515
|
+
scopedManager.registerAdapter(
|
|
516
|
+
new MockAdapter("codex", [
|
|
517
|
+
createMockAgent({
|
|
518
|
+
name: "ai-devkit-64904",
|
|
519
|
+
type: "codex",
|
|
520
|
+
pid: process.pid,
|
|
521
|
+
sessionId: `pid-${process.pid}`,
|
|
522
|
+
sessionFilePath: undefined,
|
|
523
|
+
projectPath: "/cwd/ai-devkit",
|
|
524
|
+
}),
|
|
525
|
+
]),
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
await scopedManager.listAgents();
|
|
529
|
+
|
|
530
|
+
expect(registry.lookup("ai-devkit-64904")).toMatchObject({
|
|
531
|
+
runtime: "herdr",
|
|
532
|
+
runtimeRef: {
|
|
533
|
+
session: "default",
|
|
534
|
+
paneId: "w32:p1",
|
|
535
|
+
agentName: "ai-devkit-64904",
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it("fetches Herdr panes automatically when the configured runtime is Herdr", async () => {
|
|
541
|
+
const fetchHerdrPanes = useHerdrPanes([
|
|
542
|
+
{
|
|
543
|
+
agent: "codex",
|
|
544
|
+
cwd: "/cwd/ai-devkit",
|
|
545
|
+
paneId: "w32:p1",
|
|
546
|
+
foregroundPids: [process.pid],
|
|
547
|
+
},
|
|
548
|
+
]);
|
|
549
|
+
scopedManager.registerAdapter(
|
|
550
|
+
new MockAdapter("codex", [
|
|
551
|
+
createMockAgent({
|
|
552
|
+
name: "ai-devkit-64904",
|
|
553
|
+
type: "codex",
|
|
554
|
+
pid: process.pid,
|
|
555
|
+
sessionId: `pid-${process.pid}`,
|
|
556
|
+
sessionFilePath: undefined,
|
|
557
|
+
projectPath: "/cwd/ai-devkit",
|
|
558
|
+
}),
|
|
559
|
+
]),
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
await scopedManager.listAgents();
|
|
563
|
+
|
|
564
|
+
expect(fetchHerdrPanes).toHaveBeenCalledOnce();
|
|
565
|
+
expect(registry.lookup("ai-devkit-64904")).toMatchObject({
|
|
566
|
+
runtime: "herdr",
|
|
567
|
+
runtimeRef: {
|
|
568
|
+
session: "default",
|
|
569
|
+
paneId: "w32:p1",
|
|
570
|
+
agentName: "ai-devkit-64904",
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it("upgrades an existing tmux row with a malformed runtime ref when Herdr matches", async () => {
|
|
576
|
+
useHerdrPanes([
|
|
577
|
+
{
|
|
578
|
+
agent: "codex",
|
|
579
|
+
cwd: "/cwd/ai-devkit",
|
|
580
|
+
paneId: "w32:p1",
|
|
581
|
+
foregroundPids: [process.pid],
|
|
582
|
+
},
|
|
583
|
+
]);
|
|
584
|
+
registry.register({
|
|
585
|
+
name: "ai-devkit-64904",
|
|
586
|
+
type: "codex",
|
|
587
|
+
pid: process.pid,
|
|
588
|
+
runtime: "tmux",
|
|
589
|
+
runtimeRef: {},
|
|
590
|
+
cwd: "/cwd/ai-devkit",
|
|
591
|
+
startedAt: "2026-09-14T00:00:00.000Z",
|
|
592
|
+
sessionId: `pid-${process.pid}`,
|
|
593
|
+
sessionFilePath: "",
|
|
594
|
+
});
|
|
595
|
+
scopedManager.registerAdapter(
|
|
596
|
+
new MockAdapter("codex", [
|
|
597
|
+
createMockAgent({
|
|
598
|
+
name: "ai-devkit-64904",
|
|
599
|
+
type: "codex",
|
|
600
|
+
pid: process.pid,
|
|
601
|
+
sessionId: `pid-${process.pid}`,
|
|
602
|
+
sessionFilePath: undefined,
|
|
603
|
+
projectPath: "/cwd/ai-devkit",
|
|
604
|
+
}),
|
|
605
|
+
]),
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
await scopedManager.listAgents();
|
|
609
|
+
|
|
610
|
+
expect(registry.lookup("ai-devkit-64904")).toMatchObject({
|
|
611
|
+
runtime: "herdr",
|
|
612
|
+
runtimeRef: {
|
|
613
|
+
session: "default",
|
|
614
|
+
paneId: "w32:p1",
|
|
615
|
+
agentName: "ai-devkit-64904",
|
|
616
|
+
},
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
|
|
393
620
|
it("prunes entries for dead pids", async () => {
|
|
394
621
|
registry.register({
|
|
395
622
|
name: "dead",
|
|
@@ -620,7 +847,7 @@ describe("AgentManager", () => {
|
|
|
620
847
|
expect(agents[0].lastActive.toISOString()).toBe("2026-01-01T00:00:00.000Z");
|
|
621
848
|
});
|
|
622
849
|
|
|
623
|
-
it("
|
|
850
|
+
it("does not persist passive cwd changes when an existing cwd is present", async () => {
|
|
624
851
|
const adapter = new MockAdapter("claude", [
|
|
625
852
|
createMockAgent({ name: "changing", pid: process.pid, projectPath: "/cwd/before" }),
|
|
626
853
|
]);
|
|
@@ -636,10 +863,9 @@ describe("AgentManager", () => {
|
|
|
636
863
|
|
|
637
864
|
const upserts = databaseOperations.filter((sql) => /^\s*INSERT INTO agents/i.test(sql));
|
|
638
865
|
const transactions = databaseOperations.filter((sql) => /^\s*(BEGIN|COMMIT)/i.test(sql));
|
|
639
|
-
expect(upserts).toHaveLength(
|
|
640
|
-
expect(
|
|
641
|
-
expect(
|
|
642
|
-
expect(registry.lookup("changing")?.cwd).toBe("/cwd/after");
|
|
866
|
+
expect(upserts).toHaveLength(0);
|
|
867
|
+
expect(transactions).toHaveLength(0);
|
|
868
|
+
expect(registry.lookup("changing")?.cwd).toBe("/cwd/before");
|
|
643
869
|
});
|
|
644
870
|
|
|
645
871
|
it("prunes newly dead entries only when the passive cadence is due", async () => {
|
|
@@ -7,19 +7,19 @@ import * as fs from "fs";
|
|
|
7
7
|
import * as os from "os";
|
|
8
8
|
import * as path from "path";
|
|
9
9
|
|
|
10
|
-
import { CopilotAdapter } from "
|
|
11
|
-
import type { ProcessInfo } from "
|
|
12
|
-
import { AgentStatus } from "
|
|
10
|
+
import { CopilotAdapter } from "../../../providers/copilot/CopilotAdapter.js";
|
|
11
|
+
import type { ProcessInfo } from "../../../adapters/AgentAdapter.js";
|
|
12
|
+
import { AgentStatus } from "../../../adapters/AgentAdapter.js";
|
|
13
13
|
import {
|
|
14
14
|
listAgentProcesses,
|
|
15
15
|
enrichProcesses,
|
|
16
16
|
captureProcessSnapshot,
|
|
17
|
-
} from "
|
|
18
|
-
import { generateAgentName } from "
|
|
19
|
-
import { AgentRegistry } from "
|
|
17
|
+
} from "../../../utils/process.js";
|
|
18
|
+
import { generateAgentName } from "../../../utils/matching.js";
|
|
19
|
+
import { AgentRegistry } from "../../../utils/AgentRegistry.js";
|
|
20
20
|
|
|
21
|
-
vi.mock("
|
|
22
|
-
const actual = (await importOriginal()) as typeof import("
|
|
21
|
+
vi.mock("../../../utils/process.js", async (importOriginal) => {
|
|
22
|
+
const actual = (await importOriginal()) as typeof import("../../../utils/process.js");
|
|
23
23
|
return {
|
|
24
24
|
...actual,
|
|
25
25
|
listAgentProcesses: vi.fn(),
|
|
@@ -28,7 +28,7 @@ vi.mock("../../utils/process.js", async (importOriginal) => {
|
|
|
28
28
|
};
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
vi.mock("
|
|
31
|
+
vi.mock("../../../utils/matching.js", () => ({
|
|
32
32
|
generateAgentName: vi.fn(),
|
|
33
33
|
}));
|
|
34
34
|
|
|
@@ -45,11 +45,10 @@ describe("CopilotAdapter", () => {
|
|
|
45
45
|
let sessionStateDir: string;
|
|
46
46
|
|
|
47
47
|
beforeEach(() => {
|
|
48
|
-
adapter = new CopilotAdapter();
|
|
49
48
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "copilot-test-"));
|
|
50
49
|
sessionStateDir = path.join(tmpDir, "session-state");
|
|
51
50
|
fs.mkdirSync(sessionStateDir, { recursive: true });
|
|
52
|
-
|
|
51
|
+
adapter = new CopilotAdapter(undefined, { sessionStateDir });
|
|
53
52
|
|
|
54
53
|
mockedListAgentProcesses.mockReset();
|
|
55
54
|
mockedEnrichProcesses.mockReset();
|
|
@@ -296,8 +295,7 @@ describe("CopilotAdapter", () => {
|
|
|
296
295
|
|
|
297
296
|
it("carries the managed wrapper name to a process-only child before the session lock exists", async () => {
|
|
298
297
|
const registry = new AgentRegistry(path.join(tmpDir, "agents.json"));
|
|
299
|
-
adapter = new CopilotAdapter(registry);
|
|
300
|
-
(adapter as any).sessionStateDir = sessionStateDir;
|
|
298
|
+
adapter = new CopilotAdapter(registry, { sessionStateDir });
|
|
301
299
|
const processes: ProcessInfo[] = [
|
|
302
300
|
{ pid: 86800, command: "copilot", cwd: "/repo", tty: "ttys001", ppid: 84174 },
|
|
303
301
|
{
|
|
@@ -363,8 +361,7 @@ describe("CopilotAdapter", () => {
|
|
|
363
361
|
|
|
364
362
|
it("carries the managed wrapper name to a lock-backed child process", async () => {
|
|
365
363
|
const registry = new AgentRegistry(path.join(tmpDir, "agents.json"));
|
|
366
|
-
adapter = new CopilotAdapter(registry);
|
|
367
|
-
(adapter as any).sessionStateDir = sessionStateDir;
|
|
364
|
+
adapter = new CopilotAdapter(registry, { sessionStateDir });
|
|
368
365
|
const processes: ProcessInfo[] = [
|
|
369
366
|
{ pid: 14095, command: "copilot", cwd: "/repo", tty: "ttys001", ppid: 84174 },
|
|
370
367
|
{
|
|
@@ -597,7 +594,7 @@ describe("CopilotAdapter", () => {
|
|
|
597
594
|
});
|
|
598
595
|
|
|
599
596
|
it("returns empty when session-state directory does not exist", async () => {
|
|
600
|
-
|
|
597
|
+
adapter = new CopilotAdapter(undefined, { sessionStateDir: path.join(tmpDir, "missing") });
|
|
601
598
|
|
|
602
599
|
await expect(adapter.listSessions()).resolves.toEqual([]);
|
|
603
600
|
});
|
|
@@ -7,20 +7,22 @@ import * as fs from "fs";
|
|
|
7
7
|
import * as os from "os";
|
|
8
8
|
import * as path from "path";
|
|
9
9
|
|
|
10
|
-
import { GeminiCliAdapter } from "
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
10
|
+
import { GeminiCliAdapter } from "../../../providers/gemini/GeminiCliAdapter.js";
|
|
11
|
+
import { GeminiSessionLocator } from "../../../providers/gemini/GeminiSessionLocator.js";
|
|
12
|
+
import { GeminiSessionParser } from "../../../providers/gemini/GeminiSessionParser.js";
|
|
13
|
+
import type { ProcessInfo } from "../../../adapters/AgentAdapter.js";
|
|
14
|
+
import { AgentStatus } from "../../../adapters/AgentAdapter.js";
|
|
15
|
+
import { AgentRegistry, type RegistryEntry } from "../../../utils/AgentRegistry.js";
|
|
14
16
|
import {
|
|
15
17
|
listAgentProcesses,
|
|
16
18
|
enrichProcesses,
|
|
17
19
|
captureProcessSnapshot,
|
|
18
|
-
} from "
|
|
19
|
-
import { matchProcessesToSessions, generateAgentName } from "
|
|
20
|
+
} from "../../../utils/process.js";
|
|
21
|
+
import { matchProcessesToSessions, generateAgentName } from "../../../utils/matching.js";
|
|
20
22
|
import * as crypto from "crypto";
|
|
21
23
|
|
|
22
|
-
vi.mock("
|
|
23
|
-
const actual = (await importOriginal()) as typeof import("
|
|
24
|
+
vi.mock("../../../utils/process.js", async (importOriginal) => {
|
|
25
|
+
const actual = (await importOriginal()) as typeof import("../../../utils/process.js");
|
|
24
26
|
return {
|
|
25
27
|
...actual,
|
|
26
28
|
listAgentProcesses: vi.fn(),
|
|
@@ -29,7 +31,7 @@ vi.mock("../../utils/process.js", async (importOriginal) => {
|
|
|
29
31
|
};
|
|
30
32
|
});
|
|
31
33
|
|
|
32
|
-
vi.mock("
|
|
34
|
+
vi.mock("../../../utils/matching.js", () => ({
|
|
33
35
|
matchProcessesToSessions: vi.fn(),
|
|
34
36
|
generateAgentName: vi.fn(),
|
|
35
37
|
}));
|
|
@@ -47,12 +49,18 @@ const mockedGenerateAgentName = generateAgentName as MockedFunction<typeof gener
|
|
|
47
49
|
describe("GeminiCliAdapter", () => {
|
|
48
50
|
let adapter: GeminiCliAdapter;
|
|
49
51
|
let tmpHome: string;
|
|
52
|
+
let parser: GeminiSessionParser;
|
|
53
|
+
let locator: GeminiSessionLocator;
|
|
50
54
|
|
|
51
55
|
beforeEach(() => {
|
|
52
56
|
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "gemini-adapter-test-"));
|
|
53
57
|
process.env.HOME = tmpHome;
|
|
54
58
|
|
|
55
59
|
adapter = new GeminiCliAdapter(new AgentRegistry(path.join(tmpHome, "agents.json")));
|
|
60
|
+
parser = new GeminiSessionParser();
|
|
61
|
+
locator = new GeminiSessionLocator({
|
|
62
|
+
geminiTmpDir: path.join(tmpHome, ".gemini", "tmp"),
|
|
63
|
+
});
|
|
56
64
|
mockedListAgentProcesses.mockReset();
|
|
57
65
|
mockedEnrichProcesses.mockReset();
|
|
58
66
|
mockedCaptureProcessSnapshot.mockReset();
|
|
@@ -621,7 +629,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
621
629
|
startTime: new Date(),
|
|
622
630
|
};
|
|
623
631
|
// tmp dir absent by default
|
|
624
|
-
const result =
|
|
632
|
+
const result = locator.discoverSessions([proc]);
|
|
625
633
|
expect(result.sessions).toEqual([]);
|
|
626
634
|
expect(result.contentCache.size).toBe(0);
|
|
627
635
|
});
|
|
@@ -643,7 +651,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
643
651
|
messages: [],
|
|
644
652
|
});
|
|
645
653
|
|
|
646
|
-
const result =
|
|
654
|
+
const result = locator.discoverSessions([proc]);
|
|
647
655
|
expect(result.sessions).toEqual([]);
|
|
648
656
|
});
|
|
649
657
|
|
|
@@ -664,7 +672,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
664
672
|
messages: [],
|
|
665
673
|
});
|
|
666
674
|
|
|
667
|
-
const result =
|
|
675
|
+
const result = locator.discoverSessions([proc]);
|
|
668
676
|
expect(result.sessions).toEqual([]);
|
|
669
677
|
});
|
|
670
678
|
|
|
@@ -690,7 +698,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
690
698
|
messages: [],
|
|
691
699
|
});
|
|
692
700
|
|
|
693
|
-
const result =
|
|
701
|
+
const result = locator.discoverSessions([proc]);
|
|
694
702
|
expect(result.sessions).toHaveLength(1);
|
|
695
703
|
expect(result.sessions[0].sessionId).toBe("s-good");
|
|
696
704
|
});
|
|
@@ -716,7 +724,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
716
724
|
messages: [],
|
|
717
725
|
});
|
|
718
726
|
|
|
719
|
-
const result =
|
|
727
|
+
const result = locator.discoverSessions([proc]);
|
|
720
728
|
expect(result.sessions).toHaveLength(1);
|
|
721
729
|
expect(result.sessions[0].resolvedCwd).toBe(procCwd);
|
|
722
730
|
});
|
|
@@ -742,12 +750,12 @@ describe("GeminiCliAdapter", () => {
|
|
|
742
750
|
}),
|
|
743
751
|
);
|
|
744
752
|
|
|
745
|
-
const result =
|
|
753
|
+
const result = locator.discoverSessions([proc]);
|
|
746
754
|
expect(result.sessions).toEqual([]);
|
|
747
755
|
});
|
|
748
756
|
});
|
|
749
757
|
|
|
750
|
-
describe("
|
|
758
|
+
describe("GeminiSessionParser", () => {
|
|
751
759
|
describe("determineStatus", () => {
|
|
752
760
|
it('should return "waiting" when the last message is from gemini', () => {
|
|
753
761
|
const session = {
|
|
@@ -758,7 +766,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
758
766
|
lastActive: new Date(),
|
|
759
767
|
lastMessageType: "gemini",
|
|
760
768
|
};
|
|
761
|
-
expect(
|
|
769
|
+
expect(parser.determineStatus(session)).toBe(AgentStatus.WAITING);
|
|
762
770
|
});
|
|
763
771
|
|
|
764
772
|
it('should return "waiting" when the last message is from assistant', () => {
|
|
@@ -770,7 +778,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
770
778
|
lastActive: new Date(),
|
|
771
779
|
lastMessageType: "assistant",
|
|
772
780
|
};
|
|
773
|
-
expect(
|
|
781
|
+
expect(parser.determineStatus(session)).toBe(AgentStatus.WAITING);
|
|
774
782
|
});
|
|
775
783
|
|
|
776
784
|
it('should return "running" when the last message is from the user', () => {
|
|
@@ -782,7 +790,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
782
790
|
lastActive: new Date(),
|
|
783
791
|
lastMessageType: "user",
|
|
784
792
|
};
|
|
785
|
-
expect(
|
|
793
|
+
expect(parser.determineStatus(session)).toBe(AgentStatus.RUNNING);
|
|
786
794
|
});
|
|
787
795
|
|
|
788
796
|
it('should return "idle" when last activity is older than the threshold', () => {
|
|
@@ -794,7 +802,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
794
802
|
lastActive: new Date(Date.now() - 10 * 60 * 1000),
|
|
795
803
|
lastMessageType: "gemini",
|
|
796
804
|
};
|
|
797
|
-
expect(
|
|
805
|
+
expect(parser.determineStatus(session)).toBe(AgentStatus.IDLE);
|
|
798
806
|
});
|
|
799
807
|
});
|
|
800
808
|
|
|
@@ -812,7 +820,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
812
820
|
],
|
|
813
821
|
});
|
|
814
822
|
|
|
815
|
-
const result =
|
|
823
|
+
const result = parser.parseSession(undefined, filePath);
|
|
816
824
|
expect(result).toMatchObject({
|
|
817
825
|
sessionId: "s1",
|
|
818
826
|
projectPath: "/repo",
|
|
@@ -829,24 +837,24 @@ describe("GeminiCliAdapter", () => {
|
|
|
829
837
|
messages: [],
|
|
830
838
|
});
|
|
831
839
|
|
|
832
|
-
const result =
|
|
840
|
+
const result = parser.parseSession(content, "/does/not/exist.json");
|
|
833
841
|
expect(result?.sessionId).toBe("s2");
|
|
834
842
|
});
|
|
835
843
|
|
|
836
844
|
it("should return null for a missing file with no cached content", () => {
|
|
837
|
-
expect(
|
|
845
|
+
expect(parser.parseSession(undefined, "/missing.json")).toBeNull();
|
|
838
846
|
});
|
|
839
847
|
|
|
840
848
|
it("should return null when the file is not valid JSON", () => {
|
|
841
849
|
const filePath = path.join(tmpHome, "broken.json");
|
|
842
850
|
fs.writeFileSync(filePath, "not json");
|
|
843
|
-
expect(
|
|
851
|
+
expect(parser.parseSession(undefined, filePath)).toBeNull();
|
|
844
852
|
});
|
|
845
853
|
|
|
846
854
|
it("should return null when sessionId is missing", () => {
|
|
847
855
|
const filePath = path.join(tmpHome, "no-id.json");
|
|
848
856
|
fs.writeFileSync(filePath, JSON.stringify({ messages: [] }));
|
|
849
|
-
expect(
|
|
857
|
+
expect(parser.parseSession(undefined, filePath)).toBeNull();
|
|
850
858
|
});
|
|
851
859
|
|
|
852
860
|
it("should default the summary when no user message has content", () => {
|
|
@@ -865,7 +873,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
865
873
|
],
|
|
866
874
|
});
|
|
867
875
|
|
|
868
|
-
const result =
|
|
876
|
+
const result = parser.parseSession(undefined, filePath);
|
|
869
877
|
expect(result?.summary).toBe("Gemini CLI session active");
|
|
870
878
|
});
|
|
871
879
|
|
|
@@ -881,7 +889,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
881
889
|
],
|
|
882
890
|
});
|
|
883
891
|
|
|
884
|
-
const result =
|
|
892
|
+
const result = parser.parseSession(undefined, filePath);
|
|
885
893
|
expect(result?.summary.length).toBe(120);
|
|
886
894
|
expect(result?.summary.endsWith("...")).toBe(true);
|
|
887
895
|
});
|
|
@@ -902,7 +910,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
902
910
|
],
|
|
903
911
|
});
|
|
904
912
|
|
|
905
|
-
const result =
|
|
913
|
+
const result = parser.parseSession(undefined, filePath);
|
|
906
914
|
expect(result?.summary).toBe("hello from part continued");
|
|
907
915
|
});
|
|
908
916
|
|
|
@@ -922,7 +930,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
922
930
|
],
|
|
923
931
|
});
|
|
924
932
|
|
|
925
|
-
expect(() =>
|
|
933
|
+
expect(() => parser.parseSession(undefined, filePath)).not.toThrow();
|
|
926
934
|
});
|
|
927
935
|
|
|
928
936
|
it("should drop non-text parts (data/file) when resolving user content", () => {
|
|
@@ -945,7 +953,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
945
953
|
],
|
|
946
954
|
});
|
|
947
955
|
|
|
948
|
-
const result =
|
|
956
|
+
const result = parser.parseSession(undefined, filePath);
|
|
949
957
|
expect(result?.summary).toBe("readable text + more");
|
|
950
958
|
});
|
|
951
959
|
|
|
@@ -958,7 +966,7 @@ describe("GeminiCliAdapter", () => {
|
|
|
958
966
|
messages: [{ id: "m1", timestamp: "2026-04-18T00:00:01Z", type: "user", content: "hi" }],
|
|
959
967
|
});
|
|
960
968
|
|
|
961
|
-
const result =
|
|
969
|
+
const result = parser.parseSession(undefined, filePath);
|
|
962
970
|
expect(result?.lastActive.toISOString()).toBe("2026-04-18T00:10:00.000Z");
|
|
963
971
|
});
|
|
964
972
|
});
|