@ai-devkit/agent-manager 0.26.3 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 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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"}
1
+ {"version":3,"sources":["../../../src/__tests__/utils/AgentRegistry.test.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport Database from 'better-sqlite3';\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 pinned: false,\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 vi.restoreAllMocks();\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 it('preserves an existing name conflict when its probe fails with EPERM', () => {\n registry.register(makeEntry({ name: 'claimed-name', pid: process.pid }));\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('operation not permitted'), { code: 'EPERM' });\n });\n\n expect(() => registry.register(makeEntry({\n name: 'claimed-name',\n pid: process.pid + 1,\n }))).toThrow();\n expect(registry.lookup('claimed-name')?.pid).toBe(process.pid);\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 it('cleans up a cross-type row when its pid has been reused', () => {\n registry.register(makeEntry({ name: 'old-claude', type: 'claude', pid: process.pid }));\n\n registry.register(makeEntry({\n name: 'new-codex',\n type: 'codex',\n pid: process.pid,\n tmuxSession: '',\n }));\n\n expect(registry.lookup('old-claude')).toBeNull();\n expect(registry.lookup('new-codex')).toMatchObject({ type: 'codex', pid: process.pid });\n expect(registry.list()).toHaveLength(1);\n });\n\n it('rolls back the whole batch when a live name conflict rejects one entry', () => {\n registry.register(makeEntry({ name: 'taken', pid: process.pid }));\n\n expect(() => registry.registerBatch([\n makeEntry({ name: 'fresh', pid: 999998 }),\n makeEntry({ name: 'taken', type: 'codex', pid: 999997 }),\n ])).toThrow(/UNIQUE constraint failed/);\n\n expect(registry.lookup('fresh')).toBeNull();\n expect(registry.lookup('taken')?.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('pinning', () => {\n it('defaults new rows to unpinned and toggles the persisted state', () => {\n registry.register(makeEntry());\n\n expect(registry.lookup('agent1')?.pinned).toBe(false);\n expect(registry.togglePin('claude', process.pid)).toBe(true);\n expect(registry.lookup('agent1')?.pinned).toBe(true);\n expect(registry.togglePin('claude', process.pid)).toBe(false);\n expect(registry.lookup('agent1')?.pinned).toBe(false);\n });\n\n it('updates existing recency when toggled', () => {\n let now = new Date('2026-08-16T10:00:00.000Z');\n const clocked = new AgentRegistry(regPath, { now: () => now });\n clocked.register(makeEntry());\n now = new Date('2026-08-16T10:01:00.000Z');\n\n clocked.togglePin('claude', process.pid);\n\n expect(clocked.lookup('agent1')?.updatedAt).toBe(now.toISOString());\n const db = new Database(regPath.replace(/\\.json$/, '.db'), { readonly: true });\n const row = db.prepare('SELECT updated_at FROM agents WHERE type = ? AND pid = ?')\n .get('claude', process.pid) as { updated_at: string };\n db.close();\n expect(row.updated_at).toBe(now.toISOString());\n });\n\n it('returns null when the process row has disappeared', () => {\n expect(registry.togglePin('claude', 999999)).toBeNull();\n });\n\n it('preserves a pin when poll registration updates the row', () => {\n registry.register(makeEntry({ sessionId: 'before' }));\n registry.togglePin('claude', process.pid);\n\n registry.register(makeEntry({ sessionId: 'after' }));\n\n expect(registry.lookup('agent1')).toMatchObject({ sessionId: 'after', pinned: true });\n });\n\n it('preserves a pin through rename', () => {\n registry.register(makeEntry({ name: 'before' }));\n registry.togglePin('claude', process.pid);\n\n registry.rename('before', 'after');\n\n expect(registry.lookup('after')?.pinned).toBe(true);\n });\n\n it('removes the pin with a pruned process row', () => {\n registry.register(makeEntry({ pid: 999999 }));\n registry.togglePin('claude', 999999);\n\n registry.prune();\n\n expect(registry.lookup('agent1')).toBeNull();\n });\n\n it('reports a clear error when a readonly registry toggles a pin', () => {\n registry.register(makeEntry());\n const readonlyRegistry = new AgentRegistry(regPath, { readonly: true });\n\n expect(() => readonlyRegistry.togglePin('claude', process.pid)).toThrow(/readonly/i);\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 it('returns true when the process probe is forbidden with EPERM', () => {\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('operation not permitted'), { code: 'EPERM' });\n });\n\n expect(registry.isAlive(makeEntry())).toBe(true);\n });\n\n it('returns false when the process probe reports ESRCH', () => {\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('no such process'), { code: 'ESRCH' });\n });\n\n expect(registry.isAlive(makeEntry())).toBe(false);\n });\n\n it('returns true when the process probe fails without a definitive error code', () => {\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw new Error('indeterminate probe failure');\n });\n\n expect(registry.isAlive(makeEntry())).toBe(true);\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('preserves entries when liveness probing fails with EPERM', () => {\n registry.register(makeEntry({ name: 'custom-name', tmuxSession: 'tmux-custom' }));\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('operation not permitted'), { code: 'EPERM' });\n });\n\n registry.prune();\n\n expect(registry.lookup('custom-name')).toMatchObject({\n name: 'custom-name',\n tmuxSession: 'tmux-custom',\n });\n });\n\n it('removes entries when liveness probing fails with ESRCH', () => {\n registry.register(makeEntry({ name: 'dead' }));\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('no such process'), { code: 'ESRCH' });\n });\n\n registry.prune();\n\n expect(registry.lookup('dead')).toBeNull();\n });\n\n it('does nothing when file is missing', () => {\n expect(() => registry.prune()).not.toThrow();\n });\n\n it('keeps forced prune available before the passive cadence is due', () => {\n let nowMs = Date.parse('2026-08-14T10:00:00.000Z');\n const clocked = new AgentRegistry(regPath, {\n now: () => new Date(nowMs),\n pruneIntervalMs: 30_000,\n });\n clocked.register(makeEntry({ name: 'forced', pid: process.pid }));\n const alive = vi.spyOn(clocked, 'isAlive').mockReturnValue(true);\n clocked.pruneIfDue();\n alive.mockReturnValue(false);\n nowMs += 1;\n\n clocked.prune();\n\n expect(alive).toHaveBeenCalledTimes(2);\n expect(clocked.lookup('forced')).toBeNull();\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('throws RenameConflictError when the conflicting entry probe fails with EPERM', () => {\n registry.register(makeEntry({ name: 'agent-a', pid: process.pid }));\n registry.register(makeEntry({ name: 'agent-b', pid: process.ppid }));\n vi.spyOn(process, 'kill').mockImplementation(() => {\n throw Object.assign(new Error('operation not permitted'), { code: 'EPERM' });\n });\n\n expect(() => registry.rename('agent-a', 'agent-b')).toThrow(RenameConflictError);\n expect(registry.lookup('agent-b')?.pid).toBe(process.ppid);\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","Database","AgentRegistry","RenameNotFoundError","RenameConflictError","makeEntry","over","name","type","pid","process","tmuxSession","cwd","startedAt","sessionId","sessionFilePath","pinned","describe","tmpDir","regPath","registry","beforeEach","mkdtempSync","join","tmpdir","afterEach","vi","restoreAllMocks","rmSync","recursive","force","it","register","expect","existsSync","replace","toBe","list","ppid","toHaveLength","all","saved","lookup","toBeNull","spyOn","mockImplementation","Object","assign","Error","code","toThrow","registerBatch","other","toMatchObject","togglePin","now","Date","clocked","updatedAt","toISOString","db","readonly","row","prepare","get","close","updated_at","rename","prune","readonlyRegistry","toEqual","legacyEntry","mkdirSync","dirname","writeFileSync","JSON","stringify","entries","legacyRegistry","isAlive","remaining","before","after","not","nowMs","parse","pruneIntervalMs","alive","mockReturnValue","pruneIfDue","toHaveBeenCalledTimes","default","entry"],"mappings":"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"}
@@ -37,6 +37,8 @@ export interface AgentInfo {
37
37
  sessionId: string;
38
38
  /** Timestamp of last activity */
39
39
  lastActive: Date;
40
+ /** Whether the live process is pinned in the agent console */
41
+ pinned?: boolean;
40
42
  /** Path to the session JSONL file on disk */
41
43
  sessionFilePath?: string;
42
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AgentAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/AgentAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/AgentAdapter.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/AgentAdapter.ts"],"sourcesContent":["/**\n * Agent Adapter Interface\n * \n * Defines the contract for detecting and managing different types of AI agents.\n * Each adapter is responsible for detecting agents of a specific type (e.g., claude).\n */\n\n/**\n * Type of AI agent\n */\nexport type AgentType = 'claude' | 'gemini_cli' | 'grok_cli' | 'codex' | 'opencode' | 'copilot' | 'pi' | 'other';\n\n/**\n * Current status of an agent\n */\nexport enum AgentStatus {\n RUNNING = 'running',\n WAITING = 'waiting',\n IDLE = 'idle',\n UNKNOWN = 'unknown'\n}\n\n/**\n * Information about a detected agent\n */\nexport interface AgentInfo {\n /** Project-based name (e.g., \"ai-devkit\" or \"ai-devkit (merry)\") */\n name: string;\n\n /** Type of agent */\n type: AgentType;\n\n /** Current status */\n status: AgentStatus;\n\n /** Last user prompt from history */\n summary: string;\n\n /** Process ID */\n pid: number;\n\n /** Working directory/project path */\n projectPath: string;\n\n /** Session UUID */\n sessionId: string;\n\n /** Timestamp of last activity */\n lastActive: Date;\n\n /** Path to the session JSONL file on disk */\n sessionFilePath?: string;\n}\n\n/**\n * Information about a running process\n */\nexport interface ProcessInfo {\n /** Process ID */\n pid: number;\n\n /** Parent process ID, populated by process discovery when available */\n ppid?: number;\n\n /** Process command */\n command: string;\n\n /** Working directory */\n cwd: string;\n\n /** Terminal TTY (e.g., \"ttys030\") */\n tty: string;\n\n /** Process start time, populated by process enrichment */\n startTime?: Date;\n}\n\n/**\n * A single message in a conversation\n */\nexport interface ConversationMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n timestamp?: string;\n}\n\n/**\n * A historical session discovered on disk (running or not).\n *\n * Used by `listSessions` to surface enough context for a user to identify\n * a session and resume it via the originating tool's resume command.\n */\nexport interface SessionSummary {\n /** Tool that produced this session */\n type: AgentType;\n\n /**\n * ID accepted by the tool's resume command. Adapters MUST pass this\n * through verbatim — no normalization, no encoding/decoding — so it\n * round-trips into `claude --resume <id>` (and equivalents).\n */\n sessionId: string;\n\n /** Working directory the session was started in (best-known value) */\n cwd: string;\n\n /**\n * Trimmed first user message; empty string if none. Adapters apply\n * the same noise-filter their existing parsers use (skip tool_result\n * blocks, request-interruption notices, system-injected skill\n * content). The CLI table renderer substitutes a placeholder for\n * empty values; JSON output keeps the empty string raw.\n */\n firstUserMessage: string;\n\n /** Last activity timestamp (from session content; falls back to file mtime) */\n lastActive: Date;\n\n /** Session start time (from session content; falls back to file birthtime/mtime) */\n startedAt: Date;\n\n /** Absolute path to the session file on disk (debug/diagnostics) */\n sessionFilePath: string;\n}\n\n/**\n * Filters passed by the CLI to {@link AgentAdapter.listSessions}.\n *\n * The CLI is the source of truth for filter defaults and semantics\n * (e.g. cwd defaults to process.cwd(); --all clears it). Adapters apply\n * the values they receive — they don't invent defaults.\n */\nexport interface ListSessionsOptions {\n /**\n * Filter to sessions whose recorded cwd matches this path using strict\n * equality (no prefix/ancestor matching in v1). Undefined = no cwd\n * filter.\n */\n cwd?: string;\n\n /**\n * Filter to a single tool. Enforced by `AgentManager.listSessions`,\n * which skips adapters whose `type` doesn't match. Adapters MAY\n * ignore this field — by the time their `listSessions` runs, the\n * type filter is already satisfied. Undefined = include every\n * registered adapter.\n */\n type?: AgentType;\n}\n\nexport interface AgentDetectionContext {\n /** One enriched process snapshot shared across this manager refresh. */\n readonly processes: readonly ProcessInfo[];\n}\n\n/**\n * Agent Adapter Interface\n *\n * Implementations must provide detection logic for a specific agent type.\n */\nexport interface AgentAdapter {\n /** Type of agent this adapter handles */\n readonly type: AgentType;\n\n /** Executable basenames required for shared process discovery. */\n readonly processNames?: readonly string[];\n\n /**\n * Detect running agents of this type\n * @returns List of detected agents\n */\n detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;\n\n /**\n * Check if this adapter can handle the given process\n * @param processInfo Process information\n * @returns True if this adapter can handle the process\n */\n canHandle(processInfo: ProcessInfo): boolean;\n\n /**\n * Read the full conversation from a session file\n * @param sessionFilePath Path to the session JSONL file\n * @param options.verbose Include tool call/result details\n * @returns Array of conversation messages\n */\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[];\n\n /**\n * Enumerate historical sessions for this tool from disk.\n *\n * Applies `opts.cwd` as a strict-equality filter when set. Returns\n * {@link SessionSummary} entries unsorted; sorting and global filters\n * are handled by `AgentManager` and the CLI.\n *\n * @param opts Filter options computed by the CLI\n * @returns Array of sessions discovered on disk\n */\n listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;\n}\n"],"names":["AgentStatus"],"mappings":"AAAA;;;;;CAKC,GAED;;CAEC,GAGD;;CAEC,GACD,OAAO,IAAA,AAAKA,qCAAAA;;;;;WAAAA;MAKX"}
1
+ {"version":3,"sources":["../../src/adapters/AgentAdapter.ts"],"sourcesContent":["/**\n * Agent Adapter Interface\n * \n * Defines the contract for detecting and managing different types of AI agents.\n * Each adapter is responsible for detecting agents of a specific type (e.g., claude).\n */\n\n/**\n * Type of AI agent\n */\nexport type AgentType = 'claude' | 'gemini_cli' | 'grok_cli' | 'codex' | 'opencode' | 'copilot' | 'pi' | 'other';\n\n/**\n * Current status of an agent\n */\nexport enum AgentStatus {\n RUNNING = 'running',\n WAITING = 'waiting',\n IDLE = 'idle',\n UNKNOWN = 'unknown'\n}\n\n/**\n * Information about a detected agent\n */\nexport interface AgentInfo {\n /** Project-based name (e.g., \"ai-devkit\" or \"ai-devkit (merry)\") */\n name: string;\n\n /** Type of agent */\n type: AgentType;\n\n /** Current status */\n status: AgentStatus;\n\n /** Last user prompt from history */\n summary: string;\n\n /** Process ID */\n pid: number;\n\n /** Working directory/project path */\n projectPath: string;\n\n /** Session UUID */\n sessionId: string;\n\n /** Timestamp of last activity */\n lastActive: Date;\n\n /** Whether the live process is pinned in the agent console */\n pinned?: boolean;\n\n /** Path to the session JSONL file on disk */\n sessionFilePath?: string;\n}\n\n/**\n * Information about a running process\n */\nexport interface ProcessInfo {\n /** Process ID */\n pid: number;\n\n /** Parent process ID, populated by process discovery when available */\n ppid?: number;\n\n /** Process command */\n command: string;\n\n /** Working directory */\n cwd: string;\n\n /** Terminal TTY (e.g., \"ttys030\") */\n tty: string;\n\n /** Process start time, populated by process enrichment */\n startTime?: Date;\n}\n\n/**\n * A single message in a conversation\n */\nexport interface ConversationMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n timestamp?: string;\n}\n\n/**\n * A historical session discovered on disk (running or not).\n *\n * Used by `listSessions` to surface enough context for a user to identify\n * a session and resume it via the originating tool's resume command.\n */\nexport interface SessionSummary {\n /** Tool that produced this session */\n type: AgentType;\n\n /**\n * ID accepted by the tool's resume command. Adapters MUST pass this\n * through verbatim — no normalization, no encoding/decoding — so it\n * round-trips into `claude --resume <id>` (and equivalents).\n */\n sessionId: string;\n\n /** Working directory the session was started in (best-known value) */\n cwd: string;\n\n /**\n * Trimmed first user message; empty string if none. Adapters apply\n * the same noise-filter their existing parsers use (skip tool_result\n * blocks, request-interruption notices, system-injected skill\n * content). The CLI table renderer substitutes a placeholder for\n * empty values; JSON output keeps the empty string raw.\n */\n firstUserMessage: string;\n\n /** Last activity timestamp (from session content; falls back to file mtime) */\n lastActive: Date;\n\n /** Session start time (from session content; falls back to file birthtime/mtime) */\n startedAt: Date;\n\n /** Absolute path to the session file on disk (debug/diagnostics) */\n sessionFilePath: string;\n}\n\n/**\n * Filters passed by the CLI to {@link AgentAdapter.listSessions}.\n *\n * The CLI is the source of truth for filter defaults and semantics\n * (e.g. cwd defaults to process.cwd(); --all clears it). Adapters apply\n * the values they receive — they don't invent defaults.\n */\nexport interface ListSessionsOptions {\n /**\n * Filter to sessions whose recorded cwd matches this path using strict\n * equality (no prefix/ancestor matching in v1). Undefined = no cwd\n * filter.\n */\n cwd?: string;\n\n /**\n * Filter to a single tool. Enforced by `AgentManager.listSessions`,\n * which skips adapters whose `type` doesn't match. Adapters MAY\n * ignore this field — by the time their `listSessions` runs, the\n * type filter is already satisfied. Undefined = include every\n * registered adapter.\n */\n type?: AgentType;\n}\n\nexport interface AgentDetectionContext {\n /** One enriched process snapshot shared across this manager refresh. */\n readonly processes: readonly ProcessInfo[];\n}\n\n/**\n * Agent Adapter Interface\n *\n * Implementations must provide detection logic for a specific agent type.\n */\nexport interface AgentAdapter {\n /** Type of agent this adapter handles */\n readonly type: AgentType;\n\n /** Executable basenames required for shared process discovery. */\n readonly processNames?: readonly string[];\n\n /**\n * Detect running agents of this type\n * @returns List of detected agents\n */\n detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;\n\n /**\n * Check if this adapter can handle the given process\n * @param processInfo Process information\n * @returns True if this adapter can handle the process\n */\n canHandle(processInfo: ProcessInfo): boolean;\n\n /**\n * Read the full conversation from a session file\n * @param sessionFilePath Path to the session JSONL file\n * @param options.verbose Include tool call/result details\n * @returns Array of conversation messages\n */\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[];\n\n /**\n * Enumerate historical sessions for this tool from disk.\n *\n * Applies `opts.cwd` as a strict-equality filter when set. Returns\n * {@link SessionSummary} entries unsorted; sorting and global filters\n * are handled by `AgentManager` and the CLI.\n *\n * @param opts Filter options computed by the CLI\n * @returns Array of sessions discovered on disk\n */\n listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;\n}\n"],"names":["AgentStatus"],"mappings":"AAAA;;;;;CAKC,GAED;;CAEC,GAGD;;CAEC,GACD,OAAO,IAAA,AAAKA,qCAAAA;;;;;WAAAA;MAKX"}
@@ -2,13 +2,14 @@ import Database from 'better-sqlite3';
2
2
  export declare const DEFAULT_AGENT_REGISTRY_DB_PATH: string;
