@ai-devkit/agent-manager 0.26.4 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/dist/AgentManager.d.ts +5 -0
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +21 -1
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +112 -0
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/adapters/CodexAdapter.test.js +199 -0
- package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
- package/dist/__tests__/database/DurableAgentsDatabase.test.js +100 -0
- package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +1 -0
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +7 -7
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintAgentService.test.js +7 -7
- package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintRunner.test.js +1 -1
- package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
- package/dist/__tests__/print/DurableAgent.test.js +17 -0
- package/dist/__tests__/print/DurableAgent.test.js.map +1 -0
- package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js +186 -0
- package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js.map +1 -0
- package/dist/__tests__/print/{PrintAgentStore.test.js → DurableAgentRepository.test.js} +63 -110
- package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -0
- package/dist/__tests__/utils/AgentRegistry.test.js +67 -0
- package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
- package/dist/adapters/AgentAdapter.d.ts +2 -0
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.d.ts +1 -0
- package/dist/adapters/CodexAdapter.d.ts.map +1 -1
- package/dist/adapters/CodexAdapter.js +16 -6
- package/dist/adapters/CodexAdapter.js.map +1 -1
- package/dist/database/connection.d.ts +1 -0
- package/dist/database/connection.d.ts.map +1 -1
- package/dist/database/connection.js +20 -5
- package/dist/database/connection.js.map +1 -1
- package/dist/database/migrations/002_pins.sql +1 -0
- package/dist/database/migrations/003_durable_agents.sql +43 -0
- package/dist/durable/ClaudeCliProbe.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudeCliProbe.js +1 -1
- package/dist/durable/ClaudeCliProbe.js.map +1 -0
- package/dist/{print → durable}/ClaudePrintAgentService.d.ts +11 -11
- package/dist/durable/ClaudePrintAgentService.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudePrintAgentService.js +12 -12
- package/dist/durable/ClaudePrintAgentService.js.map +1 -0
- package/dist/{print → durable}/ClaudePrintRunner.d.ts +3 -3
- package/dist/durable/ClaudePrintRunner.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudePrintRunner.js +2 -2
- package/dist/durable/ClaudePrintRunner.js.map +1 -0
- package/dist/durable/DurableAgent.d.ts +61 -0
- package/dist/durable/DurableAgent.d.ts.map +1 -0
- package/dist/durable/DurableAgent.js +46 -0
- package/dist/durable/DurableAgent.js.map +1 -0
- package/dist/durable/DurableAgentRepository.d.ts +60 -0
- package/dist/durable/DurableAgentRepository.d.ts.map +1 -0
- package/dist/durable/DurableAgentRepository.js +325 -0
- package/dist/durable/DurableAgentRepository.js.map +1 -0
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/utils/AgentRegistry.d.ts +5 -0
- package/dist/utils/AgentRegistry.d.ts.map +1 -1
- package/dist/utils/AgentRegistry.js +19 -2
- package/dist/utils/AgentRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +21 -0
- package/src/__tests__/AgentManager.test.ts +117 -0
- package/src/__tests__/adapters/CodexAdapter.test.ts +132 -0
- package/src/__tests__/database/DurableAgentsDatabase.test.ts +77 -0
- package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +6 -6
- package/src/__tests__/print/ClaudePrintAgentService.test.ts +7 -7
- package/src/__tests__/print/ClaudePrintRunner.test.ts +3 -3
- package/src/__tests__/print/{PrintAgent.test.ts → DurableAgent.test.ts} +6 -6
- package/src/__tests__/print/DurableAgentRepository.sqlite.test.ts +105 -0
- package/src/__tests__/print/DurableAgentRepository.test.ts +163 -0
- package/src/__tests__/utils/AgentRegistry.test.ts +68 -0
- package/src/adapters/AgentAdapter.ts +3 -0
- package/src/adapters/CodexAdapter.ts +14 -11
- package/src/database/connection.ts +18 -5
- package/src/database/migrations/002_pins.sql +1 -0
- package/src/database/migrations/003_durable_agents.sql +43 -0
- package/src/{print → durable}/ClaudeCliProbe.ts +1 -1
- package/src/{print → durable}/ClaudePrintAgentService.ts +21 -21
- package/src/{print → durable}/ClaudePrintRunner.ts +4 -4
- package/src/durable/DurableAgent.ts +91 -0
- package/src/durable/DurableAgentRepository.ts +307 -0
- package/src/index.ts +27 -26
- package/src/utils/AgentRegistry.ts +21 -0
- package/dist/__tests__/print/PrintAgent.test.js +0 -17
- package/dist/__tests__/print/PrintAgent.test.js.map +0 -1
- package/dist/__tests__/print/PrintAgentStore.test.js.map +0 -1
- package/dist/print/ClaudeCliProbe.d.ts.map +0 -1
- package/dist/print/ClaudeCliProbe.js.map +0 -1
- package/dist/print/ClaudePrintAgentService.d.ts.map +0 -1
- package/dist/print/ClaudePrintAgentService.js.map +0 -1
- package/dist/print/ClaudePrintRunner.d.ts.map +0 -1
- package/dist/print/ClaudePrintRunner.js.map +0 -1
- package/dist/print/PrintAgent.d.ts +0 -57
- package/dist/print/PrintAgent.d.ts.map +0 -1
- package/dist/print/PrintAgent.js +0 -42
- package/dist/print/PrintAgent.js.map +0 -1
- package/dist/print/PrintAgentStore.d.ts +0 -69
- package/dist/print/PrintAgentStore.d.ts.map +0 -1
- package/dist/print/PrintAgentStore.js +0 -484
- package/dist/print/PrintAgentStore.js.map +0 -1
- package/src/__tests__/print/PrintAgentStore.test.ts +0 -192
- package/src/print/PrintAgent.ts +0 -86
- package/src/print/PrintAgentStore.ts +0 -503
- /package/dist/{print → durable}/ClaudeCliProbe.d.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/durable/DurableAgentRepository.ts"],"sourcesContent":["import fs from 'fs';\nimport { randomUUID } from 'crypto';\nimport { execFileSync } from 'child_process';\nimport { DatabaseConnection, DEFAULT_AGENT_REGISTRY_DB_PATH } from '../database/index.js';\nimport { AGENT_MODES, type DurableActiveRun, type DurableAgent, type ProcessIdentity, type DurableRunStatus, type DurableSessionHealth } from './DurableAgent.js';\nimport {\n DurableAgentBusyError,\n DurableAgentNameConflictError,\n DurableAgentNotFoundError,\n DurableAgentRepositoryError,\n} from './DurableAgent.js';\n\ninterface DurableAgentRow {\n id: string; name: string; provider: 'claude'; mode: typeof AGENT_MODES.DURABLE; cwd: string; provider_session_id: string;\n state: DurableAgent['state']; session_health: DurableSessionHealth; created_at: string; updated_at: string;\n last_active_at: string | null; last_result_status: DurableRunStatus | null;\n last_result_completed_at: string | null; last_result_exit_code: number | null; last_result_summary: string | null;\n active_run_token: string | null; active_owner_pid: number | null; active_owner_started_at: string | null;\n active_provider_pid: number | null; active_provider_started_at: string | null; active_run_started_at: string | null;\n}\n\nexport interface CreateDurableAgentInput { name: string; cwd: string }\n\nexport interface DurableAgentRepositoryOptions {\n dbPath?: string;\n readonly?: boolean;\n /** @deprecated SQLite busy_timeout replaces filesystem lock polling. */\n lockTimeoutMs?: number;\n now?: () => Date;\n processInspector?: ProcessInspector;\n /** @deprecated Active ownership is committed atomically. */\n incompleteLockGraceMs?: number;\n /** @deprecated SQLite transactions replace mutation lock directories. */\n mutationLockStaleMs?: number;\n}\n\nexport interface ProcessInspector { getIdentity(pid: number): ProcessIdentity | null }\nexport interface DurableRunCompletion {\n status: DurableRunStatus; exitCode: number | null; summary: string; sessionHealth: DurableSessionHealth;\n}\n\nexport class DurableAgentRepository {\n readonly dbPath: string;\n private readonly now: () => Date;\n private readonly processInspector: ProcessInspector;\n private readonly readonly: boolean;\n private readonly db: DatabaseConnection;\n\n constructor(options: DurableAgentRepositoryOptions = {}) {\n this.dbPath = options.dbPath ?? DEFAULT_AGENT_REGISTRY_DB_PATH;\n this.now = options.now ?? (() => new Date());\n this.processInspector = options.processInspector ?? new LocalProcessInspector();\n this.readonly = options.readonly ?? false;\n try {\n this.db = new DatabaseConnection({ dbPath: this.dbPath, readonly: this.readonly });\n } catch (error) {\n if (error instanceof DurableAgentRepositoryError) throw error;\n throw new DurableAgentRepositoryError(`Cannot open durable-agent database: ${(error as Error).message}`);\n }\n }\n\n async create(input: CreateDurableAgentInput): Promise<DurableAgent> {\n this.assertWritable();\n const cwd = this.canonicalDirectory(input.cwd);\n const timestamp = this.now().toISOString();\n const id = randomUUID();\n let providerSessionId = randomUUID();\n while (providerSessionId === id) providerSessionId = randomUUID();\n try {\n this.db.execute(`INSERT INTO durable_agents (\n id, name, provider, mode, cwd, provider_session_id, state, session_health, created_at, updated_at\n ) VALUES (?, ?, 'claude', ?, ?, ?, 'ready', 'uninitialized', ?, ?)`,\n [id, input.name, AGENT_MODES.DURABLE, cwd, providerSessionId, timestamp, timestamp]);\n } catch (error) {\n if (/UNIQUE constraint failed: durable_agents\\.name/i.test((error as Error).message)) {\n throw new DurableAgentNameConflictError(input.name);\n }\n throw this.storageError('Failed to create durable agent', error);\n }\n return this.requireById(id);\n }\n\n async list(): Promise<DurableAgent[]> {\n if (!this.readonly) await this.reconcile();\n return this.listRaw();\n }\n\n async getById(id: string): Promise<DurableAgent | null> {\n if (!this.readonly) await this.reconcile();\n return this.findById(id);\n }\n\n async resolve(reference: string): Promise<DurableAgent | DurableAgent[] | null> {\n const agents = await this.list();\n const byId = agents.find((agent) => agent.id === reference);\n if (byId) return byId;\n const matches = agents.filter((agent) => agent.name.toLowerCase() === reference.toLowerCase());\n return matches.length === 0 ? null : matches.length === 1 ? matches[0]! : matches;\n }\n\n async acquireRun(id: string): Promise<{ agent: DurableAgent; token: string }> {\n this.assertWritable();\n const snapshot = this.findById(id);\n if (!snapshot) throw new DurableAgentNotFoundError(id);\n this.validateBoundCwd(snapshot.cwd);\n const observed = snapshot.activeRun;\n const observedLive = observed ? this.isActive(observed) : false;\n if (observedLive) throw new DurableAgentBusyError(id, snapshot.name);\n const owner = this.processInspector.getIdentity(process.pid);\n if (!owner) throw new DurableAgentRepositoryError('Cannot determine the current process identity.');\n const token = randomUUID();\n const startedAt = this.now().toISOString();\n let recovered = false;\n try {\n this.immediate(() => {\n const current = this.findById(id);\n if (!current) throw new DurableAgentNotFoundError(id);\n if (current.state === 'running') {\n if (!observed || current.activeRun?.token !== observed.token || observedLive) {\n throw new DurableAgentBusyError(id, current.name);\n }\n recovered = true;\n }\n const changed = this.db.execute(`UPDATE durable_agents SET\n state = 'running', active_run_token = ?, active_owner_pid = ?, active_owner_started_at = ?,\n active_provider_pid = NULL, active_provider_started_at = NULL, active_run_started_at = ?, updated_at = ?,\n session_health = CASE WHEN state = 'running' THEN 'unknown' ELSE session_health END,\n last_result_status = CASE WHEN state = 'running' THEN 'interrupted' ELSE last_result_status END,\n last_result_completed_at = CASE WHEN state = 'running' THEN ? ELSE last_result_completed_at END,\n last_result_exit_code = CASE WHEN state = 'running' THEN NULL ELSE last_result_exit_code END,\n last_result_summary = CASE WHEN state = 'running' THEN 'Previous print run was interrupted.' ELSE last_result_summary END\n WHERE id = ? AND (state <> 'running' OR (\n active_run_token = ? AND active_owner_started_at = ? AND active_run_started_at = ?\n ))\n `, [token, owner.pid, owner.startedAt, startedAt, startedAt, startedAt, id,\n observed?.token ?? null, observed?.owner.startedAt ?? null, observed?.startedAt ?? null]);\n if (changed.changes !== 1) throw new DurableAgentBusyError(id, current.name);\n });\n } catch (error) {\n if (error instanceof DurableAgentBusyError || error instanceof DurableAgentNotFoundError) throw error;\n if (/busy|locked/i.test((error as Error).message)) throw new DurableAgentBusyError(id, snapshot.name);\n throw this.storageError('Failed to acquire durable-agent run', error);\n }\n const agent = this.requireById(id);\n if (recovered && agent.lastResult?.status !== 'interrupted') {\n throw new DurableAgentRepositoryError('Failed to record interrupted print run.');\n }\n return { agent, token };\n }\n\n async recordProviderProcess(id: string, token: string, identity: ProcessIdentity): Promise<void> {\n this.assertWritable();\n const changed = this.db.execute(`UPDATE durable_agents SET\n active_provider_pid = ?, active_provider_started_at = ?, updated_at = ?\n WHERE id = ? AND state = 'running' AND active_run_token = ?`,\n [identity.pid, identity.startedAt, this.now().toISOString(), id, token]);\n if (changed.changes !== 1) throw new DurableAgentRepositoryError('Print run ownership changed.');\n }\n\n async completeRun(id: string, token: string, result: DurableRunCompletion): Promise<DurableAgent> {\n this.assertWritable();\n const completedAt = this.now().toISOString();\n const changed = this.db.execute(`UPDATE durable_agents SET\n state = ?, session_health = ?, active_run_token = NULL, active_owner_pid = NULL,\n active_owner_started_at = NULL, active_provider_pid = NULL, active_provider_started_at = NULL,\n active_run_started_at = NULL, last_active_at = ?, updated_at = ?, last_result_status = ?,\n last_result_completed_at = ?, last_result_exit_code = ?, last_result_summary = ?\n WHERE id = ? AND state = 'running' AND active_run_token = ?`, [\n result.status === 'succeeded' ? 'ready' : 'degraded', result.sessionHealth, completedAt, completedAt,\n result.status, completedAt, result.exitCode, result.summary.slice(0, 4096), id, token,\n ]);\n if (changed.changes !== 1) throw new DurableAgentRepositoryError('Print run ownership changed.');\n return this.requireById(id);\n }\n\n async reconcile(): Promise<void> {\n this.assertWritable();\n const running = this.listRaw().filter((agent) => agent.state === 'running' && agent.activeRun);\n for (const snapshot of running) {\n if (this.isActive(snapshot.activeRun!)) continue;\n const completedAt = this.now().toISOString();\n this.db.execute(`UPDATE durable_agents SET\n state = 'degraded', session_health = 'unknown', active_run_token = NULL,\n active_owner_pid = NULL, active_owner_started_at = NULL, active_provider_pid = NULL,\n active_provider_started_at = NULL, active_run_started_at = NULL, updated_at = ?, last_active_at = ?,\n last_result_status = 'interrupted', last_result_completed_at = ?, last_result_exit_code = NULL,\n last_result_summary = 'Previous print run was interrupted.'\n WHERE id = ? AND state = 'running' AND active_run_token = ?\n AND active_owner_started_at = ? AND active_run_started_at = ?`, [\n completedAt, completedAt, completedAt, snapshot.id, snapshot.activeRun!.token,\n snapshot.activeRun!.owner.startedAt, snapshot.activeRun!.startedAt,\n ]);\n }\n }\n\n private immediate<T>(operation: () => T): T {\n this.db.instance.exec('BEGIN IMMEDIATE');\n try {\n const result = operation();\n this.db.instance.exec('COMMIT');\n return result;\n } catch (error) {\n try { this.db.instance.exec('ROLLBACK'); } catch { /* retain original failure */ }\n throw error;\n }\n }\n\n private listRaw(): DurableAgent[] {\n try {\n return this.db.query<DurableAgentRow>(\n 'SELECT * FROM durable_agents ORDER BY updated_at DESC, name COLLATE NOCASE',\n ).map((row) => this.fromRow(row));\n } catch (error) {\n throw this.storageError('Failed to read durable-agent database', error);\n }\n }\n\n private findById(id: string): DurableAgent | null {\n const row = this.db.queryOne<DurableAgentRow>('SELECT * FROM durable_agents WHERE id = ?', [id]);\n return row ? this.fromRow(row) : null;\n }\n\n private requireById(id: string): DurableAgent {\n const agent = this.findById(id);\n if (!agent) throw new DurableAgentNotFoundError(id);\n return agent;\n }\n\n private fromRow(row: DurableAgentRow): DurableAgent {\n const activeRun: DurableActiveRun | null = row.active_run_token === null ? null : {\n token: row.active_run_token,\n owner: { pid: row.active_owner_pid!, startedAt: row.active_owner_started_at! },\n provider: row.active_provider_pid === null ? null : {\n pid: row.active_provider_pid, startedAt: row.active_provider_started_at!,\n },\n startedAt: row.active_run_started_at!,\n };\n return {\n id: row.id, name: row.name, provider: row.provider, mode: row.mode, cwd: row.cwd,\n providerSessionId: row.provider_session_id, state: row.state, sessionHealth: row.session_health,\n createdAt: row.created_at, updatedAt: row.updated_at, lastActiveAt: row.last_active_at,\n lastResult: row.last_result_status === null ? null : {\n status: row.last_result_status, completedAt: row.last_result_completed_at!,\n exitCode: row.last_result_exit_code, summary: row.last_result_summary ?? '',\n },\n activeRun,\n };\n }\n\n private canonicalDirectory(input: string): string {\n try {\n const resolved = fs.realpathSync(input);\n if (!fs.statSync(resolved).isDirectory()) throw new Error('not a directory');\n return resolved;\n } catch {\n throw new DurableAgentRepositoryError(`Durable agent cwd is not an existing directory: ${input}`);\n }\n }\n\n private validateBoundCwd(bound: string): void {\n try {\n const stat = fs.lstatSync(bound);\n if (!stat.isDirectory() || stat.isSymbolicLink() || fs.realpathSync(bound) !== bound) throw new Error('binding changed');\n } catch {\n throw new DurableAgentRepositoryError(`Durable agent cwd binding is no longer safe: ${bound}`);\n }\n }\n\n private isActive(metadata: DurableActiveRun): boolean {\n return this.sameProcess(metadata.owner) || (metadata.provider !== null && this.sameProcess(metadata.provider));\n }\n\n private sameProcess(expected: ProcessIdentity): boolean {\n const actual = this.processInspector.getIdentity(expected.pid);\n return actual !== null && actual.startedAt === expected.startedAt;\n }\n\n private assertWritable(): void {\n if (this.readonly) throw new DurableAgentRepositoryError('Durable-agent repository is readonly.');\n }\n\n private storageError(prefix: string, error: unknown): DurableAgentRepositoryError {\n return error instanceof DurableAgentRepositoryError ? error\n : new DurableAgentRepositoryError(`${prefix}: ${(error as Error).message}`);\n }\n}\n\nexport class LocalProcessInspector implements ProcessInspector {\n getIdentity(pid: number): ProcessIdentity | null {\n if (!Number.isInteger(pid) || pid <= 0) return null;\n try {\n if (process.platform === 'linux') {\n const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');\n const close = stat.lastIndexOf(')');\n const fields = stat.slice(close + 2).split(' ');\n const startTicks = fields[19];\n return startTicks ? { pid, startedAt: `linux:${startTicks}` } : null;\n }\n const startedAt = execFileSync('ps', ['-o', 'lstart=', '-p', String(pid)], {\n encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return startedAt ? { pid, startedAt } : null;\n } catch {\n return null;\n }\n }\n}\n"],"names":["fs","randomUUID","execFileSync","DatabaseConnection","DEFAULT_AGENT_REGISTRY_DB_PATH","AGENT_MODES","DurableAgentBusyError","DurableAgentNameConflictError","DurableAgentNotFoundError","DurableAgentRepositoryError","DurableAgentRepository","dbPath","now","processInspector","readonly","db","options","Date","LocalProcessInspector","error","message","create","input","assertWritable","cwd","canonicalDirectory","timestamp","toISOString","id","providerSessionId","execute","name","DURABLE","test","storageError","requireById","list","reconcile","listRaw","getById","findById","resolve","reference","agents","byId","find","agent","matches","filter","toLowerCase","length","acquireRun","snapshot","validateBoundCwd","observed","activeRun","observedLive","isActive","owner","getIdentity","process","pid","token","startedAt","recovered","immediate","current","state","changed","changes","lastResult","status","recordProviderProcess","identity","completeRun","result","completedAt","sessionHealth","exitCode","summary","slice","running","operation","instance","exec","query","map","row","fromRow","queryOne","active_run_token","active_owner_pid","active_owner_started_at","provider","active_provider_pid","active_provider_started_at","active_run_started_at","mode","provider_session_id","session_health","createdAt","created_at","updatedAt","updated_at","lastActiveAt","last_active_at","last_result_status","last_result_completed_at","last_result_exit_code","last_result_summary","resolved","realpathSync","statSync","isDirectory","Error","bound","stat","lstatSync","isSymbolicLink","metadata","sameProcess","expected","actual","prefix","Number","isInteger","platform","readFileSync","close","lastIndexOf","fields","split","startTicks","String","encoding","stdio","trim"],"mappings":"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"}
|
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';
|
|
@@ -22,15 +22,15 @@ export { AGENTS } from './utils/agents.js';
|
|
|
22
22
|
export type { AgentConfig, StartableAgentType } from './utils/agents.js';
|
|
23
23
|
export type { AgentRequest } from './utils/agent-requests.js';
|
|
24
24
|
export { getAgentRequestPath, readLatestAgentRequest, writeAgentRequest } from './utils/agent-requests.js';
|
|
25
|
-
export {
|
|
26
|
-
export type {
|
|
27
|
-
export {
|
|
28
|
-
export { LocalProcessInspector } from './
|
|
29
|
-
export type {
|
|
30
|
-
export { ClaudeCliProbe } from './
|
|
31
|
-
export type { ClaudeCliProbeOptions } from './
|
|
32
|
-
export { ClaudePrintRunner } from './
|
|
33
|
-
export type { ClaudePrintRunnerOptions, ClaudePrintRunRequest, ClaudePrintRunResult, } from './
|
|
34
|
-
export { ClaudePrintAgentService } from './
|
|
35
|
-
export type { ClaudePrintAgentServiceOptions, ClaudePrintSendResult, } from './
|
|
25
|
+
export { AGENT_MODES, DurableAgentError, DurableAgentBusyError, DurableAgentNotFoundError, DurableAgentRepositoryError, DurableAgentNameConflictError, ClaudePrintError, } from './durable/DurableAgent.js';
|
|
26
|
+
export type { DurableAgent, DurableAgentState, DurableSessionHealth, DurableRunStatus, DurableActiveRun, DurableLastResult, ProcessIdentity, } from './durable/DurableAgent.js';
|
|
27
|
+
export { DurableAgentRepository } from './durable/DurableAgentRepository.js';
|
|
28
|
+
export { LocalProcessInspector } from './durable/DurableAgentRepository.js';
|
|
29
|
+
export type { CreateDurableAgentInput, DurableAgentRepositoryOptions, ProcessInspector, DurableRunCompletion, } from './durable/DurableAgentRepository.js';
|
|
30
|
+
export { ClaudeCliProbe } from './durable/ClaudeCliProbe.js';
|
|
31
|
+
export type { ClaudeCliProbeOptions } from './durable/ClaudeCliProbe.js';
|
|
32
|
+
export { ClaudePrintRunner } from './durable/ClaudePrintRunner.js';
|
|
33
|
+
export type { ClaudePrintRunnerOptions, ClaudePrintRunRequest, ClaudePrintRunResult, } from './durable/ClaudePrintRunner.js';
|
|
34
|
+
export { ClaudePrintAgentService } from './durable/ClaudePrintAgentService.js';
|
|
35
|
+
export type { ClaudePrintAgentServiceOptions, ClaudePrintSendResult, } from './durable/ClaudePrintAgentService.js';
|
|
36
36
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,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,WAAW,EACX,iBAAiB,EACjB,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,EAC3B,6BAA6B,EAC7B,gBAAgB,GACnB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACR,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,YAAY,EACR,uBAAuB,EACvB,6BAA6B,EAC7B,gBAAgB,EAChB,oBAAoB,GACvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EACR,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,YAAY,EACR,8BAA8B,EAC9B,qBAAqB,GACxB,MAAM,sCAAsC,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';
|
|
@@ -15,11 +15,11 @@ export { AgentRegistry, RenameNotFoundError, RenameConflictError } from './utils
|
|
|
15
15
|
export { TmuxManager } from './terminal/TmuxManager.js';
|
|
16
16
|
export { AGENTS } from './utils/agents.js';
|
|
17
17
|
export { getAgentRequestPath, readLatestAgentRequest, writeAgentRequest } from './utils/agent-requests.js';
|
|
18
|
-
export {
|
|
19
|
-
export {
|
|
20
|
-
export { LocalProcessInspector } from './
|
|
21
|
-
export { ClaudeCliProbe } from './
|
|
22
|
-
export { ClaudePrintRunner } from './
|
|
23
|
-
export { ClaudePrintAgentService } from './
|
|
18
|
+
export { AGENT_MODES, DurableAgentError, DurableAgentBusyError, DurableAgentNotFoundError, DurableAgentRepositoryError, DurableAgentNameConflictError, ClaudePrintError } from './durable/DurableAgent.js';
|
|
19
|
+
export { DurableAgentRepository } from './durable/DurableAgentRepository.js';
|
|
20
|
+
export { LocalProcessInspector } from './durable/DurableAgentRepository.js';
|
|
21
|
+
export { ClaudeCliProbe } from './durable/ClaudeCliProbe.js';
|
|
22
|
+
export { ClaudePrintRunner } from './durable/ClaudePrintRunner.js';
|
|
23
|
+
export { ClaudePrintAgentService } from './durable/ClaudePrintAgentService.js';
|
|
24
24
|
|
|
25
25
|
//# sourceMappingURL=index.js.map
|
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
|
|
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 AGENT_MODES,\n DurableAgentError,\n DurableAgentBusyError,\n DurableAgentNotFoundError,\n DurableAgentRepositoryError,\n DurableAgentNameConflictError,\n ClaudePrintError,\n} from './durable/DurableAgent.js';\nexport type {\n DurableAgent,\n DurableAgentState,\n DurableSessionHealth,\n DurableRunStatus,\n DurableActiveRun,\n DurableLastResult,\n ProcessIdentity,\n} from './durable/DurableAgent.js';\nexport { DurableAgentRepository } from './durable/DurableAgentRepository.js';\nexport { LocalProcessInspector } from './durable/DurableAgentRepository.js';\nexport type {\n CreateDurableAgentInput,\n DurableAgentRepositoryOptions,\n ProcessInspector,\n DurableRunCompletion,\n} from './durable/DurableAgentRepository.js';\nexport { ClaudeCliProbe } from './durable/ClaudeCliProbe.js';\nexport type { ClaudeCliProbeOptions } from './durable/ClaudeCliProbe.js';\nexport { ClaudePrintRunner } from './durable/ClaudePrintRunner.js';\nexport type {\n ClaudePrintRunnerOptions,\n ClaudePrintRunRequest,\n ClaudePrintRunResult,\n} from './durable/ClaudePrintRunner.js';\nexport { ClaudePrintAgentService } from './durable/ClaudePrintAgentService.js';\nexport type {\n ClaudePrintAgentServiceOptions,\n ClaudePrintSendResult,\n} from './durable/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","AGENT_MODES","DurableAgentError","DurableAgentBusyError","DurableAgentNotFoundError","DurableAgentRepositoryError","DurableAgentNameConflictError","ClaudePrintError","DurableAgentRepository","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,WAAW,EACXC,iBAAiB,EACjBC,qBAAqB,EACrBC,yBAAyB,EACzBC,2BAA2B,EAC3BC,6BAA6B,EAC7BC,gBAAgB,QACb,4BAA4B;AAUnC,SAASC,sBAAsB,QAAQ,sCAAsC;AAC7E,SAASC,qBAAqB,QAAQ,sCAAsC;AAO5E,SAASC,cAAc,QAAQ,8BAA8B;AAE7D,SAASC,iBAAiB,QAAQ,iCAAiC;AAMnE,SAASC,uBAAuB,QAAQ,uCAAuC"}
|
|
@@ -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;
|
|
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":"AAAA,OAAOA,QAAQ,KAAK;AACpB,OAAOC,UAAU,OAAO;AAExB,SACIC,kBAAkB,EAClBC,0BAA0B,QACvB,uBAAuB;AAE9B,OAAO,MAAMC,4BAA4BC;;IACrC,YAAY,AAAOC,SAAiB,CAAE;QAClC,KAAK,CAAC,CAAC,OAAO,EAAEA,UAAU,wBAAwB,CAAC,QADpCA,YAAAA;QAEf,IAAI,CAACC,IAAI,GAAG;IAChB;AACJ;AAEA,OAAO,MAAMC,4BAA4BH;;IACrC,YAAY,AAAOC,SAAiB,CAAE;QAClC,KAAK,CAAC,CAAC,OAAO,EAAEA,UAAU,oBAAoB,CAAC,QADhCA,YAAAA;QAEf,IAAI,CAACC,IAAI,GAAG;IAChB;AACJ;AAyBA,MAAME,wBAAwBR,KAAKS,IAAI,CAACV,GAAGW,OAAO,IAAI,cAAc;AACpE,MAAMC,4BAA4B;AAElC,IAAIC,kBAAwC;AAQ5C,OAAO,MAAMC;IACDC,GAAuB;IACdC,IAAgB;IAChBC,gBAAwB;IACjCC,aAAiC;IAEzC,YAAYC,WAAmBV,qBAAqB,EAAEW,UAAgC,CAAC,CAAC,CAAE;QACtF,IAAI,CAACJ,GAAG,GAAGI,QAAQJ,GAAG,IAAK,CAAA,IAAM,IAAIK,MAAK;QAC1C,IAAI,CAACJ,eAAe,GAAGG,QAAQH,eAAe,IAAIL;QAClD,IAAI,CAACG,EAAE,GAAG,IAAIb,mBAAmB;YAC7BoB,QAAQnB,2BAA2BgB;YACnCI,SAASH,QAAQI,mBAAmB;QACxC;IACJ;IAEA,OAAOC,UAAyB;QAC5B,IAAI,CAACZ,iBAAiB;YAClBA,kBAAkB,IAAIC;QAC1B;QACA,OAAOD;IACX;IAEQa,WAAWC,GAAgB,EAAiB;QAChD,OAAO;YACHpB,MAAMoB,IAAIpB,IAAI;YACdqB,MAAMD,IAAIC,IAAI;YACdC,KAAKF,IAAIE,GAAG;YACZC,aAAaH,IAAII,YAAY;YAC7BC,KAAKL,IAAIK,GAAG;YACZC,WAAWN,IAAIO,UAAU;YACzBC,WAAWR,IAAIS,UAAU;YACzBC,iBAAiBV,IAAIW,iBAAiB;QAC1C;IACJ;IAEQC,WAAWC,QAAuB,EAAEC,QAAmC,EAAiB;QAC5F,IAAI,CAACA,UAAU,OAAOD;QACtB,MAAME,oBAAoBC,QAAQH,SAASV,WAAW;QACtD,OAAO;YACH,GAAGW,QAAQ;YACXlC,MAAMmC,oBAAoBF,SAASjC,IAAI,GAAGkC,SAASlC,IAAI;YACvDuB,aAAaU,SAASV,WAAW,IAAIW,SAASX,WAAW;YACzDE,KAAKQ,SAASR,GAAG,IAAIS,SAAST,GAAG;YACjCC,WAAWQ,SAASR,SAAS,IAAIO,SAASP,SAAS;YACnDE,WAAWK,SAASL,SAAS,IAAIM,SAASN,SAAS;YACnDE,iBAAiBG,SAASH,eAAe,IAAII,SAASJ,eAAe;QACzE;IACJ;IAEQO,eAAehB,IAAe,EAAEC,GAAW,EAA6B;QAC5E,MAAMF,MAAM,IAAI,CAACZ,EAAE,CAAC8B,QAAQ,CACxB,mDACA;YAACjB;YAAMC;SAAI;QAEf,OAAOF,MAAM,IAAI,CAACD,UAAU,CAACC,OAAOmB;IACxC;IAEQC,WAAWxC,IAAY,EAA6B;QACxD,MAAMoB,MAAM,IAAI,CAACZ,EAAE,CAAC8B,QAAQ,CAAc,uCAAuC;YAACtC;SAAK;QACvF,OAAOoB,MAAM,IAAI,CAACD,UAAU,CAACC,OAAOmB;IACxC;IAEQE,iBAAiBpB,IAAe,EAAEC,GAAW,EAAmB;QACpE,OAAO,IAAI,CAACd,EAAE,CAACkC,KAAK,CAChB,oDACA;YAACpB;YAAKD;SAAK,EACbsB,GAAG,CAAC,CAACvB,MAAQ,IAAI,CAACD,UAAU,CAACC;IACnC;IAEQwB,aAAaC,IAAmB,EAAEC,KAAoB,EAAW;QACrE,OAAOD,KAAK7C,IAAI,KAAK8C,MAAM9C,IAAI,IACxB6C,KAAKxB,IAAI,KAAKyB,MAAMzB,IAAI,IACxBwB,KAAKvB,GAAG,KAAKwB,MAAMxB,GAAG,IACtBuB,KAAKtB,WAAW,KAAKuB,MAAMvB,WAAW,IACtCsB,KAAKpB,GAAG,KAAKqB,MAAMrB,GAAG,IACtBoB,KAAKnB,SAAS,KAAKoB,MAAMpB,SAAS,IAClCmB,KAAKjB,SAAS,KAAKkB,MAAMlB,SAAS,IAClCiB,KAAKf,eAAe,KAAKgB,MAAMhB,eAAe;IACzD;IAEQiB,mBAAmB/C,IAAY,EAAEqB,IAAe,EAAEC,GAAW,EAAQ;QACzE,MAAM0B,WAAW,IAAI,CAACR,UAAU,CAACxC;QACjC,IAAI,CAACgD,UAAU;QACf,IAAIA,SAAS3B,IAAI,KAAKA,QAAQ2B,SAAS1B,GAAG,KAAKA,KAAK;QACpD,IAAI,CAAC,IAAI,CAAC2B,OAAO,CAACD,WAAW;YACzB,IAAI,CAACxC,EAAE,CAAC0C,OAAO,CAAC,iDAAiD;gBAACF,SAAS3B,IAAI;gBAAE2B,SAAS1B,GAAG;aAAC;QAClG;IACJ;IAEQ6B,eAAeC,KAAoB,EAAQ;QAC/C,IAAI,CAAC5C,EAAE,CAAC6C,QAAQ,CAACC,OAAO,CAAC,CAAC;;;;;;;;;;;;;;;QAe1B,CAAC,EAAEC,GAAG,CAAC;YAAE,GAAGH,KAAK;YAAEI,WAAW,IAAI,CAAC/C,GAAG,GAAGgD,WAAW;QAAG;IAC3D;IAEQC,WAAWzB,QAAuB,EAAW;QACjD,MAAMC,WAAW,IAAI,CAACG,cAAc,CAACJ,SAASZ,IAAI,EAAEY,SAASX,GAAG;QAChE,MAAMqC,SAAS,IAAI,CAAC3B,UAAU,CAACC,UAAUC;QACzC,OAAO,CAACA,YACD,CAAC,IAAI,CAACU,YAAY,CAACe,QAAQzB,aAC3B,IAAI,CAACO,gBAAgB,CAACR,SAASZ,IAAI,EAAEY,SAASX,GAAG,EAAEsC,MAAM,GAAG;IACvE;IAEQC,KAAK5B,QAAuB,EAAQ;QACxC,MAAMC,WAAW,IAAI,CAACG,cAAc,CAACJ,SAASZ,IAAI,EAAEY,SAASX,GAAG;QAChE,MAAMqC,SAAS,IAAI,CAAC3B,UAAU,CAACC,UAAUC;QACzC,MAAM4B,eAAe,IAAI,CAACrB,gBAAgB,CAACR,SAASZ,IAAI,EAAEY,SAASX,GAAG;QACtE,IAAIY,YAAY,IAAI,CAACU,YAAY,CAACe,QAAQzB,aAAa4B,aAAaF,MAAM,KAAK,GAAG;QAElF,KAAK,MAAMZ,YAAYc,aAAc;YACjC,IAAI,CAACtD,EAAE,CAAC0C,OAAO,CAAC,iDAAiD;gBAACF,SAAS3B,IAAI;gBAAE2B,SAAS1B,GAAG;aAAC;QAClG;QACA,IAAIY,YAAY,IAAI,CAACU,YAAY,CAACe,QAAQzB,WAAW;QAErD,IAAI,CAACa,kBAAkB,CAACY,OAAO3D,IAAI,EAAE2D,OAAOtC,IAAI,EAAEsC,OAAOrC,GAAG;QAC5D,IAAI,CAAC6B,cAAc,CAACQ;IACxB;IAEAV,QAAQG,KAAoB,EAAW;QACnC,IAAI;YACAW,QAAQC,IAAI,CAACZ,MAAM9B,GAAG,EAAE;YACxB,OAAO;QACX,EAAE,OAAO2C,OAAO;YACZ,MAAMC,OAAOD,SAAS,OAAOA,UAAU,YAAY,UAAUA,QACvDA,MAAMC,IAAI,GACV3B;YACN,OAAO2B,SAAS;QACpB;IACJ;IAEQC,QAAQC,KAAa,EAAQ;QACjC,MAAMC,UAAU,IAAI,CAACC,IAAI;QACzB,MAAMC,QAAQF,QAAQG,MAAM,CAAC,CAACC,IAAM,CAAC,IAAI,CAACxB,OAAO,CAACwB;QAClD,IAAIF,MAAMX,MAAM,GAAG,GAAG;YAClB,IAAI,CAACpD,EAAE,CAACkE,WAAW,CAAC;gBAChB,KAAK,MAAMtB,SAASmB,MAAO;oBACvB,IAAI,CAAC/D,EAAE,CAAC0C,OAAO,CAAC,iDAAiD;wBAACE,MAAM/B,IAAI;wBAAE+B,MAAM9B,GAAG;qBAAC;gBAC5F;YACJ;QACJ;QACA,IAAI,CAACX,YAAY,GAAGyD;IACxB;IAEAO,QAAc;QACV,IAAI,CAACR,OAAO,CAAC,IAAI,CAAC1D,GAAG,GAAGmE,OAAO;IACnC;IAEAC,aAAmB;QACf,MAAMT,QAAQ,IAAI,CAAC3D,GAAG,GAAGmE,OAAO;QAChC,MAAME,UAAU,IAAI,CAACnE,YAAY,KAAK4B,YAAYA,YAAY6B,QAAQ,IAAI,CAACzD,YAAY;QACvF,IAAImE,YAAYvC,aAAauC,WAAW,KAAKA,UAAU,IAAI,CAACpE,eAAe,EAAE;QAC7E,IAAI,CAACyD,OAAO,CAACC;IACjB;IAEAW,SAAS3B,KAAoB,EAAQ;QACjC,IAAI,CAAC4B,aAAa,CAAC;YAAC5B;SAAM;IAC9B;IAEA4B,cAAcX,OAAwB,EAAQ;QAC1C,IAAIA,QAAQT,MAAM,KAAK,GAAG;QAC1B,IAAI,CAACS,QAAQY,IAAI,CAAC,CAAC7B,QAAU,IAAI,CAACM,UAAU,CAACN,SAAS;QACtD,IAAI,CAAC5C,EAAE,CAACkE,WAAW,CAAC;YAChB,KAAK,MAAMzC,YAAYoC,QAAS;gBAC5B,IAAI,CAACR,IAAI,CAAC5B;YACd;QACJ;IACJ;IAEAiD,OAAOC,WAAmB,EAAEC,OAAe,EAAQ;QAC/C,MAAMlD,WAAW,IAAI,CAACM,UAAU,CAAC2C;QACjC,IAAI,CAACjD,UAAU;YACX,MAAM,IAAIrC,oBAAoBsF;QAClC;QACA,MAAMnC,WAAW,IAAI,CAACR,UAAU,CAAC4C;QACjC,IAAIpC,YAAY,IAAI,CAACC,OAAO,CAACD,WAAW;YACpC,MAAM,IAAI/C,oBAAoBmF;QAClC;QAEA,IAAI,CAAC5E,EAAE,CAACkE,WAAW,CAAC;YAChB,IAAI1B,UAAU;gBACV,IAAI,CAACxC,EAAE,CAAC0C,OAAO,CAAC,iDAAiD;oBAACF,SAAS3B,IAAI;oBAAE2B,SAAS1B,GAAG;iBAAC;YAClG;YACA,IAAI,CAACd,EAAE,CAAC0C,OAAO,CACX,yEACA;gBAACkC;gBAAS,IAAI,CAAC3E,GAAG,GAAGgD,WAAW;gBAAIvB,SAASb,IAAI;gBAAEa,SAASZ,GAAG;aAAC;QAExE;IACJ;IAEA+D,OAAOrF,IAAY,EAAwB;QACvC,OAAO,IAAI,CAACwC,UAAU,CAACxC,SAAS;IACpC;IAEAsE,OAAwB;QACpB,MAAMgB,OAAO,IAAI,CAAC9E,EAAE,CAACkC,KAAK,CAAc;QACxC,OAAO4C,KAAK3C,GAAG,CAAC,CAACvB,MAAQ,IAAI,CAACD,UAAU,CAACC;IAC7C;AACJ"}
|
|
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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"}
|
package/package.json
CHANGED
package/src/AgentManager.ts
CHANGED
|
@@ -26,6 +26,13 @@ export interface ListAgentsOptions {
|
|
|
26
26
|
sortBy?: AgentSortKey;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export class AgentNotRunningError extends Error {
|
|
30
|
+
constructor(public agentName: string) {
|
|
31
|
+
super(`Agent "${agentName}" is no longer running.`);
|
|
32
|
+
this.name = 'AgentNotRunningError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
29
36
|
/**
|
|
30
37
|
* Agent Manager Class
|
|
31
38
|
*
|
|
@@ -194,6 +201,8 @@ export class AgentManager {
|
|
|
194
201
|
const entry = preExistingByIdentity.get(identityKey(agent.type, agent.pid));
|
|
195
202
|
if (entry) {
|
|
196
203
|
agent.name = entry.name;
|
|
204
|
+
agent.pinned = entry.pinned;
|
|
205
|
+
if (entry.pinned && entry.updatedAt) agent.lastActive = new Date(entry.updatedAt);
|
|
197
206
|
}
|
|
198
207
|
}
|
|
199
208
|
|
|
@@ -211,9 +220,21 @@ export class AgentManager {
|
|
|
211
220
|
startedAt: existing?.startedAt ?? new Date().toISOString(),
|
|
212
221
|
sessionId: agent.sessionId,
|
|
213
222
|
sessionFilePath: agent.sessionFilePath ?? '',
|
|
223
|
+
pinned: existing?.pinned ?? agent.pinned ?? false,
|
|
214
224
|
};
|
|
215
225
|
}
|
|
216
226
|
|
|
227
|
+
togglePin(agentName: string): boolean {
|
|
228
|
+
const entry = this.registry.lookup(agentName);
|
|
229
|
+
if (!entry || !this.registry.isAlive(entry)) {
|
|
230
|
+
if (entry) this.registry.prune();
|
|
231
|
+
throw new AgentNotRunningError(agentName);
|
|
232
|
+
}
|
|
233
|
+
const pinned = this.registry.togglePin(entry.type, entry.pid);
|
|
234
|
+
if (pinned === null) throw new AgentNotRunningError(agentName);
|
|
235
|
+
return pinned;
|
|
236
|
+
}
|
|
237
|
+
|
|
217
238
|
/**
|
|
218
239
|
* List historical sessions across every registered adapter.
|
|
219
240
|
*
|
|
@@ -554,6 +554,58 @@ describe('AgentManager', () => {
|
|
|
554
554
|
expect(writes).toEqual([]);
|
|
555
555
|
});
|
|
556
556
|
|
|
557
|
+
it('exposes a persisted pin and preserves it across a changed poll refresh', async () => {
|
|
558
|
+
const adapter = new MockAdapter('claude', [
|
|
559
|
+
createMockAgent({ name: 'pinned', pid: process.pid, sessionId: 'before' }),
|
|
560
|
+
]);
|
|
561
|
+
scopedManager.registerAdapter(adapter);
|
|
562
|
+
await scopedManager.listAgents();
|
|
563
|
+
registry.togglePin('claude', process.pid);
|
|
564
|
+
adapter.setAgents([
|
|
565
|
+
createMockAgent({ name: 'pinned', pid: process.pid, sessionId: 'after' }),
|
|
566
|
+
]);
|
|
567
|
+
|
|
568
|
+
const agents = await scopedManager.listAgents();
|
|
569
|
+
|
|
570
|
+
expect(agents[0].pinned).toBe(true);
|
|
571
|
+
expect(registry.lookup('pinned')).toMatchObject({ sessionId: 'after', pinned: true });
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('uses registry updated_at as lastActive for pinned recency ordering', async () => {
|
|
575
|
+
const adapter = new MockAdapter('claude', [
|
|
576
|
+
createMockAgent({
|
|
577
|
+
name: 'recently-pinned',
|
|
578
|
+
pid: process.pid,
|
|
579
|
+
lastActive: new Date('2026-01-01T00:00:00.000Z'),
|
|
580
|
+
}),
|
|
581
|
+
]);
|
|
582
|
+
scopedManager.registerAdapter(adapter);
|
|
583
|
+
await scopedManager.listAgents();
|
|
584
|
+
nowMs += 60_000;
|
|
585
|
+
scopedManager.togglePin('recently-pinned');
|
|
586
|
+
|
|
587
|
+
const agents = await scopedManager.listAgents();
|
|
588
|
+
|
|
589
|
+
expect(agents[0].pinned).toBe(true);
|
|
590
|
+
expect(agents[0].lastActive.toISOString()).toBe('2026-08-14T10:01:00.000Z');
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it('preserves adapter lastActive for unpinned agents', async () => {
|
|
594
|
+
scopedManager.registerAdapter(new MockAdapter('claude', [
|
|
595
|
+
createMockAgent({
|
|
596
|
+
name: 'unpinned',
|
|
597
|
+
pid: process.pid,
|
|
598
|
+
lastActive: new Date('2026-01-01T00:00:00.000Z'),
|
|
599
|
+
}),
|
|
600
|
+
]));
|
|
601
|
+
|
|
602
|
+
await scopedManager.listAgents();
|
|
603
|
+
const agents = await scopedManager.listAgents();
|
|
604
|
+
|
|
605
|
+
expect(agents[0].pinned).toBe(false);
|
|
606
|
+
expect(agents[0].lastActive.toISOString()).toBe('2026-01-01T00:00:00.000Z');
|
|
607
|
+
});
|
|
608
|
+
|
|
557
609
|
it('persists changed fields once in one write transaction', async () => {
|
|
558
610
|
const adapter = new MockAdapter('claude', [
|
|
559
611
|
createMockAgent({ name: 'changing', pid: process.pid, projectPath: '/cwd/before' }),
|
|
@@ -645,6 +697,71 @@ describe('AgentManager', () => {
|
|
|
645
697
|
});
|
|
646
698
|
});
|
|
647
699
|
|
|
700
|
+
describe('togglePin', () => {
|
|
701
|
+
it('resolves the agent name to its process identity and toggles the pin', () => {
|
|
702
|
+
const registry = new AgentRegistry(path.join(tmpDir, 'toggle.json'));
|
|
703
|
+
const scopedManager = new AgentManager(registry);
|
|
704
|
+
registry.register({
|
|
705
|
+
name: 'renamed-agent',
|
|
706
|
+
type: 'claude',
|
|
707
|
+
pid: process.pid,
|
|
708
|
+
tmuxSession: '',
|
|
709
|
+
cwd: '/tmp',
|
|
710
|
+
startedAt: '2026-08-16T00:00:00.000Z',
|
|
711
|
+
sessionId: 'session',
|
|
712
|
+
sessionFilePath: '',
|
|
713
|
+
pinned: false,
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
expect(scopedManager.togglePin('renamed-agent')).toBe(true);
|
|
717
|
+
expect(registry.lookup('renamed-agent')?.pinned).toBe(true);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it('reports when the agent is no longer running', () => {
|
|
721
|
+
expect(() => manager.togglePin('missing')).toThrow(/no longer running/i);
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
it('rejects a dead process and prunes its row', () => {
|
|
725
|
+
const registry = new AgentRegistry(path.join(tmpDir, 'dead-toggle.json'));
|
|
726
|
+
const scopedManager = new AgentManager(registry);
|
|
727
|
+
registry.register({
|
|
728
|
+
name: 'dead',
|
|
729
|
+
type: 'claude',
|
|
730
|
+
pid: 999999,
|
|
731
|
+
tmuxSession: '',
|
|
732
|
+
cwd: '/tmp',
|
|
733
|
+
startedAt: '2026-08-16T00:00:00.000Z',
|
|
734
|
+
sessionId: 'session',
|
|
735
|
+
sessionFilePath: '',
|
|
736
|
+
pinned: false,
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
expect(() => scopedManager.togglePin('dead')).toThrow(/no longer running/i);
|
|
740
|
+
expect(registry.lookup('dead')).toBeNull();
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
it('surfaces a clear readonly mutation error', () => {
|
|
744
|
+
const regPath = path.join(tmpDir, 'readonly-toggle.json');
|
|
745
|
+
const writable = new AgentRegistry(regPath);
|
|
746
|
+
writable.register({
|
|
747
|
+
name: 'readonly-agent',
|
|
748
|
+
type: 'claude',
|
|
749
|
+
pid: process.pid,
|
|
750
|
+
tmuxSession: '',
|
|
751
|
+
cwd: '/tmp',
|
|
752
|
+
startedAt: '2026-08-16T00:00:00.000Z',
|
|
753
|
+
sessionId: 'session',
|
|
754
|
+
sessionFilePath: '',
|
|
755
|
+
pinned: false,
|
|
756
|
+
});
|
|
757
|
+
const readonlyManager = new AgentManager(new AgentRegistry(regPath, { readonly: true }));
|
|
758
|
+
|
|
759
|
+
expect(() => readonlyManager.togglePin('readonly-agent')).toThrow(
|
|
760
|
+
'Agent registry is readonly; cannot toggle pin.',
|
|
761
|
+
);
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
|
|
648
765
|
describe('clear', () => {
|
|
649
766
|
it('should remove all adapters', () => {
|
|
650
767
|
manager.registerAdapter(new MockAdapter('claude'));
|