← back to AbramsOS

lib/chat-registry.js

167 lines

// Single source of truth for chat MODELS + AGENT personas.
// Consumed by BOTH the backend (routes/chat.js dispatch) and the frontend
// (nav-bar spec cards, rendered via /api/chat/registry). Nothing here holds a
// secret — provider credentials live in process.env and are read only inside
// the adapters. Any model or agent added here appears everywhere automatically.

// visibility: 'external' lanes have their prompt REDACTED before it leaves the box.
//             'local' lanes (Ollama) may see raw PII (never leaves the network).
// Gemini lanes removed from the picker 2026-09-01 — Google prepay is $0 (429).
// The gemini.js adapter + the dispatch 'gemini' case are intentionally kept, so
// re-enabling is just re-adding these two entries once the Google wallet is funded.
const MODELS = [
  {
    id: 'ollama-qwen3',
    label: 'Qwen3 14B (local)',
    provider: 'ollama',
    providerModel: 'qwen3:14b',
    visibility: 'local',
    contextWindow: '~40K tokens',
    speed: 'medium',
    cost: '$0 (on-box)',
    dataNote: 'Private — runs on your own hardware. Sees raw data; nothing leaves the box.',
  },
  {
    id: 'ollama-hermes3',
    label: 'Hermes 3 8B (local)',
    provider: 'ollama',
    providerModel: 'hermes3:8b',
    visibility: 'local',
    contextWindow: '~8K tokens',
    speed: 'fast',
    cost: '$0 (on-box)',
    dataNote: 'Private — runs on your own hardware. Sees raw data; nothing leaves the box.',
  },
  {
    id: 'claude-cli',
    label: 'Claude (CLI)',
    provider: 'claude-cli',
    providerModel: 'claude',
    visibility: 'external',
    contextWindow: 'large',
    speed: 'medium',
    cost: 'Max-plan (no metered API)',
    dataNote: 'External — Anthropic via local CLI. PII is redacted before send.',
  },
  {
    id: 'openai-gpt-5.2',
    label: 'GPT-5.2 (OpenAI)',
    provider: 'openai',
    providerModel: 'gpt-5.2',
    visibility: 'external',
    contextWindow: 'large',
    speed: 'medium',
    cost: 'metered · OpenAI account',
    dataNote: 'External — OpenAI Responses API. PII is redacted + NER-scrubbed before send.',
  },
];

// Each agent = a system prompt + a data-scope. `scope` names the grounding
// bundle in lib/chat-grounding.js. `null` scope = no record grounding (General).
const AGENTS = [
  {
    id: 'general',
    label: 'General',
    icon: '🏠',
    scope: null,
    speed: 'n/a',
    dataNote: 'No personal records pulled. Answers from the conversation only.',
    system:
      'You are the AbramsOS assistant, a calm household operations helper. ' +
      'You answer questions and explain how AbramsOS works. You never take actions ' +
      '(no sending email, no filing, no purchases) — you inform only. If asked to do ' +
      'something that changes state, explain that those actions stay gated behind the user.',
  },
  {
    id: 'claims',
    label: 'Claims / Autopilot',
    icon: '📋',
    scope: 'claims',
    speed: 'n/a',
    dataNote: 'Grounds on your claim cases (types, remedies, deadlines).',
    system:
      'You are the AbramsOS Claims agent. You help the user understand their open ' +
      'claim cases, class-action settlements, deadlines, and desired remedies, grounded ' +
      'only in the CONTEXT records provided. You draft nothing that gets sent and file ' +
      'nothing — filing and sending stay gated behind explicit user approval. Be precise ' +
      'about dates and never invent a claim that is not in the context.',
  },
  {
    id: 'savings',
    label: 'Savings',
    icon: '🐷',
    scope: 'savings',
    speed: 'n/a',
    dataNote: 'Grounds on your savings suggestions and coupons.',
    system:
      'You are the AbramsOS Savings agent. Using only the CONTEXT records, help the user ' +
      'understand cheaper/better substitutes for what they buy and active coupons. You ' +
      'never auto-buy anything. Quote the estimated savings and its basis honestly; if the ' +
      'context has no suggestion for something, say so.',
  },
  {
    id: 'bills',
    label: 'Bill Audit',
    icon: '🧾',
    scope: 'bills',
    speed: 'n/a',
    dataNote: 'Grounds on your tracked bills (payee, amount, cadence, due dates).',
    system:
      'You are the AbramsOS Bill-Audit agent. Using only the CONTEXT records, help the user ' +
      'understand their recurring bills — amounts, cadence, due dates, autopay status — and ' +
      'spot likely overcharges or duplicate services. You never pay or cancel anything.',
  },
  {
    id: 'vitals',
    label: 'Vitals / Health',
    icon: '❤️',
    scope: 'vitals',
    speed: 'n/a',
    dataNote: 'Grounds on your health readings. Health data is sensitive — prefer a local model.',
    system:
      'You are the AbramsOS Vitals agent. Using only the CONTEXT records (health readings), ' +
      'help the user read trends in their own measurements. You are NOT a doctor and give no ' +
      'diagnosis or treatment advice — surface trends and suggest they discuss anything ' +
      'concerning with a clinician. Never invent a reading not in the context.',
  },
  {
    id: 'reminders',
    label: 'Reminders / Deadlines',
    icon: '⏰',
    scope: 'reminders',
    speed: 'n/a',
    dataNote: 'Grounds on your upcoming reminders and deadlines.',
    system:
      'You are the AbramsOS Reminders agent. Using only the CONTEXT records, help the user ' +
      'see what is coming up and what is overdue. Be exact about due dates and order things ' +
      'by urgency. You do not create or dismiss reminders — that stays in the app UI.',
  },
  {
    id: 'warranties',
    label: 'Warranties',
    icon: '🛡️',
    scope: 'warranties',
    speed: 'n/a',
    dataNote: 'Grounds on your service commitments and warranty windows.',
    system:
      'You are the AbramsOS Warranties agent. Using only the CONTEXT records (service ' +
      'commitments and guarantee windows), help the user understand what coverage they have ' +
      'and when a warranty/return window closes. You never file a claim — that stays gated.',
  },
];

const MODEL_BY_ID = Object.fromEntries(MODELS.map((m) => [m.id, m]));
const AGENT_BY_ID = Object.fromEntries(AGENTS.map((a) => [a.id, a]));

const DEFAULT_MODEL_ID = 'openai-gpt-5.2'; // funded external lane (external → prompt is redacted + NER-scrubbed before send)
const DEFAULT_AGENT_ID = 'general';

module.exports = {
  MODELS,
  AGENTS,
  MODEL_BY_ID,
  AGENT_BY_ID,
  DEFAULT_MODEL_ID,
  DEFAULT_AGENT_ID,
};