AkurAI Build
Menu

popagent

public

Latest change 6edc7e928c57dab23c0973ed3f7ee70b3ebb72d5 - Initial popagent baseline by AkurAI Build

import type { HookRunRecord } from "./hooks";
import { storage } from "./storage";
import type { HookAuditRun } from "./api-types";
import { retryableInit } from "./retryable-init";
const MAX_HOOK_RUNS_PER_SESSION = 500;
const HOOK_AUDIT_RETENTION_DAYS = 30;



export class HookAuditStore {
  readonly storage = storage;
  private readonly initializeOnce = retryableInit(() => this.initialize());
  constructor(
    private readonly maxRunsPerSession = MAX_HOOK_RUNS_PER_SESSION,
    private readonly retentionDays = HOOK_AUDIT_RETENTION_DAYS,
  ) {}


  init(): Promise<void> {
    return this.initializeOnce();
  }

  async record(run: HookRunRecord): Promise<void> {
    await this.init();
    await this.storage.db.tx(async (db) => {
      await db.none(`
        INSERT INTO popagent_hook_runs
          (id, event_id, session_id, turn_id, tool_call_id, event_name, handler_id,
           started_at, completed_at, duration_ms, status, reason, error)
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
      `, [
        run.id, run.eventId, run.sessionId, run.turnId ?? null, run.toolCallId ?? null,
        run.eventName, run.handlerId, run.startedAt, run.completedAt, run.durationMs,
        run.status, run.reason ?? null, run.error ?? null,
      ]);
    });
    await this.cleanupAll(run.sessionId);
  }

  async cleanupAll(sessionId?: string): Promise<void> {
    await this.init();
    await this.storage.db.tx(async (db) => {
      await db.none(
        "DELETE FROM popagent_hook_runs WHERE completed_at < NOW() - ($1 * INTERVAL '1 day')",
        [this.retentionDays],
      );
      if (sessionId) await db.none(`
        DELETE FROM popagent_hook_runs WHERE id IN (
          SELECT id FROM popagent_hook_runs WHERE session_id = $1
          ORDER BY started_at DESC, id DESC OFFSET $2
        )
      `, [sessionId, this.maxRunsPerSession]);
      else await db.none(`
        DELETE FROM popagent_hook_runs WHERE id IN (
          SELECT id FROM (
            SELECT id, ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY started_at DESC, id DESC) AS rn
            FROM popagent_hook_runs
          ) ranked WHERE rn > $1
        )
      `, [this.maxRunsPerSession]);
    });
  }

  async listForSession(sessionId: string): Promise<HookAuditRun[]> {
    await this.init();
    return this.storage.db.any<HookAuditRun>(`
      WITH recent AS (
        SELECT id, event_id, session_id, turn_id, tool_call_id, event_name, handler_id,
               started_at, completed_at, duration_ms, status, reason, error
        FROM popagent_hook_runs
        WHERE session_id = $1
        ORDER BY started_at DESC, id DESC
        LIMIT $2
      )
      SELECT id, event_id AS "eventId", session_id AS "sessionId",
             turn_id AS "turnId", tool_call_id AS "toolCallId",
             event_name AS "eventName", handler_id AS "handlerId",
             started_at::text AS "startedAt", completed_at::text AS "completedAt",
             duration_ms AS "durationMs", status, reason, error
      FROM recent
      ORDER BY started_at ASC, id ASC
    `, [sessionId, this.maxRunsPerSession]);
  }

  async deleteForSession(sessionId: string): Promise<void> {
    await this.init();
    await this.storage.db.none("DELETE FROM popagent_hook_runs WHERE session_id = $1", [sessionId]);
  }

  private async initialize(): Promise<void> {
    await this.storage.init();
    await this.storage.db.none(`
      CREATE TABLE IF NOT EXISTS popagent_hook_runs (
        id TEXT PRIMARY KEY,
        event_id TEXT NOT NULL,
        session_id TEXT NOT NULL,
        turn_id TEXT,
        tool_call_id TEXT,
        event_name TEXT NOT NULL,
        handler_id TEXT NOT NULL,
        started_at TIMESTAMPTZ NOT NULL,
        completed_at TIMESTAMPTZ NOT NULL,
        duration_ms DOUBLE PRECISION NOT NULL,
        status TEXT NOT NULL,
        reason TEXT,
        error TEXT
      );
      CREATE INDEX IF NOT EXISTS popagent_hook_runs_session_idx
        ON popagent_hook_runs (session_id, started_at DESC);
      CREATE INDEX IF NOT EXISTS popagent_hook_runs_completed_idx
        ON popagent_hook_runs (completed_at);
    `);
  }
}

export const hookAudit = new HookAuditStore();