/** * LLM-backed VariantAgent for the live-mode E2E suite. * * Implements the same one-method interface as createFakeAgent() in * tests/live-e2e/agent.mjs: generateVariants(event, context) returns * { scopedCss, variants[] }. The orchestrator handles wrap, write, accept, * and carbonize cleanup deterministically, so this module's only job is * producing variant content for the wrapper. * * Primary provider/model: Anthropic + Claude Haiku 4.5. DeepSeek V4 Flash is * a secondary cheap fallback used only when ANTHROPIC_API_KEY is absent and * DEEPSEEK_API_KEY is present, or when explicitly forced with * IMPECCABLE_E2E_LLM_PROVIDER=deepseek. Override the model via { model } when * constructing, or via IMPECCABLE_E2E_LLM_MODEL at the call site. * * Prompt caching: live.md (the live-mode skill spec) is the bulk of the * system prompt and is stable across calls. We mark a cache_control breakpoint * on the last system block so both the JSON-contract instructions and the * spec are cached as one prefix. Subsequent calls in the same run pay only * the cache-read rate (~0.1× input) when the selected provider honors it. * * Returns null from createLlmAgent() when the selected provider's API key is * unset; the test runner reads that and skips the case rather than failing. */ import fs from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import Anthropic from '@anthropic-ai/sdk'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const REPO_ROOT = path.join(__dirname, '..', '..', '..'); const LIVE_MD_PATH = path.join(REPO_ROOT, 'skill', 'reference', 'live.md'); const DEFAULT_ANTHROPIC_MODEL = 'claude-haiku-4-5'; // DeepSeek model list: https://api-docs.deepseek.com/api/list-models const DEFAULT_DEEPSEEK_MODEL = 'deepseek-v4-flash'; const DEFAULT_DEEPSEEK_API_BASE_URL = 'https://api.deepseek.com/anthropic'; const SYSTEM_INSTRUCTIONS = [ 'You are an automated subagent inside Impeccable\'s live-mode test harness.', 'Given an element the user picked, an action, and a count, you produce variant DOM content in a strict JSON shape.', '', 'OUTPUT CONTRACT — return ONLY a JSON object with this exact shape. No prose, no code fences, no commentary:', '', '{', ' "scopedCss": "string — contents of the preview CSS block, authored according to wrapInfo.cssAuthoring",', ' "variants": [', ' {', ' "innerHtml": "string — single top-level HTML element matching the picked element\'s tag, e.g.