/** * LLM-backed LiveAgent for the live-mode E2E suite. * * Implements the same interface as createFakeAgent() in * tests/live-e2e/agent.mjs: generateVariants(event, context) returns * { scopedCss, variants[] }, and applyManualEdits(event, context) returns the * production manual-edit Apply result shape. It also implements * handleSteer(event, context) for page-level Steer bar messages. The * orchestrator handles wrap, write, accept, and carbonize cleanup * deterministically. * * 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'; import { applyManualEditBatchToSource, loadManualEditEventBatch } from '../agent.mjs'; import { applySteerEdits } from '../agent.mjs'; 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'); // Frontier default: gpt-5.6-terra at medium reasoning effort. Haiku stayed // too far below the models that actually drive live sessions in the field; // a spec change Haiku tolerates can still confuse or be confused by the // frontier tier, so the harness should exercise the tier users run. const DEFAULT_OPENAI_MODEL = 'gpt-5.6-terra'; const DEFAULT_OPENAI_REASONING_EFFORT = 'medium'; 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 LLM_REQUEST_MAX_RETRIES = 1; const VARIANT_REQUEST_TIMEOUT_MS = 105_000; const MANUAL_EDIT_REQUEST_TIMEOUT_MS = 55_000; const MANUAL_EDIT_RESPONSE_MAX_ATTEMPTS = 3; export const VARIANT_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; replace mode matches the picked element tag, insert mode is net-new content",', ' "params": [/* optional 0-4 ParamSpec entries */]', ' }', ' ]', '}', '', 'ParamSpec is one of:', ' { "id": "string", "kind": "range", "min": number, "max": number, "step": number, "default": number, "label": "string" }', ' { "id": "string", "kind": "steps", "default": "string", "label": "string", "options": [{ "value": "string", "label": "string" }, ...] }', ' { "id": "string", "kind": "toggle", "default": boolean, "label": "string" }', '', 'REQUIREMENTS', '- Replace mode: each variant.innerHtml must be a single top-level HTML element using the EXACT same tag as the picked element.', '- Insert mode (`event.mode === "insert"`): each variant.innerHtml must be net-new content that honors event.freeformPrompt. It does NOT replace the anchor and does NOT need to use the anchor tag or preserve anchor copy.', '- Insert mode variants must contain visible inserted content. Do not return empty roots, placeholder-only roots, inline style= attributes, or test hooks like
.', '- Replace mode: the single top-level element is the replacement root itself. If the picked element is, keep those elements/classes as direct descendants of the replacement root; do not wrap them in a new structural div such as