/** * Drizzle Agent Repository Adapter * * Implements AgentRepository interface using Drizzle ORM. */ import { eq, getTableColumns } from 'drizzle-orm'; import { nanoid } from 'nanoid'; import type { DrizzleDatabase } from '../../index.js'; import { agents, tasks, phases, initiatives, type Agent } from '../../schema.js'; import type { AgentRepository, AgentStatus, AgentWithContext, CreateAgentData, UpdateAgentData, } from '../agent-repository.js'; /** * Drizzle adapter for AgentRepository. * * Uses dependency injection for database instance, * enabling isolated test databases. */ export class DrizzleAgentRepository implements AgentRepository { constructor(private db: DrizzleDatabase) {} async create(data: CreateAgentData): Promise { const id = nanoid(); const now = new Date(); const [created] = await this.db.insert(agents).values({ id, name: data.name, taskId: data.taskId ?? null, initiativeId: data.initiativeId ?? null, sessionId: data.sessionId ?? null, worktreeId: data.worktreeId, provider: data.provider ?? 'claude', accountId: data.accountId ?? null, status: data.status ?? 'idle', mode: data.mode ?? 'execute', createdAt: now, updatedAt: now, }).returning(); return created; } async findById(id: string): Promise { const result = await this.db .select() .from(agents) .where(eq(agents.id, id)) .limit(1); return result[0] ?? null; } async findByName(name: string): Promise { const result = await this.db .select() .from(agents) .where(eq(agents.name, name)) .limit(1); return result[0] ?? null; } async findByTaskId(taskId: string): Promise { const result = await this.db .select() .from(agents) .where(eq(agents.taskId, taskId)) .limit(1); return result[0] ?? null; } async findBySessionId(sessionId: string): Promise { const result = await this.db .select() .from(agents) .where(eq(agents.sessionId, sessionId)) .limit(1); return result[0] ?? null; } async findAll(): Promise { return this.db.select().from(agents); } async findByStatus(status: AgentStatus): Promise { return this.db.select().from(agents).where(eq(agents.status, status)); } async update(id: string, data: UpdateAgentData): Promise { const now = new Date(); const [updated] = await this.db .update(agents) .set({ ...data, updatedAt: now }) .where(eq(agents.id, id)) .returning(); if (!updated) { throw new Error(`Agent not found: ${id}`); } return updated; } async delete(id: string): Promise { const [deleted] = await this.db.delete(agents).where(eq(agents.id, id)).returning(); if (!deleted) { throw new Error(`Agent not found: ${id}`); } } async findWaitingWithContext(): Promise { return this.db .select({ ...getTableColumns(agents), taskName: tasks.name, phaseName: phases.name, initiativeName: initiatives.name, taskDescription: tasks.description, }) .from(agents) .where(eq(agents.status, 'waiting_for_input')) .leftJoin(tasks, eq(agents.taskId, tasks.id)) .leftJoin(phases, eq(tasks.phaseId, phases.id)) .leftJoin(initiatives, eq(agents.initiativeId, initiatives.id)); } }