/** * 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. * * Default model: Claude Haiku 4.5 — fast, cheap, smart enough for variant * generation in test fixtures. Override via { model } when constructing, * or via the IMPECCABLE_E2E_LLM_MODEL env var at the call site (test runner). * * 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). * * Returns null from createLlmAgent() when ANTHROPIC_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, 'source', 'skills', 'impeccable', 'reference', 'live.md', ); const DEFAULT_MODEL = 'claude-haiku-4-5'; 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 a