import {
  __esm
} from "./chunk-2ESYSVXG.js";

// src/db/migrations.ts
function runMigrations(db) {
  db.exec(`
    CREATE TABLE IF NOT EXISTS api_keys (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      hash TEXT NOT NULL UNIQUE,
      prefix TEXT NOT NULL,
      permissions_json TEXT NOT NULL DEFAULT '[]',
      created_at INTEGER NOT NULL,
      revoked_at INTEGER
    );

    CREATE TABLE IF NOT EXISTS settings (
      key TEXT PRIMARY KEY,
      value TEXT,
      type TEXT NOT NULL DEFAULT 'text',
      updated_at INTEGER NOT NULL
    );

    CREATE TABLE IF NOT EXISTS agents (
      id TEXT PRIMARY KEY,
      current_version INTEGER NOT NULL,
      name TEXT NOT NULL,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      archived_at INTEGER
    );

    CREATE TABLE IF NOT EXISTS agent_versions (
      agent_id TEXT NOT NULL,
      version INTEGER NOT NULL,
      model TEXT NOT NULL,
      system TEXT,
      tools_json TEXT NOT NULL DEFAULT '[]',
      mcp_servers_json TEXT NOT NULL DEFAULT '{}',
      created_at INTEGER NOT NULL,
      -- backend column added in migration below (opencode adapter); default
      -- 'claude' so existing rows and new inserts that omit it work the same
      PRIMARY KEY (agent_id, version),
      FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE CASCADE
    );

    CREATE TABLE IF NOT EXISTS environments (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      config_json TEXT NOT NULL,
      state TEXT NOT NULL DEFAULT 'preparing',
      state_message TEXT,
      template_sandbox TEXT,
      checkpoint_id TEXT,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL DEFAULT 0,
      archived_at INTEGER
    );

    CREATE TABLE IF NOT EXISTS sessions (
      id TEXT PRIMARY KEY,
      agent_id TEXT NOT NULL,
      agent_version INTEGER NOT NULL,
      environment_id TEXT NOT NULL,
      sandbox_name TEXT,
      -- Legacy name: holds any backend's session id (claude's session_id or
      -- opencode's sessionID). Kept as claude_session_id to avoid schema
      -- churn; see lib/backends/types.ts for the abstraction.
      claude_session_id TEXT,
      status TEXT NOT NULL DEFAULT 'idle',
      stop_reason TEXT,
      title TEXT,
      metadata_json TEXT NOT NULL DEFAULT '{}',
      turn_count INTEGER NOT NULL DEFAULT 0,
      tool_calls_count INTEGER NOT NULL DEFAULT 0,
      active_seconds REAL NOT NULL DEFAULT 0,
      duration_seconds REAL NOT NULL DEFAULT 0,
      usage_input_tokens INTEGER NOT NULL DEFAULT 0,
      usage_output_tokens INTEGER NOT NULL DEFAULT 0,
      usage_cache_read_input_tokens INTEGER NOT NULL DEFAULT 0,
      usage_cache_creation_input_tokens INTEGER NOT NULL DEFAULT 0,
      usage_cost_usd REAL NOT NULL DEFAULT 0,
      last_seq INTEGER NOT NULL DEFAULT 0,
      idle_since INTEGER,
      parked_checkpoint_id TEXT,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      archived_at INTEGER,
      FOREIGN KEY (agent_id, agent_version) REFERENCES agent_versions(agent_id, version),
      FOREIGN KEY (environment_id) REFERENCES environments(id)
    );

    CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent_id);
    CREATE INDEX IF NOT EXISTS idx_sessions_env ON sessions(environment_id);
    CREATE INDEX IF NOT EXISTS idx_sessions_created_id ON sessions(created_at DESC, id);
    CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);

    CREATE TABLE IF NOT EXISTS events (
      id TEXT PRIMARY KEY,
      session_id TEXT NOT NULL,
      seq INTEGER NOT NULL,
      type TEXT NOT NULL,
      payload_json TEXT NOT NULL,
      processed_at INTEGER,
      received_at INTEGER NOT NULL,
      origin TEXT NOT NULL,
      idempotency_key TEXT,
      FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
    );

    CREATE UNIQUE INDEX IF NOT EXISTS idx_events_session_seq ON events(session_id, seq);
    CREATE UNIQUE INDEX IF NOT EXISTS idx_events_idem
      ON events(session_id, idempotency_key)
      WHERE idempotency_key IS NOT NULL;

    -- Proxy routing table: tracks which resource IDs belong to Anthropic's
    -- hosted MA API. When isProxied(id) returns true, the route handler
    -- forwards the request to api.anthropic.com instead of handling locally.
    CREATE TABLE IF NOT EXISTS proxy_resources (
      resource_id TEXT PRIMARY KEY,
      resource_type TEXT NOT NULL CHECK(resource_type IN ('agent','environment','session')),
      created_at INTEGER NOT NULL
    );

    -- Vault persistence: per-agent key-value stores that persist across sessions.
    CREATE TABLE IF NOT EXISTS vaults (
      id TEXT PRIMARY KEY,
      agent_id TEXT NOT NULL,
      name TEXT NOT NULL,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      FOREIGN KEY (agent_id) REFERENCES agents(id)
    );

    CREATE TABLE IF NOT EXISTS vault_entries (
      vault_id TEXT NOT NULL,
      key TEXT NOT NULL,
      value TEXT NOT NULL,
      updated_at INTEGER NOT NULL,
      PRIMARY KEY (vault_id, key),
      FOREIGN KEY (vault_id) REFERENCES vaults(id) ON DELETE CASCADE
    );
  `);
  const agentVersionCols = db.prepare(`PRAGMA table_info(agent_versions)`).all();
  if (!agentVersionCols.some((c) => c.name === "backend")) {
    db.exec(
      `ALTER TABLE agent_versions ADD COLUMN backend TEXT NOT NULL DEFAULT 'claude'`
    );
  }
  const sessionCols = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (!sessionCols.some((c) => c.name === "provider_name")) {
    db.exec(
      `ALTER TABLE sessions ADD COLUMN provider_name TEXT NOT NULL DEFAULT 'sprites'`
    );
  }
  const sessionCols2 = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (!sessionCols2.some((c) => c.name === "max_budget_usd")) {
    db.exec(`ALTER TABLE sessions ADD COLUMN max_budget_usd REAL`);
  }
  const avCols2 = db.prepare(`PRAGMA table_info(agent_versions)`).all();
  if (!avCols2.some((c) => c.name === "webhook_url")) {
    db.exec(`ALTER TABLE agent_versions ADD COLUMN webhook_url TEXT`);
  }
  if (!avCols2.some((c) => c.name === "webhook_events_json")) {
    db.exec(
      `ALTER TABLE agent_versions ADD COLUMN webhook_events_json TEXT NOT NULL DEFAULT '["session.status_idle","session.status_running","session.error"]'`
    );
  }
  const sessionCols3 = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (!sessionCols3.some((c) => c.name === "outcome_criteria_json")) {
    db.exec(`ALTER TABLE sessions ADD COLUMN outcome_criteria_json TEXT`);
  }
  const sessionCols4 = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (!sessionCols4.some((c) => c.name === "resources_json")) {
    db.exec(`ALTER TABLE sessions ADD COLUMN resources_json TEXT`);
  }
  const sessionCols5 = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (!sessionCols5.some((c) => c.name === "vault_ids_json")) {
    db.exec(`ALTER TABLE sessions ADD COLUMN vault_ids_json TEXT`);
  }
  const sessionCols6 = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (!sessionCols6.some((c) => c.name === "parent_session_id")) {
    db.exec(`ALTER TABLE sessions ADD COLUMN parent_session_id TEXT`);
  }
  if (!sessionCols6.some((c) => c.name === "thread_depth")) {
    db.exec(
      `ALTER TABLE sessions ADD COLUMN thread_depth INTEGER NOT NULL DEFAULT 0`
    );
  }
  const avCols3 = db.prepare(`PRAGMA table_info(agent_versions)`).all();
  if (!avCols3.some((c) => c.name === "threads_enabled")) {
    db.exec(
      `ALTER TABLE agent_versions ADD COLUMN threads_enabled INTEGER NOT NULL DEFAULT 0`
    );
  }
  const avCols4 = db.prepare(`PRAGMA table_info(agent_versions)`).all();
  if (!avCols4.some((c) => c.name === "confirmation_mode")) {
    db.exec(
      `ALTER TABLE agent_versions ADD COLUMN confirmation_mode INTEGER NOT NULL DEFAULT 0`
    );
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS memory_stores (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      description TEXT,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL
    )
  `);
  db.exec(`
    CREATE TABLE IF NOT EXISTS memories (
      id TEXT PRIMARY KEY,
      store_id TEXT NOT NULL REFERENCES memory_stores(id) ON DELETE CASCADE,
      path TEXT NOT NULL,
      content TEXT NOT NULL,
      content_sha256 TEXT NOT NULL,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      UNIQUE(store_id, path)
    )
  `);
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_sessions_parent
      ON sessions(parent_session_id) WHERE parent_session_id IS NOT NULL
  `);
  const avCols5 = db.prepare(`PRAGMA table_info(agent_versions)`).all();
  if (!avCols5.some((c) => c.name === "callable_agents_json")) {
    db.exec(
      `ALTER TABLE agent_versions ADD COLUMN callable_agents_json TEXT`
    );
  }
  const avCols = db.prepare("PRAGMA table_info(agent_versions)").all();
  if (!avCols.some((c) => c.name === "skills_json")) {
    db.exec("ALTER TABLE agent_versions ADD COLUMN skills_json TEXT NOT NULL DEFAULT '[]'");
  }
  const eventCols = db.prepare(`PRAGMA table_info(events)`).all();
  if (!eventCols.some((c) => c.name === "trace_id")) {
    db.exec(`ALTER TABLE events ADD COLUMN trace_id TEXT`);
  }
  if (!eventCols.some((c) => c.name === "span_id")) {
    db.exec(`ALTER TABLE events ADD COLUMN span_id TEXT`);
  }
  if (!eventCols.some((c) => c.name === "parent_span_id")) {
    db.exec(`ALTER TABLE events ADD COLUMN parent_span_id TEXT`);
  }
  db.exec(
    `CREATE INDEX IF NOT EXISTS idx_events_trace
       ON events(trace_id) WHERE trace_id IS NOT NULL`
  );
  const envCols = db.prepare(`PRAGMA table_info(environments)`).all();
  if (!envCols.some((c) => c.name === "description")) {
    db.exec(`ALTER TABLE environments ADD COLUMN description TEXT`);
  }
  if (!envCols.some((c) => c.name === "metadata_json")) {
    db.exec(
      `ALTER TABLE environments ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}'`
    );
  }
  const avColsModelConfig = db.prepare("PRAGMA table_info(agent_versions)").all();
  if (!avColsModelConfig.some((c) => c.name === "model_config_json")) {
    db.exec("ALTER TABLE agent_versions ADD COLUMN model_config_json TEXT NOT NULL DEFAULT '{}'");
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS files (
      id TEXT PRIMARY KEY,
      filename TEXT NOT NULL,
      size INTEGER NOT NULL,
      content_type TEXT NOT NULL,
      storage_path TEXT NOT NULL,
      scope_type TEXT,
      scope_id TEXT,
      created_at INTEGER NOT NULL
    )
  `);
  const filesCols = db.prepare("PRAGMA table_info(files)").all();
  if (!filesCols.some((c) => c.name === "scope_type")) {
    db.exec("ALTER TABLE files ADD COLUMN scope_type TEXT");
  }
  if (!filesCols.some((c) => c.name === "scope_id")) {
    db.exec("ALTER TABLE files ADD COLUMN scope_id TEXT");
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS anthropic_sync (
      local_id TEXT NOT NULL,
      resource_type TEXT NOT NULL CHECK(resource_type IN ('agent','environment','vault','session')),
      remote_id TEXT NOT NULL,
      synced_at INTEGER NOT NULL,
      config_hash TEXT,
      PRIMARY KEY (local_id, resource_type)
    )
  `);
  const apiKeyCols = db.prepare(`PRAGMA table_info(api_keys)`).all();
  if (!apiKeyCols.some((c) => c.name === "tenant_id")) {
    db.exec(`ALTER TABLE api_keys ADD COLUMN tenant_id TEXT`);
  }
  const sessionColsApiKey = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (!sessionColsApiKey.some((c) => c.name === "api_key_id")) {
    db.exec(`ALTER TABLE sessions ADD COLUMN api_key_id TEXT`);
  }
  db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_api_key_id ON sessions(api_key_id)`);
  const apiKeyColsBudget = db.prepare(`PRAGMA table_info(api_keys)`).all();
  if (!apiKeyColsBudget.some((c) => c.name === "budget_usd")) {
    db.exec(`ALTER TABLE api_keys ADD COLUMN budget_usd REAL`);
  }
  if (!apiKeyColsBudget.some((c) => c.name === "rate_limit_rpm")) {
    db.exec(`ALTER TABLE api_keys ADD COLUMN rate_limit_rpm INTEGER`);
  }
  if (!apiKeyColsBudget.some((c) => c.name === "spent_usd")) {
    db.exec(`ALTER TABLE api_keys ADD COLUMN spent_usd REAL NOT NULL DEFAULT 0`);
  }
  const agentColsFallback = db.prepare(`PRAGMA table_info(agents)`).all();
  if (!agentColsFallback.some((c) => c.name === "fallback_json")) {
    db.exec(`ALTER TABLE agents ADD COLUMN fallback_json TEXT`);
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS upstream_keys (
      id TEXT PRIMARY KEY,
      provider TEXT NOT NULL,
      hash TEXT NOT NULL UNIQUE,
      prefix TEXT NOT NULL,
      value_encrypted TEXT NOT NULL,
      weight INTEGER NOT NULL DEFAULT 1,
      disabled_at INTEGER,
      last_used_at INTEGER,
      created_at INTEGER NOT NULL
    )
  `);
  db.exec(
    `CREATE INDEX IF NOT EXISTS idx_upstream_keys_provider_active
       ON upstream_keys(provider, disabled_at, last_used_at)`
  );
  db.exec(`
    CREATE TABLE IF NOT EXISTS tenants (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      created_at INTEGER NOT NULL,
      archived_at INTEGER
    )
  `);
  for (const table of ["agents", "environments", "vaults", "sessions"]) {
    const cols = db.prepare(`PRAGMA table_info(${table})`).all();
    if (!cols.some((c) => c.name === "tenant_id")) {
      db.exec(`ALTER TABLE ${table} ADD COLUMN tenant_id TEXT`);
    }
  }
  db.exec(`CREATE INDEX IF NOT EXISTS idx_agents_tenant       ON agents(tenant_id)`);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_environments_tenant ON environments(tenant_id)`);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_vaults_tenant       ON vaults(tenant_id)`);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_tenant     ON sessions(tenant_id)`);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_tenant     ON api_keys(tenant_id)`);
  const agentVerColsHook = db.prepare(`PRAGMA table_info(agent_versions)`).all();
  if (!agentVerColsHook.some((c) => c.name === "webhook_secret")) {
    db.exec(`ALTER TABLE agent_versions ADD COLUMN webhook_secret TEXT`);
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS audit_log (
      id            TEXT PRIMARY KEY,
      created_at    INTEGER NOT NULL,
      actor_key_id  TEXT,
      actor_name    TEXT,
      tenant_id     TEXT,
      action        TEXT NOT NULL,
      resource_type TEXT,
      resource_id   TEXT,
      outcome       TEXT NOT NULL,
      metadata_json TEXT
    )
  `);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_tenant_time ON audit_log(tenant_id, created_at)`);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_actor       ON audit_log(actor_key_id, created_at)`);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_audit_action_time ON audit_log(action, created_at)`);
  const proxyCols = db.prepare(`PRAGMA table_info(proxy_resources)`).all();
  if (!proxyCols.some((c) => c.name === "tenant_id")) {
    db.exec(`ALTER TABLE proxy_resources ADD COLUMN tenant_id TEXT`);
  }
  db.exec(`CREATE INDEX IF NOT EXISTS idx_proxy_tenant ON proxy_resources(tenant_id)`);
  const memStoreCols = db.prepare(`PRAGMA table_info(memory_stores)`).all();
  if (!memStoreCols.some((c) => c.name === "agent_id")) {
    db.exec(`ALTER TABLE memory_stores ADD COLUMN agent_id TEXT`);
  }
  if (!memStoreCols.some((c) => c.name === "metadata_json")) {
    db.exec("ALTER TABLE memory_stores ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}'");
  }
  db.exec(`CREATE INDEX IF NOT EXISTS idx_memory_stores_agent ON memory_stores(agent_id)`);
  const memoriesCols = db.prepare("PRAGMA table_info(memories)").all();
  if (!memoriesCols.some((c) => c.name === "metadata_json")) {
    db.exec("ALTER TABLE memories ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}'");
  }
  const filesColsSync = db.prepare("PRAGMA table_info(files)").all();
  if (!filesColsSync.some((c) => c.name === "container_path")) {
    db.exec("ALTER TABLE files ADD COLUMN container_path TEXT");
  }
  if (!filesColsSync.some((c) => c.name === "content_hash")) {
    db.exec("ALTER TABLE files ADD COLUMN content_hash TEXT");
  }
  db.exec(
    `CREATE INDEX IF NOT EXISTS idx_files_dedup ON files(scope_id, container_path, content_hash)`
  );
  db.exec(`
    CREATE TABLE IF NOT EXISTS session_resources (
      id TEXT PRIMARY KEY,
      session_id TEXT NOT NULL,
      type TEXT NOT NULL,
      file_id TEXT,
      mount_path TEXT,
      url TEXT,
      checkout_json TEXT,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
    )
  `);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_session_resources_session ON session_resources(session_id)`);
  const syncCheckRow = db.prepare(
    "SELECT sql FROM sqlite_master WHERE type='table' AND name='anthropic_sync'"
  ).get();
  if (syncCheckRow && !syncCheckRow.sql.includes("'file'")) {
    db.exec(`
      CREATE TABLE IF NOT EXISTS anthropic_sync_new (
        local_id TEXT NOT NULL,
        resource_type TEXT NOT NULL CHECK(resource_type IN ('agent','environment','vault','session','file')),
        remote_id TEXT NOT NULL,
        synced_at INTEGER NOT NULL,
        config_hash TEXT,
        PRIMARY KEY (local_id, resource_type)
      );
      INSERT OR IGNORE INTO anthropic_sync_new SELECT * FROM anthropic_sync;
      DROP TABLE anthropic_sync;
      ALTER TABLE anthropic_sync_new RENAME TO anthropic_sync;
    `);
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS vault_credentials (
      id TEXT PRIMARY KEY,
      vault_id TEXT NOT NULL,
      display_name TEXT NOT NULL,
      auth_type TEXT NOT NULL DEFAULT 'static_bearer',
      auth_token_encrypted TEXT NOT NULL,
      mcp_server_url TEXT,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      FOREIGN KEY (vault_id) REFERENCES vaults(id) ON DELETE CASCADE,
      UNIQUE(vault_id, display_name)
    )
  `);
  db.exec(`
    CREATE TABLE IF NOT EXISTS memory_versions (
      id TEXT PRIMARY KEY,
      store_id TEXT NOT NULL,
      memory_id TEXT NOT NULL,
      operation TEXT NOT NULL,
      path TEXT NOT NULL,
      content TEXT,
      content_sha256 TEXT,
      session_id TEXT,
      created_at INTEGER NOT NULL
    )
  `);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_memver_store ON memory_versions(store_id, created_at)`);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_memver_memory ON memory_versions(memory_id, created_at)`);
  const memStoreColsArchive = db.prepare(`PRAGMA table_info(memory_stores)`).all();
  if (!memStoreColsArchive.some((c) => c.name === "archived_at")) {
    db.exec(`ALTER TABLE memory_stores ADD COLUMN archived_at INTEGER`);
  }
  const credCols = db.prepare(`PRAGMA table_info(vault_credentials)`).all();
  if (!credCols.some((c) => c.name === "expires_at")) {
    db.exec(`ALTER TABLE vault_credentials ADD COLUMN expires_at TEXT`);
  }
  if (!credCols.some((c) => c.name === "refresh_config_encrypted")) {
    db.exec(`ALTER TABLE vault_credentials ADD COLUMN refresh_config_encrypted TEXT`);
  }
  const sessCols3 = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (sessCols3.some((c) => c.name === "sprite_name")) {
    db.exec(`ALTER TABLE sessions RENAME COLUMN sprite_name TO sandbox_name`);
  }
  const envCols3 = db.prepare(`PRAGMA table_info(environments)`).all();
  if (envCols3.some((c) => c.name === "template_sprite")) {
    db.exec(`ALTER TABLE environments RENAME COLUMN template_sprite TO template_sandbox`);
  }
  {
    const cols = db.prepare("PRAGMA table_info(agents)").all();
    const names = new Set(cols.map((c) => c.name));
    if (!names.has("description")) {
      db.exec("ALTER TABLE agents ADD COLUMN description TEXT");
    }
    if (!names.has("metadata_json")) {
      db.exec("ALTER TABLE agents ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}'");
    }
  }
  const sessColsQuota = db.prepare(`PRAGMA table_info(sessions)`).all();
  if (!sessColsQuota.some((c) => c.name === "max_tokens")) {
    db.exec(`ALTER TABLE sessions ADD COLUMN max_tokens INTEGER DEFAULT NULL`);
  }
  if (!sessColsQuota.some((c) => c.name === "max_wall_duration_ms")) {
    db.exec(`ALTER TABLE sessions ADD COLUMN max_wall_duration_ms INTEGER DEFAULT NULL`);
  }
  {
    const cols = db.prepare("PRAGMA table_info(environments)").all();
    if (!cols.some((c) => c.name === "updated_at")) {
      db.exec("ALTER TABLE environments ADD COLUMN updated_at INTEGER");
      db.exec("UPDATE environments SET updated_at = created_at WHERE updated_at IS NULL");
    }
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS session_threads (
      id TEXT PRIMARY KEY,
      session_id TEXT NOT NULL,
      agent_id TEXT NOT NULL,
      agent_version INTEGER NOT NULL,
      parent_thread_id TEXT,
      status TEXT NOT NULL DEFAULT 'idle',
      stop_reason TEXT,
      usage_input_tokens INTEGER NOT NULL DEFAULT 0,
      usage_output_tokens INTEGER NOT NULL DEFAULT 0,
      usage_cache_read_input_tokens INTEGER NOT NULL DEFAULT 0,
      usage_cache_creation_input_tokens INTEGER NOT NULL DEFAULT 0,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      archived_at INTEGER
    )
  `);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_session_threads_session ON session_threads(session_id)`);
  const eventColsThread = db.prepare(`PRAGMA table_info(events)`).all();
  if (!eventColsThread.some((c) => c.name === "thread_id")) {
    db.exec(`ALTER TABLE events ADD COLUMN thread_id TEXT`);
  }
  db.exec(`CREATE INDEX IF NOT EXISTS idx_events_thread ON events(thread_id) WHERE thread_id IS NOT NULL`);
  const avColsMultiagent = db.prepare(`PRAGMA table_info(agent_versions)`).all();
  if (!avColsMultiagent.some((c) => c.name === "multiagent_json")) {
    db.exec(`ALTER TABLE agent_versions ADD COLUMN multiagent_json TEXT`);
  }
  {
    const cols = db.prepare("PRAGMA table_info(vault_credentials)").all();
    const names = new Set(cols.map((c) => c.name));
    if (!names.has("archived_at")) {
      db.exec("ALTER TABLE vault_credentials ADD COLUMN archived_at INTEGER");
    }
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS skills (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      description TEXT,
      current_version TEXT,
      tenant_id TEXT,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      archived_at INTEGER
    )
  `);
  db.exec(`
    CREATE TABLE IF NOT EXISTS skill_versions (
      id TEXT PRIMARY KEY,
      skill_id TEXT NOT NULL,
      version TEXT NOT NULL,
      content TEXT NOT NULL,
      created_at INTEGER NOT NULL,
      UNIQUE(skill_id, version),
      FOREIGN KEY (skill_id) REFERENCES skills(id)
    )
  `);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_skill_versions_skill ON skill_versions(skill_id, created_at)`);
  {
    const cols = db.prepare("PRAGMA table_info(skills)").all();
    const names = new Set(cols.map((c) => c.name));
    if (!names.has("current_version")) db.exec("ALTER TABLE skills ADD COLUMN current_version TEXT");
    if (!names.has("tenant_id")) db.exec("ALTER TABLE skills ADD COLUMN tenant_id TEXT");
    if (!names.has("archived_at")) db.exec("ALTER TABLE skills ADD COLUMN archived_at INTEGER");
    if (!names.has("description")) db.exec("ALTER TABLE skills ADD COLUMN description TEXT");
    if (!names.has("updated_at")) db.exec("ALTER TABLE skills ADD COLUMN updated_at INTEGER NOT NULL DEFAULT 0");
  }
  const skillVersionCols = db.prepare(`PRAGMA table_info(skill_versions)`).all();
  if (!skillVersionCols.some((c) => c.name === "files_json")) {
    db.exec(
      `ALTER TABLE skill_versions ADD COLUMN files_json TEXT NOT NULL DEFAULT '{}'`
    );
  }
  {
    const cols = db.prepare("PRAGMA table_info(memory_versions)").all();
    const names = new Set(cols.map((c) => c.name));
    if (!names.has("redacted_at")) {
      db.exec("ALTER TABLE memory_versions ADD COLUMN redacted_at INTEGER");
    }
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS work_items (
      id TEXT PRIMARY KEY,
      environment_id TEXT NOT NULL,
      session_id TEXT NOT NULL,
      state TEXT NOT NULL DEFAULT 'queued',
      worker_id TEXT,
      inputs_json TEXT,
      metadata_json TEXT NOT NULL DEFAULT '{}',
      tenant_id TEXT,
      created_at INTEGER NOT NULL,
      acknowledged_at INTEGER,
      started_at INTEGER,
      latest_heartbeat_at INTEGER,
      stop_requested_at INTEGER,
      stopped_at INTEGER,
      lease_expires_at INTEGER
    )
  `);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_work_env_state ON work_items(environment_id, state)`);
  db.exec(`CREATE INDEX IF NOT EXISTS idx_work_session ON work_items(session_id)`);
  {
    const cols = db.prepare("PRAGMA table_info(vaults)").all();
    const names = new Set(cols.map((c) => c.name));
    if (!names.has("metadata_json")) {
      db.exec("ALTER TABLE vaults ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}'");
    }
    if (!names.has("archived_at")) {
      db.exec("ALTER TABLE vaults ADD COLUMN archived_at INTEGER");
    }
    const agentIdCol = cols.find((c) => c.name === "agent_id");
    if (agentIdCol && agentIdCol.notnull === 1) {
      db.exec(`
        CREATE TABLE vaults_new (
          id TEXT PRIMARY KEY,
          agent_id TEXT,
          name TEXT NOT NULL,
          metadata_json TEXT NOT NULL DEFAULT '{}',
          tenant_id TEXT,
          created_at INTEGER NOT NULL,
          updated_at INTEGER NOT NULL,
          archived_at INTEGER
        );
        INSERT INTO vaults_new SELECT id, agent_id, name,
          COALESCE(metadata_json, '{}'), tenant_id, created_at, updated_at,
          archived_at FROM vaults;
        DROP TABLE vaults;
        ALTER TABLE vaults_new RENAME TO vaults;
      `);
      db.exec(`CREATE INDEX IF NOT EXISTS idx_vaults_tenant ON vaults(tenant_id)`);
    }
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS user_profiles (
      id TEXT PRIMARY KEY,
      external_id TEXT,
      display_name TEXT,
      trust_grants_json TEXT NOT NULL DEFAULT '[]',
      tenant_id TEXT,
      created_at INTEGER NOT NULL,
      updated_at INTEGER NOT NULL,
      UNIQUE(tenant_id, external_id)
    )
  `);
  try {
    db.exec(`ALTER TABLE sessions ADD COLUMN user_profile_id TEXT`);
  } catch {
  }
  try {
    db.exec(`ALTER TABLE agent_versions ADD COLUMN permission_policy_json TEXT`);
  } catch {
  }
  try {
    db.exec(`ALTER TABLE sessions ADD COLUMN debug_prompt_json TEXT`);
  } catch {
  }
}
var init_migrations = __esm({
  "src/db/migrations.ts"() {
  }
});

export {
  runMigrations,
  init_migrations
};
