export const SCHEMA_SQL = `
CREATE TABLE IF NOT EXISTS agents (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  type TEXT,
  engine TEXT,
  model TEXT,
  mission TEXT,
  schedule TEXT,
  tools TEXT,
  status TEXT,
  approval TEXT,
  generated INTEGER DEFAULT 0,
  files TEXT,
  system_prompt TEXT,
  memory TEXT,
  created_at TEXT,
  last_heartbeat TEXT,
  last_run_at TEXT,
  next_run_at TEXT,
  reports_to TEXT,
  role TEXT,
  title TEXT,
  status_reason TEXT,
  autonomy_mode TEXT,
  budget_monthly_cents INTEGER DEFAULT 0,
  spent_monthly_cents INTEGER DEFAULT 0,
  permissions_json TEXT,
  metadata_json TEXT
);

CREATE TABLE IF NOT EXISTS schedules (
  id TEXT PRIMARY KEY,
  agent_id TEXT NOT NULL,
  agent_name TEXT,
  schedule TEXT NOT NULL,
  action_kind TEXT,
  enabled INTEGER NOT NULL DEFAULT 1,
  next_run_at TEXT,
  last_run_at TEXT,
  delivery TEXT,
  approval TEXT,
  updated_at TEXT,
  catchup_policy TEXT DEFAULT 'fire_all'
);

CREATE TABLE IF NOT EXISTS runs (
  id TEXT PRIMARY KEY,
  kind TEXT NOT NULL,
  title TEXT,
  agent TEXT,
  agent_id TEXT,
  status TEXT,
  artifact TEXT,
  preview TEXT,
  output TEXT,
  command TEXT,
  triggered_by TEXT,
  at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS connectors (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  type TEXT,
  capabilities TEXT,
  status TEXT,
  metadata TEXT,
  updated_at TEXT
);

CREATE TABLE IF NOT EXISTS tool_installations (
  name TEXT PRIMARY KEY,
  title TEXT,
  description TEXT,
  category TEXT,
  enabled INTEGER NOT NULL DEFAULT 0,
  risk_level TEXT,
  required_env TEXT,
  created_at TEXT,
  updated_at TEXT
);

CREATE TABLE IF NOT EXISTS audit_logs (
  id TEXT PRIMARY KEY,
  at TEXT NOT NULL,
  type TEXT,
  summary TEXT,
  status TEXT,
  meta TEXT
);

CREATE TABLE IF NOT EXISTS approval_requests (
  id TEXT PRIMARY KEY,
  status TEXT NOT NULL DEFAULT 'pending',
  action_type TEXT NOT NULL,
  risk TEXT NOT NULL,
  summary TEXT NOT NULL,
  payload TEXT NOT NULL,
  requested_by TEXT,
  approved_by TEXT,
  created_at TEXT NOT NULL,
  decided_at TEXT,
  expires_at TEXT
);

CREATE TABLE IF NOT EXISTS local_processes (
  id TEXT PRIMARY KEY,
  pid INTEGER,
  command TEXT NOT NULL,
  args TEXT,
  cwd TEXT,
  status TEXT NOT NULL,
  risk TEXT,
  approval_request_id TEXT,
  stdout_preview TEXT,
  stderr_preview TEXT,
  exit_code INTEGER,
  started_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  completed_at TEXT
);

CREATE TABLE IF NOT EXISTS heartbeat_tasks (
  id TEXT PRIMARY KEY,
  agent_id TEXT,
  schedule_id TEXT,
  type TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'queued',
  priority INTEGER DEFAULT 0,
  payload TEXT NOT NULL,
  attempts INTEGER DEFAULT 0,
  max_attempts INTEGER DEFAULT 3,
  run_after TEXT NOT NULL,
  lease_owner TEXT,
  lease_expires_at TEXT,
  approval_request_id TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  completed_at TEXT,
  error TEXT
);

CREATE TABLE IF NOT EXISTS heartbeat_task_events (
  id TEXT PRIMARY KEY,
  task_id TEXT NOT NULL,
  type TEXT NOT NULL,
  message TEXT,
  payload TEXT,
  at TEXT NOT NULL,
  FOREIGN KEY (task_id) REFERENCES heartbeat_tasks(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS tasks (
  id TEXT PRIMARY KEY,
  workspace_id TEXT,
  title TEXT NOT NULL,
  description TEXT,
  status TEXT NOT NULL DEFAULT 'todo',
  priority TEXT NOT NULL DEFAULT 'medium',
  assignee_agent_id TEXT,
  created_by_agent_id TEXT,
  created_by_user_id TEXT,
  parent_task_id TEXT,
  project_id TEXT,
  goal_id TEXT,
  checkout_run_id TEXT,
  execution_run_id TEXT,
  blocked_reason TEXT,
  due_at TEXT,
  tags_json TEXT,
  estimate_minutes INTEGER,
  watchers_json TEXT,
  recurrence TEXT,
  acceptance_criteria TEXT,
  recurred_from_task_id TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  started_at TEXT,
  completed_at TEXT,
  cancelled_at TEXT,
  FOREIGN KEY (assignee_agent_id) REFERENCES agents(id) ON DELETE SET NULL,
  FOREIGN KEY (parent_task_id) REFERENCES tasks(id) ON DELETE SET NULL
);

CREATE TABLE IF NOT EXISTS task_checklist_items (
  id TEXT PRIMARY KEY,
  task_id TEXT NOT NULL,
  label TEXT NOT NULL,
  done INTEGER NOT NULL DEFAULT 0,
  position INTEGER NOT NULL DEFAULT 0,
  created_at TEXT NOT NULL,
  done_at TEXT,
  FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS task_comments (
  id TEXT PRIMARY KEY,
  task_id TEXT NOT NULL,
  author_agent_id TEXT,
  author_user_id TEXT,
  created_by_run_id TEXT,
  body TEXT NOT NULL,
  created_at TEXT NOT NULL,
  FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS task_events (
  id TEXT PRIMARY KEY,
  task_id TEXT NOT NULL,
  event_type TEXT NOT NULL,
  actor_type TEXT,
  actor_id TEXT,
  agent_id TEXT,
  run_id TEXT,
  payload_json TEXT,
  created_at TEXT NOT NULL,
  FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS agent_runs (
  id TEXT PRIMARY KEY,
  agent_id TEXT NOT NULL,
  task_id TEXT,
  trigger_source TEXT,
  trigger_detail TEXT,
  status TEXT NOT NULL DEFAULT 'queued',
  started_at TEXT,
  finished_at TEXT,
  error TEXT,
  session_id_before TEXT,
  session_id_after TEXT,
  usage_json TEXT,
  result_json TEXT,
  stdout_excerpt TEXT,
  stderr_excerpt TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE CASCADE,
  FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE SET NULL
);

CREATE TABLE IF NOT EXISTS agent_run_events (
  id TEXT PRIMARY KEY,
  run_id TEXT NOT NULL,
  agent_id TEXT,
  task_id TEXT,
  seq INTEGER NOT NULL,
  event_type TEXT NOT NULL,
  stream TEXT,
  level TEXT,
  message TEXT,
  payload_json TEXT,
  created_at TEXT NOT NULL,
  FOREIGN KEY (run_id) REFERENCES agent_runs(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS agent_runtime_state (
  agent_id TEXT PRIMARY KEY,
  session_id TEXT,
  state_json TEXT,
  last_run_id TEXT,
  last_run_status TEXT,
  total_input_tokens INTEGER DEFAULT 0,
  total_output_tokens INTEGER DEFAULT 0,
  total_cached_input_tokens INTEGER DEFAULT 0,
  total_cost_cents INTEGER DEFAULT 0,
  last_error TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS agent_task_sessions (
  id TEXT PRIMARY KEY,
  agent_id TEXT NOT NULL,
  task_key TEXT NOT NULL,
  adapter_type TEXT,
  session_params_json TEXT,
  session_display_id TEXT,
  last_run_id TEXT,
  last_error TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE CASCADE,
  UNIQUE(agent_id, task_key)
);

CREATE TABLE IF NOT EXISTS activity_log (
  seq INTEGER PRIMARY KEY AUTOINCREMENT,
  id TEXT NOT NULL UNIQUE,
  actor_type TEXT,
  actor_id TEXT,
  agent_id TEXT,
  run_id TEXT,
  entity_type TEXT,
  entity_id TEXT,
  action TEXT NOT NULL,
  details_json TEXT,
  created_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS agent_tool_permissions (
  id TEXT PRIMARY KEY,
  agent_id TEXT NOT NULL,
  tool_name TEXT NOT NULL,
  mode TEXT NOT NULL DEFAULT 'approval_required',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  UNIQUE(agent_id, tool_name)
);

CREATE TABLE IF NOT EXISTS artifacts (
  id TEXT PRIMARY KEY,
  run_id TEXT,
  agent_id TEXT,
  task_id TEXT,
  path TEXT NOT NULL,
  kind TEXT,
  title TEXT,
  content TEXT,
  source_md TEXT,
  created_at TEXT
);

CREATE TABLE IF NOT EXISTS hermes_jobs (
  id TEXT PRIMARY KEY,
  artifact_id TEXT,
  run_id TEXT,
  status TEXT NOT NULL DEFAULT 'queued',
  title TEXT,
  prompt TEXT NOT NULL,
  cwd TEXT,
  hermes_session_id TEXT,
  model TEXT,
  provider TEXT,
  pid INTEGER,
  timeout_ms INTEGER,
  exit_code INTEGER,
  error TEXT,
  created_at TEXT NOT NULL,
  started_at TEXT,
  completed_at TEXT,
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hermes_job_events (
  id TEXT PRIMARY KEY,
  job_id TEXT NOT NULL,
  type TEXT NOT NULL,
  message TEXT,
  payload TEXT,
  at TEXT NOT NULL,
  FOREIGN KEY (job_id) REFERENCES hermes_jobs(id) ON DELETE CASCADE
);


CREATE TABLE IF NOT EXISTS projects (
  id TEXT PRIMARY KEY,
  slug TEXT NOT NULL UNIQUE,
  name TEXT NOT NULL,
  color TEXT,
  description TEXT,
  status TEXT NOT NULL DEFAULT 'active',
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status);

CREATE TABLE IF NOT EXISTS disk_folders (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  parent_id TEXT,
  name TEXT NOT NULL,
  path TEXT NOT NULL,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  UNIQUE(project_id, parent_id, name)
);
CREATE INDEX IF NOT EXISTS idx_disk_folders_project ON disk_folders(project_id, path);

CREATE TABLE IF NOT EXISTS disk_files (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  folder_id TEXT,
  name TEXT NOT NULL,
  kind TEXT,
  mime_type TEXT,
  size_bytes INTEGER DEFAULT 0,
  sha256 TEXT NOT NULL,
  storage_path TEXT NOT NULL,
  local_only INTEGER NOT NULL DEFAULT 0,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_disk_files_project_folder ON disk_files(project_id, folder_id, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_disk_files_sha ON disk_files(sha256);

CREATE TABLE IF NOT EXISTS agent_memory (
  id TEXT PRIMARY KEY,
  agent_id TEXT,
  project_id TEXT,
  scope TEXT NOT NULL,
  kind TEXT,
  content TEXT NOT NULL,
  source TEXT,
  local_only INTEGER NOT NULL DEFAULT 0,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_agent_memory_agent ON agent_memory(agent_id, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_agent_memory_project ON agent_memory(project_id, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_agent_memory_scope ON agent_memory(scope, updated_at DESC);

CREATE TABLE IF NOT EXISTS agent_template_installations (
  id TEXT PRIMARY KEY,
  template_slug TEXT NOT NULL,
  agent_id TEXT NOT NULL,
  project_id TEXT NOT NULL,
  installed_at TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'active',
  UNIQUE(template_slug, project_id)
);
CREATE INDEX IF NOT EXISTS idx_agent_template_installations_project ON agent_template_installations(project_id);
CREATE INDEX IF NOT EXISTS idx_agent_template_installations_agent ON agent_template_installations(agent_id);

CREATE TABLE IF NOT EXISTS transcripts (
  id TEXT PRIMARY KEY,
  source_file_id TEXT NOT NULL,
  project_id TEXT,
  status TEXT NOT NULL,
  provider TEXT,
  engine TEXT,
  language TEXT,
  duration_seconds REAL,
  text TEXT,
  artifact_file_id TEXT,
  error TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  completed_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_transcripts_project ON transcripts(project_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_transcripts_source ON transcripts(source_file_id);

CREATE TABLE IF NOT EXISTS missions (
  id TEXT PRIMARY KEY,
  slug TEXT NOT NULL,
  project_id TEXT NOT NULL,
  agent_id TEXT,
  status TEXT NOT NULL,
  opts_json TEXT,
  task_id TEXT,
  error TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  completed_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_missions_project ON missions(project_id, created_at DESC);

CREATE TABLE IF NOT EXISTS config (
  key TEXT PRIMARY KEY,
  value TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS secrets (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,                  -- e.g. 'GITHUB_TOKEN'
  scope TEXT NOT NULL DEFAULT 'global', -- 'global' or 'agent:<agent_id>'
  value_encrypted BLOB NOT NULL,       -- AES-256-GCM ciphertext
  iv BLOB NOT NULL,                    -- 12-byte random IV
  auth_tag BLOB NOT NULL,              -- 16-byte GCM authentication tag
  status TEXT NOT NULL DEFAULT 'active', -- 'active' | 'rotated' | 'revoked'
  source TEXT,                         -- 'manual' | 'env_migration' | 'rotation' | 'oauth_refresh'
  metadata_json TEXT,                  -- optional JSON (e.g. OAuth scopes, token expiry)
  project_id TEXT,
  last_used_at TEXT,
  created_at TEXT NOT NULL,
  rotated_at TEXT
  -- NOTE: UNIQUE(name, scope, status) was removed to allow multiple 'rotated'
  -- rows per (name, scope) after repeated rotations. A partial unique index
  -- below enforces exactly one 'active' row per (name, scope) instead.
);
-- Partial unique index: only one active row is permitted per (name, scope).
-- 'rotated' and 'revoked' rows are unlimited, enabling repeated rotations
-- without SQLITE_CONSTRAINT violations.
CREATE UNIQUE INDEX IF NOT EXISTS idx_secrets_one_active
  ON secrets(name, scope, COALESCE(project_id, '')) WHERE status = 'active';
CREATE INDEX IF NOT EXISTS idx_secrets_name_status ON secrets(name, status);

CREATE TABLE IF NOT EXISTS connection_instances (
  id TEXT PRIMARY KEY,
  service_key TEXT NOT NULL,           -- 'github', 'supabase', etc. (matches marketplace_items.slug)
  scope TEXT NOT NULL DEFAULT 'global', -- 'global' | 'project' | 'agent'
  project_id TEXT,                     -- non-null when scope='project'
  agent_id TEXT,                       -- non-null when scope='agent'
  account_label TEXT,                  -- e.g. '@kab', 'team_xyz', 'acme-prod'
  last_4 TEXT,                         -- last 4 chars of primary credential fingerprint
  summary_json TEXT,                   -- non-secret metadata (project_url, login, etc.)
  verified_at TEXT NOT NULL,
  created_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_connection_instances_unique
  ON connection_instances(service_key, scope, COALESCE(project_id, ''), COALESCE(agent_id, ''));
CREATE INDEX IF NOT EXISTS idx_connection_instances_service ON connection_instances(service_key);
CREATE INDEX IF NOT EXISTS idx_connection_instances_project ON connection_instances(project_id) WHERE project_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_connection_instances_agent ON connection_instances(agent_id) WHERE agent_id IS NOT NULL;

CREATE TABLE IF NOT EXISTS chat_sessions (
  id TEXT PRIMARY KEY,
  title TEXT,
  model TEXT,
  created_at TEXT NOT NULL,
  last_message_at TEXT,
  message_count INTEGER NOT NULL DEFAULT 0,
  total_input_tokens INTEGER NOT NULL DEFAULT 0,
  total_output_tokens INTEGER NOT NULL DEFAULT 0,
  total_cache_read_tokens INTEGER NOT NULL DEFAULT 0,
  total_cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
  last_context_tokens INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS whatsapp_messages (
  id TEXT PRIMARY KEY,
  direction TEXT NOT NULL,
  chat_id TEXT NOT NULL,
  sender TEXT,
  message_id TEXT,
  text TEXT,
  media_path TEXT,
  run_id TEXT,
  created_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS whatsapp_allowlist (
  id TEXT PRIMARY KEY,
  chat_id TEXT NOT NULL,
  kind TEXT NOT NULL DEFAULT 'dm',
  label TEXT,
  created_at TEXT NOT NULL,
  UNIQUE(chat_id)
);

-- Per-chat conversation session boundary. session_started_at is the cutoff for
-- history reconstruction: buildWhatsAppHistory only replays whatsapp_messages rows
-- with created_at >= session_started_at. The /new command advances it to now,
-- which clears prior context without deleting the audit log.
CREATE TABLE IF NOT EXISTS whatsapp_sessions (
  chat_id TEXT PRIMARY KEY,
  session_started_at TEXT NOT NULL,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS tool_allowlists (
  id TEXT PRIMARY KEY,
  tool_name TEXT NOT NULL,
  scope_key TEXT NOT NULL,         -- e.g. 'recipient_domain', 'repo_full_name', 'zone_id'
  scope_value TEXT NOT NULL,       -- e.g. 'madarx.io', 'org/repo', 'zone-abc123'
  org_id TEXT,
  created_by_user_id TEXT,
  label TEXT,
  created_at TEXT NOT NULL,
  UNIQUE(tool_name, scope_key, scope_value)
);
CREATE INDEX IF NOT EXISTS idx_tool_allowlists_tool ON tool_allowlists(tool_name);

CREATE TABLE IF NOT EXISTS tool_rate_limit_events (
  id TEXT PRIMARY KEY,
  tool_name TEXT NOT NULL,
  agent_id TEXT,                  -- null for main agent
  at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_rate_limit_lookup ON tool_rate_limit_events(tool_name, agent_id, at DESC);

CREATE TABLE IF NOT EXISTS tool_egress (
  id TEXT PRIMARY KEY,
  tool_name TEXT NOT NULL,
  agent_id TEXT,
  host TEXT NOT NULL,
  method TEXT NOT NULL,
  status_code INTEGER,
  bytes_out INTEGER DEFAULT 0,
  bytes_in INTEGER DEFAULT 0,
  duration_ms INTEGER,
  error TEXT,
  at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_egress_at ON tool_egress(at DESC);
CREATE INDEX IF NOT EXISTS idx_egress_tool ON tool_egress(tool_name, at DESC);

CREATE TABLE IF NOT EXISTS chat_messages (
  id TEXT PRIMARY KEY,
  session_id TEXT NOT NULL,
  role TEXT NOT NULL,
  content TEXT,
  tool_calls TEXT,
  model TEXT,
  input_tokens INTEGER NOT NULL DEFAULT 0,
  output_tokens INTEGER NOT NULL DEFAULT 0,
  cache_read_tokens INTEGER NOT NULL DEFAULT 0,
  cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
  at TEXT NOT NULL,
  FOREIGN KEY (session_id) REFERENCES chat_sessions(id) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS idx_runs_at ON runs(at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_at ON audit_logs(at DESC);
CREATE INDEX IF NOT EXISTS idx_schedules_due ON schedules(enabled, next_run_at);
CREATE INDEX IF NOT EXISTS idx_approval_requests_status_created ON approval_requests(status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_local_processes_status_updated ON local_processes(status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_heartbeat_tasks_claim ON heartbeat_tasks(status, run_after, priority DESC);
CREATE INDEX IF NOT EXISTS idx_heartbeat_tasks_lease ON heartbeat_tasks(status, lease_expires_at);
CREATE INDEX IF NOT EXISTS idx_heartbeat_task_events_task_at ON heartbeat_task_events(task_id, at ASC);
CREATE INDEX IF NOT EXISTS idx_whatsapp_messages_chat_created ON whatsapp_messages(chat_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_whatsapp_messages_message_id ON whatsapp_messages(message_id);
CREATE INDEX IF NOT EXISTS idx_chat_sessions_recent ON chat_sessions(last_message_at DESC);
CREATE INDEX IF NOT EXISTS idx_chat_messages_session ON chat_messages(session_id, at ASC);
CREATE INDEX IF NOT EXISTS idx_hermes_jobs_status_updated ON hermes_jobs(status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_hermes_job_events_job_at ON hermes_job_events(job_id, at ASC);
CREATE INDEX IF NOT EXISTS idx_tasks_status_updated ON tasks(status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_tasks_assignee_status ON tasks(assignee_agent_id, status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id, updated_at DESC);


CREATE INDEX IF NOT EXISTS idx_task_comments_task_created ON task_comments(task_id, created_at ASC);
CREATE INDEX IF NOT EXISTS idx_task_events_task_created ON task_events(task_id, created_at ASC);
CREATE INDEX IF NOT EXISTS idx_task_checklist_task ON task_checklist_items(task_id, position ASC);
CREATE INDEX IF NOT EXISTS idx_agent_runs_status_updated ON agent_runs(status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_agent_runs_task ON agent_runs(task_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_agent_run_events_run_seq ON agent_run_events(run_id, seq ASC);
CREATE INDEX IF NOT EXISTS idx_activity_log_created ON activity_log(created_at DESC, seq DESC);
CREATE INDEX IF NOT EXISTS idx_activity_log_entity ON activity_log(entity_type, entity_id, seq DESC);
CREATE INDEX IF NOT EXISTS idx_activity_log_agent ON activity_log(agent_id, seq DESC);

CREATE TABLE IF NOT EXISTS marketplace_items (
  id            TEXT PRIMARY KEY,
  kind          TEXT NOT NULL,
  slug          TEXT NOT NULL,
  title         TEXT NOT NULL,
  description   TEXT,
  category      TEXT,
  author        TEXT,
  source_kind   TEXT NOT NULL,
  source_url    TEXT,
  version       TEXT,
  manifest_json TEXT,
  risk_summary  TEXT,
  tags          TEXT,
  created_at    TEXT NOT NULL DEFAULT (datetime('now')),
  UNIQUE(kind, slug)
);
CREATE INDEX IF NOT EXISTS idx_mkt_items_kind_cat ON marketplace_items(kind, category);

CREATE TABLE IF NOT EXISTS marketplace_installations (
  item_id       TEXT PRIMARY KEY REFERENCES marketplace_items(id) ON DELETE CASCADE,
  installed     INTEGER NOT NULL DEFAULT 0,
  installed_at  TEXT,
  settings_json TEXT
);

CREATE TABLE IF NOT EXISTS voice_sessions (
  id TEXT PRIMARY KEY,
  kind TEXT NOT NULL,
  project_id TEXT,
  agent_id TEXT,
  provider TEXT,
  model TEXT,
  voice TEXT,
  started_at TEXT NOT NULL,
  ended_at TEXT,
  transcript_text TEXT,
  tokens_in INTEGER DEFAULT 0,
  tokens_out INTEGER DEFAULT 0,
  audio_seconds_in REAL DEFAULT 0,
  audio_seconds_out REAL DEFAULT 0,
  error TEXT,
  metadata_json TEXT
);

CREATE INDEX IF NOT EXISTS voice_sessions_started_at_idx ON voice_sessions(started_at DESC);

CREATE TABLE IF NOT EXISTS voice_tool_calls (
  id TEXT PRIMARY KEY,
  session_id TEXT,
  call_id TEXT,
  tool_name TEXT NOT NULL,
  status TEXT,
  args_summary TEXT,
  result_summary TEXT,
  latency_ms INTEGER,
  approval_request_id TEXT,
  created_at TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS voice_tool_calls_session_idx ON voice_tool_calls(session_id);

-- ── Privacy spec ────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS privacy_config (
  id TEXT PRIMARY KEY,
  kdf TEXT NOT NULL DEFAULT 'PBKDF2-SHA256',
  iterations INTEGER NOT NULL DEFAULT 600000,
  salt_b64 TEXT NOT NULL,
  hmac_b64 TEXT NOT NULL,
  idle_timeout_ms INTEGER NOT NULL DEFAULT 900000,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

-- ── Auth spec ────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS organizations (
  id TEXT PRIMARY KEY,
  slug TEXT NOT NULL UNIQUE,
  name TEXT NOT NULL,
  plan TEXT NOT NULL DEFAULT 'free',
  mfa_required INTEGER NOT NULL DEFAULT 0,
  ip_allowlist_json TEXT,
  session_timeout_seconds INTEGER,
  require_share_email_verification INTEGER NOT NULL DEFAULT 0,
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_orgs_slug ON organizations(slug);

CREATE TABLE IF NOT EXISTS users (
  id TEXT PRIMARY KEY,
  email TEXT NOT NULL UNIQUE,
  display_name TEXT,
  avatar_url TEXT,
  auth_provider TEXT NOT NULL DEFAULT 'local',
  mfa_enrolled INTEGER NOT NULL DEFAULT 0,
  last_sign_in_at TEXT,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);

CREATE TABLE IF NOT EXISTS org_memberships (
  id TEXT PRIMARY KEY,
  org_id TEXT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  role TEXT NOT NULL DEFAULT 'member',
  invited_by_user_id TEXT,
  invited_at TEXT,
  accepted_at TEXT,
  UNIQUE(org_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_memberships_user ON org_memberships(user_id);
CREATE INDEX IF NOT EXISTS idx_memberships_org ON org_memberships(org_id);

-- Org invites — ONE table for BOTH invite kinds (eng-review decision 1):
--   email pre-invite : email set (normalized lowercase), token_hash NULL.
--     Claimed on first login by email match (no placeholder users row, no
--     pending membership). A real membership is created only on an actual join.
--   shareable link   : token_hash set (SHA-256 of a >=128-bit CSPRNG token; the
--     raw token is NEVER stored), email NULL. Redeemed via /api/orgs/join.
-- The CHECK enforces exactly one of the two kinds per row.
CREATE TABLE IF NOT EXISTS org_invites (
  id TEXT PRIMARY KEY,
  org_id TEXT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  role TEXT NOT NULL DEFAULT 'member',
  email TEXT,
  token_hash TEXT,
  label TEXT,
  created_by_user_id TEXT NOT NULL,
  expires_at TEXT,
  max_uses INTEGER,
  used_count INTEGER NOT NULL DEFAULT 0,
  revoked_at TEXT,
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  UNIQUE(token_hash),
  CHECK ((email IS NOT NULL) + (token_hash IS NOT NULL) = 1)
);
CREATE INDEX IF NOT EXISTS idx_org_invites_org ON org_invites(org_id);
CREATE INDEX IF NOT EXISTS idx_org_invites_email ON org_invites(email);
CREATE INDEX IF NOT EXISTS idx_org_invites_token ON org_invites(token_hash);

CREATE TABLE IF NOT EXISTS teams (
  id TEXT PRIMARY KEY,
  org_id TEXT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  name TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  UNIQUE(org_id, name)
);

CREATE TABLE IF NOT EXISTS team_memberships (
  team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
  user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  role TEXT NOT NULL DEFAULT 'member',
  PRIMARY KEY (team_id, user_id)
);

CREATE TABLE IF NOT EXISTS artifact_acls (
  artifact_id TEXT PRIMARY KEY REFERENCES artifacts(id) ON DELETE CASCADE,
  scope TEXT NOT NULL DEFAULT 'private',
  team_id TEXT,
  updated_at TEXT NOT NULL DEFAULT (datetime('now')),
  updated_by TEXT
);

CREATE TABLE IF NOT EXISTS artifact_share_links (
  id TEXT PRIMARY KEY,
  artifact_id TEXT NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE,
  token TEXT,
  token_hmac TEXT NOT NULL UNIQUE,
  created_by TEXT NOT NULL,
  org_id TEXT NOT NULL,
  expires_at TEXT,
  max_views INTEGER,
  access_count INTEGER NOT NULL DEFAULT 0,
  revoked INTEGER NOT NULL DEFAULT 0,
  require_email_otp INTEGER NOT NULL DEFAULT 0,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_share_links_token ON artifact_share_links(token_hmac);
CREATE INDEX IF NOT EXISTS idx_share_links_artifact ON artifact_share_links(artifact_id);

CREATE TABLE IF NOT EXISTS artifact_share_link_otps (
  id TEXT PRIMARY KEY,
  share_link_id TEXT NOT NULL REFERENCES artifact_share_links(id) ON DELETE CASCADE,
  email TEXT NOT NULL,
  code_hash TEXT NOT NULL,
  expires_at TEXT NOT NULL,
  attempts INTEGER NOT NULL DEFAULT 0,
  verified_at TEXT,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_share_otp_link_email ON artifact_share_link_otps(share_link_id, email);

-- ── Space share links (public, local-first) ──────────────────────────────────
-- Sibling of artifact_share_links, but the grant is a whole dashboard space.
-- The public viewer live-queries artifacts by space_id at view time, so
-- artifacts created in or moved into the space after the link was made
-- are included automatically. No hard FK to dashboard_spaces: deleting a space
-- revokes its links application-side (lib/dashboard-spaces.js) and the viewer
-- 404s if the space row is gone.
CREATE TABLE IF NOT EXISTS space_share_links (
  id TEXT PRIMARY KEY,
  space_id TEXT NOT NULL,
  token TEXT,
  token_hmac TEXT NOT NULL UNIQUE,
  created_by TEXT NOT NULL,
  org_id TEXT NOT NULL,
  expires_at TEXT,
  max_views INTEGER,
  access_count INTEGER NOT NULL DEFAULT 0,
  revoked INTEGER NOT NULL DEFAULT 0,
  require_email_otp INTEGER NOT NULL DEFAULT 0,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_space_share_links_token ON space_share_links(token_hmac);
CREATE INDEX IF NOT EXISTS idx_space_share_links_space ON space_share_links(space_id);

CREATE TABLE IF NOT EXISTS space_share_link_otps (
  id TEXT PRIMARY KEY,
  share_link_id TEXT NOT NULL REFERENCES space_share_links(id) ON DELETE CASCADE,
  email TEXT NOT NULL,
  code_hash TEXT NOT NULL,
  expires_at TEXT NOT NULL,
  attempts INTEGER NOT NULL DEFAULT 0,
  verified_at TEXT,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_space_share_otp_link_email ON space_share_link_otps(share_link_id, email);

-- ── Shared-cloud publication ledger (local-first hybrid sharing) ──────────────
-- Local SQLite + filesystem remain the source of truth for every artifact and
-- space. This table is a LOCAL-ONLY ledger that records which artifacts have been
-- *explicitly* published to the Supabase shared layer, and tracks their sync
-- status. It never uploads anything by itself — lib/shared-cloud.js owns the
-- network writes and only runs on an explicit publish/unpublish action. An
-- artifact with no row here (the default) is local/private and has never left
-- the machine.
CREATE TABLE IF NOT EXISTS shared_publications (
  id TEXT PRIMARY KEY,
  artifact_id TEXT NOT NULL,
  space_id TEXT,
  org_id TEXT NOT NULL,
  cloud_artifact_id TEXT,
  cloud_space_id TEXT,
  scope TEXT NOT NULL DEFAULT 'org',
  content_hash TEXT,
  include_payload INTEGER NOT NULL DEFAULT 0,
  status TEXT NOT NULL DEFAULT 'published',
  last_error TEXT,
  published_by TEXT,
  published_at TEXT,
  last_synced_at TEXT,
  unpublished_at TEXT,
  UNIQUE(artifact_id)
);
CREATE INDEX IF NOT EXISTS idx_shared_pub_status ON shared_publications(status);
CREATE INDEX IF NOT EXISTS idx_shared_pub_org ON shared_publications(org_id);

-- ── Shared space/project publication ledger (local-only) ───────────────────────
-- Sibling of shared_publications, but for *spaces*: a shareable project folder
-- (space_type='project') or a dashboard/UI space (space_type='dashboard_space').
-- Same invariant: this table is the local source of truth for "is this space
-- cloud-shared?". It never performs network writes and only gets a row on an
-- explicit publish/unpublish action. A space with no row here (the default) is
-- local/private and has never left the machine. local_space_id points back to
-- the source-of-truth row (projects.id for projects, dashboard_spaces.id for
-- dashboard spaces). The (local_space_id, space_type) pair is unique because the
-- two id namespaces are independent and could collide.
CREATE TABLE IF NOT EXISTS shared_space_publications (
  id TEXT PRIMARY KEY,
  local_space_id TEXT NOT NULL,
  space_type TEXT NOT NULL DEFAULT 'dashboard_space',
  org_id TEXT NOT NULL,
  cloud_space_id TEXT,
  slug TEXT,
  name TEXT,
  visibility TEXT NOT NULL DEFAULT 'org',
  include_resource_metadata INTEGER NOT NULL DEFAULT 0,
  resource_count INTEGER NOT NULL DEFAULT 0,
  status TEXT NOT NULL DEFAULT 'published',
  last_error TEXT,
  published_by TEXT,
  published_at TEXT,
  last_synced_at TEXT,
  unpublished_at TEXT,
  UNIQUE(local_space_id, space_type)
);
CREATE INDEX IF NOT EXISTS idx_shared_space_pub_status ON shared_space_publications(status);
CREATE INDEX IF NOT EXISTS idx_shared_space_pub_org ON shared_space_publications(org_id);

-- ── Shared brand-profile publication ledger (local-only) ───────────────────────
-- Sibling of shared_publications, but for the org BRAND. The brand profile is a
-- singleton per install (config.brand_profile). This ledger records whether the
-- local brand has been *published* to the org (status='published'), *adopted*
-- from the org (source='org'), or stopped sharing (status='unpublished'). Like
-- its siblings it performs no network writes itself — lib/shared-cloud.js owns
-- the cloud writes on an explicit publish/adopt/unpublish action. A row is keyed
-- by org_id (one brand per org). No row (the default) means the brand is purely
-- local and has never left the machine.
CREATE TABLE IF NOT EXISTS brand_publications (
  id TEXT PRIMARY KEY,
  org_id TEXT NOT NULL,
  source TEXT NOT NULL DEFAULT 'local',
  content_hash TEXT,
  status TEXT NOT NULL DEFAULT 'published',
  last_error TEXT,
  published_by TEXT,
  published_at TEXT,
  adopted_at TEXT,
  last_synced_at TEXT,
  unpublished_at TEXT,
  UNIQUE(org_id)
);
CREATE INDEX IF NOT EXISTS idx_brand_pub_status ON brand_publications(status);
CREATE INDEX IF NOT EXISTS idx_brand_pub_org ON brand_publications(org_id);

-- ── Dashboard spaces ─────────────────────────────────────────────────────────
-- Server-side persistence for the dashboard's space sidebar. Replaces the prior
-- localStorage-only store (use-spaces.js) which lost user state across browser
-- sessions, incognito mode, and different devices. Globally scoped for now;
-- per-user / per-project scoping is a Phase A follow-up.
CREATE TABLE IF NOT EXISTS dashboard_spaces (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  position INTEGER NOT NULL DEFAULT 0,
  is_active INTEGER NOT NULL DEFAULT 0,
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_dashboard_spaces_position ON dashboard_spaces(position);
-- At most one row may be is_active=1 at a time. Enforced application-side in
-- lib/dashboard-spaces.js (transactional setActive).

-- Process / Workflow engine. The engine in lib/process/engine.js is the SOLE
-- writer of process_cases.current_stage_id / status. Stage handlers receive a
-- read-only snapshot and record outcomes via a callback — they cannot mutate
-- case state directly. This is the load-bearing safety property.
CREATE TABLE IF NOT EXISTS process_definitions (
  id TEXT PRIMARY KEY,
  slug TEXT NOT NULL UNIQUE,
  name TEXT NOT NULL,
  version INTEGER NOT NULL DEFAULT 1,
  description TEXT,
  category TEXT,
  intake_form_schema_json TEXT,
  source TEXT NOT NULL DEFAULT 'seed',
  status TEXT NOT NULL DEFAULT 'published',
  source_spec_json TEXT,
  schema_version INTEGER NOT NULL DEFAULT 1,
  created_by_user_id TEXT,
  created_by_agent_id TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS process_stages (
  id TEXT PRIMARY KEY,
  definition_id TEXT NOT NULL REFERENCES process_definitions(id) ON DELETE CASCADE,
  position INTEGER NOT NULL,
  slug TEXT NOT NULL,
  name TEXT NOT NULL,
  kind TEXT NOT NULL,
  config_json TEXT NOT NULL,
  on_pass_stage_slug TEXT,
  on_fail_stage_slug TEXT,
  required INTEGER NOT NULL DEFAULT 1,
  UNIQUE(definition_id, slug),
  UNIQUE(definition_id, position)
);
CREATE INDEX IF NOT EXISTS idx_process_stages_def ON process_stages(definition_id, position);

CREATE TABLE IF NOT EXISTS process_cases (
  id TEXT PRIMARY KEY,
  definition_id TEXT NOT NULL REFERENCES process_definitions(id) ON DELETE RESTRICT,
  definition_slug TEXT NOT NULL,
  project_id TEXT NOT NULL,
  title TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'intake',
  current_stage_id TEXT REFERENCES process_stages(id) ON DELETE SET NULL,
  current_stage_slug TEXT,
  intake_payload_json TEXT,
  context_json TEXT,
  created_by_user_id TEXT,
  created_by_agent_id TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  completed_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_process_cases_project_status ON process_cases(project_id, status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_process_cases_definition ON process_cases(definition_id, status);

CREATE TABLE IF NOT EXISTS process_stage_executions (
  id TEXT PRIMARY KEY,
  case_id TEXT NOT NULL REFERENCES process_cases(id) ON DELETE CASCADE,
  stage_id TEXT NOT NULL REFERENCES process_stages(id) ON DELETE RESTRICT,
  stage_slug TEXT NOT NULL,
  attempt INTEGER NOT NULL DEFAULT 1,
  status TEXT NOT NULL DEFAULT 'queued',
  outcome TEXT,
  heartbeat_task_id TEXT,
  agent_run_id TEXT,
  approval_request_id TEXT,
  artifact_ids_json TEXT,
  output_json TEXT,
  error TEXT,
  started_at TEXT,
  completed_at TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_pse_case_position ON process_stage_executions(case_id, created_at);
CREATE INDEX IF NOT EXISTS idx_pse_approval ON process_stage_executions(approval_request_id);

-- Per (definition, project) presentation + routing settings. Single row per
-- pair (UNIQUE constraint). Editing settings only affects NEW cases — the
-- engine snapshots the current row into process_cases.context_json at
-- startCase, so in-flight cases bind to the settings that existed at start.
CREATE TABLE IF NOT EXISTS process_definition_settings (
  id TEXT PRIMARY KEY,
  definition_id TEXT NOT NULL REFERENCES process_definitions(id) ON DELETE CASCADE,
  project_id TEXT NOT NULL,
  default_for_project INTEGER NOT NULL DEFAULT 0,
  brand_json TEXT,
  template_html TEXT,
  template_version INTEGER NOT NULL DEFAULT 1,
  agent_overrides_json TEXT,
  auto_approve_rules_json TEXT,
  share_link_enabled INTEGER NOT NULL DEFAULT 0,
  share_link_default_expiry_days INTEGER NOT NULL DEFAULT 14,
  created_by_user_id TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  UNIQUE(definition_id, project_id)
);
CREATE INDEX IF NOT EXISTS idx_pds_project ON process_definition_settings(project_id);

-- Single-use HMAC share-link consumption marker. Each row records that a
-- mint produced a token (stored as a SHA-256 hash, not the raw token) and
-- whether it has been consumed. submitIntake checks for an unconsumed row
-- bound to (case_id, stage_id) within expiry, then marks consumed.
CREATE TABLE IF NOT EXISTS share_link_consumptions (
  id TEXT PRIMARY KEY,
  case_id TEXT NOT NULL REFERENCES process_cases(id) ON DELETE CASCADE,
  stage_id TEXT NOT NULL,
  token_hash TEXT NOT NULL UNIQUE,
  expires_at TEXT NOT NULL,
  minted_by_user_id TEXT,
  minted_at TEXT NOT NULL,
  consumed_at TEXT,
  consumer_ip TEXT,
  revoked_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_share_link_case ON share_link_consumptions(case_id, stage_id);
CREATE INDEX IF NOT EXISTS idx_share_link_expiry ON share_link_consumptions(expires_at);

-- ── Mines module tables ──────────────────────────────────────────────────────
-- All tables prefixed mines_. Idempotent: CREATE TABLE IF NOT EXISTS.
-- Timestamps: ISO-8601 TEXT. Booleans: INTEGER 0/1. Numbers: REAL.
-- Embeddings: TEXT (JSON float array) — pgvector migration seam documented in
-- specs/active/mines-engineering-design.md section 1.

CREATE TABLE IF NOT EXISTS mines_portfolios (
  id                         TEXT PRIMARY KEY,
  name                       TEXT NOT NULL,
  source_catalog_artifact_id TEXT,
  created_at                 TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at                 TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE TABLE IF NOT EXISTS mines_portfolio_entries (
  id                    TEXT PRIMARY KEY,
  portfolio_id          TEXT NOT NULL REFERENCES mines_portfolios(id) ON DELETE CASCADE,
  project_id            TEXT,
  name                  TEXT NOT NULL,
  commodity             TEXT,
  deposit_type          TEXT,
  owner                 TEXT,
  available_information TEXT,
  has_drilling          INTEGER NOT NULL DEFAULT 0,
  deal_terms            TEXT,
  data_completeness     REAL,
  notes                 TEXT,
  created_at            TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at            TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_portfolio_entries_portfolio
  ON mines_portfolio_entries(portfolio_id);
CREATE INDEX IF NOT EXISTS idx_mines_portfolio_entries_commodity
  ON mines_portfolio_entries(commodity);
CREATE INDEX IF NOT EXISTS idx_mines_portfolio_entries_project
  ON mines_portfolio_entries(project_id);

CREATE TABLE IF NOT EXISTS mines_projects (
  id                    TEXT PRIMARY KEY,
  os_project_id         TEXT REFERENCES projects(id) ON DELETE SET NULL,
  name                  TEXT NOT NULL,
  slug                  TEXT NOT NULL UNIQUE,
  asset_stage           TEXT NOT NULL DEFAULT 'exploration',
  commodity             TEXT,
  deposit_type          TEXT,
  country               TEXT NOT NULL DEFAULT 'Mexico',
  state_province        TEXT,
  has_technical_report  INTEGER NOT NULL DEFAULT 0,
  data_completeness     REAL,
  created_at            TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at            TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_projects_os_project
  ON mines_projects(os_project_id);
CREATE INDEX IF NOT EXISTS idx_mines_projects_asset_stage
  ON mines_projects(asset_stage);

CREATE TABLE IF NOT EXISTS mines_source_documents (
  id                   TEXT PRIMARY KEY,
  project_id           TEXT NOT NULL REFERENCES mines_projects(id) ON DELETE CASCADE,
  document_category    TEXT NOT NULL DEFAULT 'uncategorized'
    CHECK (document_category IN (
      '00_historic','01_arcgis_data','02_assay_labs','03_cad_files',
      '04_official_database','05_drill_campaign','06_exploration_geos',
      '07_geophysics','08_backup_bd','09_petrography','10_presentations',
      '11_general_pictures','12_safety','13_reports','14_inegi_data',
      '15_sgm_data','16_claims_properties','17_minesight','18_exported_maps',
      '19_survey','20_geotechnical','21_general_files','22_qa_qc',
      'uncategorized'
    )),
  original_path        TEXT NOT NULL,
  file_name            TEXT NOT NULL,
  file_type            TEXT NOT NULL,
  file_size_bytes      INTEGER,
  sha256               TEXT,
  extraction_status    TEXT NOT NULL DEFAULT 'queued',
  extraction_error     TEXT,
  language             TEXT NOT NULL DEFAULT 'es',
  is_scanned           INTEGER NOT NULL DEFAULT 0,
  ocr_applied          INTEGER NOT NULL DEFAULT 0,
  page_count           INTEGER,
  avg_chars_per_page   REAL,
  ingested_by_agent_id TEXT,
  created_at           TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at           TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_source_docs_project_category
  ON mines_source_documents(project_id, document_category);
CREATE INDEX IF NOT EXISTS idx_mines_source_docs_status
  ON mines_source_documents(extraction_status);
CREATE INDEX IF NOT EXISTS idx_mines_source_docs_sha
  ON mines_source_documents(sha256);
CREATE INDEX IF NOT EXISTS idx_mines_source_docs_type
  ON mines_source_documents(file_type);
CREATE UNIQUE INDEX IF NOT EXISTS idx_mines_source_docs_project_path
  ON mines_source_documents(project_id, original_path);

CREATE TABLE IF NOT EXISTS mines_document_chunks (
  id                 TEXT PRIMARY KEY,
  source_document_id TEXT NOT NULL REFERENCES mines_source_documents(id) ON DELETE CASCADE,
  project_id         TEXT NOT NULL,
  document_category  TEXT NOT NULL,
  page               INTEGER,
  chunk_index        INTEGER NOT NULL,
  text               TEXT NOT NULL,
  language           TEXT NOT NULL DEFAULT 'es',
  embedding          TEXT,
  token_count        INTEGER,
  created_at         TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_chunks_doc
  ON mines_document_chunks(source_document_id, chunk_index);
CREATE INDEX IF NOT EXISTS idx_mines_chunks_project_category
  ON mines_document_chunks(project_id, document_category);
CREATE INDEX IF NOT EXISTS idx_mines_chunks_unembedded
  ON mines_document_chunks(project_id, created_at)
  WHERE embedding IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_mines_chunks_doc_index
  ON mines_document_chunks(source_document_id, chunk_index);

CREATE TABLE IF NOT EXISTS mines_drill_holes (
  id                    TEXT PRIMARY KEY,
  project_id            TEXT NOT NULL REFERENCES mines_projects(id) ON DELETE CASCADE,
  hole_id               TEXT NOT NULL,
  collar_easting        REAL,
  collar_northing       REAL,
  collar_elevation      REAL,
  azimuth               REAL,
  dip                   REAL,
  total_depth_m         REAL,
  crs                   TEXT,
  qa_qc_status          TEXT,
  source_document_id    TEXT REFERENCES mines_source_documents(id) ON DELETE SET NULL,
  confidence            REAL NOT NULL DEFAULT 1.0,
  extracted_by_agent_id TEXT,
  created_at            TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at            TEXT NOT NULL DEFAULT (datetime('now')),
  UNIQUE(project_id, hole_id)
);
CREATE INDEX IF NOT EXISTS idx_mines_drill_holes_project
  ON mines_drill_holes(project_id);
CREATE INDEX IF NOT EXISTS idx_mines_drill_holes_coords
  ON mines_drill_holes(collar_easting, collar_northing);

CREATE TABLE IF NOT EXISTS mines_assay_samples (
  id                    TEXT PRIMARY KEY,
  project_id            TEXT NOT NULL REFERENCES mines_projects(id) ON DELETE CASCADE,
  drillhole_id          TEXT REFERENCES mines_drill_holes(id) ON DELETE SET NULL,
  sample_id             TEXT NOT NULL,
  easting               REAL,
  northing              REAL,
  elevation             REAL,
  sample_type           TEXT,
  width_m               REAL,
  au_ppm                REAL,
  ag_ppm                REAL,
  cu_pct                REAL,
  other_grades_json     TEXT,
  qa_qc_status          TEXT,
  source_document_id    TEXT REFERENCES mines_source_documents(id) ON DELETE SET NULL,
  confidence            REAL NOT NULL DEFAULT 1.0,
  extracted_by_agent_id TEXT,
  approved_by_user      TEXT,
  approved_at           TEXT,
  created_at            TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at            TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_assay_samples_project
  ON mines_assay_samples(project_id);
CREATE INDEX IF NOT EXISTS idx_mines_assay_samples_drillhole
  ON mines_assay_samples(drillhole_id);
CREATE INDEX IF NOT EXISTS idx_mines_assay_samples_grade_au
  ON mines_assay_samples(project_id, au_ppm) WHERE au_ppm IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_mines_assay_samples_grade_cu
  ON mines_assay_samples(project_id, cu_pct) WHERE cu_pct IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_mines_assay_samples_coords
  ON mines_assay_samples(easting, northing);
CREATE UNIQUE INDEX IF NOT EXISTS idx_mines_assay_samples_source_sample
  ON mines_assay_samples(project_id, source_document_id, sample_id);

CREATE TABLE IF NOT EXISTS mines_spatial_assets (
  id              TEXT PRIMARY KEY,
  project_id      TEXT NOT NULL REFERENCES mines_projects(id) ON DELETE CASCADE,
  kind            TEXT NOT NULL,
  path            TEXT NOT NULL,
  crs             TEXT,
  bbox            TEXT,
  file_size_bytes INTEGER,
  indexed_at      TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_spatial_assets_project
  ON mines_spatial_assets(project_id, kind);
CREATE UNIQUE INDEX IF NOT EXISTS idx_mines_spatial_assets_project_path
  ON mines_spatial_assets(project_id, path);

CREATE TABLE IF NOT EXISTS mines_extracted_facts (
  id                    TEXT PRIMARY KEY,
  project_id            TEXT NOT NULL REFERENCES mines_projects(id) ON DELETE CASCADE,
  fact_type             TEXT NOT NULL,
  value_text            TEXT NOT NULL,
  value_json            TEXT,
  confidence            REAL NOT NULL DEFAULT 0.8,
  source_document_id    TEXT REFERENCES mines_source_documents(id) ON DELETE SET NULL,
  source_chunk_id       TEXT REFERENCES mines_document_chunks(id) ON DELETE SET NULL,
  source_quote          TEXT,
  page_or_location      TEXT,
  extracted_by_agent_id TEXT,
  approved_by_user      TEXT,
  approved_at           TEXT,
  created_at            TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at            TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_extracted_facts_project_type
  ON mines_extracted_facts(project_id, fact_type);
CREATE INDEX IF NOT EXISTS idx_mines_extracted_facts_doc
  ON mines_extracted_facts(source_document_id);

CREATE TABLE IF NOT EXISTS mines_valuation_scenarios (
  id                  TEXT PRIMARY KEY,
  project_id          TEXT NOT NULL REFERENCES mines_projects(id) ON DELETE CASCADE,
  valuation_tier      TEXT NOT NULL
    CHECK (valuation_tier IN ('t1_in_situ', 't2_dcf')),
  scenario            TEXT NOT NULL DEFAULT 'base',
  assumptions_json    TEXT NOT NULL,
  result_json         TEXT NOT NULL,
  tier_rationale      TEXT,
  sensitivity_json    TEXT,
  confidence          REAL,
  created_by_agent_id TEXT,
  artifact_id         TEXT,
  created_at          TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_valuation_scenarios_project
  ON mines_valuation_scenarios(project_id, valuation_tier, scenario);

CREATE TABLE IF NOT EXISTS mines_market_snapshots (
  id          TEXT PRIMARY KEY,
  commodity   TEXT NOT NULL,
  price_usd   REAL NOT NULL,
  price_unit  TEXT NOT NULL,
  source      TEXT,
  fetched_at  TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_mines_market_snapshots_commodity_time
  ON mines_market_snapshots(commodity, fetched_at DESC);

-- ── HAIL: Human-to-Agent Intelligence Layer ─────────────────────────────────
-- Operating-model configuration layer. The classified Configuration Interview
-- lives here (classification at capture) and never in process_cases payloads.
-- The lifecycle is orchestrated by the process engine (hail-engagement-lifecycle
-- definition); these tables hold the operating-model artifacts + gate decisions.
-- See specs/active/hail-operating-model.md.

CREATE TABLE IF NOT EXISTS hail_engagements (
  id                 TEXT PRIMARY KEY,
  principal_name     TEXT NOT NULL,
  institution_type   TEXT,
  accountable_human  TEXT NOT NULL,
  confidentiality_posture TEXT NOT NULL DEFAULT 'local_first_strict',
  project_id         TEXT REFERENCES projects(id) ON DELETE SET NULL,
  -- intentionally NOT a FK to process_cases: the engine owns case lifecycle and
  -- a RESTRICT/CASCADE coupling here would entangle engagement deletion with
  -- engine state. The engine case is the source of truth; this is a pointer.
  process_case_id    TEXT,
  current_stage      TEXT NOT NULL DEFAULT 'diagnostic',
  status             TEXT NOT NULL DEFAULT 'active',
  org_id             TEXT,
  created_by_user_id TEXT,
  created_at         TEXT NOT NULL,
  updated_at         TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hail_interviews (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  title         TEXT,
  status        TEXT NOT NULL DEFAULT 'in_progress',
  section_cursor TEXT,
  conducted_by_user_id TEXT,
  created_at    TEXT NOT NULL,
  updated_at    TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hail_interview_responses (
  id            TEXT PRIMARY KEY,
  interview_id  TEXT NOT NULL REFERENCES hail_interviews(id) ON DELETE CASCADE,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  section       TEXT NOT NULL,
  question_key  TEXT NOT NULL,
  answer        TEXT,
  sensitivity   TEXT NOT NULL,
  confidence    TEXT,
  evidence      TEXT,
  is_practitioner_note INTEGER NOT NULL DEFAULT 0,
  created_at    TEXT NOT NULL,
  updated_at    TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hail_operating_profiles (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  version       INTEGER NOT NULL DEFAULT 1,
  payload_json  TEXT NOT NULL,
  artifact_id   TEXT,
  status        TEXT NOT NULL DEFAULT 'draft',
  created_at    TEXT NOT NULL,
  updated_at    TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hail_roles (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  slug          TEXT NOT NULL,
  name          TEXT NOT NULL,
  scope         TEXT,
  counterparties_json TEXT,
  data_classes_json   TEXT,
  tools_json    TEXT,
  permissible_actions_json TEXT,
  approval_requirements_json TEXT,
  created_at    TEXT NOT NULL,
  updated_at    TEXT NOT NULL,
  UNIQUE(engagement_id, slug)
);

CREATE TABLE IF NOT EXISTS hail_role_boundaries (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  role_a_slug   TEXT NOT NULL,
  role_b_slug   TEXT,
  boundary_kind TEXT NOT NULL,
  description   TEXT NOT NULL,
  severity      TEXT NOT NULL DEFAULT 'high',
  created_at    TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hail_frictions (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  grouping      TEXT,
  description   TEXT NOT NULL,
  impact        TEXT,
  frequency     TEXT,
  current_workaround TEXT,
  suggested_agent_function TEXT,
  evidence_needed TEXT,
  risk_level    TEXT,
  priority      INTEGER NOT NULL DEFAULT 0,
  created_at    TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hail_governance_rules (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  action_type   TEXT NOT NULL,
  risk          TEXT NOT NULL,
  authority     TEXT NOT NULL,
  role_slug     TEXT,
  data_class    TEXT,
  reviewer_role TEXT,
  approval_required INTEGER NOT NULL DEFAULT 1,
  evidence_required INTEGER NOT NULL DEFAULT 0,
  notes         TEXT,
  created_at    TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hail_agent_blueprints (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  agent_slug    TEXT NOT NULL,
  payload_json  TEXT NOT NULL,
  status        TEXT NOT NULL DEFAULT 'proposed',
  provisioned_agent_id TEXT,
  created_at    TEXT NOT NULL,
  updated_at    TEXT NOT NULL,
  UNIQUE(engagement_id, agent_slug)
);

CREATE TABLE IF NOT EXISTS hail_setup_packages (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  version       INTEGER NOT NULL DEFAULT 1,
  payload_json  TEXT NOT NULL,
  artifact_id   TEXT,
  status        TEXT NOT NULL DEFAULT 'dry_run',
  approved_by_user_id TEXT,
  approval_request_id TEXT,
  applied_at    TEXT,
  created_at    TEXT NOT NULL,
  updated_at    TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS hail_setup_package_items (
  id            TEXT PRIMARY KEY,
  package_id    TEXT NOT NULL REFERENCES hail_setup_packages(id) ON DELETE CASCADE,
  ref           TEXT NOT NULL,
  item_type     TEXT NOT NULL,
  risk          TEXT NOT NULL,
  spec_json     TEXT NOT NULL,
  apply_status  TEXT NOT NULL DEFAULT 'pending',
  applied_target_id TEXT,
  approval_request_id TEXT,
  error         TEXT,
  created_at    TEXT NOT NULL,
  updated_at    TEXT NOT NULL,
  UNIQUE(package_id, ref)
);

CREATE TABLE IF NOT EXISTS hail_review_gates (
  id            TEXT PRIMARY KEY,
  engagement_id TEXT NOT NULL REFERENCES hail_engagements(id) ON DELETE CASCADE,
  gate_type     TEXT NOT NULL,
  reviewer_role TEXT NOT NULL,
  reviewer_user_id TEXT,
  decision      TEXT NOT NULL DEFAULT 'pending',
  comments      TEXT,
  approval_request_id TEXT,
  decided_at    TEXT,
  created_at    TEXT NOT NULL
);

-- engagement↔project is 1:1: a project owns at most one engagement. This backs
-- resolveEngagementForCase (quarterly-review case → engagement via project).
CREATE UNIQUE INDEX IF NOT EXISTS idx_hail_engagements_project ON hail_engagements(project_id) WHERE project_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_hail_interviews_engagement ON hail_interviews(engagement_id);
CREATE INDEX IF NOT EXISTS idx_hail_responses_engagement_section ON hail_interview_responses(engagement_id, section);
CREATE INDEX IF NOT EXISTS idx_hail_roles_engagement ON hail_roles(engagement_id);
CREATE INDEX IF NOT EXISTS idx_hail_frictions_engagement_priority ON hail_frictions(engagement_id, priority DESC);
CREATE INDEX IF NOT EXISTS idx_hail_governance_engagement ON hail_governance_rules(engagement_id);
CREATE INDEX IF NOT EXISTS idx_hail_setup_pkg_engagement ON hail_setup_packages(engagement_id, version DESC);
CREATE INDEX IF NOT EXISTS idx_hail_setup_items_pkg ON hail_setup_package_items(package_id);
CREATE INDEX IF NOT EXISTS idx_hail_gates_engagement_type ON hail_review_gates(engagement_id, gate_type);

-- ── Tool-Smith: agent-authored capabilities ─────────────────────────────────
-- These three tables hold DECLARATIVE, agent-authorable capabilities. None of
-- them stores executable code: composite recipes and connector manifests are
-- inert data interpreted at runtime by ONE pre-built, gated generic executor
-- (composite_tool_run / http_api_call). tool_proposals stages generated code
-- that is NEVER loaded — it is reviewed + merged + restarted by a human first.
-- Provenance (created_by_*) is recorded so authored capabilities are auditable.

-- Composite tools: a recipe chaining existing primitive registry tools + LLM
-- synthesis steps. risk_level is the inherited MAX over the steps. Surfaced as a
-- callable tool through the registry's dynamic-tool lister.
CREATE TABLE IF NOT EXISTS composite_tools (
  slug                TEXT PRIMARY KEY,
  title               TEXT NOT NULL,
  description         TEXT,
  spec_json           TEXT NOT NULL,
  risk_level          TEXT NOT NULL DEFAULT 'medium',
  input_schema_json   TEXT,
  status              TEXT NOT NULL DEFAULT 'active',
  created_by_user_id  TEXT,
  created_by_agent_id TEXT,
  created_at          TEXT NOT NULL,
  updated_at          TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_composite_tools_status ON composite_tools(status);

-- API connectors (Option D): a declarative manifest (https base_url, allowed_hosts,
-- secret-by-reference auth, per-endpoint risk) describing a REST surface. Each
-- endpoint expands into a callable <slug>.<endpoint> tool backed by http_api_call.
-- Named api_connectors to avoid colliding with the legacy connectors table.
CREATE TABLE IF NOT EXISTS api_connectors (
  slug                TEXT PRIMARY KEY,
  title               TEXT,
  base_url            TEXT NOT NULL,
  manifest_json       TEXT NOT NULL,
  status              TEXT NOT NULL DEFAULT 'active',
  created_by_user_id  TEXT,
  created_by_agent_id TEXT,
  created_at          TEXT NOT NULL,
  updated_at          TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_api_connectors_status ON api_connectors(status);

-- Tool proposals (Option B): generated adapter scaffold + spec + tests written to
-- staging. INERT — never callable until a human merges + restarts. Tracks review.
CREATE TABLE IF NOT EXISTS tool_proposals (
  id                  TEXT PRIMARY KEY,
  slug                TEXT NOT NULL,
  title               TEXT,
  summary             TEXT,
  staging_dir         TEXT NOT NULL,
  files_json          TEXT,
  status              TEXT NOT NULL DEFAULT 'proposed',
  task_id             TEXT,
  created_by_user_id  TEXT,
  created_by_agent_id TEXT,
  created_at          TEXT NOT NULL,
  updated_at          TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_tool_proposals_status ON tool_proposals(status);

-- OAuth2 authorization-code state ledger. Single-use CSRF state values for the
-- connection OAuth flow. A row is created at /oauth/start and consumed exactly
-- once at /oauth/callback (consumed_at set). Replay of a consumed/expired state
-- is rejected. code_verifier holds the PKCE verifier (never the access token).
CREATE TABLE IF NOT EXISTS oauth_states (
  state          TEXT PRIMARY KEY,
  service_key    TEXT NOT NULL,
  provider       TEXT NOT NULL,
  scope_json     TEXT,
  code_verifier  TEXT,
  redirect_uri   TEXT,
  created_at     TEXT NOT NULL,
  expires_at     TEXT NOT NULL,
  consumed_at    TEXT
);
CREATE INDEX IF NOT EXISTS idx_oauth_states_service ON oauth_states(service_key);


`;