3
3
  export interface DatabaseOptions {
4
4
  dbPath?: string;
5
- verbose?: boolean;
5
+ verbose?: boolean | ((message: string) => void);
6
6
  readonly?: boolean;
7
7
  }
8
8
  export declare function resolveAgentRegistryDbPath(filePath?: string): string;
9
9
  export declare class DatabaseConnection {
10
10
  private db;
11
11
  private readonly dbPath;
12
+ private readonly readonly;
12
13
  constructor(options?: DatabaseOptions);
13
14
  private configure;
14
15
  get instance(): Database.Database;
@@ -1 +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"}
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,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC;IAChD,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;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;gBAEvB,OAAO,GAAE,eAAoB;IAgBzC,OAAO,CAAC,SAAS;IAajB,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"}
@@ -11,19 +11,26 @@ export function resolveAgentRegistryDbPath(filePath) {
11
11
  export class DatabaseConnection {
12
12
  db;
13
13
  dbPath;
14
+ readonly;
14
15
  constructor(options = {}){
15
16
  this.dbPath = options.dbPath ?? DEFAULT_AGENT_REGISTRY_DB_PATH;
17
+ this.readonly = options.readonly ?? false;
16
18
  mkdirSync(dirname(this.dbPath), {
17
19
  recursive: true
18
20
  });
19
21
  this.db = new Database(this.dbPath, {
20
- readonly: options.readonly ?? false,
21
- verbose: options.verbose ? console.log : undefined
22
+ readonly: this.readonly,
23
+ verbose: typeof options.verbose === 'function' ? options.verbose : options.verbose ? console.log : undefined
22
24
  });
23
25
  this.configure();
24
- initializeSchema(this);
26
+ if (!this.readonly) initializeSchema(this);
25
27
  }
26
28
  configure() {
29
+ if (this.readonly) {
30
+ this.db.pragma('foreign_keys = ON');
31
+ this.db.pragma('busy_timeout = 5000');
32
+ return;
33
+ }
27
34
  this.db.pragma('journal_mode = WAL');
28
35
  this.db.pragma('foreign_keys = ON');
29
36
  this.db.pragma('synchronous = NORMAL');
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/database/connection.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { mkdirSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { homedir } from 'os';\nimport { initializeSchema } from './schema.js';\n\nexport const DEFAULT_AGENT_REGISTRY_DB_PATH = join(homedir(), '.ai-devkit', 'agents.db');\n\nexport interface DatabaseOptions {\n dbPath?: string;\n verbose?: boolean;\n readonly?: boolean;\n}\n\nexport function resolveAgentRegistryDbPath(filePath?: string): string {\n if (!filePath) return DEFAULT_AGENT_REGISTRY_DB_PATH;\n return filePath.endsWith('.json') ? filePath.replace(/\\.json$/, '.db') : filePath;\n}\n\nexport class DatabaseConnection {\n private db: Database.Database;\n private readonly dbPath: string;\n\n constructor(options: DatabaseOptions = {}) {\n this.dbPath = options.dbPath ?? DEFAULT_AGENT_REGISTRY_DB_PATH;\n mkdirSync(dirname(this.dbPath), { recursive: true });\n\n this.db = new Database(this.dbPath, {\n readonly: options.readonly ?? false,\n verbose: options.verbose ? console.log : undefined,\n });\n\n this.configure();\n initializeSchema(this);\n }\n\n private configure(): void {\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n this.db.pragma('synchronous = NORMAL');\n this.db.pragma('busy_timeout = 5000');\n this.db.pragma('mmap_size = 268435456');\n }\n\n get instance(): Database.Database {\n return this.db;\n }\n\n get path(): string {\n return this.dbPath;\n }\n\n query<T>(sql: string, params: unknown[] = []): T[] {\n return this.db.prepare(sql).all(...params) as T[];\n }\n\n queryOne<T>(sql: string, params: unknown[] = []): T | undefined {\n return this.db.prepare(sql).get(...params) as T | undefined;\n }\n\n execute(sql: string, params: unknown[] = []): Database.RunResult {\n return this.db.prepare(sql).run(...params);\n }\n\n transaction<T>(fn: () => T): T {\n return this.db.transaction(fn)();\n }\n\n close(): void {\n if (this.db.open) {\n this.db.close();\n }\n }\n}\n"],"names":["Database","mkdirSync","dirname","join","homedir","initializeSchema","DEFAULT_AGENT_REGISTRY_DB_PATH","resolveAgentRegistryDbPath","filePath","endsWith","replace","DatabaseConnection","db","dbPath","options","recursive","readonly","verbose","console","log","undefined","configure","pragma","instance","path","query","sql","params","prepare","all","queryOne","get","execute","run","transaction","fn","close","open"],"mappings":"AAAA,OAAOA,cAAc,iBAAiB;AACtC,SAASC,SAAS,QAAQ,KAAK;AAC/B,SAASC,OAAO,EAAEC,IAAI,QAAQ,OAAO;AACrC,SAASC,OAAO,QAAQ,KAAK;AAC7B,SAASC,gBAAgB,QAAQ,cAAc;AAE/C,OAAO,MAAMC,iCAAiCH,KAAKC,WAAW,cAAc,aAAa;AAQzF,OAAO,SAASG,2BAA2BC,QAAiB;IACxD,IAAI,CAACA,UAAU,OAAOF;IACtB,OAAOE,SAASC,QAAQ,CAAC,WAAWD,SAASE,OAAO,CAAC,WAAW,SAASF;AAC7E;AAEA,OAAO,MAAMG;IACDC,GAAsB;IACbC,OAAe;IAEhC,YAAYC,UAA2B,CAAC,CAAC,CAAE;QACvC,IAAI,CAACD,MAAM,GAAGC,QAAQD,MAAM,IAAIP;QAChCL,UAAUC,QAAQ,IAAI,CAACW,MAAM,GAAG;YAAEE,WAAW;QAAK;QAElD,IAAI,CAACH,EAAE,GAAG,IAAIZ,SAAS,IAAI,CAACa,MAAM,EAAE;YAChCG,UAAUF,QAAQE,QAAQ,IAAI;YAC9BC,SAASH,QAAQG,OAAO,GAAGC,QAAQC,GAAG,GAAGC;QAC7C;QAEA,IAAI,CAACC,SAAS;QACdhB,iBAAiB,IAAI;IACzB;IAEQgB,YAAkB;QACtB,IAAI,CAACT,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;IACnB;IAEA,IAAIC,WAA8B;QAC9B,OAAO,IAAI,CAACX,EAAE;IAClB;IAEA,IAAIY,OAAe;QACf,OAAO,IAAI,CAACX,MAAM;IACtB;IAEAY,MAASC,GAAW,EAAEC,SAAoB,EAAE,EAAO;QAC/C,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKG,GAAG,IAAIF;IACvC;IAEAG,SAAYJ,GAAW,EAAEC,SAAoB,EAAE,EAAiB;QAC5D,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKK,GAAG,IAAIJ;IACvC;IAEAK,QAAQN,GAAW,EAAEC,SAAoB,EAAE,EAAsB;QAC7D,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKO,GAAG,IAAIN;IACvC;IAEAO,YAAeC,EAAW,EAAK;QAC3B,OAAO,IAAI,CAACvB,EAAE,CAACsB,WAAW,CAACC;IAC/B;IAEAC,QAAc;QACV,IAAI,IAAI,CAACxB,EAAE,CAACyB,IAAI,EAAE;YACd,IAAI,CAACzB,EAAE,CAACwB,KAAK;QACjB;IACJ;AACJ"}
1
+ {"version":3,"sources":["../../src/database/connection.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { mkdirSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { homedir } from 'os';\nimport { initializeSchema } from './schema.js';\n\nexport const DEFAULT_AGENT_REGISTRY_DB_PATH = join(homedir(), '.ai-devkit', 'agents.db');\n\nexport interface DatabaseOptions {\n dbPath?: string;\n verbose?: boolean | ((message: string) => void);\n readonly?: boolean;\n}\n\nexport function resolveAgentRegistryDbPath(filePath?: string): string {\n if (!filePath) return DEFAULT_AGENT_REGISTRY_DB_PATH;\n return filePath.endsWith('.json') ? filePath.replace(/\\.json$/, '.db') : filePath;\n}\n\nexport class DatabaseConnection {\n private db: Database.Database;\n private readonly dbPath: string;\n private readonly readonly: boolean;\n\n constructor(options: DatabaseOptions = {}) {\n this.dbPath = options.dbPath ?? DEFAULT_AGENT_REGISTRY_DB_PATH;\n this.readonly = options.readonly ?? false;\n mkdirSync(dirname(this.dbPath), { recursive: true });\n\n this.db = new Database(this.dbPath, {\n readonly: this.readonly,\n verbose: typeof options.verbose === 'function'\n ? options.verbose\n : options.verbose ? console.log : undefined,\n });\n\n this.configure();\n if (!this.readonly) initializeSchema(this);\n }\n\n private configure(): void {\n if (this.readonly) {\n this.db.pragma('foreign_keys = ON');\n this.db.pragma('busy_timeout = 5000');\n return;\n }\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n this.db.pragma('synchronous = NORMAL');\n this.db.pragma('busy_timeout = 5000');\n this.db.pragma('mmap_size = 268435456');\n }\n\n get instance(): Database.Database {\n return this.db;\n }\n\n get path(): string {\n return this.dbPath;\n }\n\n query<T>(sql: string, params: unknown[] = []): T[] {\n return this.db.prepare(sql).all(...params) as T[];\n }\n\n queryOne<T>(sql: string, params: unknown[] = []): T | undefined {\n return this.db.prepare(sql).get(...params) as T | undefined;\n }\n\n execute(sql: string, params: unknown[] = []): Database.RunResult {\n return this.db.prepare(sql).run(...params);\n }\n\n transaction<T>(fn: () => T): T {\n return this.db.transaction(fn)();\n }\n\n close(): void {\n if (this.db.open) {\n this.db.close();\n }\n }\n}\n"],"names":["Database","mkdirSync","dirname","join","homedir","initializeSchema","DEFAULT_AGENT_REGISTRY_DB_PATH","resolveAgentRegistryDbPath","filePath","endsWith","replace","DatabaseConnection","db","dbPath","readonly","options","recursive","verbose","console","log","undefined","configure","pragma","instance","path","query","sql","params","prepare","all","queryOne","get","execute","run","transaction","fn","close","open"],"mappings":"AAAA,OAAOA,cAAc,iBAAiB;AACtC,SAASC,SAAS,QAAQ,KAAK;AAC/B,SAASC,OAAO,EAAEC,IAAI,QAAQ,OAAO;AACrC,SAASC,OAAO,QAAQ,KAAK;AAC7B,SAASC,gBAAgB,QAAQ,cAAc;AAE/C,OAAO,MAAMC,iCAAiCH,KAAKC,WAAW,cAAc,aAAa;AAQzF,OAAO,SAASG,2BAA2BC,QAAiB;IACxD,IAAI,CAACA,UAAU,OAAOF;IACtB,OAAOE,SAASC,QAAQ,CAAC,WAAWD,SAASE,OAAO,CAAC,WAAW,SAASF;AAC7E;AAEA,OAAO,MAAMG;IACDC,GAAsB;IACbC,OAAe;IACfC,SAAkB;IAEnC,YAAYC,UAA2B,CAAC,CAAC,CAAE;QACvC,IAAI,CAACF,MAAM,GAAGE,QAAQF,MAAM,IAAIP;QAChC,IAAI,CAACQ,QAAQ,GAAGC,QAAQD,QAAQ,IAAI;QACpCb,UAAUC,QAAQ,IAAI,CAACW,MAAM,GAAG;YAAEG,WAAW;QAAK;QAElD,IAAI,CAACJ,EAAE,GAAG,IAAIZ,SAAS,IAAI,CAACa,MAAM,EAAE;YAChCC,UAAU,IAAI,CAACA,QAAQ;YACvBG,SAAS,OAAOF,QAAQE,OAAO,KAAK,aAC9BF,QAAQE,OAAO,GACfF,QAAQE,OAAO,GAAGC,QAAQC,GAAG,GAAGC;QAC1C;QAEA,IAAI,CAACC,SAAS;QACd,IAAI,CAAC,IAAI,CAACP,QAAQ,EAAET,iBAAiB,IAAI;IAC7C;IAEQgB,YAAkB;QACtB,IAAI,IAAI,CAACP,QAAQ,EAAE;YACf,IAAI,CAACF,EAAE,CAACU,MAAM,CAAC;YACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;YACf;QACJ;QACA,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;QACf,IAAI,CAACV,EAAE,CAACU,MAAM,CAAC;IACnB;IAEA,IAAIC,WAA8B;QAC9B,OAAO,IAAI,CAACX,EAAE;IAClB;IAEA,IAAIY,OAAe;QACf,OAAO,IAAI,CAACX,MAAM;IACtB;IAEAY,MAASC,GAAW,EAAEC,SAAoB,EAAE,EAAO;QAC/C,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKG,GAAG,IAAIF;IACvC;IAEAG,SAAYJ,GAAW,EAAEC,SAAoB,EAAE,EAAiB;QAC5D,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKK,GAAG,IAAIJ;IACvC;IAEAK,QAAQN,GAAW,EAAEC,SAAoB,EAAE,EAAsB;QAC7D,OAAO,IAAI,CAACf,EAAE,CAACgB,OAAO,CAACF,KAAKO,GAAG,IAAIN;IACvC;IAEAO,YAAeC,EAAW,EAAK;QAC3B,OAAO,IAAI,CAACvB,EAAE,CAACsB,WAAW,CAACC;IAC/B;IAEAC,QAAc;QACV,IAAI,IAAI,CAACxB,EAAE,CAACyB,IAAI,EAAE;YACd,IAAI,CAACzB,EAAE,CAACwB,KAAK;QACjB;IACJ;AACJ"}
@@ -0,0 +1 @@
1
+ ALTER TABLE agents ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { AgentManager } from './AgentManager.js';
1
+ export { AgentManager, AgentNotRunningError } from './AgentManager.js';
2
2
  export { ClaudeCodeAdapter } from './adapters/ClaudeCodeAdapter.js';
3
3
  export { CodexAdapter } from './adapters/CodexAdapter.js';
4
4
  export { CopilotAdapter } from './adapters/CopilotAdapter.js';
@@ -16,7 +16,7 @@ export { captureProcessSnapshot, executableBasename, filterByProcessNames } from
16
16
  export type { AgentSortKey } from './utils/sortAgents.js';
17
17
  export type { ListAgentsOptions } from './AgentManager.js';
18
18
  export { AgentRegistry, RenameNotFoundError, RenameConflictError } from './utils/AgentRegistry.js';
19
- export type { RegistryEntry } from './utils/AgentRegistry.js';
19
+ export type { AgentRegistryOptions, RegistryEntry } from './utils/AgentRegistry.js';
20
20
  export { TmuxManager } from './terminal/TmuxManager.js';
21
21
  export { AGENTS } from './utils/agents.js';
22
22
  export type { AgentConfig, StartableAgentType } from './utils/agents.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,YAAY,EACR,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACxF,YAAY,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACtG,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnG,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE3G,OAAO,EACH,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,2BAA2B,EAC3B,gBAAgB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACR,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EACR,qBAAqB,EACrB,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EACR,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,YAAY,EACR,8BAA8B,EAC9B,qBAAqB,GACxB,MAAM,oCAAoC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,YAAY,EACR,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACxF,YAAY,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACtG,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnG,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE3G,OAAO,EACH,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,2BAA2B,EAC3B,gBAAgB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACR,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EACR,qBAAqB,EACrB,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EACR,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,YAAY,EACR,8BAA8B,EAC9B,qBAAqB,GACxB,MAAM,oCAAoC,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { AgentManager } from './AgentManager.js';
1
+ export { AgentManager, AgentNotRunningError } from './AgentManager.js';
2
2
  export { ClaudeCodeAdapter } from './adapters/ClaudeCodeAdapter.js';
3
3
  export { CodexAdapter } from './adapters/CodexAdapter.js';
4
4
  export { CopilotAdapter } from './adapters/CopilotAdapter.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { AgentManager } from './AgentManager.js';\n\nexport { ClaudeCodeAdapter } from './adapters/ClaudeCodeAdapter.js';\nexport { CodexAdapter } from './adapters/CodexAdapter.js';\nexport { CopilotAdapter } from './adapters/CopilotAdapter.js';\nexport { GeminiCliAdapter } from './adapters/GeminiCliAdapter.js';\nexport { GrokCliAdapter } from './adapters/GrokCliAdapter.js';\nexport { OpenCodeAdapter } from './adapters/OpenCodeAdapter.js';\nexport { PiAdapter } from './adapters/PiAdapter.js';\nexport { AgentStatus } from './adapters/AgentAdapter.js';\nexport type {\n AgentAdapter,\n AgentType,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from './adapters/AgentAdapter.js';\n\nexport { TerminalFocusManager, TerminalType } from './terminal/TerminalFocusManager.js';\nexport type { TerminalLocation } from './terminal/TerminalFocusManager.js';\nexport { TtyWriter } from './terminal/TtyWriter.js';\n\nexport { getProcessTty } from './utils/process.js';\nexport { captureProcessSnapshot, executableBasename, filterByProcessNames } from './utils/process.js';\nexport type { AgentSortKey } from './utils/sortAgents.js';\nexport type { ListAgentsOptions } from './AgentManager.js';\n\nexport { AgentRegistry, RenameNotFoundError, RenameConflictError } from './utils/AgentRegistry.js';\nexport type { RegistryEntry } from './utils/AgentRegistry.js';\nexport { TmuxManager } from './terminal/TmuxManager.js';\nexport { AGENTS } from './utils/agents.js';\nexport type { AgentConfig, StartableAgentType } from './utils/agents.js';\n\nexport type { AgentRequest } from './utils/agent-requests.js';\nexport { getAgentRequestPath, readLatestAgentRequest, writeAgentRequest } from './utils/agent-requests.js';\n\nexport {\n PrintAgentError,\n PrintAgentBusyError,\n PrintAgentNotFoundError,\n PrintAgentStoreError,\n PrintAgentNameConflictError,\n ClaudePrintError,\n} from './print/PrintAgent.js';\nexport type {\n PrintAgent,\n PrintAgentState,\n PrintSessionHealth,\n PrintRunStatus,\n PrintActiveRun,\n PrintLastResult,\n ProcessIdentity,\n} from './print/PrintAgent.js';\nexport { PrintAgentStore } from './print/PrintAgentStore.js';\nexport { LocalProcessInspector } from './print/PrintAgentStore.js';\nexport type {\n CreatePrintAgentInput,\n PrintAgentStoreOptions,\n ProcessInspector,\n PrintRunCompletion,\n} from './print/PrintAgentStore.js';\nexport { ClaudeCliProbe } from './print/ClaudeCliProbe.js';\nexport type { ClaudeCliProbeOptions } from './print/ClaudeCliProbe.js';\nexport { ClaudePrintRunner } from './print/ClaudePrintRunner.js';\nexport type {\n ClaudePrintRunnerOptions,\n ClaudePrintRunRequest,\n ClaudePrintRunResult,\n} from './print/ClaudePrintRunner.js';\nexport { ClaudePrintAgentService } from './print/ClaudePrintAgentService.js';\nexport type {\n ClaudePrintAgentServiceOptions,\n ClaudePrintSendResult,\n} from './print/ClaudePrintAgentService.js';\n"],"names":["AgentManager","ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","OpenCodeAdapter","PiAdapter","AgentStatus","TerminalFocusManager","TerminalType","TtyWriter","getProcessTty","captureProcessSnapshot","executableBasename","filterByProcessNames","AgentRegistry","RenameNotFoundError","RenameConflictError","TmuxManager","AGENTS","getAgentRequestPath","readLatestAgentRequest","writeAgentRequest","PrintAgentError","PrintAgentBusyError","PrintAgentNotFoundError","PrintAgentStoreError","PrintAgentNameConflictError","ClaudePrintError","PrintAgentStore","LocalProcessInspector","ClaudeCliProbe","ClaudePrintRunner","ClaudePrintAgentService"],"mappings":"AAAA,SAASA,YAAY,QAAQ,oBAAoB;AAEjD,SAASC,iBAAiB,QAAQ,kCAAkC;AACpE,SAASC,YAAY,QAAQ,6BAA6B;AAC1D,SAASC,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,gBAAgB,QAAQ,iCAAiC;AAClE,SAASC,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,eAAe,QAAQ,gCAAgC;AAChE,SAASC,SAAS,QAAQ,0BAA0B;AACpD,SAASC,WAAW,QAAQ,6BAA6B;AAYzD,SAASC,oBAAoB,EAAEC,YAAY,QAAQ,qCAAqC;AAExF,SAASC,SAAS,QAAQ,0BAA0B;AAEpD,SAASC,aAAa,QAAQ,qBAAqB;AACnD,SAASC,sBAAsB,EAAEC,kBAAkB,EAAEC,oBAAoB,QAAQ,qBAAqB;AAItG,SAASC,aAAa,EAAEC,mBAAmB,EAAEC,mBAAmB,QAAQ,2BAA2B;AAEnG,SAASC,WAAW,QAAQ,4BAA4B;AACxD,SAASC,MAAM,QAAQ,oBAAoB;AAI3C,SAASC,mBAAmB,EAAEC,sBAAsB,EAAEC,iBAAiB,QAAQ,4BAA4B;AAE3G,SACIC,eAAe,EACfC,mBAAmB,EACnBC,uBAAuB,EACvBC,oBAAoB,EACpBC,2BAA2B,EAC3BC,gBAAgB,QACb,wBAAwB;AAU/B,SAASC,eAAe,QAAQ,6BAA6B;AAC7D,SAASC,qBAAqB,QAAQ,6BAA6B;AAOnE,SAASC,cAAc,QAAQ,4BAA4B;AAE3D,SAASC,iBAAiB,QAAQ,+BAA+B;AAMjE,SAASC,uBAAuB,QAAQ,qCAAqC"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { AgentManager, AgentNotRunningError } from './AgentManager.js';\n\nexport { ClaudeCodeAdapter } from './adapters/ClaudeCodeAdapter.js';\nexport { CodexAdapter } from './adapters/CodexAdapter.js';\nexport { CopilotAdapter } from './adapters/CopilotAdapter.js';\nexport { GeminiCliAdapter } from './adapters/GeminiCliAdapter.js';\nexport { GrokCliAdapter } from './adapters/GrokCliAdapter.js';\nexport { OpenCodeAdapter } from './adapters/OpenCodeAdapter.js';\nexport { PiAdapter } from './adapters/PiAdapter.js';\nexport { AgentStatus } from './adapters/AgentAdapter.js';\nexport type {\n AgentAdapter,\n AgentType,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from './adapters/AgentAdapter.js';\n\nexport { TerminalFocusManager, TerminalType } from './terminal/TerminalFocusManager.js';\nexport type { TerminalLocation } from './terminal/TerminalFocusManager.js';\nexport { TtyWriter } from './terminal/TtyWriter.js';\n\nexport { getProcessTty } from './utils/process.js';\nexport { captureProcessSnapshot, executableBasename, filterByProcessNames } from './utils/process.js';\nexport type { AgentSortKey } from './utils/sortAgents.js';\nexport type { ListAgentsOptions } from './AgentManager.js';\n\nexport { AgentRegistry, RenameNotFoundError, RenameConflictError } from './utils/AgentRegistry.js';\nexport type { AgentRegistryOptions, RegistryEntry } from './utils/AgentRegistry.js';\nexport { TmuxManager } from './terminal/TmuxManager.js';\nexport { AGENTS } from './utils/agents.js';\nexport type { AgentConfig, StartableAgentType } from './utils/agents.js';\n\nexport type { AgentRequest } from './utils/agent-requests.js';\nexport { getAgentRequestPath, readLatestAgentRequest, writeAgentRequest } from './utils/agent-requests.js';\n\nexport {\n PrintAgentError,\n PrintAgentBusyError,\n PrintAgentNotFoundError,\n PrintAgentStoreError,\n PrintAgentNameConflictError,\n ClaudePrintError,\n} from './print/PrintAgent.js';\nexport type {\n PrintAgent,\n PrintAgentState,\n PrintSessionHealth,\n PrintRunStatus,\n PrintActiveRun,\n PrintLastResult,\n ProcessIdentity,\n} from './print/PrintAgent.js';\nexport { PrintAgentStore } from './print/PrintAgentStore.js';\nexport { LocalProcessInspector } from './print/PrintAgentStore.js';\nexport type {\n CreatePrintAgentInput,\n PrintAgentStoreOptions,\n ProcessInspector,\n PrintRunCompletion,\n} from './print/PrintAgentStore.js';\nexport { ClaudeCliProbe } from './print/ClaudeCliProbe.js';\nexport type { ClaudeCliProbeOptions } from './print/ClaudeCliProbe.js';\nexport { ClaudePrintRunner } from './print/ClaudePrintRunner.js';\nexport type {\n ClaudePrintRunnerOptions,\n ClaudePrintRunRequest,\n ClaudePrintRunResult,\n} from './print/ClaudePrintRunner.js';\nexport { ClaudePrintAgentService } from './print/ClaudePrintAgentService.js';\nexport type {\n ClaudePrintAgentServiceOptions,\n ClaudePrintSendResult,\n} from './print/ClaudePrintAgentService.js';\n"],"names":["AgentManager","AgentNotRunningError","ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","OpenCodeAdapter","PiAdapter","AgentStatus","TerminalFocusManager","TerminalType","TtyWriter","getProcessTty","captureProcessSnapshot","executableBasename","filterByProcessNames","AgentRegistry","RenameNotFoundError","RenameConflictError","TmuxManager","AGENTS","getAgentRequestPath","readLatestAgentRequest","writeAgentRequest","PrintAgentError","PrintAgentBusyError","PrintAgentNotFoundError","PrintAgentStoreError","PrintAgentNameConflictError","ClaudePrintError","PrintAgentStore","LocalProcessInspector","ClaudeCliProbe","ClaudePrintRunner","ClaudePrintAgentService"],"mappings":"AAAA,SAASA,YAAY,EAAEC,oBAAoB,QAAQ,oBAAoB;AAEvE,SAASC,iBAAiB,QAAQ,kCAAkC;AACpE,SAASC,YAAY,QAAQ,6BAA6B;AAC1D,SAASC,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,gBAAgB,QAAQ,iCAAiC;AAClE,SAASC,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,eAAe,QAAQ,gCAAgC;AAChE,SAASC,SAAS,QAAQ,0BAA0B;AACpD,SAASC,WAAW,QAAQ,6BAA6B;AAYzD,SAASC,oBAAoB,EAAEC,YAAY,QAAQ,qCAAqC;AAExF,SAASC,SAAS,QAAQ,0BAA0B;AAEpD,SAASC,aAAa,QAAQ,qBAAqB;AACnD,SAASC,sBAAsB,EAAEC,kBAAkB,EAAEC,oBAAoB,QAAQ,qBAAqB;AAItG,SAASC,aAAa,EAAEC,mBAAmB,EAAEC,mBAAmB,QAAQ,2BAA2B;AAEnG,SAASC,WAAW,QAAQ,4BAA4B;AACxD,SAASC,MAAM,QAAQ,oBAAoB;AAI3C,SAASC,mBAAmB,EAAEC,sBAAsB,EAAEC,iBAAiB,QAAQ,4BAA4B;AAE3G,SACIC,eAAe,EACfC,mBAAmB,EACnBC,uBAAuB,EACvBC,oBAAoB,EACpBC,2BAA2B,EAC3BC,gBAAgB,QACb,wBAAwB;AAU/B,SAASC,eAAe,QAAQ,6BAA6B;AAC7D,SAASC,qBAAqB,QAAQ,6BAA6B;AAOnE,SAASC,cAAc,QAAQ,4BAA4B;AAE3D,SAASC,iBAAiB,QAAQ,+BAA+B;AAMjE,SAASC,uBAAuB,QAAQ,qCAAqC"}
@@ -16,23 +16,41 @@ export interface RegistryEntry {
16
16
  startedAt: string;
17
17
  sessionId: string;
18
18
  sessionFilePath: string;
19
+ pinned: boolean;
20
+ updatedAt?: string;
21
+ }
22
+ export interface AgentRegistryOptions {
23
+ now?: () => Date;
24
+ pruneIntervalMs?: number;
25
+ onDatabaseOperation?: (sql: string) => void;
26
+ readonly?: boolean;
19
27
  }
20
28
  export declare class AgentRegistry {
21
29
  private db;
22
- constructor(filePath?: string);
30
+ private readonly now;
31
+ private readonly pruneIntervalMs;
32
+ private readonly readonly;
33
+ private lastPrunedAt;
34
+ constructor(filePath?: string, options?: AgentRegistryOptions);
23
35
  static default(): AgentRegistry;
24
36
  private rowToEntry;
25
37
  private mergeEntry;
26
38
  private findByIdentity;
27
39
  private findByName;
40
+ private findPidConflicts;
41
+ private entriesEqual;
28
42
  private deleteNameConflict;
29
43
  private insertOrUpdate;
44
+ private needsWrite;
30
45
  private save;
31
46
  isAlive(entry: RegistryEntry): boolean;
47
+ private pruneAt;
32
48
  prune(): void;
49
+ pruneIfDue(): void;
33
50
  register(entry: RegistryEntry): void;
34
51
  registerBatch(entries: RegistryEntry[]): void;
35
52
  rename(currentName: string, newName: string): void;
53
+ togglePin(type: AgentType, pid: number): boolean | null;
36
54
  lookup(name: string): RegistryEntry | null;
37
55
  list(): RegistryEntry[];
38
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AgentRegistry.d.ts","sourceRoot":"","sources":["../../src/utils/AgentRegistry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAM7D,qBAAa,mBAAoB,SAAQ,KAAK;IACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CAC3B;AAkBD,qBAAa,aAAa;IACtB,OAAO,CAAC,EAAE,CAAqB;gBAEnB,QAAQ,GAAE,MAA8B;IAIpD,MAAM,CAAC,OAAO,IAAI,aAAa;IAO/B,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IAStC,KAAK,IAAI,IAAI;IAUb,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAIpC,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI;IAU7C,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAqBlD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAI1C,IAAI,IAAI,aAAa,EAAE;CAI1B"}
1
+ {"version":3,"file":"AgentRegistry.d.ts","sourceRoot":"","sources":["../../src/utils/AgentRegistry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAM7D,qBAAa,mBAAoB,SAAQ,KAAK;IACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAoBD,MAAM,WAAW,oBAAoB;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,EAAE,CAAqB;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,YAAY,CAAqB;gBAE7B,QAAQ,GAAE,MAA8B,EAAE,OAAO,GAAE,oBAAyB;IAWxF,MAAM,CAAC,OAAO,IAAI,aAAa;IAO/B,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,IAAI;IAeZ,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IAYtC,OAAO,CAAC,OAAO;IAaf,KAAK,IAAI,IAAI;IAIb,UAAU,IAAI,IAAI;IAOlB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAIpC,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI;IAU7C,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAqBlD,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAYvD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAI1C,IAAI,IAAI,aAAa,EAAE;CAI1B"}
@@ -16,12 +16,22 @@ export class RenameConflictError extends Error {
16
16
  }
17
17
  }
18
18
  const DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.ai-devkit', 'agents.json');
19
+ const DEFAULT_PRUNE_INTERVAL_MS = 30_000;
19
20
  let defaultInstance = null;
20
21
  export class AgentRegistry {
21
22
  db;
22
- constructor(filePath = DEFAULT_REGISTRY_PATH){
23
+ now;
24
+ pruneIntervalMs;
25
+ readonly;
26
+ lastPrunedAt;
27
+ constructor(filePath = DEFAULT_REGISTRY_PATH, options = {}){
28
+ this.now = options.now ?? (()=>new Date());
29
+ this.pruneIntervalMs = options.pruneIntervalMs ?? DEFAULT_PRUNE_INTERVAL_MS;
30
+ this.readonly = options.readonly ?? false;
23
31
  this.db = new DatabaseConnection({
24
- dbPath: resolveAgentRegistryDbPath(filePath)
32
+ dbPath: resolveAgentRegistryDbPath(filePath),
33
+ verbose: options.onDatabaseOperation,
34
+ readonly: this.readonly
25
35
  });
26
36
  }
27
37
  static default() {
@@ -39,7 +49,9 @@ export class AgentRegistry {
39
49
  cwd: row.cwd,
40
50
  startedAt: row.started_at,
41
51
  sessionId: row.session_id,
42
- sessionFilePath: row.session_file_path
52
+ sessionFilePath: row.session_file_path,
53
+ pinned: row.pinned !== 0,
54
+ updatedAt: row.updated_at
43
55
  };
44
56
  }
45
57
  mergeEntry(incoming, existing) {
@@ -68,6 +80,15 @@ export class AgentRegistry {
68
80
  ]);
69
81
  return row ? this.rowToEntry(row) : undefined;
70
82
  }
83
+ findPidConflicts(type, pid) {
84
+ return this.db.query('SELECT * FROM agents WHERE pid = ? AND type <> ?', [
85
+ pid,
86
+ type
87
+ ]).map((row)=>this.rowToEntry(row));
88
+ }
89
+ entriesEqual(left, right) {
90
+ return left.name === right.name && left.type === right.type && left.pid === right.pid && left.tmuxSession === right.tmuxSession && left.cwd === right.cwd && left.startedAt === right.startedAt && left.sessionId === right.sessionId && left.sessionFilePath === right.sessionFilePath;
91
+ }
71
92
  deleteNameConflict(name, type, pid) {
72
93
  const conflict = this.findByName(name);
73
94
  if (!conflict) return;
@@ -97,32 +118,61 @@ export class AgentRegistry {
97
118
  updated_at = excluded.updated_at
98
119
  `).run({
99
120
  ...entry,
100
- updatedAt: new Date().toISOString()
121
+ updatedAt: this.now().toISOString()
101
122
  });
102
123
  }
103
- save(entry) {
104
- this.deleteNameConflict(entry.name, entry.type, entry.pid);
105
- this.insertOrUpdate(entry);
124
+ needsWrite(incoming) {
125
+ const existing = this.findByIdentity(incoming.type, incoming.pid);
126
+ const merged = this.mergeEntry(incoming, existing);
127
+ return !existing || !this.entriesEqual(merged, existing) || this.findPidConflicts(incoming.type, incoming.pid).length > 0;
128
+ }
129
+ save(incoming) {
130
+ const existing = this.findByIdentity(incoming.type, incoming.pid);
131
+ const merged = this.mergeEntry(incoming, existing);
132
+ const pidConflicts = this.findPidConflicts(incoming.type, incoming.pid);
133
+ if (existing && this.entriesEqual(merged, existing) && pidConflicts.length === 0) return;
134
+ for (const conflict of pidConflicts){
135
+ this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [
136
+ conflict.type,
137
+ conflict.pid
138
+ ]);
139
+ }
140
+ if (existing && this.entriesEqual(merged, existing)) return;
141
+ this.deleteNameConflict(merged.name, merged.type, merged.pid);
142
+ this.insertOrUpdate(merged);
106
143
  }
107
144
  isAlive(entry) {
108
145
  try {
109
146
  process.kill(entry.pid, 0);
110
147
  return true;
111
- } catch {
112
- return false;
148
+ } catch (error) {
149
+ const code = error && typeof error === 'object' && 'code' in error ? error.code : undefined;
150
+ return code !== 'ESRCH';
113
151
  }
114
152
  }
115
- prune() {
153
+ pruneAt(nowMs) {
116
154
  const entries = this.list();
117
155
  const stale = entries.filter((e)=>!this.isAlive(e));
118
- this.db.transaction(()=>{
119
- for (const entry of stale){
120
- this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [
121
- entry.type,
122
- entry.pid
123
- ]);
124
- }
125
- });
156
+ if (stale.length > 0) {
157
+ this.db.transaction(()=>{
158
+ for (const entry of stale){
159
+ this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [
160
+ entry.type,
161
+ entry.pid
162
+ ]);
163
+ }
164
+ });
165
+ }
166
+ this.lastPrunedAt = nowMs;
167
+ }
168
+ prune() {
169
+ this.pruneAt(this.now().getTime());
170
+ }
171
+ pruneIfDue() {
172
+ const nowMs = this.now().getTime();
173
+ const elapsed = this.lastPrunedAt === undefined ? undefined : nowMs - this.lastPrunedAt;
174
+ if (elapsed !== undefined && elapsed >= 0 && elapsed < this.pruneIntervalMs) return;
175
+ this.pruneAt(nowMs);
126
176
  }
127
177
  register(entry) {
128
178
  this.registerBatch([
@@ -131,10 +181,10 @@ export class AgentRegistry {
131
181
  }
132
182
  registerBatch(entries) {
133
183
  if (entries.length === 0) return;
184
+ if (!entries.some((entry)=>this.needsWrite(entry))) return;
134
185
  this.db.transaction(()=>{
135
186
  for (const incoming of entries){
136
- const existing = this.findByIdentity(incoming.type, incoming.pid);
137
- this.save(this.mergeEntry(incoming, existing));
187
+ this.save(incoming);
138
188
  }
139
189
  });
140
190
  }
@@ -156,12 +206,24 @@ export class AgentRegistry {
156
206
  }
157
207
  this.db.execute('UPDATE agents SET name = ?, updated_at = ? WHERE type = ? AND pid = ?', [
158
208
  newName,
159
- new Date().toISOString(),
209
+ this.now().toISOString(),
160
210
  existing.type,
161
211
  existing.pid
162
212
  ]);
163
213
  });
164
214
  }
215
+ togglePin(type, pid) {
216
+ if (this.readonly) {
217
+ throw new Error('Agent registry is readonly; cannot toggle pin.');
218
+ }
219
+ const result = this.db.execute('UPDATE agents SET pinned = NOT pinned, updated_at = ? WHERE type = ? AND pid = ?', [
220
+ this.now().toISOString(),
221
+ type,
222
+ pid
223
+ ]);
224
+ if (result.changes === 0) return null;
225
+ return this.findByIdentity(type, pid)?.pinned ?? null;
226
+ }
165
227
  lookup(name) {
166
228
  return this.findByName(name) ?? null;
167
229
  }