@ai-devkit/agent-manager 0.26.1 → 0.26.2

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.
@@ -311,6 +311,43 @@ describe('AgentManager', ()=>{
311
311
  expect(registry.list()[0].tmuxSession).toBe('merry');
312
312
  expect(registry.list()[0].startedAt).toBe('2026-05-30T00:00:00.000Z');
313
313
  });
314
+ it('preserves a user-managed name when a fallback row was written later for the same pid', async ()=>{
315
+ registry.register({
316
+ name: 'agent-list-debug',
317
+ type: 'codex',
318
+ pid: process.pid,
319
+ tmuxSession: 'agent-list-debug',
320
+ cwd: '/cwd/debug',
321
+ startedAt: '2026-05-30T00:00:00.000Z',
322
+ sessionId: 'pid-debug',
323
+ sessionFilePath: ''
324
+ });
325
+ registry.register({
326
+ name: `ai-devkit-${process.pid}`,
327
+ type: 'codex',
328
+ pid: process.pid,
329
+ tmuxSession: '',
330
+ cwd: '/cwd/debug',
331
+ startedAt: '2026-05-31T00:00:00.000Z',
332
+ sessionId: 'pid-debug',
333
+ sessionFilePath: ''
334
+ });
335
+ scopedManager.registerAdapter(new MockAdapter('codex', [
336
+ createMockAgent({
337
+ name: `ai-devkit-${process.pid}`,
338
+ type: 'codex',
339
+ pid: process.pid
340
+ })
341
+ ]));
342
+ const agents = await scopedManager.listAgents();
343
+ expect(agents[0].name).toBe('agent-list-debug');
344
+ expect(registry.list()).toHaveLength(1);
345
+ expect(registry.list()[0]).toMatchObject({
346
+ name: 'agent-list-debug',
347
+ pid: process.pid,
348
+ tmuxSession: 'agent-list-debug'
349
+ });
350
+ });
314
351
  it('writes a fresh startedAt for new entries', async ()=>{
315
352
  const before = new Date().toISOString();
316
353
  scopedManager.registerAdapter(new MockAdapter('claude', [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/__tests__/AgentManager.test.ts"],"sourcesContent":["/**\n * Tests for AgentManager\n */\n\n\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { AgentManager } from '../AgentManager.js';\nimport type {\n AgentAdapter,\n AgentInfo,\n AgentType,\n ConversationMessage,\n SessionSummary,\n} from '../adapters/AgentAdapter.js';\nimport { AgentStatus } from '../adapters/AgentAdapter.js';\nimport { AgentRegistry, type RegistryEntry } from '../utils/AgentRegistry.js';\n\n// Mock adapter for testing\nclass MockAdapter implements AgentAdapter {\n public lastListSessionsOpts: unknown = undefined;\n\n constructor(\n public readonly type: AgentType,\n private mockAgents: AgentInfo[] = [],\n private shouldFail: boolean = false,\n private mockSessions: SessionSummary[] = [],\n private shouldFailListSessions: boolean = false,\n ) { }\n\n async detectAgents(): Promise<AgentInfo[]> {\n if (this.shouldFail) {\n throw new Error(`Mock adapter ${this.type} failed`);\n }\n return this.mockAgents;\n }\n\n canHandle(): boolean {\n return true;\n }\n\n getConversation(): ConversationMessage[] {\n return [];\n }\n\n async listSessions(opts?: unknown): Promise<SessionSummary[]> {\n this.lastListSessionsOpts = opts;\n if (this.shouldFailListSessions) {\n throw new Error(`Mock adapter ${this.type} listSessions failed`);\n }\n return this.mockSessions;\n }\n\n setAgents(agents: AgentInfo[]): void {\n this.mockAgents = agents;\n }\n\n setFail(shouldFail: boolean): void {\n this.shouldFail = shouldFail;\n }\n\n setSessions(sessions: SessionSummary[]): void {\n this.mockSessions = sessions;\n }\n\n setFailListSessions(shouldFail: boolean): void {\n this.shouldFailListSessions = shouldFail;\n }\n}\n\n// Helper to create mock agent\nfunction createMockAgent(overrides: Partial<AgentInfo> = {}): AgentInfo {\n return {\n name: 'test-agent',\n type: 'claude',\n status: AgentStatus.RUNNING,\n summary: 'Test summary',\n pid: 12345,\n projectPath: '/test/path',\n sessionId: 'test-session-id',\n lastActive: new Date(),\n ...overrides,\n };\n}\n\ndescribe('AgentManager', () => {\n let manager: AgentManager;\n let tmpDir: string;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-manager-'));\n manager = new AgentManager(new AgentRegistry(path.join(tmpDir, 'agents.json')));\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe('registerAdapter', () => {\n it('should register a new adapter', () => {\n const adapter = new MockAdapter('claude');\n\n manager.registerAdapter(adapter);\n\n expect(manager.hasAdapter('claude')).toBe(true);\n expect(manager.getAdapterCount()).toBe(1);\n });\n\n it('should throw error when registering duplicate adapter type', () => {\n const adapter1 = new MockAdapter('claude');\n const adapter2 = new MockAdapter('claude');\n\n manager.registerAdapter(adapter1);\n\n expect(() => manager.registerAdapter(adapter2)).toThrow(\n 'Adapter for type \"claude\" is already registered'\n );\n });\n\n it('should allow registering multiple different adapter types', () => {\n const adapter1 = new MockAdapter('claude');\n const adapter2 = new MockAdapter('gemini_cli');\n\n manager.registerAdapter(adapter1);\n manager.registerAdapter(adapter2);\n\n expect(manager.getAdapterCount()).toBe(2);\n expect(manager.hasAdapter('claude')).toBe(true);\n expect(manager.hasAdapter('gemini_cli')).toBe(true);\n });\n });\n\n describe('unregisterAdapter', () => {\n it('should unregister an existing adapter', () => {\n const adapter = new MockAdapter('claude');\n manager.registerAdapter(adapter);\n\n const removed = manager.unregisterAdapter('claude');\n\n expect(removed).toBe(true);\n expect(manager.hasAdapter('claude')).toBe(false);\n expect(manager.getAdapterCount()).toBe(0);\n });\n\n it('should return false when unregistering non-existent adapter', () => {\n const removed = manager.unregisterAdapter('NonExistent');\n expect(removed).toBe(false);\n });\n });\n\n describe('getAdapters', () => {\n it('should return empty array when no adapters registered', () => {\n const adapters = manager.getAdapters();\n expect(adapters).toEqual([]);\n });\n\n it('should return all registered adapters', () => {\n const adapter1 = new MockAdapter('claude');\n const adapter2 = new MockAdapter('gemini_cli');\n\n manager.registerAdapter(adapter1);\n manager.registerAdapter(adapter2);\n\n const adapters = manager.getAdapters();\n expect(adapters).toHaveLength(2);\n expect(adapters).toContain(adapter1);\n expect(adapters).toContain(adapter2);\n });\n });\n\n describe('listAgents', () => {\n it('should return empty array when no adapters registered', async () => {\n const agents = await manager.listAgents();\n expect(agents).toEqual([]);\n });\n\n it('should return agents from single adapter', async () => {\n const mockAgents = [\n createMockAgent({ name: 'agent1' }),\n createMockAgent({ name: 'agent2' }),\n ];\n const adapter = new MockAdapter('claude', mockAgents);\n\n manager.registerAdapter(adapter);\n const agents = await manager.listAgents();\n\n expect(agents).toHaveLength(2);\n expect(agents[0].name).toBe('agent1');\n expect(agents[1].name).toBe('agent2');\n });\n\n it('should aggregate agents from multiple adapters', async () => {\n const claudeAgents = [createMockAgent({ name: 'claude-agent', type: 'claude' })];\n const geminiAgents = [createMockAgent({ name: 'gemini-agent', type: 'gemini_cli' })];\n\n manager.registerAdapter(new MockAdapter('claude', claudeAgents));\n manager.registerAdapter(new MockAdapter('gemini_cli', geminiAgents));\n\n const agents = await manager.listAgents();\n\n expect(agents).toHaveLength(2);\n expect(agents.find(a => a.name === 'claude-agent')).toBeDefined();\n expect(agents.find(a => a.name === 'gemini-agent')).toBeDefined();\n });\n\n it('should sort agents by status priority (waiting first)', async () => {\n const mockAgents = [\n createMockAgent({ name: 'idle-agent', status: AgentStatus.IDLE }),\n createMockAgent({ name: 'waiting-agent', status: AgentStatus.WAITING }),\n createMockAgent({ name: 'running-agent', status: AgentStatus.RUNNING }),\n createMockAgent({ name: 'unknown-agent', status: AgentStatus.UNKNOWN }),\n ];\n const adapter = new MockAdapter('claude', mockAgents);\n\n manager.registerAdapter(adapter);\n const agents = await manager.listAgents();\n\n expect(agents[0].name).toBe('waiting-agent');\n expect(agents[1].name).toBe('running-agent');\n expect(agents[2].name).toBe('idle-agent');\n expect(agents[3].name).toBe('unknown-agent');\n });\n\n it('should handle adapter errors gracefully', async () => {\n const goodAdapter = new MockAdapter('claude', [\n createMockAgent({ name: 'good-agent' }),\n ]);\n const badAdapter = new MockAdapter('gemini_cli', [], true); // Will fail\n\n manager.registerAdapter(goodAdapter);\n manager.registerAdapter(badAdapter);\n\n // Should not throw, should return results from working adapter\n const agents = await manager.listAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0].name).toBe('good-agent');\n });\n\n it('should return empty array when all adapters fail', async () => {\n const adapter1 = new MockAdapter('claude', [], true);\n const adapter2 = new MockAdapter('gemini_cli', [], true);\n\n manager.registerAdapter(adapter1);\n manager.registerAdapter(adapter2);\n\n const agents = await manager.listAgents();\n expect(agents).toEqual([]);\n });\n });\n\n describe('listAgents — registry persistence', () => {\n let tmpDir: string;\n let regPath: string;\n let registry: AgentRegistry;\n let scopedManager: AgentManager;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-manager-'));\n regPath = path.join(tmpDir, 'agents.json');\n registry = new AgentRegistry(regPath);\n scopedManager = new AgentManager(registry);\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n it('persists every detected agent to the registry', async () => {\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({\n name: 'a',\n pid: process.pid,\n sessionId: 'sid-a',\n sessionFilePath: '/path/a.jsonl',\n projectPath: '/cwd/a',\n }),\n ]));\n\n await scopedManager.listAgents();\n\n const entries = registry.list();\n expect(entries).toHaveLength(1);\n expect(entries[0]).toMatchObject({\n name: 'a',\n type: 'claude',\n pid: process.pid,\n cwd: '/cwd/a',\n sessionId: 'sid-a',\n sessionFilePath: '/path/a.jsonl',\n tmuxSession: '',\n });\n expect(entries[0].startedAt).toMatch(/^\\d{4}-\\d{2}-\\d{2}T/);\n });\n\n it('prunes entries for dead pids', async () => {\n registry.register({\n name: 'dead',\n type: 'claude',\n pid: 999999,\n tmuxSession: '',\n cwd: '/cwd/dead',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'sid-dead',\n sessionFilePath: '/path/dead.jsonl',\n });\n\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({ name: 'live', pid: process.pid }),\n ]));\n\n await scopedManager.listAgents();\n\n const entries = registry.list();\n expect(entries.map((e) => e.name)).toEqual(['live']);\n });\n\n it('preserves an existing name (e.g. user-set \"merry\") across cycles', async () => {\n registry.register({\n name: 'merry',\n type: 'claude',\n pid: process.pid,\n tmuxSession: 'merry',\n cwd: '/cwd/merry',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'sid-merry',\n sessionFilePath: '/path/merry.jsonl',\n });\n\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({ name: 'default-name', pid: process.pid }),\n ]));\n\n const agents = await scopedManager.listAgents();\n\n expect(agents[0].name).toBe('merry');\n expect(registry.list()[0].name).toBe('merry');\n expect(registry.list()[0].tmuxSession).toBe('merry');\n expect(registry.list()[0].startedAt).toBe('2026-05-30T00:00:00.000Z');\n });\n\n it('writes a fresh startedAt for new entries', async () => {\n const before = new Date().toISOString();\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({ name: 'new', pid: process.pid }),\n ]));\n\n await scopedManager.listAgents();\n\n const entry = registry.list()[0];\n expect(entry.startedAt >= before).toBe(true);\n });\n\n it('batches the write — a single registerBatch call per listAgents', async () => {\n const spy = vi.spyOn(registry, 'registerBatch');\n\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({ name: 'a', pid: process.pid }),\n ]));\n scopedManager.registerAdapter(new MockAdapter('codex', [\n createMockAgent({ name: 'b', type: 'codex', pid: process.pid + 1 }),\n ]));\n\n await scopedManager.listAgents();\n\n expect(spy).toHaveBeenCalledTimes(1);\n expect((spy.mock.calls[0][0] as RegistryEntry[]).map((e) => e.name).sort())\n .toEqual(['a', 'b']);\n });\n\n it('skips registerBatch when no agents detected (still calls prune)', async () => {\n const writeSpy = vi.spyOn(registry, 'registerBatch');\n const pruneSpy = vi.spyOn(registry, 'prune');\n\n scopedManager.registerAdapter(new MockAdapter('claude', []));\n await scopedManager.listAgents();\n\n expect(writeSpy).not.toHaveBeenCalled();\n expect(pruneSpy).toHaveBeenCalledTimes(1);\n });\n });\n\n describe('clear', () => {\n it('should remove all adapters', () => {\n manager.registerAdapter(new MockAdapter('claude'));\n manager.registerAdapter(new MockAdapter('gemini_cli'));\n\n manager.clear();\n\n expect(manager.getAdapterCount()).toBe(0);\n expect(manager.getAdapters()).toEqual([]);\n });\n });\n\n describe('listSessions', () => {\n function createMockSession(overrides: Partial<SessionSummary> = {}): SessionSummary {\n return {\n type: 'claude',\n sessionId: 'session-1',\n cwd: '/repo',\n firstUserMessage: 'hello',\n lastActive: new Date('2025-01-01T00:00:00Z'),\n startedAt: new Date('2025-01-01T00:00:00Z'),\n sessionFilePath: '/tmp/session-1.jsonl',\n ...overrides,\n };\n }\n\n it('returns empty array when no adapters are registered', async () => {\n const result = await manager.listSessions();\n expect(result).toEqual([]);\n });\n\n it('merges sessions from every registered adapter', async () => {\n const claudeSession = createMockSession({ type: 'claude', sessionId: 'c1' });\n const codexSession = createMockSession({ type: 'codex', sessionId: 'cx1' });\n manager.registerAdapter(new MockAdapter('claude', [], false, [claudeSession]));\n manager.registerAdapter(new MockAdapter('codex', [], false, [codexSession]));\n\n const result = await manager.listSessions();\n\n expect(result).toHaveLength(2);\n expect(result.map((s) => s.sessionId).sort()).toEqual(['c1', 'cx1']);\n });\n\n it('sorts merged sessions by lastActive descending', async () => {\n const older = createMockSession({\n sessionId: 'older',\n lastActive: new Date('2025-01-01T00:00:00Z'),\n });\n const newer = createMockSession({\n type: 'codex',\n sessionId: 'newer',\n lastActive: new Date('2025-06-01T00:00:00Z'),\n });\n manager.registerAdapter(new MockAdapter('claude', [], false, [older]));\n manager.registerAdapter(new MockAdapter('codex', [], false, [newer]));\n\n const result = await manager.listSessions();\n\n expect(result.map((s) => s.sessionId)).toEqual(['newer', 'older']);\n });\n\n it('skips adapters whose type does not match opts.type', async () => {\n const claudeAdapter = new MockAdapter(\n 'claude',\n [],\n false,\n [createMockSession({ type: 'claude', sessionId: 'c1' })],\n );\n const codexAdapter = new MockAdapter(\n 'codex',\n [],\n false,\n [createMockSession({ type: 'codex', sessionId: 'cx1' })],\n );\n manager.registerAdapter(claudeAdapter);\n manager.registerAdapter(codexAdapter);\n\n const result = await manager.listSessions({ type: 'claude' });\n\n expect(result).toHaveLength(1);\n expect(result[0].sessionId).toBe('c1');\n // Codex adapter must not have been called\n expect(codexAdapter.lastListSessionsOpts).toBeUndefined();\n expect(claudeAdapter.lastListSessionsOpts).toEqual({ type: 'claude' });\n });\n\n it('tolerates an adapter that throws and still returns the others', async () => {\n const goodSession = createMockSession({ sessionId: 'good' });\n manager.registerAdapter(new MockAdapter('claude', [], false, [goodSession]));\n manager.registerAdapter(\n new MockAdapter('codex', [], false, [], true /* failListSessions */),\n );\n\n const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);\n\n try {\n const result = await manager.listSessions();\n expect(result).toHaveLength(1);\n expect(result[0].sessionId).toBe('good');\n expect(consoleErrorSpy).toHaveBeenCalled();\n } finally {\n consoleErrorSpy.mockRestore();\n }\n });\n\n it('passes the same opts to every called adapter', async () => {\n const a = new MockAdapter('claude', [], false, []);\n const b = new MockAdapter('codex', [], false, []);\n manager.registerAdapter(a);\n manager.registerAdapter(b);\n\n await manager.listSessions({ cwd: '/Users/test/proj' });\n\n expect(a.lastListSessionsOpts).toEqual({ cwd: '/Users/test/proj' });\n expect(b.lastListSessionsOpts).toEqual({ cwd: '/Users/test/proj' });\n });\n });\n\n describe('resolveAgent', () => {\n it('should return null for empty input or empty agents list', () => {\n const agent = createMockAgent({ name: 'test-agent' });\n expect(manager.resolveAgent('', [agent])).toBeNull();\n expect(manager.resolveAgent('test', [])).toBeNull();\n });\n\n it('should resolve exact match (case-insensitive)', () => {\n const agent = createMockAgent({ name: 'My-Agent' });\n const agents = [agent, createMockAgent({ name: 'Other' })];\n\n // Exact match\n expect(manager.resolveAgent('My-Agent', agents)).toBe(agent);\n // Case-insensitive\n expect(manager.resolveAgent('my-agent', agents)).toBe(agent);\n });\n\n it('should resolve unique partial match', () => {\n const agent = createMockAgent({ name: 'ai-devkit' });\n const agents = [\n agent,\n createMockAgent({ name: 'other-project' })\n ];\n\n const result = manager.resolveAgent('dev', agents);\n expect(result).toBe(agent);\n });\n\n it('should return array for ambiguous partial match', () => {\n const agent1 = createMockAgent({ name: 'my-website' });\n const agent2 = createMockAgent({ name: 'my-app' });\n const agents = [agent1, agent2, createMockAgent({ name: 'other' })];\n\n const result = manager.resolveAgent('my', agents);\n\n expect(Array.isArray(result)).toBe(true);\n const matches = result as AgentInfo[];\n expect(matches).toHaveLength(2);\n expect(matches).toContain(agent1);\n expect(matches).toContain(agent2);\n });\n\n it('should return null for no match', () => {\n const agents = [createMockAgent({ name: 'ai-devkit' })];\n expect(manager.resolveAgent('xyz', agents)).toBeNull();\n });\n\n it('should prefer exact match over partial matches', () => {\n // Edge case: \"test\" matches \"test\" (exact) and \"testing\" (partial)\n // Should return exact \"test\"\n const exact = createMockAgent({ name: 'test' });\n const partial = createMockAgent({ name: 'testing' });\n const agents = [exact, partial];\n\n expect(manager.resolveAgent('test', agents)).toBe(exact);\n });\n });\n});\n"],"names":["fs","os","path","AgentManager","AgentStatus","AgentRegistry","MockAdapter","lastListSessionsOpts","undefined","type","mockAgents","shouldFail","mockSessions","shouldFailListSessions","detectAgents","Error","canHandle","getConversation","listSessions","opts","setAgents","agents","setFail","setSessions","sessions","setFailListSessions","createMockAgent","overrides","name","status","RUNNING","summary","pid","projectPath","sessionId","lastActive","Date","describe","manager","tmpDir","beforeEach","mkdtempSync","join","tmpdir","afterEach","rmSync","recursive","force","it","adapter","registerAdapter","expect","hasAdapter","toBe","getAdapterCount","adapter1","adapter2","toThrow","removed","unregisterAdapter","adapters","getAdapters","toEqual","toHaveLength","toContain","listAgents","claudeAgents","geminiAgents","find","a","toBeDefined","IDLE","WAITING","UNKNOWN","goodAdapter","badAdapter","regPath","registry","scopedManager","process","sessionFilePath","entries","list","toMatchObject","cwd","tmuxSession","startedAt","toMatch","register","map","e","before","toISOString","entry","spy","vi","spyOn","toHaveBeenCalledTimes","mock","calls","sort","writeSpy","pruneSpy","not","toHaveBeenCalled","clear","createMockSession","firstUserMessage","result","claudeSession","codexSession","s","older","newer","claudeAdapter","codexAdapter","toBeUndefined","goodSession","consoleErrorSpy","console","mockImplementation","mockRestore","b","agent","resolveAgent","toBeNull","agent1","agent2","Array","isArray","matches","exact","partial"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/__tests__/AgentManager.test.ts"],"sourcesContent":["/**\n * Tests for AgentManager\n */\n\n\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { AgentManager } from '../AgentManager.js';\nimport type {\n AgentAdapter,\n AgentInfo,\n AgentType,\n ConversationMessage,\n SessionSummary,\n} from '../adapters/AgentAdapter.js';\nimport { AgentStatus } from '../adapters/AgentAdapter.js';\nimport { AgentRegistry, type RegistryEntry } from '../utils/AgentRegistry.js';\n\n// Mock adapter for testing\nclass MockAdapter implements AgentAdapter {\n public lastListSessionsOpts: unknown = undefined;\n\n constructor(\n public readonly type: AgentType,\n private mockAgents: AgentInfo[] = [],\n private shouldFail: boolean = false,\n private mockSessions: SessionSummary[] = [],\n private shouldFailListSessions: boolean = false,\n ) { }\n\n async detectAgents(): Promise<AgentInfo[]> {\n if (this.shouldFail) {\n throw new Error(`Mock adapter ${this.type} failed`);\n }\n return this.mockAgents;\n }\n\n canHandle(): boolean {\n return true;\n }\n\n getConversation(): ConversationMessage[] {\n return [];\n }\n\n async listSessions(opts?: unknown): Promise<SessionSummary[]> {\n this.lastListSessionsOpts = opts;\n if (this.shouldFailListSessions) {\n throw new Error(`Mock adapter ${this.type} listSessions failed`);\n }\n return this.mockSessions;\n }\n\n setAgents(agents: AgentInfo[]): void {\n this.mockAgents = agents;\n }\n\n setFail(shouldFail: boolean): void {\n this.shouldFail = shouldFail;\n }\n\n setSessions(sessions: SessionSummary[]): void {\n this.mockSessions = sessions;\n }\n\n setFailListSessions(shouldFail: boolean): void {\n this.shouldFailListSessions = shouldFail;\n }\n}\n\n// Helper to create mock agent\nfunction createMockAgent(overrides: Partial<AgentInfo> = {}): AgentInfo {\n return {\n name: 'test-agent',\n type: 'claude',\n status: AgentStatus.RUNNING,\n summary: 'Test summary',\n pid: 12345,\n projectPath: '/test/path',\n sessionId: 'test-session-id',\n lastActive: new Date(),\n ...overrides,\n };\n}\n\ndescribe('AgentManager', () => {\n let manager: AgentManager;\n let tmpDir: string;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-manager-'));\n manager = new AgentManager(new AgentRegistry(path.join(tmpDir, 'agents.json')));\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe('registerAdapter', () => {\n it('should register a new adapter', () => {\n const adapter = new MockAdapter('claude');\n\n manager.registerAdapter(adapter);\n\n expect(manager.hasAdapter('claude')).toBe(true);\n expect(manager.getAdapterCount()).toBe(1);\n });\n\n it('should throw error when registering duplicate adapter type', () => {\n const adapter1 = new MockAdapter('claude');\n const adapter2 = new MockAdapter('claude');\n\n manager.registerAdapter(adapter1);\n\n expect(() => manager.registerAdapter(adapter2)).toThrow(\n 'Adapter for type \"claude\" is already registered'\n );\n });\n\n it('should allow registering multiple different adapter types', () => {\n const adapter1 = new MockAdapter('claude');\n const adapter2 = new MockAdapter('gemini_cli');\n\n manager.registerAdapter(adapter1);\n manager.registerAdapter(adapter2);\n\n expect(manager.getAdapterCount()).toBe(2);\n expect(manager.hasAdapter('claude')).toBe(true);\n expect(manager.hasAdapter('gemini_cli')).toBe(true);\n });\n });\n\n describe('unregisterAdapter', () => {\n it('should unregister an existing adapter', () => {\n const adapter = new MockAdapter('claude');\n manager.registerAdapter(adapter);\n\n const removed = manager.unregisterAdapter('claude');\n\n expect(removed).toBe(true);\n expect(manager.hasAdapter('claude')).toBe(false);\n expect(manager.getAdapterCount()).toBe(0);\n });\n\n it('should return false when unregistering non-existent adapter', () => {\n const removed = manager.unregisterAdapter('NonExistent');\n expect(removed).toBe(false);\n });\n });\n\n describe('getAdapters', () => {\n it('should return empty array when no adapters registered', () => {\n const adapters = manager.getAdapters();\n expect(adapters).toEqual([]);\n });\n\n it('should return all registered adapters', () => {\n const adapter1 = new MockAdapter('claude');\n const adapter2 = new MockAdapter('gemini_cli');\n\n manager.registerAdapter(adapter1);\n manager.registerAdapter(adapter2);\n\n const adapters = manager.getAdapters();\n expect(adapters).toHaveLength(2);\n expect(adapters).toContain(adapter1);\n expect(adapters).toContain(adapter2);\n });\n });\n\n describe('listAgents', () => {\n it('should return empty array when no adapters registered', async () => {\n const agents = await manager.listAgents();\n expect(agents).toEqual([]);\n });\n\n it('should return agents from single adapter', async () => {\n const mockAgents = [\n createMockAgent({ name: 'agent1' }),\n createMockAgent({ name: 'agent2' }),\n ];\n const adapter = new MockAdapter('claude', mockAgents);\n\n manager.registerAdapter(adapter);\n const agents = await manager.listAgents();\n\n expect(agents).toHaveLength(2);\n expect(agents[0].name).toBe('agent1');\n expect(agents[1].name).toBe('agent2');\n });\n\n it('should aggregate agents from multiple adapters', async () => {\n const claudeAgents = [createMockAgent({ name: 'claude-agent', type: 'claude' })];\n const geminiAgents = [createMockAgent({ name: 'gemini-agent', type: 'gemini_cli' })];\n\n manager.registerAdapter(new MockAdapter('claude', claudeAgents));\n manager.registerAdapter(new MockAdapter('gemini_cli', geminiAgents));\n\n const agents = await manager.listAgents();\n\n expect(agents).toHaveLength(2);\n expect(agents.find(a => a.name === 'claude-agent')).toBeDefined();\n expect(agents.find(a => a.name === 'gemini-agent')).toBeDefined();\n });\n\n it('should sort agents by status priority (waiting first)', async () => {\n const mockAgents = [\n createMockAgent({ name: 'idle-agent', status: AgentStatus.IDLE }),\n createMockAgent({ name: 'waiting-agent', status: AgentStatus.WAITING }),\n createMockAgent({ name: 'running-agent', status: AgentStatus.RUNNING }),\n createMockAgent({ name: 'unknown-agent', status: AgentStatus.UNKNOWN }),\n ];\n const adapter = new MockAdapter('claude', mockAgents);\n\n manager.registerAdapter(adapter);\n const agents = await manager.listAgents();\n\n expect(agents[0].name).toBe('waiting-agent');\n expect(agents[1].name).toBe('running-agent');\n expect(agents[2].name).toBe('idle-agent');\n expect(agents[3].name).toBe('unknown-agent');\n });\n\n it('should handle adapter errors gracefully', async () => {\n const goodAdapter = new MockAdapter('claude', [\n createMockAgent({ name: 'good-agent' }),\n ]);\n const badAdapter = new MockAdapter('gemini_cli', [], true); // Will fail\n\n manager.registerAdapter(goodAdapter);\n manager.registerAdapter(badAdapter);\n\n // Should not throw, should return results from working adapter\n const agents = await manager.listAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0].name).toBe('good-agent');\n });\n\n it('should return empty array when all adapters fail', async () => {\n const adapter1 = new MockAdapter('claude', [], true);\n const adapter2 = new MockAdapter('gemini_cli', [], true);\n\n manager.registerAdapter(adapter1);\n manager.registerAdapter(adapter2);\n\n const agents = await manager.listAgents();\n expect(agents).toEqual([]);\n });\n });\n\n describe('listAgents — registry persistence', () => {\n let tmpDir: string;\n let regPath: string;\n let registry: AgentRegistry;\n let scopedManager: AgentManager;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-manager-'));\n regPath = path.join(tmpDir, 'agents.json');\n registry = new AgentRegistry(regPath);\n scopedManager = new AgentManager(registry);\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n it('persists every detected agent to the registry', async () => {\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({\n name: 'a',\n pid: process.pid,\n sessionId: 'sid-a',\n sessionFilePath: '/path/a.jsonl',\n projectPath: '/cwd/a',\n }),\n ]));\n\n await scopedManager.listAgents();\n\n const entries = registry.list();\n expect(entries).toHaveLength(1);\n expect(entries[0]).toMatchObject({\n name: 'a',\n type: 'claude',\n pid: process.pid,\n cwd: '/cwd/a',\n sessionId: 'sid-a',\n sessionFilePath: '/path/a.jsonl',\n tmuxSession: '',\n });\n expect(entries[0].startedAt).toMatch(/^\\d{4}-\\d{2}-\\d{2}T/);\n });\n\n it('prunes entries for dead pids', async () => {\n registry.register({\n name: 'dead',\n type: 'claude',\n pid: 999999,\n tmuxSession: '',\n cwd: '/cwd/dead',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'sid-dead',\n sessionFilePath: '/path/dead.jsonl',\n });\n\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({ name: 'live', pid: process.pid }),\n ]));\n\n await scopedManager.listAgents();\n\n const entries = registry.list();\n expect(entries.map((e) => e.name)).toEqual(['live']);\n });\n\n it('preserves an existing name (e.g. user-set \"merry\") across cycles', async () => {\n registry.register({\n name: 'merry',\n type: 'claude',\n pid: process.pid,\n tmuxSession: 'merry',\n cwd: '/cwd/merry',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'sid-merry',\n sessionFilePath: '/path/merry.jsonl',\n });\n\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({ name: 'default-name', pid: process.pid }),\n ]));\n\n const agents = await scopedManager.listAgents();\n\n expect(agents[0].name).toBe('merry');\n expect(registry.list()[0].name).toBe('merry');\n expect(registry.list()[0].tmuxSession).toBe('merry');\n expect(registry.list()[0].startedAt).toBe('2026-05-30T00:00:00.000Z');\n });\n\n it('preserves a user-managed name when a fallback row was written later for the same pid', async () => {\n registry.register({\n name: 'agent-list-debug',\n type: 'codex',\n pid: process.pid,\n tmuxSession: 'agent-list-debug',\n cwd: '/cwd/debug',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'pid-debug',\n sessionFilePath: '',\n });\n registry.register({\n name: `ai-devkit-${process.pid}`,\n type: 'codex',\n pid: process.pid,\n tmuxSession: '',\n cwd: '/cwd/debug',\n startedAt: '2026-05-31T00:00:00.000Z',\n sessionId: 'pid-debug',\n sessionFilePath: '',\n });\n\n scopedManager.registerAdapter(new MockAdapter('codex', [\n createMockAgent({ name: `ai-devkit-${process.pid}`, type: 'codex', pid: process.pid }),\n ]));\n\n const agents = await scopedManager.listAgents();\n\n expect(agents[0].name).toBe('agent-list-debug');\n expect(registry.list()).toHaveLength(1);\n expect(registry.list()[0]).toMatchObject({\n name: 'agent-list-debug',\n pid: process.pid,\n tmuxSession: 'agent-list-debug',\n });\n });\n\n it('writes a fresh startedAt for new entries', async () => {\n const before = new Date().toISOString();\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({ name: 'new', pid: process.pid }),\n ]));\n\n await scopedManager.listAgents();\n\n const entry = registry.list()[0];\n expect(entry.startedAt >= before).toBe(true);\n });\n\n it('batches the write — a single registerBatch call per listAgents', async () => {\n const spy = vi.spyOn(registry, 'registerBatch');\n\n scopedManager.registerAdapter(new MockAdapter('claude', [\n createMockAgent({ name: 'a', pid: process.pid }),\n ]));\n scopedManager.registerAdapter(new MockAdapter('codex', [\n createMockAgent({ name: 'b', type: 'codex', pid: process.pid + 1 }),\n ]));\n\n await scopedManager.listAgents();\n\n expect(spy).toHaveBeenCalledTimes(1);\n expect((spy.mock.calls[0][0] as RegistryEntry[]).map((e) => e.name).sort())\n .toEqual(['a', 'b']);\n });\n\n it('skips registerBatch when no agents detected (still calls prune)', async () => {\n const writeSpy = vi.spyOn(registry, 'registerBatch');\n const pruneSpy = vi.spyOn(registry, 'prune');\n\n scopedManager.registerAdapter(new MockAdapter('claude', []));\n await scopedManager.listAgents();\n\n expect(writeSpy).not.toHaveBeenCalled();\n expect(pruneSpy).toHaveBeenCalledTimes(1);\n });\n });\n\n describe('clear', () => {\n it('should remove all adapters', () => {\n manager.registerAdapter(new MockAdapter('claude'));\n manager.registerAdapter(new MockAdapter('gemini_cli'));\n\n manager.clear();\n\n expect(manager.getAdapterCount()).toBe(0);\n expect(manager.getAdapters()).toEqual([]);\n });\n });\n\n describe('listSessions', () => {\n function createMockSession(overrides: Partial<SessionSummary> = {}): SessionSummary {\n return {\n type: 'claude',\n sessionId: 'session-1',\n cwd: '/repo',\n firstUserMessage: 'hello',\n lastActive: new Date('2025-01-01T00:00:00Z'),\n startedAt: new Date('2025-01-01T00:00:00Z'),\n sessionFilePath: '/tmp/session-1.jsonl',\n ...overrides,\n };\n }\n\n it('returns empty array when no adapters are registered', async () => {\n const result = await manager.listSessions();\n expect(result).toEqual([]);\n });\n\n it('merges sessions from every registered adapter', async () => {\n const claudeSession = createMockSession({ type: 'claude', sessionId: 'c1' });\n const codexSession = createMockSession({ type: 'codex', sessionId: 'cx1' });\n manager.registerAdapter(new MockAdapter('claude', [], false, [claudeSession]));\n manager.registerAdapter(new MockAdapter('codex', [], false, [codexSession]));\n\n const result = await manager.listSessions();\n\n expect(result).toHaveLength(2);\n expect(result.map((s) => s.sessionId).sort()).toEqual(['c1', 'cx1']);\n });\n\n it('sorts merged sessions by lastActive descending', async () => {\n const older = createMockSession({\n sessionId: 'older',\n lastActive: new Date('2025-01-01T00:00:00Z'),\n });\n const newer = createMockSession({\n type: 'codex',\n sessionId: 'newer',\n lastActive: new Date('2025-06-01T00:00:00Z'),\n });\n manager.registerAdapter(new MockAdapter('claude', [], false, [older]));\n manager.registerAdapter(new MockAdapter('codex', [], false, [newer]));\n\n const result = await manager.listSessions();\n\n expect(result.map((s) => s.sessionId)).toEqual(['newer', 'older']);\n });\n\n it('skips adapters whose type does not match opts.type', async () => {\n const claudeAdapter = new MockAdapter(\n 'claude',\n [],\n false,\n [createMockSession({ type: 'claude', sessionId: 'c1' })],\n );\n const codexAdapter = new MockAdapter(\n 'codex',\n [],\n false,\n [createMockSession({ type: 'codex', sessionId: 'cx1' })],\n );\n manager.registerAdapter(claudeAdapter);\n manager.registerAdapter(codexAdapter);\n\n const result = await manager.listSessions({ type: 'claude' });\n\n expect(result).toHaveLength(1);\n expect(result[0].sessionId).toBe('c1');\n // Codex adapter must not have been called\n expect(codexAdapter.lastListSessionsOpts).toBeUndefined();\n expect(claudeAdapter.lastListSessionsOpts).toEqual({ type: 'claude' });\n });\n\n it('tolerates an adapter that throws and still returns the others', async () => {\n const goodSession = createMockSession({ sessionId: 'good' });\n manager.registerAdapter(new MockAdapter('claude', [], false, [goodSession]));\n manager.registerAdapter(\n new MockAdapter('codex', [], false, [], true /* failListSessions */),\n );\n\n const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);\n\n try {\n const result = await manager.listSessions();\n expect(result).toHaveLength(1);\n expect(result[0].sessionId).toBe('good');\n expect(consoleErrorSpy).toHaveBeenCalled();\n } finally {\n consoleErrorSpy.mockRestore();\n }\n });\n\n it('passes the same opts to every called adapter', async () => {\n const a = new MockAdapter('claude', [], false, []);\n const b = new MockAdapter('codex', [], false, []);\n manager.registerAdapter(a);\n manager.registerAdapter(b);\n\n await manager.listSessions({ cwd: '/Users/test/proj' });\n\n expect(a.lastListSessionsOpts).toEqual({ cwd: '/Users/test/proj' });\n expect(b.lastListSessionsOpts).toEqual({ cwd: '/Users/test/proj' });\n });\n });\n\n describe('resolveAgent', () => {\n it('should return null for empty input or empty agents list', () => {\n const agent = createMockAgent({ name: 'test-agent' });\n expect(manager.resolveAgent('', [agent])).toBeNull();\n expect(manager.resolveAgent('test', [])).toBeNull();\n });\n\n it('should resolve exact match (case-insensitive)', () => {\n const agent = createMockAgent({ name: 'My-Agent' });\n const agents = [agent, createMockAgent({ name: 'Other' })];\n\n // Exact match\n expect(manager.resolveAgent('My-Agent', agents)).toBe(agent);\n // Case-insensitive\n expect(manager.resolveAgent('my-agent', agents)).toBe(agent);\n });\n\n it('should resolve unique partial match', () => {\n const agent = createMockAgent({ name: 'ai-devkit' });\n const agents = [\n agent,\n createMockAgent({ name: 'other-project' })\n ];\n\n const result = manager.resolveAgent('dev', agents);\n expect(result).toBe(agent);\n });\n\n it('should return array for ambiguous partial match', () => {\n const agent1 = createMockAgent({ name: 'my-website' });\n const agent2 = createMockAgent({ name: 'my-app' });\n const agents = [agent1, agent2, createMockAgent({ name: 'other' })];\n\n const result = manager.resolveAgent('my', agents);\n\n expect(Array.isArray(result)).toBe(true);\n const matches = result as AgentInfo[];\n expect(matches).toHaveLength(2);\n expect(matches).toContain(agent1);\n expect(matches).toContain(agent2);\n });\n\n it('should return null for no match', () => {\n const agents = [createMockAgent({ name: 'ai-devkit' })];\n expect(manager.resolveAgent('xyz', agents)).toBeNull();\n });\n\n it('should prefer exact match over partial matches', () => {\n // Edge case: \"test\" matches \"test\" (exact) and \"testing\" (partial)\n // Should return exact \"test\"\n const exact = createMockAgent({ name: 'test' });\n const partial = createMockAgent({ name: 'testing' });\n const agents = [exact, partial];\n\n expect(manager.resolveAgent('test', agents)).toBe(exact);\n });\n });\n});\n"],"names":["fs","os","path","AgentManager","AgentStatus","AgentRegistry","MockAdapter","lastListSessionsOpts","undefined","type","mockAgents","shouldFail","mockSessions","shouldFailListSessions","detectAgents","Error","canHandle","getConversation","listSessions","opts","setAgents","agents","setFail","setSessions","sessions","setFailListSessions","createMockAgent","overrides","name","status","RUNNING","summary","pid","projectPath","sessionId","lastActive","Date","describe","manager","tmpDir","beforeEach","mkdtempSync","join","tmpdir","afterEach","rmSync","recursive","force","it","adapter","registerAdapter","expect","hasAdapter","toBe","getAdapterCount","adapter1","adapter2","toThrow","removed","unregisterAdapter","adapters","getAdapters","toEqual","toHaveLength","toContain","listAgents","claudeAgents","geminiAgents","find","a","toBeDefined","IDLE","WAITING","UNKNOWN","goodAdapter","badAdapter","regPath","registry","scopedManager","process","sessionFilePath","entries","list","toMatchObject","cwd","tmuxSession","startedAt","toMatch","register","map","e","before","toISOString","entry","spy","vi","spyOn","toHaveBeenCalledTimes","mock","calls","sort","writeSpy","pruneSpy","not","toHaveBeenCalled","clear","createMockSession","firstUserMessage","result","claudeSession","codexSession","s","older","newer","claudeAdapter","codexAdapter","toBeUndefined","goodSession","consoleErrorSpy","console","mockImplementation","mockRestore","b","agent","resolveAgent","toBeNull","agent1","agent2","Array","isArray","matches","exact","partial"],"mappings":"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"}
@@ -31,36 +31,37 @@ describe('AgentRegistry', ()=>{
31
31
  });
32
32
  });
33
33
  describe('register', ()=>{
34
- it('creates the file and parent directory if missing', ()=>{
34
+ it('creates the SQLite database and parent directory if missing', ()=>{
35
35
  registry.register(makeEntry());
36
- expect(fs.existsSync(regPath)).toBe(true);
37
- const parsed = JSON.parse(fs.readFileSync(regPath, 'utf8'));
38
- expect(parsed.entries).toHaveLength(1);
39
- expect(parsed.entries[0].name).toBe('agent1');
36
+ expect(fs.existsSync(regPath.replace(/\.json$/, '.db'))).toBe(true);
37
+ expect(registry.list()[0].name).toBe('agent1');
40
38
  });
41
39
  it('appends a new entry when name is unique', ()=>{
42
40
  registry.register(makeEntry({
43
41
  name: 'a'
44
42
  }));
45
43
  registry.register(makeEntry({
46
- name: 'b'
44
+ name: 'b',
45
+ pid: process.ppid
47
46
  }));
48
47
  expect(registry.list()).toHaveLength(2);
49
48
  });
50
- it('upserts in place when name already exists', ()=>{
49
+ it('upserts in place when type and pid already exist', ()=>{
51
50
  registry.register(makeEntry({
52
51
  name: 'a',
53
- pid: 100
52
+ pid: process.pid
54
53
  }));
55
54
  registry.register(makeEntry({
56
- name: 'a',
57
- pid: 200
55
+ name: 'fallback',
56
+ pid: process.pid,
57
+ tmuxSession: ''
58
58
  }));
59
59
  const all = registry.list();
60
60
  expect(all).toHaveLength(1);
61
- expect(all[0].pid).toBe(200);
61
+ expect(all[0].pid).toBe(process.pid);
62
+ expect(all[0].name).toBe('a');
62
63
  });
63
- it('writes atomically (no leftover .tmp on success)', ()=>{
64
+ it('does not write through the legacy fixed .tmp path', ()=>{
64
65
  registry.register(makeEntry());
65
66
  expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);
66
67
  });
@@ -79,24 +80,26 @@ describe('AgentRegistry', ()=>{
79
80
  tmuxSession: 'pinned'
80
81
  }));
81
82
  registry.register(makeEntry({
82
- name: 'a',
83
+ name: 'fallback',
83
84
  tmuxSession: '',
84
- pid: 999
85
+ pid: process.pid
85
86
  }));
86
87
  const saved = registry.lookup('a');
87
88
  expect(saved?.tmuxSession).toBe('pinned');
88
- expect(saved?.pid).toBe(999);
89
+ expect(saved?.pid).toBe(process.pid);
89
90
  });
90
- it('replaces tmuxSession when incoming is non-empty', ()=>{
91
+ it('lets a managed start entry replace a generated fallback for the same pid', ()=>{
91
92
  registry.register(makeEntry({
92
- name: 'a',
93
- tmuxSession: 'old'
93
+ name: `ai-devkit-${process.pid}`,
94
+ tmuxSession: ''
94
95
  }));
95
96
  registry.register(makeEntry({
96
- name: 'a',
97
- tmuxSession: 'new'
97
+ name: 'custom-name',
98
+ tmuxSession: 'custom-name'
98
99
  }));
99
- expect(registry.lookup('a')?.tmuxSession).toBe('new');
100
+ expect(registry.lookup('custom-name')?.tmuxSession).toBe('custom-name');
101
+ expect(registry.lookup(`ai-devkit-${process.pid}`)).toBeNull();
102
+ expect(registry.list()).toHaveLength(1);
100
103
  });
101
104
  });
102
105
  describe('registerBatch', ()=>{
@@ -104,21 +107,20 @@ describe('AgentRegistry', ()=>{
104
107
  registry.registerBatch([]);
105
108
  expect(fs.existsSync(regPath)).toBe(false);
106
109
  });
107
- it('upserts multiple entries with a single write', ()=>{
108
- const writeSpy = vi.spyOn(fs, 'writeFileSync');
110
+ it('upserts multiple entries in a single batch', ()=>{
109
111
  registry.registerBatch([
110
112
  makeEntry({
111
113
  name: 'a'
112
114
  }),
113
115
  makeEntry({
114
- name: 'b'
116
+ name: 'b',
117
+ pid: process.pid + 1
115
118
  }),
116
119
  makeEntry({
117
- name: 'c'
120
+ name: 'c',
121
+ pid: process.pid + 2
118
122
  })
119
123
  ]);
120
- expect(writeSpy).toHaveBeenCalledTimes(1);
121
- writeSpy.mockRestore();
122
124
  expect(registry.list()).toHaveLength(3);
123
125
  });
124
126
  it('applies the tmuxSession merge per entry', ()=>{
@@ -128,19 +130,37 @@ describe('AgentRegistry', ()=>{
128
130
  }));
129
131
  registry.registerBatch([
130
132
  makeEntry({
131
- name: 'a',
133
+ name: 'fallback',
132
134
  tmuxSession: '',
133
- pid: 7
135
+ pid: process.pid
134
136
  }),
135
137
  makeEntry({
136
138
  name: 'b',
137
- tmuxSession: ''
139
+ tmuxSession: '',
140
+ pid: process.pid + 1
138
141
  })
139
142
  ]);
140
143
  expect(registry.lookup('a')?.tmuxSession).toBe('pinned');
141
- expect(registry.lookup('a')?.pid).toBe(7);
144
+ expect(registry.lookup('a')?.pid).toBe(process.pid);
142
145
  expect(registry.lookup('b')?.tmuxSession).toBe('');
143
146
  });
147
+ it('handles concurrent registry instances without duplicate pid rows', ()=>{
148
+ const other = new AgentRegistry(regPath);
149
+ registry.register(makeEntry({
150
+ name: `ai-devkit-${process.pid}`,
151
+ tmuxSession: ''
152
+ }));
153
+ other.register(makeEntry({
154
+ name: 'custom-name',
155
+ tmuxSession: 'custom-name'
156
+ }));
157
+ registry.register(makeEntry({
158
+ name: `ai-devkit-${process.pid}`,
159
+ tmuxSession: ''
160
+ }));
161
+ expect(registry.list()).toHaveLength(1);
162
+ expect(registry.lookup('custom-name')?.pid).toBe(process.pid);
163
+ });
144
164
  });
145
165
  describe('lookup', ()=>{
146
166
  it('returns null when name not found', ()=>{
@@ -154,24 +174,26 @@ describe('AgentRegistry', ()=>{
154
174
  });
155
175
  });
156
176
  describe('list', ()=>{
157
- it('returns empty array when file does not exist', ()=>{
177
+ it('returns empty array when database does not contain entries', ()=>{
158
178
  expect(registry.list()).toEqual([]);
159
179
  });
160
- it('returns empty array when file is malformed', ()=>{
161
- fs.mkdirSync(path.dirname(regPath), {
162
- recursive: true
180
+ it('ignores existing legacy agents.json entries', ()=>{
181
+ const legacyEntry = makeEntry({
182
+ name: 'legacy',
183
+ tmuxSession: 'legacy'
163
184
  });
164
- fs.writeFileSync(regPath, 'not json', 'utf8');
165
- expect(registry.list()).toEqual([]);
166
- });
167
- it('coerces non-array entries to []', ()=>{
168
185
  fs.mkdirSync(path.dirname(regPath), {
169
186
  recursive: true
170
187
  });
171
188
  fs.writeFileSync(regPath, JSON.stringify({
172
- entries: 'oops'
189
+ entries: [
190
+ legacyEntry
191
+ ]
173
192
  }), 'utf8');
174
- expect(registry.list()).toEqual([]);
193
+ const legacyRegistry = new AgentRegistry(regPath);
194
+ expect(legacyRegistry.lookup('legacy')).toBeNull();
195
+ expect(legacyRegistry.list()).toEqual([]);
196
+ expect(fs.existsSync(regPath.replace(/\.json$/, '.db'))).toBe(true);
175
197
  });
176
198
  });
177
199
  describe('isAlive', ()=>{
@@ -205,10 +227,10 @@ describe('AgentRegistry', ()=>{
205
227
  registry.register(makeEntry({
206
228
  pid: process.pid
207
229
  }));
208
- const before = fs.readFileSync(regPath, 'utf8');
230
+ const before = registry.list();
209
231
  registry.prune();
210
- const after = fs.readFileSync(regPath, 'utf8');
211
- expect(after).toBe(before);
232
+ const after = registry.list();
233
+ expect(after).toEqual(before);
212
234
  });
213
235
  it('does nothing when file is missing', ()=>{
214
236
  expect(()=>registry.prune()).not.toThrow();
@@ -252,7 +274,7 @@ describe('AgentRegistry', ()=>{
252
274
  }));
253
275
  registry.register(makeEntry({
254
276
  name: 'agent-b',
255
- pid: process.pid
277
+ pid: process.ppid
256
278
  }));
257
279
  expect(()=>registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);
258
280
  });
@@ -268,7 +290,7 @@ describe('AgentRegistry', ()=>{
268
290
  expect(()=>registry.rename('agent-a', 'agent-b')).not.toThrow();
269
291
  expect(registry.lookup('agent-b')?.pid).toBe(process.pid);
270
292
  });
271
- it('writes atomically (no leftover .tmp on success)', ()=>{
293
+ it('does not create the legacy fixed .tmp path on rename', ()=>{
272
294
  registry.register(makeEntry({
273
295
  name: 'old-name',
274
296
  pid: process.pid
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/utils/AgentRegistry.test.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { AgentRegistry, RenameNotFoundError, RenameConflictError, type RegistryEntry } from '../../utils/AgentRegistry.js';\n\nfunction makeEntry(over: Partial<RegistryEntry> = {}): RegistryEntry {\n return {\n name: 'agent1',\n type: 'claude',\n pid: process.pid,\n tmuxSession: 'agent1',\n cwd: '/tmp',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'sid-1',\n sessionFilePath: '/tmp/session.jsonl',\n ...over,\n };\n}\n\ndescribe('AgentRegistry', () => {\n let tmpDir: string;\n let regPath: string;\n let registry: AgentRegistry;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-registry-'));\n regPath = path.join(tmpDir, 'nested', 'agents.json');\n registry = new AgentRegistry(regPath);\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe('register', () => {\n it('creates the file and parent directory if missing', () => {\n registry.register(makeEntry());\n expect(fs.existsSync(regPath)).toBe(true);\n const parsed = JSON.parse(fs.readFileSync(regPath, 'utf8'));\n expect(parsed.entries).toHaveLength(1);\n expect(parsed.entries[0].name).toBe('agent1');\n });\n\n it('appends a new entry when name is unique', () => {\n registry.register(makeEntry({ name: 'a' }));\n registry.register(makeEntry({ name: 'b' }));\n expect(registry.list()).toHaveLength(2);\n });\n\n it('upserts in place when name already exists', () => {\n registry.register(makeEntry({ name: 'a', pid: 100 }));\n registry.register(makeEntry({ name: 'a', pid: 200 }));\n const all = registry.list();\n expect(all).toHaveLength(1);\n expect(all[0].pid).toBe(200);\n });\n\n it('writes atomically (no leftover .tmp on success)', () => {\n registry.register(makeEntry());\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n\n it('persists session fields', () => {\n registry.register(makeEntry({ sessionId: 'sid-xyz', sessionFilePath: '/foo/bar.jsonl' }));\n const saved = registry.list()[0];\n expect(saved.sessionId).toBe('sid-xyz');\n expect(saved.sessionFilePath).toBe('/foo/bar.jsonl');\n });\n\n it('preserves existing tmuxSession when incoming is empty string', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'pinned' }));\n registry.register(makeEntry({ name: 'a', tmuxSession: '', pid: 999 }));\n const saved = registry.lookup('a');\n expect(saved?.tmuxSession).toBe('pinned');\n expect(saved?.pid).toBe(999);\n });\n\n it('replaces tmuxSession when incoming is non-empty', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'old' }));\n registry.register(makeEntry({ name: 'a', tmuxSession: 'new' }));\n expect(registry.lookup('a')?.tmuxSession).toBe('new');\n });\n });\n\n describe('registerBatch', () => {\n it('is a no-op on empty array', () => {\n registry.registerBatch([]);\n expect(fs.existsSync(regPath)).toBe(false);\n });\n\n it('upserts multiple entries with a single write', () => {\n const writeSpy = vi.spyOn(fs, 'writeFileSync');\n registry.registerBatch([\n makeEntry({ name: 'a' }),\n makeEntry({ name: 'b' }),\n makeEntry({ name: 'c' }),\n ]);\n expect(writeSpy).toHaveBeenCalledTimes(1);\n writeSpy.mockRestore();\n expect(registry.list()).toHaveLength(3);\n });\n\n it('applies the tmuxSession merge per entry', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'pinned' }));\n registry.registerBatch([\n makeEntry({ name: 'a', tmuxSession: '', pid: 7 }),\n makeEntry({ name: 'b', tmuxSession: '' }),\n ]);\n expect(registry.lookup('a')?.tmuxSession).toBe('pinned');\n expect(registry.lookup('a')?.pid).toBe(7);\n expect(registry.lookup('b')?.tmuxSession).toBe('');\n });\n });\n\n describe('lookup', () => {\n it('returns null when name not found', () => {\n expect(registry.lookup('missing')).toBeNull();\n });\n\n it('returns the entry when name matches', () => {\n registry.register(makeEntry({ name: 'a' }));\n expect(registry.lookup('a')?.name).toBe('a');\n });\n });\n\n describe('list', () => {\n it('returns empty array when file does not exist', () => {\n expect(registry.list()).toEqual([]);\n });\n\n it('returns empty array when file is malformed', () => {\n fs.mkdirSync(path.dirname(regPath), { recursive: true });\n fs.writeFileSync(regPath, 'not json', 'utf8');\n expect(registry.list()).toEqual([]);\n });\n\n it('coerces non-array entries to []', () => {\n fs.mkdirSync(path.dirname(regPath), { recursive: true });\n fs.writeFileSync(regPath, JSON.stringify({ entries: 'oops' }), 'utf8');\n expect(registry.list()).toEqual([]);\n });\n });\n\n describe('isAlive', () => {\n it('returns true for the current process', () => {\n expect(registry.isAlive(makeEntry({ pid: process.pid }))).toBe(true);\n });\n\n it('returns false for a PID that does not exist', () => {\n expect(registry.isAlive(makeEntry({ pid: 999999 }))).toBe(false);\n });\n });\n\n describe('prune', () => {\n it('removes entries whose PIDs are dead', () => {\n registry.register(makeEntry({ name: 'alive', pid: process.pid }));\n registry.register(makeEntry({ name: 'dead', pid: 999999 }));\n registry.prune();\n const remaining = registry.list();\n expect(remaining).toHaveLength(1);\n expect(remaining[0].name).toBe('alive');\n });\n\n it('is a no-op when all entries are alive', () => {\n registry.register(makeEntry({ pid: process.pid }));\n const before = fs.readFileSync(regPath, 'utf8');\n registry.prune();\n const after = fs.readFileSync(regPath, 'utf8');\n expect(after).toBe(before);\n });\n\n it('does nothing when file is missing', () => {\n expect(() => registry.prune()).not.toThrow();\n });\n });\n\n describe('default()', () => {\n it('returns a singleton instance', () => {\n expect(AgentRegistry.default()).toBe(AgentRegistry.default());\n });\n });\n\n describe('rename', () => {\n it('updates the name of an existing entry', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid }));\n registry.rename('old-name', 'new-name');\n expect(registry.lookup('new-name')?.name).toBe('new-name');\n expect(registry.lookup('old-name')).toBeNull();\n });\n\n it('preserves all other fields on the renamed entry', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid, tmuxSession: 'old-name', cwd: '/my/cwd' }));\n registry.rename('old-name', 'new-name');\n const entry = registry.lookup('new-name');\n expect(entry?.tmuxSession).toBe('old-name');\n expect(entry?.cwd).toBe('/my/cwd');\n expect(entry?.pid).toBe(process.pid);\n });\n\n it('throws RenameNotFoundError when current name does not exist', () => {\n expect(() => registry.rename('ghost', 'new-name')).toThrow(RenameNotFoundError);\n });\n\n it('throws RenameConflictError when new name is already in use by a live entry', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: process.pid }));\n expect(() => registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);\n });\n\n it('succeeds when new name exists only as a stale (dead) entry', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: 999999 }));\n expect(() => registry.rename('agent-a', 'agent-b')).not.toThrow();\n expect(registry.lookup('agent-b')?.pid).toBe(process.pid);\n });\n\n it('writes atomically (no leftover .tmp on success)', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid }));\n registry.rename('old-name', 'new-name');\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n });\n});\n"],"names":["fs","os","path","AgentRegistry","RenameNotFoundError","RenameConflictError","makeEntry","over","name","type","pid","process","tmuxSession","cwd","startedAt","sessionId","sessionFilePath","describe","tmpDir","regPath","registry","beforeEach","mkdtempSync","join","tmpdir","afterEach","rmSync","recursive","force","it","register","expect","existsSync","toBe","parsed","JSON","parse","readFileSync","entries","toHaveLength","list","all","saved","lookup","registerBatch","writeSpy","vi","spyOn","toHaveBeenCalledTimes","mockRestore","toBeNull","toEqual","mkdirSync","dirname","writeFileSync","stringify","isAlive","prune","remaining","before","after","not","toThrow","default","rename","entry"],"mappings":"AAAA,OAAOA,QAAQ,KAAK;AACpB,OAAOC,QAAQ,KAAK;AACpB,OAAOC,UAAU,OAAO;AACxB,SAASC,aAAa,EAAEC,mBAAmB,EAAEC,mBAAmB,QAA4B,+BAA+B;AAE3H,SAASC,UAAUC,OAA+B,CAAC,CAAC;IAChD,OAAO;QACHC,MAAM;QACNC,MAAM;QACNC,KAAKC,QAAQD,GAAG;QAChBE,aAAa;QACbC,KAAK;QACLC,WAAW;QACXC,WAAW;QACXC,iBAAiB;QACjB,GAAGT,IAAI;IACX;AACJ;AAEAU,SAAS,iBAAiB;IACtB,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJC,WAAW;QACPH,SAASlB,GAAGsB,WAAW,CAACpB,KAAKqB,IAAI,CAACtB,GAAGuB,MAAM,IAAI;QAC/CL,UAAUjB,KAAKqB,IAAI,CAACL,QAAQ,UAAU;QACtCE,WAAW,IAAIjB,cAAcgB;IACjC;IAEAM,UAAU;QACNzB,GAAG0B,MAAM,CAACR,QAAQ;YAAES,WAAW;YAAMC,OAAO;QAAK;IACrD;IAEAX,SAAS,YAAY;QACjBY,GAAG,oDAAoD;YACnDT,SAASU,QAAQ,CAACxB;YAClByB,OAAO/B,GAAGgC,UAAU,CAACb,UAAUc,IAAI,CAAC;YACpC,MAAMC,SAASC,KAAKC,KAAK,CAACpC,GAAGqC,YAAY,CAAClB,SAAS;YACnDY,OAAOG,OAAOI,OAAO,EAAEC,YAAY,CAAC;YACpCR,OAAOG,OAAOI,OAAO,CAAC,EAAE,CAAC9B,IAAI,EAAEyB,IAAI,CAAC;QACxC;QAEAJ,GAAG,2CAA2C;YAC1CT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;YAAI;YACxCY,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;YAAI;YACxCuB,OAAOX,SAASoB,IAAI,IAAID,YAAY,CAAC;QACzC;QAEAV,GAAG,6CAA6C;YAC5CT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAKE,KAAK;YAAI;YAClDU,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAKE,KAAK;YAAI;YAClD,MAAM+B,MAAMrB,SAASoB,IAAI;YACzBT,OAAOU,KAAKF,YAAY,CAAC;YACzBR,OAAOU,GAAG,CAAC,EAAE,CAAC/B,GAAG,EAAEuB,IAAI,CAAC;QAC5B;QAEAJ,GAAG,mDAAmD;YAClDT,SAASU,QAAQ,CAACxB;YAClByB,OAAO/B,GAAGgC,UAAU,CAAC,GAAGb,QAAQ,IAAI,CAAC,GAAGc,IAAI,CAAC;QACjD;QAEAJ,GAAG,2BAA2B;YAC1BT,SAASU,QAAQ,CAACxB,UAAU;gBAAES,WAAW;gBAAWC,iBAAiB;YAAiB;YACtF,MAAM0B,QAAQtB,SAASoB,IAAI,EAAE,CAAC,EAAE;YAChCT,OAAOW,MAAM3B,SAAS,EAAEkB,IAAI,CAAC;YAC7BF,OAAOW,MAAM1B,eAAe,EAAEiB,IAAI,CAAC;QACvC;QAEAJ,GAAG,gEAAgE;YAC/DT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAKI,aAAa;YAAS;YAC/DQ,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAKI,aAAa;gBAAIF,KAAK;YAAI;YACnE,MAAMgC,QAAQtB,SAASuB,MAAM,CAAC;YAC9BZ,OAAOW,OAAO9B,aAAaqB,IAAI,CAAC;YAChCF,OAAOW,OAAOhC,KAAKuB,IAAI,CAAC;QAC5B;QAEAJ,GAAG,mDAAmD;YAClDT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAKI,aAAa;YAAM;YAC5DQ,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAKI,aAAa;YAAM;YAC5DmB,OAAOX,SAASuB,MAAM,CAAC,MAAM/B,aAAaqB,IAAI,CAAC;QACnD;IACJ;IAEAhB,SAAS,iBAAiB;QACtBY,GAAG,6BAA6B;YAC5BT,SAASwB,aAAa,CAAC,EAAE;YACzBb,OAAO/B,GAAGgC,UAAU,CAACb,UAAUc,IAAI,CAAC;QACxC;QAEAJ,GAAG,gDAAgD;YAC/C,MAAMgB,WAAWC,GAAGC,KAAK,CAAC/C,IAAI;YAC9BoB,SAASwB,aAAa,CAAC;gBACnBtC,UAAU;oBAAEE,MAAM;gBAAI;gBACtBF,UAAU;oBAAEE,MAAM;gBAAI;gBACtBF,UAAU;oBAAEE,MAAM;gBAAI;aACzB;YACDuB,OAAOc,UAAUG,qBAAqB,CAAC;YACvCH,SAASI,WAAW;YACpBlB,OAAOX,SAASoB,IAAI,IAAID,YAAY,CAAC;QACzC;QAEAV,GAAG,2CAA2C;YAC1CT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAKI,aAAa;YAAS;YAC/DQ,SAASwB,aAAa,CAAC;gBACnBtC,UAAU;oBAAEE,MAAM;oBAAKI,aAAa;oBAAIF,KAAK;gBAAE;gBAC/CJ,UAAU;oBAAEE,MAAM;oBAAKI,aAAa;gBAAG;aAC1C;YACDmB,OAAOX,SAASuB,MAAM,CAAC,MAAM/B,aAAaqB,IAAI,CAAC;YAC/CF,OAAOX,SAASuB,MAAM,CAAC,MAAMjC,KAAKuB,IAAI,CAAC;YACvCF,OAAOX,SAASuB,MAAM,CAAC,MAAM/B,aAAaqB,IAAI,CAAC;QACnD;IACJ;IAEAhB,SAAS,UAAU;QACfY,GAAG,oCAAoC;YACnCE,OAAOX,SAASuB,MAAM,CAAC,YAAYO,QAAQ;QAC/C;QAEArB,GAAG,uCAAuC;YACtCT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;YAAI;YACxCuB,OAAOX,SAASuB,MAAM,CAAC,MAAMnC,MAAMyB,IAAI,CAAC;QAC5C;IACJ;IAEAhB,SAAS,QAAQ;QACbY,GAAG,gDAAgD;YAC/CE,OAAOX,SAASoB,IAAI,IAAIW,OAAO,CAAC,EAAE;QACtC;QAEAtB,GAAG,8CAA8C;YAC7C7B,GAAGoD,SAAS,CAAClD,KAAKmD,OAAO,CAAClC,UAAU;gBAAEQ,WAAW;YAAK;YACtD3B,GAAGsD,aAAa,CAACnC,SAAS,YAAY;YACtCY,OAAOX,SAASoB,IAAI,IAAIW,OAAO,CAAC,EAAE;QACtC;QAEAtB,GAAG,mCAAmC;YAClC7B,GAAGoD,SAAS,CAAClD,KAAKmD,OAAO,CAAClC,UAAU;gBAAEQ,WAAW;YAAK;YACtD3B,GAAGsD,aAAa,CAACnC,SAASgB,KAAKoB,SAAS,CAAC;gBAAEjB,SAAS;YAAO,IAAI;YAC/DP,OAAOX,SAASoB,IAAI,IAAIW,OAAO,CAAC,EAAE;QACtC;IACJ;IAEAlC,SAAS,WAAW;QAChBY,GAAG,wCAAwC;YACvCE,OAAOX,SAASoC,OAAO,CAAClD,UAAU;gBAAEI,KAAKC,QAAQD,GAAG;YAAC,KAAKuB,IAAI,CAAC;QACnE;QAEAJ,GAAG,+CAA+C;YAC9CE,OAAOX,SAASoC,OAAO,CAAClD,UAAU;gBAAEI,KAAK;YAAO,KAAKuB,IAAI,CAAC;QAC9D;IACJ;IAEAhB,SAAS,SAAS;QACdY,GAAG,uCAAuC;YACtCT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAASE,KAAKC,QAAQD,GAAG;YAAC;YAC9DU,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAQE,KAAK;YAAO;YACxDU,SAASqC,KAAK;YACd,MAAMC,YAAYtC,SAASoB,IAAI;YAC/BT,OAAO2B,WAAWnB,YAAY,CAAC;YAC/BR,OAAO2B,SAAS,CAAC,EAAE,CAAClD,IAAI,EAAEyB,IAAI,CAAC;QACnC;QAEAJ,GAAG,yCAAyC;YACxCT,SAASU,QAAQ,CAACxB,UAAU;gBAAEI,KAAKC,QAAQD,GAAG;YAAC;YAC/C,MAAMiD,SAAS3D,GAAGqC,YAAY,CAAClB,SAAS;YACxCC,SAASqC,KAAK;YACd,MAAMG,QAAQ5D,GAAGqC,YAAY,CAAClB,SAAS;YACvCY,OAAO6B,OAAO3B,IAAI,CAAC0B;QACvB;QAEA9B,GAAG,qCAAqC;YACpCE,OAAO,IAAMX,SAASqC,KAAK,IAAII,GAAG,CAACC,OAAO;QAC9C;IACJ;IAEA7C,SAAS,aAAa;QAClBY,GAAG,gCAAgC;YAC/BE,OAAO5B,cAAc4D,OAAO,IAAI9B,IAAI,CAAC9B,cAAc4D,OAAO;QAC9D;IACJ;IAEA9C,SAAS,UAAU;QACfY,GAAG,yCAAyC;YACxCT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;YAAC;YACjEU,SAAS4C,MAAM,CAAC,YAAY;YAC5BjC,OAAOX,SAASuB,MAAM,CAAC,aAAanC,MAAMyB,IAAI,CAAC;YAC/CF,OAAOX,SAASuB,MAAM,CAAC,aAAaO,QAAQ;QAChD;QAEArB,GAAG,mDAAmD;YAClDT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;gBAAEE,aAAa;gBAAYC,KAAK;YAAU;YAC1GO,SAAS4C,MAAM,CAAC,YAAY;YAC5B,MAAMC,QAAQ7C,SAASuB,MAAM,CAAC;YAC9BZ,OAAOkC,OAAOrD,aAAaqB,IAAI,CAAC;YAChCF,OAAOkC,OAAOpD,KAAKoB,IAAI,CAAC;YACxBF,OAAOkC,OAAOvD,KAAKuB,IAAI,CAACtB,QAAQD,GAAG;QACvC;QAEAmB,GAAG,+DAA+D;YAC9DE,OAAO,IAAMX,SAAS4C,MAAM,CAAC,SAAS,aAAaF,OAAO,CAAC1D;QAC/D;QAEAyB,GAAG,8EAA8E;YAC7ET,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEU,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEqB,OAAO,IAAMX,SAAS4C,MAAM,CAAC,WAAW,YAAYF,OAAO,CAACzD;QAChE;QAEAwB,GAAG,8DAA8D;YAC7DT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEU,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAWE,KAAK;YAAO;YAC3DqB,OAAO,IAAMX,SAAS4C,MAAM,CAAC,WAAW,YAAYH,GAAG,CAACC,OAAO;YAC/D/B,OAAOX,SAASuB,MAAM,CAAC,YAAYjC,KAAKuB,IAAI,CAACtB,QAAQD,GAAG;QAC5D;QAEAmB,GAAG,mDAAmD;YAClDT,SAASU,QAAQ,CAACxB,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;YAAC;YACjEU,SAAS4C,MAAM,CAAC,YAAY;YAC5BjC,OAAO/B,GAAGgC,UAAU,CAAC,GAAGb,QAAQ,IAAI,CAAC,GAAGc,IAAI,CAAC;QACjD;IACJ;AACJ"}
1
+ {"version":3,"sources":["../../../src/__tests__/utils/AgentRegistry.test.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { AgentRegistry, RenameNotFoundError, RenameConflictError, type RegistryEntry } from '../../utils/AgentRegistry.js';\n\nfunction makeEntry(over: Partial<RegistryEntry> = {}): RegistryEntry {\n return {\n name: 'agent1',\n type: 'claude',\n pid: process.pid,\n tmuxSession: 'agent1',\n cwd: '/tmp',\n startedAt: '2026-05-30T00:00:00.000Z',\n sessionId: 'sid-1',\n sessionFilePath: '/tmp/session.jsonl',\n ...over,\n };\n}\n\ndescribe('AgentRegistry', () => {\n let tmpDir: string;\n let regPath: string;\n let registry: AgentRegistry;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-registry-'));\n regPath = path.join(tmpDir, 'nested', 'agents.json');\n registry = new AgentRegistry(regPath);\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe('register', () => {\n it('creates the SQLite database and parent directory if missing', () => {\n registry.register(makeEntry());\n expect(fs.existsSync(regPath.replace(/\\.json$/, '.db'))).toBe(true);\n expect(registry.list()[0].name).toBe('agent1');\n });\n\n it('appends a new entry when name is unique', () => {\n registry.register(makeEntry({ name: 'a' }));\n registry.register(makeEntry({ name: 'b', pid: process.ppid }));\n expect(registry.list()).toHaveLength(2);\n });\n\n it('upserts in place when type and pid already exist', () => {\n registry.register(makeEntry({ name: 'a', pid: process.pid }));\n registry.register(makeEntry({ name: 'fallback', pid: process.pid, tmuxSession: '' }));\n const all = registry.list();\n expect(all).toHaveLength(1);\n expect(all[0].pid).toBe(process.pid);\n expect(all[0].name).toBe('a');\n });\n\n it('does not write through the legacy fixed .tmp path', () => {\n registry.register(makeEntry());\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n\n it('persists session fields', () => {\n registry.register(makeEntry({ sessionId: 'sid-xyz', sessionFilePath: '/foo/bar.jsonl' }));\n const saved = registry.list()[0];\n expect(saved.sessionId).toBe('sid-xyz');\n expect(saved.sessionFilePath).toBe('/foo/bar.jsonl');\n });\n\n it('preserves existing tmuxSession when incoming is empty string', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'pinned' }));\n registry.register(makeEntry({ name: 'fallback', tmuxSession: '', pid: process.pid }));\n const saved = registry.lookup('a');\n expect(saved?.tmuxSession).toBe('pinned');\n expect(saved?.pid).toBe(process.pid);\n });\n\n it('lets a managed start entry replace a generated fallback for the same pid', () => {\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n registry.register(makeEntry({ name: 'custom-name', tmuxSession: 'custom-name' }));\n expect(registry.lookup('custom-name')?.tmuxSession).toBe('custom-name');\n expect(registry.lookup(`ai-devkit-${process.pid}`)).toBeNull();\n expect(registry.list()).toHaveLength(1);\n });\n });\n\n describe('registerBatch', () => {\n it('is a no-op on empty array', () => {\n registry.registerBatch([]);\n expect(fs.existsSync(regPath)).toBe(false);\n });\n\n it('upserts multiple entries in a single batch', () => {\n registry.registerBatch([\n makeEntry({ name: 'a' }),\n makeEntry({ name: 'b', pid: process.pid + 1 }),\n makeEntry({ name: 'c', pid: process.pid + 2 }),\n ]);\n expect(registry.list()).toHaveLength(3);\n });\n\n it('applies the tmuxSession merge per entry', () => {\n registry.register(makeEntry({ name: 'a', tmuxSession: 'pinned' }));\n registry.registerBatch([\n makeEntry({ name: 'fallback', tmuxSession: '', pid: process.pid }),\n makeEntry({ name: 'b', tmuxSession: '', pid: process.pid + 1 }),\n ]);\n expect(registry.lookup('a')?.tmuxSession).toBe('pinned');\n expect(registry.lookup('a')?.pid).toBe(process.pid);\n expect(registry.lookup('b')?.tmuxSession).toBe('');\n });\n\n it('handles concurrent registry instances without duplicate pid rows', () => {\n const other = new AgentRegistry(regPath);\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n other.register(makeEntry({ name: 'custom-name', tmuxSession: 'custom-name' }));\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, tmuxSession: '' }));\n\n expect(registry.list()).toHaveLength(1);\n expect(registry.lookup('custom-name')?.pid).toBe(process.pid);\n });\n });\n\n describe('lookup', () => {\n it('returns null when name not found', () => {\n expect(registry.lookup('missing')).toBeNull();\n });\n\n it('returns the entry when name matches', () => {\n registry.register(makeEntry({ name: 'a' }));\n expect(registry.lookup('a')?.name).toBe('a');\n });\n });\n\n describe('list', () => {\n it('returns empty array when database does not contain entries', () => {\n expect(registry.list()).toEqual([]);\n });\n\n it('ignores existing legacy agents.json entries', () => {\n const legacyEntry = makeEntry({ name: 'legacy', tmuxSession: 'legacy' });\n fs.mkdirSync(path.dirname(regPath), { recursive: true });\n fs.writeFileSync(regPath, JSON.stringify({ entries: [legacyEntry] }), 'utf8');\n\n const legacyRegistry = new AgentRegistry(regPath);\n\n expect(legacyRegistry.lookup('legacy')).toBeNull();\n expect(legacyRegistry.list()).toEqual([]);\n expect(fs.existsSync(regPath.replace(/\\.json$/, '.db'))).toBe(true);\n });\n });\n\n describe('isAlive', () => {\n it('returns true for the current process', () => {\n expect(registry.isAlive(makeEntry({ pid: process.pid }))).toBe(true);\n });\n\n it('returns false for a PID that does not exist', () => {\n expect(registry.isAlive(makeEntry({ pid: 999999 }))).toBe(false);\n });\n });\n\n describe('prune', () => {\n it('removes entries whose PIDs are dead', () => {\n registry.register(makeEntry({ name: 'alive', pid: process.pid }));\n registry.register(makeEntry({ name: 'dead', pid: 999999 }));\n registry.prune();\n const remaining = registry.list();\n expect(remaining).toHaveLength(1);\n expect(remaining[0].name).toBe('alive');\n });\n\n it('is a no-op when all entries are alive', () => {\n registry.register(makeEntry({ pid: process.pid }));\n const before = registry.list();\n registry.prune();\n const after = registry.list();\n expect(after).toEqual(before);\n });\n\n it('does nothing when file is missing', () => {\n expect(() => registry.prune()).not.toThrow();\n });\n });\n\n describe('default()', () => {\n it('returns a singleton instance', () => {\n expect(AgentRegistry.default()).toBe(AgentRegistry.default());\n });\n });\n\n describe('rename', () => {\n it('updates the name of an existing entry', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid }));\n registry.rename('old-name', 'new-name');\n expect(registry.lookup('new-name')?.name).toBe('new-name');\n expect(registry.lookup('old-name')).toBeNull();\n });\n\n it('preserves all other fields on the renamed entry', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid, tmuxSession: 'old-name', cwd: '/my/cwd' }));\n registry.rename('old-name', 'new-name');\n const entry = registry.lookup('new-name');\n expect(entry?.tmuxSession).toBe('old-name');\n expect(entry?.cwd).toBe('/my/cwd');\n expect(entry?.pid).toBe(process.pid);\n });\n\n it('throws RenameNotFoundError when current name does not exist', () => {\n expect(() => registry.rename('ghost', 'new-name')).toThrow(RenameNotFoundError);\n });\n\n it('throws RenameConflictError when new name is already in use by a live entry', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: process.ppid }));\n expect(() => registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);\n });\n\n it('succeeds when new name exists only as a stale (dead) entry', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: 999999 }));\n expect(() => registry.rename('agent-a', 'agent-b')).not.toThrow();\n expect(registry.lookup('agent-b')?.pid).toBe(process.pid);\n });\n\n it('does not create the legacy fixed .tmp path on rename', () => {\n registry.register(makeEntry({ name: 'old-name', pid: process.pid }));\n registry.rename('old-name', 'new-name');\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n });\n});\n"],"names":["fs","os","path","AgentRegistry","RenameNotFoundError","RenameConflictError","makeEntry","over","name","type","pid","process","tmuxSession","cwd","startedAt","sessionId","sessionFilePath","describe","tmpDir","regPath","registry","beforeEach","mkdtempSync","join","tmpdir","afterEach","rmSync","recursive","force","it","register","expect","existsSync","replace","toBe","list","ppid","toHaveLength","all","saved","lookup","toBeNull","registerBatch","other","toEqual","legacyEntry","mkdirSync","dirname","writeFileSync","JSON","stringify","entries","legacyRegistry","isAlive","prune","remaining","before","after","not","toThrow","default","rename","entry"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import Database from 'better-sqlite3';
2
+ export declare const DEFAULT_AGENT_REGISTRY_DB_PATH: string;
3
+ export interface DatabaseOptions {
4
+ dbPath?: string;
5
+ verbose?: boolean;
6
+ readonly?: boolean;
7
+ }
8
+ export declare function resolveAgentRegistryDbPath(filePath?: string): string;
9
+ export declare class DatabaseConnection {
10
+ private db;
11
+ private readonly dbPath;
12
+ constructor(options?: DatabaseOptions);
13
+ private configure;
14
+ get instance(): Database.Database;
15
+ get path(): string;
16
+ query<T>(sql: string, params?: unknown[]): T[];
17
+ queryOne<T>(sql: string, params?: unknown[]): T | undefined;
18
+ execute(sql: string, params?: unknown[]): Database.RunResult;
19
+ transaction<T>(fn: () => T): T;
20
+ close(): void;
21
+ }
22
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/database/connection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAMtC,eAAO,MAAM,8BAA8B,QAA6C,CAAC;AAEzF,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,GAAE,eAAoB;IAazC,OAAO,CAAC,SAAS;IAQjB,IAAI,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAEhC;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,CAAC,EAAE;IAIlD,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,CAAC,GAAG,SAAS;IAI/D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,QAAQ,CAAC,SAAS;IAIhE,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAI9B,KAAK,IAAI,IAAI;CAKhB"}
@@ -0,0 +1,58 @@
1
+ import Database from 'better-sqlite3';
2
+ import { mkdirSync } from 'fs';
3
+ import { dirname, join } from 'path';
4
+ import { homedir } from 'os';
5
+ import { initializeSchema } from './schema.js';
6
+ export const DEFAULT_AGENT_REGISTRY_DB_PATH = join(homedir(), '.ai-devkit', 'agents.db');
7
+ export function resolveAgentRegistryDbPath(filePath) {
8
+ if (!filePath) return DEFAULT_AGENT_REGISTRY_DB_PATH;
9
+ return filePath.endsWith('.json') ? filePath.replace(/\.json$/, '.db') : filePath;
10
+ }
11
+ export class DatabaseConnection {
12
+ db;
13
+ dbPath;
14
+ constructor(options = {}){
15
+ this.dbPath = options.dbPath ?? DEFAULT_AGENT_REGISTRY_DB_PATH;
16
+ mkdirSync(dirname(this.dbPath), {
17
+ recursive: true
18
+ });
19
+ this.db = new Database(this.dbPath, {
20
+ readonly: options.readonly ?? false,
21
+ verbose: options.verbose ? console.log : undefined
22
+ });
23
+ this.configure();
24
+ initializeSchema(this);
25
+ }
26
+ configure() {
27
+ this.db.pragma('journal_mode = WAL');
28
+ this.db.pragma('foreign_keys = ON');
29
+ this.db.pragma('synchronous = NORMAL');
30
+ this.db.pragma('busy_timeout = 5000');
31
+ this.db.pragma('mmap_size = 268435456');
32
+ }
33
+ get instance() {
34
+ return this.db;
35
+ }
36
+ get path() {
37
+ return this.dbPath;
38
+ }
39
+ query(sql, params = []) {
40
+ return this.db.prepare(sql).all(...params);
41
+ }
42
+ queryOne(sql, params = []) {
43
+ return this.db.prepare(sql).get(...params);
44
+ }
45
+ execute(sql, params = []) {
46
+ return this.db.prepare(sql).run(...params);
47
+ }
48
+ transaction(fn) {
49
+ return this.db.transaction(fn)();
50
+ }
51
+ close() {
52
+ if (this.db.open) {
53
+ this.db.close();
54
+ }
55
+ }
56
+ }
57
+
58
+ //# sourceMappingURL=connection.js.map