export const REPORTING_VIEWS_SQL = `
CREATE TABLE IF NOT EXISTS printing_press_clis (
  slug TEXT PRIMARY KEY,
  source_kind TEXT NOT NULL,
  source_url TEXT,
  installed_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  path TEXT NOT NULL,
  declared_scopes TEXT,
  required_secrets TEXT,
  quota_bytes INTEGER DEFAULT 104857600,
  manifest_json TEXT,
  status TEXT DEFAULT 'active'
);

CREATE INDEX IF NOT EXISTS idx_printing_press_clis_status ON printing_press_clis (status);

CREATE VIEW IF NOT EXISTS agent_workload_view AS
SELECT
  a.id AS agent_id,
  a.name AS agent_name,
  COUNT(t.id) AS total_tasks,
  SUM(CASE WHEN t.status IN ('todo', 'in_progress', 'in_review', 'blocked') THEN 1 ELSE 0 END) AS active_tasks,
  SUM(CASE WHEN t.status = 'blocked' THEN 1 ELSE 0 END) AS blocked_tasks,
  SUM(CASE WHEN t.status = 'done' THEN 1 ELSE 0 END) AS completed_tasks
FROM agents a
LEFT JOIN tasks t ON t.assignee_agent_id = a.id
GROUP BY a.id, a.name;

CREATE VIEW IF NOT EXISTS manager_delegation_view AS
SELECT
  a.reports_to AS manager_id,
  m.name AS manager_name,
  COUNT(t.id) AS delegated_task_count
FROM tasks t
LEFT JOIN agents a ON a.id = t.assignee_agent_id
LEFT JOIN agents m ON m.id = a.reports_to
WHERE a.reports_to IS NOT NULL
GROUP BY a.reports_to, m.name;

CREATE VIEW IF NOT EXISTS task_lifecycle_view AS
SELECT
  t.id AS task_id,
  t.title,
  t.status,
  t.priority,
  t.assignee_agent_id,
  t.created_at,
  t.started_at,
  t.completed_at,
  CAST((julianday(COALESCE(t.completed_at, CURRENT_TIMESTAMP)) - julianday(t.created_at)) * 86400 AS INTEGER) AS age_seconds
FROM tasks t;

CREATE VIEW IF NOT EXISTS run_failure_view AS
SELECT
  r.id AS run_id,
  r.agent_id,
  r.task_id,
  r.status,
  r.error,
  r.created_at,
  r.finished_at
FROM agent_runs r
WHERE r.status IN ('failed', 'timed_out', 'cancelled');

CREATE VIEW IF NOT EXISTS approval_latency_view AS
SELECT
  id AS approval_request_id,
  action_type,
  status,
  created_at,
  decided_at,
  CASE
    WHEN decided_at IS NULL THEN NULL
    ELSE CAST((julianday(decided_at) - julianday(created_at)) * 86400 AS INTEGER)
  END AS latency_seconds
FROM approval_requests;
`;
