All files / src/db/repositories/drizzle agent.ts

100% Statements 23/23
100% Branches 26/26
100% Functions 10/10
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                                156x     89x 89x   89x                             88x       6x           6x       2x           2x       61x           61x       2x           2x       2x       4x       6x 6x           6x 1x     5x       2x   2x 1x        
/**
 * Drizzle Agent Repository Adapter
 *
 * Implements AgentRepository interface using Drizzle ORM.
 */
 
import { eq } from 'drizzle-orm';
import { nanoid } from 'nanoid';
import type { DrizzleDatabase } from '../../index.js';
import { agents, type Agent } from '../../schema.js';
import type {
  AgentRepository,
  AgentStatus,
  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<Agent> {
    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<Agent | null> {
    const result = await this.db
      .select()
      .from(agents)
      .where(eq(agents.id, id))
      .limit(1);
 
    return result[0] ?? null;
  }
 
  async findByName(name: string): Promise<Agent | null> {
    const result = await this.db
      .select()
      .from(agents)
      .where(eq(agents.name, name))
      .limit(1);
 
    return result[0] ?? null;
  }
 
  async findByTaskId(taskId: string): Promise<Agent | null> {
    const result = await this.db
      .select()
      .from(agents)
      .where(eq(agents.taskId, taskId))
      .limit(1);
 
    return result[0] ?? null;
  }
 
  async findBySessionId(sessionId: string): Promise<Agent | null> {
    const result = await this.db
      .select()
      .from(agents)
      .where(eq(agents.sessionId, sessionId))
      .limit(1);
 
    return result[0] ?? null;
  }
 
  async findAll(): Promise<Agent[]> {
    return this.db.select().from(agents);
  }
 
  async findByStatus(status: AgentStatus): Promise<Agent[]> {
    return this.db.select().from(agents).where(eq(agents.status, status));
  }
 
  async update(id: string, data: UpdateAgentData): Promise<Agent> {
    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<void> {
    const [deleted] = await this.db.delete(agents).where(eq(agents.id, id)).returning();
 
    if (!deleted) {
      throw new Error(`Agent not found: ${id}`);
    }
  }
}