@ai-devkit/agent-manager 0.26.4 → 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,6 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
+ import Database from 'better-sqlite3';
4
5
  import { AgentRegistry, RenameNotFoundError, RenameConflictError } from '../../utils/AgentRegistry.js';
5
6
  function makeEntry(over = {}) {
6
7
  return {
@@ -12,6 +13,7 @@ function makeEntry(over = {}) {
12
13
  startedAt: '2026-05-30T00:00:00.000Z',
13
14
  sessionId: 'sid-1',
14
15
  sessionFilePath: '/tmp/session.jsonl',
16
+ pinned: false,
15
17
  ...over
16
18
  };
17
19
  }
@@ -228,6 +230,71 @@ describe('AgentRegistry', ()=>{
228
230
  expect(registry.lookup('a')?.name).toBe('a');
229
231
  });
230
232
  });
233
+ describe('pinning', ()=>{
234
+ it('defaults new rows to unpinned and toggles the persisted state', ()=>{
235
+ registry.register(makeEntry());
236
+ expect(registry.lookup('agent1')?.pinned).toBe(false);
237
+ expect(registry.togglePin('claude', process.pid)).toBe(true);
238
+ expect(registry.lookup('agent1')?.pinned).toBe(true);
239
+ expect(registry.togglePin('claude', process.pid)).toBe(false);
240
+ expect(registry.lookup('agent1')?.pinned).toBe(false);
241
+ });
242
+ it('updates existing recency when toggled', ()=>{
243
+ let now = new Date('2026-08-16T10:00:00.000Z');
244
+ const clocked = new AgentRegistry(regPath, {
245
+ now: ()=>now
246
+ });
247
+ clocked.register(makeEntry());
248
+ now = new Date('2026-08-16T10:01:00.000Z');
249
+ clocked.togglePin('claude', process.pid);
250
+ expect(clocked.lookup('agent1')?.updatedAt).toBe(now.toISOString());
251
+ const db = new Database(regPath.replace(/\.json$/, '.db'), {
252
+ readonly: true
253
+ });
254
+ const row = db.prepare('SELECT updated_at FROM agents WHERE type = ? AND pid = ?').get('claude', process.pid);
255
+ db.close();
256
+ expect(row.updated_at).toBe(now.toISOString());
257
+ });
258
+ it('returns null when the process row has disappeared', ()=>{
259
+ expect(registry.togglePin('claude', 999999)).toBeNull();
260
+ });
261
+ it('preserves a pin when poll registration updates the row', ()=>{
262
+ registry.register(makeEntry({
263
+ sessionId: 'before'
264
+ }));
265
+ registry.togglePin('claude', process.pid);
266
+ registry.register(makeEntry({
267
+ sessionId: 'after'
268
+ }));
269
+ expect(registry.lookup('agent1')).toMatchObject({
270
+ sessionId: 'after',
271
+ pinned: true
272
+ });
273
+ });
274
+ it('preserves a pin through rename', ()=>{
275
+ registry.register(makeEntry({
276
+ name: 'before'
277
+ }));
278
+ registry.togglePin('claude', process.pid);
279
+ registry.rename('before', 'after');
280
+ expect(registry.lookup('after')?.pinned).toBe(true);
281
+ });
282
+ it('removes the pin with a pruned process row', ()=>{
283
+ registry.register(makeEntry({
284
+ pid: 999999
285
+ }));
286
+ registry.togglePin('claude', 999999);
287
+ registry.prune();
288
+ expect(registry.lookup('agent1')).toBeNull();
289
+ });
290
+ it('reports a clear error when a readonly registry toggles a pin', ()=>{
291
+ registry.register(makeEntry());
292
+ const readonlyRegistry = new AgentRegistry(regPath, {
293
+ readonly: true
294
+ });
295
+ expect(()=>readonlyRegistry.togglePin('claude', process.pid)).toThrow(/readonly/i);
296
+ });
297
+ });
231
298
  describe('list', ()=>{
232
299
  it('returns empty array when database does not contain entries', ()=>{
233
300
  expect(registry.list()).toEqual([]);
@@ -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 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('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","AgentRegistry","RenameNotFoundError","RenameConflictError","makeEntry","over","name","type","pid","process","tmuxSession","cwd","startedAt","sessionId","sessionFilePath","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","toEqual","legacyEntry","mkdirSync","dirname","writeFileSync","JSON","stringify","entries","legacyRegistry","isAlive","prune","remaining","before","after","not","nowMs","Date","parse","clocked","now","pruneIntervalMs","alive","mockReturnValue","pruneIfDue","toHaveBeenCalledTimes","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":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC;AAEjH;;GAEG;AACH,oBAAY,WAAW;IACnB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,OAAO,YAAY;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IAEb,oBAAoB;IACpB,IAAI,EAAE,SAAS,CAAC;IAEhB,qBAAqB;IACrB,MAAM,EAAE,WAAW,CAAC;IAEpB,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAEhB,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;IAEZ,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IAEpB,mBAAmB;IACnB,SAAS,EAAE,MAAM,CAAC;IAElB,iCAAiC;IACjC,UAAU,EAAE,IAAI,CAAC;IAEjB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;IAEZ,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAEhB,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IAEZ,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IAEZ,0DAA0D;IAC1D,SAAS,CAAC,EAAE,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC3B,sCAAsC;IACtC,IAAI,EAAE,SAAS,CAAC;IAEhB;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB,+EAA+E;IAC/E,UAAU,EAAE,IAAI,CAAC;IAEjB,oFAAoF;IACpF,SAAS,EAAE,IAAI,CAAC;IAEhB,oEAAoE;IACpE,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAChC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IAClC,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,SAAS,WAAW,EAAE,CAAC;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IACzB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAEzB,kEAAkE;IAClE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAE1C;;;OAGG;IACH,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAEpE;;;;OAIG;IACH,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC;IAE7C;;;;;OAKG;IACH,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE,CAAC;IAEjG;;;;;;;;;OASG;IACH,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;CACvE"}
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"}
@@ -9,6 +9,7 @@ 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,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;gBAEpB,OAAO,GAAE,eAAoB;IAezC,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,
22
+ readonly: this.readonly,
21
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 | ((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\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: typeof options.verbose === 'function'\n ? options.verbose\n : 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,SAAS,OAAOH,QAAQG,OAAO,KAAK,aAC9BH,QAAQG,OAAO,GACfH,QAAQG,OAAO,GAAGC,QAAQC,GAAG,GAAGC;QAC1C;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';
@@ -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,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"}
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 { 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","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,16 +16,20 @@ export interface RegistryEntry {
16
16
  startedAt: string;
17
17
  sessionId: string;
18
18
  sessionFilePath: string;
19
+ pinned: boolean;
20
+ updatedAt?: string;
19
21
  }
20
22
  export interface AgentRegistryOptions {
21
23
  now?: () => Date;
22
24
  pruneIntervalMs?: number;
23
25
  onDatabaseOperation?: (sql: string) => void;
26
+ readonly?: boolean;
24
27
  }
25
28
  export declare class AgentRegistry {
26
29
  private db;
27
30
  private readonly now;
28
31
  private readonly pruneIntervalMs;
32
+ private readonly readonly;
29
33
  private lastPrunedAt;
30
34
  constructor(filePath?: string, options?: AgentRegistryOptions);
31
35
  static default(): AgentRegistry;
@@ -46,6 +50,7 @@ export declare class AgentRegistry {
46
50
  register(entry: RegistryEntry): void;
47
51
  registerBatch(entries: RegistryEntry[]): void;
48
52
  rename(currentName: string, newName: string): void;
53
+ togglePin(type: AgentType, pid: number): boolean | null;
49
54
  lookup(name: string): RegistryEntry | null;
50
55
  list(): RegistryEntry[];
51
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;AAmBD,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;CAC/C;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,YAAY,CAAqB;gBAE7B,QAAQ,GAAE,MAA8B,EAAE,OAAO,GAAE,oBAAyB;IASxF,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,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,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"}
@@ -22,13 +22,16 @@ export class AgentRegistry {
22
22
  db;
23
23
  now;
24
24
  pruneIntervalMs;
25
+ readonly;
25
26
  lastPrunedAt;
26
27
  constructor(filePath = DEFAULT_REGISTRY_PATH, options = {}){
27
28
  this.now = options.now ?? (()=>new Date());
28
29
  this.pruneIntervalMs = options.pruneIntervalMs ?? DEFAULT_PRUNE_INTERVAL_MS;
30
+ this.readonly = options.readonly ?? false;
29
31
  this.db = new DatabaseConnection({
30
32
  dbPath: resolveAgentRegistryDbPath(filePath),
31
- verbose: options.onDatabaseOperation
33
+ verbose: options.onDatabaseOperation,
34
+ readonly: this.readonly
32
35
  });
33
36
  }
34
37
  static default() {
@@ -46,7 +49,9 @@ export class AgentRegistry {
46
49
  cwd: row.cwd,
47
50
  startedAt: row.started_at,
48
51
  sessionId: row.session_id,
49
- sessionFilePath: row.session_file_path
52
+ sessionFilePath: row.session_file_path,
53
+ pinned: row.pinned !== 0,
54
+ updatedAt: row.updated_at
50
55
  };
51
56
  }
52
57
  mergeEntry(incoming, existing) {
@@ -207,6 +212,18 @@ export class AgentRegistry {
207
212
  ]);
208
213
  });
209
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
+ }
210
227
  lookup(name) {
211
228
  return this.findByName(name) ?? null;
212
229
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/AgentRegistry.ts"],"sourcesContent":["import os from 'os';\nimport path from 'path';\nimport type { AgentType } from '../adapters/AgentAdapter.js';\nimport {\n DatabaseConnection,\n resolveAgentRegistryDbPath,\n} from '../database/index.js';\n\nexport class RenameNotFoundError extends Error {\n constructor(public agentName: string) {\n super(`Agent \"${agentName}\" not found in registry.`);\n this.name = 'RenameNotFoundError';\n }\n}\n\nexport class RenameConflictError extends Error {\n constructor(public agentName: string) {\n super(`Agent \"${agentName}\" is already in use.`);\n this.name = 'RenameConflictError';\n }\n}\n\nexport interface RegistryEntry {\n name: string;\n type: AgentType;\n pid: number;\n tmuxSession: string;\n cwd: string;\n startedAt: string; // ISO 8601\n sessionId: string;\n sessionFilePath: string;\n}\n\ninterface RegistryRow {\n name: string;\n type: AgentType;\n pid: number;\n tmux_session: string;\n cwd: string;\n started_at: string;\n session_id: string;\n session_file_path: string;\n updated_at: string;\n}\n\nconst DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.ai-devkit', 'agents.json');\nconst DEFAULT_PRUNE_INTERVAL_MS = 30_000;\n\nlet defaultInstance: AgentRegistry | null = null;\n\nexport interface AgentRegistryOptions {\n now?: () => Date;\n pruneIntervalMs?: number;\n onDatabaseOperation?: (sql: string) => void;\n}\n\nexport class AgentRegistry {\n private db: DatabaseConnection;\n private readonly now: () => Date;\n private readonly pruneIntervalMs: number;\n private lastPrunedAt: number | undefined;\n\n constructor(filePath: string = DEFAULT_REGISTRY_PATH, options: AgentRegistryOptions = {}) {\n this.now = options.now ?? (() => new Date());\n this.pruneIntervalMs = options.pruneIntervalMs ?? DEFAULT_PRUNE_INTERVAL_MS;\n this.db = new DatabaseConnection({\n dbPath: resolveAgentRegistryDbPath(filePath),\n verbose: options.onDatabaseOperation,\n });\n }\n\n static default(): AgentRegistry {\n if (!defaultInstance) {\n defaultInstance = new AgentRegistry();\n }\n return defaultInstance;\n }\n\n private rowToEntry(row: RegistryRow): RegistryEntry {\n return {\n name: row.name,\n type: row.type,\n pid: row.pid,\n tmuxSession: row.tmux_session,\n cwd: row.cwd,\n startedAt: row.started_at,\n sessionId: row.session_id,\n sessionFilePath: row.session_file_path,\n };\n }\n\n private mergeEntry(incoming: RegistryEntry, existing: RegistryEntry | undefined): RegistryEntry {\n if (!existing) return incoming;\n const incomingIsManaged = Boolean(incoming.tmuxSession);\n return {\n ...existing,\n name: incomingIsManaged ? incoming.name : existing.name,\n tmuxSession: incoming.tmuxSession || existing.tmuxSession,\n cwd: incoming.cwd || existing.cwd,\n startedAt: existing.startedAt || incoming.startedAt,\n sessionId: incoming.sessionId || existing.sessionId,\n sessionFilePath: incoming.sessionFilePath || existing.sessionFilePath,\n };\n }\n\n private findByIdentity(type: AgentType, pid: number): RegistryEntry | undefined {\n const row = this.db.queryOne<RegistryRow>(\n 'SELECT * FROM agents WHERE type = ? AND pid = ?',\n [type, pid],\n );\n return row ? this.rowToEntry(row) : undefined;\n }\n\n private findByName(name: string): RegistryEntry | undefined {\n const row = this.db.queryOne<RegistryRow>('SELECT * FROM agents WHERE name = ?', [name]);\n return row ? this.rowToEntry(row) : undefined;\n }\n\n private findPidConflicts(type: AgentType, pid: number): RegistryEntry[] {\n return this.db.query<RegistryRow>(\n 'SELECT * FROM agents WHERE pid = ? AND type <> ?',\n [pid, type],\n ).map((row) => this.rowToEntry(row));\n }\n\n private entriesEqual(left: RegistryEntry, right: RegistryEntry): boolean {\n return left.name === right.name\n && left.type === right.type\n && left.pid === right.pid\n && left.tmuxSession === right.tmuxSession\n && left.cwd === right.cwd\n && left.startedAt === right.startedAt\n && left.sessionId === right.sessionId\n && left.sessionFilePath === right.sessionFilePath;\n }\n\n private deleteNameConflict(name: string, type: AgentType, pid: number): void {\n const conflict = this.findByName(name);\n if (!conflict) return;\n if (conflict.type === type && conflict.pid === pid) return;\n if (!this.isAlive(conflict)) {\n this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [conflict.type, conflict.pid]);\n }\n }\n\n private insertOrUpdate(entry: RegistryEntry): void {\n this.db.instance.prepare(`\n INSERT INTO agents (\n type, pid, name, tmux_session, cwd, started_at, session_id, session_file_path, updated_at\n )\n VALUES (\n @type, @pid, @name, @tmuxSession, @cwd, @startedAt, @sessionId, @sessionFilePath, @updatedAt\n )\n ON CONFLICT(type, pid) DO UPDATE SET\n name = excluded.name,\n tmux_session = excluded.tmux_session,\n cwd = excluded.cwd,\n started_at = agents.started_at,\n session_id = excluded.session_id,\n session_file_path = excluded.session_file_path,\n updated_at = excluded.updated_at\n `).run({ ...entry, updatedAt: this.now().toISOString() });\n }\n\n private needsWrite(incoming: RegistryEntry): boolean {\n const existing = this.findByIdentity(incoming.type, incoming.pid);\n const merged = this.mergeEntry(incoming, existing);\n return !existing\n || !this.entriesEqual(merged, existing)\n || this.findPidConflicts(incoming.type, incoming.pid).length > 0;\n }\n\n private save(incoming: RegistryEntry): void {\n const existing = this.findByIdentity(incoming.type, incoming.pid);\n const merged = this.mergeEntry(incoming, existing);\n const pidConflicts = this.findPidConflicts(incoming.type, incoming.pid);\n if (existing && this.entriesEqual(merged, existing) && pidConflicts.length === 0) return;\n\n for (const conflict of pidConflicts) {\n this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [conflict.type, conflict.pid]);\n }\n if (existing && this.entriesEqual(merged, existing)) return;\n\n this.deleteNameConflict(merged.name, merged.type, merged.pid);\n this.insertOrUpdate(merged);\n }\n\n isAlive(entry: RegistryEntry): boolean {\n try {\n process.kill(entry.pid, 0);\n return true;\n } catch (error) {\n const code = error && typeof error === 'object' && 'code' in error\n ? error.code\n : undefined;\n return code !== 'ESRCH';\n }\n }\n\n private pruneAt(nowMs: number): void {\n const entries = this.list();\n const stale = entries.filter((e) => !this.isAlive(e));\n if (stale.length > 0) {\n this.db.transaction(() => {\n for (const entry of stale) {\n this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [entry.type, entry.pid]);\n }\n });\n }\n this.lastPrunedAt = nowMs;\n }\n\n prune(): void {\n this.pruneAt(this.now().getTime());\n }\n\n pruneIfDue(): void {\n const nowMs = this.now().getTime();\n const elapsed = this.lastPrunedAt === undefined ? undefined : nowMs - this.lastPrunedAt;\n if (elapsed !== undefined && elapsed >= 0 && elapsed < this.pruneIntervalMs) return;\n this.pruneAt(nowMs);\n }\n\n register(entry: RegistryEntry): void {\n this.registerBatch([entry]);\n }\n\n registerBatch(entries: RegistryEntry[]): void {\n if (entries.length === 0) return;\n if (!entries.some((entry) => this.needsWrite(entry))) return;\n this.db.transaction(() => {\n for (const incoming of entries) {\n this.save(incoming);\n }\n });\n }\n\n rename(currentName: string, newName: string): void {\n const existing = this.findByName(currentName);\n if (!existing) {\n throw new RenameNotFoundError(currentName);\n }\n const conflict = this.findByName(newName);\n if (conflict && this.isAlive(conflict)) {\n throw new RenameConflictError(newName);\n }\n\n this.db.transaction(() => {\n if (conflict) {\n this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [conflict.type, conflict.pid]);\n }\n this.db.execute(\n 'UPDATE agents SET name = ?, updated_at = ? WHERE type = ? AND pid = ?',\n [newName, this.now().toISOString(), existing.type, existing.pid],\n );\n });\n }\n\n lookup(name: string): RegistryEntry | null {\n return this.findByName(name) ?? null;\n }\n\n list(): RegistryEntry[] {\n const rows = this.db.query<RegistryRow>('SELECT * FROM agents ORDER BY started_at ASC, name ASC');\n return rows.map((row) => this.rowToEntry(row));\n }\n}\n"],"names":["os","path","DatabaseConnection","resolveAgentRegistryDbPath","RenameNotFoundError","Error","agentName","name","RenameConflictError","DEFAULT_REGISTRY_PATH","join","homedir","DEFAULT_PRUNE_INTERVAL_MS","defaultInstance","AgentRegistry","db","now","pruneIntervalMs","lastPrunedAt","filePath","options","Date","dbPath","verbose","onDatabaseOperation","default","rowToEntry","row","type","pid","tmuxSession","tmux_session","cwd","startedAt","started_at","sessionId","session_id","sessionFilePath","session_file_path","mergeEntry","incoming","existing","incomingIsManaged","Boolean","findByIdentity","queryOne","undefined","findByName","findPidConflicts","query","map","entriesEqual","left","right","deleteNameConflict","conflict","isAlive","execute","insertOrUpdate","entry","instance","prepare","run","updatedAt","toISOString","needsWrite","merged","length","save","pidConflicts","process","kill","error","code","pruneAt","nowMs","entries","list","stale","filter","e","transaction","prune","getTime","pruneIfDue","elapsed","register","registerBatch","some","rename","currentName","newName","lookup","rows"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utils/AgentRegistry.ts"],"sourcesContent":["import os from 'os';\nimport path from 'path';\nimport type { AgentType } from '../adapters/AgentAdapter.js';\nimport {\n DatabaseConnection,\n resolveAgentRegistryDbPath,\n} from '../database/index.js';\n\nexport class RenameNotFoundError extends Error {\n constructor(public agentName: string) {\n super(`Agent \"${agentName}\" not found in registry.`);\n this.name = 'RenameNotFoundError';\n }\n}\n\nexport class RenameConflictError extends Error {\n constructor(public agentName: string) {\n super(`Agent \"${agentName}\" is already in use.`);\n this.name = 'RenameConflictError';\n }\n}\n\nexport interface RegistryEntry {\n name: string;\n type: AgentType;\n pid: number;\n tmuxSession: string;\n cwd: string;\n startedAt: string; // ISO 8601\n sessionId: string;\n sessionFilePath: string;\n pinned: boolean;\n updatedAt?: string;\n}\n\ninterface RegistryRow {\n name: string;\n type: AgentType;\n pid: number;\n tmux_session: string;\n cwd: string;\n started_at: string;\n session_id: string;\n session_file_path: string;\n updated_at: string;\n pinned: number;\n}\n\nconst DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.ai-devkit', 'agents.json');\nconst DEFAULT_PRUNE_INTERVAL_MS = 30_000;\n\nlet defaultInstance: AgentRegistry | null = null;\n\nexport interface AgentRegistryOptions {\n now?: () => Date;\n pruneIntervalMs?: number;\n onDatabaseOperation?: (sql: string) => void;\n readonly?: boolean;\n}\n\nexport class AgentRegistry {\n private db: DatabaseConnection;\n private readonly now: () => Date;\n private readonly pruneIntervalMs: number;\n private readonly readonly: boolean;\n private lastPrunedAt: number | undefined;\n\n constructor(filePath: string = DEFAULT_REGISTRY_PATH, options: AgentRegistryOptions = {}) {\n this.now = options.now ?? (() => new Date());\n this.pruneIntervalMs = options.pruneIntervalMs ?? DEFAULT_PRUNE_INTERVAL_MS;\n this.readonly = options.readonly ?? false;\n this.db = new DatabaseConnection({\n dbPath: resolveAgentRegistryDbPath(filePath),\n verbose: options.onDatabaseOperation,\n readonly: this.readonly,\n });\n }\n\n static default(): AgentRegistry {\n if (!defaultInstance) {\n defaultInstance = new AgentRegistry();\n }\n return defaultInstance;\n }\n\n private rowToEntry(row: RegistryRow): RegistryEntry {\n return {\n name: row.name,\n type: row.type,\n pid: row.pid,\n tmuxSession: row.tmux_session,\n cwd: row.cwd,\n startedAt: row.started_at,\n sessionId: row.session_id,\n sessionFilePath: row.session_file_path,\n pinned: row.pinned !== 0,\n updatedAt: row.updated_at,\n };\n }\n\n private mergeEntry(incoming: RegistryEntry, existing: RegistryEntry | undefined): RegistryEntry {\n if (!existing) return incoming;\n const incomingIsManaged = Boolean(incoming.tmuxSession);\n return {\n ...existing,\n name: incomingIsManaged ? incoming.name : existing.name,\n tmuxSession: incoming.tmuxSession || existing.tmuxSession,\n cwd: incoming.cwd || existing.cwd,\n startedAt: existing.startedAt || incoming.startedAt,\n sessionId: incoming.sessionId || existing.sessionId,\n sessionFilePath: incoming.sessionFilePath || existing.sessionFilePath,\n };\n }\n\n private findByIdentity(type: AgentType, pid: number): RegistryEntry | undefined {\n const row = this.db.queryOne<RegistryRow>(\n 'SELECT * FROM agents WHERE type = ? AND pid = ?',\n [type, pid],\n );\n return row ? this.rowToEntry(row) : undefined;\n }\n\n private findByName(name: string): RegistryEntry | undefined {\n const row = this.db.queryOne<RegistryRow>('SELECT * FROM agents WHERE name = ?', [name]);\n return row ? this.rowToEntry(row) : undefined;\n }\n\n private findPidConflicts(type: AgentType, pid: number): RegistryEntry[] {\n return this.db.query<RegistryRow>(\n 'SELECT * FROM agents WHERE pid = ? AND type <> ?',\n [pid, type],\n ).map((row) => this.rowToEntry(row));\n }\n\n private entriesEqual(left: RegistryEntry, right: RegistryEntry): boolean {\n return left.name === right.name\n && left.type === right.type\n && left.pid === right.pid\n && left.tmuxSession === right.tmuxSession\n && left.cwd === right.cwd\n && left.startedAt === right.startedAt\n && left.sessionId === right.sessionId\n && left.sessionFilePath === right.sessionFilePath;\n }\n\n private deleteNameConflict(name: string, type: AgentType, pid: number): void {\n const conflict = this.findByName(name);\n if (!conflict) return;\n if (conflict.type === type && conflict.pid === pid) return;\n if (!this.isAlive(conflict)) {\n this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [conflict.type, conflict.pid]);\n }\n }\n\n private insertOrUpdate(entry: RegistryEntry): void {\n this.db.instance.prepare(`\n INSERT INTO agents (\n type, pid, name, tmux_session, cwd, started_at, session_id, session_file_path, updated_at\n )\n VALUES (\n @type, @pid, @name, @tmuxSession, @cwd, @startedAt, @sessionId, @sessionFilePath, @updatedAt\n )\n ON CONFLICT(type, pid) DO UPDATE SET\n name = excluded.name,\n tmux_session = excluded.tmux_session,\n cwd = excluded.cwd,\n started_at = agents.started_at,\n session_id = excluded.session_id,\n session_file_path = excluded.session_file_path,\n updated_at = excluded.updated_at\n `).run({ ...entry, updatedAt: this.now().toISOString() });\n }\n\n private needsWrite(incoming: RegistryEntry): boolean {\n const existing = this.findByIdentity(incoming.type, incoming.pid);\n const merged = this.mergeEntry(incoming, existing);\n return !existing\n || !this.entriesEqual(merged, existing)\n || this.findPidConflicts(incoming.type, incoming.pid).length > 0;\n }\n\n private save(incoming: RegistryEntry): void {\n const existing = this.findByIdentity(incoming.type, incoming.pid);\n const merged = this.mergeEntry(incoming, existing);\n const pidConflicts = this.findPidConflicts(incoming.type, incoming.pid);\n if (existing && this.entriesEqual(merged, existing) && pidConflicts.length === 0) return;\n\n for (const conflict of pidConflicts) {\n this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [conflict.type, conflict.pid]);\n }\n if (existing && this.entriesEqual(merged, existing)) return;\n\n this.deleteNameConflict(merged.name, merged.type, merged.pid);\n this.insertOrUpdate(merged);\n }\n\n isAlive(entry: RegistryEntry): boolean {\n try {\n process.kill(entry.pid, 0);\n return true;\n } catch (error) {\n const code = error && typeof error === 'object' && 'code' in error\n ? error.code\n : undefined;\n return code !== 'ESRCH';\n }\n }\n\n private pruneAt(nowMs: number): void {\n const entries = this.list();\n const stale = entries.filter((e) => !this.isAlive(e));\n if (stale.length > 0) {\n this.db.transaction(() => {\n for (const entry of stale) {\n this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [entry.type, entry.pid]);\n }\n });\n }\n this.lastPrunedAt = nowMs;\n }\n\n prune(): void {\n this.pruneAt(this.now().getTime());\n }\n\n pruneIfDue(): void {\n const nowMs = this.now().getTime();\n const elapsed = this.lastPrunedAt === undefined ? undefined : nowMs - this.lastPrunedAt;\n if (elapsed !== undefined && elapsed >= 0 && elapsed < this.pruneIntervalMs) return;\n this.pruneAt(nowMs);\n }\n\n register(entry: RegistryEntry): void {\n this.registerBatch([entry]);\n }\n\n registerBatch(entries: RegistryEntry[]): void {\n if (entries.length === 0) return;\n if (!entries.some((entry) => this.needsWrite(entry))) return;\n this.db.transaction(() => {\n for (const incoming of entries) {\n this.save(incoming);\n }\n });\n }\n\n rename(currentName: string, newName: string): void {\n const existing = this.findByName(currentName);\n if (!existing) {\n throw new RenameNotFoundError(currentName);\n }\n const conflict = this.findByName(newName);\n if (conflict && this.isAlive(conflict)) {\n throw new RenameConflictError(newName);\n }\n\n this.db.transaction(() => {\n if (conflict) {\n this.db.execute('DELETE FROM agents WHERE type = ? AND pid = ?', [conflict.type, conflict.pid]);\n }\n this.db.execute(\n 'UPDATE agents SET name = ?, updated_at = ? WHERE type = ? AND pid = ?',\n [newName, this.now().toISOString(), existing.type, existing.pid],\n );\n });\n }\n\n togglePin(type: AgentType, pid: number): boolean | null {\n if (this.readonly) {\n throw new Error('Agent registry is readonly; cannot toggle pin.');\n }\n const result = this.db.execute(\n 'UPDATE agents SET pinned = NOT pinned, updated_at = ? WHERE type = ? AND pid = ?',\n [this.now().toISOString(), type, pid],\n );\n if (result.changes === 0) return null;\n return this.findByIdentity(type, pid)?.pinned ?? null;\n }\n\n lookup(name: string): RegistryEntry | null {\n return this.findByName(name) ?? null;\n }\n\n list(): RegistryEntry[] {\n const rows = this.db.query<RegistryRow>('SELECT * FROM agents ORDER BY started_at ASC, name ASC');\n return rows.map((row) => this.rowToEntry(row));\n }\n}\n"],"names":["os","path","DatabaseConnection","resolveAgentRegistryDbPath","RenameNotFoundError","Error","agentName","name","RenameConflictError","DEFAULT_REGISTRY_PATH","join","homedir","DEFAULT_PRUNE_INTERVAL_MS","defaultInstance","AgentRegistry","db","now","pruneIntervalMs","readonly","lastPrunedAt","filePath","options","Date","dbPath","verbose","onDatabaseOperation","default","rowToEntry","row","type","pid","tmuxSession","tmux_session","cwd","startedAt","started_at","sessionId","session_id","sessionFilePath","session_file_path","pinned","updatedAt","updated_at","mergeEntry","incoming","existing","incomingIsManaged","Boolean","findByIdentity","queryOne","undefined","findByName","findPidConflicts","query","map","entriesEqual","left","right","deleteNameConflict","conflict","isAlive","execute","insertOrUpdate","entry","instance","prepare","run","toISOString","needsWrite","merged","length","save","pidConflicts","process","kill","error","code","pruneAt","nowMs","entries","list","stale","filter","e","transaction","prune","getTime","pruneIfDue","elapsed","register","registerBatch","some","rename","currentName","newName","togglePin","result","changes","lookup","rows"],"mappings":"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"}