visual-ai-assertions 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +4 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/errors.ts","../src/core/prompt.ts","../src/templates/elements-visibility.ts","../src/templates/accessibility.ts","../src/templates/layout.ts","../src/templates/page-load.ts","../src/templates/content.ts","../src/providers/error-mapper.ts","../src/providers/anthropic.ts","../src/providers/google.ts","../src/providers/openai.ts","../src/core/config.ts","../src/core/pricing.ts","../src/core/debug.ts","../src/core/diff.ts","../src/core/image.ts","../src/types.ts","../src/core/response.ts","../src/core/client.ts","../src/format.ts"],"sourcesContent":["// Client\nexport { visualAI } from \"./core/client.js\";\n\n// Constants\nexport {\n Provider,\n Model,\n Content,\n Layout,\n Accessibility,\n ReasoningEffort,\n DEFAULT_MODELS,\n} from \"./constants.js\";\nexport type {\n KnownModelName,\n ContentCheckName,\n LayoutCheckName,\n AccessibilityCheckName,\n ReasoningEffortLevel,\n} from \"./constants.js\";\nexport type { VisualAIClient } from \"./core/client.js\";\n\n// Types\nexport type {\n CheckResult,\n CheckOptions,\n CompareResult,\n CompareOptions,\n ChangeEntry,\n Confidence,\n AskResult,\n AskOptions,\n Issue,\n IssuePriority,\n IssueCategory,\n StatementResult,\n UsageInfo,\n VisualAIConfig,\n ImageInput,\n ProviderName,\n ElementsVisibilityOptions,\n AccessibilityOptions,\n LayoutOptions,\n PageLoadOptions,\n ContentOptions,\n DiffImageResult,\n SupportedMimeType,\n} from \"./types.js\";\nexport type { VisualAIErrorCode, VisualAIKnownError } from \"./errors.js\";\n\n// Zod schemas (for advanced users)\nexport {\n CheckResultSchema,\n CompareResultSchema,\n ChangeEntrySchema,\n ConfidenceSchema,\n AskResultSchema,\n IssueSchema,\n IssuePrioritySchema,\n IssueCategorySchema,\n StatementResultSchema,\n UsageInfoSchema,\n} from \"./types.js\";\n\n// Errors\nexport {\n VisualAIError,\n VisualAIAuthError,\n VisualAIRateLimitError,\n VisualAIProviderError,\n VisualAIImageError,\n VisualAIResponseParseError,\n VisualAITruncationError,\n VisualAIConfigError,\n VisualAIAssertionError,\n isVisualAIKnownError,\n} from \"./errors.js\";\n\n// Formatting & assertions\nexport {\n formatCheckResult,\n formatCompareResult,\n assertVisualResult,\n assertVisualCompareResult,\n} from \"./format.js\";\n","import type { ProviderName } from \"./types.js\";\n\n// --- Reasoning effort constants ---\n\n/** Supported reasoning effort levels. */\nexport const ReasoningEffort = {\n LOW: \"low\",\n MEDIUM: \"medium\",\n HIGH: \"high\",\n XHIGH: \"xhigh\",\n} as const;\n\n/** Union of valid reasoning effort values, derived from the ReasoningEffort constant. */\nexport type ReasoningEffortLevel = (typeof ReasoningEffort)[keyof typeof ReasoningEffort];\n\n// --- Provider constants ---\n\n/** Supported provider identifiers used internally for pricing and provider selection. */\nexport const Provider = {\n ANTHROPIC: \"anthropic\",\n OPENAI: \"openai\",\n GOOGLE: \"google\",\n} as const satisfies Record<string, ProviderName>;\n\n// --- Model constants (grouped by provider) ---\n\n/** Known model names grouped by provider. */\nexport const Model = {\n Anthropic: {\n OPUS_4_6: \"claude-opus-4-6\",\n SONNET_4_6: \"claude-sonnet-4-6\",\n HAIKU_4_5: \"claude-haiku-4-5\",\n },\n OpenAI: {\n GPT_5_4: \"gpt-5.4\",\n GPT_5_4_PRO: \"gpt-5.4-pro\",\n GPT_5_4_MINI: \"gpt-5.4-mini\",\n GPT_5_4_NANO: \"gpt-5.4-nano\",\n GPT_5_2: \"gpt-5.2\",\n GPT_5_MINI: \"gpt-5-mini\",\n },\n Google: {\n GEMINI_3_1_PRO_PREVIEW: \"gemini-3.1-pro-preview\",\n GEMINI_3_1_FLASH_LITE_PREVIEW: \"gemini-3.1-flash-lite-preview\",\n GEMINI_3_FLASH_PREVIEW: \"gemini-3-flash-preview\",\n },\n} as const;\n\n// --- Derived utility types ---\n\n/** Union of all built-in model name literals exposed by `Model`. */\nexport type KnownModelName =\n | (typeof Model.Anthropic)[keyof typeof Model.Anthropic]\n | (typeof Model.OpenAI)[keyof typeof Model.OpenAI]\n | (typeof Model.Google)[keyof typeof Model.Google];\n\n// --- Default model per provider ---\n\n/** Default model selection used when a caller omits `config.model`. */\nexport const DEFAULT_MODELS = {\n [Provider.ANTHROPIC]: Model.Anthropic.SONNET_4_6,\n [Provider.OPENAI]: Model.OpenAI.GPT_5_4_MINI,\n [Provider.GOOGLE]: Model.Google.GEMINI_3_FLASH_PREVIEW,\n} as const satisfies Record<ProviderName, KnownModelName>;\n\nexport const DEFAULT_MAX_TOKENS = 4096;\n\n/**\n * Increased token budget for OpenAI when reasoning effort is high/xhigh.\n * Reasoning tokens share the output budget on OpenAI, so the default 4096\n * is insufficient for higher reasoning levels.\n */\nexport const OPENAI_REASONING_MAX_TOKENS = 16384;\n\n// --- Reverse map: model → provider ---\n\nexport const MODEL_TO_PROVIDER: ReadonlyMap<string, ProviderName> = new Map([\n ...Object.values(Model.Anthropic).map((m) => [m, Provider.ANTHROPIC] as const),\n ...Object.values(Model.OpenAI).map((m) => [m, Provider.OPENAI] as const),\n ...Object.values(Model.Google).map((m) => [m, Provider.GOOGLE] as const),\n]);\n\n// --- Valid providers array ---\n\n/** List of accepted provider names for validation and public consumption. */\nexport const VALID_PROVIDERS: readonly ProviderName[] = Object.values(Provider);\n\n// --- Provider default reasoning ---\n\n/**\n * What each provider uses when no reasoning effort is explicitly requested.\n * These are informational only — displayed in usage logs, not sent to providers.\n */\nexport const PROVIDER_DEFAULT_REASONING: Readonly<Record<ProviderName, string>> = {\n openai: \"medium\",\n anthropic: \"off\",\n google: \"off\",\n};\n\n// --- Check name constants ---\n\n/** Built-in content checks available through `client.content()`. */\nexport const Content = {\n /** Detects Lorem ipsum, TODO, TBD, and similar placeholder text */\n PLACEHOLDER_TEXT: \"placeholder-text\",\n /** Detects error messages, banners, stack traces, or error codes */\n ERROR_MESSAGES: \"error-messages\",\n /** Detects broken image icons or failed-to-load image indicators */\n BROKEN_IMAGES: \"broken-images\",\n /** Detects UI elements that unintentionally overlap and obscure content */\n OVERLAPPING_ELEMENTS: \"overlapping-elements\",\n} as const;\n\n/** Built-in layout checks available through `client.layout()`. */\nexport const Layout = {\n /** Detects elements that unintentionally overlap each other */\n OVERLAP: \"overlap\",\n /** Detects content cut off or extending beyond container boundaries */\n OVERFLOW: \"overflow\",\n /** Detects inconsistent alignment of text, images, and UI components */\n ALIGNMENT: \"alignment\",\n} as const;\n\n/** Built-in accessibility checks available through `client.accessibility()`. */\nexport const Accessibility = {\n /** Detects insufficient color contrast between text and backgrounds */\n CONTRAST: \"contrast\",\n /** Detects text that is cut off, overlapping, too small, or obscured */\n READABILITY: \"readability\",\n /** Detects interactive elements that are not visually distinct */\n INTERACTIVE_VISIBILITY: \"interactive-visibility\",\n} as const;\n\n// --- Derived check-name union types ---\n\n/** Union of all built-in content check names. */\nexport type ContentCheckName = (typeof Content)[keyof typeof Content];\n/** Union of all built-in layout check names. */\nexport type LayoutCheckName = (typeof Layout)[keyof typeof Layout];\n/** Union of all built-in accessibility check names. */\nexport type AccessibilityCheckName = (typeof Accessibility)[keyof typeof Accessibility];\n","import type { CheckResult, CompareResult } from \"./types.js\";\n\n/**\n * Discrete error codes exposed by visual-ai-assertions for programmatic handling.\n */\nexport type VisualAIErrorCode =\n | \"VISUAL_AI_ERROR\"\n | \"AUTH_FAILED\"\n | \"RATE_LIMITED\"\n | \"PROVIDER_ERROR\"\n | \"IMAGE_INVALID\"\n | \"RESPONSE_PARSE_FAILED\"\n | \"RESPONSE_TRUNCATED\"\n | \"CONFIG_INVALID\"\n | \"ASSERTION_FAILED\";\n\n/**\n * Base class for all library errors.\n *\n * @example\n * ```ts\n * try {\n * // ...\n * } catch (error) {\n * if (error instanceof VisualAIError) {\n * console.error(error.code, error.message);\n * }\n * }\n * ```\n */\nexport class VisualAIError<TCode extends VisualAIErrorCode = VisualAIErrorCode> extends Error {\n readonly code: TCode;\n\n constructor(message: string, code: TCode = \"VISUAL_AI_ERROR\" as TCode) {\n super(message);\n this.code = code;\n this.name = \"VisualAIError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a provider rejects the configured API credentials.\n *\n * @example\n * ```ts\n * throw new VisualAIAuthError(\"Anthropic API key not found\");\n * ```\n */\nexport class VisualAIAuthError extends VisualAIError<\"AUTH_FAILED\"> {\n declare readonly code: \"AUTH_FAILED\";\n\n constructor(message: string) {\n super(message, \"AUTH_FAILED\");\n this.name = \"VisualAIAuthError\";\n }\n}\n\n/**\n * Thrown when a provider enforces a rate limit.\n *\n * Carries `retryAfter` when the provider includes retry guidance.\n *\n * @example\n * ```ts\n * throw new VisualAIRateLimitError(\"Rate limited\", 30);\n * ```\n */\nexport class VisualAIRateLimitError extends VisualAIError<\"RATE_LIMITED\"> {\n declare readonly code: \"RATE_LIMITED\";\n retryAfter?: number;\n\n constructor(message: string, retryAfter?: number) {\n super(message, \"RATE_LIMITED\");\n this.name = \"VisualAIRateLimitError\";\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Thrown when a provider returns an unexpected non-auth, non-rate-limit failure.\n *\n * Carries `statusCode` when the provider exposes an HTTP status.\n *\n * @example\n * ```ts\n * throw new VisualAIProviderError(\"Provider returned 500\", 500);\n * ```\n */\nexport class VisualAIProviderError extends VisualAIError<\"PROVIDER_ERROR\"> {\n declare readonly code: \"PROVIDER_ERROR\";\n statusCode?: number;\n\n constructor(message: string, statusCode?: number) {\n super(message, \"PROVIDER_ERROR\");\n this.name = \"VisualAIProviderError\";\n this.statusCode = statusCode;\n }\n}\n\n/**\n * Thrown when an image input cannot be loaded, decoded, or validated.\n *\n * @example\n * ```ts\n * throw new VisualAIImageError(\"Unsupported image format: image/bmp\");\n * ```\n */\nexport class VisualAIImageError extends VisualAIError<\"IMAGE_INVALID\"> {\n declare readonly code: \"IMAGE_INVALID\";\n\n constructor(message: string) {\n super(message, \"IMAGE_INVALID\");\n this.name = \"VisualAIImageError\";\n }\n}\n\n/**\n * Thrown when a provider response cannot be parsed into the library result schema.\n *\n * Carries `rawResponse` so callers can inspect the original model output.\n *\n * @example\n * ```ts\n * throw new VisualAIResponseParseError(\"Invalid JSON\", rawText);\n * ```\n */\nexport class VisualAIResponseParseError extends VisualAIError<\"RESPONSE_PARSE_FAILED\"> {\n declare readonly code: \"RESPONSE_PARSE_FAILED\";\n rawResponse: string;\n\n constructor(message: string, rawResponse: string) {\n super(message, \"RESPONSE_PARSE_FAILED\");\n this.name = \"VisualAIResponseParseError\";\n this.rawResponse = rawResponse;\n }\n}\n\n/**\n * Thrown when a provider response is truncated due to token budget exhaustion.\n *\n * Carries `partialResponse` and `maxTokens` so callers can inspect the output\n * and adjust configuration.\n *\n * @example\n * ```ts\n * throw new VisualAITruncationError(\"Response truncated\", partialText, 4096);\n * ```\n */\nexport class VisualAITruncationError extends VisualAIError<\"RESPONSE_TRUNCATED\"> {\n declare readonly code: \"RESPONSE_TRUNCATED\";\n readonly partialResponse: string;\n readonly maxTokens: number;\n\n constructor(message: string, partialResponse: string, maxTokens: number) {\n super(message, \"RESPONSE_TRUNCATED\");\n this.name = \"VisualAITruncationError\";\n this.partialResponse = partialResponse;\n this.maxTokens = maxTokens;\n }\n}\n\n/**\n * Thrown when library configuration is missing or invalid.\n *\n * @example\n * ```ts\n * throw new VisualAIConfigError(\"At least one statement is required for check()\");\n * ```\n */\nexport class VisualAIConfigError extends VisualAIError<\"CONFIG_INVALID\"> {\n declare readonly code: \"CONFIG_INVALID\";\n\n constructor(message: string) {\n super(message, \"CONFIG_INVALID\");\n this.name = \"VisualAIConfigError\";\n }\n}\n\n/**\n * Thrown by assertion helpers when a visual check or comparison fails.\n *\n * Carries the failed `result` for further inspection.\n *\n * @example\n * ```ts\n * throw new VisualAIAssertionError(\"Visual assertion failed\", result);\n * ```\n */\nexport class VisualAIAssertionError extends VisualAIError<\"ASSERTION_FAILED\"> {\n declare readonly code: \"ASSERTION_FAILED\";\n result: CheckResult | CompareResult;\n\n constructor(message: string, result: CheckResult | CompareResult) {\n super(message, \"ASSERTION_FAILED\");\n this.name = \"VisualAIAssertionError\";\n this.result = result;\n }\n}\n\n/**\n * Union of all concrete error subclasses exposed by the library.\n */\nexport type VisualAIKnownError =\n | VisualAIAuthError\n | VisualAIRateLimitError\n | VisualAIProviderError\n | VisualAIImageError\n | VisualAIResponseParseError\n | VisualAITruncationError\n | VisualAIConfigError\n | VisualAIAssertionError;\n\n/**\n * Narrows an unknown thrown value to the concrete visual-ai-assertions error union.\n *\n * Use this helper when you want `switch (error.code)` to narrow to subclass-specific fields.\n *\n * @param error Unknown thrown value.\n * @returns `true` when the value is one of the concrete library error subclasses.\n * @example\n * ```ts\n * try {\n * // ...\n * } catch (error) {\n * if (isVisualAIKnownError(error)) {\n * switch (error.code) {\n * case \"RATE_LIMITED\":\n * console.error(error.retryAfter);\n * break;\n * case \"PROVIDER_ERROR\":\n * console.error(error.statusCode);\n * break;\n * }\n * }\n * }\n * ```\n */\nexport function isVisualAIKnownError(error: unknown): error is VisualAIKnownError {\n return (\n error instanceof VisualAIAuthError ||\n error instanceof VisualAIRateLimitError ||\n error instanceof VisualAIProviderError ||\n error instanceof VisualAIImageError ||\n error instanceof VisualAIResponseParseError ||\n error instanceof VisualAITruncationError ||\n error instanceof VisualAIConfigError ||\n error instanceof VisualAIAssertionError\n );\n}\n","const JSON_INSTRUCTIONS = `\nIMPORTANT: You MUST respond with valid JSON only. No markdown, no code blocks, no extra text.\n`;\n\nconst ISSUE_SCHEMA_INSTRUCTIONS = `\nEach issue must have:\n- \"priority\": \"critical\" | \"major\" | \"minor\"\n- \"category\": \"accessibility\" | \"missing-element\" | \"layout\" | \"content\" | \"styling\" | \"functionality\" | \"performance\" | \"other\"\n- \"description\": what the issue is\n- \"suggestion\": how to fix or improve it\n`;\n\nconst CHECK_OUTPUT_SCHEMA = `IMPORTANT: Follow this evaluation order:\n1. First, evaluate EACH statement independently and populate the \"statements\" array\n2. Then, set \"pass\" to true ONLY if every statement passed (logical AND of all statement results)\n3. Write \"reasoning\" as a brief overall summary of the evaluation\n4. Include \"issues\" only for statements that failed\n\nRespond with a JSON object matching this exact structure:\n{\n \"pass\": boolean, // true ONLY if ALL statements passed — derive from statements array\n \"reasoning\": string, // brief overall summary of the evaluation\n \"issues\": [...], // one issue per failing statement (empty if all pass)\n \"statements\": [ // one entry per statement, in order — evaluate these FIRST\n {\n \"statement\": string, // the original statement text\n \"pass\": boolean, // whether this statement is true\n \"reasoning\": string, // explanation for this statement\n \"confidence\": \"high\" | \"medium\" | \"low\"\n // high = clearly visible/absent with no ambiguity\n // medium = present but partially obscured, small, or borderline\n // low = cannot determine with certainty from the screenshot\n }\n ]\n}\n${ISSUE_SCHEMA_INSTRUCTIONS}\n\nOnly include issues for statements that fail. If all statements pass, issues should be an empty array.\n\nExample for a failing check:\n{\n \"pass\": false,\n \"reasoning\": \"The submit button is not visible on the page.\",\n \"issues\": [\n { \"priority\": \"major\", \"category\": \"missing-element\", \"description\": \"Submit button is not visible on the page\", \"suggestion\": \"Verify the submit button component is rendered and not hidden by CSS\" }\n ],\n \"statements\": [\n { \"statement\": \"The page header is visible\", \"pass\": true, \"reasoning\": \"Header with logo is clearly visible at the top\", \"confidence\": \"high\" },\n { \"statement\": \"The submit button is visible\", \"pass\": false, \"reasoning\": \"No submit button found in the visible area of the page\", \"confidence\": \"high\" }\n ]\n}\n${JSON_INSTRUCTIONS}`;\n\nconst ASK_OUTPUT_SCHEMA = `Respond with a JSON object matching this exact structure:\n{\n \"summary\": string, // high-level analysis summary\n \"issues\": [...] // list of issues/findings, can be empty\n}\n${ISSUE_SCHEMA_INSTRUCTIONS}\n\nPrioritize issues by severity:\n- \"critical\": blocks functionality, breaks accessibility, data loss risk\n- \"major\": significant usability or visual problem\n- \"minor\": cosmetic issue, minor improvement suggestion\n\nExample:\n{\n \"summary\": \"Found 2 issues: a critical accessibility problem and a minor cosmetic issue.\",\n \"issues\": [\n { \"priority\": \"critical\", \"category\": \"accessibility\", \"description\": \"Submit button has insufficient color contrast\", \"suggestion\": \"Increase contrast so text is clearly readable against the background\" },\n { \"priority\": \"minor\", \"category\": \"content\", \"description\": \"Placeholder text 'Lorem ipsum' visible in sidebar\", \"suggestion\": \"Replace with actual content or remove the placeholder section\" }\n ]\n}\n${JSON_INSTRUCTIONS}`;\n\nconst COMPARE_OUTPUT_SCHEMA = `Respond with a JSON object matching this exact structure:\n{\n \"pass\": boolean, // true if no critical or major changes found\n \"reasoning\": string, // overall summary of changes detected\n \"changes\": [ // list of all visual differences detected (empty if images are identical)\n {\n \"description\": string, // what changed between the images\n \"severity\": \"critical\" | \"major\" | \"minor\"\n // critical = element removed, layout broken, functionality lost\n // major = significant visual change that may be unintentional\n // minor = small stylistic difference (color, spacing, font)\n }\n ]\n}\n\nIf the images appear identical, set pass to true, explain in reasoning, and return an empty changes array.\n${JSON_INSTRUCTIONS}`;\n\nconst DEFAULT_CHECK_ROLE =\n \"You are a visual QA assistant. Evaluate the provided image precisely and objectively.\";\n\nconst DEFAULT_ASK_ROLE =\n \"You are a visual QA assistant. Analyze the provided image based on the user's request.\";\n\nconst COMPARE_ROLE =\n \"You are performing a visual regression test. Compare the BEFORE image (baseline) to the AFTER image (current) and identify all visual differences. Flag changes that appear unintentional or problematic.\";\n\nconst COMPARE_EDGE_RULES: readonly string[] = [\n \"The BEFORE image is the baseline/expected state.\",\n \"Flag removals and layout changes as higher severity. Color and spacing changes are lower severity unless they break readability.\",\n \"If the images appear identical, report no changes and pass.\",\n];\n\nexport interface CheckPromptOptions {\n readonly role?: string;\n readonly instructions?: readonly string[];\n}\n\nexport interface ComparePromptOptions {\n readonly userPrompt?: string;\n readonly instructions?: readonly string[];\n}\n\nfunction buildInstructionsSection(instructions: readonly string[]): string {\n return (\n \"Additional instructions:\\n\" + instructions.map((instruction) => `- ${instruction}`).join(\"\\n\")\n );\n}\n\nexport function buildCheckPrompt(\n statements: string | string[],\n options?: CheckPromptOptions,\n): string {\n const stmts = Array.isArray(statements) ? statements : [statements];\n const statementsBlock = stmts.map((s, i) => `${i + 1}. \"${s}\"`).join(\"\\n\");\n\n const sections = [options?.role ?? DEFAULT_CHECK_ROLE];\n\n if (options?.instructions && options.instructions.length > 0) {\n sections.push(buildInstructionsSection(options.instructions));\n }\n\n sections.push(`Statements to evaluate:\\n${statementsBlock}`);\n sections.push(CHECK_OUTPUT_SCHEMA);\n\n return sections.join(\"\\n\\n\");\n}\n\nexport function buildAskPrompt(\n userPrompt: string,\n options?: { readonly instructions?: readonly string[] },\n): string {\n const sections = [DEFAULT_ASK_ROLE];\n\n if (options?.instructions && options.instructions.length > 0) {\n sections.push(buildInstructionsSection(options.instructions));\n }\n\n sections.push(`User request: ${userPrompt}`);\n sections.push(ASK_OUTPUT_SCHEMA);\n\n return sections.join(\"\\n\\n\");\n}\n\nexport function buildAiDiffPrompt(): string {\n return `You are given two screenshots of the same page or component.\nGenerate a single annotated image that clearly highlights the visual differences between the first and second images.\n- Overlay semi-transparent red rectangles or outlines on areas that changed\n- Keep unchanged areas visible but slightly dimmed\n- The output image should match the dimensions of the input images\n- Focus on meaningful visual differences: layout shifts, missing elements, color changes, text differences`;\n}\n\nexport function buildAiDiffCodeExecutionPrompt(): string {\n return `${buildAiDiffPrompt()}\n\nWrite Python code using PIL/Pillow for image processing and matplotlib for rendering to accomplish the above.\nYour code MUST load both input images and display the result using matplotlib.`;\n}\n\nexport function buildComparePrompt(options?: ComparePromptOptions): string {\n const evaluation = options?.userPrompt\n ? `User request: ${options.userPrompt}`\n : \"Identify all visual differences between the baseline and current screenshot. Flag any changes that appear unintentional or problematic.\";\n\n const instructions = options?.instructions\n ? [...COMPARE_EDGE_RULES, ...options.instructions]\n : COMPARE_EDGE_RULES;\n\n const sections = [\n COMPARE_ROLE,\n buildInstructionsSection(instructions),\n evaluation,\n COMPARE_OUTPUT_SCHEMA,\n ];\n\n return sections.join(\"\\n\\n\");\n}\n","import type { ElementsVisibilityOptions } from \"../types.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst ELEMENTS_VISIBLE_ROLE =\n \"Check whether specific UI elements are present and fully visible in this screenshot.\";\n\nconst ELEMENTS_HIDDEN_ROLE =\n \"Check whether specific UI elements are absent or hidden in this screenshot.\";\n\nconst ELEMENTS_VISIBLE_EDGE_RULES: readonly string[] = [\n \"If an element is partially visible (cut off by screenshot boundary), it is NOT considered fully visible — the check for that element should fail. Note the partial visibility in your reasoning.\",\n];\n\nconst ELEMENTS_HIDDEN_EDGE_RULES: readonly string[] = [\n \"If an element is partially visible (cut off by screenshot boundary), it is NOT considered hidden — the check for that element should fail. Note the partial visibility in your reasoning.\",\n];\n\nexport function buildElementsVisibilityPrompt(\n elements: string[],\n visible: boolean,\n options?: ElementsVisibilityOptions,\n): string {\n const statements = visible\n ? elements.map((el) => `The element \"${el}\" is fully visible on the page`)\n : elements.map((el) => `The element \"${el}\" is NOT visible on the page`);\n\n const defaultRules = visible ? ELEMENTS_VISIBLE_EDGE_RULES : ELEMENTS_HIDDEN_EDGE_RULES;\n const instructions = options?.instructions\n ? [...defaultRules, ...options.instructions]\n : defaultRules;\n\n return buildCheckPrompt(statements, {\n role: visible ? ELEMENTS_VISIBLE_ROLE : ELEMENTS_HIDDEN_ROLE,\n instructions,\n });\n}\n","import type { AccessibilityOptions } from \"../types.js\";\nimport { Accessibility, type AccessibilityCheckName } from \"../constants.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst ALL_CHECKS: AccessibilityCheckName[] = Object.values(Accessibility);\n\nconst ACCESSIBILITY_ROLE =\n \"Evaluate this screenshot for visual accessibility. Focus on what you can actually perceive — apparent contrast levels, text legibility, and visual distinctiveness of interactive elements.\";\n\nconst ACCESSIBILITY_EDGE_RULES: readonly string[] = [\n \"Do not state specific contrast ratios. Describe contrast as 'appears sufficient' or 'appears low'.\",\n \"Dark mode and light mode themes are both valid. Do not flag a valid dark theme as a contrast issue.\",\n];\n\nconst CHECK_STATEMENTS: Record<AccessibilityCheckName, string> = {\n [Accessibility.CONTRAST]:\n \"All text and interactive elements appear to have sufficient color contrast — text is clearly readable against its background\",\n [Accessibility.READABILITY]:\n \"All text is readable — no text is cut off, overlapping, too small to read, or obscured by background images\",\n [Accessibility.INTERACTIVE_VISIBILITY]:\n \"All interactive elements (buttons, links, inputs) are clearly identifiable and visually distinct from non-interactive content\",\n};\n\nexport function buildAccessibilityPrompt(options?: AccessibilityOptions): string {\n const checks = options?.checks ?? [...ALL_CHECKS];\n const statements = checks.map((c) => CHECK_STATEMENTS[c]);\n\n const instructions = options?.instructions\n ? [...ACCESSIBILITY_EDGE_RULES, ...options.instructions]\n : ACCESSIBILITY_EDGE_RULES;\n\n return buildCheckPrompt(statements, {\n role: ACCESSIBILITY_ROLE,\n instructions,\n });\n}\n","import type { LayoutOptions } from \"../types.js\";\nimport { Layout, type LayoutCheckName } from \"../constants.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst ALL_CHECKS: LayoutCheckName[] = Object.values(Layout);\n\nconst LAYOUT_ROLE =\n \"Evaluate this screenshot for visual layout problems — overlapping elements, content that appears cut off or overflowing, and inconsistent alignment patterns.\";\n\nconst LAYOUT_EDGE_RULES: readonly string[] = [\n \"Intentional overlaps (stacked avatars, dropdown menus, overlapping cards) are acceptable. Flag only overlaps that obscure content or appear broken.\",\n \"Scrollable containers with partially visible content are not overflow issues.\",\n];\n\nconst CHECK_STATEMENTS: Record<LayoutCheckName, string> = {\n [Layout.OVERLAP]:\n \"No elements overlap each other unintentionally — all content is clearly separated and readable\",\n [Layout.OVERFLOW]:\n \"No content appears to be unintentionally cut off or extending beyond its container boundaries\",\n [Layout.ALIGNMENT]:\n \"Elements are properly aligned — text, images, and UI components follow a consistent grid or alignment pattern\",\n};\n\nexport function buildLayoutPrompt(options?: LayoutOptions): string {\n const checks = options?.checks ?? [...ALL_CHECKS];\n const statements = checks.map((c) => CHECK_STATEMENTS[c]);\n\n const instructions = options?.instructions\n ? [...LAYOUT_EDGE_RULES, ...options.instructions]\n : LAYOUT_EDGE_RULES;\n\n return buildCheckPrompt(statements, { role: LAYOUT_ROLE, instructions });\n}\n","import type { PageLoadOptions } from \"../types.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst PAGE_LOAD_ROLE =\n \"Evaluate whether this page has finished loading. Look for loading indicators, empty content areas, and missing resources.\";\n\nexport function buildPageLoadPrompt(options?: PageLoadOptions): string {\n const expectLoaded = options?.expectLoaded ?? true;\n\n const statements = expectLoaded\n ? [\n \"The page content has finished loading — no spinning indicators, skeleton placeholders, or progress bars are visible\",\n \"The main content area has actual content displayed (not blank or empty)\",\n \"No broken image icons or missing resource indicators are visible\",\n ]\n : [\n \"The page shows a loading state — loading spinners, skeleton screens, or progress indicators are visible\",\n ];\n\n return buildCheckPrompt(statements, {\n role: PAGE_LOAD_ROLE,\n instructions: options?.instructions,\n });\n}\n","import type { ContentOptions } from \"../types.js\";\nimport { Content, type ContentCheckName } from \"../constants.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst ALL_CHECKS: ContentCheckName[] = Object.values(Content);\n\nconst CONTENT_ROLE =\n \"Evaluate this screenshot for content quality problems — placeholder or dummy content, error states, and broken resources that should not appear in a production UI.\";\n\nconst CHECK_STATEMENTS: Record<ContentCheckName, string> = {\n [Content.PLACEHOLDER_TEXT]:\n \"No placeholder text like 'Lorem ipsum', 'TODO', 'TBD', 'placeholder', or similar dummy content is visible on the page\",\n [Content.ERROR_MESSAGES]:\n \"No error messages, error banners, stack traces, or error codes are visible on the page\",\n [Content.BROKEN_IMAGES]:\n \"No broken image icons, missing image placeholders, or failed-to-load image indicators are visible\",\n [Content.OVERLAPPING_ELEMENTS]:\n \"No UI elements are unintentionally overlapping each other, obscuring text, buttons, or other interactive content\",\n};\n\nexport function buildContentPrompt(options?: ContentOptions): string {\n const checks = options?.checks ?? [...ALL_CHECKS];\n const statements = checks.map((c) => CHECK_STATEMENTS[c]);\n\n return buildCheckPrompt(statements, {\n role: CONTENT_ROLE,\n instructions: options?.instructions,\n });\n}\n","import { VisualAIAuthError, VisualAIProviderError, VisualAIRateLimitError } from \"../errors.js\";\n\nexport function mapProviderError(err: unknown): Error {\n if (!(err instanceof Error)) {\n return new VisualAIProviderError(String(err));\n }\n\n const status = (err as { status?: number }).status;\n\n if (status === 401 || status === 403) {\n return new VisualAIAuthError(err.message);\n }\n if (status === 429) {\n const headers = (err as { headers?: Record<string, string> }).headers;\n const retryAfter = parseRetryAfter(headers?.[\"retry-after\"]);\n return new VisualAIRateLimitError(err.message, retryAfter);\n }\n if (status !== undefined) {\n return new VisualAIProviderError(err.message, status);\n }\n\n return new VisualAIProviderError(err.message);\n}\n\nfunction parseRetryAfter(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n","import { VisualAIAuthError, VisualAIConfigError, VisualAITruncationError } from \"../errors.js\";\nimport { mapProviderError } from \"./error-mapper.js\";\nimport type { NormalizedImage } from \"../types.js\";\nimport type {\n ProviderConfig,\n ProviderDriver,\n RawProviderResponse,\n SendMessageOptions,\n} from \"./types.js\";\n\n/** Minimal interface for the Anthropic SDK client used by this driver. */\ninterface AnthropicMessage {\n content: Array<{ type: string; text?: string }>;\n usage: { input_tokens: number; output_tokens: number };\n stop_reason?: string;\n}\n\ninterface AnthropicClient {\n messages: {\n create(params: Record<string, unknown>): Promise<AnthropicMessage>;\n };\n}\n\nexport class AnthropicDriver implements ProviderDriver {\n private client: AnthropicClient | null;\n private model: string;\n private maxTokens: number;\n private apiKeyOrEnv: string | undefined;\n private reasoningEffort: ProviderConfig[\"reasoningEffort\"];\n\n constructor(config: ProviderConfig) {\n this.model = config.model;\n this.maxTokens = config.maxTokens;\n this.client = null;\n this.apiKeyOrEnv = config.apiKey;\n this.reasoningEffort = config.reasoningEffort;\n }\n\n private async getClient(): Promise<AnthropicClient> {\n if (this.client) return this.client;\n\n let Anthropic: unknown;\n try {\n const mod: unknown = await import(\"@anthropic-ai/sdk\");\n Anthropic = (mod as { default: unknown }).default;\n } catch {\n throw new VisualAIConfigError(\n \"Anthropic SDK not installed. Run: npm install @anthropic-ai/sdk\",\n );\n }\n\n const apiKey = this.apiKeyOrEnv ?? process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n throw new VisualAIAuthError(\n \"Anthropic API key not found. Set ANTHROPIC_API_KEY or pass apiKey in config.\",\n );\n }\n\n this.client = new (Anthropic as new (opts: { apiKey: string }) => AnthropicClient)({ apiKey });\n return this.client;\n }\n\n async sendMessage(\n images: NormalizedImage[],\n prompt: string,\n _options?: SendMessageOptions,\n ): Promise<RawProviderResponse> {\n const client = await this.getClient();\n\n const imageBlocks = images.map((img) => ({\n type: \"image\" as const,\n source: {\n type: \"base64\" as const,\n media_type: img.mimeType,\n data: img.base64,\n },\n }));\n\n try {\n const requestParams: Record<string, unknown> = {\n model: this.model,\n max_tokens: this.maxTokens,\n messages: [\n {\n role: \"user\",\n content: [...imageBlocks, { type: \"text\" as const, text: prompt }],\n },\n ],\n };\n\n if (this.reasoningEffort) {\n requestParams.thinking = { type: \"adaptive\" };\n requestParams.output_config = {\n effort: this.reasoningEffort === \"xhigh\" ? \"max\" : this.reasoningEffort,\n };\n }\n\n const message = await client.messages.create(requestParams);\n\n const textBlock = message.content.find((block) => block.type === \"text\");\n const text = textBlock?.text ?? \"\";\n\n if (message.stop_reason === \"max_tokens\") {\n throw new VisualAITruncationError(\n `Response truncated: Anthropic stopped due to max_tokens limit (${this.maxTokens} tokens). Increase maxTokens in your config or lower reasoningEffort.`,\n text,\n this.maxTokens,\n );\n }\n\n return {\n text,\n usage: {\n inputTokens: message.usage.input_tokens,\n outputTokens: message.usage.output_tokens,\n },\n };\n } catch (err) {\n if (err instanceof VisualAITruncationError) throw err;\n throw mapProviderError(err);\n }\n }\n}\n","import { buildAiDiffCodeExecutionPrompt } from \"../core/prompt.js\";\nimport {\n VisualAIAuthError,\n VisualAIConfigError,\n VisualAIProviderError,\n VisualAITruncationError,\n} from \"../errors.js\";\nimport { mapProviderError } from \"./error-mapper.js\";\nimport type { NormalizedImage } from \"../types.js\";\nimport type { ReasoningEffortLevel } from \"../constants.js\";\nimport type {\n ImageGenerationOptions,\n ImageGenerationResponse,\n ProviderConfig,\n ProviderDriver,\n RawProviderResponse,\n SendMessageOptions,\n} from \"./types.js\";\n\nconst DEFAULT_IMAGE_GEN_MODEL = \"gemini-2.5-flash-image\";\n\n/** Gemini 3+ models use code execution for image generation instead of responseModalities. */\nexport function needsCodeExecution(model: string): boolean {\n const match = model.match(/^gemini-(\\d+)/);\n return match !== null && match[1] !== undefined && parseInt(match[1], 10) >= 3;\n}\n\n/** Response parts from Gemini API. Code execution responses include executableCode/codeExecutionResult\n * parts alongside inlineData; these fields are kept for type accuracy of the full response shape. */\ninterface GeminiImagePart {\n text?: string;\n inlineData?: {\n data: string;\n mimeType: string;\n };\n executableCode?: {\n code: string;\n language?: string;\n };\n codeExecutionResult?: {\n outcome?: string;\n output?: string;\n };\n}\n\n/** Minimal interface for the Google GenAI SDK client used by this driver. */\ninterface GoogleGenerateContentResponse {\n text?: string;\n candidates?: Array<{\n content?: {\n parts?: GeminiImagePart[];\n };\n finishReason?: string;\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n thoughtsTokenCount?: number;\n };\n}\n\ninterface GoogleClient {\n models: {\n generateContent(params: Record<string, unknown>): Promise<GoogleGenerateContentResponse>;\n };\n}\n\nconst GOOGLE_THINKING_LEVEL = {\n low: \"minimal\",\n medium: \"low\",\n high: \"medium\",\n xhigh: \"high\",\n} as const satisfies Record<ReasoningEffortLevel, string>;\n\nexport class GoogleDriver implements ProviderDriver {\n private client: GoogleClient | null;\n private model: string;\n private maxTokens: number;\n private apiKeyOrEnv: string | undefined;\n private reasoningEffort: ProviderConfig[\"reasoningEffort\"];\n\n constructor(config: ProviderConfig) {\n this.model = config.model;\n this.maxTokens = config.maxTokens;\n this.client = null;\n this.apiKeyOrEnv = config.apiKey;\n this.reasoningEffort = config.reasoningEffort;\n }\n\n private toGeminiParts(images: NormalizedImage[]) {\n return images.map((img) => ({\n inlineData: { data: img.base64, mimeType: img.mimeType },\n }));\n }\n\n private async getClient(): Promise<GoogleClient> {\n if (this.client) return this.client;\n\n let GoogleGenAI: unknown;\n try {\n const mod: unknown = await import(\"@google/genai\");\n GoogleGenAI = (mod as { GoogleGenAI: unknown }).GoogleGenAI;\n } catch {\n throw new VisualAIConfigError(\n \"Google GenAI SDK not installed. Run: npm install @google/genai\",\n );\n }\n\n const apiKey = this.apiKeyOrEnv ?? process.env.GOOGLE_API_KEY;\n if (!apiKey) {\n throw new VisualAIAuthError(\n \"Google API key not found. Set GOOGLE_API_KEY or pass apiKey in config.\",\n );\n }\n\n this.client = new (GoogleGenAI as new (opts: { apiKey: string }) => GoogleClient)({ apiKey });\n return this.client;\n }\n\n async sendMessage(\n images: NormalizedImage[],\n prompt: string,\n _options?: SendMessageOptions,\n ): Promise<RawProviderResponse> {\n const client = await this.getClient();\n\n try {\n const response = await client.models.generateContent({\n model: this.model,\n contents: [...this.toGeminiParts(images), prompt],\n config: {\n responseMimeType: \"application/json\",\n maxOutputTokens: this.maxTokens,\n ...(this.reasoningEffort && {\n thinkingConfig: {\n thinkingLevel: GOOGLE_THINKING_LEVEL[this.reasoningEffort],\n },\n }),\n },\n });\n\n const finishReason = response.candidates?.[0]?.finishReason;\n if (finishReason === \"MAX_TOKENS\") {\n throw new VisualAITruncationError(\n `Response truncated: Google returned finishReason \"MAX_TOKENS\". The model exhausted the output token budget (${this.maxTokens} tokens). Increase maxTokens in your config or lower reasoningEffort.`,\n response.text ?? \"\",\n this.maxTokens,\n );\n }\n if (finishReason && finishReason !== \"STOP\") {\n throw new VisualAIProviderError(\n `Response blocked: Google returned finishReason \"${finishReason}\".`,\n );\n }\n\n const text = response.text ?? \"\";\n const thoughtsTokenCount = response.usageMetadata?.thoughtsTokenCount;\n\n return {\n text,\n usage: response.usageMetadata\n ? {\n inputTokens: response.usageMetadata.promptTokenCount ?? 0,\n outputTokens: response.usageMetadata.candidatesTokenCount ?? 0,\n ...(thoughtsTokenCount !== undefined && { reasoningTokens: thoughtsTokenCount }),\n }\n : undefined,\n };\n } catch (err) {\n if (err instanceof VisualAITruncationError || err instanceof VisualAIProviderError) throw err;\n throw mapProviderError(err);\n }\n }\n\n async generateImage(\n images: NormalizedImage[],\n prompt: string,\n options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n const client = await this.getClient();\n const imageModel = options?.model ?? DEFAULT_IMAGE_GEN_MODEL;\n const resolvedPrompt =\n options?.promptKind === \"ai-diff\" && needsCodeExecution(imageModel)\n ? buildAiDiffCodeExecutionPrompt()\n : prompt;\n\n // Gemini 3+ models require code execution to generate images;\n // older models use native image generation via responseModalities.\n const config = needsCodeExecution(imageModel)\n ? { tools: [{ codeExecution: {} }] }\n : { responseModalities: [\"TEXT\", \"IMAGE\"] };\n\n try {\n const response = await client.models.generateContent({\n model: imageModel,\n contents: [...this.toGeminiParts(images), resolvedPrompt],\n config,\n });\n\n const parts = response.candidates?.[0]?.content?.parts;\n if (!parts) {\n throw new VisualAIProviderError(\"Gemini image generation returned no response parts\");\n }\n\n const imagePart = parts.find((p) => p.inlineData?.data);\n if (!imagePart?.inlineData) {\n throw new VisualAIProviderError(\n \"Gemini image generation returned no image data. Ensure the model supports image output.\",\n );\n }\n\n return {\n imageData: Buffer.from(imagePart.inlineData.data, \"base64\"),\n mimeType: imagePart.inlineData.mimeType,\n usage: response.usageMetadata\n ? {\n inputTokens: response.usageMetadata.promptTokenCount ?? 0,\n outputTokens: response.usageMetadata.candidatesTokenCount ?? 0,\n }\n : undefined,\n };\n } catch (err) {\n if (err instanceof VisualAIProviderError) throw err;\n throw mapProviderError(err);\n }\n }\n}\n","import { VisualAIAuthError, VisualAIConfigError, VisualAITruncationError } from \"../errors.js\";\nimport { mapProviderError } from \"./error-mapper.js\";\nimport type { NormalizedImage } from \"../types.js\";\nimport type {\n ProviderConfig,\n ProviderDriver,\n RawProviderResponse,\n SendMessageOptions,\n} from \"./types.js\";\n\n/** Minimal interface for the OpenAI SDK client used by this driver. */\ninterface OpenAIResponseResult {\n output_text?: string;\n usage?: {\n input_tokens: number;\n output_tokens: number;\n output_tokens_details?: { reasoning_tokens?: number };\n };\n status?: string;\n incomplete_details?: { reason?: string };\n}\n\ninterface OpenAIClient {\n responses: {\n create(params: Record<string, unknown>): Promise<OpenAIResponseResult>;\n };\n}\n\nexport class OpenAIDriver implements ProviderDriver {\n private client: OpenAIClient | null;\n private model: string;\n private maxTokens: number;\n private apiKeyOrEnv: string | undefined;\n private reasoningEffort: ProviderConfig[\"reasoningEffort\"];\n\n constructor(config: ProviderConfig) {\n this.model = config.model;\n this.maxTokens = config.maxTokens;\n this.client = null;\n this.apiKeyOrEnv = config.apiKey;\n this.reasoningEffort = config.reasoningEffort;\n }\n\n private async getClient(): Promise<OpenAIClient> {\n if (this.client) return this.client;\n\n let OpenAI: unknown;\n try {\n const mod: unknown = await import(\"openai\");\n OpenAI = (mod as { default: unknown }).default;\n } catch {\n throw new VisualAIConfigError(\"OpenAI SDK not installed. Run: npm install openai\");\n }\n\n const apiKey = this.apiKeyOrEnv ?? process.env.OPENAI_API_KEY;\n if (!apiKey) {\n throw new VisualAIAuthError(\n \"OpenAI API key not found. Set OPENAI_API_KEY or pass apiKey in config.\",\n );\n }\n\n this.client = new (OpenAI as new (opts: { apiKey: string }) => OpenAIClient)({ apiKey });\n return this.client;\n }\n\n async sendMessage(\n images: NormalizedImage[],\n prompt: string,\n options?: SendMessageOptions,\n ): Promise<RawProviderResponse> {\n const client = await this.getClient();\n\n const imageBlocks = images.map((img) => ({\n type: \"input_image\" as const,\n image_url: `data:${img.mimeType};base64,${img.base64}`,\n }));\n\n try {\n const format = options?.responseSchema\n ? {\n type: \"json_schema\" as const,\n json_schema: {\n name: \"visual_ai_response\",\n strict: true,\n schema: options.responseSchema,\n },\n }\n : { type: \"json_object\" as const, name: \"visual_ai_response\" };\n\n const requestParams: Record<string, unknown> = {\n model: this.model,\n max_output_tokens: this.maxTokens,\n text: { format },\n input: [\n {\n role: \"user\",\n content: [...imageBlocks, { type: \"input_text\" as const, text: prompt }],\n },\n ],\n };\n\n if (this.reasoningEffort) {\n requestParams.reasoning = { effort: this.reasoningEffort };\n }\n\n const response = await client.responses.create(requestParams);\n\n if (response.status && response.status !== \"completed\") {\n const detail = response.incomplete_details?.reason\n ? ` (${response.incomplete_details.reason})`\n : \"\";\n throw new VisualAITruncationError(\n `Response truncated: OpenAI returned status \"${response.status}\"${detail}. The model exhausted the output token budget (${this.maxTokens} tokens). This commonly happens with higher reasoning effort levels. Increase maxTokens in your config (e.g., maxTokens: 16384) or lower reasoningEffort.`,\n response.output_text ?? \"\",\n this.maxTokens,\n );\n }\n\n const text = response.output_text ?? \"\";\n const reasoningTokens = response.usage?.output_tokens_details?.reasoning_tokens;\n\n return {\n text,\n usage: response.usage\n ? {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n ...(reasoningTokens !== undefined && { reasoningTokens }),\n }\n : undefined,\n };\n } catch (err) {\n if (err instanceof VisualAITruncationError) throw err;\n throw mapProviderError(err);\n }\n }\n}\n","import {\n DEFAULT_MAX_TOKENS,\n DEFAULT_MODELS,\n MODEL_TO_PROVIDER,\n OPENAI_REASONING_MAX_TOKENS,\n} from \"../constants.js\";\nimport { VisualAIConfigError } from \"../errors.js\";\nimport type { ProviderName, VisualAIConfig } from \"../types.js\";\n\nexport interface ResolvedConfig {\n provider: ProviderName;\n apiKey: string | undefined;\n model: string;\n maxTokens: number;\n reasoningEffort: VisualAIConfig[\"reasoningEffort\"];\n debug: boolean;\n debugPrompt: boolean;\n debugResponse: boolean;\n trackUsage: boolean;\n}\n\nconst MODEL_PREFIX_TO_PROVIDER: [string, ProviderName][] = [\n [\"claude-\", \"anthropic\"],\n [\"gpt-\", \"openai\"],\n [\"o1-\", \"openai\"],\n [\"o3-\", \"openai\"],\n [\"o4-\", \"openai\"],\n [\"gemini-\", \"google\"],\n];\n\nfunction inferProviderFromModel(model: string): ProviderName | undefined {\n const known = MODEL_TO_PROVIDER.get(model);\n if (known) return known;\n\n const prefixMatch = MODEL_PREFIX_TO_PROVIDER.find(([prefix]) => model.startsWith(prefix));\n return prefixMatch?.[1];\n}\n\nfunction resolveProvider(config: VisualAIConfig): ProviderName {\n const model = config.model ?? process.env.VISUAL_AI_MODEL;\n if (model) {\n const inferred = inferProviderFromModel(model);\n if (inferred) return inferred;\n }\n\n const apiKeyProviderMap: [string, ProviderName][] = [\n [\"ANTHROPIC_API_KEY\", \"anthropic\"],\n [\"OPENAI_API_KEY\", \"openai\"],\n [\"GOOGLE_API_KEY\", \"google\"],\n ];\n const detected = apiKeyProviderMap.find(([key]) => process.env[key]);\n if (detected) return detected[1];\n\n throw new VisualAIConfigError(\n \"Cannot determine provider. Set a model name (config or VISUAL_AI_MODEL) or an API key env variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY).\",\n );\n}\n\nfunction parseBooleanEnv(envName: string, value: string | undefined): boolean | undefined {\n if (value === undefined || value === \"\") return undefined;\n const lower = value.toLowerCase();\n if (lower === \"true\" || lower === \"1\") return true;\n if (lower === \"false\" || lower === \"0\") return false;\n throw new VisualAIConfigError(\n `Invalid ${envName} value: \"${value}\". Use \"true\", \"1\", \"false\", or \"0\".`,\n );\n}\n\nlet debugDeprecationWarned = false;\n\n/** @internal Reset the deprecation warning guard. For testing only. */\nexport function resetDebugDeprecationWarning(): void {\n debugDeprecationWarned = false;\n}\n\nexport function resolveConfig(config: VisualAIConfig): ResolvedConfig {\n const provider = resolveProvider(config);\n const model = config.model ?? process.env.VISUAL_AI_MODEL ?? DEFAULT_MODELS[provider];\n const debug =\n config.debug ?? parseBooleanEnv(\"VISUAL_AI_DEBUG\", process.env.VISUAL_AI_DEBUG) ?? false;\n const debugPrompt =\n config.debugPrompt ??\n parseBooleanEnv(\"VISUAL_AI_DEBUG_PROMPT\", process.env.VISUAL_AI_DEBUG_PROMPT) ??\n false;\n const debugResponse =\n config.debugResponse ??\n parseBooleanEnv(\"VISUAL_AI_DEBUG_RESPONSE\", process.env.VISUAL_AI_DEBUG_RESPONSE) ??\n false;\n\n if (debug && !debugPrompt && !debugResponse && !debugDeprecationWarned) {\n debugDeprecationWarned = true;\n process.stderr.write(\n `[visual-ai-assertions] Warning: VISUAL_AI_DEBUG no longer enables prompt/response logging. Use VISUAL_AI_DEBUG_PROMPT=true and/or VISUAL_AI_DEBUG_RESPONSE=true instead.\\n`,\n );\n }\n\n const userSetMaxTokens = config.maxTokens !== undefined;\n let maxTokens = config.maxTokens ?? DEFAULT_MAX_TOKENS;\n\n // OpenAI reasoning tokens share the output budget, so auto-increase for high/xhigh\n if (\n !userSetMaxTokens &&\n provider === \"openai\" &&\n (config.reasoningEffort === \"high\" || config.reasoningEffort === \"xhigh\")\n ) {\n maxTokens = OPENAI_REASONING_MAX_TOKENS;\n if (debug) {\n process.stderr.write(\n `[visual-ai-assertions] Auto-increased maxTokens from ${DEFAULT_MAX_TOKENS} to ${OPENAI_REASONING_MAX_TOKENS} for OpenAI with reasoningEffort \"${config.reasoningEffort}\".\\n`,\n );\n }\n }\n\n return {\n provider,\n apiKey: config.apiKey,\n model,\n maxTokens,\n reasoningEffort: config.reasoningEffort,\n debug,\n debugPrompt,\n debugResponse,\n trackUsage:\n config.trackUsage ??\n parseBooleanEnv(\"VISUAL_AI_TRACK_USAGE\", process.env.VISUAL_AI_TRACK_USAGE) ??\n false,\n };\n}\n","import { Model, Provider } from \"../constants.js\";\nimport type { ProviderName } from \"../types.js\";\n\ninterface ModelPricing {\n inputPricePerToken: number;\n outputPricePerToken: number;\n}\n\nconst PER_MILLION = 1_000_000;\n\nconst PRICING_TABLE: Record<string, ModelPricing> = {\n [`${Provider.ANTHROPIC}:${Model.Anthropic.OPUS_4_6}`]: {\n inputPricePerToken: 5 / PER_MILLION,\n outputPricePerToken: 25 / PER_MILLION,\n },\n [`${Provider.ANTHROPIC}:${Model.Anthropic.SONNET_4_6}`]: {\n inputPricePerToken: 3 / PER_MILLION,\n outputPricePerToken: 15 / PER_MILLION,\n },\n [`${Provider.ANTHROPIC}:${Model.Anthropic.HAIKU_4_5}`]: {\n inputPricePerToken: 1 / PER_MILLION,\n outputPricePerToken: 5 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4}`]: {\n inputPricePerToken: 2.5 / PER_MILLION,\n outputPricePerToken: 15 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_PRO}`]: {\n inputPricePerToken: 30 / PER_MILLION,\n outputPricePerToken: 180 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_2}`]: {\n inputPricePerToken: 1.75 / PER_MILLION,\n outputPricePerToken: 14 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_MINI}`]: {\n inputPricePerToken: 0.75 / PER_MILLION,\n outputPricePerToken: 4.5 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_NANO}`]: {\n inputPricePerToken: 0.2 / PER_MILLION,\n outputPricePerToken: 1.25 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_MINI}`]: {\n inputPricePerToken: 0.25 / PER_MILLION,\n outputPricePerToken: 2 / PER_MILLION,\n },\n [`${Provider.GOOGLE}:${Model.Google.GEMINI_3_1_PRO_PREVIEW}`]: {\n inputPricePerToken: 2 / PER_MILLION,\n outputPricePerToken: 12 / PER_MILLION,\n },\n [`${Provider.GOOGLE}:${Model.Google.GEMINI_3_1_FLASH_LITE_PREVIEW}`]: {\n inputPricePerToken: 0.25 / PER_MILLION,\n outputPricePerToken: 1.5 / PER_MILLION,\n },\n [`${Provider.GOOGLE}:${Model.Google.GEMINI_3_FLASH_PREVIEW}`]: {\n inputPricePerToken: 0.5 / PER_MILLION,\n outputPricePerToken: 3 / PER_MILLION,\n },\n};\n\nexport function calculateCost(\n provider: ProviderName,\n model: string,\n inputTokens: number,\n outputTokens: number,\n): number | undefined {\n const key = `${provider}:${model}`;\n const pricing = PRICING_TABLE[key];\n if (!pricing) return undefined;\n\n return inputTokens * pricing.inputPricePerToken + outputTokens * pricing.outputPricePerToken;\n}\n","import { PROVIDER_DEFAULT_REASONING } from \"../constants.js\";\nimport { calculateCost } from \"./pricing.js\";\nimport type { ResolvedConfig } from \"./config.js\";\nimport type {\n ProviderDriver,\n RawProviderResponse,\n SendMessageOptions,\n} from \"../providers/types.js\";\nimport type { NormalizedImage, UsageInfo } from \"../types.js\";\nimport { VisualAIError, VisualAIResponseParseError, VisualAITruncationError } from \"../errors.js\";\n\nexport type DebugLogKind = \"prompt\" | \"response\" | \"error\";\n\nexport function debugLog(\n config: ResolvedConfig,\n label: string,\n data: string,\n kind: DebugLogKind = \"error\",\n): void {\n const enabled =\n kind === \"prompt\"\n ? config.debugPrompt\n : kind === \"response\"\n ? config.debugResponse\n : config.debug;\n\n if (enabled) {\n process.stderr.write(`[visual-ai-assertions] ${label}: ${data}\\n`);\n }\n}\n\nexport function usageLog(config: ResolvedConfig, method: string, usage: UsageInfo): void {\n if (!config.trackUsage) return;\n const costStr =\n usage.estimatedCost !== undefined ? `$${usage.estimatedCost.toFixed(6)}` : \"unknown\";\n const reasoningStr = config.reasoningEffort\n ? `reasoning: ${config.reasoningEffort}`\n : `reasoning: ${PROVIDER_DEFAULT_REASONING[config.provider]} (provider default)`;\n const reasoningTokenStr =\n usage.reasoningTokens !== undefined ? ` (${usage.reasoningTokens} reasoning)` : \"\";\n process.stderr.write(\n `[visual-ai-assertions] ${method} usage: ${usage.inputTokens} input + ${usage.outputTokens} output${reasoningTokenStr} tokens (${costStr}) in ${usage.durationSeconds?.toFixed(3) ?? \"0.000\"}s [${config.model}, ${reasoningStr}]\\n`,\n );\n}\n\nexport function processUsage(\n method: string,\n rawUsage: RawProviderResponse[\"usage\"],\n durationSeconds: number,\n config: ResolvedConfig,\n): UsageInfo {\n const inputTokens = rawUsage?.inputTokens ?? 0;\n const outputTokens = rawUsage?.outputTokens ?? 0;\n const usage: UsageInfo = {\n inputTokens,\n outputTokens,\n ...(rawUsage?.reasoningTokens !== undefined && { reasoningTokens: rawUsage.reasoningTokens }),\n estimatedCost: calculateCost(config.provider, config.model, inputTokens, outputTokens),\n durationSeconds,\n };\n usageLog(config, method, usage);\n return usage;\n}\n\nconst MAX_RAW_RESPONSE_PREVIEW = 500;\n\nexport function formatError(error: unknown): string {\n if (error instanceof VisualAITruncationError) {\n const preview =\n error.partialResponse.length > MAX_RAW_RESPONSE_PREVIEW\n ? error.partialResponse.slice(0, MAX_RAW_RESPONSE_PREVIEW) + \"...\"\n : error.partialResponse;\n return `${error.name} (${error.code}): ${error.message}. Partial response: ${preview}`;\n }\n if (error instanceof VisualAIResponseParseError) {\n const truncated =\n error.rawResponse.length > MAX_RAW_RESPONSE_PREVIEW\n ? error.rawResponse.slice(0, MAX_RAW_RESPONSE_PREVIEW) + \"...\"\n : error.rawResponse;\n return `${error.name} (${error.code}): ${error.message}. Raw (truncated): ${truncated}`;\n }\n if (error instanceof VisualAIError) {\n return `${error.name} (${error.code}): ${error.message}`;\n }\n if (error instanceof Error) {\n return `${error.name}: ${error.message}`;\n }\n return String(error);\n}\n\nexport async function withErrorDebug<T>(\n config: ResolvedConfig,\n method: string,\n fn: () => Promise<T>,\n): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n debugLog(config, `${method} error`, formatError(error), \"error\");\n throw error;\n }\n}\n\nexport async function timedSendMessage(\n driver: ProviderDriver,\n images: NormalizedImage[],\n prompt: string,\n options?: SendMessageOptions,\n): Promise<RawProviderResponse & { durationSeconds: number }> {\n const start = performance.now();\n const response = await driver.sendMessage(images, prompt, options);\n const durationSeconds = (performance.now() - start) / 1000;\n return { ...response, durationSeconds };\n}\n","import { Model } from \"../constants.js\";\nimport sharp from \"sharp\";\nimport { VisualAIConfigError } from \"../errors.js\";\nimport type { DiffImageResult, NormalizedImage } from \"../types.js\";\nimport { buildAiDiffPrompt } from \"./prompt.js\";\n\ninterface ImageGenerationDriver {\n generateImage?: (\n images: NormalizedImage[],\n prompt: string,\n options?: { model?: string; promptKind?: \"ai-diff\" },\n ) => Promise<{\n imageData: Buffer;\n mimeType: string;\n }>;\n}\n\nexport async function generateAiDiff(\n imgA: NormalizedImage,\n imgB: NormalizedImage,\n model: string,\n driver: ImageGenerationDriver,\n): Promise<DiffImageResult> {\n if (!driver.generateImage) {\n throw new VisualAIConfigError(\n \"AI-generated diff images require a provider that supports image generation. Currently only the Google (Gemini) provider supports this.\",\n );\n }\n\n if (model !== Model.Google.GEMINI_3_FLASH_PREVIEW) {\n throw new VisualAIConfigError(\n \"Annotated diff images are only supported when visualAI is configured with the Google model gemini-3-flash-preview.\",\n );\n }\n\n const response = await driver.generateImage([imgA, imgB], buildAiDiffPrompt(), {\n model,\n promptKind: \"ai-diff\",\n });\n\n const img = sharp(response.imageData);\n const meta = await img.metadata();\n const pngData = await img.png().toBuffer();\n\n return {\n data: pngData,\n width: meta.width ?? 0,\n height: meta.height ?? 0,\n mimeType: \"image/png\",\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\nimport sharp from \"sharp\";\nimport { VisualAIImageError } from \"../errors.js\";\nimport type { NormalizedImage, SupportedMimeType } from \"../types.js\";\n\nconst SUPPORTED_FORMATS: ReadonlySet<SupportedMimeType> = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/webp\",\n \"image/gif\",\n]);\n\nconst EXTENSION_TO_MIME: Record<string, SupportedMimeType> = {\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".webp\": \"image/webp\",\n \".gif\": \"image/gif\",\n};\n\nconst MAX_DIMENSION = 1568;\nconst URL_FETCH_TIMEOUT_MS = 10_000;\n\nfunction isSupportedMimeType(value: string): value is SupportedMimeType {\n return SUPPORTED_FORMATS.has(value as SupportedMimeType);\n}\n\nfunction getMimeFromExtension(filePath: string): SupportedMimeType | undefined {\n const ext = extname(filePath).toLowerCase();\n return EXTENSION_TO_MIME[ext];\n}\n\nfunction isFilePath(input: string): boolean {\n return (\n input.startsWith(\"/\") ||\n input.startsWith(\"./\") ||\n input.startsWith(\"../\") ||\n input.includes(\"\\\\\")\n );\n}\n\nfunction isUrl(input: string): boolean {\n return input.startsWith(\"http://\") || input.startsWith(\"https://\");\n}\n\nfunction isBase64Image(input: string): boolean {\n return (\n input.startsWith(\"iVBOR\") || // PNG (0x89 0x50 0x4E 0x47)\n input.startsWith(\"/9j/\") || // JPEG (0xFF 0xD8 0xFF)\n input.startsWith(\"R0lGOD\") || // GIF (0x47 0x49 0x46)\n input.startsWith(\"UklGR\") // WebP (0x52 0x49 0x46 0x46)\n );\n}\n\nfunction detectMimeType(data: Buffer): SupportedMimeType {\n // Check magic bytes\n if (data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff) {\n return \"image/jpeg\";\n }\n if (data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4e && data[3] === 0x47) {\n return \"image/png\";\n }\n if (\n data[0] === 0x52 &&\n data[1] === 0x49 &&\n data[2] === 0x46 &&\n data[3] === 0x46 &&\n data[8] === 0x57 &&\n data[9] === 0x45 &&\n data[10] === 0x42 &&\n data[11] === 0x50\n ) {\n return \"image/webp\";\n }\n if (data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46) {\n return \"image/gif\";\n }\n\n throw new VisualAIImageError(\"Unable to detect image format from file content\");\n}\n\nasync function resizeIfNeeded(data: Buffer, mimeType: SupportedMimeType): Promise<Buffer> {\n if (mimeType === \"image/gif\") {\n return data;\n }\n\n // Fast path for PNG: read dimensions directly from header bytes\n if (mimeType === \"image/png\" && data.length >= 24) {\n const width = data.readUInt32BE(16);\n const height = data.readUInt32BE(20);\n if (width <= MAX_DIMENSION && height <= MAX_DIMENSION) {\n return data;\n }\n }\n\n // Fall back to sharp for other formats or when resizing is needed\n const pipeline = sharp(data);\n const metadata = await pipeline.metadata();\n const width = metadata.width ?? 0;\n const height = metadata.height ?? 0;\n\n if (width <= MAX_DIMENSION && height <= MAX_DIMENSION) {\n return data;\n }\n\n return pipeline\n .resize({\n width: MAX_DIMENSION,\n height: MAX_DIMENSION,\n fit: \"inside\",\n withoutEnlargement: true,\n })\n .toBuffer();\n}\n\nasync function loadFromFilePath(\n filePath: string,\n): Promise<{ data: Buffer; mimeType: SupportedMimeType }> {\n let fileData: Buffer;\n try {\n fileData = await readFile(filePath);\n } catch (err) {\n throw new VisualAIImageError(\n `Failed to read image file: ${filePath} — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const mimeType = getMimeFromExtension(filePath) ?? detectMimeType(fileData);\n return { data: fileData, mimeType };\n}\n\nasync function loadFromUrl(url: string): Promise<{ data: Buffer; mimeType: SupportedMimeType }> {\n let response: Response;\n try {\n response = await fetch(url, {\n signal: AbortSignal.timeout(URL_FETCH_TIMEOUT_MS),\n });\n } catch (err) {\n throw new VisualAIImageError(\n `Failed to fetch image from URL: ${url} — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (!response.ok) {\n throw new VisualAIImageError(\n `Failed to fetch image from URL: ${url} — HTTP ${response.status}`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const data = Buffer.from(arrayBuffer);\n const contentType = response.headers.get(\"content-type\")?.split(\";\")[0]?.trim() ?? null;\n const mimeType =\n contentType && isSupportedMimeType(contentType) ? contentType : detectMimeType(data);\n\n return { data, mimeType };\n}\n\nfunction loadFromBase64(input: string): { data: Buffer; mimeType: SupportedMimeType } {\n // Handle data URLs: data:image/png;base64,iVBOR...\n let base64Data = input;\n let mimeType: SupportedMimeType | undefined;\n\n if (input.startsWith(\"data:\")) {\n const match = /^data:(image\\/[^;]+);base64,(.+)$/.exec(input);\n if (!match?.[1] || !match[2]) {\n throw new VisualAIImageError(\"Invalid data URL format\");\n }\n if (!isSupportedMimeType(match[1])) {\n throw new VisualAIImageError(`Unsupported image format: ${match[1]}`);\n }\n mimeType = match[1];\n base64Data = match[2];\n }\n\n // Validate base64 characters\n if (!/^[A-Za-z0-9+/\\n\\r]+=*$/.test(base64Data)) {\n throw new VisualAIImageError(\"Invalid base64 string\");\n }\n\n const data = Buffer.from(base64Data, \"base64\");\n\n if (data.length === 0) {\n throw new VisualAIImageError(\"Empty image data after base64 decode\");\n }\n\n return { data, mimeType: mimeType ?? detectMimeType(data) };\n}\n\nexport async function normalizeImage(\n input: Buffer | Uint8Array | string,\n): Promise<NormalizedImage> {\n let data: Buffer;\n let mimeType: SupportedMimeType;\n\n if (Buffer.isBuffer(input)) {\n mimeType = detectMimeType(input);\n data = input;\n } else if (input instanceof Uint8Array) {\n const buf = Buffer.from(input);\n mimeType = detectMimeType(buf);\n data = buf;\n } else if (typeof input === \"string\") {\n if (isUrl(input)) {\n ({ data, mimeType } = await loadFromUrl(input));\n } else if (input.startsWith(\"data:\")) {\n ({ data, mimeType } = loadFromBase64(input));\n } else if (isBase64Image(input)) {\n ({ data, mimeType } = loadFromBase64(input));\n } else if (isFilePath(input)) {\n ({ data, mimeType } = await loadFromFilePath(input));\n } else {\n throw new VisualAIImageError(\n `Unrecognized image input: \"${input.slice(0, 80)}\". ` +\n `Expected a file path, URL, data URL, or base64-encoded image string.`,\n );\n }\n } else {\n throw new VisualAIImageError(\n \"Invalid image input: expected Buffer, Uint8Array, file path, URL, or base64 string\",\n );\n }\n\n data = await resizeIfNeeded(data, mimeType);\n\n let cachedBase64: string | undefined;\n return {\n data,\n mimeType,\n get base64(): string {\n if (cachedBase64 === undefined) {\n cachedBase64 = data.toString(\"base64\");\n }\n return cachedBase64;\n },\n };\n}\n","import { z } from \"zod\";\nimport type {\n AccessibilityCheckName,\n ContentCheckName,\n LayoutCheckName,\n ReasoningEffortLevel,\n} from \"./constants.js\";\n\n// --- Issue types ---\n\n/** Zod schema for issue severity levels returned by checks and questions. */\nexport const IssuePrioritySchema = z.enum([\"critical\", \"major\", \"minor\"]);\n/** Severity level for a detected visual issue. */\nexport type IssuePriority = z.infer<typeof IssuePrioritySchema>;\n\n/** Zod schema for the categories assigned to detected visual issues. */\nexport const IssueCategorySchema = z.enum([\n \"accessibility\",\n \"missing-element\",\n \"layout\",\n \"content\",\n \"styling\",\n \"functionality\",\n \"performance\",\n \"other\",\n]);\n/** Category assigned to a detected visual issue. */\nexport type IssueCategory = z.infer<typeof IssueCategorySchema>;\n\n/** Zod schema for a structured issue reported by the model. */\nexport const IssueSchema = z.object({\n priority: IssuePrioritySchema,\n category: IssueCategorySchema,\n description: z.string(),\n suggestion: z.string(),\n});\n/** Structured issue reported by a visual check or question. */\nexport type Issue = z.infer<typeof IssueSchema>;\n\n// --- Per-statement result (for check) ---\n\n/** Zod schema for model confidence labels on statement-level results. */\nexport const ConfidenceSchema = z.enum([\"high\", \"medium\", \"low\"]);\n/** Confidence level attached to a statement result. */\nexport type Confidence = z.infer<typeof ConfidenceSchema>;\n\n/** Zod schema for an individual statement evaluation within `check()`. */\nexport const StatementResultSchema = z.object({\n statement: z.string(),\n pass: z.boolean(),\n reasoning: z.string(),\n confidence: ConfidenceSchema.optional(),\n});\n/** Outcome of a single statement evaluated by `check()`. */\nexport type StatementResult = z.infer<typeof StatementResultSchema>;\n\n// --- Usage info ---\n\n/** Zod schema for token and latency metadata attached to API calls. */\nexport const UsageInfoSchema = z.object({\n inputTokens: z.number(),\n outputTokens: z.number(),\n /** Reasoning/thinking tokens consumed by the model (informational, typically included within outputTokens). */\n reasoningTokens: z.number().optional(),\n estimatedCost: z.number().optional(),\n durationSeconds: z.number().nonnegative().optional(),\n});\n/** Token usage and optional cost/latency metadata for a provider call. */\nexport type UsageInfo = z.infer<typeof UsageInfoSchema>;\n\n// --- Base result (shared fields) ---\n\nconst BaseResultSchema = z.object({\n pass: z.boolean(),\n reasoning: z.string(),\n usage: UsageInfoSchema.optional(),\n});\n\n// --- check() / template result ---\n\n/** Zod schema for results returned by `check()` and template helpers. */\nexport const CheckResultSchema = BaseResultSchema.extend({\n issues: z.array(IssueSchema),\n statements: z.array(StatementResultSchema),\n});\n/** Result returned by `check()` and the template convenience methods. */\nexport type CheckResult = z.infer<typeof CheckResultSchema>;\n\n// --- compare() result ---\n\n/** Zod schema for an individual visual change reported by `compare()`. */\nexport const ChangeEntrySchema = z.object({\n description: z.string(),\n severity: IssuePrioritySchema,\n});\n/** Single visual change reported by `compare()`. */\nexport type ChangeEntry = z.infer<typeof ChangeEntrySchema>;\n\n/** Zod schema for the parsed model response returned by `compare()`. */\nexport const CompareResultSchema = BaseResultSchema.extend({\n changes: z.array(ChangeEntrySchema).max(50),\n});\n// diffImage is appended client-side after the AI response is parsed,\n// so it intentionally does not appear in CompareResultSchema.\n/** Result returned by `compare()`, optionally including an AI-generated diff image. */\nexport type CompareResult = z.infer<typeof CompareResultSchema> & {\n diffImage?: DiffImageResult;\n};\n\n// --- ask() result ---\n\n/** Zod schema for results returned by `ask()`. */\nexport const AskResultSchema = z.object({\n summary: z.string(),\n issues: z.array(IssueSchema),\n usage: UsageInfoSchema.optional(),\n});\n/** Result returned by `ask()`. */\nexport type AskResult = z.infer<typeof AskResultSchema>;\n\n// --- Image input ---\n\n/** Supported input shapes for image arguments accepted by the client. */\nexport type ImageInput = Buffer | Uint8Array | string;\n\n/** Supported image MIME types accepted by all providers. */\nexport type SupportedMimeType = \"image/jpeg\" | \"image/png\" | \"image/webp\" | \"image/gif\";\n\n// --- Provider names ---\n\n/** Supported provider identifiers. */\nexport type ProviderName = \"anthropic\" | \"openai\" | \"google\";\n\n// --- VisualAI config ---\n\n/**\n * Configuration for creating a visual AI client.\n *\n * @example\n * ```ts\n * const client = visualAI({\n * model: \"gpt-5-mini\",\n * apiKey: process.env.OPENAI_API_KEY,\n * });\n * ```\n */\nexport interface VisualAIConfig {\n apiKey?: string;\n model?: string;\n /** Enable error diagnostic logging to stderr. Does not enable prompt/response logging — use `debugPrompt` and `debugResponse` for that. */\n debug?: boolean;\n /** Log prompts to stderr. */\n debugPrompt?: boolean;\n /** Log responses to stderr. */\n debugResponse?: boolean;\n maxTokens?: number;\n reasoningEffort?: ReasoningEffortLevel;\n trackUsage?: boolean;\n}\n\n// --- Template option types ---\n\n/** Optional instructions for `check()`. */\nexport interface CheckOptions {\n instructions?: readonly string[];\n}\n\n/** Optional instructions for `ask()`. */\nexport interface AskOptions {\n instructions?: readonly string[];\n}\n\n// --- Diff image types ---\n\n/** Metadata and binary content for an AI-generated diff image. */\nexport interface DiffImageResult {\n data: Buffer;\n width: number;\n height: number;\n mimeType: \"image/png\";\n}\n\n/** Optional prompt, instructions, and diff configuration for `compare()`. */\nexport interface CompareOptions {\n prompt?: string;\n instructions?: readonly string[];\n diffImage?: boolean;\n}\n\n/** Optional instructions for `elementsVisible()` and `elementsHidden()`. */\nexport interface ElementsVisibilityOptions {\n instructions?: readonly string[];\n}\n\n/** Options for the built-in accessibility template. */\nexport interface AccessibilityOptions {\n checks?: AccessibilityCheckName[];\n instructions?: readonly string[];\n}\n\n/** Options for the built-in layout template. */\nexport interface LayoutOptions {\n checks?: LayoutCheckName[];\n instructions?: readonly string[];\n}\n\n/** Options for the built-in page-load template. */\nexport interface PageLoadOptions {\n expectLoaded?: boolean;\n instructions?: readonly string[];\n}\n\n/** Options for the built-in content template. */\nexport interface ContentOptions {\n checks?: ContentCheckName[];\n instructions?: readonly string[];\n}\n\n// --- Normalized image (internal) ---\n\n/** Internal normalized image representation passed to provider drivers. */\nexport interface NormalizedImage {\n readonly data: Buffer;\n readonly mimeType: SupportedMimeType;\n readonly base64: string;\n}\n","import { z } from \"zod\";\nimport { VisualAIResponseParseError } from \"../errors.js\";\nimport { AskResultSchema, CheckResultSchema, CompareResultSchema } from \"../types.js\";\nimport type { AskResult, CheckResult, CompareResult } from \"../types.js\";\n\nfunction stripCodeFences(text: string): string {\n const trimmed = text.trim();\n const match = /^```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```$/s.exec(trimmed);\n return match?.[1] ?? trimmed;\n}\n\nexport const CheckResponseSchema = CheckResultSchema.omit({ usage: true });\nexport const AskResponseSchema = AskResultSchema.omit({ usage: true });\nexport const CompareResponseSchema = CompareResultSchema.omit({ usage: true });\n\nfunction parseResponse<T>(raw: string, schema: z.ZodType<T>): T {\n let parsed: unknown;\n try {\n parsed = JSON.parse(stripCodeFences(raw));\n } catch {\n throw new VisualAIResponseParseError(\n `Failed to parse AI response as JSON: ${raw.slice(0, 200)}`,\n raw,\n );\n }\n\n const result = schema.safeParse(parsed);\n if (!result.success) {\n throw new VisualAIResponseParseError(\n `AI response does not match expected schema: ${result.error.message}`,\n raw,\n );\n }\n\n return result.data;\n}\n\nfunction reconcileCheckResult(result: Omit<CheckResult, \"usage\">): Omit<CheckResult, \"usage\"> {\n if (result.statements.length === 0) {\n return result;\n }\n\n const passCount = result.statements.filter((s) => s.pass).length;\n const total = result.statements.length;\n const computedPass = passCount === total;\n const countPrefix = `${passCount} of ${total} checks passed`;\n const reasoning = `${countPrefix}. ${result.reasoning}`;\n\n return {\n ...result,\n pass: computedPass,\n reasoning,\n };\n}\n\nexport function parseCheckResponse(raw: string): Omit<CheckResult, \"usage\"> {\n const result = parseResponse(raw, CheckResponseSchema);\n return reconcileCheckResult(result);\n}\n\nexport function parseAskResponse(raw: string): Omit<AskResult, \"usage\"> {\n return parseResponse(raw, AskResponseSchema);\n}\n\nexport function parseCompareResponse(raw: string): Omit<CompareResult, \"usage\"> {\n return parseResponse(raw, CompareResponseSchema);\n}\n","import { Model } from \"../constants.js\";\nimport { VisualAIConfigError } from \"../errors.js\";\nimport {\n buildAccessibilityPrompt,\n buildContentPrompt,\n buildElementsVisibilityPrompt,\n buildLayoutPrompt,\n buildPageLoadPrompt,\n} from \"../templates/index.js\";\nimport type {\n AccessibilityOptions,\n AskOptions,\n AskResult,\n CheckOptions,\n CheckResult,\n CompareOptions,\n CompareResult,\n ContentOptions,\n ElementsVisibilityOptions,\n ImageInput,\n LayoutOptions,\n PageLoadOptions,\n ProviderName,\n VisualAIConfig,\n} from \"../types.js\";\nimport { AnthropicDriver } from \"../providers/anthropic.js\";\nimport { GoogleDriver } from \"../providers/google.js\";\nimport { OpenAIDriver } from \"../providers/openai.js\";\nimport type { ProviderConfig, ProviderDriver, SendMessageOptions } from \"../providers/types.js\";\nimport { resolveConfig } from \"./config.js\";\nimport { debugLog, processUsage, timedSendMessage, withErrorDebug } from \"./debug.js\";\nimport { generateAiDiff } from \"./diff.js\";\nimport { normalizeImage } from \"./image.js\";\nimport { buildAskPrompt, buildCheckPrompt, buildComparePrompt } from \"./prompt.js\";\nimport {\n AskResponseSchema,\n CheckResponseSchema,\n CompareResponseSchema,\n parseAskResponse,\n parseCheckResponse,\n parseCompareResponse,\n} from \"./response.js\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport type { z } from \"zod\";\n\nfunction toSchemaOptions(schema: z.ZodType): SendMessageOptions {\n return {\n responseSchema: zodToJsonSchema(schema, { target: \"openAi\" }) as Record<string, unknown>,\n };\n}\n\n/**\n * High-level client for running visual checks against screenshots or other images.\n *\n * @example\n * ```ts\n * const client = visualAI({ model: \"gpt-5-mini\" });\n * const result = await client.check(\"./tests/fixtures/small.png\", \"The button is visible\");\n * ```\n */\nexport interface VisualAIClient {\n /**\n * Verifies one or more statements against a single image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param statements One or more statements to validate against the image.\n * @param options Optional additional instructions appended to the prompt.\n * @returns A structured result describing pass/fail, issues, and statement reasoning.\n * @throws {VisualAIConfigError} When no statements are provided.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * const result = await client.check(screenshot, [\n * \"The primary CTA is visible\",\n * \"There is no error banner\",\n * ]);\n * ```\n */\n check(\n image: ImageInput,\n statements: string | string[],\n options?: CheckOptions,\n ): Promise<CheckResult>;\n /**\n * Asks an open-ended question about an image and returns a structured summary.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param prompt Prompt describing what to inspect in the image.\n * @param options Optional additional instructions appended to the prompt.\n * @returns A summary with any detected issues.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * const result = await client.ask(screenshot, \"What looks visually broken on this page?\");\n * ```\n */\n ask(image: ImageInput, prompt: string, options?: AskOptions): Promise<AskResult>;\n /**\n * Compares two images and reports meaningful visual differences.\n *\n * @param imageA Baseline image source.\n * @param imageB Candidate image source.\n * @param options Optional comparison prompt, instructions, and diff-image settings.\n * `gemini-3-flash-preview` generates an annotated diff image by default;\n * pass `{ diffImage: false }` to opt out.\n * @returns A structured comparison result with optional diff image metadata.\n * @throws {VisualAIImageError} When either image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * const result = await client.compare(beforeScreenshot, afterScreenshot, {\n * diffImage: true,\n * });\n * ```\n */\n compare(imageA: ImageInput, imageB: ImageInput, options?: CompareOptions): Promise<CompareResult>;\n /**\n * Checks that the listed elements are visible in an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param elements Element descriptions that should be present and visible.\n * @param options Optional additional instructions appended to the prompt.\n * @returns A structured pass/fail result for the requested elements.\n * @throws {VisualAIConfigError} When `elements` is empty.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.elementsVisible(screenshot, [\"Save button\", \"Profile avatar\"]);\n * ```\n */\n elementsVisible(\n image: ImageInput,\n elements: string[],\n options?: ElementsVisibilityOptions,\n ): Promise<CheckResult>;\n /**\n * Checks that the listed elements are not visible in an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param elements Element descriptions that should be absent or hidden.\n * @param options Optional additional instructions appended to the prompt.\n * @returns A structured pass/fail result for the requested elements.\n * @throws {VisualAIConfigError} When `elements` is empty.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.elementsHidden(screenshot, [\"Cookie banner\"]);\n * ```\n */\n elementsHidden(\n image: ImageInput,\n elements: string[],\n options?: ElementsVisibilityOptions,\n ): Promise<CheckResult>;\n /**\n * Runs the built-in accessibility template against an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param options Optional checks and extra instructions for the accessibility prompt.\n * @returns A structured accessibility-focused check result.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.accessibility(screenshot, { checks: [\"contrast\"] });\n * ```\n */\n accessibility(image: ImageInput, options?: AccessibilityOptions): Promise<CheckResult>;\n /**\n * Runs the built-in layout template against an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param options Optional checks and extra instructions for the layout prompt.\n * @returns A structured layout-focused check result.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.layout(screenshot, { checks: [\"overflow\", \"alignment\"] });\n * ```\n */\n layout(image: ImageInput, options?: LayoutOptions): Promise<CheckResult>;\n /**\n * Runs the built-in page-load template against an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param options Optional page-load expectations and extra instructions.\n * @returns A structured page-load check result.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.pageLoad(screenshot, { expectLoaded: true });\n * ```\n */\n pageLoad(image: ImageInput, options?: PageLoadOptions): Promise<CheckResult>;\n /**\n * Runs the built-in content template against an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param options Optional content checks and extra instructions.\n * @returns A structured content-focused check result.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.content(screenshot, { checks: [\"placeholder-text\"] });\n * ```\n */\n content(image: ImageInput, options?: ContentOptions): Promise<CheckResult>;\n}\n\ntype ProviderFactory = (config: ProviderConfig) => ProviderDriver;\n\nconst PROVIDER_REGISTRY = {\n anthropic: (config) => new AnthropicDriver(config),\n openai: (config) => new OpenAIDriver(config),\n google: (config) => new GoogleDriver(config),\n} as const satisfies Record<ProviderName, ProviderFactory>;\n\nfunction createDriver(provider: ProviderName, config: ProviderConfig): ProviderDriver {\n return PROVIDER_REGISTRY[provider](config);\n}\n\nconst checkSchemaOptions = toSchemaOptions(CheckResponseSchema);\nconst askSchemaOptions = toSchemaOptions(AskResponseSchema);\nconst compareSchemaOptions = toSchemaOptions(CompareResponseSchema);\n\n/**\n * Creates a configured visual AI client.\n *\n * @param config Model selection and runtime options for subsequent requests.\n * @returns A `VisualAIClient` instance with check, compare, ask, and template helpers.\n * @throws {VisualAIConfigError} When the provider or model configuration is invalid.\n * @throws {VisualAIAuthError} When required API credentials are missing.\n * @example\n * ```ts\n * import { expect, test } from \"@playwright/test\";\n * import { visualAI } from \"visual-ai-assertions\";\n *\n * test(\"hero loads correctly\", async ({ page }) => {\n * const client = visualAI({\n * model: \"gpt-5-mini\",\n * apiKey: process.env.OPENAI_API_KEY,\n * });\n *\n * await page.goto(\"https://example.com\");\n * const screenshot = await page.screenshot();\n * const result = await client.check(screenshot, [\n * \"The hero heading is visible\",\n * \"There is no loading spinner\",\n * ]);\n *\n * expect(result.pass).toBe(true);\n * });\n * ```\n */\nexport function visualAI(config: VisualAIConfig = {}): VisualAIClient {\n const resolvedConfig = resolveConfig(config);\n const driverConfig: ProviderConfig = {\n apiKey: resolvedConfig.apiKey,\n model: resolvedConfig.model,\n maxTokens: resolvedConfig.maxTokens,\n reasoningEffort: resolvedConfig.reasoningEffort,\n };\n const driver = createDriver(resolvedConfig.provider, driverConfig);\n\n async function checkElementsVisibility(\n image: ImageInput,\n elements: string[],\n visible: boolean,\n options?: ElementsVisibilityOptions,\n ): Promise<CheckResult> {\n const methodName = visible ? \"elementsVisible\" : \"elementsHidden\";\n if (elements.length === 0) {\n throw new VisualAIConfigError(`At least one element is required for ${methodName}()`);\n }\n\n return withErrorDebug(resolvedConfig, methodName, async () => {\n const img = await normalizeImage(image);\n const prompt = buildElementsVisibilityPrompt(elements, visible, options);\n debugLog(resolvedConfig, `${methodName} prompt`, prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, `${methodName} response`, response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(methodName, response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n }\n\n return {\n async check(image, statements, options) {\n const stmts = Array.isArray(statements) ? statements : [statements];\n if (stmts.length === 0) {\n throw new VisualAIConfigError(\"At least one statement is required for check()\");\n }\n\n return withErrorDebug(resolvedConfig, \"check\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildCheckPrompt(stmts, { instructions: options?.instructions });\n debugLog(resolvedConfig, \"check prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"check response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"check\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n async ask(image, userPrompt, options) {\n return withErrorDebug(resolvedConfig, \"ask\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildAskPrompt(userPrompt, { instructions: options?.instructions });\n debugLog(resolvedConfig, \"ask prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, askSchemaOptions);\n debugLog(resolvedConfig, \"ask response\", response.text, \"response\");\n\n const result = parseAskResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"ask\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n async compare(imageA, imageB, options) {\n return withErrorDebug(resolvedConfig, \"compare\", async () => {\n const [imgA, imgB] = await Promise.all([normalizeImage(imageA), normalizeImage(imageB)]);\n const prompt = buildComparePrompt({\n userPrompt: options?.prompt,\n instructions: options?.instructions,\n });\n debugLog(resolvedConfig, \"compare prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [imgA, imgB], prompt, compareSchemaOptions);\n debugLog(resolvedConfig, \"compare response\", response.text, \"response\");\n\n const supportsAnnotatedDiff =\n resolvedConfig.provider === \"google\" &&\n resolvedConfig.model === Model.Google.GEMINI_3_FLASH_PREVIEW;\n const effectiveDiffImage = options?.diffImage ?? (supportsAnnotatedDiff ? true : false);\n\n let diffImage;\n if (effectiveDiffImage) {\n try {\n diffImage = await generateAiDiff(imgA, imgB, resolvedConfig.model, driver);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `[visual-ai-assertions] warning: diff generation failed: ${msg}\\n`,\n );\n }\n }\n\n const result = parseCompareResponse(response.text);\n return {\n ...result,\n ...(diffImage ? { diffImage } : {}),\n usage: processUsage(\"compare\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n elementsVisible(image, elements, options) {\n return checkElementsVisibility(image, elements, true, options);\n },\n\n elementsHidden(image, elements, options) {\n return checkElementsVisibility(image, elements, false, options);\n },\n\n async accessibility(image, options) {\n return withErrorDebug(resolvedConfig, \"accessibility\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildAccessibilityPrompt(options);\n debugLog(resolvedConfig, \"accessibility prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"accessibility response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\n \"accessibility\",\n response.usage,\n response.durationSeconds,\n resolvedConfig,\n ),\n };\n });\n },\n\n async layout(image, options) {\n return withErrorDebug(resolvedConfig, \"layout\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildLayoutPrompt(options);\n debugLog(resolvedConfig, \"layout prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"layout response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"layout\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n async pageLoad(image, options) {\n return withErrorDebug(resolvedConfig, \"pageLoad\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildPageLoadPrompt(options);\n debugLog(resolvedConfig, \"pageLoad prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"pageLoad response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"pageLoad\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n async content(image, options) {\n return withErrorDebug(resolvedConfig, \"content\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildContentPrompt(options);\n debugLog(resolvedConfig, \"content prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"content response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"content\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n };\n}\n","import type { CheckResult, CompareResult } from \"./types.js\";\nimport { VisualAIAssertionError } from \"./errors.js\";\n\n/**\n * Formats a check result into a readable multiline string for logs or test failures.\n *\n * @param result Structured result returned by `check()` or a template helper.\n * @param label Optional label appended to the output header.\n * @returns A human-readable summary of the check result.\n * @example\n * ```ts\n * console.log(formatCheckResult(result, \"Checkout page\"));\n * ```\n */\nexport function formatCheckResult(result: CheckResult, label?: string): string {\n if (result.pass) {\n const header = label ? `Visual AI Check Passed (${label})` : \"Visual AI Check Passed\";\n return `${header}\\n${\"=\".repeat(header.length)}\\n${result.reasoning}`;\n }\n\n const header = label ? `Visual AI Check Failed (${label})` : \"Visual AI Check Failed\";\n const lines: string[] = [header, \"=\".repeat(header.length), result.reasoning];\n\n if (result.statements.length > 0) {\n lines.push(\"\", \"Statements:\");\n for (const s of result.statements) {\n const status = s.pass ? \"PASS\" : \"FAIL\";\n const confidence = s.confidence ? ` (${s.confidence})` : \"\";\n lines.push(` ${status} \"${s.statement}\"`);\n lines.push(` ${s.reasoning}${confidence}`);\n }\n }\n\n if (result.issues.length > 0) {\n lines.push(\"\", \"Issues:\");\n for (const issue of result.issues) {\n lines.push(` [${issue.priority}/${issue.category}] ${issue.description}`);\n lines.push(` → ${issue.suggestion}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Formats a compare result into a readable multiline string for logs or test failures.\n *\n * @param result Structured result returned by `compare()`.\n * @param label Optional label appended to the output header.\n * @returns A human-readable summary of the compare result.\n * @example\n * ```ts\n * console.log(formatCompareResult(result, \"Before/after deploy\"));\n * ```\n */\nexport function formatCompareResult(result: CompareResult, label?: string): string {\n const status = result.pass ? \"Passed\" : \"Failed\";\n const header = label ? `Visual AI Compare ${status} (${label})` : `Visual AI Compare ${status}`;\n const lines: string[] = [header, \"=\".repeat(header.length), result.reasoning];\n\n if (result.changes.length > 0) {\n lines.push(\"\", \"Changes:\");\n for (const change of result.changes) {\n lines.push(` [${change.severity}] ${change.description}`);\n }\n }\n\n if (result.diffImage) {\n const { width, height } = result.diffImage;\n lines.push(\"\", `Diff image: ${width}x${height} (AI-generated)`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Throws a `VisualAIAssertionError` when a check result did not pass.\n *\n * @param result Structured result returned by `check()` or a template helper.\n * @param label Optional label appended to the assertion message.\n * @returns Nothing when the result passes.\n * @throws {VisualAIAssertionError} When `result.pass` is `false`.\n * @example\n * ```ts\n * assertVisualResult(result, \"Homepage\");\n * ```\n */\nexport function assertVisualResult(result: CheckResult, label?: string): void {\n if (!result.pass) {\n throw new VisualAIAssertionError(formatCheckResult(result, label), result);\n }\n}\n\n/**\n * Throws a `VisualAIAssertionError` when a compare result did not pass.\n *\n * @param result Structured result returned by `compare()`.\n * @param label Optional label appended to the assertion message.\n * @returns Nothing when the result passes.\n * @throws {VisualAIAssertionError} When `result.pass` is `false`.\n * @example\n * ```ts\n * assertVisualCompareResult(result, \"Login flow\");\n * ```\n */\nexport function assertVisualCompareResult(result: CompareResult, label?: string): void {\n if (!result.pass) {\n throw new VisualAIAssertionError(formatCompareResult(result, label), result);\n }\n}\n"],"mappings":"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","names":["ALL_CHECKS","CHECK_STATEMENTS","ALL_CHECKS","CHECK_STATEMENTS","sharp","import_sharp","width","height","sharp","header"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/errors.ts","../src/core/prompt.ts","../src/templates/elements-visibility.ts","../src/templates/accessibility.ts","../src/templates/layout.ts","../src/templates/page-load.ts","../src/templates/content.ts","../src/providers/error-mapper.ts","../src/providers/anthropic.ts","../src/providers/google.ts","../src/providers/openai.ts","../src/core/config.ts","../src/core/pricing.ts","../src/core/debug.ts","../src/core/diff.ts","../src/core/image.ts","../src/types.ts","../src/core/response.ts","../src/core/client.ts","../src/format.ts"],"sourcesContent":["// Client\nexport { visualAI } from \"./core/client.js\";\n\n// Constants\nexport {\n Provider,\n Model,\n Content,\n Layout,\n Accessibility,\n ReasoningEffort,\n DEFAULT_MODELS,\n} from \"./constants.js\";\nexport type {\n KnownModelName,\n ContentCheckName,\n LayoutCheckName,\n AccessibilityCheckName,\n ReasoningEffortLevel,\n} from \"./constants.js\";\nexport type { VisualAIClient } from \"./core/client.js\";\n\n// Types\nexport type {\n CheckResult,\n CheckOptions,\n CompareResult,\n CompareOptions,\n ChangeEntry,\n Confidence,\n AskResult,\n AskOptions,\n Issue,\n IssuePriority,\n IssueCategory,\n StatementResult,\n UsageInfo,\n VisualAIConfig,\n ImageInput,\n ProviderName,\n ElementsVisibilityOptions,\n AccessibilityOptions,\n LayoutOptions,\n PageLoadOptions,\n ContentOptions,\n DiffImageResult,\n SupportedMimeType,\n} from \"./types.js\";\nexport type { VisualAIErrorCode, VisualAIKnownError } from \"./errors.js\";\n\n// Zod schemas (for advanced users)\nexport {\n CheckResultSchema,\n CompareResultSchema,\n ChangeEntrySchema,\n ConfidenceSchema,\n AskResultSchema,\n IssueSchema,\n IssuePrioritySchema,\n IssueCategorySchema,\n StatementResultSchema,\n UsageInfoSchema,\n} from \"./types.js\";\n\n// Errors\nexport {\n VisualAIError,\n VisualAIAuthError,\n VisualAIRateLimitError,\n VisualAIProviderError,\n VisualAIImageError,\n VisualAIResponseParseError,\n VisualAITruncationError,\n VisualAIConfigError,\n VisualAIAssertionError,\n isVisualAIKnownError,\n} from \"./errors.js\";\n\n// Formatting & assertions\nexport {\n formatCheckResult,\n formatCompareResult,\n assertVisualResult,\n assertVisualCompareResult,\n} from \"./format.js\";\n","import type { ProviderName } from \"./types.js\";\n\n// --- Reasoning effort constants ---\n\n/** Supported reasoning effort levels. */\nexport const ReasoningEffort = {\n LOW: \"low\",\n MEDIUM: \"medium\",\n HIGH: \"high\",\n XHIGH: \"xhigh\",\n} as const;\n\n/** Union of valid reasoning effort values, derived from the ReasoningEffort constant. */\nexport type ReasoningEffortLevel = (typeof ReasoningEffort)[keyof typeof ReasoningEffort];\n\n// --- Provider constants ---\n\n/** Supported provider identifiers used internally for pricing and provider selection. */\nexport const Provider = {\n ANTHROPIC: \"anthropic\",\n OPENAI: \"openai\",\n GOOGLE: \"google\",\n} as const satisfies Record<string, ProviderName>;\n\n// --- Model constants (grouped by provider) ---\n\n/** Known model names grouped by provider. */\nexport const Model = {\n Anthropic: {\n OPUS_4_6: \"claude-opus-4-6\",\n SONNET_4_6: \"claude-sonnet-4-6\",\n HAIKU_4_5: \"claude-haiku-4-5\",\n },\n OpenAI: {\n GPT_5_4: \"gpt-5.4\",\n GPT_5_4_PRO: \"gpt-5.4-pro\",\n GPT_5_4_MINI: \"gpt-5.4-mini\",\n GPT_5_4_NANO: \"gpt-5.4-nano\",\n GPT_5_2: \"gpt-5.2\",\n GPT_5_MINI: \"gpt-5-mini\",\n },\n Google: {\n GEMINI_3_1_PRO_PREVIEW: \"gemini-3.1-pro-preview\",\n GEMINI_3_1_FLASH_LITE_PREVIEW: \"gemini-3.1-flash-lite-preview\",\n GEMINI_3_FLASH_PREVIEW: \"gemini-3-flash-preview\",\n },\n} as const;\n\n// --- Derived utility types ---\n\n/** Union of all built-in model name literals exposed by `Model`. */\nexport type KnownModelName =\n | (typeof Model.Anthropic)[keyof typeof Model.Anthropic]\n | (typeof Model.OpenAI)[keyof typeof Model.OpenAI]\n | (typeof Model.Google)[keyof typeof Model.Google];\n\n// --- Default model per provider ---\n\n/** Default model selection used when a caller omits `config.model`. */\nexport const DEFAULT_MODELS = {\n [Provider.ANTHROPIC]: Model.Anthropic.SONNET_4_6,\n [Provider.OPENAI]: Model.OpenAI.GPT_5_4_MINI,\n [Provider.GOOGLE]: Model.Google.GEMINI_3_FLASH_PREVIEW,\n} as const satisfies Record<ProviderName, KnownModelName>;\n\nexport const DEFAULT_MAX_TOKENS = 4096;\n\n/**\n * Increased token budget for OpenAI when reasoning effort is high/xhigh.\n * Reasoning tokens share the output budget on OpenAI, so the default 4096\n * is insufficient for higher reasoning levels.\n */\nexport const OPENAI_REASONING_MAX_TOKENS = 16384;\n\n// --- Reverse map: model → provider ---\n\nexport const MODEL_TO_PROVIDER: ReadonlyMap<string, ProviderName> = new Map([\n ...Object.values(Model.Anthropic).map((m) => [m, Provider.ANTHROPIC] as const),\n ...Object.values(Model.OpenAI).map((m) => [m, Provider.OPENAI] as const),\n ...Object.values(Model.Google).map((m) => [m, Provider.GOOGLE] as const),\n]);\n\n// --- Valid providers array ---\n\n/** List of accepted provider names for validation and public consumption. */\nexport const VALID_PROVIDERS: readonly ProviderName[] = Object.values(Provider);\n\n// --- Provider default reasoning ---\n\n/**\n * What each provider uses when no reasoning effort is explicitly requested.\n * These are informational only — displayed in usage logs, not sent to providers.\n */\nexport const PROVIDER_DEFAULT_REASONING: Readonly<Record<ProviderName, string>> = {\n openai: \"medium\",\n anthropic: \"off\",\n google: \"off\",\n};\n\n// --- Check name constants ---\n\n/** Built-in content checks available through `client.content()`. */\nexport const Content = {\n /** Detects Lorem ipsum, TODO, TBD, and similar placeholder text */\n PLACEHOLDER_TEXT: \"placeholder-text\",\n /** Detects error messages, banners, stack traces, or error codes */\n ERROR_MESSAGES: \"error-messages\",\n /** Detects broken image icons or failed-to-load image indicators */\n BROKEN_IMAGES: \"broken-images\",\n /** Detects UI elements that unintentionally overlap and obscure content */\n OVERLAPPING_ELEMENTS: \"overlapping-elements\",\n} as const;\n\n/** Built-in layout checks available through `client.layout()`. */\nexport const Layout = {\n /** Detects elements that unintentionally overlap each other */\n OVERLAP: \"overlap\",\n /** Detects content cut off or extending beyond container boundaries */\n OVERFLOW: \"overflow\",\n /** Detects inconsistent alignment of text, images, and UI components */\n ALIGNMENT: \"alignment\",\n} as const;\n\n/** Built-in accessibility checks available through `client.accessibility()`. */\nexport const Accessibility = {\n /** Detects insufficient color contrast between text and backgrounds */\n CONTRAST: \"contrast\",\n /** Detects text that is cut off, overlapping, too small, or obscured */\n READABILITY: \"readability\",\n /** Detects interactive elements that are not visually distinct */\n INTERACTIVE_VISIBILITY: \"interactive-visibility\",\n} as const;\n\n// --- Derived check-name union types ---\n\n/** Union of all built-in content check names. */\nexport type ContentCheckName = (typeof Content)[keyof typeof Content];\n/** Union of all built-in layout check names. */\nexport type LayoutCheckName = (typeof Layout)[keyof typeof Layout];\n/** Union of all built-in accessibility check names. */\nexport type AccessibilityCheckName = (typeof Accessibility)[keyof typeof Accessibility];\n","import type { CheckResult, CompareResult } from \"./types.js\";\n\n/**\n * Discrete error codes exposed by visual-ai-assertions for programmatic handling.\n */\nexport type VisualAIErrorCode =\n | \"VISUAL_AI_ERROR\"\n | \"AUTH_FAILED\"\n | \"RATE_LIMITED\"\n | \"PROVIDER_ERROR\"\n | \"IMAGE_INVALID\"\n | \"RESPONSE_PARSE_FAILED\"\n | \"RESPONSE_TRUNCATED\"\n | \"CONFIG_INVALID\"\n | \"ASSERTION_FAILED\";\n\n/**\n * Base class for all library errors.\n *\n * @example\n * ```ts\n * try {\n * // ...\n * } catch (error) {\n * if (error instanceof VisualAIError) {\n * console.error(error.code, error.message);\n * }\n * }\n * ```\n */\nexport class VisualAIError<TCode extends VisualAIErrorCode = VisualAIErrorCode> extends Error {\n readonly code: TCode;\n\n constructor(message: string, code: TCode = \"VISUAL_AI_ERROR\" as TCode) {\n super(message);\n this.code = code;\n this.name = \"VisualAIError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a provider rejects the configured API credentials.\n *\n * @example\n * ```ts\n * throw new VisualAIAuthError(\"Anthropic API key not found\");\n * ```\n */\nexport class VisualAIAuthError extends VisualAIError<\"AUTH_FAILED\"> {\n declare readonly code: \"AUTH_FAILED\";\n\n constructor(message: string) {\n super(message, \"AUTH_FAILED\");\n this.name = \"VisualAIAuthError\";\n }\n}\n\n/**\n * Thrown when a provider enforces a rate limit.\n *\n * Carries `retryAfter` when the provider includes retry guidance.\n *\n * @example\n * ```ts\n * throw new VisualAIRateLimitError(\"Rate limited\", 30);\n * ```\n */\nexport class VisualAIRateLimitError extends VisualAIError<\"RATE_LIMITED\"> {\n declare readonly code: \"RATE_LIMITED\";\n retryAfter?: number;\n\n constructor(message: string, retryAfter?: number) {\n super(message, \"RATE_LIMITED\");\n this.name = \"VisualAIRateLimitError\";\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Thrown when a provider returns an unexpected non-auth, non-rate-limit failure.\n *\n * Carries `statusCode` when the provider exposes an HTTP status.\n *\n * @example\n * ```ts\n * throw new VisualAIProviderError(\"Provider returned 500\", 500);\n * ```\n */\nexport class VisualAIProviderError extends VisualAIError<\"PROVIDER_ERROR\"> {\n declare readonly code: \"PROVIDER_ERROR\";\n statusCode?: number;\n\n constructor(message: string, statusCode?: number) {\n super(message, \"PROVIDER_ERROR\");\n this.name = \"VisualAIProviderError\";\n this.statusCode = statusCode;\n }\n}\n\n/**\n * Thrown when an image input cannot be loaded, decoded, or validated.\n *\n * @example\n * ```ts\n * throw new VisualAIImageError(\"Unsupported image format: image/bmp\");\n * ```\n */\nexport class VisualAIImageError extends VisualAIError<\"IMAGE_INVALID\"> {\n declare readonly code: \"IMAGE_INVALID\";\n\n constructor(message: string) {\n super(message, \"IMAGE_INVALID\");\n this.name = \"VisualAIImageError\";\n }\n}\n\n/**\n * Thrown when a provider response cannot be parsed into the library result schema.\n *\n * Carries `rawResponse` so callers can inspect the original model output.\n *\n * @example\n * ```ts\n * throw new VisualAIResponseParseError(\"Invalid JSON\", rawText);\n * ```\n */\nexport class VisualAIResponseParseError extends VisualAIError<\"RESPONSE_PARSE_FAILED\"> {\n declare readonly code: \"RESPONSE_PARSE_FAILED\";\n rawResponse: string;\n\n constructor(message: string, rawResponse: string) {\n super(message, \"RESPONSE_PARSE_FAILED\");\n this.name = \"VisualAIResponseParseError\";\n this.rawResponse = rawResponse;\n }\n}\n\n/**\n * Thrown when a provider response is truncated due to token budget exhaustion.\n *\n * Carries `partialResponse` and `maxTokens` so callers can inspect the output\n * and adjust configuration.\n *\n * @example\n * ```ts\n * throw new VisualAITruncationError(\"Response truncated\", partialText, 4096);\n * ```\n */\nexport class VisualAITruncationError extends VisualAIError<\"RESPONSE_TRUNCATED\"> {\n declare readonly code: \"RESPONSE_TRUNCATED\";\n readonly partialResponse: string;\n readonly maxTokens: number;\n\n constructor(message: string, partialResponse: string, maxTokens: number) {\n super(message, \"RESPONSE_TRUNCATED\");\n this.name = \"VisualAITruncationError\";\n this.partialResponse = partialResponse;\n this.maxTokens = maxTokens;\n }\n}\n\n/**\n * Thrown when library configuration is missing or invalid.\n *\n * @example\n * ```ts\n * throw new VisualAIConfigError(\"At least one statement is required for check()\");\n * ```\n */\nexport class VisualAIConfigError extends VisualAIError<\"CONFIG_INVALID\"> {\n declare readonly code: \"CONFIG_INVALID\";\n\n constructor(message: string) {\n super(message, \"CONFIG_INVALID\");\n this.name = \"VisualAIConfigError\";\n }\n}\n\n/**\n * Thrown by assertion helpers when a visual check or comparison fails.\n *\n * Carries the failed `result` for further inspection.\n *\n * @example\n * ```ts\n * throw new VisualAIAssertionError(\"Visual assertion failed\", result);\n * ```\n */\nexport class VisualAIAssertionError extends VisualAIError<\"ASSERTION_FAILED\"> {\n declare readonly code: \"ASSERTION_FAILED\";\n result: CheckResult | CompareResult;\n\n constructor(message: string, result: CheckResult | CompareResult) {\n super(message, \"ASSERTION_FAILED\");\n this.name = \"VisualAIAssertionError\";\n this.result = result;\n }\n}\n\n/**\n * Union of all concrete error subclasses exposed by the library.\n */\nexport type VisualAIKnownError =\n | VisualAIAuthError\n | VisualAIRateLimitError\n | VisualAIProviderError\n | VisualAIImageError\n | VisualAIResponseParseError\n | VisualAITruncationError\n | VisualAIConfigError\n | VisualAIAssertionError;\n\n/**\n * Narrows an unknown thrown value to the concrete visual-ai-assertions error union.\n *\n * Use this helper when you want `switch (error.code)` to narrow to subclass-specific fields.\n *\n * @param error Unknown thrown value.\n * @returns `true` when the value is one of the concrete library error subclasses.\n * @example\n * ```ts\n * try {\n * // ...\n * } catch (error) {\n * if (isVisualAIKnownError(error)) {\n * switch (error.code) {\n * case \"RATE_LIMITED\":\n * console.error(error.retryAfter);\n * break;\n * case \"PROVIDER_ERROR\":\n * console.error(error.statusCode);\n * break;\n * }\n * }\n * }\n * ```\n */\nexport function isVisualAIKnownError(error: unknown): error is VisualAIKnownError {\n return (\n error instanceof VisualAIAuthError ||\n error instanceof VisualAIRateLimitError ||\n error instanceof VisualAIProviderError ||\n error instanceof VisualAIImageError ||\n error instanceof VisualAIResponseParseError ||\n error instanceof VisualAITruncationError ||\n error instanceof VisualAIConfigError ||\n error instanceof VisualAIAssertionError\n );\n}\n","const JSON_INSTRUCTIONS = `\nIMPORTANT: You MUST respond with valid JSON only. No markdown, no code blocks, no extra text.\n`;\n\nconst ISSUE_SCHEMA_INSTRUCTIONS = `\nEach issue must have:\n- \"priority\": \"critical\" | \"major\" | \"minor\"\n- \"category\": \"accessibility\" | \"missing-element\" | \"layout\" | \"content\" | \"styling\" | \"functionality\" | \"performance\" | \"other\"\n- \"description\": what the issue is\n- \"suggestion\": how to fix or improve it\n`;\n\nconst CHECK_OUTPUT_SCHEMA = `IMPORTANT: Follow this evaluation order:\n1. First, evaluate EACH statement independently and populate the \"statements\" array\n2. Then, set \"pass\" to true ONLY if every statement passed (logical AND of all statement results)\n3. Write \"reasoning\" as a brief overall summary of the evaluation\n4. Include \"issues\" only for statements that failed\n\nRespond with a JSON object matching this exact structure:\n{\n \"pass\": boolean, // true ONLY if ALL statements passed — derive from statements array\n \"reasoning\": string, // brief overall summary of the evaluation\n \"issues\": [...], // one issue per failing statement (empty if all pass)\n \"statements\": [ // one entry per statement, in order — evaluate these FIRST\n {\n \"statement\": string, // the original statement text\n \"pass\": boolean, // whether this statement is true\n \"reasoning\": string, // explanation for this statement\n \"confidence\": \"high\" | \"medium\" | \"low\"\n // high = clearly visible/absent with no ambiguity\n // medium = present but partially obscured, small, or borderline\n // low = cannot determine with certainty from the screenshot\n }\n ]\n}\n${ISSUE_SCHEMA_INSTRUCTIONS}\n\nOnly include issues for statements that fail. If all statements pass, issues should be an empty array.\n\nExample for a failing check:\n{\n \"pass\": false,\n \"reasoning\": \"The submit button is not visible on the page.\",\n \"issues\": [\n { \"priority\": \"major\", \"category\": \"missing-element\", \"description\": \"Submit button is not visible on the page\", \"suggestion\": \"Verify the submit button component is rendered and not hidden by CSS\" }\n ],\n \"statements\": [\n { \"statement\": \"The page header is visible\", \"pass\": true, \"reasoning\": \"Header with logo is clearly visible at the top\", \"confidence\": \"high\" },\n { \"statement\": \"The submit button is visible\", \"pass\": false, \"reasoning\": \"No submit button found in the visible area of the page\", \"confidence\": \"high\" }\n ]\n}\n${JSON_INSTRUCTIONS}`;\n\nconst ASK_OUTPUT_SCHEMA = `Respond with a JSON object matching this exact structure:\n{\n \"summary\": string, // high-level analysis summary\n \"issues\": [...] // list of issues/findings, can be empty\n}\n${ISSUE_SCHEMA_INSTRUCTIONS}\n\nPrioritize issues by severity:\n- \"critical\": blocks functionality, breaks accessibility, data loss risk\n- \"major\": significant usability or visual problem\n- \"minor\": cosmetic issue, minor improvement suggestion\n\nExample:\n{\n \"summary\": \"Found 2 issues: a critical accessibility problem and a minor cosmetic issue.\",\n \"issues\": [\n { \"priority\": \"critical\", \"category\": \"accessibility\", \"description\": \"Submit button has insufficient color contrast\", \"suggestion\": \"Increase contrast so text is clearly readable against the background\" },\n { \"priority\": \"minor\", \"category\": \"content\", \"description\": \"Placeholder text 'Lorem ipsum' visible in sidebar\", \"suggestion\": \"Replace with actual content or remove the placeholder section\" }\n ]\n}\n${JSON_INSTRUCTIONS}`;\n\nconst COMPARE_OUTPUT_SCHEMA = `Respond with a JSON object matching this exact structure:\n{\n \"pass\": boolean, // true if no critical or major changes found\n \"reasoning\": string, // overall summary of changes detected\n \"changes\": [ // list of all visual differences detected (empty if images are identical)\n {\n \"description\": string, // what changed between the images\n \"severity\": \"critical\" | \"major\" | \"minor\"\n // critical = element removed, layout broken, functionality lost\n // major = significant visual change that may be unintentional\n // minor = small stylistic difference (color, spacing, font)\n }\n ]\n}\n\nIf the images appear identical, set pass to true, explain in reasoning, and return an empty changes array.\n${JSON_INSTRUCTIONS}`;\n\nconst DEFAULT_CHECK_ROLE =\n \"You are a visual QA assistant. Evaluate the provided image precisely and objectively.\";\n\nconst DEFAULT_ASK_ROLE =\n \"You are a visual QA assistant. Analyze the provided image based on the user's request.\";\n\nconst COMPARE_ROLE =\n \"You are performing a visual regression test. Compare the BEFORE image (baseline) to the AFTER image (current) and identify all visual differences. Flag changes that appear unintentional or problematic.\";\n\nconst COMPARE_EDGE_RULES: readonly string[] = [\n \"The BEFORE image is the baseline/expected state.\",\n \"Flag removals and layout changes as higher severity. Color and spacing changes are lower severity unless they break readability.\",\n \"If the images appear identical, report no changes and pass.\",\n];\n\nexport interface CheckPromptOptions {\n readonly role?: string;\n readonly instructions?: readonly string[];\n}\n\nexport interface ComparePromptOptions {\n readonly userPrompt?: string;\n readonly instructions?: readonly string[];\n}\n\nfunction buildInstructionsSection(instructions: readonly string[]): string {\n return (\n \"Additional instructions:\\n\" + instructions.map((instruction) => `- ${instruction}`).join(\"\\n\")\n );\n}\n\nexport function buildCheckPrompt(\n statements: string | string[],\n options?: CheckPromptOptions,\n): string {\n const stmts = Array.isArray(statements) ? statements : [statements];\n const statementsBlock = stmts.map((s, i) => `${i + 1}. \"${s}\"`).join(\"\\n\");\n\n const sections = [options?.role ?? DEFAULT_CHECK_ROLE];\n\n if (options?.instructions && options.instructions.length > 0) {\n sections.push(buildInstructionsSection(options.instructions));\n }\n\n sections.push(`Statements to evaluate:\\n${statementsBlock}`);\n sections.push(CHECK_OUTPUT_SCHEMA);\n\n return sections.join(\"\\n\\n\");\n}\n\nexport function buildAskPrompt(\n userPrompt: string,\n options?: { readonly instructions?: readonly string[] },\n): string {\n const sections = [DEFAULT_ASK_ROLE];\n\n if (options?.instructions && options.instructions.length > 0) {\n sections.push(buildInstructionsSection(options.instructions));\n }\n\n sections.push(`User request: ${userPrompt}`);\n sections.push(ASK_OUTPUT_SCHEMA);\n\n return sections.join(\"\\n\\n\");\n}\n\nexport function buildAiDiffPrompt(): string {\n return `You are given two screenshots of the same page or component.\nGenerate a single annotated image that clearly highlights the visual differences between the first and second images.\n- Overlay semi-transparent red rectangles or outlines on areas that changed\n- Keep unchanged areas visible but slightly dimmed\n- The output image should match the dimensions of the input images\n- Focus on meaningful visual differences: layout shifts, missing elements, color changes, text differences`;\n}\n\nexport function buildAiDiffCodeExecutionPrompt(): string {\n return `${buildAiDiffPrompt()}\n\nWrite Python code using PIL/Pillow for image processing and matplotlib for rendering to accomplish the above.\nYour code MUST load both input images and display the result using matplotlib.`;\n}\n\nexport function buildComparePrompt(options?: ComparePromptOptions): string {\n const evaluation = options?.userPrompt\n ? `User request: ${options.userPrompt}`\n : \"Identify all visual differences between the baseline and current screenshot. Flag any changes that appear unintentional or problematic.\";\n\n const instructions = options?.instructions\n ? [...COMPARE_EDGE_RULES, ...options.instructions]\n : COMPARE_EDGE_RULES;\n\n const sections = [\n COMPARE_ROLE,\n buildInstructionsSection(instructions),\n evaluation,\n COMPARE_OUTPUT_SCHEMA,\n ];\n\n return sections.join(\"\\n\\n\");\n}\n","import type { ElementsVisibilityOptions } from \"../types.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst ELEMENTS_VISIBLE_ROLE =\n \"Check whether specific UI elements are present and fully visible in this screenshot.\";\n\nconst ELEMENTS_HIDDEN_ROLE =\n \"Check whether specific UI elements are absent or hidden in this screenshot.\";\n\nconst ELEMENTS_VISIBLE_EDGE_RULES: readonly string[] = [\n \"If an element is partially visible (cut off by screenshot boundary), it is NOT considered fully visible — the check for that element should fail. Note the partial visibility in your reasoning.\",\n];\n\nconst ELEMENTS_HIDDEN_EDGE_RULES: readonly string[] = [\n \"If an element is partially visible (cut off by screenshot boundary), it is NOT considered hidden — the check for that element should fail. Note the partial visibility in your reasoning.\",\n];\n\nexport function buildElementsVisibilityPrompt(\n elements: string[],\n visible: boolean,\n options?: ElementsVisibilityOptions,\n): string {\n const statements = visible\n ? elements.map((el) => `The element \"${el}\" is fully visible on the page`)\n : elements.map((el) => `The element \"${el}\" is NOT visible on the page`);\n\n const defaultRules = visible ? ELEMENTS_VISIBLE_EDGE_RULES : ELEMENTS_HIDDEN_EDGE_RULES;\n const instructions = options?.instructions\n ? [...defaultRules, ...options.instructions]\n : defaultRules;\n\n return buildCheckPrompt(statements, {\n role: visible ? ELEMENTS_VISIBLE_ROLE : ELEMENTS_HIDDEN_ROLE,\n instructions,\n });\n}\n","import type { AccessibilityOptions } from \"../types.js\";\nimport { Accessibility, type AccessibilityCheckName } from \"../constants.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst ALL_CHECKS: AccessibilityCheckName[] = Object.values(Accessibility);\n\nconst ACCESSIBILITY_ROLE =\n \"Evaluate this screenshot for visual accessibility. Focus on what you can actually perceive — apparent contrast levels, text legibility, and visual distinctiveness of interactive elements.\";\n\nconst ACCESSIBILITY_EDGE_RULES: readonly string[] = [\n \"Do not state specific contrast ratios. Describe contrast as 'appears sufficient' or 'appears low'.\",\n \"Dark mode and light mode themes are both valid. Do not flag a valid dark theme as a contrast issue.\",\n];\n\nconst CHECK_STATEMENTS: Record<AccessibilityCheckName, string> = {\n [Accessibility.CONTRAST]:\n \"All text and interactive elements appear to have sufficient color contrast — text is clearly readable against its background\",\n [Accessibility.READABILITY]:\n \"All text is readable — no text is cut off, overlapping, too small to read, or obscured by background images\",\n [Accessibility.INTERACTIVE_VISIBILITY]:\n \"All interactive elements (buttons, links, inputs) are clearly identifiable and visually distinct from non-interactive content\",\n};\n\nexport function buildAccessibilityPrompt(options?: AccessibilityOptions): string {\n const checks = options?.checks ?? [...ALL_CHECKS];\n const statements = checks.map((c) => CHECK_STATEMENTS[c]);\n\n const instructions = options?.instructions\n ? [...ACCESSIBILITY_EDGE_RULES, ...options.instructions]\n : ACCESSIBILITY_EDGE_RULES;\n\n return buildCheckPrompt(statements, {\n role: ACCESSIBILITY_ROLE,\n instructions,\n });\n}\n","import type { LayoutOptions } from \"../types.js\";\nimport { Layout, type LayoutCheckName } from \"../constants.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst ALL_CHECKS: LayoutCheckName[] = Object.values(Layout);\n\nconst LAYOUT_ROLE =\n \"Evaluate this screenshot for visual layout problems — overlapping elements, content that appears cut off or overflowing, and inconsistent alignment patterns.\";\n\nconst LAYOUT_EDGE_RULES: readonly string[] = [\n \"Intentional overlaps (stacked avatars, dropdown menus, overlapping cards) are acceptable. Flag only overlaps that obscure content or appear broken.\",\n \"Scrollable containers with partially visible content are not overflow issues.\",\n];\n\nconst CHECK_STATEMENTS: Record<LayoutCheckName, string> = {\n [Layout.OVERLAP]:\n \"No elements overlap each other unintentionally — all content is clearly separated and readable\",\n [Layout.OVERFLOW]:\n \"No content appears to be unintentionally cut off or extending beyond its container boundaries\",\n [Layout.ALIGNMENT]:\n \"Elements are properly aligned — text, images, and UI components follow a consistent grid or alignment pattern\",\n};\n\nexport function buildLayoutPrompt(options?: LayoutOptions): string {\n const checks = options?.checks ?? [...ALL_CHECKS];\n const statements = checks.map((c) => CHECK_STATEMENTS[c]);\n\n const instructions = options?.instructions\n ? [...LAYOUT_EDGE_RULES, ...options.instructions]\n : LAYOUT_EDGE_RULES;\n\n return buildCheckPrompt(statements, { role: LAYOUT_ROLE, instructions });\n}\n","import type { PageLoadOptions } from \"../types.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst PAGE_LOAD_ROLE =\n \"Evaluate whether this page has finished loading. Look for loading indicators, empty content areas, and missing resources.\";\n\nexport function buildPageLoadPrompt(options?: PageLoadOptions): string {\n const expectLoaded = options?.expectLoaded ?? true;\n\n const statements = expectLoaded\n ? [\n \"The page content has finished loading — no spinning indicators, skeleton placeholders, or progress bars are visible\",\n \"The main content area has actual content displayed (not blank or empty)\",\n \"No broken image icons or missing resource indicators are visible\",\n ]\n : [\n \"The page shows a loading state — loading spinners, skeleton screens, or progress indicators are visible\",\n ];\n\n return buildCheckPrompt(statements, {\n role: PAGE_LOAD_ROLE,\n instructions: options?.instructions,\n });\n}\n","import type { ContentOptions } from \"../types.js\";\nimport { Content, type ContentCheckName } from \"../constants.js\";\nimport { buildCheckPrompt } from \"../core/prompt.js\";\n\nconst ALL_CHECKS: ContentCheckName[] = Object.values(Content);\n\nconst CONTENT_ROLE =\n \"Evaluate this screenshot for content quality problems — placeholder or dummy content, error states, and broken resources that should not appear in a production UI.\";\n\nconst CHECK_STATEMENTS: Record<ContentCheckName, string> = {\n [Content.PLACEHOLDER_TEXT]:\n \"No placeholder text like 'Lorem ipsum', 'TODO', 'TBD', 'placeholder', or similar dummy content is visible on the page\",\n [Content.ERROR_MESSAGES]:\n \"No error messages, error banners, stack traces, or error codes are visible on the page\",\n [Content.BROKEN_IMAGES]:\n \"No broken image icons, missing image placeholders, or failed-to-load image indicators are visible\",\n [Content.OVERLAPPING_ELEMENTS]:\n \"No UI elements are unintentionally overlapping each other, obscuring text, buttons, or other interactive content\",\n};\n\nexport function buildContentPrompt(options?: ContentOptions): string {\n const checks = options?.checks ?? [...ALL_CHECKS];\n const statements = checks.map((c) => CHECK_STATEMENTS[c]);\n\n return buildCheckPrompt(statements, {\n role: CONTENT_ROLE,\n instructions: options?.instructions,\n });\n}\n","import { VisualAIAuthError, VisualAIProviderError, VisualAIRateLimitError } from \"../errors.js\";\n\nexport function mapProviderError(err: unknown): Error {\n if (!(err instanceof Error)) {\n return new VisualAIProviderError(String(err));\n }\n\n const status = (err as { status?: number }).status;\n\n if (status === 401 || status === 403) {\n return new VisualAIAuthError(err.message);\n }\n if (status === 429) {\n const headers = (err as { headers?: Record<string, string> }).headers;\n const retryAfter = parseRetryAfter(headers?.[\"retry-after\"]);\n return new VisualAIRateLimitError(err.message, retryAfter);\n }\n if (status !== undefined) {\n return new VisualAIProviderError(err.message, status);\n }\n\n return new VisualAIProviderError(err.message);\n}\n\nfunction parseRetryAfter(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n","import { VisualAIAuthError, VisualAIConfigError, VisualAITruncationError } from \"../errors.js\";\nimport { mapProviderError } from \"./error-mapper.js\";\nimport type { NormalizedImage } from \"../types.js\";\nimport type {\n ProviderConfig,\n ProviderDriver,\n RawProviderResponse,\n SendMessageOptions,\n} from \"./types.js\";\n\n/** Minimal interface for the Anthropic SDK client used by this driver. */\ninterface AnthropicMessage {\n content: Array<{ type: string; text?: string }>;\n usage: { input_tokens: number; output_tokens: number };\n stop_reason?: string;\n}\n\ninterface AnthropicClient {\n messages: {\n create(params: Record<string, unknown>): Promise<AnthropicMessage>;\n };\n}\n\nexport class AnthropicDriver implements ProviderDriver {\n private client: AnthropicClient | null;\n private model: string;\n private maxTokens: number;\n private apiKeyOrEnv: string | undefined;\n private reasoningEffort: ProviderConfig[\"reasoningEffort\"];\n\n constructor(config: ProviderConfig) {\n this.model = config.model;\n this.maxTokens = config.maxTokens;\n this.client = null;\n this.apiKeyOrEnv = config.apiKey;\n this.reasoningEffort = config.reasoningEffort;\n }\n\n private async getClient(): Promise<AnthropicClient> {\n if (this.client) return this.client;\n\n let Anthropic: unknown;\n try {\n const mod: unknown = await import(\"@anthropic-ai/sdk\");\n Anthropic = (mod as { default: unknown }).default;\n } catch {\n throw new VisualAIConfigError(\n \"Anthropic SDK not installed. Run: npm install @anthropic-ai/sdk\",\n );\n }\n\n const apiKey = this.apiKeyOrEnv ?? process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n throw new VisualAIAuthError(\n \"Anthropic API key not found. Set ANTHROPIC_API_KEY or pass apiKey in config.\",\n );\n }\n\n this.client = new (Anthropic as new (opts: { apiKey: string }) => AnthropicClient)({ apiKey });\n return this.client;\n }\n\n async sendMessage(\n images: NormalizedImage[],\n prompt: string,\n _options?: SendMessageOptions,\n ): Promise<RawProviderResponse> {\n const client = await this.getClient();\n\n const imageBlocks = images.map((img) => ({\n type: \"image\" as const,\n source: {\n type: \"base64\" as const,\n media_type: img.mimeType,\n data: img.base64,\n },\n }));\n\n try {\n const requestParams: Record<string, unknown> = {\n model: this.model,\n max_tokens: this.maxTokens,\n messages: [\n {\n role: \"user\",\n content: [...imageBlocks, { type: \"text\" as const, text: prompt }],\n },\n ],\n };\n\n if (this.reasoningEffort) {\n requestParams.thinking = { type: \"adaptive\" };\n requestParams.output_config = {\n effort: this.reasoningEffort === \"xhigh\" ? \"max\" : this.reasoningEffort,\n };\n }\n\n const message = await client.messages.create(requestParams);\n\n const textBlock = message.content.find((block) => block.type === \"text\");\n const text = textBlock?.text ?? \"\";\n\n if (message.stop_reason === \"max_tokens\") {\n throw new VisualAITruncationError(\n `Response truncated: Anthropic stopped due to max_tokens limit (${this.maxTokens} tokens). Increase maxTokens in your config or lower reasoningEffort.`,\n text,\n this.maxTokens,\n );\n }\n\n return {\n text,\n usage: {\n inputTokens: message.usage.input_tokens,\n outputTokens: message.usage.output_tokens,\n },\n };\n } catch (err) {\n if (err instanceof VisualAITruncationError) throw err;\n throw mapProviderError(err);\n }\n }\n}\n","import { buildAiDiffCodeExecutionPrompt } from \"../core/prompt.js\";\nimport {\n VisualAIAuthError,\n VisualAIConfigError,\n VisualAIProviderError,\n VisualAITruncationError,\n} from \"../errors.js\";\nimport { mapProviderError } from \"./error-mapper.js\";\nimport type { NormalizedImage } from \"../types.js\";\nimport type { ReasoningEffortLevel } from \"../constants.js\";\nimport type {\n ImageGenerationOptions,\n ImageGenerationResponse,\n ProviderConfig,\n ProviderDriver,\n RawProviderResponse,\n SendMessageOptions,\n} from \"./types.js\";\n\nconst DEFAULT_IMAGE_GEN_MODEL = \"gemini-2.5-flash-image\";\n\n/** Gemini 3+ models use code execution for image generation instead of responseModalities. */\nexport function needsCodeExecution(model: string): boolean {\n const match = model.match(/^gemini-(\\d+)/);\n return match !== null && match[1] !== undefined && parseInt(match[1], 10) >= 3;\n}\n\n/** Response parts from Gemini API. Code execution responses include executableCode/codeExecutionResult\n * parts alongside inlineData; these fields are kept for type accuracy of the full response shape. */\ninterface GeminiImagePart {\n text?: string;\n inlineData?: {\n data: string;\n mimeType: string;\n };\n executableCode?: {\n code: string;\n language?: string;\n };\n codeExecutionResult?: {\n outcome?: string;\n output?: string;\n };\n}\n\n/** Minimal interface for the Google GenAI SDK client used by this driver. */\ninterface GoogleGenerateContentResponse {\n text?: string;\n candidates?: Array<{\n content?: {\n parts?: GeminiImagePart[];\n };\n finishReason?: string;\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n thoughtsTokenCount?: number;\n };\n}\n\ninterface GoogleClient {\n models: {\n generateContent(params: Record<string, unknown>): Promise<GoogleGenerateContentResponse>;\n };\n}\n\nconst GOOGLE_THINKING_LEVEL = {\n low: \"minimal\",\n medium: \"low\",\n high: \"medium\",\n xhigh: \"high\",\n} as const satisfies Record<ReasoningEffortLevel, string>;\n\nexport class GoogleDriver implements ProviderDriver {\n private client: GoogleClient | null;\n private model: string;\n private maxTokens: number;\n private apiKeyOrEnv: string | undefined;\n private reasoningEffort: ProviderConfig[\"reasoningEffort\"];\n\n constructor(config: ProviderConfig) {\n this.model = config.model;\n this.maxTokens = config.maxTokens;\n this.client = null;\n this.apiKeyOrEnv = config.apiKey;\n this.reasoningEffort = config.reasoningEffort;\n }\n\n private toGeminiParts(images: NormalizedImage[]) {\n return images.map((img) => ({\n inlineData: { data: img.base64, mimeType: img.mimeType },\n }));\n }\n\n private async getClient(): Promise<GoogleClient> {\n if (this.client) return this.client;\n\n let GoogleGenAI: unknown;\n try {\n const mod: unknown = await import(\"@google/genai\");\n GoogleGenAI = (mod as { GoogleGenAI: unknown }).GoogleGenAI;\n } catch {\n throw new VisualAIConfigError(\n \"Google GenAI SDK not installed. Run: npm install @google/genai\",\n );\n }\n\n const apiKey = this.apiKeyOrEnv ?? process.env.GOOGLE_API_KEY;\n if (!apiKey) {\n throw new VisualAIAuthError(\n \"Google API key not found. Set GOOGLE_API_KEY or pass apiKey in config.\",\n );\n }\n\n this.client = new (GoogleGenAI as new (opts: { apiKey: string }) => GoogleClient)({ apiKey });\n return this.client;\n }\n\n async sendMessage(\n images: NormalizedImage[],\n prompt: string,\n _options?: SendMessageOptions,\n ): Promise<RawProviderResponse> {\n const client = await this.getClient();\n\n try {\n const response = await client.models.generateContent({\n model: this.model,\n contents: [...this.toGeminiParts(images), prompt],\n config: {\n responseMimeType: \"application/json\",\n maxOutputTokens: this.maxTokens,\n ...(this.reasoningEffort && {\n thinkingConfig: {\n thinkingLevel: GOOGLE_THINKING_LEVEL[this.reasoningEffort],\n },\n }),\n },\n });\n\n const finishReason = response.candidates?.[0]?.finishReason;\n if (finishReason === \"MAX_TOKENS\") {\n throw new VisualAITruncationError(\n `Response truncated: Google returned finishReason \"MAX_TOKENS\". The model exhausted the output token budget (${this.maxTokens} tokens). Increase maxTokens in your config or lower reasoningEffort.`,\n response.text ?? \"\",\n this.maxTokens,\n );\n }\n if (finishReason && finishReason !== \"STOP\") {\n throw new VisualAIProviderError(\n `Response blocked: Google returned finishReason \"${finishReason}\".`,\n );\n }\n\n const text = response.text ?? \"\";\n const thoughtsTokenCount = response.usageMetadata?.thoughtsTokenCount;\n\n return {\n text,\n usage: response.usageMetadata\n ? {\n inputTokens: response.usageMetadata.promptTokenCount ?? 0,\n outputTokens: response.usageMetadata.candidatesTokenCount ?? 0,\n ...(thoughtsTokenCount !== undefined && { reasoningTokens: thoughtsTokenCount }),\n }\n : undefined,\n };\n } catch (err) {\n if (err instanceof VisualAITruncationError || err instanceof VisualAIProviderError) throw err;\n throw mapProviderError(err);\n }\n }\n\n async generateImage(\n images: NormalizedImage[],\n prompt: string,\n options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n const client = await this.getClient();\n const imageModel = options?.model ?? DEFAULT_IMAGE_GEN_MODEL;\n const resolvedPrompt =\n options?.promptKind === \"ai-diff\" && needsCodeExecution(imageModel)\n ? buildAiDiffCodeExecutionPrompt()\n : prompt;\n\n // Gemini 3+ models require code execution to generate images;\n // older models use native image generation via responseModalities.\n const config = needsCodeExecution(imageModel)\n ? { tools: [{ codeExecution: {} }] }\n : { responseModalities: [\"TEXT\", \"IMAGE\"] };\n\n try {\n const response = await client.models.generateContent({\n model: imageModel,\n contents: [...this.toGeminiParts(images), resolvedPrompt],\n config,\n });\n\n const parts = response.candidates?.[0]?.content?.parts;\n if (!parts) {\n throw new VisualAIProviderError(\"Gemini image generation returned no response parts\");\n }\n\n const imagePart = parts.find((p) => p.inlineData?.data);\n if (!imagePart?.inlineData) {\n throw new VisualAIProviderError(\n \"Gemini image generation returned no image data. Ensure the model supports image output.\",\n );\n }\n\n return {\n imageData: Buffer.from(imagePart.inlineData.data, \"base64\"),\n mimeType: imagePart.inlineData.mimeType,\n usage: response.usageMetadata\n ? {\n inputTokens: response.usageMetadata.promptTokenCount ?? 0,\n outputTokens: response.usageMetadata.candidatesTokenCount ?? 0,\n }\n : undefined,\n };\n } catch (err) {\n if (err instanceof VisualAIProviderError) throw err;\n throw mapProviderError(err);\n }\n }\n}\n","import { VisualAIAuthError, VisualAIConfigError, VisualAITruncationError } from \"../errors.js\";\nimport { mapProviderError } from \"./error-mapper.js\";\nimport type { NormalizedImage } from \"../types.js\";\nimport type {\n ProviderConfig,\n ProviderDriver,\n RawProviderResponse,\n SendMessageOptions,\n} from \"./types.js\";\n\n/** Minimal interface for the OpenAI SDK client used by this driver. */\ninterface OpenAIResponseResult {\n output_text?: string;\n usage?: {\n input_tokens: number;\n output_tokens: number;\n output_tokens_details?: { reasoning_tokens?: number };\n };\n status?: string;\n incomplete_details?: { reason?: string };\n}\n\ninterface OpenAIClient {\n responses: {\n create(params: Record<string, unknown>): Promise<OpenAIResponseResult>;\n };\n}\n\nexport class OpenAIDriver implements ProviderDriver {\n private client: OpenAIClient | null;\n private model: string;\n private maxTokens: number;\n private apiKeyOrEnv: string | undefined;\n private reasoningEffort: ProviderConfig[\"reasoningEffort\"];\n\n constructor(config: ProviderConfig) {\n this.model = config.model;\n this.maxTokens = config.maxTokens;\n this.client = null;\n this.apiKeyOrEnv = config.apiKey;\n this.reasoningEffort = config.reasoningEffort;\n }\n\n private async getClient(): Promise<OpenAIClient> {\n if (this.client) return this.client;\n\n let OpenAI: unknown;\n try {\n const mod: unknown = await import(\"openai\");\n OpenAI = (mod as { default: unknown }).default;\n } catch {\n throw new VisualAIConfigError(\"OpenAI SDK not installed. Run: npm install openai\");\n }\n\n const apiKey = this.apiKeyOrEnv ?? process.env.OPENAI_API_KEY;\n if (!apiKey) {\n throw new VisualAIAuthError(\n \"OpenAI API key not found. Set OPENAI_API_KEY or pass apiKey in config.\",\n );\n }\n\n this.client = new (OpenAI as new (opts: { apiKey: string }) => OpenAIClient)({ apiKey });\n return this.client;\n }\n\n async sendMessage(\n images: NormalizedImage[],\n prompt: string,\n options?: SendMessageOptions,\n ): Promise<RawProviderResponse> {\n const client = await this.getClient();\n\n const imageBlocks = images.map((img) => ({\n type: \"input_image\" as const,\n image_url: `data:${img.mimeType};base64,${img.base64}`,\n }));\n\n try {\n const format = options?.responseSchema\n ? {\n type: \"json_schema\" as const,\n name: \"visual_ai_response\",\n strict: true,\n schema: options.responseSchema,\n }\n : { type: \"json_object\" as const };\n\n const requestParams: Record<string, unknown> = {\n model: this.model,\n max_output_tokens: this.maxTokens,\n text: { format },\n input: [\n {\n role: \"user\",\n content: [...imageBlocks, { type: \"input_text\" as const, text: prompt }],\n },\n ],\n };\n\n if (this.reasoningEffort) {\n requestParams.reasoning = { effort: this.reasoningEffort };\n }\n\n const response = await client.responses.create(requestParams);\n\n if (response.status && response.status !== \"completed\") {\n const detail = response.incomplete_details?.reason\n ? ` (${response.incomplete_details.reason})`\n : \"\";\n throw new VisualAITruncationError(\n `Response truncated: OpenAI returned status \"${response.status}\"${detail}. The model exhausted the output token budget (${this.maxTokens} tokens). This commonly happens with higher reasoning effort levels. Increase maxTokens in your config (e.g., maxTokens: 16384) or lower reasoningEffort.`,\n response.output_text ?? \"\",\n this.maxTokens,\n );\n }\n\n const text = response.output_text ?? \"\";\n const reasoningTokens = response.usage?.output_tokens_details?.reasoning_tokens;\n\n return {\n text,\n usage: response.usage\n ? {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n ...(reasoningTokens !== undefined && { reasoningTokens }),\n }\n : undefined,\n };\n } catch (err) {\n if (err instanceof VisualAITruncationError) throw err;\n throw mapProviderError(err);\n }\n }\n}\n","import {\n DEFAULT_MAX_TOKENS,\n DEFAULT_MODELS,\n MODEL_TO_PROVIDER,\n OPENAI_REASONING_MAX_TOKENS,\n} from \"../constants.js\";\nimport { VisualAIConfigError } from \"../errors.js\";\nimport type { ProviderName, VisualAIConfig } from \"../types.js\";\n\nexport interface ResolvedConfig {\n provider: ProviderName;\n apiKey: string | undefined;\n model: string;\n maxTokens: number;\n reasoningEffort: VisualAIConfig[\"reasoningEffort\"];\n debug: boolean;\n debugPrompt: boolean;\n debugResponse: boolean;\n trackUsage: boolean;\n}\n\nconst MODEL_PREFIX_TO_PROVIDER: [string, ProviderName][] = [\n [\"claude-\", \"anthropic\"],\n [\"gpt-\", \"openai\"],\n [\"o1-\", \"openai\"],\n [\"o3-\", \"openai\"],\n [\"o4-\", \"openai\"],\n [\"gemini-\", \"google\"],\n];\n\nfunction inferProviderFromModel(model: string): ProviderName | undefined {\n const known = MODEL_TO_PROVIDER.get(model);\n if (known) return known;\n\n const prefixMatch = MODEL_PREFIX_TO_PROVIDER.find(([prefix]) => model.startsWith(prefix));\n return prefixMatch?.[1];\n}\n\nfunction resolveProvider(config: VisualAIConfig): ProviderName {\n const model = config.model ?? process.env.VISUAL_AI_MODEL;\n if (model) {\n const inferred = inferProviderFromModel(model);\n if (inferred) return inferred;\n }\n\n const apiKeyProviderMap: [string, ProviderName][] = [\n [\"ANTHROPIC_API_KEY\", \"anthropic\"],\n [\"OPENAI_API_KEY\", \"openai\"],\n [\"GOOGLE_API_KEY\", \"google\"],\n ];\n const detected = apiKeyProviderMap.find(([key]) => process.env[key]);\n if (detected) return detected[1];\n\n throw new VisualAIConfigError(\n \"Cannot determine provider. Set a model name (config or VISUAL_AI_MODEL) or an API key env variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY).\",\n );\n}\n\nfunction parseBooleanEnv(envName: string, value: string | undefined): boolean | undefined {\n if (value === undefined || value === \"\") return undefined;\n const lower = value.toLowerCase();\n if (lower === \"true\" || lower === \"1\") return true;\n if (lower === \"false\" || lower === \"0\") return false;\n throw new VisualAIConfigError(\n `Invalid ${envName} value: \"${value}\". Use \"true\", \"1\", \"false\", or \"0\".`,\n );\n}\n\nlet debugDeprecationWarned = false;\n\n/** @internal Reset the deprecation warning guard. For testing only. */\nexport function resetDebugDeprecationWarning(): void {\n debugDeprecationWarned = false;\n}\n\nexport function resolveConfig(config: VisualAIConfig): ResolvedConfig {\n const provider = resolveProvider(config);\n const model = config.model ?? process.env.VISUAL_AI_MODEL ?? DEFAULT_MODELS[provider];\n const debug =\n config.debug ?? parseBooleanEnv(\"VISUAL_AI_DEBUG\", process.env.VISUAL_AI_DEBUG) ?? false;\n const debugPrompt =\n config.debugPrompt ??\n parseBooleanEnv(\"VISUAL_AI_DEBUG_PROMPT\", process.env.VISUAL_AI_DEBUG_PROMPT) ??\n false;\n const debugResponse =\n config.debugResponse ??\n parseBooleanEnv(\"VISUAL_AI_DEBUG_RESPONSE\", process.env.VISUAL_AI_DEBUG_RESPONSE) ??\n false;\n\n if (debug && !debugPrompt && !debugResponse && !debugDeprecationWarned) {\n debugDeprecationWarned = true;\n process.stderr.write(\n `[visual-ai-assertions] Warning: VISUAL_AI_DEBUG no longer enables prompt/response logging. Use VISUAL_AI_DEBUG_PROMPT=true and/or VISUAL_AI_DEBUG_RESPONSE=true instead.\\n`,\n );\n }\n\n const userSetMaxTokens = config.maxTokens !== undefined;\n let maxTokens = config.maxTokens ?? DEFAULT_MAX_TOKENS;\n\n // OpenAI reasoning tokens share the output budget, so auto-increase for high/xhigh\n if (\n !userSetMaxTokens &&\n provider === \"openai\" &&\n (config.reasoningEffort === \"high\" || config.reasoningEffort === \"xhigh\")\n ) {\n maxTokens = OPENAI_REASONING_MAX_TOKENS;\n if (debug) {\n process.stderr.write(\n `[visual-ai-assertions] Auto-increased maxTokens from ${DEFAULT_MAX_TOKENS} to ${OPENAI_REASONING_MAX_TOKENS} for OpenAI with reasoningEffort \"${config.reasoningEffort}\".\\n`,\n );\n }\n }\n\n return {\n provider,\n apiKey: config.apiKey,\n model,\n maxTokens,\n reasoningEffort: config.reasoningEffort,\n debug,\n debugPrompt,\n debugResponse,\n trackUsage:\n config.trackUsage ??\n parseBooleanEnv(\"VISUAL_AI_TRACK_USAGE\", process.env.VISUAL_AI_TRACK_USAGE) ??\n false,\n };\n}\n","import { Model, Provider } from \"../constants.js\";\nimport type { ProviderName } from \"../types.js\";\n\ninterface ModelPricing {\n inputPricePerToken: number;\n outputPricePerToken: number;\n}\n\nconst PER_MILLION = 1_000_000;\n\nconst PRICING_TABLE: Record<string, ModelPricing> = {\n [`${Provider.ANTHROPIC}:${Model.Anthropic.OPUS_4_6}`]: {\n inputPricePerToken: 5 / PER_MILLION,\n outputPricePerToken: 25 / PER_MILLION,\n },\n [`${Provider.ANTHROPIC}:${Model.Anthropic.SONNET_4_6}`]: {\n inputPricePerToken: 3 / PER_MILLION,\n outputPricePerToken: 15 / PER_MILLION,\n },\n [`${Provider.ANTHROPIC}:${Model.Anthropic.HAIKU_4_5}`]: {\n inputPricePerToken: 1 / PER_MILLION,\n outputPricePerToken: 5 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4}`]: {\n inputPricePerToken: 2.5 / PER_MILLION,\n outputPricePerToken: 15 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_PRO}`]: {\n inputPricePerToken: 30 / PER_MILLION,\n outputPricePerToken: 180 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_2}`]: {\n inputPricePerToken: 1.75 / PER_MILLION,\n outputPricePerToken: 14 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_MINI}`]: {\n inputPricePerToken: 0.75 / PER_MILLION,\n outputPricePerToken: 4.5 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_NANO}`]: {\n inputPricePerToken: 0.2 / PER_MILLION,\n outputPricePerToken: 1.25 / PER_MILLION,\n },\n [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_MINI}`]: {\n inputPricePerToken: 0.25 / PER_MILLION,\n outputPricePerToken: 2 / PER_MILLION,\n },\n [`${Provider.GOOGLE}:${Model.Google.GEMINI_3_1_PRO_PREVIEW}`]: {\n inputPricePerToken: 2 / PER_MILLION,\n outputPricePerToken: 12 / PER_MILLION,\n },\n [`${Provider.GOOGLE}:${Model.Google.GEMINI_3_1_FLASH_LITE_PREVIEW}`]: {\n inputPricePerToken: 0.25 / PER_MILLION,\n outputPricePerToken: 1.5 / PER_MILLION,\n },\n [`${Provider.GOOGLE}:${Model.Google.GEMINI_3_FLASH_PREVIEW}`]: {\n inputPricePerToken: 0.5 / PER_MILLION,\n outputPricePerToken: 3 / PER_MILLION,\n },\n};\n\nexport function calculateCost(\n provider: ProviderName,\n model: string,\n inputTokens: number,\n outputTokens: number,\n): number | undefined {\n const key = `${provider}:${model}`;\n const pricing = PRICING_TABLE[key];\n if (!pricing) return undefined;\n\n return inputTokens * pricing.inputPricePerToken + outputTokens * pricing.outputPricePerToken;\n}\n","import { PROVIDER_DEFAULT_REASONING } from \"../constants.js\";\nimport { calculateCost } from \"./pricing.js\";\nimport type { ResolvedConfig } from \"./config.js\";\nimport type {\n ProviderDriver,\n RawProviderResponse,\n SendMessageOptions,\n} from \"../providers/types.js\";\nimport type { NormalizedImage, UsageInfo } from \"../types.js\";\nimport { VisualAIError, VisualAIResponseParseError, VisualAITruncationError } from \"../errors.js\";\n\nexport type DebugLogKind = \"prompt\" | \"response\" | \"error\";\n\nexport function debugLog(\n config: ResolvedConfig,\n label: string,\n data: string,\n kind: DebugLogKind = \"error\",\n): void {\n const enabled =\n kind === \"prompt\"\n ? config.debugPrompt\n : kind === \"response\"\n ? config.debugResponse\n : config.debug;\n\n if (enabled) {\n process.stderr.write(`[visual-ai-assertions] ${label}: ${data}\\n`);\n }\n}\n\nexport function usageLog(config: ResolvedConfig, method: string, usage: UsageInfo): void {\n if (!config.trackUsage) return;\n const costStr =\n usage.estimatedCost !== undefined ? `$${usage.estimatedCost.toFixed(6)}` : \"unknown\";\n const reasoningStr = config.reasoningEffort\n ? `reasoning: ${config.reasoningEffort}`\n : `reasoning: ${PROVIDER_DEFAULT_REASONING[config.provider]} (provider default)`;\n const reasoningTokenStr =\n usage.reasoningTokens !== undefined ? ` (${usage.reasoningTokens} reasoning)` : \"\";\n process.stderr.write(\n `[visual-ai-assertions] ${method} usage: ${usage.inputTokens} input + ${usage.outputTokens} output${reasoningTokenStr} tokens (${costStr}) in ${usage.durationSeconds?.toFixed(3) ?? \"0.000\"}s [${config.model}, ${reasoningStr}]\\n`,\n );\n}\n\nexport function processUsage(\n method: string,\n rawUsage: RawProviderResponse[\"usage\"],\n durationSeconds: number,\n config: ResolvedConfig,\n): UsageInfo {\n const inputTokens = rawUsage?.inputTokens ?? 0;\n const outputTokens = rawUsage?.outputTokens ?? 0;\n const usage: UsageInfo = {\n inputTokens,\n outputTokens,\n ...(rawUsage?.reasoningTokens !== undefined && { reasoningTokens: rawUsage.reasoningTokens }),\n estimatedCost: calculateCost(config.provider, config.model, inputTokens, outputTokens),\n durationSeconds,\n };\n usageLog(config, method, usage);\n return usage;\n}\n\nconst MAX_RAW_RESPONSE_PREVIEW = 500;\n\nexport function formatError(error: unknown): string {\n if (error instanceof VisualAITruncationError) {\n const preview =\n error.partialResponse.length > MAX_RAW_RESPONSE_PREVIEW\n ? error.partialResponse.slice(0, MAX_RAW_RESPONSE_PREVIEW) + \"...\"\n : error.partialResponse;\n return `${error.name} (${error.code}): ${error.message}. Partial response: ${preview}`;\n }\n if (error instanceof VisualAIResponseParseError) {\n const truncated =\n error.rawResponse.length > MAX_RAW_RESPONSE_PREVIEW\n ? error.rawResponse.slice(0, MAX_RAW_RESPONSE_PREVIEW) + \"...\"\n : error.rawResponse;\n return `${error.name} (${error.code}): ${error.message}. Raw (truncated): ${truncated}`;\n }\n if (error instanceof VisualAIError) {\n return `${error.name} (${error.code}): ${error.message}`;\n }\n if (error instanceof Error) {\n return `${error.name}: ${error.message}`;\n }\n return String(error);\n}\n\nexport async function withErrorDebug<T>(\n config: ResolvedConfig,\n method: string,\n fn: () => Promise<T>,\n): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n debugLog(config, `${method} error`, formatError(error), \"error\");\n throw error;\n }\n}\n\nexport async function timedSendMessage(\n driver: ProviderDriver,\n images: NormalizedImage[],\n prompt: string,\n options?: SendMessageOptions,\n): Promise<RawProviderResponse & { durationSeconds: number }> {\n const start = performance.now();\n const response = await driver.sendMessage(images, prompt, options);\n const durationSeconds = (performance.now() - start) / 1000;\n return { ...response, durationSeconds };\n}\n","import { Model } from \"../constants.js\";\nimport sharp from \"sharp\";\nimport { VisualAIConfigError } from \"../errors.js\";\nimport type { DiffImageResult, NormalizedImage } from \"../types.js\";\nimport { buildAiDiffPrompt } from \"./prompt.js\";\n\ninterface ImageGenerationDriver {\n generateImage?: (\n images: NormalizedImage[],\n prompt: string,\n options?: { model?: string; promptKind?: \"ai-diff\" },\n ) => Promise<{\n imageData: Buffer;\n mimeType: string;\n }>;\n}\n\nexport async function generateAiDiff(\n imgA: NormalizedImage,\n imgB: NormalizedImage,\n model: string,\n driver: ImageGenerationDriver,\n): Promise<DiffImageResult> {\n if (!driver.generateImage) {\n throw new VisualAIConfigError(\n \"AI-generated diff images require a provider that supports image generation. Currently only the Google (Gemini) provider supports this.\",\n );\n }\n\n if (model !== Model.Google.GEMINI_3_FLASH_PREVIEW) {\n throw new VisualAIConfigError(\n \"Annotated diff images are only supported when visualAI is configured with the Google model gemini-3-flash-preview.\",\n );\n }\n\n const response = await driver.generateImage([imgA, imgB], buildAiDiffPrompt(), {\n model,\n promptKind: \"ai-diff\",\n });\n\n const img = sharp(response.imageData);\n const meta = await img.metadata();\n const pngData = await img.png().toBuffer();\n\n return {\n data: pngData,\n width: meta.width ?? 0,\n height: meta.height ?? 0,\n mimeType: \"image/png\",\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\nimport sharp from \"sharp\";\nimport { VisualAIImageError } from \"../errors.js\";\nimport type { NormalizedImage, SupportedMimeType } from \"../types.js\";\n\nconst SUPPORTED_FORMATS: ReadonlySet<SupportedMimeType> = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/webp\",\n \"image/gif\",\n]);\n\nconst EXTENSION_TO_MIME: Record<string, SupportedMimeType> = {\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".webp\": \"image/webp\",\n \".gif\": \"image/gif\",\n};\n\nconst MAX_DIMENSION = 1568;\nconst URL_FETCH_TIMEOUT_MS = 10_000;\n\nfunction isSupportedMimeType(value: string): value is SupportedMimeType {\n return SUPPORTED_FORMATS.has(value as SupportedMimeType);\n}\n\nfunction getMimeFromExtension(filePath: string): SupportedMimeType | undefined {\n const ext = extname(filePath).toLowerCase();\n return EXTENSION_TO_MIME[ext];\n}\n\nfunction isFilePath(input: string): boolean {\n return (\n input.startsWith(\"/\") ||\n input.startsWith(\"./\") ||\n input.startsWith(\"../\") ||\n input.includes(\"\\\\\")\n );\n}\n\nfunction isUrl(input: string): boolean {\n return input.startsWith(\"http://\") || input.startsWith(\"https://\");\n}\n\nfunction isBase64Image(input: string): boolean {\n return (\n input.startsWith(\"iVBOR\") || // PNG (0x89 0x50 0x4E 0x47)\n input.startsWith(\"/9j/\") || // JPEG (0xFF 0xD8 0xFF)\n input.startsWith(\"R0lGOD\") || // GIF (0x47 0x49 0x46)\n input.startsWith(\"UklGR\") // WebP (0x52 0x49 0x46 0x46)\n );\n}\n\nfunction detectMimeType(data: Buffer): SupportedMimeType {\n // Check magic bytes\n if (data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff) {\n return \"image/jpeg\";\n }\n if (data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4e && data[3] === 0x47) {\n return \"image/png\";\n }\n if (\n data[0] === 0x52 &&\n data[1] === 0x49 &&\n data[2] === 0x46 &&\n data[3] === 0x46 &&\n data[8] === 0x57 &&\n data[9] === 0x45 &&\n data[10] === 0x42 &&\n data[11] === 0x50\n ) {\n return \"image/webp\";\n }\n if (data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46) {\n return \"image/gif\";\n }\n\n throw new VisualAIImageError(\"Unable to detect image format from file content\");\n}\n\nasync function resizeIfNeeded(data: Buffer, mimeType: SupportedMimeType): Promise<Buffer> {\n if (mimeType === \"image/gif\") {\n return data;\n }\n\n // Fast path for PNG: read dimensions directly from header bytes\n if (mimeType === \"image/png\" && data.length >= 24) {\n const width = data.readUInt32BE(16);\n const height = data.readUInt32BE(20);\n if (width <= MAX_DIMENSION && height <= MAX_DIMENSION) {\n return data;\n }\n }\n\n // Fall back to sharp for other formats or when resizing is needed\n const pipeline = sharp(data);\n const metadata = await pipeline.metadata();\n const width = metadata.width ?? 0;\n const height = metadata.height ?? 0;\n\n if (width <= MAX_DIMENSION && height <= MAX_DIMENSION) {\n return data;\n }\n\n return pipeline\n .resize({\n width: MAX_DIMENSION,\n height: MAX_DIMENSION,\n fit: \"inside\",\n withoutEnlargement: true,\n })\n .toBuffer();\n}\n\nasync function loadFromFilePath(\n filePath: string,\n): Promise<{ data: Buffer; mimeType: SupportedMimeType }> {\n let fileData: Buffer;\n try {\n fileData = await readFile(filePath);\n } catch (err) {\n throw new VisualAIImageError(\n `Failed to read image file: ${filePath} — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const mimeType = getMimeFromExtension(filePath) ?? detectMimeType(fileData);\n return { data: fileData, mimeType };\n}\n\nasync function loadFromUrl(url: string): Promise<{ data: Buffer; mimeType: SupportedMimeType }> {\n let response: Response;\n try {\n response = await fetch(url, {\n signal: AbortSignal.timeout(URL_FETCH_TIMEOUT_MS),\n });\n } catch (err) {\n throw new VisualAIImageError(\n `Failed to fetch image from URL: ${url} — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (!response.ok) {\n throw new VisualAIImageError(\n `Failed to fetch image from URL: ${url} — HTTP ${response.status}`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const data = Buffer.from(arrayBuffer);\n const contentType = response.headers.get(\"content-type\")?.split(\";\")[0]?.trim() ?? null;\n const mimeType =\n contentType && isSupportedMimeType(contentType) ? contentType : detectMimeType(data);\n\n return { data, mimeType };\n}\n\nfunction loadFromBase64(input: string): { data: Buffer; mimeType: SupportedMimeType } {\n // Handle data URLs: data:image/png;base64,iVBOR...\n let base64Data = input;\n let mimeType: SupportedMimeType | undefined;\n\n if (input.startsWith(\"data:\")) {\n const match = /^data:(image\\/[^;]+);base64,(.+)$/.exec(input);\n if (!match?.[1] || !match[2]) {\n throw new VisualAIImageError(\"Invalid data URL format\");\n }\n if (!isSupportedMimeType(match[1])) {\n throw new VisualAIImageError(`Unsupported image format: ${match[1]}`);\n }\n mimeType = match[1];\n base64Data = match[2];\n }\n\n // Validate base64 characters\n if (!/^[A-Za-z0-9+/\\n\\r]+=*$/.test(base64Data)) {\n throw new VisualAIImageError(\"Invalid base64 string\");\n }\n\n const data = Buffer.from(base64Data, \"base64\");\n\n if (data.length === 0) {\n throw new VisualAIImageError(\"Empty image data after base64 decode\");\n }\n\n return { data, mimeType: mimeType ?? detectMimeType(data) };\n}\n\nexport async function normalizeImage(\n input: Buffer | Uint8Array | string,\n): Promise<NormalizedImage> {\n let data: Buffer;\n let mimeType: SupportedMimeType;\n\n if (Buffer.isBuffer(input)) {\n mimeType = detectMimeType(input);\n data = input;\n } else if (input instanceof Uint8Array) {\n const buf = Buffer.from(input);\n mimeType = detectMimeType(buf);\n data = buf;\n } else if (typeof input === \"string\") {\n if (isUrl(input)) {\n ({ data, mimeType } = await loadFromUrl(input));\n } else if (input.startsWith(\"data:\")) {\n ({ data, mimeType } = loadFromBase64(input));\n } else if (isBase64Image(input)) {\n ({ data, mimeType } = loadFromBase64(input));\n } else if (isFilePath(input)) {\n ({ data, mimeType } = await loadFromFilePath(input));\n } else {\n throw new VisualAIImageError(\n `Unrecognized image input: \"${input.slice(0, 80)}\". ` +\n `Expected a file path, URL, data URL, or base64-encoded image string.`,\n );\n }\n } else {\n throw new VisualAIImageError(\n \"Invalid image input: expected Buffer, Uint8Array, file path, URL, or base64 string\",\n );\n }\n\n data = await resizeIfNeeded(data, mimeType);\n\n let cachedBase64: string | undefined;\n return {\n data,\n mimeType,\n get base64(): string {\n if (cachedBase64 === undefined) {\n cachedBase64 = data.toString(\"base64\");\n }\n return cachedBase64;\n },\n };\n}\n","import { z } from \"zod\";\nimport type {\n AccessibilityCheckName,\n ContentCheckName,\n LayoutCheckName,\n ReasoningEffortLevel,\n} from \"./constants.js\";\n\n// --- Issue types ---\n\n/** Zod schema for issue severity levels returned by checks and questions. */\nexport const IssuePrioritySchema = z.enum([\"critical\", \"major\", \"minor\"]);\n/** Severity level for a detected visual issue. */\nexport type IssuePriority = z.infer<typeof IssuePrioritySchema>;\n\n/** Zod schema for the categories assigned to detected visual issues. */\nexport const IssueCategorySchema = z.enum([\n \"accessibility\",\n \"missing-element\",\n \"layout\",\n \"content\",\n \"styling\",\n \"functionality\",\n \"performance\",\n \"other\",\n]);\n/** Category assigned to a detected visual issue. */\nexport type IssueCategory = z.infer<typeof IssueCategorySchema>;\n\n/** Zod schema for a structured issue reported by the model. */\nexport const IssueSchema = z.object({\n priority: IssuePrioritySchema,\n category: IssueCategorySchema,\n description: z.string(),\n suggestion: z.string(),\n});\n/** Structured issue reported by a visual check or question. */\nexport type Issue = z.infer<typeof IssueSchema>;\n\n// --- Per-statement result (for check) ---\n\n/** Zod schema for model confidence labels on statement-level results. */\nexport const ConfidenceSchema = z.enum([\"high\", \"medium\", \"low\"]);\n/** Confidence level attached to a statement result. */\nexport type Confidence = z.infer<typeof ConfidenceSchema>;\n\n/** Zod schema for an individual statement evaluation within `check()`. */\nexport const StatementResultSchema = z.object({\n statement: z.string(),\n pass: z.boolean(),\n reasoning: z.string(),\n confidence: ConfidenceSchema.optional(),\n});\n/** Outcome of a single statement evaluated by `check()`. */\nexport type StatementResult = z.infer<typeof StatementResultSchema>;\n\n// --- Usage info ---\n\n/** Zod schema for token and latency metadata attached to API calls. */\nexport const UsageInfoSchema = z.object({\n inputTokens: z.number(),\n outputTokens: z.number(),\n /** Reasoning/thinking tokens consumed by the model (informational, typically included within outputTokens). */\n reasoningTokens: z.number().optional(),\n estimatedCost: z.number().optional(),\n durationSeconds: z.number().nonnegative().optional(),\n});\n/** Token usage and optional cost/latency metadata for a provider call. */\nexport type UsageInfo = z.infer<typeof UsageInfoSchema>;\n\n// --- Base result (shared fields) ---\n\nconst BaseResultSchema = z.object({\n pass: z.boolean(),\n reasoning: z.string(),\n usage: UsageInfoSchema.optional(),\n});\n\n// --- check() / template result ---\n\n/** Zod schema for results returned by `check()` and template helpers. */\nexport const CheckResultSchema = BaseResultSchema.extend({\n issues: z.array(IssueSchema),\n statements: z.array(StatementResultSchema),\n});\n/** Result returned by `check()` and the template convenience methods. */\nexport type CheckResult = z.infer<typeof CheckResultSchema>;\n\n// --- compare() result ---\n\n/** Zod schema for an individual visual change reported by `compare()`. */\nexport const ChangeEntrySchema = z.object({\n description: z.string(),\n severity: IssuePrioritySchema,\n});\n/** Single visual change reported by `compare()`. */\nexport type ChangeEntry = z.infer<typeof ChangeEntrySchema>;\n\n/** Zod schema for the parsed model response returned by `compare()`. */\nexport const CompareResultSchema = BaseResultSchema.extend({\n changes: z.array(ChangeEntrySchema).max(50),\n});\n// diffImage is appended client-side after the AI response is parsed,\n// so it intentionally does not appear in CompareResultSchema.\n/** Result returned by `compare()`, optionally including an AI-generated diff image. */\nexport type CompareResult = z.infer<typeof CompareResultSchema> & {\n diffImage?: DiffImageResult;\n};\n\n// --- ask() result ---\n\n/** Zod schema for results returned by `ask()`. */\nexport const AskResultSchema = z.object({\n summary: z.string(),\n issues: z.array(IssueSchema),\n usage: UsageInfoSchema.optional(),\n});\n/** Result returned by `ask()`. */\nexport type AskResult = z.infer<typeof AskResultSchema>;\n\n// --- Image input ---\n\n/** Supported input shapes for image arguments accepted by the client. */\nexport type ImageInput = Buffer | Uint8Array | string;\n\n/** Supported image MIME types accepted by all providers. */\nexport type SupportedMimeType = \"image/jpeg\" | \"image/png\" | \"image/webp\" | \"image/gif\";\n\n// --- Provider names ---\n\n/** Supported provider identifiers. */\nexport type ProviderName = \"anthropic\" | \"openai\" | \"google\";\n\n// --- VisualAI config ---\n\n/**\n * Configuration for creating a visual AI client.\n *\n * @example\n * ```ts\n * const client = visualAI({\n * model: \"gpt-5-mini\",\n * apiKey: process.env.OPENAI_API_KEY,\n * });\n * ```\n */\nexport interface VisualAIConfig {\n apiKey?: string;\n model?: string;\n /** Enable error diagnostic logging to stderr. Does not enable prompt/response logging — use `debugPrompt` and `debugResponse` for that. */\n debug?: boolean;\n /** Log prompts to stderr. */\n debugPrompt?: boolean;\n /** Log responses to stderr. */\n debugResponse?: boolean;\n maxTokens?: number;\n reasoningEffort?: ReasoningEffortLevel;\n trackUsage?: boolean;\n}\n\n// --- Template option types ---\n\n/** Optional instructions for `check()`. */\nexport interface CheckOptions {\n instructions?: readonly string[];\n}\n\n/** Optional instructions for `ask()`. */\nexport interface AskOptions {\n instructions?: readonly string[];\n}\n\n// --- Diff image types ---\n\n/** Metadata and binary content for an AI-generated diff image. */\nexport interface DiffImageResult {\n data: Buffer;\n width: number;\n height: number;\n mimeType: \"image/png\";\n}\n\n/** Optional prompt, instructions, and diff configuration for `compare()`. */\nexport interface CompareOptions {\n prompt?: string;\n instructions?: readonly string[];\n diffImage?: boolean;\n}\n\n/** Optional instructions for `elementsVisible()` and `elementsHidden()`. */\nexport interface ElementsVisibilityOptions {\n instructions?: readonly string[];\n}\n\n/** Options for the built-in accessibility template. */\nexport interface AccessibilityOptions {\n checks?: AccessibilityCheckName[];\n instructions?: readonly string[];\n}\n\n/** Options for the built-in layout template. */\nexport interface LayoutOptions {\n checks?: LayoutCheckName[];\n instructions?: readonly string[];\n}\n\n/** Options for the built-in page-load template. */\nexport interface PageLoadOptions {\n expectLoaded?: boolean;\n instructions?: readonly string[];\n}\n\n/** Options for the built-in content template. */\nexport interface ContentOptions {\n checks?: ContentCheckName[];\n instructions?: readonly string[];\n}\n\n// --- Normalized image (internal) ---\n\n/** Internal normalized image representation passed to provider drivers. */\nexport interface NormalizedImage {\n readonly data: Buffer;\n readonly mimeType: SupportedMimeType;\n readonly base64: string;\n}\n","import { z } from \"zod\";\nimport { VisualAIResponseParseError } from \"../errors.js\";\nimport { AskResultSchema, CheckResultSchema, CompareResultSchema } from \"../types.js\";\nimport type { AskResult, CheckResult, CompareResult } from \"../types.js\";\n\nfunction stripCodeFences(text: string): string {\n const trimmed = text.trim();\n const match = /^```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```$/s.exec(trimmed);\n return match?.[1] ?? trimmed;\n}\n\nexport const CheckResponseSchema = CheckResultSchema.omit({ usage: true });\nexport const AskResponseSchema = AskResultSchema.omit({ usage: true });\nexport const CompareResponseSchema = CompareResultSchema.omit({ usage: true });\n\nfunction parseResponse<T>(raw: string, schema: z.ZodType<T>): T {\n let parsed: unknown;\n try {\n parsed = JSON.parse(stripCodeFences(raw));\n } catch {\n throw new VisualAIResponseParseError(\n `Failed to parse AI response as JSON: ${raw.slice(0, 200)}`,\n raw,\n );\n }\n\n const result = schema.safeParse(parsed);\n if (!result.success) {\n throw new VisualAIResponseParseError(\n `AI response does not match expected schema: ${result.error.message}`,\n raw,\n );\n }\n\n return result.data;\n}\n\nfunction reconcileCheckResult(result: Omit<CheckResult, \"usage\">): Omit<CheckResult, \"usage\"> {\n if (result.statements.length === 0) {\n return result;\n }\n\n const passCount = result.statements.filter((s) => s.pass).length;\n const total = result.statements.length;\n const computedPass = passCount === total;\n const countPrefix = `${passCount} of ${total} checks passed`;\n const reasoning = `${countPrefix}. ${result.reasoning}`;\n\n return {\n ...result,\n pass: computedPass,\n reasoning,\n };\n}\n\nexport function parseCheckResponse(raw: string): Omit<CheckResult, \"usage\"> {\n const result = parseResponse(raw, CheckResponseSchema);\n return reconcileCheckResult(result);\n}\n\nexport function parseAskResponse(raw: string): Omit<AskResult, \"usage\"> {\n return parseResponse(raw, AskResponseSchema);\n}\n\nexport function parseCompareResponse(raw: string): Omit<CompareResult, \"usage\"> {\n return parseResponse(raw, CompareResponseSchema);\n}\n","import { Model } from \"../constants.js\";\nimport { VisualAIConfigError } from \"../errors.js\";\nimport {\n buildAccessibilityPrompt,\n buildContentPrompt,\n buildElementsVisibilityPrompt,\n buildLayoutPrompt,\n buildPageLoadPrompt,\n} from \"../templates/index.js\";\nimport type {\n AccessibilityOptions,\n AskOptions,\n AskResult,\n CheckOptions,\n CheckResult,\n CompareOptions,\n CompareResult,\n ContentOptions,\n ElementsVisibilityOptions,\n ImageInput,\n LayoutOptions,\n PageLoadOptions,\n ProviderName,\n VisualAIConfig,\n} from \"../types.js\";\nimport { AnthropicDriver } from \"../providers/anthropic.js\";\nimport { GoogleDriver } from \"../providers/google.js\";\nimport { OpenAIDriver } from \"../providers/openai.js\";\nimport type { ProviderConfig, ProviderDriver, SendMessageOptions } from \"../providers/types.js\";\nimport { resolveConfig } from \"./config.js\";\nimport { debugLog, processUsage, timedSendMessage, withErrorDebug } from \"./debug.js\";\nimport { generateAiDiff } from \"./diff.js\";\nimport { normalizeImage } from \"./image.js\";\nimport { buildAskPrompt, buildCheckPrompt, buildComparePrompt } from \"./prompt.js\";\nimport {\n AskResponseSchema,\n CheckResponseSchema,\n CompareResponseSchema,\n parseAskResponse,\n parseCheckResponse,\n parseCompareResponse,\n} from \"./response.js\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport type { z } from \"zod\";\n\nfunction toSchemaOptions(schema: z.ZodType): SendMessageOptions {\n return {\n responseSchema: zodToJsonSchema(schema, { target: \"openAi\" }) as Record<string, unknown>,\n };\n}\n\n/**\n * High-level client for running visual checks against screenshots or other images.\n *\n * @example\n * ```ts\n * const client = visualAI({ model: \"gpt-5-mini\" });\n * const result = await client.check(\"./tests/fixtures/small.png\", \"The button is visible\");\n * ```\n */\nexport interface VisualAIClient {\n /**\n * Verifies one or more statements against a single image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param statements One or more statements to validate against the image.\n * @param options Optional additional instructions appended to the prompt.\n * @returns A structured result describing pass/fail, issues, and statement reasoning.\n * @throws {VisualAIConfigError} When no statements are provided.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * const result = await client.check(screenshot, [\n * \"The primary CTA is visible\",\n * \"There is no error banner\",\n * ]);\n * ```\n */\n check(\n image: ImageInput,\n statements: string | string[],\n options?: CheckOptions,\n ): Promise<CheckResult>;\n /**\n * Asks an open-ended question about an image and returns a structured summary.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param prompt Prompt describing what to inspect in the image.\n * @param options Optional additional instructions appended to the prompt.\n * @returns A summary with any detected issues.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * const result = await client.ask(screenshot, \"What looks visually broken on this page?\");\n * ```\n */\n ask(image: ImageInput, prompt: string, options?: AskOptions): Promise<AskResult>;\n /**\n * Compares two images and reports meaningful visual differences.\n *\n * @param imageA Baseline image source.\n * @param imageB Candidate image source.\n * @param options Optional comparison prompt, instructions, and diff-image settings.\n * `gemini-3-flash-preview` generates an annotated diff image by default;\n * pass `{ diffImage: false }` to opt out.\n * @returns A structured comparison result with optional diff image metadata.\n * @throws {VisualAIImageError} When either image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * const result = await client.compare(beforeScreenshot, afterScreenshot, {\n * diffImage: true,\n * });\n * ```\n */\n compare(imageA: ImageInput, imageB: ImageInput, options?: CompareOptions): Promise<CompareResult>;\n /**\n * Checks that the listed elements are visible in an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param elements Element descriptions that should be present and visible.\n * @param options Optional additional instructions appended to the prompt.\n * @returns A structured pass/fail result for the requested elements.\n * @throws {VisualAIConfigError} When `elements` is empty.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.elementsVisible(screenshot, [\"Save button\", \"Profile avatar\"]);\n * ```\n */\n elementsVisible(\n image: ImageInput,\n elements: string[],\n options?: ElementsVisibilityOptions,\n ): Promise<CheckResult>;\n /**\n * Checks that the listed elements are not visible in an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param elements Element descriptions that should be absent or hidden.\n * @param options Optional additional instructions appended to the prompt.\n * @returns A structured pass/fail result for the requested elements.\n * @throws {VisualAIConfigError} When `elements` is empty.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.elementsHidden(screenshot, [\"Cookie banner\"]);\n * ```\n */\n elementsHidden(\n image: ImageInput,\n elements: string[],\n options?: ElementsVisibilityOptions,\n ): Promise<CheckResult>;\n /**\n * Runs the built-in accessibility template against an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param options Optional checks and extra instructions for the accessibility prompt.\n * @returns A structured accessibility-focused check result.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.accessibility(screenshot, { checks: [\"contrast\"] });\n * ```\n */\n accessibility(image: ImageInput, options?: AccessibilityOptions): Promise<CheckResult>;\n /**\n * Runs the built-in layout template against an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param options Optional checks and extra instructions for the layout prompt.\n * @returns A structured layout-focused check result.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.layout(screenshot, { checks: [\"overflow\", \"alignment\"] });\n * ```\n */\n layout(image: ImageInput, options?: LayoutOptions): Promise<CheckResult>;\n /**\n * Runs the built-in page-load template against an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param options Optional page-load expectations and extra instructions.\n * @returns A structured page-load check result.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.pageLoad(screenshot, { expectLoaded: true });\n * ```\n */\n pageLoad(image: ImageInput, options?: PageLoadOptions): Promise<CheckResult>;\n /**\n * Runs the built-in content template against an image.\n *\n * @param image Image source as a buffer, URL, file path, or base64 string.\n * @param options Optional content checks and extra instructions.\n * @returns A structured content-focused check result.\n * @throws {VisualAIImageError} When the image cannot be loaded or decoded.\n * @throws {VisualAIError} When the provider rejects the request or returns invalid output.\n * @example\n * ```ts\n * await client.content(screenshot, { checks: [\"placeholder-text\"] });\n * ```\n */\n content(image: ImageInput, options?: ContentOptions): Promise<CheckResult>;\n}\n\ntype ProviderFactory = (config: ProviderConfig) => ProviderDriver;\n\nconst PROVIDER_REGISTRY = {\n anthropic: (config) => new AnthropicDriver(config),\n openai: (config) => new OpenAIDriver(config),\n google: (config) => new GoogleDriver(config),\n} as const satisfies Record<ProviderName, ProviderFactory>;\n\nfunction createDriver(provider: ProviderName, config: ProviderConfig): ProviderDriver {\n return PROVIDER_REGISTRY[provider](config);\n}\n\nconst checkSchemaOptions = toSchemaOptions(CheckResponseSchema);\nconst askSchemaOptions = toSchemaOptions(AskResponseSchema);\nconst compareSchemaOptions = toSchemaOptions(CompareResponseSchema);\n\n/**\n * Creates a configured visual AI client.\n *\n * @param config Model selection and runtime options for subsequent requests.\n * @returns A `VisualAIClient` instance with check, compare, ask, and template helpers.\n * @throws {VisualAIConfigError} When the provider or model configuration is invalid.\n * @throws {VisualAIAuthError} When required API credentials are missing.\n * @example\n * ```ts\n * import { expect, test } from \"@playwright/test\";\n * import { visualAI } from \"visual-ai-assertions\";\n *\n * test(\"hero loads correctly\", async ({ page }) => {\n * const client = visualAI({\n * model: \"gpt-5-mini\",\n * apiKey: process.env.OPENAI_API_KEY,\n * });\n *\n * await page.goto(\"https://example.com\");\n * const screenshot = await page.screenshot();\n * const result = await client.check(screenshot, [\n * \"The hero heading is visible\",\n * \"There is no loading spinner\",\n * ]);\n *\n * expect(result.pass).toBe(true);\n * });\n * ```\n */\nexport function visualAI(config: VisualAIConfig = {}): VisualAIClient {\n const resolvedConfig = resolveConfig(config);\n const driverConfig: ProviderConfig = {\n apiKey: resolvedConfig.apiKey,\n model: resolvedConfig.model,\n maxTokens: resolvedConfig.maxTokens,\n reasoningEffort: resolvedConfig.reasoningEffort,\n };\n const driver = createDriver(resolvedConfig.provider, driverConfig);\n\n async function checkElementsVisibility(\n image: ImageInput,\n elements: string[],\n visible: boolean,\n options?: ElementsVisibilityOptions,\n ): Promise<CheckResult> {\n const methodName = visible ? \"elementsVisible\" : \"elementsHidden\";\n if (elements.length === 0) {\n throw new VisualAIConfigError(`At least one element is required for ${methodName}()`);\n }\n\n return withErrorDebug(resolvedConfig, methodName, async () => {\n const img = await normalizeImage(image);\n const prompt = buildElementsVisibilityPrompt(elements, visible, options);\n debugLog(resolvedConfig, `${methodName} prompt`, prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, `${methodName} response`, response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(methodName, response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n }\n\n return {\n async check(image, statements, options) {\n const stmts = Array.isArray(statements) ? statements : [statements];\n if (stmts.length === 0) {\n throw new VisualAIConfigError(\"At least one statement is required for check()\");\n }\n\n return withErrorDebug(resolvedConfig, \"check\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildCheckPrompt(stmts, { instructions: options?.instructions });\n debugLog(resolvedConfig, \"check prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"check response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"check\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n async ask(image, userPrompt, options) {\n return withErrorDebug(resolvedConfig, \"ask\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildAskPrompt(userPrompt, { instructions: options?.instructions });\n debugLog(resolvedConfig, \"ask prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, askSchemaOptions);\n debugLog(resolvedConfig, \"ask response\", response.text, \"response\");\n\n const result = parseAskResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"ask\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n async compare(imageA, imageB, options) {\n return withErrorDebug(resolvedConfig, \"compare\", async () => {\n const [imgA, imgB] = await Promise.all([normalizeImage(imageA), normalizeImage(imageB)]);\n const prompt = buildComparePrompt({\n userPrompt: options?.prompt,\n instructions: options?.instructions,\n });\n debugLog(resolvedConfig, \"compare prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [imgA, imgB], prompt, compareSchemaOptions);\n debugLog(resolvedConfig, \"compare response\", response.text, \"response\");\n\n const supportsAnnotatedDiff =\n resolvedConfig.provider === \"google\" &&\n resolvedConfig.model === Model.Google.GEMINI_3_FLASH_PREVIEW;\n const effectiveDiffImage = options?.diffImage ?? (supportsAnnotatedDiff ? true : false);\n\n let diffImage;\n if (effectiveDiffImage) {\n try {\n diffImage = await generateAiDiff(imgA, imgB, resolvedConfig.model, driver);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `[visual-ai-assertions] warning: diff generation failed: ${msg}\\n`,\n );\n }\n }\n\n const result = parseCompareResponse(response.text);\n return {\n ...result,\n ...(diffImage ? { diffImage } : {}),\n usage: processUsage(\"compare\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n elementsVisible(image, elements, options) {\n return checkElementsVisibility(image, elements, true, options);\n },\n\n elementsHidden(image, elements, options) {\n return checkElementsVisibility(image, elements, false, options);\n },\n\n async accessibility(image, options) {\n return withErrorDebug(resolvedConfig, \"accessibility\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildAccessibilityPrompt(options);\n debugLog(resolvedConfig, \"accessibility prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"accessibility response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\n \"accessibility\",\n response.usage,\n response.durationSeconds,\n resolvedConfig,\n ),\n };\n });\n },\n\n async layout(image, options) {\n return withErrorDebug(resolvedConfig, \"layout\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildLayoutPrompt(options);\n debugLog(resolvedConfig, \"layout prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"layout response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"layout\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n async pageLoad(image, options) {\n return withErrorDebug(resolvedConfig, \"pageLoad\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildPageLoadPrompt(options);\n debugLog(resolvedConfig, \"pageLoad prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"pageLoad response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"pageLoad\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n\n async content(image, options) {\n return withErrorDebug(resolvedConfig, \"content\", async () => {\n const img = await normalizeImage(image);\n const prompt = buildContentPrompt(options);\n debugLog(resolvedConfig, \"content prompt\", prompt, \"prompt\");\n\n const response = await timedSendMessage(driver, [img], prompt, checkSchemaOptions);\n debugLog(resolvedConfig, \"content response\", response.text, \"response\");\n\n const result = parseCheckResponse(response.text);\n return {\n ...result,\n usage: processUsage(\"content\", response.usage, response.durationSeconds, resolvedConfig),\n };\n });\n },\n };\n}\n","import type { CheckResult, CompareResult } from \"./types.js\";\nimport { VisualAIAssertionError } from \"./errors.js\";\n\n/**\n * Formats a check result into a readable multiline string for logs or test failures.\n *\n * @param result Structured result returned by `check()` or a template helper.\n * @param label Optional label appended to the output header.\n * @returns A human-readable summary of the check result.\n * @example\n * ```ts\n * console.log(formatCheckResult(result, \"Checkout page\"));\n * ```\n */\nexport function formatCheckResult(result: CheckResult, label?: string): string {\n if (result.pass) {\n const header = label ? `Visual AI Check Passed (${label})` : \"Visual AI Check Passed\";\n return `${header}\\n${\"=\".repeat(header.length)}\\n${result.reasoning}`;\n }\n\n const header = label ? `Visual AI Check Failed (${label})` : \"Visual AI Check Failed\";\n const lines: string[] = [header, \"=\".repeat(header.length), result.reasoning];\n\n if (result.statements.length > 0) {\n lines.push(\"\", \"Statements:\");\n for (const s of result.statements) {\n const status = s.pass ? \"PASS\" : \"FAIL\";\n const confidence = s.confidence ? ` (${s.confidence})` : \"\";\n lines.push(` ${status} \"${s.statement}\"`);\n lines.push(` ${s.reasoning}${confidence}`);\n }\n }\n\n if (result.issues.length > 0) {\n lines.push(\"\", \"Issues:\");\n for (const issue of result.issues) {\n lines.push(` [${issue.priority}/${issue.category}] ${issue.description}`);\n lines.push(` → ${issue.suggestion}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Formats a compare result into a readable multiline string for logs or test failures.\n *\n * @param result Structured result returned by `compare()`.\n * @param label Optional label appended to the output header.\n * @returns A human-readable summary of the compare result.\n * @example\n * ```ts\n * console.log(formatCompareResult(result, \"Before/after deploy\"));\n * ```\n */\nexport function formatCompareResult(result: CompareResult, label?: string): string {\n const status = result.pass ? \"Passed\" : \"Failed\";\n const header = label ? `Visual AI Compare ${status} (${label})` : `Visual AI Compare ${status}`;\n const lines: string[] = [header, \"=\".repeat(header.length), result.reasoning];\n\n if (result.changes.length > 0) {\n lines.push(\"\", \"Changes:\");\n for (const change of result.changes) {\n lines.push(` [${change.severity}] ${change.description}`);\n }\n }\n\n if (result.diffImage) {\n const { width, height } = result.diffImage;\n lines.push(\"\", `Diff image: ${width}x${height} (AI-generated)`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Throws a `VisualAIAssertionError` when a check result did not pass.\n *\n * @param result Structured result returned by `check()` or a template helper.\n * @param label Optional label appended to the assertion message.\n * @returns Nothing when the result passes.\n * @throws {VisualAIAssertionError} When `result.pass` is `false`.\n * @example\n * ```ts\n * assertVisualResult(result, \"Homepage\");\n * ```\n */\nexport function assertVisualResult(result: CheckResult, label?: string): void {\n if (!result.pass) {\n throw new VisualAIAssertionError(formatCheckResult(result, label), result);\n }\n}\n\n/**\n * Throws a `VisualAIAssertionError` when a compare result did not pass.\n *\n * @param result Structured result returned by `compare()`.\n * @param label Optional label appended to the assertion message.\n * @returns Nothing when the result passes.\n * @throws {VisualAIAssertionError} When `result.pass` is `false`.\n * @example\n * ```ts\n * assertVisualCompareResult(result, \"Login flow\");\n * ```\n */\nexport function assertVisualCompareResult(result: CompareResult, label?: string): void {\n if (!result.pass) {\n throw new VisualAIAssertionError(formatCompareResult(result, label), result);\n }\n}\n"],"mappings":"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","names":["ALL_CHECKS","CHECK_STATEMENTS","ALL_CHECKS","CHECK_STATEMENTS","sharp","import_sharp","width","height","sharp","header"]}
|