@thinkingos/vsl-sdk 0.4.5 → 1.1.0

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/capture/domExtractor.ts","../src/segmentation/level1.ts","../src/segmentation/level2.ts","../src/segmentation/level3.ts","../src/segmentation/level4.ts","../src/segmentation/segmenter.ts","../src/types/vsl.ts","../src/builder/vslBuilder.ts","../src/cache/cacheStore.ts","../src/cache/invalidation.ts","../src/diff/diffEngine.ts","../src/vision/enrichWithVision.ts","../src/session/snapshotSession.ts","../src/llm/types.ts","../src/llm/actions.ts","../src/llm/prompt.ts","../src/llm/schema.ts","../src/llm/transport.ts","../src/llm/openai.ts","../src/llm/anthropic.ts","../src/llm/alibaba.ts","../src/executor/types.ts","../src/executor/resolveTarget.ts","../src/executor/actionExecutor.ts"],"sourcesContent":["/**\n * VSL SDK — публичное API. Точка входа:\n * - Snapshot generation (M1.1): capture → segment → build;\n * - Cache & Diff (M1.2): Cache Store, invalidation, Diff Engine (§6.2),\n * Snapshot Session (первый вызов → VslDocument, далее → VslDiff).\n * - LLM Integration (M1.3): LLM Adapter (OpenAI/Anthropic), Action Model, промпты и retry.\n */\n\n// ——— M1.1: Snapshot generation (сохранено полностью) ———\n\nexport { extractDomTree, ownText } from './capture/domExtractor';\nexport type { ExtractedElement, Rect } from './capture/domExtractor';\nexport { LEVEL1_TAG_MAP, resolveLevel1Type } from './segmentation/level1';\nexport { ARIA_ROLE_TYPE_MAP, resolveAriaRoleType, resolveSt } from './segmentation/level2';\nexport { isAriaHidden, segmentTree } from './segmentation/segmenter';\nexport type { SegmentedElement } from './segmentation/segmenter';\nexport { buildVslDocument } from './builder/vslBuilder';\nexport type { BuildOptions } from './builder/vslBuilder';\nexport { VSL_VERSION } from './types/vsl';\nexport type {\n VisualFragment,\n VisualFragmentType,\n VslCanvas,\n VslDocument,\n VslObject,\n VslState,\n VslType,\n VslViewport,\n} from './types/vsl';\n\n// ——— M1.2: Cache Store + invalidation (T1.2.1/T1.2.2) ———\n\nexport { createCacheStore, computeContentHash, computeCoordHash } from './cache/cacheStore';\nexport type { CacheEntry, CacheStore } from './cache/cacheStore';\nexport { attachMutationObserver } from './cache/invalidation';\nexport type { MutationObserverHandle } from './cache/invalidation';\n\n// ——— M1.2: Diff Engine (T1.2.3, формат ARCHITECTURE.md §6.2) ———\n\nexport { diffVslDocuments } from './diff/diffEngine';\nexport type {\n DiffOptions,\n VslDiff,\n VslDiffChanges,\n VslModifiedObject,\n VslRemovedObject,\n} from './diff/diffEngine';\n\n// ——— M1.2: Snapshot Session (T1.2.4) ———\n\nexport { VslSnapshotSession, isVslDiff } from './session/snapshotSession';\nexport type { SnapshotInput, SnapshotResult } from './session/snapshotSession';\n\n// ——— M1.3: LLM Integration (T1.3.1–T1.3.5) ———\n\nexport { OpenAIAdapter } from './llm/openai';\nexport { AnthropicAdapter } from './llm/anthropic';\nexport { AlibabaAdapter } from './llm/alibaba';\nexport { LlmError, LlmValidationError } from './llm/types';\nexport { VALID_ACTIONS, collectIds, validateAction } from './llm/actions';\nexport type { ValidAction } from './llm/actions';\nexport {\n ACTION_TOOL_DESCRIPTION,\n ACTION_TOOL_NAME,\n ACTION_TOOL_SCHEMA,\n} from './llm/schema';\nexport { buildSystemPrompt, buildUserPrompt } from './llm/prompt';\nexport type { FewShotExample } from './llm/prompt';\nexport { retryWithBackoff } from './llm/transport';\nexport type { RetryOptions } from './llm/transport';\nexport type {\n DecideInput,\n LlmAction,\n LlmAdapter,\n LlmAdapterConfig,\n LlmResponse,\n LlmUsage,\n LlmTransport,\n SendPromptOptions,\n Sleep,\n VslFragmentMeta,\n VslInput,\n VslObjectWithVf,\n VisualFragmentData,\n VisualFragmentStore,\n} from './llm/types';\n\n// ——— M1.4: Action Executor (T1.4.1–T1.4.3) ———\n\nexport { executeAction } from './executor/actionExecutor';\nexport { resolveTarget } from './executor/resolveTarget';\nexport { ActionExecutionError } from './executor/types';\nexport type { ActionResult, ExecutorOptions } from './executor/types';\n\nexport const VSL_SDK_VERSION = '0.1.0';","/**\n * DOM extraction (T1.1.2, ROADMAP.md M1.1).\n *\n * Обходит дерево от корня (по умолчанию document.body) и извлекает все видимые\n * элементы с координатами (getBoundingClientRect), собственным текстом, атрибутами и CSS-подмножеством (Level 3, T1.5.1).\n * Покрывает тот же набор элементов, что и root.querySelectorAll('*'), минус отфильтрованные.\n *\n * Фильтр невидимых (семантика ARCHITECTURE.md; CSS-уровень 3 вне scope M1.1):\n * - непрендеримые теги (script/style/link/meta/noscript/template/head/title/base) —\n * никогда не отрисовываются → пропуск поддерева;\n * - display: none → элемент и всё поддерево не отрисованы → пропуск поддерева;\n * - visibility: hidden → сам элемент невидим, но дети могут быть видимы\n * (visibility: visible) → элемент не включается, поддерево обходится;\n * - нулевой прямоугольник (width <= 0 || height <= 0) → нет видимого бокса, но дети\n * (например, overflow) могут быть видимы → элемент не включается, поддерево обходится.\n *\n * indexPath — индексы среди ЭЛЕМЕНТНЫХ детей на каждом уровне, вычисляются по\n * структуре DOM ДО фильтрации: позиция стабильна независимо от фильтров — основа\n * детерминированных ID VSL (решение note_1789916091535: без Date.now()/Math.random(),\n * база для diffing в M1.2).\n */\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface ExtractedElement {\n /** Имя тега в нижнем регистре (например, 'button'). */\n tag: string;\n /** Путь от корня обхода: индексы среди элементных детей каждого уровня (до фильтрации). */\n indexPath: number[];\n /** Координаты и размеры из getBoundingClientRect (px, viewport). */\n rect: Rect;\n /** Собственный текст (только прямые текстовые узлы), нормализованный; null если пуст. */\n text: string | null;\n /** Все атрибуты элемента (verbatim). */\n attributes: Record<string, string>;\n /** CSS-подмножество для Level 3 (computed styles в момент extraction, T1.5.1). */\n css?: ElementCss;\n /** Видимые дочерние элементы. */\n children: ExtractedElement[];\n}\n\n/**\n * CSS-подмножество для Segmentation Level 3 (T1.5.1): computed styles,\n * захваченные в момент extraction (viewOf/getComputedStyle). Все поля\n * optional: отсутствие значения или поддержки свойства средой → поле\n * просто не задано.\n */\nexport interface ElementCss {\n cursor?: string;\n position?: string;\n top?: string;\n bottom?: string;\n display?: string;\n gap?: string;\n fontWeight?: string;\n fontSize?: string;\n opacity?: string;\n pointerEvents?: string;\n overflow?: string;\n height?: string;\n /** Background color (computed) — для sty.bg в VslObject. */\n backgroundColor?: string;\n /** Text/foreground color (computed) — для sty.fg в VslObject. */\n color?: string;\n /** Border shorthand (computed) — для sty.border в VslObject. */\n border?: string;\n /** Border radius (computed) — для sty.radius в VslObject. */\n borderRadius?: string;\n /** Box shadow (computed) — для sty.shadow в VslObject. */\n boxShadow?: string;\n /** Font family (computed) — для sty.font.family в VslObject. */\n fontFamily?: string;\n}\n\n/** Теги, которые никогда не отрисовываются визуально. */\nconst NON_RENDERABLE_TAGS = new Set([\n 'script',\n 'style',\n 'link',\n 'meta',\n 'noscript',\n 'template',\n 'head',\n 'title',\n 'base',\n]);\n\n/**\n * Окно (контекст стилей) элемента: ownerDocument.defaultView — портативно в\n * браузере, jsdom и любом DOM-окружении без глобального window (фикс\n * packaging-smoke demo:cache-diff: ReferenceError: window is not defined).\n */\nfunction viewOf(el: Element): Window | null {\n return el.ownerDocument.defaultView;\n}\n\nfunction isDisplayNone(el: Element): boolean {\n return viewOf(el)?.getComputedStyle(el).display === 'none';\n}\n\nfunction isVisibilityHidden(el: Element): boolean {\n return viewOf(el)?.getComputedStyle(el).visibility === 'hidden';\n}\n\n/** nodeType текстового узла по DOM-спецификации (3) — без зависимости от глобального Node. */\nconst TEXT_NODE_TYPE = 3;\n\n/** Собственный текст элемента: только прямые текстовые узлы, whitespace-нормализация. */\nexport function ownText(el: Element): string {\n let raw = '';\n for (const node of Array.from(el.childNodes)) {\n if (node.nodeType === TEXT_NODE_TYPE) {\n raw += node.textContent ?? '';\n }\n }\n return raw.replace(/\\s+/g, ' ').trim();\n}\n\nfunction extractAttributes(el: Element): Record<string, string> {\n const attributes: Record<string, string> = {};\n for (const attr of Array.from(el.attributes)) {\n attributes[attr.name] = attr.value;\n }\n return attributes;\n}\n\n/** CSS-свойства для ElementCss (уровень 3): camelCase-ключ → kebab-case-свойство. */\nconst CSS_PROPERTIES: ReadonlyArray<[keyof ElementCss, string]> = [\n ['cursor', 'cursor'],\n ['position', 'position'],\n ['top', 'top'],\n ['bottom', 'bottom'],\n ['display', 'display'],\n ['gap', 'gap'],\n ['fontWeight', 'font-weight'],\n ['fontSize', 'font-size'],\n ['opacity', 'opacity'],\n ['pointerEvents', 'pointer-events'],\n ['overflow', 'overflow'],\n ['height', 'height'],\n ['backgroundColor', 'background-color'],\n ['color', 'color'],\n ['border', 'border'],\n ['borderRadius', 'border-radius'],\n ['boxShadow', 'box-shadow'],\n ['fontFamily', 'font-family'],\n];\n\n/**\n * Захватывает CSS-подмножество элемента (computed styles) для Level 3 (T1.5.1).\n * Пустое значение ('' — свойство не поддержано средой) → undefined, чтобы\n * классификация не принимала отсутствие поддержки за значение.\n */\nfunction captureCss(el: Element): ElementCss {\n const view = viewOf(el);\n if (view === null) return {};\n const style = view.getComputedStyle(el);\n const css: ElementCss = {};\n for (const [key, property] of CSS_PROPERTIES) {\n const value = style.getPropertyValue(property);\n if (value !== '') css[key] = value;\n }\n return css;\n}\n\nfunction toRect(rect: DOMRect): Rect {\n return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };\n}\n\nfunction hasZeroBox(rect: Rect): boolean {\n return rect.width <= 0 || rect.height <= 0;\n}\n\nfunction collectVisibleChildren(parent: Element, parentPath: readonly number[]): ExtractedElement[] {\n const result: ExtractedElement[] = [];\n Array.from(parent.children).forEach((child, index) => {\n const path = [...parentPath, index];\n const tag = child.tagName.toLowerCase();\n\n // Никогда не отрисовывается / display:none — пропускаем всё поддерево.\n if (NON_RENDERABLE_TAGS.has(tag) || isDisplayNone(child)) {\n return;\n }\n\n const rect = toRect(child.getBoundingClientRect());\n\n // Сам элемент без видимого бокса, но дети могут быть видимы —\n // не включаем его, поддерево обходим.\n if (isVisibilityHidden(child) || hasZeroBox(rect)) {\n result.push(...collectVisibleChildren(child, path));\n return;\n }\n\n result.push({\n tag,\n indexPath: path,\n rect,\n text: ownText(child) || null,\n attributes: extractAttributes(child),\n css: captureCss(child),\n children: collectVisibleChildren(child, path),\n });\n });\n return result;\n}\n\nfunction defaultRoot(): Element {\n const body = document.body;\n if (!body) {\n throw new Error('extractDomTree: document.body is null — call after the DOM is loaded');\n }\n return body;\n}\n\n/**\n * Извлекает видимое поддерево DOM, начиная с root (по умолчанию document.body).\n * Сам root не включается — возвращается лес его видимых потомков.\n */\nexport function extractDomTree(root: Element = defaultRoot()): ExtractedElement[] {\n return collectVisibleChildren(root, []);\n}","/**\n * Segmentation Level 1: семантические теги (T1.1.3, ROADMAP.md M1.1).\n *\n * Контракт — таблица «Уровень 1: Семантические теги» из ARCHITECTURE.md:\n * ровно 10 тегов, покрывающих ~40% элементов типичной страницы. Теги вне\n * таблицы (div/span/p/footer/…) НЕ классифицируются на этом уровне —\n * их семантика, если есть, добавляется Level 2 (ARIA) в T1.1.4.\n *\n * Функция чистая и детерминированная: (тег) → тип VSL | null.\n */\n\nimport type { VslType } from '../types/vsl';\n\n/** Таблица маппинга HTML-тегов (нижний регистр) → семантические типы VSL. */\nexport const LEVEL1_TAG_MAP: Readonly<Record<string, VslType>> = {\n button: 'button',\n input: 'input',\n a: 'link',\n nav: 'nav',\n header: 'header',\n main: 'main',\n section: 'container',\n img: 'image',\n select: 'select',\n textarea: 'textarea',\n};\n\n/**\n * Возвращает семантический тип VSL для HTML-тега по таблице Level 1\n * или null, если тег на этом уровне не классифицируется.\n * Регистр тега не учитывается (защита от tagName в любом регистре).\n */\nexport function resolveLevel1Type(tag: string): VslType | null {\n return LEVEL1_TAG_MAP[tag.toLowerCase()] ?? null;\n}","/**\n * Segmentation Level 2: ARIA-атрибуты (T1.1.4, ROADMAP.md M1.1).\n *\n * Контракт — таблица «Уровень 2: ARIA-атрибуты» из ARCHITECTURE.md:\n * - role → тип VSL (button / dialog→modal / tab / tabpanel→container);\n * - aria-label → txt (доступное имя элемента);\n * - aria-pressed / aria-expanded / aria-disabled = \"true\" → st\n * (checked / expanded / disabled);\n * - aria-hidden=\"true\" → элемент декоративный (обрезается в segmenter.ts).\n *\n * L2 дополняет L1: тип из ARIA-роли применяется только если тег не дал типа.\n */\n\nimport type { VslState, VslType } from '../types/vsl';\n\n/** Таблица маппинга ARIA-ролей (нижний регистр) → типы VSL. */\nexport const ARIA_ROLE_TYPE_MAP: Readonly<Record<string, VslType>> = {\n button: 'button',\n dialog: 'modal',\n tab: 'tab',\n tabpanel: 'container',\n combobox: 'input',\n listbox: 'select',\n searchbox: 'input',\n spinbutton: 'input',\n slider: 'input',\n};\n\n/**\n * Возвращает тип VSL по ARIA-роли или null, если роль не из контракта\n * (или роли нет). Регистр не учитывается.\n */\nexport function resolveAriaRoleType(role: string | undefined): VslType | null {\n if (!role) return null;\n return ARIA_ROLE_TYPE_MAP[role.toLowerCase()] ?? null;\n}\n\n/**\n * Определяет dropdown toggle по атрибуту aria-haspopup.\n * Возвращает 'dropdown_toggle' для aria-haspopup='menu' или 'listbox',\n * иначе null. Используется для кнопок, открывающих dropdown меню.\n */\nexport function resolveDropdownToggle(attributes: Record<string, string>): VslType | null {\n const hasPopup = attributes['aria-haspopup'];\n if (hasPopup === 'menu' || hasPopup === 'listbox') {\n return 'dropdown_toggle';\n }\n return null;\n}\n\nconst TRUE = 'true';\n\n/**\n * Состояние VSL из ARIA-атрибутов. По контракту маппируется только точное\n * значение \"true\" (\"false\"/\"mixed\"/прочее → null). При нескольких состояниях\n * приоритет — порядок таблицы контракта: checked → expanded → disabled.\n */\nexport function resolveSt(attributes: Record<string, string>): VslState | null {\n if (attributes['aria-pressed'] === TRUE) return 'checked';\n if (attributes['aria-expanded'] === TRUE) return 'expanded';\n if (attributes['aria-disabled'] === TRUE) return 'disabled';\n return null;\n}","/**\n * Segmentation Level 3: CSS-анализ (T1.5.1, ROADMAP.md M1.5).\n *\n * Контракт — таблица «Уровень 3: CSS-анализ» из ARCHITECTURE.md:L145-156:\n * ровно 8 паттернов над CSS-подмножеством, захваченным domExtractor\n * (ElementCss: computed styles через viewOf/getComputedStyle):\n *\n * | CSS-паттерн | → Тип |\n * |--------------------------------------|------------------------|\n * | cursor: pointer + onclick | button |\n * | display: flex + gap > 0 | container |\n * | font-weight: bold + font-size > 20px | heading |\n * | position: fixed + top: 0 | header |\n * | position: fixed + bottom: 0 | footer |\n * | overflow: hidden + height > 200px | scrollable_container |\n * | display: none / visibility: hidden | пропустить (domExtractor) |\n * | opacity: 0 + pointer-events: none | пропустить (isPointerInvisible) |\n *\n * Функции чистые и детерминированные. Порядок проверки = порядку таблицы\n * (первое совпадение выигрывает). Итоговый приоритет классификации:\n * L1 > L2 > L3 (интеграция в segmenter — dev_5, T1.5.2).\n */\n\nimport type { ElementCss } from '../capture/domExtractor';\nimport type { VslType } from '../types/vsl';\n\n/** Числовое значение CSS-свойства ('24px' → 24; 'auto'/'normal'/'' → NaN). */\nfunction px(value: string | undefined): number {\n return value === undefined ? Number.NaN : Number.parseFloat(value);\n}\n\n/**\n * Возвращает тип VSL по CSS-паттерну (уровень 3) или null, если ни один из\n * 6 классифицирующих паттернов не совпал (skip-паттерны отдельно: см.\n * isPointerInvisible; display:none/visibility:hidden фильтруются в domExtractor).\n *\n * @param css захваченное CSS-подмножество (undefined — захват не производился)\n * @param attributes атрибуты элемента (для onclick в паттерне button)\n */\nexport function resolveLevel3Type(\n css: ElementCss | undefined,\n attributes: Readonly<Record<string, string>>,\n): VslType | null {\n if (css === undefined) return null;\n\n // 1. cursor: pointer + onclick → button (интерактивный элемент).\n if (css.cursor === 'pointer' && attributes['onclick'] !== undefined) return 'button';\n\n // 2. display: flex + gap > 0 → container (контейнер с layout).\n if (css.display === 'flex' && px(css.gap) > 0) return 'container';\n\n // 3. font-weight: bold + font-size > 20px → heading (заголовок).\n // Computed styles отдают жирность числом ('700'), inline — словом ('bold').\n const isBold = css.fontWeight === 'bold' || px(css.fontWeight) >= 700;\n if (isBold && px(css.fontSize) > 20) return 'heading';\n\n // 4. position: fixed + top: 0 → header (фиксированный header).\n if (css.position === 'fixed' && px(css.top) === 0) return 'header';\n\n // 5. position: fixed + bottom: 0 → footer (фиксированный footer).\n if (css.position === 'fixed' && px(css.bottom) === 0) return 'footer';\n\n // 6. overflow: hidden + height > 200px → scrollable_container.\n if (css.overflow === 'hidden' && px(css.height) > 200) return 'scrollable_container';\n\n return null;\n}\n\n/**\n * Элемент невидим для взаимодействия (skip-паттерн таблицы уровня 3):\n * opacity: 0 + pointer-events: none → пропускается вместе с поддеревом\n * (применяется в segmenter, аналог aria-hidden — dev_5).\n *\n * display: none / visibility: hidden — другие skip-паттерны таблицы,\n * отфильтровываются раньше, в domExtractor (isDisplayNone/isVisibilityHidden).\n */\nexport function isPointerInvisible(css: ElementCss | undefined): boolean {\n if (css === undefined) return false;\n return px(css.opacity) === 0 && css.pointerEvents === 'none';\n}","/**\n * Segmentation Level 4: структурный анализ (T1.5.2, ROADMAP.md M1.5).\n *\n * Контракт — таблица «Уровень 4: Структурный анализ» из ARCHITECTURE.md:L158-172:\n * паттерны определяются по ГЕОМЕТРИИ ДЕТЕЙ уже аннотированного элемента\n * (L1/L2/L3 применены раньше — итоговый приоритет L1>L2>L3>L4, интеграция\n * в segmenter — следующий шаг; rect захвачен domExtractor):\n *\n * | Паттерн | Условие | → Тип |\n * |---------------------------|--------------------------------------------------|-------------|\n * | Горизонтальный ряд кнопок | ≥3 элемента с t: button в ряд, одинаковая высота | toolbar |\n * | Вертикальный список | ≥3 элемента с одинаковой шириной, вертикально | list |\n * | Сетка карточек | Элементы в строках и столбцах, одинаковый размер | grid |\n * | Label + input рядом | text элемент рядом с input элемент | form_field |\n * | Tab bar | Горизонтальный ряд элементов с role=\"tab\" | tab_bar |\n * | Sidebar + main | Вертикальный split: узкий слева + широкий справа | layout |\n *\n * Функция чистая и детерминированная: (элемент + дети) → тип VSL | null.\n * Порядок проверки = порядку таблицы (первое совпадение выигрывает).\n * Без импорта segmenter — нет циклической зависимости (SegmentedElement\n * структурно совместим с Level4Input: rect/ch/t/attributes/txt).\n */\n\nimport type { Rect } from '../capture/domExtractor';\nimport type { VslType } from '../types/vsl';\n\n/** Допуск сравнения геометрии (px): суб-пиксельный рендеринг и округления. */\nconst EPS = 2;\n\n/** Ребёнок для L4-анализа: уже аннотирован L1/L2/L3 (t может отсутствовать). */\nexport interface Level4Child {\n rect: Rect;\n t?: VslType | null;\n attributes?: Readonly<Record<string, string>>;\n txt?: string | null;\n}\n\n/** Вход L4: элемент-кандидат (контейнер группы) с его детьми. */\nexport interface Level4Input {\n rect: Rect;\n ch?: readonly Level4Child[];\n}\n\nconst bottomOf = (r: Rect): number => r.y + r.height;\nconst rightOf = (r: Rect): number => r.x + r.width;\n\n/** В одном горизонтальном ряду: перекрытие по Y > половины меньшей высоты. */\nfunction sameRow(a: Rect, b: Rect): boolean {\n const overlap = Math.min(bottomOf(a), bottomOf(b)) - Math.max(a.y, b.y);\n return overlap > 0.5 * Math.min(a.height, b.height);\n}\n\n/** В одной вертикальной колонке: перекрытие по X > половины меньшей ширины. */\nfunction sameColumn(a: Rect, b: Rect): boolean {\n const overlap = Math.min(rightOf(a), rightOf(b)) - Math.max(a.x, b.x);\n return overlap > 0.5 * Math.min(a.width, b.width);\n}\n\nconst eq = (a: number, b: number): boolean => Math.abs(a - b) <= EPS;\nconst sameHeight = (a: Rect, b: Rect): boolean => eq(a.height, b.height);\nconst sameWidth = (a: Rect, b: Rect): boolean => eq(a.width, b.width);\n\n/**\n * toolbar: ≥3 кнопок (t: button) в одном ряду одинаковой высоты.\n * Упрощение контракта: все кнопки-кандидаты в одном ряду — многострочные\n * панели кнопок не классифицируются как toolbar (их ловит grid при равных\n * размерах).\n */\nfunction isToolbarRow(children: readonly Level4Child[]): boolean {\n const buttons = children.filter((c) => c.t === 'button');\n const first = buttons[0];\n if (buttons.length < 3 || first === undefined) return false;\n return buttons\n .slice(1)\n .every((b) => sameRow(first.rect, b.rect) && sameHeight(first.rect, b.rect));\n}\n\n/** Дети выстроены вертикально подряд: соседние по Y не перекрываются. */\nfunction verticallyStacked(children: readonly Level4Child[]): boolean {\n const sorted = [...children].sort((a, b) => a.rect.y - b.rect.y);\n for (let i = 1; i <sorted.length; i += 1) {\n if (sorted[i]!.rect.y <bottomOf(sorted[i - 1]!.rect) - EPS) return false;\n }\n return true;\n}\n\n/** list: ≥3 детей одинаковой ширины, вертикально (в колонке, без наложений по Y). */\nfunction isVerticalList(children: readonly Level4Child[]): boolean {\n const first = children[0];\n if (children.length < 3 || first === undefined) return false;\n return (\n children\n .slice(1)\n .every((c) => sameWidth(first.rect, c.rect) && sameColumn(first.rect, c.rect)) &&\n verticallyStacked(children)\n );\n}\n\n/** Количество кластеров значений с допуском eps (полосы строк/столбцов сетки). */\nfunction bandCount(values: readonly number[], eps: number): number {\n const sorted = [...values].sort((a, b) => a - b);\n let count = 1;\n let anchor = sorted[0]!;\n for (const value of sorted.slice(1)) {\n if (value - anchor > eps) {\n count += 1;\n anchor = value;\n }\n }\n return count;\n}\n\n/** grid: ≥4 детей одного размера, минимум 2 строки и 2 столбца. */\nfunction isGrid(children: readonly Level4Child[]): boolean {\n const first = children[0];\n if (children.length < 4 || first === undefined) return false;\n const sameSize = children\n .slice(1)\n .every((c) => sameWidth(first.rect, c.rect) && sameHeight(first.rect, c.rect));\n if (!sameSize) return false;\n return (\n bandCount(children.map((c) => c.rect.y), first.rect.height + EPS) >= 2 &&\n bandCount(children.map((c) => c.rect.x), first.rect.width + EPS) >= 2\n );\n}\n\n/** Соседство: общий ряд/колонка с зазором не более 16px (или перекрытием). */\nfunction adjacent(a: Rect, b: Rect): boolean {\n const gapX = Math.max(a.x, b.x) - Math.min(rightOf(a), rightOf(b));\n const gapY = Math.max(a.y, b.y) - Math.min(bottomOf(a), bottomOf(b));\n return (sameRow(a, b) && gapX <= 8 * EPS) || (sameColumn(a, b) && gapY <= 8 * EPS);\n}\n\n/** form_field: text-элемент (label с txt) рядом с input-элементом. */\nfunction hasLabelNextToInput(children: readonly Level4Child[]): boolean {\n return children.some((input) => {\n if (input.t !== 'input') return false;\n return children.some((label) => {\n if (label === input) return false;\n if ((label.txt ?? '').length === 0) return false;\n return adjacent(label.rect, input.rect);\n });\n });\n}\n\n/** tab_bar: ≥2 элементов с role=\"tab\" в одном горизонтальном ряду. */\nfunction isTabBarRow(children: readonly Level4Child[]): boolean {\n const tabs = children.filter((c) => c.attributes?.['role'] === 'tab');\n const first = tabs[0];\n if (tabs.length < 2 || first === undefined) return false;\n return tabs.slice(1).every((t) => sameRow(first.rect, t.rect));\n}\n\n/** layout: ровно 2 ребёнка — узкий слева + широкий справа (вертикальный split). */\nfunction isSidebarSplit(children: readonly Level4Child[], parent: Rect): boolean {\n if (children.length !== 2) return false;\n const sorted = [...children].sort((a, b) => a.rect.x - b.rect.x);\n const left = sorted[0];\n const right = sorted[1];\n if (left === undefined || right === undefined) return false;\n return (\n left.rect.width <parent.width / 3 &&\n right.rect.width > parent.width / 2 &&\n rightOf(left.rect) <= right.rect.x + EPS &&\n sameRow(left.rect, right.rect)\n );\n}\n\n/**\n * Возвращает тип VSL по структурному паттерну (уровень 4) или null, если ни\n * один из 6 паттернов не совпал. Вызывается в segmenter ПОСЛЕ L1/L2/L3\n * (приоритет L1>L2>L3>L4): аннотирует контейнер группы по геометрии детей.\n *\n * @param el элемент-кандидат: rect + уже аннотированные дети (ch)\n */\nexport function resolveLevel4Type(el: Level4Input): VslType | null {\n const children = el.ch ?? [];\n if (children.length === 0) return null;\n if (isToolbarRow(children)) return 'toolbar';\n if (isVerticalList(children)) return 'list';\n if (isGrid(children)) return 'grid';\n if (hasLabelNextToInput(children)) return 'form_field';\n if (isTabBarRow(children)) return 'tab_bar';\n if (isSidebarSplit(children, el.rect)) return 'layout';\n return null;\n}","/**\n * Segmentation Engine: объединение уровней 1-4 (T1.1.3/T1.1.4/T1.5.1/T1.5.2).\n *\n * Обходит ExtractedElement-дерево (domExtractor) и аннотирует каждый элемент:\n * - t: приоритет L1 (тег) → L2 (ARIA-роль) → L3 (CSS-паттерны) → L4\n * (структурные паттерны по геометрии уже аннотированных детей); null —\n * не классифицирован (включение и типизация контейнеров — решение\n * VSL Builder, T1.1.5);\n * - txt: aria-label (приоритет по семантике доступного имени) ?? собственный текст;\n * - st: состояние из aria-pressed/expanded/disabled (\"true\" only).\n *\n * Skip-поддеревья (элемент не попадает в VSL вместе с поддеревом):\n * - aria-hidden=\"true\" — декоративный по семантике accessibility-дерева;\n * - opacity:0 + pointer-events:none — визуально невидимый (L3,\n * isPointerInvisible).\n *\n * L4 применяется после рекурсивной обработки детей: структурные паттерны\n * (toolbar/list/grid/form_field/tab_bar/layout) анализируют t детей.\n */\n\nimport type { ElementCss, ExtractedElement, Rect } from '../capture/domExtractor';\nimport type { VslFragmentMeta, VslState, VslType } from '../types/vsl';\nimport { resolveLevel1Type } from './level1';\nimport { resolveAriaRoleType, resolveDropdownToggle, resolveSt } from './level2';\nimport { isPointerInvisible, resolveLevel3Type } from './level3';\nimport { resolveLevel4Type } from './level4';\n\nexport interface SegmentedElement {\n tag: string;\n indexPath: number[];\n rect: Rect;\n /** Атрибуты verbatim (нужны Builder для умных дефолтов act и валидации). */\n attributes: Record<string, string>;\n /** Тип VSL: приоритет L1 > L2 > L3 > L4; null — не классифицирован. */\n t: VslType | null;\n /** Текст: aria-label ?? собственный текст; null, если нет ни того, ни другого. */\n txt: string | null;\n /** Состояние из ARIA (checked/expanded/disabled) или null. */\n st: VslState | null;\n /** CSS-подмножество для визуальных стилей (передаётся в Builder для sty). */\n css?: ElementCss;\n ch: SegmentedElement[];\n /** Ссылка на визуальный фрагмент (заполняется enrichWithVision, T1.5.4). */\n vf?: string;\n /** Метаданные фрагмента (§4.4). */\n vf_meta?: VslFragmentMeta;\n}\n\n/** Элемент декоративен (aria-hidden=\"true\") → в VSL не включается. */\nexport function isAriaHidden(attributes: Record<string, string>): boolean {\n return attributes['aria-hidden'] === 'true';\n}\n\nfunction segmentOne(el: ExtractedElement): SegmentedElement {\n const { attributes } = el;\n // Специальная проверка для input[type=file] → file_input\n // (не ломает контракт resolveLevel1Type, который принимает только tag)\n const isFileInput = el.tag === 'input' && attributes['type'] === 'file';\n return {\n tag: el.tag,\n indexPath: el.indexPath,\n rect: el.rect,\n attributes,\n t: isFileInput\n ? 'file_input'\n : (resolveDropdownToggle(attributes) ??\n resolveLevel1Type(el.tag) ??\n resolveAriaRoleType(attributes['role']) ??\n resolveLevel3Type(el.css, attributes)),\n txt: attributes['aria-label'] ?? el.text,\n st: resolveSt(attributes),\n css: el.css,\n ch: [],\n };\n}\n\n/**\n * Аннотирует видимое дерево (результат extractDomTree) семантикой L1-L4,\n * обрезая skip-поддеревья (aria-hidden, isPointerInvisible). Порядок\n * элементов сохраняется. L4 анализирует геометрию детей ПОСЛЕ их\n * аннотирования (post-order по t).\n */\nexport function segmentTree(elements: readonly ExtractedElement[]): SegmentedElement[] {\n const result: SegmentedElement[] = [];\n for (const el of elements) {\n if (isAriaHidden(el.attributes)) continue;\n if (isPointerInvisible(el.css)) continue;\n const segmented = segmentOne(el);\n segmented.ch = segmentTree(el.children);\n if (segmented.t === null) {\n segmented.t = resolveLevel4Type(segmented);\n }\n result.push(segmented);\n }\n return result;\n}","/**\n * Типы VSL JSON (DESIGN_SYSTEM.md §4, ARCHITECTURE.md §2.3).\n */\n\n/**\n * Семантический тип объекта VSL (поле `t`): L1-теги + L2-роли (T1.1.3/T1.1.4),\n * L3-CSS (T1.5.1), L4-структурные (T1.5.2), L5-vision (T1.5.4).\n */\nexport type VslType =\n | 'button'\n | 'input'\n | 'file_input'\n | 'link'\n | 'nav'\n | 'header'\n | 'main'\n | 'container'\n | 'image'\n | 'select'\n | 'textarea'\n | 'modal'\n | 'tab'\n | 'dropdown_toggle'\n // Level 3 — CSS-анализ (§2.2.1):\n | 'heading'\n | 'footer'\n | 'scrollable_container'\n // Level 4 — структурный анализ (§2.2.1):\n | 'toolbar'\n | 'list'\n | 'grid'\n | 'form_field'\n | 'tab_bar'\n | 'layout'\n // Level 5 — vision fallback (§2.2.2):\n | 'icon'\n | 'chart'\n | 'custom_widget'\n | 'unknown';\n\n/** Состояние объекта VSL (поле `st`) — из ARIA-атрибутов (T1.1.4). */\nexport type VslState = 'checked' | 'expanded' | 'disabled';\n\n/** Тип визуального фрагмента — выход классификации vision (§2.2.2 Шаг 2). */\nexport type VisualFragmentType = 'image' | 'icon' | 'chart' | 'custom_widget' | 'unknown';\n\n/** Метаданные visual fragment (DESIGN_SYSTEM.md §4.4). */\nexport interface VslFragmentMeta {\n type?: string;\n format?: string;\n size?: [number, number];\n hash?: string;\n cached_at?: string;\n}\n\n/**\n * Визуальные стили объекта VSL (DESIGN_SYSTEM.md §4.3).\n * Optional: только для визуально значимых элементов.\n * Все значения — computed styles в момент snapshot.\n */\nexport interface VslStyle {\n /** Background color (computed). */\n bg?: string;\n /** Text/foreground color (computed). */\n fg?: string;\n /** Border shorthand (computed). */\n border?: string;\n /** Border radius (computed). */\n radius?: string;\n /** Box shadow (computed). */\n shadow?: string;\n /** Font properties. */\n font?: {\n family?: string;\n size?: string;\n weight?: string;\n };\n}\n\n/**\n * Запись реестра visual_fragments документа (§2.2.2, ARCHITECTURE.md:L265-284).\n * ТОЛЬКО метаданные: base64-данные изображений живут в VisualFragmentStore\n * (src/llm/types.ts, DEC-015) и подаются по запросу (lazy loading §4.4) —\n * документ остаётся в бюджете 10-100 KB (ARCHITECTURE.md:L16-18).\n */\nexport interface VisualFragment {\n /** Тип фрагмента (§2.2.2 Шаг 2). */\n type: VisualFragmentType;\n /** Формат закодированных данных (например, 'webp'). */\n format: string;\n /** Размер [ширина, высота] в px. */\n size: [number, number];\n /** Хэш кэша hash(bounding_box + pixel_content) — ключ инвалидации (§5.2). */\n hash?: string;\n /** ISO 8601 — время кэширования. */\n cached_at?: string;\n /** Эмбеддинг (512-dim CLIP) — контракт §2.2.2; в M1.5 не заполняется. */\n embedding?: number[];\n}\n\n/** Версия формата VSL (DESIGN_SYSTEM.md §4.1). */\nexport const VSL_VERSION = '1.0.0' as const;\n\nexport interface VslViewport {\n width: number;\n height: number;\n unit: 'px';\n}\n\nexport interface VslCanvas {\n viewport: VslViewport;\n background: string;\n scale: number;\n orientation: 'landscape' | 'portrait';\n /** ISO 8601; для детерминизма тестов фиксируется через BuildOptions. */\n timestamp: string;\n url?: string;\n title?: string;\n}\n\nexport interface VslObject {\n /** Детерминированный ID из DOM-пути (note_1789916091535, база M1.2 diff). */\n id: string;\n t: VslType;\n /** Роль (атрибут role), уточняет семантику. */\n r?: string;\n /** Позиция [x, y] — относительные координаты 0.0–1.0 от viewport. */\n p: [number, number];\n /** Размер [width, height] в px. */\n s: [number, number];\n st?: VslState;\n txt?: string;\n /** Preview длинного текста (первые ~50 символов) — lazy text loading (M1.7, DEC-026). */\n txt_preview?: string;\n /** Ссылка на полный текст в text_blocks документа — lazy text loading (M1.7, DEC-026). */\n txt_ref?: string;\n act?: string[];\n ch?: VslObject[];\n /** Ссылка на визуальный фрагмент (§2.2.2) — ключ в visual_fragments документа. */\n vf?: string;\n /** Метаданные фрагмента (§4.4). */\n vf_meta?: VslFragmentMeta;\n /** Визуальные стили (DESIGN_SYSTEM.md §4.3). Optional: только для визуально значимых элементов. */\n sty?: VslStyle;\n /** Accept attribute для file_input (например, '.pdf,.docx'). */\n accept?: string;\n /** Multiple attribute для file_input — разрешена загрузка нескольких файлов. */\n multiple?: boolean;\n /** Атрибут aria-haspopup — указывает на dropdown/menu (для LLM). */\n hasPopup?: string;\n}\n\nexport interface VslDocument {\n vsl_version: string;\n canvas: VslCanvas;\n objects: VslObject[];\n /**\n * Реестр визуальных фрагментов (§2.2.2): ключ — vf-ссылка объекта\n * (инвариант: каждый vf объекта — ключ этой map). Только метаданные:\n * base64-данные изображений живут в VisualFragmentStore (src/llm/types.ts,\n * DEC-015) и подаются по запросу (lazy loading §4.4) — документ остаётся\n * в бюджете 10-100 KB (ARCHITECTURE.md:L16-18). Optional: документы\n * без фрагментов валидны (обратно-совместимость).\n */\n visual_fragments?: Record<string, VisualFragment>;\n /**\n * Кэш полных текстов для lazy text loading (M1.7, DEC-026): ключ — txt_ref\n * объекта (формат tb_xxx, инкрементный счётчик). Значение — полный текст.\n * Только для элементов с txt > 200 символов. Optional: документы без\n * текстовых блоков валидны (обратно-совместимость).\n */\n text_blocks?: Record<string, string>;\n}","/**\n * VSL Builder (T1.1.5, ROADMAP.md M1.1; контракт ARCHITECTURE.md §2.3).\n *\n * Вход: семантическое дерево от Segmentation Engine (segmentTree).\n * Выход: VSL JSON по DESIGN_SYSTEM.md §4 (vsl_version, canvas, objects tree).\n *\n * Отбор элементов — эвристика Semantic Density (ARCHITECTURE.md) в адаптации\n * к L1/L2-сегментации:\n * - t !== null (типизирован L1/L2) → семантический объект, включается;\n * - t === null → score = role(3) + aria-label(2) + interactive-атрибуты(2) +\n * txt(1) + включённые дети(1); score >= 3 → контейнер;\n * - t === null и есть включённые потомки → контейнер-обёртка (обобщение\n * контракта «== 0 с семантическими детьми → grouping» на зазор 1–2);\n * - иначе — декоративный, пропускается вместе с поддеревом.\n *\n * Детерминизм (решение для M1.2 Cache/Diff): id = tag_indexPath из DOM-пути,\n * без Date.now()/Math.random(); timestamp/url/title фиксируются через\n * BuildOptions (в тестах — обязательно).\n */\n\nimport type { SegmentedElement } from '../segmentation/segmenter';\nimport type { ElementCss } from '../capture/domExtractor';\nimport {\n VSL_VERSION,\n type VisualFragment,\n type VisualFragmentType,\n type VslCanvas,\n type VslDocument,\n type VslObject,\n type VslState,\n type VslStyle,\n type VslType,\n} from '../types/vsl';\n\n/** Порог lazy text loading: тексты длиннее этого порога заменяются на preview + ref (DEC-026, M1.7). */\nconst LAZY_TEXT_THRESHOLD = 200;\n/** Длина preview для lazy text (DEC-026, M1.7). */\nconst LAZY_TEXT_PREVIEW_LENGTH = 50;\n\n/** Опции сборки: для детерминированных тестов фиксируйте timestamp/url/title. */\nexport interface BuildOptions {\n viewport?: { width: number; height: number };\n background?: string;\n timestamp?: string;\n url?: string;\n title?: string;\n}\n\nconst INTERACTIVE_ATTRS: readonly string[] = [\n 'onclick',\n 'onmousedown',\n 'ontouchstart',\n 'tabindex',\n 'contenteditable',\n];\n\n/** Относительная координата [0..1], округление до 4 знаков (компактность JSON). */\nfunction relative(value: number, dimension: number): number {\n if (dimension <= 0) return 0;\n return Math.round((value / dimension) * 10000) / 10000;\n}\n\n/** Умные дефолты act по README_AI §4.4; disabled → взаимодействие недоступно. */\nfunction defaultActions(\n t: VslType,\n st: VslState | null,\n attributes: Record<string, string>,\n): string[] | undefined {\n if (st === 'disabled') return undefined;\n switch (t) {\n case 'button':\n case 'link':\n case 'tab':\n return ['click'];\n case 'dropdown_toggle':\n return ['click', 'expand', 'collapse'];\n case 'modal':\n return ['close'];\n case 'input': {\n const type = attributes['type'];\n return type === 'checkbox' || type === 'radio'\n ? ['click', 'check', 'uncheck']\n : ['click', 'type', 'clear'];\n }\n case 'select':\n return ['click', 'select'];\n case 'textarea':\n return ['click', 'type', 'clear'];\n case 'file_input':\n return ['click', 'upload'];\n default:\n return undefined;\n }\n}\n\n/** Score нетипизированного элемента без учёта детей (дети учтены включённостью). */\nfunction baseSemanticScore(el: SegmentedElement): number {\n let score = 0;\n if (el.attributes['role'] !== undefined) score += 3;\n if (el.attributes['aria-label'] !== undefined) score += 2;\n if (INTERACTIVE_ATTRS.some((attr) => el.attributes[attr] !== undefined)) score += 2;\n if (el.txt) score += 1;\n return score;\n}\n\n/**\n * Извлекает визуальные стили из CSS-подмножества для VslObject.\n * Возвращает undefined, если нет значимых стилей (экономия JSON).\n * Фильтрует дефолтные значения: transparent, none, 0px, rgb(0,0,0).\n */\nfunction extractVisualStyles(css?: ElementCss): VslStyle | undefined {\n if (!css) return undefined;\n\n const style: VslStyle = {};\n let hasContent = false;\n\n // Background (пропускаем transparent и rgba(0,0,0,0))\n if (\n css.backgroundColor &&\n css.backgroundColor !== 'transparent' &&\n !css.backgroundColor.includes('rgba(0, 0, 0, 0)')\n ) {\n style.bg = css.backgroundColor;\n hasContent = true;\n }\n\n // Foreground (пропускаем черный по умолчанию) — ВСЕГДА включаем, если есть\n if (css.color && css.color !== 'rgb(0, 0, 0)') {\n style.fg = css.color;\n hasContent = true;\n }\n\n // Border (пропускаем none и 0px) — ВСЕГДА включаем, если есть\n if (css.border && css.border !== 'none' && !css.border.startsWith('0px')) {\n style.border = css.border;\n hasContent = true;\n }\n\n // Border radius (пропускаем 0px) — ВСЕГДА включаем, если есть\n if (css.borderRadius && css.borderRadius !== '0px') {\n style.radius = css.borderRadius;\n hasContent = true;\n }\n\n // Box shadow (пропускаем none) — ВСЕГДА включаем, если есть\n if (css.boxShadow && css.boxShadow !== 'none') {\n style.shadow = css.boxShadow;\n hasContent = true;\n }\n\n // Font (только если есть family/size/weight) — ВСЕГДА включаем, если есть\n if (css.fontFamily || css.fontSize || css.fontWeight) {\n style.font = {};\n if (css.fontFamily) style.font.family = css.fontFamily;\n if (css.fontSize) style.font.size = css.fontSize;\n if (css.fontWeight) style.font.weight = css.fontWeight;\n hasContent = true;\n }\n\n return hasContent ? style : undefined;\n}\n\n\nfunction toVslObject(\n el: SegmentedElement,\n viewport: { width: number; height: number },\n includedChildren: VslObject[],\n textBlocks: Map<string, string>,\n counter: { value: number },\n): VslObject {\n const t: VslType = el.t ?? 'container';\n const object: VslObject = {\n id: `${el.tag}_${el.indexPath.join('_')}`,\n t,\n p: [relative(el.rect.x, viewport.width), relative(el.rect.y, viewport.height)],\n s: [Math.round(el.rect.width), Math.round(el.rect.height)],\n };\n const role = el.attributes['role'];\n if (role !== undefined) object.r = role;\n if (el.st) object.st = el.st;\n if (el.txt) {\n if (el.txt.length > LAZY_TEXT_THRESHOLD) {\n object.txt_preview = el.txt.slice(0, LAZY_TEXT_PREVIEW_LENGTH) + '…';\n const refId = `tb_${String(counter.value++).padStart(3, '0')}`;\n object.txt_ref = refId;\n textBlocks.set(refId, el.txt);\n } else {\n object.txt = el.txt;\n }\n }\n const act = defaultActions(t, el.st, el.attributes);\n if (act !== undefined) object.act = act;\n if (includedChildren.length > 0) object.ch = includedChildren;\n if (el.vf !== undefined) object.vf = el.vf;\n if (el.vf_meta !== undefined) object.vf_meta = el.vf_meta;\n const sty = extractVisualStyles(el.css);\n if (sty !== undefined) object.sty = sty;\n // file_input: извлекаем accept и multiple атрибуты\n if (t === 'file_input') {\n if (el.attributes['accept'] !== undefined) object.accept = el.attributes['accept'];\n if (el.attributes['multiple'] !== undefined) object.multiple = true;\n }\n // dropdown_toggle: извлекаем aria-haspopup как hasPopup\n if (t === 'dropdown_toggle' && el.attributes['aria-haspopup'] !== undefined) {\n object.hasPopup = el.attributes['aria-haspopup'];\n }\n return object;\n}\n\n/** Нижняя граница обхода: сначала дети, затем решение о включении элемента. */\nfunction convert(\n el: SegmentedElement,\n viewport: { width: number; height: number },\n textBlocks: Map<string, string>,\n counter: { value: number },\n): VslObject | null {\n const includedChildren = el.ch\n .map((child) => convert(child, viewport, textBlocks, counter))\n .filter((obj): obj is VslObject => obj !== null);\n\n if (el.t !== null) return toVslObject(el, viewport, includedChildren, textBlocks, counter);\n\n const score = baseSemanticScore(el) + (includedChildren.length > 0 ? 1 : 0);\n if (score >= 3 || includedChildren.length > 0) {\n return toVslObject(el, viewport, includedChildren, textBlocks, counter);\n }\n return null; // декоративный — пропускается вместе с поддеревом\n}\n\n/**\n * Будет ли элемент включён в VSL (решение convert без построения объектов):\n * типизирован, или score ≥ 3, или есть включённые дети. Используется\n * enrichWithVision для отбора vision-кандидатов (спасение отбрасываемых\n * элементов, T1.5.4) — единственный источник правды об отбрасывании.\n */\nexport function isIncludedInVsl(el: SegmentedElement): boolean {\n if (el.t !== null) return true;\n const includedChildren = el.ch.filter(isIncludedInVsl).length;\n const score = baseSemanticScore(el) + (includedChildren > 0 ? 1 : 0);\n return score >= 3 || includedChildren > 0;\n}\n\nfunction buildCanvas(\n options: BuildOptions,\n viewport: { width: number; height: number },\n): VslCanvas {\n const url = options.url ?? window.location.href;\n const title = options.title ?? document.title;\n return {\n viewport: { ...viewport, unit: 'px' },\n background: options.background ?? '#ffffff',\n scale: 1,\n orientation: viewport.width >= viewport.height ? 'landscape' : 'portrait',\n timestamp: options.timestamp ?? new Date().toISOString(),\n ...(url ? { url } : {}),\n ...(title ? { title } : {}),\n };\n}\n\n/**\n * Собирает реестр visual_fragments документа (§2.2.2, AC[5]): обходит\n * построенные объекты, для каждого объекта с vf-ссылкой копирует метаданные\n * из vf_meta (заполняются enrichWithVision). Инвариант vf→map выполняется\n * по построению: запись создаётся ровно для каждой встреченной vf-ссылки;\n * дубликаты vf (одинаковые фрагменты на разных объектах) дедуплицируются\n * ключом map. Возвращает undefined, если фрагментов нет (обратно-совместимость:\n * документы без фрагментов валидны).\n */\nfunction collectVisualFragments(\n objects: readonly VslObject[],\n): Record<string, VisualFragment> | undefined {\n const map: Record<string, VisualFragment> = {};\n const visit = (list: readonly VslObject[]): void => {\n for (const object of list) {\n if (object.vf !== undefined && object.vf_meta !== undefined) {\n map[object.vf] = {\n type: object.vf_meta.type as VisualFragmentType,\n format: object.vf_meta.format ?? 'webp',\n size: object.vf_meta.size ?? [0, 0],\n hash: object.vf_meta.hash,\n cached_at: object.vf_meta.cached_at,\n };\n }\n visit(object.ch ?? []);\n }\n };\n visit(objects);\n return Object.keys(map).length > 0 ? map : undefined;\n}\n\n/**\n * Строит VSL JSON из семантического дерева (segmentTree) по DESIGN_SYSTEM §4.\n * Viewport фиксируется один раз на всю сборку — p-нормализация консистентна.\n */\nexport function buildVslDocument(\n elements: readonly SegmentedElement[],\n options: BuildOptions = {},\n): VslDocument {\n const viewport = {\n width: options.viewport?.width ?? window.innerWidth,\n height: options.viewport?.height ?? window.innerHeight,\n };\n const textBlocks = new Map<string, string>();\n const counter = { value: 0 };\n const objects = elements\n .map((el) => convert(el, viewport, textBlocks, counter))\n .filter((obj): obj is VslObject => obj !== null);\n const vslDocument: VslDocument = {\n vsl_version: VSL_VERSION,\n canvas: buildCanvas(options, viewport),\n objects,\n };\n const visualFragments = collectVisualFragments(objects);\n if (visualFragments !== undefined) vslDocument.visual_fragments = visualFragments;\n if (textBlocks.size > 0) vslDocument.text_blocks = Object.fromEntries(textBlocks);\n return vslDocument;\n}","/**\n * Cache Store (T1.2.1, ROADMAP.md M1.2; контракт ARCHITECTURE.md §2.4/§5).\n *\n * In-memory Map id → CacheEntry. Запись кэша содержит VslObject и два\n * детерминированных хэша (решение dc_4):\n * - contentHash — содержимое {t, r?, st?, txt?, act?} в порядке канона §4;\n * - coordHash — координаты {p, s}.\n * id в хэш не входит (он ключ Map), ch не входит (дети отслеживаются\n * собственными записями). Хэш — sha256 (node:crypto) поверх JSON.stringify\n * объекта с фиксированным порядком ключей.\n *\n * Invalidation (ARCHITECTURE.md §5.2, решение dc_5):\n * - invalidate(id) — точечная инвалидация записи;\n * - invalidateBySelector — MVP-семантика: '*' → полный сброс; точный id;\n * иначе селектор трактуется как тег id ('button' → все 'button_*'), т.к.\n * CSS-селекторы к кэшированным VslObject неприменимы (id = tag_indexPath);\n * - invalidateCoordinates() — «сброс координат» при viewport resize:\n * coordHash всех записей обнуляется, содержимое сохраняется.\n */\n\nimport { createHash } from 'node:crypto';\nimport type { VslObject } from '../types/vsl';\n\n/** Запись кэша: объект VSL + хэши содержимого и координат. */\nexport interface CacheEntry {\n /** Кэшированный VslObject (поддерево ch не хранится — дети в своих записях). */\n object: VslObject;\n /** sha256 от {t, r?, st?, txt?, act?} в фиксированном порядке ключей. */\n contentHash: string;\n /** sha256 от {p, s}; пустая строка — координаты инвалидированы (resize). */\n coordHash: string;\n}\n\nfunction sha256(payload: string): string {\n return createHash('sha256').update(payload).digest('hex');\n}\n\n/**\n * contentHash: только смысловые поля, фиксированный порядок t→r→st→txt→act.\n * Опциональные поля включаются только при наличии — сериализация детерминирована.\n */\nexport function computeContentHash(object: VslObject): string {\n const payload: Record<string, unknown> = { t: object.t };\n if (object.r !== undefined) payload.r = object.r;\n if (object.st !== undefined) payload.st = object.st;\n if (object.txt !== undefined) payload.txt = object.txt;\n if (object.act !== undefined) payload.act = object.act;\n return sha256(JSON.stringify(payload));\n}\n\n/** coordHash: только координаты {p, s}. */\nexport function computeCoordHash(object: VslObject): string {\n return sha256(JSON.stringify({ p: object.p, s: object.s }));\n}\n\n/** Публичный контракт Cache Store (ROADMAP T1.2.1). */\nexport interface CacheStore {\n /** Сохранить объект под явным id (ROADMAP T1.2.1: cache.set(id, element)). */\n set(id: string, object: VslObject): void;\n /** Запись по id или undefined. */\n get(id: string): CacheEntry | undefined;\n /** Наличие записи. */\n has(id: string): boolean;\n /** Точечная инвалидация (§5.2 cache.invalidate). */\n invalidate(id: string): void;\n /** MVP-семантика (решение dc_5): '*' | точный id | 'tag' → префикс 'tag_'. */\n invalidateBySelector(selector: string): void;\n /** Viewport resize (§5.2 «сброс координат»): обнулить coordHash, объекты сохранить. */\n invalidateCoordinates(): void;\n /** Полный сброс (§5.2 cache.clear). */\n clear(): void;\n /** Все id в порядке вставки. */\n keys(): string[];\n /** Число записей. */\n readonly size: number;\n}\n\nexport function createCacheStore(): CacheStore {\n const entries = new Map<string, CacheEntry>();\n\n return {\n set(id: string, object: VslObject): void {\n entries.set(id, {\n object,\n contentHash: computeContentHash(object),\n coordHash: computeCoordHash(object),\n });\n },\n\n get(id: string): CacheEntry | undefined {\n return entries.get(id);\n },\n\n has(id: string): boolean {\n return entries.has(id);\n },\n\n invalidate(id: string): void {\n entries.delete(id);\n },\n\n invalidateBySelector(selector: string): void {\n if (selector === '*') {\n entries.clear();\n return;\n }\n if (entries.has(selector)) {\n entries.delete(selector);\n return;\n }\n const prefix = `${selector}_`;\n for (const id of entries.keys()) {\n if (id.startsWith(prefix)) entries.delete(id);\n }\n },\n\n invalidateCoordinates(): void {\n for (const [id, entry] of entries) {\n entries.set(id, { ...entry, coordHash: '' });\n }\n },\n\n clear(): void {\n entries.clear();\n },\n\n keys(): string[] {\n return [...entries.keys()];\n },\n\n get size(): number {\n return entries.size;\n },\n };\n}","/**\n * Cache invalidation через MutationObserver (T1.2.2, ROADMAP.md M1.2; ARCHITECTURE.md §5.2).\n *\n * attachMutationObserver(root, store) подписывается на мутации поддерева root\n * и точечно инвалидирует записи Cache Store:\n * - childList (изменился состав детей) → родительский элемент (его поле ch);\n * - attributes → сам целевой элемент;\n * - characterData → owner-элемент текстового узла.\n *\n * Маппинг element → id воспроизводит арифметику domExtractor + vslBuilder\n * (id = tag_indexPath): подъём от элемента до root, на каждом уровне — индекс\n * среди ЭЛЕМЕНТНЫХ детей (Array.from(parent.children)); domExtractor считает\n * indexPath по живому DOM ДО фильтрации, поэтому индексы совпадают 1:1.\n * Контракт: root наблюдателя === корень extraction (document.body по умолчанию).\n *\n * Если id элемента мутации нет в store — инвалидируется ближайший предок из\n * store (подъём вверх до root). Root сам не имеет VSL-записи (extractDomTree\n * не включает корень) → childList на прямых детях root — no-op в MVP.\n *\n * Известное MVP-ограничение (соответствует дизайну фикстур dc_6: добавления/\n * удаления только в конец списков детей): вставка/удаление в середину сдвигает\n * indexPath последующих сиблингов — их id меняются; точечная инвалидация\n * «переехавших» записей вне MVP, итоговую консистентность гарантирует diff\n * по полному переизвлечению (T1.2.4).\n */\n\nimport type { CacheStore } from './cacheStore';\n\n/** Результат attachMutationObserver: наблюдатель + ручная отписка. */\nexport interface MutationObserverHandle {\n /** Живой MutationObserver (для тестов и расширенного контроля). */\n observer: MutationObserver;\n /** Отписаться от мутаций. */\n disconnect(): void;\n}\n\n/** id = tag_indexPath — в точности формула vslBuilder.toVslObject. */\nfunction elementId(el: Element, root: Element): string | null {\n const indexPath: number[] = [];\n let current: Element | null = el;\n while (current !== null && current !== root) {\n const parent: HTMLElement | null = current.parentElement;\n if (parent === null) return null; // элемент вне поддерева root\n indexPath.unshift(Array.from(parent.children).indexOf(current));\n current = parent;\n }\n if (current !== root || indexPath.length === 0) return null; // сам root\n return `${el.tagName.toLowerCase()}_${indexPath.join('_')}`;\n}\n\n/**\n * Инвалидация цели мутации: собственный id из store — удаляется; иначе\n * ближайший предок из store. Цели вне root / сам root — no-op (см. шапку).\n */\nfunction invalidateMutationTarget(node: Node, root: Element, store: CacheStore): void {\n const start = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;\n if (start === null || start === root || !root.contains(start)) return;\n\n let current: Element | null = start;\n while (current !== null && current !== root) {\n const id = elementId(current, root);\n if (id !== null && store.has(id)) {\n store.invalidate(id);\n return;\n }\n current = current.parentElement;\n }\n}\n\n/**\n * Подписывает store на мутации поддерева root (§5.2 «DOM mutation observer →\n * invalidation изменённых элементов»). Возвращает handle с disconnect().\n */\nexport function attachMutationObserver(root: Element, store: CacheStore): MutationObserverHandle {\n const observer = new MutationObserver((records) => {\n for (const record of records) {\n invalidateMutationTarget(record.target, root, store);\n }\n });\n observer.observe(root, {\n childList: true,\n attributes: true,\n characterData: true,\n subtree: true,\n });\n return { observer, disconnect: () => observer.disconnect() };\n}","/**\n * Diff Engine (T1.2.3, ROADMAP.md M1.2; формат ARCHITECTURE.md §6.2).\n *\n * diffVslDocuments(prev, next, options) сравнивает два VslDocument по ПЛОСКОМУ\n * индексу id (id = tag_indexPath глобально уникальны — база M1.1) и выдаёт\n * дифф строго в формате §6.2:\n * - added — новые объекты полными поддеревьями (tree order next);\n * - modified — id + только изменившиеся поля в порядке канона §4\n * (t→r→p→s→st→txt→act); УДАЛЁННОЕ опциональное поле передаётся явным null\n * (r/st/txt/act; решение dc_5: отсутствие в Partial амбивалентно —\n * «не изменилось» vs «удалено»); t/p/s/id никогда не null;\n * - removed — {id} в tree order prev;\n * - unchanged_refs — id неизменённых объектов (tree order next).\n *\n * Равенство объектов — по паре хэшей Cache Store (решение dc_4):\n * contentHash {t,r?,st?,txt?,act?} + coordHash {p,s}, sha256 по фиксированному\n * порядку ключей. Поле ch в сравнении НЕ участвует — diff плоский, дети\n * отслеживаются собственными id: изменение состава/содержимого детей проявляется\n * их собственными added/removed/modified записями. Canvas не диффуется —\n * сравнивается только дерево objects.\n *\n * Пустой дифф валиден: идентичные документы → все id в unchanged_refs,\n * остальные списки пусты.\n *\n * Предусловие: id уникальны в пределах каждого документа (гарантия M1.1).\n */\n\nimport { computeCoordHash, computeContentHash } from '../cache/cacheStore';\nimport type { VslDocument, VslObject, VslState, VslType } from '../types/vsl';\n\n/** Изменившийся объект: id + только изменившиеся поля (порядок канона §4). */\nexport interface VslModifiedObject {\n id: string;\n t?: VslType;\n /** null = опциональное поле r удалено в next. */\n r?: string | null;\n p?: [number, number];\n s?: [number, number];\n /** null = состояние st удалено в next. */\n st?: VslState | null;\n /** null = текст txt удалён в next. */\n txt?: string | null;\n /** null = список действий act удалён в next. */\n act?: string[] | null;\n}\n\n/** Удалённый объект: только ссылка на id (§6.2). */\nexport interface VslRemovedObject {\n id: string;\n}\n\n/** Блок changes формата §6.2. */\nexport interface VslDiffChanges {\n /** Новые объекты — полные поддеревья, tree order next. */\n added: VslObject[];\n /** Изменившиеся объекты, tree order next. */\n modified: VslModifiedObject[];\n /** Удалённые объекты, tree order prev. */\n removed: VslRemovedObject[];\n /** id неизменённых объектов, tree order next. */\n unchanged_refs: string[];\n}\n\n/** Дифф двух VslDocument (формат ARCHITECTURE.md §6.2). */\nexport interface VslDiff {\n /** Версия нового состояния (счётчик session). */\n diff_version: number;\n /** Версия базового (предыдущего) состояния. */\n base_version: number;\n /** ISO 8601. */\n timestamp: string;\n changes: VslDiffChanges;\n}\n\n/** Опции diffVslDocuments: версии заголовка обязательны (явный контракт session). */\nexport interface DiffOptions {\n diffVersion: number;\n baseVersion: number;\n /** ISO 8601; по умолчанию — текущее время (недетерминированно; в тестах фиксировать). */\n timestamp?: string;\n}\n\n/** Плоский индекс id → VslObject в tree order (обход дерева в глубину). */\nfunction flatten(objects: readonly VslObject[], map: Map<string, VslObject>): void {\n for (const object of objects) {\n map.set(object.id, object);\n flatten(object.ch ?? [], map);\n }\n}\n\n/** Равенство объектов по паре хэшей dc_4 (ch/id в сравнении не участвуют). */\nfunction objectsEqual(a: VslObject, b: VslObject): boolean {\n return (\n computeContentHash(a) === computeContentHash(b) &&\n computeCoordHash(a) === computeCoordHash(b)\n );\n}\n\nfunction tuple2Equal(\n a: [number, number] | null | undefined,\n b: [number, number] | null | undefined,\n): boolean {\n if (a === null || a === undefined) return b === null || b === undefined;\n if (b === null || b === undefined) return false;\n return a[0] === b[0] && a[1] === b[1];\n}\n\nfunction stringArrayEqual(\n a: readonly string[] | undefined,\n b: readonly string[] | undefined,\n): boolean {\n if (a === undefined || b === undefined) return a === b;\n return a.length === b.length && a.every((value, i) => value === b[i]);\n}\n\n/** Field-by-field в порядке канона §4; удалённые опциональные поля → явный null (dc_5). */\nfunction buildModified(prev: VslObject, next: VslObject): VslModifiedObject {\n const result: VslModifiedObject = { id: next.id };\n if (prev.t !== next.t) result.t = next.t;\n if (prev.r !== next.r) result.r = next.r ?? null;\n if (!tuple2Equal(prev.p, next.p)) result.p = next.p;\n if (!tuple2Equal(prev.s, next.s)) result.s = next.s;\n if (prev.st !== next.st) result.st = next.st ?? null;\n if (prev.txt !== next.txt) result.txt = next.txt ?? null;\n if (!stringArrayEqual(prev.act, next.act)) result.act = next.act ?? null;\n return result;\n}\n\n/** Сравнивает два VSL-документа и возвращает дифф формата §6.2 (см. шапку модуля). */\nexport function diffVslDocuments(\n prev: VslDocument,\n next: VslDocument,\n options: DiffOptions,\n): VslDiff {\n const prevFlat = new Map<string, VslObject>();\n const nextFlat = new Map<string, VslObject>();\n flatten(prev.objects, prevFlat);\n flatten(next.objects, nextFlat);\n\n const added: VslObject[] = [];\n const modified: VslModifiedObject[] = [];\n const unchangedRefs: string[] = [];\n\n // Tree order next (DFS): добавленные корни поддеревьев → изменённые →\n // неизменённые. Потомки добавленного корня НЕ дублируются отдельными записями\n // added — они уже перенесены внутри ch добавленного поддерева (§6.2).\n const walk = (objects: readonly VslObject[], insideAdded: boolean): void => {\n for (const nextObject of objects) {\n if (insideAdded) {\n walk(nextObject.ch ?? [], true);\n continue;\n }\n const prevObject = prevFlat.get(nextObject.id);\n if (prevObject === undefined) {\n added.push(nextObject); // корень добавленного поддерева\n walk(nextObject.ch ?? [], true);\n } else if (objectsEqual(prevObject, nextObject)) {\n unchangedRefs.push(nextObject.id);\n walk(nextObject.ch ?? [], false);\n } else {\n modified.push(buildModified(prevObject, nextObject));\n walk(nextObject.ch ?? [], false);\n }\n }\n };\n walk(next.objects, false);\n\n // Tree order prev: удалённые.\n const removed: VslRemovedObject[] = [];\n for (const id of prevFlat.keys()) {\n if (!nextFlat.has(id)) removed.push({ id });\n }\n\n return {\n diff_version: options.diffVersion,\n base_version: options.baseVersion,\n timestamp: options.timestamp ?? new Date().toISOString(),\n changes: { added, modified, removed, unchanged_refs: unchangedRefs },\n };\n}","/**\n * enrichWithVision (T1.5.4, dev_8): async-шаг конвейера ДО buildVslDocument —\n * Level 5 vision fallback (§2.2.1, ARCHITECTURE.md:L174-202).\n *\n * Отбор кандидатов: элементы, которые builder ОТБРОСИТ (t === null и\n * !isIncludedInVsl — нетипизированный, score < 3, без включённых детей).\n * Для каждого: extract (bounding box → WebP base64, кэш по hash) → classify\n * (порт VisionClassifier) → аннотация t + vf/vf_meta НА МЕСТЕ (мутирует\n * дерево, как segmentTree) — после этого builder включает элемент в VSL\n * (типизирован), а convert переносит vf/vf_meta в объект (AC[5]).\n *\n * Best-effort (vision не должен ломать снапшот): ошибки extract/classify\n * одного элемента не роняют обогащение — элемент остаётся отброшенным\n * (поведение как до vision), ошибка учитывается в failedCount.\n *\n * Кэш и AC[3]: запись FragmentCacheEntry возвращается extract по ссылке и\n * хранит classification — повторный extract того же hash даёт тот же entry,\n * поэтому классификатор вызывается один раз на фрагмент. Обход СТРОГО\n * последовательный: параллельный запуск классифицировал бы одинаковые\n * фрагменты дважды (оба extract завершились бы до первой записи classification).\n *\n * Данные изображений НЕ попадают в VslDocument (бюджет 10-100 KB) —\n * возвращаются в VisualFragmentStore (DEC-015, lazy loading §4.4) для\n * DecideInput.visualFragments.\n */\n\nimport { isIncludedInVsl } from '../builder/vslBuilder';\nimport type { VisualFragmentStore } from '../llm/types';\nimport type { SegmentedElement } from '../segmentation/segmenter';\nimport type { FragmentExtraction, FragmentExtractor } from './fragmentExtractor';\nimport type { VisionClassifier } from './types';\n\n/** Зависимости enrichWithVision (Dependency Inversion — фейки в unit-тестах). */\nexport interface EnrichWithVisionDeps {\n /** Классификатор фрагментов (LlmVisionClassifier; в тестах — фейк). */\n classifier: VisionClassifier;\n /** Извлекатель фрагментов с кэшем по hash (AC[3]). */\n extractor: FragmentExtractor;\n}\n\n/** Результат обогащения: счётчики + стор данных фрагментов (lazy, DEC-015). */\nexport interface EnrichWithVisionResult {\n /** Число спасённых элементов (аннотированы t + vf/vf_meta). */\n enrichedCount: number;\n /** Число кандидатов, пропущенных из-за ошибок extract/classify (best-effort). */\n failedCount: number;\n /** Данные фрагментов vf_id → {mediaType, data} для DecideInput.visualFragments. */\n fragments: VisualFragmentStore;\n}\n\n/**\n * Собирает vision-кандидатов обходом дерева (pre-order): элемент отбрасывается\n * builder-ом ⟺ t === null и !isIncludedInVsl (нетипизированный, score < 3,\n * без включённых детей) — ровно их спасает vision fallback.\n */\nfunction collectCandidates(elements: readonly SegmentedElement[]): SegmentedElement[] {\n const candidates: SegmentedElement[] = [];\n const visit = (element: SegmentedElement): void => {\n if (element.t === null && !isIncludedInVsl(element)) candidates.push(element);\n for (const child of element.ch) {\n visit(child);\n }\n };\n for (const element of elements) visit(element);\n return candidates;\n}\n\n/** Подсказка классификатору: контекст элемента (тег; текст, если есть). */\nfunction buildHint(element: SegmentedElement): string {\n return element.txt === null ? `tag=${element.tag}` : `tag=${element.tag}; text: ${element.txt}`;\n}\n\n/**\n * Обогащает дерево элементов vision-классификацией (Level 5 fallback).\n * МУТИРУЕТ элементы на месте (t + vf/vf_meta) — как segmentTree; после\n * обогащения buildVslDocument включает спасённые элементы в VSL.\n * Последовательный обход (см. шапку: кэш + AC[3]).\n */\nexport async function enrichWithVision(\n elements: SegmentedElement[],\n deps: EnrichWithVisionDeps,\n): Promise<EnrichWithVisionResult> {\n const fragments = new Map<string, import('../llm/types').VisualFragmentData>();\n let enrichedCount = 0;\n let failedCount = 0;\n\n for (const element of collectCandidates(elements)) {\n let extraction: FragmentExtraction | null;\n try {\n extraction = await deps.extractor.extract(element.rect);\n } catch {\n // Ошибки capture/crop не глушатся в extract (его контракт) — best-effort\n // здесь: элемент остаётся отброшенным, как до vision.\n failedCount += 1;\n continue;\n }\n if (extraction === null) continue; // пустая геометрия — не ошибка, элемент остаётся отброшенным\n\n const { entry } = extraction;\n let classification = entry.classification;\n try {\n if (classification === undefined) {\n classification = await deps.classifier.classify(entry.data, buildHint(element));\n entry.classification = classification; // sticky в кэше: повторные hash-hit без вызова модели (AC[3])\n }\n } catch {\n failedCount += 1;\n continue;\n }\n element.t = classification.type;\n element.vf = entry.vfId;\n element.vf_meta = {\n type: classification.type,\n format: entry.meta.format,\n size: entry.meta.size,\n hash: entry.meta.hash,\n cached_at: entry.meta.cached_at,\n };\n fragments.set(entry.vfId, entry.data);\n enrichedCount += 1;\n }\n\n return { enrichedCount, failedCount, fragments };\n}","/**\n * VSL Snapshot Session (T1.2.4, ROADMAP.md M1.2; ARCHITECTURE.md §2.4/§5.2/§6.3).\n *\n * Stateful-фасад над пайплайном capture→segment→build→cache→diff:\n * - первый вызов или смена URL → полный VslDocument; кэш очищается и\n * заполняется объектами документа; счётчик версий сбрасывается в 1;\n * - тот же URL → VslDiff последнего документа против нового (формат §6.2):\n * diff_version = version + 1, base_version = version;\n * - viewport изменился при том же URL → дополнительно\n * store.invalidateCoordinates() — «сброс координат» §5.2, НЕ полный сброс.\n *\n * Состояние {lastUrl, lastViewport, lastDocument, version} — в памяти\n * экземпляра; VslDocument (канон §4) версиями НЕ расширяется.\n *\n * Политика кэша — store отражает текущий документ: full → clear + заполнение;\n * diff → удалённые invalidate, добавленные (поддеревья) и изменённые set из\n * нового документа; неизменённые остаются кэшированными (после resize — с\n * обнулённым coordHash: наблюдаемый эффект invalidateCoordinates).\n *\n * input.url/viewport/timestamp/background передаются в BuildOptions ЯВНО —\n * дефолты builder (window.innerWidth/innerHeight/location.href/document.title)\n * вычислялись бы от глобалов: недетерминированны в тестах и недоступны в Node;\n * title — из input, иначе root.ownerDocument.title (портативный фолбэк — фикс\n * packaging-smoke demo:cache-diff).\n */\n\nimport { extractDomTree } from '../capture/domExtractor';\nimport { segmentTree, type SegmentedElement } from '../segmentation/segmenter';\nimport { buildVslDocument } from '../builder/vslBuilder';\nimport type { BuildOptions } from '../builder/vslBuilder';\nimport { createCacheStore } from '../cache/cacheStore';\nimport type { CacheStore } from '../cache/cacheStore';\nimport { diffVslDocuments } from '../diff/diffEngine';\nimport type { DiffOptions, VslDiff } from '../diff/diffEngine';\nimport type { VslDocument, VslObject } from '../types/vsl';\nimport { enrichWithVision, type EnrichWithVisionDeps } from '../vision/enrichWithVision';\nimport type { VisualFragmentStore } from '../llm/types';\n\n/** Результат snapshot(): полный документ (1-й вызов / URL change) или дифф. */\nexport type SnapshotResult = VslDocument | VslDiff;\n\n/** Type guard: различение диффа и полного документа (контракт T1.2.4). */\nexport function isVslDiff(result: SnapshotResult): result is VslDiff {\n return 'changes' in result;\n}\n\n/**\n * Результат snapshotWithVision (T1.5.5): снапшот (документ/дифф) + ДАННЫЕ\n * фрагментов (base64) — стора в документе нет по контракту (DEC-015):\n * документ несёт только vf/vf_meta, данные идут в decide({visualFragments}).\n */\nexport interface VisionSnapshotResult {\n snapshot: SnapshotResult;\n fragments: VisualFragmentStore;\n}\n\n/** Входные параметры снапшота; url/viewport обязательны (инвалидация §5.2). */\nexport interface SnapshotInput {\n /** URL страницы: изменение → полный сброс кэша и новый полный документ. */\n url: string;\n /** Текущий viewport: изменение при том же URL → сброс координат кэша. */\n viewport: { width: number; height: number };\n title?: string;\n /** ISO 8601; фиксировать в тестах (детерминизм canvas.timestamp и диффа). */\n timestamp?: string;\n background?: string;\n}\n\n/** Плоский индекс id → VslObject (обход дерева в глубину). */\nfunction flattenObjects(objects: readonly VslObject[], map: Map<string, VslObject>): void {\n for (const object of objects) {\n map.set(object.id, object);\n flattenObjects(object.ch ?? [], map);\n }\n}\n\n/** Сохраняет поддерево в store пообъектно (id уникальны — база M1.1). */\nfunction storeTree(store: CacheStore, objects: readonly VslObject[]): void {\n for (const object of objects) {\n store.set(object.id, object);\n storeTree(store, object.ch ?? []);\n }\n}\n\n/**\n * Session-фасад (§2.4 «первый вызов → полный JSON, последующие → дифф»).\n * Store инъекцией — тесты наблюдают coordHash-инвалидацию напрямую.\n */\nexport class VslSnapshotSession {\n private readonly store: CacheStore;\n private lastUrl: string | null = null;\n private lastViewport: { width: number; height: number } | null = null;\n private lastDocument: VslDocument | null = null;\n private version = 0;\n\n constructor(store: CacheStore = createCacheStore()) {\n this.store = store;\n }\n\n /**\n * Сбрасывает состояние сессии (URL, viewport, документ, версия).\n * Следующий вызов snapshot() вернёт полный документ (как первый вызов).\n */\n clear(): void {\n this.store.clear();\n this.lastUrl = null;\n this.lastViewport = null;\n this.lastDocument = null;\n this.version = 0;\n }\n\n /** Возвращает последний полный документ (не diff). */\n getLastDocument(): VslDocument | null {\n return this.lastDocument;\n }\n\n /** Текущая версия документа (инкрементируется при каждом diff). */\n getVersion(): number {\n return this.version;\n }\n\n /**\n * Строит VSL из DOM поддерева root (extractDomTree → segmentTree →\n * buildVslDocument) и возвращает полный документ либо дифф (см. шапку).\n */\n snapshot(root: Element, input: SnapshotInput): SnapshotResult {\n const elements = segmentTree(extractDomTree(root));\n return this.commit(elements, this.buildOptions(root, input), input);\n }\n\n /**\n * Вариант snapshot() для pre-extracted elements (MCP сервер, Node.js контекст).\n * Принимает ExtractedElement[] напрямую (без DOM Element), проходит тот же\n * pipeline: segmentTree → buildVslDocument → cache/diff.\n */\n snapshotFromElements(\n extractedElements: readonly import('../capture/domExtractor').ExtractedElement[],\n input: SnapshotInput,\n ): SnapshotResult {\n const elements = segmentTree(extractedElements as never);\n return this.commit(elements, this.buildOptionsFromInput(input), input);\n }\n\n /**\n * Vision-вариант конвейера (T1.5.5): segmentTree → enrichWithVision (Level 5\n * fallback — аннотация t + vf/vf_meta на элементах ДО сборки) → buildVslDocument\n * → cache/diff — та же логика состояния, что snapshot(). Синхронный snapshot()\n * сохранён (существующие контракты/тесты не ломаются); данные фрагментов\n * (base64) возвращаются ОТДЕЛЬНО — в документе только метаданные (DEC-015).\n */\n async snapshotWithVision(\n root: Element,\n input: SnapshotInput,\n vision: EnrichWithVisionDeps,\n ): Promise<VisionSnapshotResult> {\n const elements = segmentTree(extractDomTree(root));\n const visionResult = await enrichWithVision(elements, vision);\n const snapshot = this.commit(elements, this.buildOptions(root, input), input);\n return { snapshot, fragments: visionResult.fragments };\n }\n\n /**\n * Vision-вариант для pre-extracted elements (MCP сервер, Node.js контекст).\n * Полный pipeline: segmentTree → enrichWithVision → buildVslDocument → cache/diff.\n */\n async snapshotWithVisionFromElements(\n extractedElements: readonly import('../capture/domExtractor').ExtractedElement[],\n input: SnapshotInput,\n vision: EnrichWithVisionDeps,\n ): Promise<VisionSnapshotResult> {\n const elements = segmentTree(extractedElements as never);\n const visionResult = await enrichWithVision(elements, vision);\n const snapshot = this.commit(elements, this.buildOptionsFromInput(input), input);\n return { snapshot, fragments: visionResult.fragments };\n }\n\n /** BuildOptions из input + портативный title (логика бывшего snapshot()). */\n private buildOptions(root: Element, input: SnapshotInput): BuildOptions {\n const options: BuildOptions = {\n viewport: { width: input.viewport.width, height: input.viewport.height },\n url: input.url,\n };\n // title: из input, иначе из документа root — ПОРТАТИВНО, без глобального\n // document (дефолт builder document.title упал бы в Node — packaging-smoke).\n options.title = input.title ?? root.ownerDocument.title;\n if (input.timestamp !== undefined) options.timestamp = input.timestamp;\n if (input.background !== undefined) options.background = input.background;\n return options;\n }\n\n /**\n * BuildOptions из input без DOM Element (для snapshotFromElements).\n * Title берётся только из input (нет фолбэка на root.ownerDocument.title).\n */\n private buildOptionsFromInput(input: SnapshotInput): BuildOptions {\n const options: BuildOptions = {\n viewport: { width: input.viewport.width, height: input.viewport.height },\n url: input.url,\n };\n if (input.title !== undefined) options.title = input.title;\n if (input.timestamp !== undefined) options.timestamp = input.timestamp;\n if (input.background !== undefined) options.background = input.background;\n return options;\n }\n\n /** Сборка документа + кэш/дифф (логика бывшего snapshot(), см. шапку модуля). */\n private commit(\n elements: readonly SegmentedElement[],\n options: BuildOptions,\n input: SnapshotInput,\n ): SnapshotResult {\n const nextDocument = buildVslDocument(elements, options);\n\n const firstCall = this.lastDocument === null;\n const urlChanged = !firstCall && input.url !== this.lastUrl;\n\n if (firstCall || urlChanged) {\n this.store.clear();\n storeTree(this.store, nextDocument.objects);\n this.version = 1;\n this.lastUrl = input.url;\n this.lastViewport = { ...input.viewport };\n this.lastDocument = nextDocument;\n return nextDocument;\n }\n\n const viewportChanged =\n this.lastViewport === null ||\n input.viewport.width !== this.lastViewport.width ||\n input.viewport.height !== this.lastViewport.height;\n if (viewportChanged) this.store.invalidateCoordinates();\n\n const diffOptions: DiffOptions = {\n diffVersion: this.version + 1,\n baseVersion: this.version,\n };\n if (input.timestamp !== undefined) diffOptions.timestamp = input.timestamp;\n const diff = diffVslDocuments(this.lastDocument!, nextDocument, diffOptions);\n\n // Кэш отражает новый документ (см. шапку): неизменённые остаются,\n // изменённые/добавленные обновляются из fresh-документа, удалённые —\n // инвалидируются.\n const fresh = new Map<string, VslObject>();\n flattenObjects(nextDocument.objects, fresh);\n for (const removed of diff.changes.removed) this.store.invalidate(removed.id);\n for (const modified of diff.changes.modified) {\n const object = fresh.get(modified.id);\n if (object !== undefined) this.store.set(object.id, object);\n }\n for (const added of diff.changes.added) storeTree(this.store, [added]);\n\n this.version = diff.diff_version;\n this.lastUrl = input.url;\n this.lastViewport = { ...input.viewport };\n this.lastDocument = nextDocument;\n return diff;\n }\n}","/**\n * Типы LLM Integration (M1.3, ROADMAP.md T1.3.1–T1.3.5).\n *\n * Архитектурная база:\n * - ARCHITECTURE.md §2.5 — LLM Integration Layer, промпт-шаблон {vsl_json}+{goal};\n * - ARCHITECTURE.md §7 — Action Model, формат ответа LLM (§7.4);\n * - ARCHITECTURE.md §9.4 — API адаптеров: decide({vslJson, goal});\n * - ARCHITECTURE.md §10.3 — retry 3× exponential backoff;\n * - DESIGN_SYSTEM.md §4.4 — visual fragments (vf/vf_meta, forward-compatible);\n * - DEC-002 — model-agnostic: адаптеры провайдеров без SDK-зависимостей.\n *\n * Транспорт (нативный fetch, node>=18) инъецируется через LlmTransport —\n * тесты работают на моках без сети (решение пользователя, clarification M1.3).\n * Ноль runtime-зависимостей: официальные SDK провайдеров НЕ используются.\n */\n\nimport type { VslDiff } from '../diff/diffEngine';\nimport type { VslDocument, VslObject } from '../types/vsl';\n\n/** Вход LLM-адаптера: полный документ или дифф (§2.5, §11.2 — diff-first). */\nexport type VslInput = VslDocument | VslDiff;\n\n/** Действие LLM — формат ответа модели (ARCHITECTURE.md §7.4). */\nexport interface LlmAction {\n /** Имя действия из Action Model (§7.1–7.3; см. VALID_ACTIONS в llm/actions). */\n action: string;\n /** ID элемента-цели в VSL JSON — обязателен для действий с целью. */\n target_id?: string;\n /** Значение: текст ввода для type, option для select и т.п. */\n value?: string | null;\n /** Объяснение выбора модели (свободный текст). */\n reasoning?: string;\n}\n\n/** Нормализованное использование токенов (маппинг из usage провайдера). */\nexport interface LlmUsage {\n inputTokens?: number;\n outputTokens?: number;\n}\n\n/** Ответ провайдера на sendPrompt (T1.3.1): raw-данные БЕЗ валидации. */\nexport interface LlmResponse {\n /** Текстовое содержимое ответа (если провайдер его вернул). */\n text: string | null;\n /** Raw-объект действия из tool call (валидацию выполняет decide). */\n action?: unknown;\n /** Идентификатор модели, сформировавшей ответ. */\n model: string;\n usage?: LlmUsage;\n}\n\n/** Метаданные visual fragment (DESIGN_SYSTEM.md §4.4). */\nexport interface VslFragmentMeta {\n type?: string;\n format?: string;\n size?: [number, number];\n hash?: string;\n cached_at?: string;\n}\n\n/** Объект VSL с forward-compatible полями visual fragments (§4.4). */\nexport type VslObjectWithVf = VslObject & {\n /** Ссылка на визуальный фрагмент (например, «emb_abc123»). */\n vf?: string;\n /** Метаданные фрагмента. */\n vf_meta?: VslFragmentMeta;\n};\n\n/** Данные изображения фрагмента для мультимодальной подачи (DEC-015). */\nexport interface VisualFragmentData {\n /** MIME-тип изображения, например «image/webp». */\n mediaType: string;\n /** Base64-данные изображения (без data:-префикса). */\n data: string;\n}\n\n/** Стор данных фрагментов: ключ — vf-ссылка объекта («emb_abc123»). */\nexport type VisualFragmentStore = ReadonlyMap<string, VisualFragmentData>;\n\n/**\n * Провайдер-независимая content-часть user-сообщения (T1.5.4, вариант C):\n * адаптеры маппят её в формат провайдера (OpenAI image_url / Anthropic image\n * base64-блок); форма image-части совпадает с VisualFragmentData (DEC-015).\n */\nexport type LlmContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mediaType: string; data: string };\n\n/**\n * Провайдер-независимое определение tool: одна JSON Schema на оба провайдера\n * (паттерн schema.ts: OpenAI function.parameters / Anthropic input_schema).\n */\nexport interface LlmToolDef {\n /** Имя tool (execute_action / classify_fragment). */\n name: string;\n /** Описание для модели. */\n description: string;\n /** JSON Schema аргументов. */\n schema: Record<string, unknown>;\n}\n\n/** Опции sendPrompt сверх базовых аргументов. */\nexport interface SendPromptOptions {\n /**\n * Данные изображений для vf-ссылок входа: image-блоки включаются\n * только для ссылок, присутствующих в сторе (lazy loading, §4.4);\n * без стора подача остаётся текстовой.\n */\n visualFragments?: VisualFragmentStore;\n}\n\n/** Вход decide (ARCHITECTURE.md §9.4). */\nexport interface DecideInput {\n /** VSL JSON: VslDocument или VslDiff от VslSnapshotSession. */\n vslJson: VslInput;\n /** Цель пользователя на естественном языке. */\n goal: string;\n /** Данные изображений для vf-ссылок (см. SendPromptOptions). */\n visualFragments?: VisualFragmentStore;\n}\n\n/**\n * Инъекция HTTP-транспорта: подмножество сигнатуры fetch.\n * Прод — нативный fetch; тесты — мок (ноль runtime-зависимостей).\n */\nexport type LlmTransport = (url: string, init: RequestInit) => Promise<Response>;\n\n/** Функция задержки (инъекция для детерминированных тестов retry). */\nexport type Sleep = (ms: number) => Promise<void>;\n\n/** Конфиг адаптера провайдера (§9.4: new OpenAIAdapter({apiKey, model})). */\nexport interface LlmAdapterConfig {\n /** API-ключ провайдера. */\n apiKey: string;\n /** Модель; дефолт задаёт адаптер (gpt-4o / claude-sonnet-4-20250514). */\n model?: string;\n /** База API; по умолчанию — официальный endpoint провайдера. */\n baseUrl?: string;\n /** Попыток при сбоях API (§10.3); по умолчанию 3. */\n maxRetries?: number;\n /** Транспорт; по умолчанию — нативный fetch. */\n transport?: LlmTransport;\n /** Задержка между retry-попытками (инъекция для детерминированных тестов; §10.3). */\n sleep?: Sleep;\n}\n\n/** Абстрактный интерфейс LLM Adapter (ROADMAP.md T1.3.1). */\nexport interface LlmAdapter {\n /** Низкий уровень: VSL JSON + задача → ответ провайдера (raw action). */\n sendPrompt(\n vslJson: VslInput,\n task: string,\n options?: SendPromptOptions,\n ): Promise<LlmResponse>;\n /** Convenience (§9.4): решение для цели с валидацией действия (§7.4). */\n decide(input: DecideInput): Promise<LlmAction>;\n}\n\n/**\n * Низкоуровневый вызов провайдера (T1.5.4, вариант C): system + user content +\n * один tool → LlmResponse. Реализуют OpenAIAdapter/AnthropicAdapter (sendRaw);\n * потребитель — LlmVisionClassifier (tool «classify_fragment»). Отдельный\n * интерфейс (не extends LlmAdapter): структурная типизация, контракт LlmAdapter\n * не расширяется — существующие реализации и моки не ломаются.\n */\nexport interface RawLlmCaller {\n sendRaw(\n system: string,\n userContent: readonly LlmContentPart[],\n tool: LlmToolDef,\n ): Promise<LlmResponse>;\n}\n\n/** Базовая ошибка LLM-слоя (транспорт/HTTP/формат ответа провайдера). */\nexport class LlmError extends Error {\n /** HTTP-статус, если ошибка пришла от API (для классификации retry). */\n readonly status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.name = 'LlmError';\n this.status = status;\n }\n}\n\n/** Ответ модели не прошёл валидацию: неизвестное действие, невалидный target_id. */\nexport class LlmValidationError extends LlmError {\n constructor(message: string) {\n super(message);\n this.name = 'LlmValidationError';\n }\n}","/**\n * Action Model (ARCHITECTURE.md §7) для M1.3: канонический список действий,\n * классификация по цели и валидация ответа LLM (§7.4, T1.3.5).\n *\n * Валидация (§7.4 «Action Executor», п.1): action — валидное действие,\n * target_id существует в поданном VSL JSON. Поля value/reasoning проходят\n * сквозь с мягкой нормализацией типов (string | null).\n */\n\nimport { isVslDiff } from '../session/snapshotSession';\nimport type { VslObject } from '../types/vsl';\nimport { LlmValidationError } from './types';\nimport type { LlmAction, VslInput } from './types';\n\n/**\n * Канонический список действий Action Model (§7.1–7.3) — 24 действия:\n * базовые (10) + расширенные (9) + навигационные (4) + download (1).\n */\nexport const VALID_ACTIONS = [\n // §7.1 Базовые\n 'click',\n 'type',\n 'clear',\n 'scroll',\n 'hover',\n 'focus',\n 'blur',\n 'select',\n 'check',\n 'uncheck',\n // §7.2 Расширенные\n 'drag',\n 'drop',\n 'submit',\n 'reset',\n 'open',\n 'close',\n 'expand',\n 'collapse',\n 'wait',\n // §7.3 Навигационные\n 'navigate',\n 'go_back',\n 'go_forward',\n 'refresh',\n // §7.4 Загрузка файлов\n 'download',\n] as const;\n\n/** Имя валидного действия Action Model. */\nexport type ValidAction = (typeof VALID_ACTIONS)[number];\n\n/**\n * Действия, требующие target_id (§7.1–7.2): воздействие на элемент/форму.\n * Без цели: scroll, wait, navigate, go_back, go_forward, refresh.\n */\nexport const TARGET_ACTIONS: ReadonlySet<string> = new Set([\n 'click',\n 'type',\n 'clear',\n 'hover',\n 'focus',\n 'blur',\n 'select',\n 'check',\n 'uncheck',\n 'drag',\n 'drop',\n 'submit',\n 'reset',\n 'open',\n 'close',\n 'expand',\n 'collapse',\n 'download',\n]);\n\nconst collectFromObjects = (objects: readonly VslObject[], ids: Set<string>): void => {\n for (const object of objects) {\n ids.add(object.id);\n collectFromObjects(object.ch ?? [], ids);\n }\n};\n\n/**\n * Собирает множество валидных target_id из входа LLM (§7.4 валидация):\n * - VslDocument — все id рекурсивно;\n * - VslDiff — added (рекурсивно) + modified + unchanged_refs;\n * removed НЕ включаются: элементы удалены и не могут быть целью.\n */\nexport function collectIds(vslJson: VslInput): ReadonlySet<string> {\n const ids = new Set<string>();\n if (isVslDiff(vslJson)) {\n collectFromObjects(vslJson.changes.added, ids);\n for (const modified of vslJson.changes.modified) ids.add(modified.id);\n for (const ref of vslJson.changes.unchanged_refs) ids.add(ref);\n } else {\n collectFromObjects(vslJson.objects, ids);\n }\n return ids;\n}\n\n/**\n * Валидирует raw-объект действия из tool call модели (§7.4):\n * - action — строка из VALID_ACTIONS;\n * - для TARGET_ACTIONS target_id обязателен и должен присутствовать в validIds;\n * - value/reasoning — passthrough с мягкой нормализацией (string | null);\n * target_id у действий без цели игнорируется.\n * Бросает LlmValidationError при нарушении любого условия.\n */\nexport function validateAction(raw: unknown, validIds: ReadonlySet<string>): LlmAction {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new LlmValidationError('Model response: action must be a JSON object');\n }\n const record = raw as Record<string, unknown>;\n const action = record.action;\n if (typeof action !== 'string' || !(VALID_ACTIONS as readonly string[]).includes(action)) {\n const shown = typeof action === 'string' ? action : (JSON.stringify(action) ?? 'undefined');\n throw new LlmValidationError(`Unknown action: ${shown}`);\n }\n const result: LlmAction = { action };\n if (TARGET_ACTIONS.has(action)) {\n const targetId = record.target_id;\n if (typeof targetId !== 'string') {\n throw new LlmValidationError(`Action \"${action}\" requires a string target_id`);\n }\n if (!validIds.has(targetId)) {\n throw new LlmValidationError(`target_id \"${targetId}\" not found in VSL JSON`);\n }\n result.target_id = targetId;\n }\n const value = record.value;\n if (typeof value === 'string' || value === null) result.value = value;\n const reasoning = record.reasoning;\n if (typeof reasoning === 'string') result.reasoning = reasoning;\n return result;\n}","/**\n * Промпты LLM (T1.3.4): system prompt (VSL-формат + Action Model + 3 few-shot)\n * и user-промпт по каноническому шаблону ARCHITECTURE.md §2.5.\n *\n * Промпты провайдер-независимы: few-shot включены ТЕКСТОМ в system prompt,\n * поэтому OpenAI (function calling) и Anthropic (tool use) получают идентичную\n * инструкцию; само действие модель возвращает через tool «execute_action»\n * (см. schema.ts). Список действий рендерится из VALID_ACTIONS (actions.ts) —\n * единый источник правды для промпта и enum в JSON Schema.\n *\n * Примечание (neg note f48a9bda4bd5): llm_prompts_examples.md из корня —\n * примеры домена scene editing (Phase 4), НЕ заготовка для этого промпта;\n * текст построен с нуля из ARCHITECTURE §2.5 + §7.\n */\n\nimport { isVslDiff } from '../session/snapshotSession';\nimport { VALID_ACTIONS } from './actions';\nimport type { VslObject } from '../types/vsl';\nimport type { VslInput, VisualFragmentData, VisualFragmentStore } from './types';\n\n/** Few-shot пример: заголовок, компактный VSL-фрагмент, цель, действие. */\nexport interface FewShotExample {\n title: string;\n /** Компактный VSL JSON (иллюстративный фрагмент). */\n vsl: string;\n goal: string;\n /** Ожидаемый ответ модели — JSON действия (формат §7.4). */\n action: string;\n}\n\n/** Три few-shot примера (T1.3.4): клик по кнопке, заполнение формы, навигация. */\nexport const FEW_SHOT_EXAMPLES: readonly FewShotExample[] = [\n {\n title: 'Example 1 — click a button (full document)',\n vsl: '{\"vsl_version\":\"1.0.0\",\"objects\":[{\"id\":\"header_0\",\"t\":\"header\",\"ch\":[{\"id\":\"link_0_0\",\"t\":\"link\",\"txt\":\"Войти\",\"act\":[\"click\"]},{\"id\":\"link_0_1\",\"t\":\"link\",\"txt\":\"Регистрация\",\"act\":[\"click\"]}]}]}',\n goal: 'Войти в аккаунт',\n action: '{\"action\":\"click\",\"target_id\":\"link_0_0\",\"value\":null,\"reasoning\":\"Ссылка «Войти» открывает форму входа\"}',\n },\n {\n title: 'Example 2 — fill a form (diff input)',\n vsl: '{\"diff_version\":2,\"base_version\":1,\"changes\":{\"added\":[{\"id\":\"form_1_0\",\"t\":\"input\",\"txt\":\"Поиск\",\"act\":[\"click\",\"type\"]}],\"modified\":[],\"removed\":[],\"unchanged_refs\":[\"button_1_1\"]}}',\n goal: 'Найти «vsl json»',\n action: '{\"action\":\"type\",\"target_id\":\"form_1_0\",\"value\":\"vsl json\",\"reasoning\":\"Ввожу запрос в новое поле поиска из диффа; затем нажму button_1_1 из unchanged_refs\"}',\n },\n {\n title: 'Example 3 — navigation',\n vsl: '{\"vsl_version\":\"1.0.0\",\"objects\":[{\"id\":\"main_0\",\"t\":\"main\",\"ch\":[{\"id\":\"header_0_0\",\"t\":\"header\",\"txt\":\"Настройки\"}]}]}',\n goal: 'Открыть страницу настроек по прямой ссылке',\n action: '{\"action\":\"navigate\",\"value\":\"https://example.com/settings\",\"reasoning\":\"Целевой URL известен — прямая навигация быстрее\"}',\n },\n];\n\n/** Свод правил параметров в едином формате {action, target_id, value, reasoning}. */\nconst PARAMETER_CONVENTIONS = [\n '- click, hover, focus, blur, clear, check, uncheck, open, close, expand, collapse — target_id;',\n '- type — target_id + value (text to type);',\n '- select — target_id + value (option);',\n '- scroll — value: \"up\" | \"down\" (optional amount: \"down:300\");',\n '- drag — target_id: source element, value: destination element id;',\n '- submit, reset — target_id: form id;',\n '- wait — value: condition (e.g. a selector or \"idle\");',\n '- navigate — value: URL;',\n '- download — target_id (element to click) OR value (URL to download directly);',\n '- go_back, go_forward, refresh — no parameters.',\n].join('\\n');\n\n/**\n * Строит system prompt: описание VSL-формата (включая семантику диффа §6.2),\n * правила ответа через tool «execute_action», Action Model и 3 few-shot.\n */\nexport function buildSystemPrompt(): string {\n const actions = VALID_ACTIONS.join(' | ');\n const examples = FEW_SHOT_EXAMPLES.map(\n (example) =>\n `${example.title}\\nVSL JSON: ${example.vsl}\\nUser goal: ${example.goal}\\nAction: ${example.action}`,\n ).join('\\n\\n');\n\n return [\n \"You are a screen understanding agent. You receive a VSL JSON representation of the current screen. Analyze the structure and decide what action to take to accomplish the user's goal.\",\n '',\n '## VSL JSON format',\n '',\n 'Elements form a tree via \"ch\" (children). Element keys:',\n '- \"id\" — deterministic unique element id; the ONLY valid value for target_id;',\n '- \"t\" — semantic type: button | input | link | nav | header | main | container | image | select | textarea | modal | tab;',\n '- \"r\" — ARIA role (clarifies semantics);',\n '- \"p\" — position [x, y], relative 0.0–1.0 of the viewport;',\n '- \"s\" — size [width, height] in pixels;',\n '- \"st\" — state: checked | expanded | disabled;',\n '- \"txt\" — visible text or accessible label;',\n '- \"act\" — actions the element supports (e.g. [\"click\"]);',\n '- \"vf\" / \"vf_meta\" — optional visual fragment reference; matching image content, if provided, is attached as separate image blocks.',\n '',\n 'The input is either a full document:',\n '{\"vsl_version\": \"...\", \"canvas\": {...}, \"objects\": [...]}',\n 'or a diff (diff-first mode):',\n '{\"diff_version\": N, \"base_version\": M, \"changes\": {\"added\": [...], \"modified\": [...], \"removed\": [...], \"unchanged_refs\": [...]}}',\n '',\n 'Diff semantics:',\n '- \"added\" — new subtrees, fully described;',\n '- \"modified\" — objects carrying ONLY the changed fields;',\n '- \"removed\" — ids that no longer exist: NEVER target them;',\n '- \"unchanged_refs\" — known unchanged elements: valid targets.',\n '',\n '## Lazy text loading (M1.7)',\n '',\n 'When an element text exceeds 200 characters, the VSL JSON contains only:',\n '- \"txt_preview\" — first ~50 characters of the text (with ellipsis);',\n '- \"txt_ref\" — reference to the full text block (format: tb_xxx, e.g. tb_001).',\n '',\n 'The full text is stored in \"text_blocks\" at the document root. To retrieve it, call the MCP tool vsl_get_text_block with {block_id: \"<txt_ref value>\"}.',\n '',\n 'When to fetch full text: only when the user goal REQUIRES reading the content of a long text (e.g. \"find the paragraph about X\", \"click the link in the article about Y\"). For navigation by headings/titles — txt_preview is sufficient.',\n '',\n '## Actions',\n '',\n 'Respond with exactly ONE next action. Allowed actions:',\n actions + '.',\n '',\n 'Unified parameter format {action, target_id, value, reasoning}:',\n PARAMETER_CONVENTIONS,\n '',\n '## Response rules',\n '',\n '1. Respond ONLY by calling the execute_action tool with one object:',\n ' {\"action\": \"...\", \"target_id\": \"...\", \"value\": null, \"reasoning\": \"...\"}',\n '2. When required, target_id MUST be an id present in the provided VSL JSON (document or diff, including unchanged_refs).',\n '3. Never target removed ids; never invent ids or actions outside the allowed list.',\n '4. Prefer the smallest action that moves toward the goal; explain briefly in \"reasoning\".',\n '',\n '## Examples',\n '',\n examples,\n ].join('\\n');\n}\n\n/** Строит user-промпт по шаблону §2.5: Current screen (VSL JSON) + User goal. */\nexport function buildUserPrompt(vslJson: VslInput, goal: string): string {\n return `Current screen (VSL JSON):\\n${JSON.stringify(vslJson)}\\n\\nUser goal: ${goal}`;\n}\n\n/**\n * Собирает данные изображений для vf-ссылок входа (DESIGN_SYSTEM §4.4, AC[7]):\n * обходит VslDocument (или дифф: added рекурсивно + modified), собирает `vf`-ссылки\n * и возвращает данные только тех, что есть в сторе (lazy loading: нет данных —\n * нет image-блока). Порядок стабильный (порядок первого вхождения ссылок).\n */\nexport function collectFragmentData(\n vslJson: VslInput,\n store?: VisualFragmentStore,\n): VisualFragmentData[] {\n if (store === undefined || store.size === 0) return [];\n const refs = new Set<string>();\n const visit = (objects: readonly VslObject[]): void => {\n for (const object of objects) {\n const vf = (object as { vf?: unknown }).vf;\n if (typeof vf === 'string') refs.add(vf);\n visit(object.ch ?? []);\n }\n };\n if (isVslDiff(vslJson)) {\n visit(vslJson.changes.added);\n for (const modified of vslJson.changes.modified) {\n const vf = (modified as { vf?: unknown }).vf;\n if (typeof vf === 'string') refs.add(vf);\n }\n } else {\n visit(vslJson.objects);\n }\n const fragments: VisualFragmentData[] = [];\n for (const ref of refs) {\n const data = store.get(ref);\n if (data !== undefined) fragments.push(data);\n }\n return fragments;\n}\n","/**\n * JSON Schema tool «execute_action» (T1.3.4) — одна схема для обоих провайдеров:\n * - OpenAI function calling: tools[0].function.parameters;\n * - Anthropic tool use: tools[0].input_schema.\n * Список действий — enum из VALID_ACTIONS (actions.ts): единый источник правды\n * с system prompt. Формат аргументов — §7.4 {action, target_id, value, reasoning}.\n */\n\nimport { VALID_ACTIONS } from './actions';\n\n/** Имя tool, через которое модель возвращает действие. */\nexport const ACTION_TOOL_NAME = 'execute_action' as const;\n\n/** Описание tool для моделей. */\nexport const ACTION_TOOL_DESCRIPTION =\n 'Execute exactly one next browser action toward the user goal.';\n\n/**\n * JSON Schema аргументов tool (подмножество Draft 2020-12, совместимое\n * с OpenAI function calling и Anthropic tool use).\n */\nexport const ACTION_TOOL_SCHEMA = {\n type: 'object',\n properties: {\n action: {\n type: 'string',\n description: 'Action name from the Action Model (ARCHITECTURE.md §7.1–7.3).',\n enum: [...VALID_ACTIONS],\n },\n target_id: {\n type: 'string',\n description: 'Element id from the provided VSL JSON (required for target actions).',\n },\n value: {\n type: ['string', 'null'],\n description:\n 'Value for parameterized actions: type/select/scroll/navigate/wait/drag.',\n },\n reasoning: {\n type: 'string',\n description: 'Brief explanation of the chosen action.',\n },\n },\n required: ['action'],\n additionalProperties: false,\n};","/**\n * HTTP-транспорт и retry для LLM-адаптеров (T1.3.2/T1.3.3, ARCHITECTURE §10.1/§10.3):\n * - defaultTransport — нативный fetch (node>=18, ноль runtime-зависимостей);\n * - retryWithBackoff — до 3 попыток с exponential backoff (§10.3, 2^attempt секунд);\n * - executeJsonWithRetry — запрос + статус-проверка + парс JSON, с retry.\n *\n * Retry-политика (уточнение §10.1/§10.3): повторяются ТОЛЬКО сетевые ошибки,\n * HTTP 429 и 5xx; прочие 4xx — fast-fail (401/403 повтором не чинятся).\n * Паузы: 1s, 2s перед 2-й и 3-й попыткой (форма 2^attempt секунд из §10.3).\n * sleep инъецируется (RetryOptions.sleep / LlmAdapterConfig.sleep) —\n * тесты идут без реальных задержек.\n */\n\nimport { LlmError } from './types';\nimport type { LlmTransport, Sleep } from './types';\n\n/** Транспорт по умолчанию — нативный fetch (глобальный на момент вызова). */\nexport const defaultTransport: LlmTransport = (url, init) => fetch(url, init);\n\n/** Задержка по умолчанию — реальный setTimeout (в тестах инъекция). */\nexport const defaultSleep: Sleep = (ms) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\nexport interface RetryOptions {\n /** Всего попыток, включая первую (§10.3: 3). Значения <1 приводятся к 1. */\n maxRetries?: number;\n /** Задержка между попытками (инъекция для тестов). */\n sleep?: Sleep;\n}\n\n/** Задержка §10.3: 2^attempt секунд → 1s, 2s, 4s… */\nconst backoffDelayMs = (attempt: number): number => 2 ** attempt * 1000;\n\n/**\n * Повторяется ли ошибка: сетевые сбои (не LlmError, напр. TypeError от fetch),\n * LlmError без статуса (обёртка сетевого сбоя), HTTP 429 и 5xx.\n * Прочие 4xx — fast-fail без retry.\n */\nconst isRetryable = (error: unknown): boolean => {\n if (error instanceof LlmError) {\n return error.status === undefined || error.status === 429 || error.status >= 500;\n }\n return true;\n};\n\n/**\n * Исполняет operation с retry до maxRetries попыток и exponential backoff\n * 1s/2s/… (§10.3). После исчерпания попыток (или на неремрабатой ошибке)\n * исключение уходит наружу.\n */\nexport async function retryWithBackoff<T>(\n operation: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxRetries = Math.max(1, options.maxRetries ?? 3);\n const sleep = options.sleep ?? defaultSleep;\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await operation();\n } catch (error) {\n if (!isRetryable(error) || attempt >= maxRetries - 1) throw error;\n await sleep(backoffDelayMs(attempt));\n }\n }\n}\n\n/**\n * Выполняет HTTP-запрос через transport с retry (§10.3) и статус-проверкой,\n * возвращает распарсенное тело JSON.\n *\n * Ошибки:\n * - сетевой сбой transport → LlmError без статуса (retryable);\n * - !res.ok → LlmError со статусом (429/5xx retryable, прочие 4xx — нет);\n * - тело не JSON → LlmError (после retry — наружу).\n */\nexport async function executeJsonWithRetry<T>(\n transport: LlmTransport,\n url: string,\n init: RequestInit,\n providerLabel: string,\n options: RetryOptions = {},\n): Promise<T> {\n const response = await retryWithBackoff(async () => {\n let res: Response;\n try {\n res = await transport(url, init);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new LlmError(`${providerLabel}: network error — ${message}`);\n }\n if (!res.ok) {\n throw new LlmError(`${providerLabel}: HTTP ${res.status} ${res.statusText}`, res.status);\n }\n return res;\n }, options);\n\n try {\n return (await response.json()) as T;\n } catch {\n throw new LlmError(`${providerLabel}: response is not valid JSON`);\n }\n}","/**\n * OpenAI adapter (T1.3.2): Chat Completions API + function calling\n * (tools/tool_choice «execute_action») без официального SDK — нативный fetch\n * через инъекцию транспорта (ноль runtime-зависимостей, DEC-002).\n *\n * Retry (AC[8], §10.3): executeJsonWithRetry — до 3 попыток, только\n * сеть/429/5xx. Мультимодальность (AC[7], §4.4): vf-ссылки из входа при\n * наличии данных в сторе превращаются в content-части\n * {type: 'image_url', image_url: {url: 'data:<mediaType>;base64,<data>'}}.\n */\n\nimport { collectIds, validateAction } from './actions';\nimport { buildSystemPrompt, buildUserPrompt, collectFragmentData } from './prompt';\nimport {\n ACTION_TOOL_DESCRIPTION,\n ACTION_TOOL_NAME,\n ACTION_TOOL_SCHEMA,\n} from './schema';\nimport { defaultTransport, executeJsonWithRetry } from './transport';\nimport { LlmError } from './types';\nimport type {\n DecideInput,\n LlmAction,\n LlmAdapter,\n LlmAdapterConfig,\n LlmContentPart,\n LlmResponse,\n LlmToolDef,\n LlmTransport,\n SendPromptOptions,\n Sleep,\n VslInput,\n} from './types';\n\nconst DEFAULT_MODEL = 'gpt-4o';\nconst DEFAULT_BASE_URL = 'https://api.openai.com/v1';\n\n/** Ответ chat/completions — подмножество полей, нужных адаптеру. */\ninterface OpenAiChatResponse {\n model?: string;\n choices?: Array<{\n message?: {\n content?: string | null;\n tool_calls?: Array<{\n function?: { name?: string; arguments?: string };\n }>;\n };\n }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n}\n\n/** Один content-блок user-сообщения (текст или image_url). */\ntype OpenAiContentPart = Record<string, unknown>;\n\n/**\n * Адаптер OpenAI (gpt-4o / gpt-4o-mini): system prompt + VSL JSON (§2.5)\n * → tool call «execute_action» с действием §7.4.\n */\nexport class OpenAIAdapter implements LlmAdapter {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly baseUrl: string;\n private readonly maxRetries: number;\n private readonly transport: LlmTransport;\n private readonly sleep: Sleep | undefined;\n\n constructor(config: LlmAdapterConfig) {\n if (config.apiKey.length === 0) {\n throw new LlmError('OpenAIAdapter: apiKey is required');\n }\n this.apiKey = config.apiKey;\n this.model = config.model ?? DEFAULT_MODEL;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.maxRetries = config.maxRetries ?? 3;\n this.transport = config.transport ?? defaultTransport;\n this.sleep = config.sleep;\n }\n\n async sendPrompt(\n vslJson: VslInput,\n task: string,\n options?: SendPromptOptions,\n ): Promise<LlmResponse> {\n const userContent: LlmContentPart[] = [\n { type: 'text', text: buildUserPrompt(vslJson, task) },\n ...collectFragmentData(vslJson, options?.visualFragments).map((fragment) => ({\n type: 'image' as const,\n mediaType: fragment.mediaType,\n data: fragment.data,\n })),\n ];\n return this.sendRaw(buildSystemPrompt(), userContent, {\n name: ACTION_TOOL_NAME,\n description: ACTION_TOOL_DESCRIPTION,\n schema: ACTION_TOOL_SCHEMA,\n });\n }\n\n /**\n * Низкоуровневый вызов (T1.5.4, вариант C): провайдер-независимые аргументы\n * → Chat Completions → LlmResponse. Потребители: sendPrompt (tool\n * «execute_action») и LlmVisionClassifier (tool «classify_fragment»).\n * Retry (§10.3) и парсинг tool_calls живут здесь в одном месте.\n */\n async sendRaw(\n system: string,\n userContent: readonly LlmContentPart[],\n tool: LlmToolDef,\n ): Promise<LlmResponse> {\n const content: OpenAiContentPart[] = userContent.map((part) =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : {\n type: 'image_url',\n image_url: { url: `data:${part.mediaType};base64,${part.data}` },\n },\n );\n const body = {\n model: this.model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content },\n ],\n tools: [\n {\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.schema,\n },\n },\n ],\n tool_choice: { type: 'function', function: { name: tool.name } },\n };\n\n const data = await executeJsonWithRetry<OpenAiChatResponse>(\n this.transport,\n `${this.baseUrl}/chat/completions`,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n },\n 'OpenAI',\n { maxRetries: this.maxRetries, sleep: this.sleep },\n );\n\n const message = data.choices?.[0]?.message;\n const toolCall = message?.tool_calls?.[0];\n let rawAction: unknown;\n const args = toolCall?.function?.arguments;\n if (args !== undefined) {\n try {\n rawAction = JSON.parse(args);\n } catch {\n throw new LlmError('OpenAI: tool_call.arguments is not valid JSON');\n }\n }\n return {\n text: message?.content ?? null,\n action: rawAction,\n model: data.model ?? this.model,\n usage: {\n inputTokens: data.usage?.prompt_tokens,\n outputTokens: data.usage?.completion_tokens,\n },\n };\n }\n\n /** Convenience §9.4: решение для цели + валидация (whitelist + target_id). */\n async decide(input: DecideInput): Promise<LlmAction> {\n const response = await this.sendPrompt(input.vslJson, input.goal, {\n visualFragments: input.visualFragments,\n });\n return validateAction(response.action, collectIds(input.vslJson));\n }\n}","/**\n * Anthropic adapter (T1.3.3): Messages API (/v1/messages) + tool use\n * (tools/tool_choice «execute_action») без официального SDK — нативный fetch\n * через инъекцию транспорта (ноль runtime-зависимостей, DEC-002).\n *\n * Retry (AC[8], §10.3): executeJsonWithRetry — до 3 попыток, только\n * сеть/429/5xx. Мультимодальность (AC[7], §4.4): vf-ссылки из входа при\n * наличии данных в сторе превращаются в content-блоки\n * {type: 'image', source: {type: 'base64', media_type, data}}.\n */\n\nimport { collectIds, validateAction } from './actions';\nimport { buildSystemPrompt, buildUserPrompt, collectFragmentData } from './prompt';\nimport {\n ACTION_TOOL_DESCRIPTION,\n ACTION_TOOL_NAME,\n ACTION_TOOL_SCHEMA,\n} from './schema';\nimport { defaultTransport, executeJsonWithRetry } from './transport';\nimport { LlmError } from './types';\nimport type {\n DecideInput,\n LlmAction,\n LlmAdapter,\n LlmAdapterConfig,\n LlmContentPart,\n LlmResponse,\n LlmToolDef,\n LlmTransport,\n SendPromptOptions,\n Sleep,\n VslInput,\n} from './types';\n\nconst DEFAULT_MODEL = 'claude-sonnet-4-20250514';\nconst DEFAULT_BASE_URL = 'https://api.anthropic.com';\nconst ANTHROPIC_VERSION = '2023-06-01';\n/** Anthropic требует max_tokens в каждом запросе (§7.4-ответ компактен). */\nconst DEFAULT_MAX_TOKENS = 1024;\n\n/** Блок content-ответа /v1/messages — подмножество полей, нужных адаптеру. */\ninterface AnthropicReplyBlock {\n type?: string;\n text?: string;\n name?: string;\n input?: unknown;\n}\n\n/** Ответ /v1/messages — подмножество полей, нужных адаптеру. */\ninterface AnthropicMessagesResponse {\n model?: string;\n content?: AnthropicReplyBlock[];\n usage?: { input_tokens?: number; output_tokens?: number };\n}\n\n/** Один content-блок user-сообщения (текст или image). */\ntype AnthropicContentBlock = Record<string, unknown>;\n\n/**\n * Адаптер Anthropic (claude-sonnet-4-20250514): system prompt + VSL JSON (§2.5)\n * → tool_use «execute_action» с действием §7.4.\n */\nexport class AnthropicAdapter implements LlmAdapter {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly baseUrl: string;\n private readonly maxTokens: number;\n private readonly maxRetries: number;\n private readonly transport: LlmTransport;\n private readonly sleep: Sleep | undefined;\n\n constructor(config: LlmAdapterConfig) {\n if (config.apiKey.length === 0) {\n throw new LlmError('AnthropicAdapter: apiKey is required');\n }\n this.apiKey = config.apiKey;\n this.model = config.model ?? DEFAULT_MODEL;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.maxTokens = DEFAULT_MAX_TOKENS;\n this.maxRetries = config.maxRetries ?? 3;\n this.transport = config.transport ?? defaultTransport;\n this.sleep = config.sleep;\n }\n\n async sendPrompt(\n vslJson: VslInput,\n task: string,\n options?: SendPromptOptions,\n ): Promise<LlmResponse> {\n const userContent: LlmContentPart[] = [\n { type: 'text', text: buildUserPrompt(vslJson, task) },\n ...collectFragmentData(vslJson, options?.visualFragments).map((fragment) => ({\n type: 'image' as const,\n mediaType: fragment.mediaType,\n data: fragment.data,\n })),\n ];\n return this.sendRaw(buildSystemPrompt(), userContent, {\n name: ACTION_TOOL_NAME,\n description: ACTION_TOOL_DESCRIPTION,\n schema: ACTION_TOOL_SCHEMA,\n });\n }\n\n /**\n * Низкоуровневый вызов (T1.5.4, вариант C): провайдер-независимые аргументы\n * → Messages API → LlmResponse. Потребители: sendPrompt (tool\n * «execute_action») и LlmVisionClassifier (tool «classify_fragment»).\n * tool_use-блоки фильтруются по tool.name (ранее — константа ACTION_TOOL_NAME):\n * параметризация безопасна, sendPrompt передаёт ACTION_TOOL_NAME.\n */\n async sendRaw(\n system: string,\n userContent: readonly LlmContentPart[],\n tool: LlmToolDef,\n ): Promise<LlmResponse> {\n const content: AnthropicContentBlock[] = userContent.map((part) =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : {\n type: 'image',\n source: {\n type: 'base64',\n media_type: part.mediaType,\n data: part.data,\n },\n },\n );\n const body = {\n model: this.model,\n max_tokens: this.maxTokens,\n system,\n messages: [{ role: 'user', content }],\n tools: [\n {\n name: tool.name,\n description: tool.description,\n input_schema: tool.schema,\n },\n ],\n tool_choice: { type: 'tool', name: tool.name },\n };\n\n const data = await executeJsonWithRetry<AnthropicMessagesResponse>(\n this.transport,\n `${this.baseUrl}/v1/messages`,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': ANTHROPIC_VERSION,\n },\n body: JSON.stringify(body),\n },\n 'Anthropic',\n { maxRetries: this.maxRetries, sleep: this.sleep },\n );\n\n let text: string | null = null;\n let rawAction: unknown;\n for (const block of data.content ?? []) {\n if (block.type === 'text' && typeof block.text === 'string') {\n text = text === null ? block.text : `${text}\\n${block.text}`;\n } else if (block.type === 'tool_use' && block.name === tool.name) {\n rawAction = block.input;\n }\n }\n return {\n text,\n action: rawAction,\n model: data.model ?? this.model,\n usage: {\n inputTokens: data.usage?.input_tokens,\n outputTokens: data.usage?.output_tokens,\n },\n };\n }\n\n /** Convenience §9.4: решение для цели + валидация (whitelist + target_id). */\n async decide(input: DecideInput): Promise<LlmAction> {\n const response = await this.sendPrompt(input.vslJson, input.goal, {\n visualFragments: input.visualFragments,\n });\n return validateAction(response.action, collectIds(input.vslJson));\n }\n}","/**\n * Alibaba/Qwen adapter (DashScope API, OpenAI-compatible): Chat Completions API\n * + function calling (tools/tool_choice «execute_action») без официального SDK —\n * нативный fetch через инъекцию транспорта (ноль runtime-зависимостей, DEC-002).\n *\n * DashScope compatible-mode endpoint: https://dashscope.aliyuncs.com/compatible-mode/v1\n * Формат запроса/ответа идентичен OpenAI (chat/completions, tool_calls).\n *\n * Модель по умолчанию: qwen3.8-flash — самая быстрая и дешёвая модель Qwen (09.2026).\n * Используется для vision-backend (классификация visual fragments).\n *\n * Retry (AC[8], §10.3): executeJsonWithRetry — до 3 попыток, только\n * сеть/429/5xx. Мультимодальность (AC[7], §4.4): vf-ссылки из входа при\n * наличии данных в сторе превращаются в content-части\n * {type: 'image_url', image_url: {url: 'data:<mediaType>;base64,<data>'}}.\n */\n\nimport { collectIds, validateAction } from './actions';\nimport { buildSystemPrompt, buildUserPrompt, collectFragmentData } from './prompt';\nimport {\n ACTION_TOOL_DESCRIPTION,\n ACTION_TOOL_NAME,\n ACTION_TOOL_SCHEMA,\n} from './schema';\nimport { defaultTransport, executeJsonWithRetry } from './transport';\nimport { LlmError } from './types';\nimport type {\n DecideInput,\n LlmAction,\n LlmAdapter,\n LlmAdapterConfig,\n LlmContentPart,\n LlmResponse,\n LlmToolDef,\n LlmTransport,\n SendPromptOptions,\n Sleep,\n VslInput,\n} from './types';\n\nconst DEFAULT_MODEL = 'qwen3.8-flash';\nconst DEFAULT_BASE_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1';\n\n/** Ответ chat/completions — подмножество полей, нужных адаптеру. */\ninterface AlibabaChatResponse {\n model?: string;\n choices?: Array<{\n message?: {\n content?: string | null;\n tool_calls?: Array<{\n function?: { name?: string; arguments?: string };\n }>;\n };\n }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n}\n\n/** Один content-блок user-сообщения (текст или image_url). */\ntype AlibabaContentPart = Record<string, unknown>;\n\n/**\n * Адаптер Alibaba/Qwen (qwen3.8-flash): system prompt + VSL JSON (§2.5)\n * → tool call «execute_action» с действием §7.4.\n *\n * DashScope compatible-mode — OpenAI-compatible API, формат запроса/ответа\n * идентичен OpenAI (chat/completions, tools, tool_choice, tool_calls).\n */\nexport class AlibabaAdapter implements LlmAdapter {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly baseUrl: string;\n private readonly maxRetries: number;\n private readonly transport: LlmTransport;\n private readonly sleep: Sleep | undefined;\n\n constructor(config: LlmAdapterConfig) {\n if (config.apiKey.length === 0) {\n throw new LlmError('AlibabaAdapter: apiKey is required');\n }\n this.apiKey = config.apiKey;\n this.model = config.model ?? DEFAULT_MODEL;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.maxRetries = config.maxRetries ?? 3;\n this.transport = config.transport ?? defaultTransport;\n this.sleep = config.sleep;\n }\n\n async sendPrompt(\n vslJson: VslInput,\n task: string,\n options?: SendPromptOptions,\n ): Promise<LlmResponse> {\n const userContent: LlmContentPart[] = [\n { type: 'text', text: buildUserPrompt(vslJson, task) },\n ...collectFragmentData(vslJson, options?.visualFragments).map((fragment) => ({\n type: 'image' as const,\n mediaType: fragment.mediaType,\n data: fragment.data,\n })),\n ];\n return this.sendRaw(buildSystemPrompt(), userContent, {\n name: ACTION_TOOL_NAME,\n description: ACTION_TOOL_DESCRIPTION,\n schema: ACTION_TOOL_SCHEMA,\n });\n }\n\n /**\n * Низкоуровневый вызов (T1.5.4, вариант C): провайдер-независимые аргументы\n * → DashScope Chat Completions → LlmResponse. Потребители: sendPrompt (tool\n * «execute_action») и LlmVisionClassifier (tool «classify_fragment»).\n * Retry (§10.3) и парсинг tool_calls живут здесь в одном месте.\n */\n async sendRaw(\n system: string,\n userContent: readonly LlmContentPart[],\n tool: LlmToolDef,\n ): Promise<LlmResponse> {\n const content: AlibabaContentPart[] = userContent.map((part) =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : {\n type: 'image_url',\n image_url: { url: `data:${part.mediaType};base64,${part.data}` },\n },\n );\n const body = {\n model: this.model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content },\n ],\n tools: [\n {\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.schema,\n },\n },\n ],\n tool_choice: { type: 'function', function: { name: tool.name } },\n };\n\n const data = await executeJsonWithRetry<AlibabaChatResponse>(\n this.transport,\n `${this.baseUrl}/chat/completions`,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n },\n 'Alibaba/Qwen',\n { maxRetries: this.maxRetries, sleep: this.sleep },\n );\n\n const message = data.choices?.[0]?.message;\n const toolCall = message?.tool_calls?.[0];\n let rawAction: unknown;\n const args = toolCall?.function?.arguments;\n if (args !== undefined) {\n try {\n rawAction = JSON.parse(args);\n } catch {\n throw new LlmError('Alibaba/Qwen: tool_call.arguments is not valid JSON');\n }\n }\n return {\n text: message?.content ?? null,\n action: rawAction,\n model: data.model ?? this.model,\n usage: {\n inputTokens: data.usage?.prompt_tokens,\n outputTokens: data.usage?.completion_tokens,\n },\n };\n }\n\n /** Convenience §9.4: решение для цели + валидация (whitelist + target_id). */\n async decide(input: DecideInput): Promise<LlmAction> {\n const response = await this.sendPrompt(input.vslJson, input.goal, {\n visualFragments: input.visualFragments,\n });\n return validateAction(response.action, collectIds(input.vslJson));\n }\n}","/**\n * Типы Action Executor (ARCHITECTURE.md §7.4 «Action Executor»,\n * ROADMAP.md M1.4: T1.4.1–T1.4.3).\n *\n * Executor исполняет действия Action Model M1.3 (VALID_ACTIONS, 24 действия)\n * на живом DOM. Контракты:\n * - вход — LlmAction (llm/types.ts): {action, target_id?, value?, reasoning?};\n * - value-кодирование — строго по PARAMETER_CONVENTIONS (llm/prompt.ts),\n * единый источник правды с LLM (prompt/schema согласованы в M1.3);\n * - резолв target_id — чисто вычислительный, executor не аннотирует DOM;\n * - executeAction — soft-fail: сбои возвращаются в ActionResult (error), а не\n * бросаются, чтобы шаг агента не прерывал агентный цикл (extension background).\n */\n\n/** Информация о загруженном файле (заполняется при download действии). */\nexport interface DownloadInfo {\n /** Уникальный идентификатор загрузки. */\n downloadId: string;\n /** Имя файла. */\n filename: string;\n /** URL источника. */\n url: string;\n /** Статус загрузки: pending | completed | cancelled | failed. */\n status: 'pending' | 'completed' | 'cancelled' | 'failed';\n}\n\n/** Результат исполнения одного действия (возврат executeAction агенту). */\nexport interface ActionResult {\n /** true — действие исполнено; false — отклонено (детали в error). */\n success: boolean;\n /** Имя исполненного действия (эхо LlmAction.action). */\n action: string;\n /** target_id, если действие было с целью (эхо из входа). */\n targetId?: string;\n /** Машиночитаемое описание сбоя при success=false; обратная связь агенту. */\n error?: string;\n /** Информация о загрузке (заполняется при action='download' и success=true). */\n download?: DownloadInfo;\n}\n\n/**\n * Ошибка исполнения действия: невалидные параметры, целевой элемент не найден\n * (DOM изменился с момента снапшота) или расхождение tag при резолве id.\n * Бросается resolveTarget и внутренними шагами executeAction; снаружи\n * executeAction конвертирует её в ActionResult{success:false, error}.\n */\nexport class ActionExecutionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ActionExecutionError';\n }\n}\n\n/** Опции исполнения; все опциональны (дефолты рассчитаны на прод в браузере). */\nexport interface ExecutorOptions {\n /**\n * Корень обхода DOM при резолве target_id; по умолчанию document.body.\n * Обязан совпадать с root, переданным в extractDomTree/VslSnapshotSession:\n * indexPath в VSL id считается от корня обхода (domExtractor, §5.1).\n */\n root?: Element;\n /** Таймаут wait (мс) — для селектора или \"idle\"; по умолчанию 5000. */\n waitTimeout?: number;\n /**\n * Амплитуда скролла (px) для bare-направления (\"down\" без amount);\n * по умолчанию 300. Явный \"down:300\" перекрывает дефолт.\n */\n defaultScrollAmount?: number;\n}","/**\n * Резолв target_id → DOM-элемент (ARCHITECTURE.md §7.4 «Action Executor»,\n * ROADMAP.md M1.4, T1.4.1).\n *\n * Контракт (spec M1.4 п.4; формат id — vslBuilder.ts:100):\n * - target_id = `tag_index1_index2_...`: tag — имя тега в нижнем регистре,\n * индексы — indexPath элемента (id = `${el.tag}_${el.indexPath.join('_')}`);\n * - indexPath — индексы среди ЭЛЕМЕНТНЫХ детей каждого уровня, вычисляются по\n * структуре DOM ДО фильтрации (domExtractor.collectVisibleChildren) — позиция\n * стабильна независимо от фильтров видимости/сегментации;\n * - extractDomTree возвращает ЛЕС потомков root (сам root не включается), поэтому\n * каждый VSL id содержит ≥1 индекс, а обход стартует с root.children[index];\n * - обход от корня снапшота: ExecutorOptions.root ?? document.body. Root обязан\n * совпадать с root, переданным в extractDomTree/VslSnapshotSession, иначе\n * indexPath укажет не на тот элемент;\n * - резолв чисто вычислительный: executor не аннотирует DOM и не хранит ссылок;\n * - при невалидном формате id, отсутствии элемента по indexPath или несовпадении\n * tag бросается ActionExecutionError (DOM изменился с момента снапшота).\n * Сообщения ошибок — на английском: runtime-строки — публичный API @thinkingos/vsl-sdk и\n * вход для LLM-агента (решение 22.09.2026).\n */\n\nimport { ActionExecutionError } from './types';\nimport type { ExecutorOptions } from './types';\n\n/** Разделитель сегментов target_id (формат vslBuilder.ts:100). */\nconst ID_SEPARATOR = '_';\n\n/** Сегмент индекса: непустая последовательность десятичных цифр (без знака). */\nconst INDEX_SEGMENT_RE = /^\\d+$/;\n\n/**\n * Корень обхода по умолчанию — document.body (прод в браузере).\n * Отдельная функция: обращение к document только в момент вызова, чтобы импорт\n * модуля в plain Node (demo, packaging-smoke) не падал с ReferenceError.\n */\nfunction defaultRoot(): Element {\n const body = document.body;\n if (body === null) {\n throw new ActionExecutionError(\n 'resolveTarget: document.body is not available — pass the snapshot root via ExecutorOptions.root',\n );\n }\n return body;\n}\n\n/**\n * Разбирает target_id на tag и indexPath.\n * Строгий формат (vslBuilder порождает id всегда с indexPath): минимум один индекс;\n * сегменты индексов — только десятичные цифры (пустой сегмент и знак запрещены:\n * Number('') === 0 и приведение '-1' — ловушки парсинга).\n */\nfunction parseTargetId(targetId: string): { tag: string; indexPath: number[] } {\n const segments = targetId.split(ID_SEPARATOR);\n const tag = segments[0];\n if (tag === undefined || tag.length === 0) {\n throw new ActionExecutionError(\n `Invalid target_id \"${targetId}\": missing tag — expected format \"tag_index1_index2_...\"`,\n );\n }\n const indexSegments = segments.slice(1);\n if (indexSegments.length === 0) {\n throw new ActionExecutionError(\n `Invalid target_id \"${targetId}\": missing index path — expected format \"tag_index1_index2_...\"`,\n );\n }\n const indexPath: number[] = [];\n for (const segment of indexSegments) {\n if (!INDEX_SEGMENT_RE.test(segment)) {\n throw new ActionExecutionError(\n `Invalid target_id \"${targetId}\": index segment \"${segment}\" is not a non-negative integer`,\n );\n }\n indexPath.push(Number(segment));\n }\n return { tag, indexPath };\n}\n\n/**\n * Резолвит target_id в DOM-элемент обходом от корня снапшота (§7.4, T1.4.1).\n *\n * @param targetId — id элемента из VSL JSON, формат \"tag_index1_index2_...\".\n * @param options — ExecutorOptions; root обязан совпадать с корнем снапшота\n * (extractDomTree/§5.1), по умолчанию document.body.\n * @returns найденный DOM-элемент.\n * @throws ActionExecutionError — невалидный формат id, элемент по indexPath не\n * найден или tag не совпал (DOM изменился с момента снапшота).\n */\nexport function resolveTarget(targetId: string, options: ExecutorOptions = {}): Element {\n const root = options.root ?? defaultRoot();\n const { tag, indexPath } = parseTargetId(targetId);\n\n let current: Element = root;\n let depth = 0;\n for (const index of indexPath) {\n depth += 1;\n // Индексы считаются по ЭЛЕМЕНТНЫМ детям (Element.children) — конвенция domExtractor.\n const next = current.children[index];\n if (next === undefined) {\n throw new ActionExecutionError(\n `Failed to resolve target_id \"${targetId}\": no element child at index ${index} on depth ${depth} — DOM may have changed since the snapshot`,\n );\n }\n current = next;\n }\n\n const actualTag = current.tagName.toLowerCase();\n if (actualTag !== tag) {\n throw new ActionExecutionError(\n `Failed to resolve target_id \"${targetId}\": found <${actualTag}>, expected tag \"${tag}\" — DOM may have changed since the snapshot`,\n );\n }\n return current;\n}","/**\n * Action Executor — исполнение действий Action Model M1.3 на живом DOM\n * (ARCHITECTURE.md §7.4 «Action Executor», ROADMAP.md M1.4: T1.4.1–T1.4.3).\n *\n * Контракт (spec M1.4; value-кодирование — строго по PARAMETER_CONVENTIONS\n * llm/prompt.ts, единый источник правды с LLM — решение dc_4/dc_7):\n * - вход — LlmAction (llm/types.ts): {action, target_id?, value?, reasoning?};\n * - target_id резолвится resolveTarget (tag_indexPath → обход DOM от корня\n * снапшота: ExecutorOptions.root ?? document.body) — executor не аннотирует\n * DOM и не хранит ссылок на элементы между действиями;\n * - executeAction — soft-fail: всегда возвращает Promise<ActionResult>, не\n * бросает; ActionExecutionError (и любой неожиданный error) перехватывается\n * и отражается в result.error — сбой шага не роняет агентный цикл\n * (extension background);\n * - значения input/select/checkbox выставляются программно + dispatch\n * input/change с bubbles — React-совместимость (AC[2]): React обновляет\n * controlled-компоненты только по событиям, а не от прямой смены .value;\n * - type ЗАМЕНЯЕТ значение поля (не дополняет) — детерминизм: повторный type\n * даёт то же состояние; очистка поля — отдельным действием clear;\n * - сообщения ошибок — на английском (публичный API @thinkingos/vsl-sdk и вход для\n * LLM-агента, решение 22.09.2026).\n */\n\nimport type { LlmAction } from '../llm/types';\nimport { resolveTarget } from './resolveTarget';\nimport { ActionExecutionError } from './types';\nimport type { ActionResult, DownloadInfo, ExecutorOptions } from './types';\n\n/** Амплитуда скролла по умолчанию для bare-направления (types.ts, дефолт 300). */\nconst DEFAULT_SCROLL_AMOUNT = 300;\n\n/** Допустимые направления scroll (PARAMETER_CONVENTIONS). */\nconst SCROLL_DIRECTIONS: ReadonlySet<string> = new Set(['up', 'down', 'left', 'right']);\n\n/** Таймаут wait по умолчанию (types.ts ExecutorOptions, дефолт 5000). */\nconst WAIT_DEFAULT_TIMEOUT_MS = 5000;\n\n/** Интервал опроса условия wait (мс). */\nconst WAIT_POLL_INTERVAL_MS = 50;\n/**\n * Резолвит target_id действия через resolveTarget (§7.4). Обработчики\n * target-действий вызывают его первым шагом: отсутствие строкового target_id —\n * ActionExecutionError (PARAMETER_CONVENTIONS: target-действия требуют цель).\n */\nfunction resolveTargetOf(action: LlmAction, options: ExecutorOptions): Element {\n const targetId = action.target_id;\n if (typeof targetId !== 'string') {\n throw new ActionExecutionError(`Action \"${action.action}\" requires a string target_id`);\n }\n return resolveTarget(targetId, options);\n}\n\n/** Извлекает обязательный строковый value параметризованного действия. */\nfunction requireValue(action: LlmAction): string {\n const value = action.value;\n if (typeof value !== 'string') {\n throw new ActionExecutionError(`Action \"${action.action}\" requires a string value`);\n }\n return value;\n}\n\n/**\n * Dispatch input+change с bubbles после программного изменения значения —\n * AC[2] React-совместимость: React делегирует события на корне, поэтому смена\n * .value без событий не обновляет controlled-компоненты.\n */\nfunction dispatchValueEvents(el: Element): void {\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\n/**\n * Проверяет, что цель — текстовое поле (input/textarea; contenteditable в\n * scope M1.4 не входит: builder даёт type только input/textarea), и возвращает\n * типизированную ссылку.\n */\nfunction requireTextTarget(\n action: LlmAction,\n el: Element,\n): HTMLInputElement | HTMLTextAreaElement {\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n return el;\n }\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires an <input> or <textarea> target, got <${el.tagName.toLowerCase()}>`,\n );\n}\n\n/** type/clear: замена значения поля + input/change (контракт — в шапке файла). */\nfunction typeInto(el: HTMLInputElement | HTMLTextAreaElement, text: string): void {\n el.value = text;\n dispatchValueEvents(el);\n}\n\n/**\n * Клик: нативный click() (HTMLElement/SVGElement, HTMLOrSVGElement mixin);\n * фолбэк — синтетический bubbling MouseEvent для элементов без click().\n */\nfunction clickElement(el: Element): void {\n if (typeof (el as HTMLElement).click === 'function') {\n (el as HTMLElement).click();\n return;\n }\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n}\n\n/** Hover: синтетические mouseover (bubbles) + mouseenter (не bubbling по DOM-семантике). */\nfunction hoverElement(el: Element): void {\n el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true, cancelable: true }));\n el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: false }));\n}\n\n/**\n * Focus/blur: нативные focus()/blur() — браузер (и jsdom) сами диспетчат\n * focus/blur/focusin/focusout; элемент без focus() — ActionExecutionError.\n */\nfunction focusableOrThrow(action: LlmAction, el: Element): { focus(): void; blur(): void } {\n const htmlEl = el as HTMLElement;\n if (typeof htmlEl.focus !== 'function' || typeof htmlEl.blur !== 'function') {\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires a focusable element, got <${el.tagName.toLowerCase()}>`,\n );\n }\n return htmlEl;\n}\n\n/**\n * select: value = option value (PARAMETER_CONVENTIONS), фолбэк — совпадение по\n * видимому тексту option (dc_4); иначе ActionExecutionError. Значение\n * выставляется в value выбранного option'а + input/change (AC[2]).\n */\nfunction selectOption(el: Element, raw: string): void {\n if (!(el instanceof HTMLSelectElement)) {\n throw new ActionExecutionError(\n `Action \"select\" requires a <select> target, got <${el.tagName.toLowerCase()}>`,\n );\n }\n const wanted = raw.trim();\n for (const option of Array.from(el.options)) {\n if (option.value === raw) {\n el.value = option.value;\n dispatchValueEvents(el);\n return;\n }\n }\n for (const option of Array.from(el.options)) {\n if (option.text.trim() === wanted) {\n el.value = option.value;\n dispatchValueEvents(el);\n return;\n }\n }\n throw new ActionExecutionError(\n `Action \"select\": option \"${raw}\" not found in <select> (neither by value nor by visible text)`,\n );\n}\n\n/**\n * check/uncheck: цель — input[type=checkbox|radio] (builder defaultActions\n * даёт check/uncheck обоим типам); состояние выставляется программно +\n * input/change (AC[2]).\n */\nfunction setChecked(el: Element, action: LlmAction, checked: boolean): void {\n if (!(el instanceof HTMLInputElement) || (el.type !== 'checkbox' && el.type !== 'radio')) {\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires an <input type=\"checkbox\"> or <input type=\"radio\"> target, got <${el.tagName.toLowerCase()}>`,\n );\n }\n el.checked = checked;\n dispatchValueEvents(el);\n}\n\n/**\n * Разбор value scroll: \"dir\" либо \"dir:amount\" (PARAMETER_CONVENTIONS, dc_4);\n * bare-направление берёт amount из ExecutorOptions.defaultScrollAmount.\n * Возвращает дельту (dx, dy) для window.scrollBy.\n */\nfunction parseScrollValue(value: string, defaultAmount: number): { dx: number; dy: number } {\n const parts = value.trim().split(':');\n const dir = parts[0] ?? '';\n const amountPart = parts[1];\n if (parts.length > 2 || !SCROLL_DIRECTIONS.has(dir)) {\n throw new ActionExecutionError(\n `Invalid scroll value \"${value}\" — expected \"up\", \"down\", \"left\", \"right\" or \"dir:amount\" (e.g. \"down:300\")`,\n );\n }\n let amount = defaultAmount;\n if (amountPart !== undefined) {\n if (!/^\\d+(?:\\.\\d+)?$/.test(amountPart)) {\n throw new ActionExecutionError(\n `Invalid scroll amount in \"${value}\" — expected a positive number, e.g. \"down:300\"`,\n );\n }\n amount = Number(amountPart);\n }\n const dx = dir === 'left' ? -amount : dir === 'right' ? amount : 0;\n const dy = dir === 'up' ? -amount : dir === 'down' ? amount : 0;\n return { dx, dy };\n}\n\n/**\n * Синтетическая последовательность HTML5 drag-and-drop (dc_4: drag —\n * target_id=source, value=id destination; executor stateless — источник и цель\n * резолвятся заново на каждое действие). DragEvent может отсутствовать в средах\n * вроде jsdom — фолбэк на bubbling Event с той же семантикой.\n */\nfunction dispatchDragEvent(target: Element, type: string): void {\n if (typeof DragEvent === 'function') {\n target.dispatchEvent(new DragEvent(type, { bubbles: true, cancelable: true }));\n } else {\n target.dispatchEvent(new Event(type, { bubbles: true, cancelable: true }));\n }\n}\n\n/** Проверяет, что цель — форма (dc_4: submit/reset — target_id формы). */\nfunction requireForm(action: LlmAction, el: Element): HTMLFormElement {\n if (el instanceof HTMLFormElement) {\n return el;\n }\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires a <form> target, got <${el.tagName.toLowerCase()}>`,\n );\n}\n\n/**\n * open/close (dc_4: target_id без value): <dialog> → showModal()/close(),\n * <details> → атрибут open; у прочих элементов нет стандартной open-семантики —\n * ActionExecutionError. HTMLDialogElement может отсутствовать в jsdom — guard.\n */\nfunction setOpenState(action: LlmAction, el: Element, open: boolean): void {\n if (typeof HTMLDialogElement !== 'undefined' && el instanceof HTMLDialogElement) {\n if (open) {\n el.showModal();\n } else {\n el.close();\n }\n return;\n }\n if (el instanceof HTMLDetailsElement) {\n el.open = open;\n return;\n }\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires a <dialog> or <details> target, got <${el.tagName.toLowerCase()}>`,\n );\n}\n\n/**\n * expand/collapse (dc_4: target_id без value): <details> → атрибут open,\n * иначе элемент с aria-expanded → установка атрибута (состояние читается\n * напрямую, без событий); иначе ActionExecutionError.\n */\nfunction setExpanded(action: LlmAction, el: Element, expanded: boolean): void {\n if (el instanceof HTMLDetailsElement) {\n el.open = expanded;\n return;\n }\n if (el.hasAttribute('aria-expanded')) {\n el.setAttribute('aria-expanded', expanded ? 'true' : 'false');\n return;\n }\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires a <details> or an element with aria-expanded, got <${el.tagName.toLowerCase()}>`,\n );\n}\n\n/** Пауза между опросами условия wait. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/**\n * Условие wait выполнено (dc_4/dc_7): \"idle\" → документ загрузился\n * (document.readyState === 'complete'); иначе value — CSS-селектор, элемент\n * существует. Невалидный селектор — ActionExecutionError с понятным сообщением.\n */\nfunction isWaitSatisfied(value: string): boolean {\n if (value.trim() === 'idle') {\n return document.readyState === 'complete';\n }\n try {\n return document.querySelector(value) !== null;\n } catch {\n throw new ActionExecutionError(`wait: invalid selector \"${value}\"`);\n }\n}\n\n/**\n * wait: опрос условия (селектор или \"idle\") до таймаута\n * ExecutorOptions.waitTimeout (дефолт 5000). Таймаут — ActionExecutionError\n * (soft-fail отразит его в ActionResult.error).\n */\nasync function waitForValue(value: string, options: ExecutorOptions): Promise<void> {\n const timeoutMs = options.waitTimeout ?? WAIT_DEFAULT_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n if (isWaitSatisfied(value)) {\n return;\n }\n if (Date.now() >= deadline) {\n throw new ActionExecutionError(\n `wait: condition \"${value}\" is not met within ${timeoutMs}ms`,\n );\n }\n await sleep(WAIT_POLL_INTERVAL_MS);\n }\n}\n\n/**\n * Исполняет одно действие LLM на живом DOM (§7.4, T1.4.1–T1.4.3).\n *\n * Soft-fail (решение dev_1): любые сбои — невалидные параметры, целевой\n * элемент не найден, DOM изменился с момента снапшота — возвращаются в\n * ActionResult.error, исключение агентному циклу не бросается.\n *\n * @param action — действие от LLM (после validateAction в M1.3; состав VSL\n * здесь повторно не валидируется — executor проверяет только то, что нужно\n * для исполнения).\n * @param options — ExecutorOptions; root обязан совпадать с корнем снапшота.\n * @returns ActionResult с эхом action/targetId.\n */\nexport async function executeAction(\n action: LlmAction,\n options: ExecutorOptions = {},\n): Promise<ActionResult> {\n const targetId = action.target_id;\n const echo: ActionResult = {\n success: false,\n action: action.action,\n ...(typeof targetId === 'string' ? { targetId } : {}),\n };\n try {\n switch (action.action) {\n // ——— §7.1 Базовые (T1.4.1) ———\n case 'click':\n clickElement(resolveTargetOf(action, options));\n break;\n case 'type':\n typeInto(\n requireTextTarget(action, resolveTargetOf(action, options)),\n requireValue(action),\n );\n break;\n case 'clear': {\n const field = requireTextTarget(action, resolveTargetOf(action, options));\n typeInto(field, '');\n break;\n }\n case 'scroll': {\n const amount = options.defaultScrollAmount ?? DEFAULT_SCROLL_AMOUNT;\n const { dx, dy } = parseScrollValue(requireValue(action), amount);\n window.scrollBy(dx, dy);\n break;\n }\n case 'hover':\n hoverElement(resolveTargetOf(action, options));\n break;\n case 'focus':\n focusableOrThrow(action, resolveTargetOf(action, options)).focus();\n break;\n case 'blur':\n focusableOrThrow(action, resolveTargetOf(action, options)).blur();\n break;\n case 'select':\n selectOption(resolveTargetOf(action, options), requireValue(action));\n break;\n case 'check':\n setChecked(resolveTargetOf(action, options), action, true);\n break;\n case 'uncheck':\n setChecked(resolveTargetOf(action, options), action, false);\n break;\n // ——— §7.2 Расширенные (T1.4.2) ———\n case 'drag': {\n // dc_4: target_id = source, value = id destination (второй resolveTarget).\n // Каноническая последовательность HTML5 DnD: старт у источника,\n // перетаскивание и сброс у цели, завершение у источника.\n const destinationId = requireValue(action);\n const source = resolveTargetOf(action, options);\n const destination = resolveTarget(destinationId, options);\n dispatchDragEvent(source, 'dragstart');\n dispatchDragEvent(destination, 'dragenter');\n dispatchDragEvent(destination, 'dragover');\n dispatchDragEvent(destination, 'drop');\n dispatchDragEvent(source, 'dragend');\n break;\n }\n case 'drop': {\n // Executor stateless: источник неизвестен — цель получает стандартную\n // последовательность dragover (страница разрешает drop через\n // preventDefault в своём обработчике) и drop.\n const dropTarget = resolveTargetOf(action, options);\n dispatchDragEvent(dropTarget, 'dragover');\n dispatchDragEvent(dropTarget, 'drop');\n break;\n }\n case 'submit': {\n // Синтетический cancelable submit с bubbles: нативный form.submit()\n // обходит submit-обработчики и в jsdom не реализован; делегированные\n // обработчики (React onSubmit) событие получают.\n const form = requireForm(action, resolveTargetOf(action, options));\n form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n break;\n }\n case 'reset':\n // Нативный reset(): по спецификации сам диспетчит cancelable 'reset'\n // и сбрасывает поля, если событие не отменено.\n requireForm(action, resolveTargetOf(action, options)).reset();\n break;\n case 'open':\n setOpenState(action, resolveTargetOf(action, options), true);\n break;\n case 'close':\n setOpenState(action, resolveTargetOf(action, options), false);\n break;\n case 'expand':\n setExpanded(action, resolveTargetOf(action, options), true);\n break;\n case 'collapse':\n setExpanded(action, resolveTargetOf(action, options), false);\n break;\n case 'wait':\n await waitForValue(requireValue(action), options);\n break;\n // ——— §7.3 Навигационные (T1.4.3) ———\n case 'navigate':\n // location.assign: сохраняет запись истории (в отличие от replace),\n // принимает и относительные URL (PARAMETER_CONVENTIONS: navigate=URL).\n window.location.assign(requireValue(action));\n break;\n case 'go_back':\n window.history.back();\n break;\n case 'go_forward':\n window.history.forward();\n break;\n case 'refresh':\n window.location.reload();\n break;\n case 'download': {\n // Гибридный подход (решение dc_6): target_id — клик по элементу, value — прямое скачивание по URL\n const downloadTargetId = action.target_id;\n const downloadValue = action.value;\n \n if (typeof downloadTargetId === 'string') {\n // Клик по элементу (кнопка/ссылка для скачивания)\n const el = resolveTarget(downloadTargetId, options);\n clickElement(el);\n // Браузер инициирует download event, extension context обработает его отдельно\n break;\n }\n \n if (typeof downloadValue === 'string') {\n // Прямое скачивание по URL\n const url = downloadValue;\n const a = document.createElement('a');\n a.href = url;\n a.download = ''; // Браузер сам определит filename из URL\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n // Генерируем downloadId для отслеживания\n const downloadId = `download_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const filename = url.split('/').pop() || 'download';\n const downloadInfo: DownloadInfo = {\n downloadId,\n filename,\n url,\n status: 'pending',\n };\n return {\n ...echo,\n success: true,\n download: downloadInfo,\n };\n }\n \n throw new ActionExecutionError(\n 'Action \"download\" requires either target_id (element to click) or value (URL to download)',\n );\n }\n default:\n // После validateAction (M1.3) сюда попасть нельзя — защита от прямых\n // вызовов executeAction с действием вне VALID_ACTIONS.\n throw new ActionExecutionError(\n `Unknown action: \"${action.action}\" — must be one of VALID_ACTIONS (24 actions)`,\n );\n }\n return { ...echo, success: true };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { ...echo, success: false, error: message };\n }\n}"],"mappings":"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","names":["sleep","DEFAULT_MODEL","DEFAULT_BASE_URL","DEFAULT_MODEL","DEFAULT_BASE_URL","defaultRoot"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils/idGenerator.ts","../src/security/promptInjectionFilter.ts","../src/security/remotePatternsLoader.ts","../src/capture/domExtractor.ts","../src/segmentation/level1.ts","../src/segmentation/level2.ts","../src/segmentation/level3.ts","../src/segmentation/level4.ts","../src/segmentation/segmenter.ts","../src/types/vsl.ts","../src/builder/vslBuilder.ts","../src/cache/cacheStore.ts","../src/cache/invalidation.ts","../src/diff/diffEngine.ts","../src/vision/enrichWithVision.ts","../src/session/snapshotSession.ts","../src/llm/types.ts","../src/llm/actions.ts","../src/llm/prompt.ts","../src/llm/schema.ts","../src/llm/transport.ts","../src/llm/openai.ts","../src/llm/anthropic.ts","../src/llm/alibaba.ts","../src/executor/types.ts","../src/executor/resolveTarget.ts","../src/executor/actionExecutor.ts"],"sourcesContent":["/**\n * VSL SDK — публичное API. Точка входа:\n * - Snapshot generation (M1.1): capture → segment → build;\n * - Cache & Diff (M1.2): Cache Store, invalidation, Diff Engine (§6.2),\n * Snapshot Session (первый вызов → VslDocument, далее → VslDiff).\n * - LLM Integration (M1.3): LLM Adapter (OpenAI/Anthropic), Action Model, промпты и retry.\n */\n\n// ——— M1.7: ID Generator (унифицированная генерация ID) ———\n\nexport { IdGenerator, createIdGenerator, TYPE_ABBREVIATIONS } from './utils/idGenerator';\n\n// ——— M1.1: Snapshot generation (сохранено полностью) ———\n\nexport { extractDomTree, ownText } from './capture/domExtractor';\nexport type { ExtractedElement, Rect } from './capture/domExtractor';\nexport { LEVEL1_TAG_MAP, resolveLevel1Type } from './segmentation/level1';\nexport { ARIA_ROLE_TYPE_MAP, resolveAriaRoleType, resolveSt } from './segmentation/level2';\nexport { isAriaHidden, segmentTree } from './segmentation/segmenter';\nexport type { SegmentedElement } from './segmentation/segmenter';\nexport { buildVslDocument } from './builder/vslBuilder';\nexport type { BuildOptions } from './builder/vslBuilder';\nexport { VSL_VERSION } from './types/vsl';\nexport type {\n VisualFragment,\n VisualFragmentType,\n VslCanvas,\n VslDocument,\n VslObject,\n VslState,\n VslType,\n VslViewport,\n} from './types/vsl';\n\n// ——— M1.2: Cache Store + invalidation (T1.2.1/T1.2.2) ———\n\nexport { createCacheStore, computeContentHash, computeCoordHash } from './cache/cacheStore';\nexport type { CacheEntry, CacheStore } from './cache/cacheStore';\nexport { attachMutationObserver } from './cache/invalidation';\nexport type { MutationObserverHandle } from './cache/invalidation';\n\n// ——— M1.2: Diff Engine (T1.2.3, формат ARCHITECTURE.md §6.2) ———\n\nexport { diffVslDocuments } from './diff/diffEngine';\nexport type {\n DiffOptions,\n VslDiff,\n VslDiffChanges,\n VslModifiedObject,\n VslRemovedObject,\n} from './diff/diffEngine';\n\n// ——— M1.2: Snapshot Session (T1.2.4) ———\n\nexport { VslSnapshotSession, isVslDiff } from './session/snapshotSession';\nexport type { SnapshotInput, SnapshotResult } from './session/snapshotSession';\n\n// ——— M1.3: LLM Integration (T1.3.1–T1.3.5) ———\n\nexport { OpenAIAdapter } from './llm/openai';\nexport { AnthropicAdapter } from './llm/anthropic';\nexport { AlibabaAdapter } from './llm/alibaba';\nexport { LlmError, LlmValidationError } from './llm/types';\nexport { VALID_ACTIONS, collectIds, validateAction } from './llm/actions';\nexport type { ValidAction } from './llm/actions';\nexport {\n ACTION_TOOL_DESCRIPTION,\n ACTION_TOOL_NAME,\n ACTION_TOOL_SCHEMA,\n} from './llm/schema';\nexport { buildSystemPrompt, buildUserPrompt } from './llm/prompt';\nexport type { FewShotExample } from './llm/prompt';\nexport { retryWithBackoff } from './llm/transport';\nexport type { RetryOptions } from './llm/transport';\nexport type {\n DecideInput,\n LlmAction,\n LlmAdapter,\n LlmAdapterConfig,\n LlmResponse,\n LlmUsage,\n LlmTransport,\n SendPromptOptions,\n Sleep,\n VslFragmentMeta,\n VslInput,\n VslObjectWithVf,\n VisualFragmentData,\n VisualFragmentStore,\n} from './llm/types';\n\n// ——— M1.4: Action Executor (T1.4.1–T1.4.3) ———\n\nexport { executeAction } from './executor/actionExecutor';\nexport { resolveTarget } from './executor/resolveTarget';\nexport { ActionExecutionError } from './executor/types';\nexport type { ActionResult, ExecutorOptions } from './executor/types';\n\n\n// ——— M1.8: Prompt Injection Filter (T1.8.1-T1.8.5) ———\n\nexport { PromptInjectionFilter, getGlobalFilter, createFilter } from './security/promptInjectionFilter';\nexport type {\n FilterOptions,\n InjectionPattern,\n ScanResult,\n Detection,\n SecurityLogEntry,\n PatternType,\n Severity,\n FilterAction,\n} from './security/promptInjectionFilter';\nexport { RemotePatternsLoader } from './security/remotePatternsLoader';\nexport type { RemotePatternsLoaderOptions, RemotePatternsResult } from './security/remotePatternsLoader';\nexport const VSL_SDK_VERSION = '0.1.0';","/**\n * Unified ID Generator for VSL (M1.7 optimization).\n *\n * Strategy:\n * 1. Priority: use existing DOM `id` if present (stable, readable)\n * 2. Fallback: short generated IDs with abbreviated types (btn_1, inp_2, cont_3)\n *\n * Works for both browser path (SegmentedElement) and HTTP path (cheerio elements).\n * IDs are deterministic (DOM traversal order is stable) and unique within a snapshot.\n */\n\n/** Type abbreviations for short ID generation. */\nexport const TYPE_ABBREVIATIONS: Readonly<Record<string, string>> = {\n button: 'btn',\n input: 'inp',\n link: 'link',\n container: 'cont',\n image: 'img',\n select: 'sel',\n textarea: 'txt',\n heading: 'h',\n text: 'txt',\n file_input: 'file',\n modal: 'modal',\n tab: 'tab',\n dropdown_toggle: 'dropdown',\n footer: 'footer',\n scrollable_container: 'scroll',\n toolbar: 'toolbar',\n list: 'list',\n grid: 'grid',\n form_field: 'form',\n tab_bar: 'tabs',\n layout: 'layout',\n nav: 'nav',\n header: 'header',\n main: 'main',\n icon: 'icon',\n chart: 'chart',\n custom_widget: 'widget',\n unknown: 'el',\n};\n\n/**\n * ID Generator class for a single snapshot.\n * Maintains a global counter to ensure uniqueness across the entire snapshot.\n */\nexport class IdGenerator {\n private typeCounters: Map<string, number> = new Map();\n\n /**\n * Generate ID for an element.\n *\n * @param elementId - Existing DOM id attribute (if present)\n * @param vslType - VSL type of the element (for fallback abbreviation)\n * @returns Generated ID string\n */\n generate(elementId: string | null | undefined, vslType: string | null): string {\n // Priority 1: use existing DOM id if present and non-empty\n if (elementId && elementId.trim().length > 0) {\n return elementId;\n }\n\n // Priority 2: fallback to short generated ID with per-type counter\n // Per-type counters ensure stability: adding a new button doesn't shift\n // nav/link/container IDs. Only elements of the same type are affected.\n const shortType = vslType ? (TYPE_ABBREVIATIONS[vslType] || 'el') : 'el';\n const count = (this.typeCounters.get(shortType) || 0) + 1;\n this.typeCounters.set(shortType, count);\n return `${shortType}_${count}`;\n }\n\n /** Reset counters (for testing or new snapshot). */\n reset(): void {\n this.typeCounters.clear();\n }\n\n /** Get current counter value for a type (for debugging). */\n getCounter(type?: string): number {\n if (type) {\n return this.typeCounters.get(type) || 0;\n }\n // Return total count across all types\n let total = 0;\n for (const count of this.typeCounters.values()) {\n total += count;\n }\n return total;\n }\n}\n\n/**\n * Create a new ID generator for a snapshot.\n */\nexport function createIdGenerator(): IdGenerator {\n return new IdGenerator();\n}","/**\n * Prompt Injection Filter (T1.8.1, M1.8, DEC-027).\n *\n * Слой безопасности на входе Capture Layer — сразу после извлечения текста из любого источника\n * (DOM, raw HTML), до сегментации и упаковки в VSL JSON.\n *\n * Архитектура:\n * - Scanner — regex-паттерны для обнаружения инъекций\n * - Logger — security audit trail (локальное хранение)\n * - Stripper — вырезание инъекций из текста\n *\n * 3 уровня паттернов:\n * - Bundled (patterns_v1.json в SDK)\n * - Remote (CDN/GitHub, автозагрузка при запуске)\n * - Custom (пользовательские через vsl.config.json)\n *\n * Действия:\n * - strip — вырезать инъекцию, оставить остальной контент\n * - log — только логировать обнаружение\n * - block — блокировать весь текст (вернуть пустую строку)\n *\n * False positive strategy:\n * - Confidence threshold (0.7 по умолчанию)\n * - Domain whitelist (доверенные домены пропускаются)\n * - User override (отключение фильтра для конкретных доменов)\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { RemotePatternsLoader, type RemotePatternsLoaderOptions } from './remotePatternsLoader';\n\n/** Тип паттерна (пока только regex, ML зарезервирован). */\nexport type PatternType = 'regex' | 'ml';\n\n/** Уровень серьёзности инъекции. */\nexport type Severity = 'low' | 'medium' | 'high' | 'critical';\n\n/** Действие при обнаружении инъекции. */\nexport type FilterAction = 'strip' | 'log' | 'block';\n\n/** Формат паттерна для обнаружения инъекций. */\nexport interface InjectionPattern {\n /** Уникальный идентификатор паттерна. */\n id: string;\n /** Regex-паттерн для обнаружения инъекции. */\n pattern: string;\n /** Тип паттерна (regex или ml). */\n type: PatternType;\n /** Уровень серьёзности. */\n severity: Severity;\n /** Действие при обнаружении. */\n action: FilterAction;\n /** Описание паттерна. */\n description: string;\n /** Confidence threshold (0-1), по умолчанию 0.7. */\n confidence?: number;\n}\n\n/** Результат сканирования текста. */\nexport interface ScanResult {\n /** Очищенный текст (после strip/block). */\n cleanText: string;\n /** Обнаруженные инъекции. */\n detections: Detection[];\n /** Был ли текст заблокирован полностью. */\n blocked: boolean;\n /** Время сканирования (мс). */\n scanTimeMs: number;\n}\n\n/** Обнаруженная инъекция. */\nexport interface Detection {\n /** ID паттерна, который сработал. */\n patternId: string;\n /** Описание паттерна. */\n description: string;\n /** Уровень серьёзности. */\n severity: Severity;\n /** Действие, которое было выполнено. */\n action: FilterAction;\n /** Текст, который совпал с паттерном. */\n matchedText: string;\n /** Позиция в исходном тексте (start index). */\n index: number;\n /** Контекст вокруг совпадения (для аудита). */\n context: string;\n}\n\n/** Запись в security audit log. */\nexport interface SecurityLogEntry {\n /** Timestamp обнаружения (ISO 8601). */\n timestamp: string;\n /** URL страницы (если доступен). */\n url?: string;\n /** ID паттерна. */\n patternId: string;\n /** Описание паттерна. */\n description: string;\n /** Уровень серьёзности. */\n severity: Severity;\n /** Действие, которое было выполнено. */\n action: FilterAction;\n /** Текст, который совпал с паттерном. */\n matchedText: string;\n /** Контекст вокруг совпадения. */\n context: string;\n}\n\n/** Опции конфигурации фильтра. */\nexport interface FilterOptions {\n /** Включить/выключить фильтр (по умолчанию true). */\n enabled?: boolean;\n /** Confidence threshold (0-1, по умолчанию 0.7). */\n confidenceThreshold?: number;\n /** Domain whitelist — доверенные домены, которые пропускаются. */\n domainWhitelist?: string[];\n /** Путь к bundled patterns.json. */\n bundledPatternsPath?: string;\n /** Путь к remote patterns (локальный кэш). */\n remotePatternsPath?: string;\n /** Custom patterns (из vsl.config.json). */\n customPatterns?: InjectionPattern[];\n /** Путь к security audit log. */\n logPath?: string;\n /** Максимальный размер лога (количество записей, по умолчанию 10000). */\n maxLogEntries?: number;\n /** Опции для RemotePatternsLoader (автозагрузка с CDN). */\n remoteLoader?: RemotePatternsLoaderOptions;\n}\n\n/** Компилированный паттерн с regex-объектом. */\ninterface CompiledPattern extends InjectionPattern {\n regex: RegExp;\n confidenceThreshold: number;\n}\n\n/** Путь к директории для логов по умолчанию. */\nconst DEFAULT_LOG_DIR = join(homedir(), '.vsl', 'logs');\n\n/** Путь к security audit log по умолчанию. */\nconst DEFAULT_LOG_PATH = join(DEFAULT_LOG_DIR, 'security-audit.log');\n\n/** Путь к bundled patterns по умолчанию. */\nconst DEFAULT_BUNDLED_PATTERNS_PATH = join(__dirname, '..', '..', 'patterns', 'patterns_v1.json');\n\n/** Путь к remote patterns кэшу по умолчанию. */\nconst DEFAULT_REMOTE_PATTERNS_PATH = join(homedir(), '.vsl', 'cache', 'remote-patterns.json');\n\n/** Confidence threshold по умолчанию. */\nconst DEFAULT_CONFIDENCE_THRESHOLD = 0.7;\n\n/** Максимальный размер лога по умолчанию. */\nconst DEFAULT_MAX_LOG_ENTRIES = 10000;\n\n/**\n * Prompt Injection Filter — основной класс фильтра.\n *\n * Использование:\n * * const filter = new PromptInjectionFilter({\n * enabled: true,\n * confidenceThreshold: 0.7,\n * domainWhitelist: ['github.com', 'docs.google.com'],\n * });\n *\n * const result = filter.scan(text, 'https://example.com');\n * console.log(result.cleanText); // Очищенный текст\n * console.log(result.detections); // Обнаруженные инъекции\n * */\nexport class PromptInjectionFilter {\n private enabled: boolean;\n private confidenceThreshold: number;\n private domainWhitelist: Set<string>;\n private patterns: CompiledPattern[] = [];\n private logPath: string;\n private maxLogEntries: number;\n private logEntries: SecurityLogEntry[] = [];\n private remoteLoader: RemotePatternsLoader | null = null;\n\n constructor(options: FilterOptions = {}) {\n this.enabled = options.enabled ?? true;\n this.confidenceThreshold = options.confidenceThreshold ?? DEFAULT_CONFIDENCE_THRESHOLD;\n this.domainWhitelist = new Set(options.domainWhitelist ?? []);\n this.logPath = options.logPath ?? DEFAULT_LOG_PATH;\n this.maxLogEntries = options.maxLogEntries ?? DEFAULT_MAX_LOG_ENTRIES;\n\n // Инициализируем RemotePatternsLoader (если включён)\n if (options.remoteLoader?.enabled !== false) {\n this.remoteLoader = new RemotePatternsLoader({\n remoteUrl: options.remoteLoader?.remoteUrl,\n cachePath: options.remoteLoader?.cachePath ?? options.remotePatternsPath,\n cacheTtlMs: options.remoteLoader?.cacheTtlMs,\n enabled: options.remoteLoader?.enabled ?? true,\n timeoutMs: options.remoteLoader?.timeoutMs,\n });\n }\n\n // Загружаем паттерны (синхронно — bundled + custom)\n this.loadPatterns(options);\n\n // Загружаем существующие логи (если есть)\n this.loadExistingLogs();\n }\n\n /**\n * Загружает паттерны из всех источников (bundled, remote, custom).\n */\n private loadPatterns(options: FilterOptions): void {\n const allPatterns: InjectionPattern[] = [];\n\n // 1. Bundled patterns\n const bundledPath = options.bundledPatternsPath ?? DEFAULT_BUNDLED_PATTERNS_PATH;\n if (existsSync(bundledPath)) {\n try {\n const content = readFileSync(bundledPath, 'utf-8');\n const bundled = JSON.parse(content) as InjectionPattern[];\n allPatterns.push(...bundled);\n } catch (error) {\n console.error(`[PromptInjectionFilter] Failed to load bundled patterns: ${error}`);\n }\n }\n\n // 2. Remote patterns (локальный кэш)\n const remotePath = options.remotePatternsPath ?? DEFAULT_REMOTE_PATTERNS_PATH;\n if (existsSync(remotePath)) {\n try {\n const content = readFileSync(remotePath, 'utf-8');\n const remote = JSON.parse(content) as InjectionPattern[];\n allPatterns.push(...remote);\n } catch (error) {\n console.error(`[PromptInjectionFilter] Failed to load remote patterns: ${error}`);\n }\n }\n\n // 3. Custom patterns\n if (options.customPatterns && options.customPatterns.length > 0) {\n allPatterns.push(...options.customPatterns);\n }\n\n // Компилируем regex-паттерны\n this.patterns = allPatterns\n .filter((p) => p.type === 'regex') // Пока только regex\n .map((p) => ({\n ...p,\n regex: new RegExp(p.pattern, 'gi'), // case-insensitive, global\n confidenceThreshold: p.confidence ?? this.confidenceThreshold,\n }));\n }\n\n /**\n * Асинхронно загружает remote patterns через RemotePatternsLoader.\n * Вызывается опционально для обновления паттернов с CDN/GitHub.\n *\n * @returns Результат загрузки remote patterns\n */\n async loadRemotePatterns(): Promise<{ patterns: number; source: string; fallbackUsed: boolean; error?: string }> {\n if (!this.remoteLoader) {\n return { patterns: 0, source: 'none', fallbackUsed: false };\n }\n\n const result = await this.remoteLoader.load();\n\n // Добавляем remote patterns к существующим\n if (result.patterns.length > 0) {\n const remoteCompiled = result.patterns\n .filter((p) => p.type === 'regex')\n .map((p) => ({\n ...p,\n regex: new RegExp(p.pattern, 'gi'),\n confidenceThreshold: p.confidence ?? this.confidenceThreshold,\n }));\n\n // Удаляем старые remote patterns (по source) и добавляем новые\n // Для простоты — просто добавляем новые паттерны\n this.patterns.push(...remoteCompiled);\n }\n\n return {\n patterns: result.patterns.length,\n source: result.source,\n fallbackUsed: result.fallbackUsed,\n error: result.error,\n };\n }\n\n /**\n * Получает информацию о кэше remote patterns.\n */\n getRemoteCacheInfo(): { exists: boolean; lastFetched?: string; patternCount?: number } {\n if (!this.remoteLoader) {\n return { exists: false };\n }\n return this.remoteLoader.getCacheInfo();\n }\n\n /**\n * Очищает кэш remote patterns.\n */\n clearRemoteCache(): void {\n if (this.remoteLoader) {\n this.remoteLoader.clearCache();\n }\n }\n\n /**\n * Загружает существующие логи из файла.\n */\n private loadExistingLogs(): void {\n if (!existsSync(this.logPath)) {\n return;\n }\n\n try {\n const content = readFileSync(this.logPath, 'utf-8');\n const lines = content.trim().split('\\n').filter((line) => line.length > 0);\n this.logEntries = lines.map((line) => JSON.parse(line) as SecurityLogEntry);\n } catch (error) {\n console.error(`[PromptInjectionFilter] Failed to load existing logs: ${error}`);\n this.logEntries = [];\n }\n }\n\n /**\n * Проверяет, находится ли URL в whitelist.\n */\n isWhitelisted(url?: string): boolean {\n if (!url) return false;\n\n try {\n const hostname = new URL(url).hostname;\n return this.domainWhitelist.has(hostname);\n } catch {\n return false;\n }\n }\n\n /**\n * Сканирует текст на наличие инъекций.\n *\n * @param text — текст для сканирования\n * @param url — URL страницы (для whitelist и логирования)\n * @returns Результат сканирования с очищенным текстом и списком обнаружений\n */\n scan(text: string, url?: string): ScanResult {\n const startTime = performance.now();\n\n // Если фильтр выключен или URL в whitelist — возвращаем текст как есть\n if (!this.enabled || this.isWhitelisted(url)) {\n return {\n cleanText: text,\n detections: [],\n blocked: false,\n scanTimeMs: performance.now() - startTime,\n };\n }\n\n const detections: Detection[] = [];\n let cleanText = text;\n let blocked = false;\n\n // Сканируем каждый паттерн\n for (const pattern of this.patterns) {\n // Сбрасываем lastIndex для global regex\n pattern.regex.lastIndex = 0;\n\n let match: RegExpExecArray | null;\n while ((match = pattern.regex.exec(text)) !== null) {\n const matchedText = match[0];\n const index = match.index;\n\n // Извлекаем контекст (50 символов до и после)\n const contextStart = Math.max(0, index - 50);\n const contextEnd = Math.min(text.length, index + matchedText.length + 50);\n const context = text.slice(contextStart, contextEnd);\n\n const detection: Detection = {\n patternId: pattern.id,\n description: pattern.description,\n severity: pattern.severity,\n action: pattern.action,\n matchedText,\n index,\n context,\n };\n\n detections.push(detection);\n\n // Логируем обнаружение\n this.logDetection(detection, url);\n\n // Выполняем действие\n if (pattern.action === 'block') {\n blocked = true;\n cleanText = '';\n break; // Блокируем весь текст, выходим из цикла\n } else if (pattern.action === 'strip') {\n // Вырезаем инъекцию из текста\n cleanText = cleanText.replace(matchedText, '');\n }\n // action === 'log' — только логируем, не модифицируем текст\n\n // Предотвращаем бесконечный цикл для zero-length matches\n if (match.index === pattern.regex.lastIndex) {\n pattern.regex.lastIndex++;\n }\n }\n }\n\n // Нормализуем пробелы после strip\n if (detections.some((d) => d.action === 'strip')) {\n cleanText = cleanText.replace(/\\s+/g, ' ').trim();\n }\n\n return {\n cleanText,\n detections,\n blocked,\n scanTimeMs: performance.now() - startTime,\n };\n }\n\n /**\n * Логирует обнаружение в security audit trail.\n */\n private logDetection(detection: Detection, url?: string): void {\n const entry: SecurityLogEntry = {\n timestamp: new Date().toISOString(),\n url,\n patternId: detection.patternId,\n description: detection.description,\n severity: detection.severity,\n action: detection.action,\n matchedText: detection.matchedText,\n context: detection.context,\n };\n\n this.logEntries.push(entry);\n\n // Ротация логов — удаляем старые записи, если превышен лимит\n if (this.logEntries.length > this.maxLogEntries) {\n this.logEntries = this.logEntries.slice(-this.maxLogEntries);\n }\n\n // Записываем в файл\n this.writeLogEntry(entry);\n }\n\n /**\n * Записывает одну запись в лог-файл.\n */\n private writeLogEntry(entry: SecurityLogEntry): void {\n try {\n // Создаём директорию, если не существует\n const logDir = join(this.logPath, '..');\n if (!existsSync(logDir)) {\n mkdirSync(logDir, { recursive: true });\n }\n\n // Добавляем запись в файл (JSON Lines формат)\n appendFileSync(this.logPath, JSON.stringify(entry) + '\\n', 'utf-8');\n } catch (error) {\n console.error(`[PromptInjectionFilter] Failed to write log entry: ${error}`);\n }\n }\n\n /**\n * Получает все логи (для аудита).\n */\n getLogs(): SecurityLogEntry[] {\n return [...this.logEntries];\n }\n\n /**\n * Очищает логи (для тестирования).\n */\n clearLogs(): void {\n this.logEntries = [];\n try {\n if (existsSync(this.logPath)) {\n writeFileSync(this.logPath, '', 'utf-8');\n }\n } catch (error) {\n console.error(`[PromptInjectionFilter] Failed to clear logs: ${error}`);\n }\n }\n\n /**\n * Добавляет custom pattern динамически.\n */\n addCustomPattern(pattern: InjectionPattern): void {\n if (pattern.type !== 'regex') {\n console.warn(`[PromptInjectionFilter] Only regex patterns are supported, skipping: ${pattern.id}`);\n return;\n }\n\n const compiled: CompiledPattern = {\n ...pattern,\n regex: new RegExp(pattern.pattern, 'gi'),\n confidenceThreshold: pattern.confidence ?? this.confidenceThreshold,\n };\n\n this.patterns.push(compiled);\n }\n\n /**\n * Удаляет custom pattern по ID.\n */\n removePattern(patternId: string): boolean {\n const initialLength = this.patterns.length;\n this.patterns = this.patterns.filter((p) => p.id !== patternId);\n return this.patterns.length <initialLength;\n }\n\n /**\n * Получает количество загруженных паттернов.\n */\n getPatternCount(): number {\n return this.patterns.length;\n }\n\n /**\n * Включает/выключает фильтр.\n */\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n /**\n * Проверяет, включён ли фильтр.\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n}\n\n/**\n * Фабричная функция для создания фильтра с дефолтными настройками.\n */\nexport function createFilter(options?: FilterOptions): PromptInjectionFilter {\n return new PromptInjectionFilter(options);\n}\n\n/**\n * Глобальный экземпляр фильтра (singleton) для использования в модулях.\n * Инициализируется лениво при первом вызове.\n */\nlet globalFilter: PromptInjectionFilter | null = null;\n\n/**\n * Получает глобальный экземпляр фильтра.\n */\nexport function getGlobalFilter(): PromptInjectionFilter {\n if (!globalFilter) {\n globalFilter = new PromptInjectionFilter();\n }\n return globalFilter;\n}\n\n/**\n * Сбрасывает глобальный экземпляр фильтра (для тестирования).\n */\nexport function resetGlobalFilter(): void {\n globalFilter = null;\n}","/**\n * Remote Patterns Loader (T1.8.3, M1.8, DEC-027).\n *\n * Загружает паттерны инъекций с удалённого CDN/GitHub, кэширует локально,\n * обеспечивает fallback на bundled patterns при отсутствии интернета.\n *\n * Архитектура:\n * - Fetch: загрузка с remote URL (https://vsl.dev/patterns/latest.json)\n * - Cache: локальное кэширование (~/.vsl/cache/remote-patterns.json)\n * - Fallback: если fetch не удался, используем кэш или bundled patterns\n * - TTL: кэш валиден 24 часа (настраивается)\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { InjectionPattern } from './promptInjectionFilter';\n\n/** Опции remote patterns loader. */\nexport interface RemotePatternsLoaderOptions {\n /** URL для загрузки remote patterns. */\n remoteUrl?: string;\n /** Путь к локальному кэшу remote patterns. */\n cachePath?: string;\n /** TTL кэша в миллисекундах (по умолчанию 24 часа). */\n cacheTtlMs?: number;\n /** Включить/выключить remote loader (по умолчанию true). */\n enabled?: boolean;\n /** Timeout для HTTP-запроса в миллисекундах (по умолчанию 5000). */\n timeoutMs?: number;\n}\n\n/** Метаданные кэша для отслеживания TTL. */\ninterface CacheMetadata {\n /** Timestamp последней загрузки (ISO 8601). */\n lastFetched: string;\n /** URL, с которого были загружены паттерны. */\n sourceUrl: string;\n /** Количество загруженных паттернов. */\n patternCount: number;\n}\n\n/** Формат файла кэша (паттерны + метаданные). */\ninterface CacheFile {\n metadata: CacheMetadata;\n patterns: InjectionPattern[];\n}\n\n/** Результат загрузки remote patterns. */\nexport interface RemotePatternsResult {\n /** Загруженные паттерны. */\n patterns: InjectionPattern[];\n /** Источник паттернов: 'remote' (свежая загрузка), 'cache' (из кэша), 'none' (нет паттернов). */\n source: 'remote' | 'cache' | 'none';\n /** Был ли использован fallback (если remote fetch не удался). */\n fallbackUsed: boolean;\n /** Ошибка (если произошла). */\n error?: string;\n}\n\n/** Default URL для загрузки remote patterns. */\nconst DEFAULT_REMOTE_URL = 'https://raw.githubusercontent.com/MaxZhadobin/visual-scene-language/vsl_mcp/patterns/latest.json';\n\n/** Default путь к кэшу. */\nconst DEFAULT_CACHE_PATH = join(homedir(), '.vsl', 'cache', 'remote-patterns.json');\n\n/** Default TTL кэша — 24 часа. */\nconst DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\n\n/** Default timeout для HTTP-запроса — 5 секунд. */\nconst DEFAULT_TIMEOUT_MS = 5000;\n\n/**\n * Remote Patterns Loader — загрузка паттернов с CDN/GitHub.\n *\n * Использование:\n * * const loader = new RemotePatternsLoader({\n * remoteUrl: 'https://vsl.dev/patterns/latest.json',\n * cacheTtlMs: 24 * 60 * 60 * 1000, // 24 часа\n * });\n *\n * const result = await loader.load();\n * console.log(result.patterns); // Загруженные паттерны\n * console.log(result.source); // 'remote' | 'cache' | 'none'\n * */\nexport class RemotePatternsLoader {\n private remoteUrl: string;\n private cachePath: string;\n private cacheTtlMs: number;\n private enabled: boolean;\n private timeoutMs: number;\n\n constructor(options: RemotePatternsLoaderOptions = {}) {\n this.remoteUrl = options.remoteUrl ?? DEFAULT_REMOTE_URL;\n this.cachePath = options.cachePath ?? DEFAULT_CACHE_PATH;\n this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;\n this.enabled = options.enabled ?? true;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n /**\n * Загружает remote patterns с fallback на кэш.\n *\n * Логика:\n * 1. Если enabled === false — возвращаем empty\n * 2. Проверяем кэш — если валиден (TTL не истёк), используем кэш\n * 3. Пытаемся загрузить с remote URL\n * 4. Если загрузка успешна — обновляем кэш, возвращаем remote patterns\n * 5. Если загрузка не удалась — используем кэш (fallback)\n * 6. Если кэша нет — возвращаем empty\n */\n async load(): Promise<RemotePatternsResult> {\n if (!this.enabled) {\n return { patterns: [], source: 'none', fallbackUsed: false };\n }\n\n // Проверяем валидность кэша\n const cachedData = this.readCache();\n const cacheIsValid = this.isCacheValid(cachedData);\n\n // Если кэш валиден и не нужно обновлять — используем кэш\n if (cacheIsValid) {\n return {\n patterns: cachedData!.patterns,\n source: 'cache',\n fallbackUsed: false,\n };\n }\n\n // Пытаемся загрузить с remote\n try {\n const patterns = await this.fetchRemotePatterns();\n \n // Обновляем кэш\n this.writeCache(patterns);\n\n return {\n patterns,\n source: 'remote',\n fallbackUsed: false,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Fallback на кэш\n if (cachedData) {\n return {\n patterns: cachedData.patterns,\n source: 'cache',\n fallbackUsed: true,\n error: errorMessage,\n };\n }\n\n // Кэша нет — возвращаем empty\n return {\n patterns: [],\n source: 'none',\n fallbackUsed: true,\n error: errorMessage,\n };\n }\n }\n\n /**\n * Загружает паттерны с remote URL.\n */\n private async fetchRemotePatterns(): Promise<InjectionPattern[]> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const response = await fetch(this.remoteUrl, {\n signal: controller.signal,\n headers: {\n 'Accept': 'application/json',\n 'User-Agent': 'VSL-Patterns-Loader/1.0',\n },\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data = await response.json();\n\n // Поддержка двух форматов:\n // 1. Простой массив паттернов (обратная совместимость)\n // 2. Объект с { metadata, patterns } (новый формат с метаданными)\n let patternsArray: unknown[];\n if (Array.isArray(data)) {\n patternsArray = data;\n } else if (typeof data === 'object' && data !== null && Array.isArray((data as Record<string, unknown>).patterns)) {\n patternsArray = (data as Record<string, unknown>).patterns as unknown[];\n } else {\n throw new Error('Invalid patterns format: expected array or { patterns: [] }');\n }\n\n // Валидация каждого паттерна\n const patterns: InjectionPattern[] = [];\n for (const item of patternsArray) {\n if (this.isValidPattern(item)) {\n patterns.push(item as InjectionPattern);\n }\n }\n\n return patterns;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Проверяет валидность паттерна.\n */\n private isValidPattern(item: unknown): boolean {\n if (typeof item !== 'object' || item === null) return false;\n \n const obj = item as Record<string, unknown>;\n return (\n typeof obj.id === 'string' &&\n typeof obj.pattern === 'string' &&\n typeof obj.type === 'string' &&\n (obj.type === 'regex' || obj.type === 'ml') &&\n typeof obj.severity === 'string' &&\n ['low', 'medium', 'high', 'critical'].includes(obj.severity as string) &&\n typeof obj.action === 'string' &&\n ['strip', 'log', 'block'].includes(obj.action as string) &&\n typeof obj.description === 'string'\n );\n }\n\n /**\n * Читает кэш из файла.\n */\n private readCache(): CacheFile | null {\n if (!existsSync(this.cachePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(this.cachePath, 'utf-8');\n const data = JSON.parse(content) as CacheFile;\n\n // Валидация структуры кэша\n if (!data.metadata || !Array.isArray(data.patterns)) {\n return null;\n }\n\n return data;\n } catch {\n return null;\n }\n }\n\n /**\n * Записывает паттерны в кэш.\n */\n private writeCache(patterns: InjectionPattern[]): void {\n const cacheData: CacheFile = {\n metadata: {\n lastFetched: new Date().toISOString(),\n sourceUrl: this.remoteUrl,\n patternCount: patterns.length,\n },\n patterns,\n };\n\n try {\n // Создаём директорию, если не существует\n const cacheDir = dirname(this.cachePath);\n if (!existsSync(cacheDir)) {\n mkdirSync(cacheDir, { recursive: true });\n }\n\n writeFileSync(this.cachePath, JSON.stringify(cacheData, null, 2), 'utf-8');\n } catch (error) {\n console.error(`[RemotePatternsLoader] Failed to write cache: ${error}`);\n }\n }\n\n /**\n * Проверяет валидность кэша (TTL не истёк).\n */\n private isCacheValid(cachedData: CacheFile | null): boolean {\n if (!cachedData) return false;\n\n try {\n const lastFetched = new Date(cachedData.metadata.lastFetched).getTime();\n const now = Date.now();\n return (now - lastFetched) <this.cacheTtlMs;\n } catch {\n return false;\n }\n }\n\n /**\n * Очищает кэш (для тестирования).\n */\n clearCache(): void {\n try {\n if (existsSync(this.cachePath)) {\n writeFileSync(this.cachePath, '', 'utf-8');\n }\n } catch (error) {\n console.error(`[RemotePatternsLoader] Failed to clear cache: ${error}`);\n }\n }\n\n /**\n * Получает информацию о кэше (для диагностики).\n */\n getCacheInfo(): { exists: boolean; lastFetched?: string; patternCount?: number } {\n const cachedData = this.readCache();\n if (!cachedData) {\n return { exists: false };\n }\n\n return {\n exists: true,\n lastFetched: cachedData.metadata.lastFetched,\n patternCount: cachedData.metadata.patternCount,\n };\n }\n\n /**\n * Получает URL для загрузки remote patterns.\n */\n getRemoteUrl(): string {\n return this.remoteUrl;\n }\n\n /**\n * Устанавливает URL для загрузки remote patterns.\n */\n setRemoteUrl(url: string): void {\n this.remoteUrl = url;\n }\n}\n\n/**\n * Фабричная функция для создания remote patterns loader.\n */\nexport function createRemotePatternsLoader(options?: RemotePatternsLoaderOptions): RemotePatternsLoader {\n return new RemotePatternsLoader(options);\n}","/**\n * DOM extraction (T1.1.2, ROADMAP.md M1.1).\n *\n * Обходит дерево от корня (по умолчанию document.body) и извлекает все видимые\n * элементы с координатами (getBoundingClientRect), собственным текстом, атрибутами и CSS-подмножеством (Level 3, T1.5.1).\n * Покрывает тот же набор элементов, что и root.querySelectorAll('*'), минус отфильтрованные.\n *\n * Фильтр невидимых (семантика ARCHITECTURE.md; CSS-уровень 3 вне scope M1.1):\n * - непрендеримые теги (script/style/link/meta/noscript/template/head/title/base) —\n * никогда не отрисовываются → пропуск поддерева;\n * - display: none → элемент и всё поддерево не отрисованы → пропуск поддерева;\n * - visibility: hidden → сам элемент невидим, но дети могут быть видимы\n * (visibility: visible) → элемент не включается, поддерево обходится;\n * - нулевой прямоугольник (width <= 0 || height <= 0) → нет видимого бокса, но дети\n * (например, overflow) могут быть видимы → элемент не включается, поддерево обходится.\n *\n * indexPath — индексы среди ЭЛЕМЕНТНЫХ детей на каждом уровне, вычисляются по\n * структуре DOM ДО фильтрации: позиция стабильна независимо от фильтров — основа\n * детерминированных ID VSL (решение note_1789916091535: без Date.now()/Math.random(),\n * база для diffing в M1.2).\n */\nimport { getGlobalFilter } from '../security/promptInjectionFilter';\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface ExtractedElement {\n /** Имя тега в нижнем регистре (например, 'button'). */\n tag: string;\n /** Путь от корня обхода: индексы среди элементных детей каждого уровня (до фильтрации). */\n indexPath: number[];\n /** Координаты и размеры из getBoundingClientRect (px, viewport). */\n rect: Rect;\n /** Собственный текст (только прямые текстовые узлы), нормализованный; null если пуст. */\n text: string | null;\n /** Все атрибуты элемента (verbatim). */\n attributes: Record<string, string>;\n /** CSS-подмножество для Level 3 (computed styles в момент extraction, T1.5.1). */\n css?: ElementCss;\n /** Видимые дочерние элементы. */\n children: ExtractedElement[];\n}\n\n/**\n * CSS-подмножество для Segmentation Level 3 (T1.5.1): computed styles,\n * захваченные в момент extraction (viewOf/getComputedStyle). Все поля\n * optional: отсутствие значения или поддержки свойства средой → поле\n * просто не задано.\n */\nexport interface ElementCss {\n cursor?: string;\n position?: string;\n top?: string;\n bottom?: string;\n display?: string;\n gap?: string;\n fontWeight?: string;\n fontSize?: string;\n opacity?: string;\n pointerEvents?: string;\n overflow?: string;\n height?: string;\n /** Background color (computed) — для sty.bg в VslObject. */\n backgroundColor?: string;\n /** Text/foreground color (computed) — для sty.fg в VslObject. */\n color?: string;\n /** Border shorthand (computed) — для sty.border в VslObject. */\n border?: string;\n /** Border radius (computed) — для sty.radius в VslObject. */\n borderRadius?: string;\n /** Box shadow (computed) — для sty.shadow в VslObject. */\n boxShadow?: string;\n /** Font family (computed) — для sty.font.family в VslObject. */\n fontFamily?: string;\n}\n\n/** Теги, которые никогда не отрисовываются визуально. */\nconst NON_RENDERABLE_TAGS = new Set([\n 'script',\n 'style',\n 'link',\n 'meta',\n 'noscript',\n 'template',\n 'head',\n 'title',\n 'base',\n]);\n\n/**\n * Окно (контекст стилей) элемента: ownerDocument.defaultView — портативно в\n * браузере, jsdom и любом DOM-окружении без глобального window (фикс\n * packaging-smoke demo:cache-diff: ReferenceError: window is not defined).\n */\nfunction viewOf(el: Element): Window | null {\n return el.ownerDocument.defaultView;\n}\n\nfunction isDisplayNone(el: Element): boolean {\n return viewOf(el)?.getComputedStyle(el).display === 'none';\n}\n\nfunction isVisibilityHidden(el: Element): boolean {\n return viewOf(el)?.getComputedStyle(el).visibility === 'hidden';\n}\n\n/** nodeType текстового узла по DOM-спецификации (3) — без зависимости от глобального Node. */\nconst TEXT_NODE_TYPE = 3;\n\n/** Собственный текст элемента: только прямые текстовые узлы, whitespace-нормализация. */\nexport function ownText(el: Element): string {\n let raw = '';\n for (const node of Array.from(el.childNodes)) {\n if (node.nodeType === TEXT_NODE_TYPE) {\n raw += node.textContent ?? '';\n }\n }\n return raw.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * Извлекает текст элемента и применяет Prompt Injection Filter (T1.8.5, M1.8).\n * Внутренняя функция — обёртка над ownText() с фильтрацией.\n */\nfunction filteredOwnText(el: Element, url?: string): string {\n const raw = ownText(el);\n if (!raw) return raw;\n\n const filter = getGlobalFilter();\n if (!filter.isEnabled()) {\n return raw;\n }\n\n const result = filter.scan(raw, url);\n return result.cleanText;\n}\n\nfunction extractAttributes(el: Element): Record<string, string> {\n const attributes: Record<string, string> = {};\n for (const attr of Array.from(el.attributes)) {\n attributes[attr.name] = attr.value;\n }\n return attributes;\n}\n\n/** CSS-свойства для ElementCss (уровень 3): camelCase-ключ → kebab-case-свойство. */\nconst CSS_PROPERTIES: ReadonlyArray<[keyof ElementCss, string]> = [\n ['cursor', 'cursor'],\n ['position', 'position'],\n ['top', 'top'],\n ['bottom', 'bottom'],\n ['display', 'display'],\n ['gap', 'gap'],\n ['fontWeight', 'font-weight'],\n ['fontSize', 'font-size'],\n ['opacity', 'opacity'],\n ['pointerEvents', 'pointer-events'],\n ['overflow', 'overflow'],\n ['height', 'height'],\n ['backgroundColor', 'background-color'],\n ['color', 'color'],\n ['border', 'border'],\n ['borderRadius', 'border-radius'],\n ['boxShadow', 'box-shadow'],\n ['fontFamily', 'font-family'],\n];\n\n/**\n * Захватывает CSS-подмножество элемента (computed styles) для Level 3 (T1.5.1).\n * Пустое значение ('' — свойство не поддержано средой) → undefined, чтобы\n * классификация не принимала отсутствие поддержки за значение.\n */\nfunction captureCss(el: Element): ElementCss {\n const view = viewOf(el);\n if (view === null) return {};\n const style = view.getComputedStyle(el);\n const css: ElementCss = {};\n for (const [key, property] of CSS_PROPERTIES) {\n const value = style.getPropertyValue(property);\n if (value !== '') css[key] = value;\n }\n return css;\n}\n\nfunction toRect(rect: DOMRect): Rect {\n return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };\n}\n\nfunction hasZeroBox(rect: Rect): boolean {\n return rect.width <= 0 || rect.height <= 0;\n}\n\nfunction collectVisibleChildren(parent: Element, parentPath: readonly number[], url?: string): ExtractedElement[] {\n const result: ExtractedElement[] = [];\n Array.from(parent.children).forEach((child, index) => {\n const path = [...parentPath, index];\n const tag = child.tagName.toLowerCase();\n\n // Никогда не отрисовывается / display:none — пропускаем всё поддерево.\n if (NON_RENDERABLE_TAGS.has(tag) || isDisplayNone(child)) {\n return;\n }\n\n const rect = toRect(child.getBoundingClientRect());\n\n // Сам элемент без видимого бокса, но дети могут быть видимы —\n // не включаем его, поддерево обходим.\n if (isVisibilityHidden(child) || hasZeroBox(rect)) {\n result.push(...collectVisibleChildren(child, path, url));\n return;\n }\n\n result.push({\n tag,\n indexPath: path,\n rect,\n text: filteredOwnText(child, url) || null,\n attributes: extractAttributes(child),\n css: captureCss(child),\n children: collectVisibleChildren(child, path, url),\n });\n });\n return result;\n}\n\nfunction defaultRoot(): Element {\n const body = document.body;\n if (!body) {\n throw new Error('extractDomTree: document.body is null — call after the DOM is loaded');\n }\n return body;\n}\n\n/**\n * Извлекает видимое поддерево DOM, начиная с root (по умолчанию document.body).\n * Сам root не включается — возвращается лес его видимых потомков.\n *\n * @param root — корневой элемент для обхода (по умолчанию document.body)\n * @param url — URL страницы (для Prompt Injection Filter whitelist, T1.8.5)\n */\nexport function extractDomTree(root: Element = defaultRoot(), url?: string): ExtractedElement[] {\n return collectVisibleChildren(root, [], url);\n}","/**\n * Segmentation Level 1: семантические теги (T1.1.3, ROADMAP.md M1.1).\n *\n * Контракт — таблица «Уровень 1: Семантические теги» из ARCHITECTURE.md:\n * ровно 10 тегов, покрывающих ~40% элементов типичной страницы. Теги вне\n * таблицы (div/span/p/footer/…) НЕ классифицируются на этом уровне —\n * их семантика, если есть, добавляется Level 2 (ARIA) в T1.1.4.\n *\n * Функция чистая и детерминированная: (тег) → тип VSL | null.\n */\n\nimport type { VslType } from '../types/vsl';\n\n/** Таблица маппинга HTML-тегов (нижний регистр) → семантические типы VSL. */\nexport const LEVEL1_TAG_MAP: Readonly<Record<string, VslType>> = {\n button: 'button',\n input: 'input',\n a: 'link',\n nav: 'nav',\n header: 'header',\n main: 'main',\n section: 'container',\n img: 'image',\n select: 'select',\n textarea: 'textarea',\n};\n\n/**\n * Возвращает семантический тип VSL для HTML-тега по таблице Level 1\n * или null, если тег на этом уровне не классифицируется.\n * Регистр тега не учитывается (защита от tagName в любом регистре).\n */\nexport function resolveLevel1Type(tag: string): VslType | null {\n return LEVEL1_TAG_MAP[tag.toLowerCase()] ?? null;\n}","/**\n * Segmentation Level 2: ARIA-атрибуты (T1.1.4, ROADMAP.md M1.1).\n *\n * Контракт — таблица «Уровень 2: ARIA-атрибуты» из ARCHITECTURE.md:\n * - role → тип VSL (button / dialog→modal / tab / tabpanel→container);\n * - aria-label → txt (доступное имя элемента);\n * - aria-pressed / aria-expanded / aria-disabled = \"true\" → st\n * (checked / expanded / disabled);\n * - aria-hidden=\"true\" → элемент декоративный (обрезается в segmenter.ts).\n *\n * L2 дополняет L1: тип из ARIA-роли применяется только если тег не дал типа.\n */\n\nimport type { VslState, VslType } from '../types/vsl';\n\n/** Таблица маппинга ARIA-ролей (нижний регистр) → типы VSL. */\nexport const ARIA_ROLE_TYPE_MAP: Readonly<Record<string, VslType>> = {\n button: 'button',\n dialog: 'modal',\n tab: 'tab',\n tabpanel: 'container',\n combobox: 'input',\n listbox: 'select',\n searchbox: 'input',\n spinbutton: 'input',\n slider: 'input',\n};\n\n/**\n * Возвращает тип VSL по ARIA-роли или null, если роль не из контракта\n * (или роли нет). Регистр не учитывается.\n */\nexport function resolveAriaRoleType(role: string | undefined): VslType | null {\n if (!role) return null;\n return ARIA_ROLE_TYPE_MAP[role.toLowerCase()] ?? null;\n}\n\n/**\n * Определяет dropdown toggle по атрибуту aria-haspopup.\n * Возвращает 'dropdown_toggle' для aria-haspopup='menu' или 'listbox',\n * иначе null. Используется для кнопок, открывающих dropdown меню.\n */\nexport function resolveDropdownToggle(attributes: Record<string, string>): VslType | null {\n const hasPopup = attributes['aria-haspopup'];\n if (hasPopup === 'menu' || hasPopup === 'listbox') {\n return 'dropdown_toggle';\n }\n return null;\n}\n\nconst TRUE = 'true';\n\n/**\n * Состояние VSL из ARIA-атрибутов. По контракту маппируется только точное\n * значение \"true\" (\"false\"/\"mixed\"/прочее → null). При нескольких состояниях\n * приоритет — порядок таблицы контракта: checked → expanded → disabled.\n */\nexport function resolveSt(attributes: Record<string, string>): VslState | null {\n if (attributes['aria-pressed'] === TRUE) return 'checked';\n if (attributes['aria-expanded'] === TRUE) return 'expanded';\n if (attributes['aria-disabled'] === TRUE) return 'disabled';\n return null;\n}","/**\n * Segmentation Level 3: CSS-анализ (T1.5.1, ROADMAP.md M1.5).\n *\n * Контракт — таблица «Уровень 3: CSS-анализ» из ARCHITECTURE.md:L145-156:\n * ровно 8 паттернов над CSS-подмножеством, захваченным domExtractor\n * (ElementCss: computed styles через viewOf/getComputedStyle):\n *\n * | CSS-паттерн | → Тип |\n * |--------------------------------------|------------------------|\n * | cursor: pointer + onclick | button |\n * | display: flex + gap > 0 | container |\n * | font-weight: bold + font-size > 20px | heading |\n * | position: fixed + top: 0 | header |\n * | position: fixed + bottom: 0 | footer |\n * | overflow: hidden + height > 200px | scrollable_container |\n * | display: none / visibility: hidden | пропустить (domExtractor) |\n * | opacity: 0 + pointer-events: none | пропустить (isPointerInvisible) |\n *\n * Функции чистые и детерминированные. Порядок проверки = порядку таблицы\n * (первое совпадение выигрывает). Итоговый приоритет классификации:\n * L1 > L2 > L3 (интеграция в segmenter — dev_5, T1.5.2).\n */\n\nimport type { ElementCss } from '../capture/domExtractor';\nimport type { VslType } from '../types/vsl';\n\n/** Числовое значение CSS-свойства ('24px' → 24; 'auto'/'normal'/'' → NaN). */\nfunction px(value: string | undefined): number {\n return value === undefined ? Number.NaN : Number.parseFloat(value);\n}\n\n/**\n * Возвращает тип VSL по CSS-паттерну (уровень 3) или null, если ни один из\n * 6 классифицирующих паттернов не совпал (skip-паттерны отдельно: см.\n * isPointerInvisible; display:none/visibility:hidden фильтруются в domExtractor).\n *\n * @param css захваченное CSS-подмножество (undefined — захват не производился)\n * @param attributes атрибуты элемента (для onclick в паттерне button)\n */\nexport function resolveLevel3Type(\n css: ElementCss | undefined,\n attributes: Readonly<Record<string, string>>,\n): VslType | null {\n if (css === undefined) return null;\n\n // 1. cursor: pointer + onclick → button (интерактивный элемент).\n if (css.cursor === 'pointer' && attributes['onclick'] !== undefined) return 'button';\n\n // 2. display: flex + gap > 0 → container (контейнер с layout).\n if (css.display === 'flex' && px(css.gap) > 0) return 'container';\n\n // 3. font-weight: bold + font-size > 20px → heading (заголовок).\n // Computed styles отдают жирность числом ('700'), inline — словом ('bold').\n const isBold = css.fontWeight === 'bold' || px(css.fontWeight) >= 700;\n if (isBold && px(css.fontSize) > 20) return 'heading';\n\n // 4. position: fixed + top: 0 → header (фиксированный header).\n if (css.position === 'fixed' && px(css.top) === 0) return 'header';\n\n // 5. position: fixed + bottom: 0 → footer (фиксированный footer).\n if (css.position === 'fixed' && px(css.bottom) === 0) return 'footer';\n\n // 6. overflow: hidden + height > 200px → scrollable_container.\n if (css.overflow === 'hidden' && px(css.height) > 200) return 'scrollable_container';\n\n return null;\n}\n\n/**\n * Элемент невидим для взаимодействия (skip-паттерн таблицы уровня 3):\n * opacity: 0 + pointer-events: none → пропускается вместе с поддеревом\n * (применяется в segmenter, аналог aria-hidden — dev_5).\n *\n * display: none / visibility: hidden — другие skip-паттерны таблицы,\n * отфильтровываются раньше, в domExtractor (isDisplayNone/isVisibilityHidden).\n */\nexport function isPointerInvisible(css: ElementCss | undefined): boolean {\n if (css === undefined) return false;\n return px(css.opacity) === 0 && css.pointerEvents === 'none';\n}","/**\n * Segmentation Level 4: структурный анализ (T1.5.2, ROADMAP.md M1.5).\n *\n * Контракт — таблица «Уровень 4: Структурный анализ» из ARCHITECTURE.md:L158-172:\n * паттерны определяются по ГЕОМЕТРИИ ДЕТЕЙ уже аннотированного элемента\n * (L1/L2/L3 применены раньше — итоговый приоритет L1>L2>L3>L4, интеграция\n * в segmenter — следующий шаг; rect захвачен domExtractor):\n *\n * | Паттерн | Условие | → Тип |\n * |---------------------------|--------------------------------------------------|-------------|\n * | Горизонтальный ряд кнопок | ≥3 элемента с t: button в ряд, одинаковая высота | toolbar |\n * | Вертикальный список | ≥3 элемента с одинаковой шириной, вертикально | list |\n * | Сетка карточек | Элементы в строках и столбцах, одинаковый размер | grid |\n * | Label + input рядом | text элемент рядом с input элемент | form_field |\n * | Tab bar | Горизонтальный ряд элементов с role=\"tab\" | tab_bar |\n * | Sidebar + main | Вертикальный split: узкий слева + широкий справа | layout |\n *\n * Функция чистая и детерминированная: (элемент + дети) → тип VSL | null.\n * Порядок проверки = порядку таблицы (первое совпадение выигрывает).\n * Без импорта segmenter — нет циклической зависимости (SegmentedElement\n * структурно совместим с Level4Input: rect/ch/t/attributes/txt).\n */\n\nimport type { Rect } from '../capture/domExtractor';\nimport type { VslType } from '../types/vsl';\n\n/** Допуск сравнения геометрии (px): суб-пиксельный рендеринг и округления. */\nconst EPS = 2;\n\n/** Ребёнок для L4-анализа: уже аннотирован L1/L2/L3 (t может отсутствовать). */\nexport interface Level4Child {\n rect: Rect;\n t?: VslType | null;\n attributes?: Readonly<Record<string, string>>;\n txt?: string | null;\n}\n\n/** Вход L4: элемент-кандидат (контейнер группы) с его детьми. */\nexport interface Level4Input {\n rect: Rect;\n ch?: readonly Level4Child[];\n}\n\nconst bottomOf = (r: Rect): number => r.y + r.height;\nconst rightOf = (r: Rect): number => r.x + r.width;\n\n/** В одном горизонтальном ряду: перекрытие по Y > половины меньшей высоты. */\nfunction sameRow(a: Rect, b: Rect): boolean {\n const overlap = Math.min(bottomOf(a), bottomOf(b)) - Math.max(a.y, b.y);\n return overlap > 0.5 * Math.min(a.height, b.height);\n}\n\n/** В одной вертикальной колонке: перекрытие по X > половины меньшей ширины. */\nfunction sameColumn(a: Rect, b: Rect): boolean {\n const overlap = Math.min(rightOf(a), rightOf(b)) - Math.max(a.x, b.x);\n return overlap > 0.5 * Math.min(a.width, b.width);\n}\n\nconst eq = (a: number, b: number): boolean => Math.abs(a - b) <= EPS;\nconst sameHeight = (a: Rect, b: Rect): boolean => eq(a.height, b.height);\nconst sameWidth = (a: Rect, b: Rect): boolean => eq(a.width, b.width);\n\n/**\n * toolbar: ≥3 кнопок (t: button) в одном ряду одинаковой высоты.\n * Упрощение контракта: все кнопки-кандидаты в одном ряду — многострочные\n * панели кнопок не классифицируются как toolbar (их ловит grid при равных\n * размерах).\n */\nfunction isToolbarRow(children: readonly Level4Child[]): boolean {\n const buttons = children.filter((c) => c.t === 'button');\n const first = buttons[0];\n if (buttons.length < 3 || first === undefined) return false;\n return buttons\n .slice(1)\n .every((b) => sameRow(first.rect, b.rect) && sameHeight(first.rect, b.rect));\n}\n\n/** Дети выстроены вертикально подряд: соседние по Y не перекрываются. */\nfunction verticallyStacked(children: readonly Level4Child[]): boolean {\n const sorted = [...children].sort((a, b) => a.rect.y - b.rect.y);\n for (let i = 1; i <sorted.length; i += 1) {\n if (sorted[i]!.rect.y <bottomOf(sorted[i - 1]!.rect) - EPS) return false;\n }\n return true;\n}\n\n/** list: ≥3 детей одинаковой ширины, вертикально (в колонке, без наложений по Y). */\nfunction isVerticalList(children: readonly Level4Child[]): boolean {\n const first = children[0];\n if (children.length < 3 || first === undefined) return false;\n return (\n children\n .slice(1)\n .every((c) => sameWidth(first.rect, c.rect) && sameColumn(first.rect, c.rect)) &&\n verticallyStacked(children)\n );\n}\n\n/** Количество кластеров значений с допуском eps (полосы строк/столбцов сетки). */\nfunction bandCount(values: readonly number[], eps: number): number {\n const sorted = [...values].sort((a, b) => a - b);\n let count = 1;\n let anchor = sorted[0]!;\n for (const value of sorted.slice(1)) {\n if (value - anchor > eps) {\n count += 1;\n anchor = value;\n }\n }\n return count;\n}\n\n/** grid: ≥4 детей одного размера, минимум 2 строки и 2 столбца. */\nfunction isGrid(children: readonly Level4Child[]): boolean {\n const first = children[0];\n if (children.length < 4 || first === undefined) return false;\n const sameSize = children\n .slice(1)\n .every((c) => sameWidth(first.rect, c.rect) && sameHeight(first.rect, c.rect));\n if (!sameSize) return false;\n return (\n bandCount(children.map((c) => c.rect.y), first.rect.height + EPS) >= 2 &&\n bandCount(children.map((c) => c.rect.x), first.rect.width + EPS) >= 2\n );\n}\n\n/** Соседство: общий ряд/колонка с зазором не более 16px (или перекрытием). */\nfunction adjacent(a: Rect, b: Rect): boolean {\n const gapX = Math.max(a.x, b.x) - Math.min(rightOf(a), rightOf(b));\n const gapY = Math.max(a.y, b.y) - Math.min(bottomOf(a), bottomOf(b));\n return (sameRow(a, b) && gapX <= 8 * EPS) || (sameColumn(a, b) && gapY <= 8 * EPS);\n}\n\n/** form_field: text-элемент (label с txt) рядом с input-элементом. */\nfunction hasLabelNextToInput(children: readonly Level4Child[]): boolean {\n return children.some((input) => {\n if (input.t !== 'input') return false;\n return children.some((label) => {\n if (label === input) return false;\n if ((label.txt ?? '').length === 0) return false;\n return adjacent(label.rect, input.rect);\n });\n });\n}\n\n/** tab_bar: ≥2 элементов с role=\"tab\" в одном горизонтальном ряду. */\nfunction isTabBarRow(children: readonly Level4Child[]): boolean {\n const tabs = children.filter((c) => c.attributes?.['role'] === 'tab');\n const first = tabs[0];\n if (tabs.length < 2 || first === undefined) return false;\n return tabs.slice(1).every((t) => sameRow(first.rect, t.rect));\n}\n\n/** layout: ровно 2 ребёнка — узкий слева + широкий справа (вертикальный split). */\nfunction isSidebarSplit(children: readonly Level4Child[], parent: Rect): boolean {\n if (children.length !== 2) return false;\n const sorted = [...children].sort((a, b) => a.rect.x - b.rect.x);\n const left = sorted[0];\n const right = sorted[1];\n if (left === undefined || right === undefined) return false;\n return (\n left.rect.width <parent.width / 3 &&\n right.rect.width > parent.width / 2 &&\n rightOf(left.rect) <= right.rect.x + EPS &&\n sameRow(left.rect, right.rect)\n );\n}\n\n/**\n * Возвращает тип VSL по структурному паттерну (уровень 4) или null, если ни\n * один из 6 паттернов не совпал. Вызывается в segmenter ПОСЛЕ L1/L2/L3\n * (приоритет L1>L2>L3>L4): аннотирует контейнер группы по геометрии детей.\n *\n * @param el элемент-кандидат: rect + уже аннотированные дети (ch)\n */\nexport function resolveLevel4Type(el: Level4Input): VslType | null {\n const children = el.ch ?? [];\n if (children.length === 0) return null;\n if (isToolbarRow(children)) return 'toolbar';\n if (isVerticalList(children)) return 'list';\n if (isGrid(children)) return 'grid';\n if (hasLabelNextToInput(children)) return 'form_field';\n if (isTabBarRow(children)) return 'tab_bar';\n if (isSidebarSplit(children, el.rect)) return 'layout';\n return null;\n}","/**\n * Segmentation Engine: объединение уровней 1-4 (T1.1.3/T1.1.4/T1.5.1/T1.5.2).\n *\n * Обходит ExtractedElement-дерево (domExtractor) и аннотирует каждый элемент:\n * - t: приоритет L1 (тег) → L2 (ARIA-роль) → L3 (CSS-паттерны) → L4\n * (структурные паттерны по геометрии уже аннотированных детей); null —\n * не классифицирован (включение и типизация контейнеров — решение\n * VSL Builder, T1.1.5);\n * - txt: aria-label (приоритет по семантике доступного имени) ?? собственный текст;\n * - st: состояние из aria-pressed/expanded/disabled (\"true\" only).\n *\n * Skip-поддеревья (элемент не попадает в VSL вместе с поддеревом):\n * - aria-hidden=\"true\" — декоративный по семантике accessibility-дерева;\n * - opacity:0 + pointer-events:none — визуально невидимый (L3,\n * isPointerInvisible).\n *\n * L4 применяется после рекурсивной обработки детей: структурные паттерны\n * (toolbar/list/grid/form_field/tab_bar/layout) анализируют t детей.\n */\n\nimport type { ElementCss, ExtractedElement, Rect } from '../capture/domExtractor';\nimport type { VslFragmentMeta, VslState, VslType } from '../types/vsl';\nimport { resolveLevel1Type } from './level1';\nimport { resolveAriaRoleType, resolveDropdownToggle, resolveSt } from './level2';\nimport { isPointerInvisible, resolveLevel3Type } from './level3';\nimport { resolveLevel4Type } from './level4';\n\nexport interface SegmentedElement {\n tag: string;\n indexPath: number[];\n rect: Rect;\n /** Атрибуты verbatim (нужны Builder для умных дефолтов act и валидации). */\n attributes: Record<string, string>;\n /** Тип VSL: приоритет L1 > L2 > L3 > L4; null — не классифицирован. */\n t: VslType | null;\n /** Текст: aria-label ?? собственный текст; null, если нет ни того, ни другого. */\n txt: string | null;\n /** Состояние из ARIA (checked/expanded/disabled) или null. */\n st: VslState | null;\n /** CSS-подмножество для визуальных стилей (передаётся в Builder для sty). */\n css?: ElementCss;\n ch: SegmentedElement[];\n /** Ссылка на визуальный фрагмент (заполняется enrichWithVision, T1.5.4). */\n vf?: string;\n /** Метаданные фрагмента (§4.4). */\n vf_meta?: VslFragmentMeta;\n}\n\n/** Элемент декоративен (aria-hidden=\"true\") → в VSL не включается. */\nexport function isAriaHidden(attributes: Record<string, string>): boolean {\n return attributes['aria-hidden'] === 'true';\n}\n\nfunction segmentOne(el: ExtractedElement): SegmentedElement {\n const { attributes } = el;\n // Специальная проверка для input[type=file] → file_input\n // (не ломает контракт resolveLevel1Type, который принимает только tag)\n const isFileInput = el.tag === 'input' && attributes['type'] === 'file';\n return {\n tag: el.tag,\n indexPath: el.indexPath,\n rect: el.rect,\n attributes,\n t: isFileInput\n ? 'file_input'\n : (resolveDropdownToggle(attributes) ??\n resolveLevel1Type(el.tag) ??\n resolveAriaRoleType(attributes['role']) ??\n resolveLevel3Type(el.css, attributes)),\n txt: attributes['aria-label'] ?? el.text,\n st: resolveSt(attributes),\n css: el.css,\n ch: [],\n };\n}\n\n/**\n * Аннотирует видимое дерево (результат extractDomTree) семантикой L1-L4,\n * обрезая skip-поддеревья (aria-hidden, isPointerInvisible). Порядок\n * элементов сохраняется. L4 анализирует геометрию детей ПОСЛЕ их\n * аннотирования (post-order по t).\n */\nexport function segmentTree(elements: readonly ExtractedElement[]): SegmentedElement[] {\n const result: SegmentedElement[] = [];\n for (const el of elements) {\n if (isAriaHidden(el.attributes)) continue;\n if (isPointerInvisible(el.css)) continue;\n const segmented = segmentOne(el);\n segmented.ch = segmentTree(el.children);\n if (segmented.t === null) {\n segmented.t = resolveLevel4Type(segmented);\n }\n result.push(segmented);\n }\n return result;\n}","/**\n * Типы VSL JSON (DESIGN_SYSTEM.md §4, ARCHITECTURE.md §2.3).\n */\n\n/**\n * Семантический тип объекта VSL (поле `t`): L1-теги + L2-роли (T1.1.3/T1.1.4),\n * L3-CSS (T1.5.1), L4-структурные (T1.5.2), L5-vision (T1.5.4).\n */\nexport type VslType =\n | 'button'\n | 'input'\n | 'file_input'\n | 'link'\n | 'nav'\n | 'header'\n | 'main'\n | 'container'\n | 'image'\n | 'select'\n | 'textarea'\n | 'modal'\n | 'tab'\n | 'dropdown_toggle'\n // Level 3 — CSS-анализ (§2.2.1):\n | 'heading'\n | 'footer'\n | 'scrollable_container'\n // Level 4 — структурный анализ (§2.2.1):\n | 'toolbar'\n | 'list'\n | 'grid'\n | 'form_field'\n | 'tab_bar'\n | 'layout'\n // Level 5 — vision fallback (§2.2.2):\n | 'icon'\n | 'chart'\n | 'custom_widget'\n | 'unknown';\n\n/** Состояние объекта VSL (поле `st`) — из ARIA-атрибутов (T1.1.4). */\nexport type VslState = 'checked' | 'expanded' | 'disabled';\n\n/** Тип визуального фрагмента — выход классификации vision (§2.2.2 Шаг 2). */\nexport type VisualFragmentType = 'image' | 'icon' | 'chart' | 'custom_widget' | 'unknown';\n\n/** Метаданные visual fragment (DESIGN_SYSTEM.md §4.4). */\nexport interface VslFragmentMeta {\n type?: string;\n format?: string;\n size?: [number, number];\n hash?: string;\n cached_at?: string;\n}\n\n/**\n * Визуальные стили объекта VSL (DESIGN_SYSTEM.md §4.3).\n * Optional: только для визуально значимых элементов.\n * Все значения — computed styles в момент snapshot.\n */\nexport interface VslStyle {\n /** Background color (computed). */\n bg?: string;\n /** Text/foreground color (computed). */\n fg?: string;\n /** Border shorthand (computed). */\n border?: string;\n /** Border radius (computed). */\n radius?: string;\n /** Box shadow (computed). */\n shadow?: string;\n /** Font properties. */\n font?: {\n family?: string;\n size?: string;\n weight?: string;\n };\n}\n\n/**\n * Запись реестра visual_fragments документа (§2.2.2, ARCHITECTURE.md:L265-284).\n * ТОЛЬКО метаданные: base64-данные изображений живут в VisualFragmentStore\n * (src/llm/types.ts, DEC-015) и подаются по запросу (lazy loading §4.4) —\n * документ остаётся в бюджете 10-100 KB (ARCHITECTURE.md:L16-18).\n */\nexport interface VisualFragment {\n /** Тип фрагмента (§2.2.2 Шаг 2). */\n type: VisualFragmentType;\n /** Формат закодированных данных (например, 'webp'). */\n format: string;\n /** Размер [ширина, высота] в px. */\n size: [number, number];\n /** Хэш кэша hash(bounding_box + pixel_content) — ключ инвалидации (§5.2). */\n hash?: string;\n /** ISO 8601 — время кэширования. */\n cached_at?: string;\n /** Эмбеддинг (512-dim CLIP) — контракт §2.2.2; в M1.5 не заполняется. */\n embedding?: number[];\n}\n\n/** Версия формата VSL (DESIGN_SYSTEM.md §4.1). */\nexport const VSL_VERSION = '1.0.0' as const;\n\nexport interface VslViewport {\n width: number;\n height: number;\n unit: 'px';\n}\n\nexport interface VslCanvas {\n viewport: VslViewport;\n background: string;\n scale: number;\n orientation: 'landscape' | 'portrait';\n /** ISO 8601; для детерминизма тестов фиксируется через BuildOptions. */\n timestamp: string;\n url?: string;\n title?: string;\n}\n\nexport interface VslObject {\n /** Детерминированный ID из DOM-пути (note_1789916091535, база M1.2 diff). */\n id: string;\n t: VslType;\n /** Роль (атрибут role), уточняет семантику. */\n r?: string;\n /** Позиция [x, y] — относительные координаты 0.0–1.0 от viewport. */\n p: [number, number];\n /** Размер [width, height] в px. */\n s: [number, number];\n st?: VslState;\n txt?: string;\n /** Preview длинного текста (первые ~50 символов) — lazy text loading (M1.7, DEC-026). */\n txt_preview?: string;\n /** Ссылка на полный текст в text_blocks документа — lazy text loading (M1.7, DEC-026). */\n txt_ref?: string;\n act?: string[];\n ch?: VslObject[];\n /** Ссылка на визуальный фрагмент (§2.2.2) — ключ в visual_fragments документа. */\n vf?: string;\n /** Метаданные фрагмента (§4.4). */\n vf_meta?: VslFragmentMeta;\n /** Визуальные стили (DESIGN_SYSTEM.md §4.3). Optional: только для визуально значимых элементов. */\n sty?: VslStyle;\n /** Accept attribute для file_input (например, '.pdf,.docx'). */\n accept?: string;\n /** Multiple attribute для file_input — разрешена загрузка нескольких файлов. */\n multiple?: boolean;\n /** Атрибут aria-haspopup — указывает на dropdown/menu (для LLM). */\n hasPopup?: string;\n}\n\nexport interface VslDocument {\n vsl_version: string;\n canvas: VslCanvas;\n objects: VslObject[];\n /**\n * Реестр визуальных фрагментов (§2.2.2): ключ — vf-ссылка объекта\n * (инвариант: каждый vf объекта — ключ этой map). Только метаданные:\n * base64-данные изображений живут в VisualFragmentStore (src/llm/types.ts,\n * DEC-015) и подаются по запросу (lazy loading §4.4) — документ остаётся\n * в бюджете 10-100 KB (ARCHITECTURE.md:L16-18). Optional: документы\n * без фрагментов валидны (обратно-совместимость).\n */\n visual_fragments?: Record<string, VisualFragment>;\n /**\n * Кэш полных текстов для lazy text loading (M1.7, DEC-026): ключ — txt_ref\n * объекта (формат tb_xxx, инкрементный счётчик). Значение — полный текст.\n * Только для элементов с txt > 200 символов. Optional: документы без\n * текстовых блоков валидны (обратно-совместимость).\n */\n text_blocks?: Record<string, string>;\n}","/**\n * VSL Builder (T1.1.5, ROADMAP.md M1.1; контракт ARCHITECTURE.md §2.3).\n *\n * Вход: семантическое дерево от Segmentation Engine (segmentTree).\n * Выход: VSL JSON по DESIGN_SYSTEM.md §4 (vsl_version, canvas, objects tree).\n *\n * Отбор элементов — эвристика Semantic Density (ARCHITECTURE.md) в адаптации\n * к L1/L2-сегментации:\n * - t !== null (типизирован L1/L2) → семантический объект, включается;\n * - t === null → score = role(3) + aria-label(2) + interactive-атрибуты(2) +\n * txt(1) + включённые дети(1); score >= 3 → контейнер;\n * - t === null и есть включённые потомки → контейнер-обёртка (обобщение\n * контракта «== 0 с семантическими детьми → grouping» на зазор 1–2);\n * - иначе — декоративный, пропускается вместе с поддеревом.\n *\n * Детерминизм (решение для M1.2 Cache/Diff): id = tag_indexPath из DOM-пути,\n * без Date.now()/Math.random(); timestamp/url/title фиксируются через\n * BuildOptions (в тестах — обязательно).\n */\n\nimport type { SegmentedElement } from '../segmentation/segmenter';\nimport type { ElementCss } from '../capture/domExtractor';\nimport {\n VSL_VERSION,\n type VisualFragment,\n type VisualFragmentType,\n type VslCanvas,\n type VslDocument,\n type VslObject,\n type VslState,\n type VslStyle,\n type VslType,\n} from '../types/vsl';\nimport { IdGenerator } from '../utils/idGenerator';\n\n/** Порог lazy text loading: тексты длиннее этого порога заменяются на preview + ref (DEC-026, M1.7). */\nconst LAZY_TEXT_THRESHOLD = 200;\n/** Длина preview для lazy text (DEC-026, M1.7). */\nconst LAZY_TEXT_PREVIEW_LENGTH = 50;\n\n/** Опции сборки: для детерминированных тестов фиксируйте timestamp/url/title. */\nexport interface BuildOptions {\n viewport?: { width: number; height: number };\n background?: string;\n timestamp?: string;\n url?: string;\n title?: string;\n}\n\nconst INTERACTIVE_ATTRS: readonly string[] = [\n 'onclick',\n 'onmousedown',\n 'ontouchstart',\n 'tabindex',\n 'contenteditable',\n];\n\n/** Относительная координата [0..1], округление до 2 знаков (компактность JSON, M1.7). */\nfunction relative(value: number, dimension: number): number {\n if (dimension <= 0) return 0;\n return Math.round((value / dimension) * 100) / 100;\n}\n\n/** Умные дефолты act по README_AI §4.4; disabled → взаимодействие недоступно. */\nfunction defaultActions(\n t: VslType,\n st: VslState | null,\n attributes: Record<string, string>,\n): string[] | undefined {\n if (st === 'disabled') return undefined;\n switch (t) {\n case 'button':\n case 'link':\n case 'tab':\n return ['click'];\n case 'dropdown_toggle':\n return ['click', 'expand', 'collapse'];\n case 'modal':\n return ['close'];\n case 'input': {\n const type = attributes['type'];\n return type === 'checkbox' || type === 'radio'\n ? ['click', 'check', 'uncheck']\n : ['click', 'type', 'clear'];\n }\n case 'select':\n return ['click', 'select'];\n case 'textarea':\n return ['click', 'type', 'clear'];\n case 'file_input':\n return ['click', 'upload'];\n default:\n return undefined;\n }\n}\n\n/** Score нетипизированного элемента без учёта детей (дети учтены включённостью). */\nfunction baseSemanticScore(el: SegmentedElement): number {\n let score = 0;\n if (el.attributes['role'] !== undefined) score += 3;\n if (el.attributes['aria-label'] !== undefined) score += 2;\n if (INTERACTIVE_ATTRS.some((attr) => el.attributes[attr] !== undefined)) score += 2;\n if (el.txt) score += 1;\n return score;\n}\n\n/**\n * Извлекает визуальные стили из CSS-подмножества для VslObject.\n * Возвращает undefined, если нет значимых стилей (экономия JSON).\n * Фильтрует дефолтные значения: transparent, none, 0px, rgb(0,0,0).\n */\nfunction extractVisualStyles(css?: ElementCss): VslStyle | undefined {\n if (!css) return undefined;\n\n const style: VslStyle = {};\n let hasContent = false;\n\n // Background (пропускаем transparent и rgba(0,0,0,0))\n if (\n css.backgroundColor &&\n css.backgroundColor !== 'transparent' &&\n !css.backgroundColor.includes('rgba(0, 0, 0, 0)')\n ) {\n style.bg = css.backgroundColor;\n hasContent = true;\n }\n\n // Foreground (пропускаем черный по умолчанию) — ВСЕГДА включаем, если есть\n if (css.color && css.color !== 'rgb(0, 0, 0)') {\n style.fg = css.color;\n hasContent = true;\n }\n\n // Border (пропускаем none и 0px) — ВСЕГДА включаем, если есть\n if (css.border && css.border !== 'none' && !css.border.startsWith('0px')) {\n style.border = css.border;\n hasContent = true;\n }\n\n // Border radius (пропускаем 0px) — ВСЕГДА включаем, если есть\n if (css.borderRadius && css.borderRadius !== '0px') {\n style.radius = css.borderRadius;\n hasContent = true;\n }\n\n // Box shadow (пропускаем none) — ВСЕГДА включаем, если есть\n if (css.boxShadow && css.boxShadow !== 'none') {\n style.shadow = css.boxShadow;\n hasContent = true;\n }\n\n // Font (только если есть family/size/weight) — ВСЕГДА включаем, если есть\n if (css.fontFamily || css.fontSize || css.fontWeight) {\n style.font = {};\n if (css.fontFamily) style.font.family = css.fontFamily;\n if (css.fontSize) style.font.size = css.fontSize;\n if (css.fontWeight) style.font.weight = css.fontWeight;\n hasContent = true;\n }\n\n return hasContent ? style : undefined;\n}\n\n\nfunction toVslObject(\n el: SegmentedElement,\n viewport: { width: number; height: number },\n includedChildren: VslObject[],\n textBlocks: Map<string, string>,\n counter: { value: number },\n idGenerator: IdGenerator,\n ): VslObject {\n const t: VslType = el.t ?? 'container';\n const object: VslObject = {\n id: idGenerator.generate(el.attributes['id'], t),\n t,\n p: [relative(el.rect.x, viewport.width), relative(el.rect.y, viewport.height)],\n s: [Math.round(el.rect.width), Math.round(el.rect.height)],\n };\n const role = el.attributes['role'];\n if (role !== undefined) object.r = role;\n if (el.st) object.st = el.st;\n if (el.txt) {\n if (el.txt.length > LAZY_TEXT_THRESHOLD) {\n object.txt_preview = el.txt.slice(0, LAZY_TEXT_PREVIEW_LENGTH) + '…';\n const refId = `tb_${String(counter.value++).padStart(3, '0')}`;\n object.txt_ref = refId;\n textBlocks.set(refId, el.txt);\n } else {\n object.txt = el.txt;\n }\n }\n const act = defaultActions(t, el.st, el.attributes);\n if (act !== undefined) object.act = act;\n if (includedChildren.length > 0) object.ch = includedChildren;\n if (el.vf !== undefined) object.vf = el.vf;\n if (el.vf_meta !== undefined) object.vf_meta = el.vf_meta;\n const sty = extractVisualStyles(el.css);\n if (sty !== undefined) object.sty = sty;\n // file_input: извлекаем accept и multiple атрибуты\n if (t === 'file_input') {\n if (el.attributes['accept'] !== undefined) object.accept = el.attributes['accept'];\n if (el.attributes['multiple'] !== undefined) object.multiple = true;\n }\n // dropdown_toggle: извлекаем aria-haspopup как hasPopup\n if (t === 'dropdown_toggle' && el.attributes['aria-haspopup'] !== undefined) {\n object.hasPopup = el.attributes['aria-haspopup'];\n }\n return object;\n }\n\n/** Нижняя граница обхода: сначала дети, затем решение о включении элемента. */\nfunction convert(\n el: SegmentedElement,\n viewport: { width: number; height: number },\n textBlocks: Map<string, string>,\n counter: { value: number },\n idGenerator: IdGenerator,\n ): VslObject | null {\n const includedChildren = el.ch\n .map((child) => convert(child, viewport, textBlocks, counter, idGenerator))\n .filter((obj): obj is VslObject => obj !== null);\n\n if (el.t !== null) return toVslObject(el, viewport, includedChildren, textBlocks, counter, idGenerator);\n\n const score = baseSemanticScore(el) + (includedChildren.length > 0 ? 1 : 0);\n if (score >= 3 || includedChildren.length > 0) {\n return toVslObject(el, viewport, includedChildren, textBlocks, counter, idGenerator);\n }\n return null; // декоративный — пропускается вместе с поддеревом\n }\n\n/**\n * Будет ли элемент включён в VSL (решение convert без построения объектов):\n * типизирован, или score ≥ 3, или есть включённые дети. Используется\n * enrichWithVision для отбора vision-кандидатов (спасение отбрасываемых\n * элементов, T1.5.4) — единственный источник правды об отбрасывании.\n */\nexport function isIncludedInVsl(el: SegmentedElement): boolean {\n if (el.t !== null) return true;\n const includedChildren = el.ch.filter(isIncludedInVsl).length;\n const score = baseSemanticScore(el) + (includedChildren > 0 ? 1 : 0);\n return score >= 3 || includedChildren > 0;\n}\n\nfunction buildCanvas(\n options: BuildOptions,\n viewport: { width: number; height: number },\n): VslCanvas {\n const url = options.url ?? window.location.href;\n const title = options.title ?? document.title;\n return {\n viewport: { ...viewport, unit: 'px' },\n background: options.background ?? '#ffffff',\n scale: 1,\n orientation: viewport.width >= viewport.height ? 'landscape' : 'portrait',\n timestamp: options.timestamp ?? new Date().toISOString(),\n ...(url ? { url } : {}),\n ...(title ? { title } : {}),\n };\n}\n\n/**\n * Собирает реестр visual_fragments документа (§2.2.2, AC[5]): обходит\n * построенные объекты, для каждого объекта с vf-ссылкой копирует метаданные\n * из vf_meta (заполняются enrichWithVision). Инвариант vf→map выполняется\n * по построению: запись создаётся ровно для каждой встреченной vf-ссылки;\n * дубликаты vf (одинаковые фрагменты на разных объектах) дедуплицируются\n * ключом map. Возвращает undefined, если фрагментов нет (обратно-совместимость:\n * документы без фрагментов валидны).\n */\nfunction collectVisualFragments(\n objects: readonly VslObject[],\n): Record<string, VisualFragment> | undefined {\n const map: Record<string, VisualFragment> = {};\n const visit = (list: readonly VslObject[]): void => {\n for (const object of list) {\n if (object.vf !== undefined && object.vf_meta !== undefined) {\n map[object.vf] = {\n type: object.vf_meta.type as VisualFragmentType,\n format: object.vf_meta.format ?? 'webp',\n size: object.vf_meta.size ?? [0, 0],\n hash: object.vf_meta.hash,\n cached_at: object.vf_meta.cached_at,\n };\n }\n visit(object.ch ?? []);\n }\n };\n visit(objects);\n return Object.keys(map).length > 0 ? map : undefined;\n}\n\n/**\n * Строит VSL JSON из семантического дерева (segmentTree) по DESIGN_SYSTEM §4.\n * Viewport фиксируется один раз на всю сборку — p-нормализация консистентна.\n */\nexport function buildVslDocument(\n elements: readonly SegmentedElement[],\n options: BuildOptions = {},\n ): VslDocument {\n const viewport = {\n width: options.viewport?.width ?? window.innerWidth,\n height: options.viewport?.height ?? window.innerHeight,\n };\n const textBlocks = new Map<string, string>();\n const counter = { value: 0 };\n const idGenerator = new IdGenerator();\n const objects = elements\n .map((el) => convert(el, viewport, textBlocks, counter, idGenerator))\n .filter((obj): obj is VslObject => obj !== null);\n const vslDocument: VslDocument = {\n vsl_version: VSL_VERSION,\n canvas: buildCanvas(options, viewport),\n objects,\n };\n const visualFragments = collectVisualFragments(objects);\n if (visualFragments !== undefined) vslDocument.visual_fragments = visualFragments;\n if (textBlocks.size > 0) vslDocument.text_blocks = Object.fromEntries(textBlocks);\n return vslDocument;\n }","/**\n * Cache Store (T1.2.1, ROADMAP.md M1.2; контракт ARCHITECTURE.md §2.4/§5).\n *\n * In-memory Map id → CacheEntry. Запись кэша содержит VslObject и два\n * детерминированных хэша (решение dc_4):\n * - contentHash — содержимое {t, r?, st?, txt?, act?} в порядке канона §4;\n * - coordHash — координаты {p, s}.\n * id в хэш не входит (он ключ Map), ch не входит (дети отслеживаются\n * собственными записями). Хэш — sha256 (node:crypto) поверх JSON.stringify\n * объекта с фиксированным порядком ключей.\n *\n * Invalidation (ARCHITECTURE.md §5.2, решение dc_5):\n * - invalidate(id) — точечная инвалидация записи;\n * - invalidateBySelector — MVP-семантика: '*' → полный сброс; точный id;\n * иначе селектор трактуется как тег id ('button' → все 'button_*'), т.к.\n * CSS-селекторы к кэшированным VslObject неприменимы (id = tag_indexPath);\n * - invalidateCoordinates() — «сброс координат» при viewport resize:\n * coordHash всех записей обнуляется, содержимое сохраняется.\n */\n\nimport { createHash } from 'node:crypto';\nimport type { VslObject } from '../types/vsl';\n\n/** Запись кэша: объект VSL + хэши содержимого и координат. */\nexport interface CacheEntry {\n /** Кэшированный VslObject (поддерево ch не хранится — дети в своих записях). */\n object: VslObject;\n /** sha256 от {t, r?, st?, txt?, act?} в фиксированном порядке ключей. */\n contentHash: string;\n /** sha256 от {p, s}; пустая строка — координаты инвалидированы (resize). */\n coordHash: string;\n}\n\nfunction sha256(payload: string): string {\n return createHash('sha256').update(payload).digest('hex');\n}\n\n/**\n * contentHash: только смысловые поля, фиксированный порядок t→r→st→txt→act.\n * Опциональные поля включаются только при наличии — сериализация детерминирована.\n */\nexport function computeContentHash(object: VslObject): string {\n const payload: Record<string, unknown> = { t: object.t };\n if (object.r !== undefined) payload.r = object.r;\n if (object.st !== undefined) payload.st = object.st;\n if (object.txt !== undefined) payload.txt = object.txt;\n if (object.act !== undefined) payload.act = object.act;\n return sha256(JSON.stringify(payload));\n}\n\n/** coordHash: только координаты {p, s}. */\nexport function computeCoordHash(object: VslObject): string {\n return sha256(JSON.stringify({ p: object.p, s: object.s }));\n}\n\n/** Публичный контракт Cache Store (ROADMAP T1.2.1). */\nexport interface CacheStore {\n /** Сохранить объект под явным id (ROADMAP T1.2.1: cache.set(id, element)). */\n set(id: string, object: VslObject): void;\n /** Запись по id или undefined. */\n get(id: string): CacheEntry | undefined;\n /** Наличие записи. */\n has(id: string): boolean;\n /** Точечная инвалидация (§5.2 cache.invalidate). */\n invalidate(id: string): void;\n /** MVP-семантика (решение dc_5): '*' | точный id | 'tag' → префикс 'tag_'. */\n invalidateBySelector(selector: string): void;\n /** Viewport resize (§5.2 «сброс координат»): обнулить coordHash, объекты сохранить. */\n invalidateCoordinates(): void;\n /** Полный сброс (§5.2 cache.clear). */\n clear(): void;\n /** Все id в порядке вставки. */\n keys(): string[];\n /** Число записей. */\n readonly size: number;\n}\n\nexport function createCacheStore(): CacheStore {\n const entries = new Map<string, CacheEntry>();\n\n return {\n set(id: string, object: VslObject): void {\n entries.set(id, {\n object,\n contentHash: computeContentHash(object),\n coordHash: computeCoordHash(object),\n });\n },\n\n get(id: string): CacheEntry | undefined {\n return entries.get(id);\n },\n\n has(id: string): boolean {\n return entries.has(id);\n },\n\n invalidate(id: string): void {\n entries.delete(id);\n },\n\n invalidateBySelector(selector: string): void {\n if (selector === '*') {\n entries.clear();\n return;\n }\n if (entries.has(selector)) {\n entries.delete(selector);\n return;\n }\n const prefix = `${selector}_`;\n for (const id of entries.keys()) {\n if (id.startsWith(prefix)) entries.delete(id);\n }\n },\n\n invalidateCoordinates(): void {\n for (const [id, entry] of entries) {\n entries.set(id, { ...entry, coordHash: '' });\n }\n },\n\n clear(): void {\n entries.clear();\n },\n\n keys(): string[] {\n return [...entries.keys()];\n },\n\n get size(): number {\n return entries.size;\n },\n };\n}","/**\n * Cache invalidation через MutationObserver (T1.2.2, ROADMAP.md M1.2; ARCHITECTURE.md §5.2).\n *\n * attachMutationObserver(root, store) подписывается на мутации поддерева root\n * и точечно инвалидирует записи Cache Store:\n * - childList (изменился состав детей) → родительский элемент (его поле ch);\n * - attributes → сам целевой элемент;\n * - characterData → owner-элемент текстового узла.\n *\n * Маппинг element → id воспроизводит арифметику domExtractor + vslBuilder\n * (id = tag_indexPath): подъём от элемента до root, на каждом уровне — индекс\n * среди ЭЛЕМЕНТНЫХ детей (Array.from(parent.children)); domExtractor считает\n * indexPath по живому DOM ДО фильтрации, поэтому индексы совпадают 1:1.\n * Контракт: root наблюдателя === корень extraction (document.body по умолчанию).\n *\n * Если id элемента мутации нет в store — инвалидируется ближайший предок из\n * store (подъём вверх до root). Root сам не имеет VSL-записи (extractDomTree\n * не включает корень) → childList на прямых детях root — no-op в MVP.\n *\n * Известное MVP-ограничение (соответствует дизайну фикстур dc_6: добавления/\n * удаления только в конец списков детей): вставка/удаление в середину сдвигает\n * indexPath последующих сиблингов — их id меняются; точечная инвалидация\n * «переехавших» записей вне MVP, итоговую консистентность гарантирует diff\n * по полному переизвлечению (T1.2.4).\n */\n\nimport type { CacheStore } from './cacheStore';\n\n/** Результат attachMutationObserver: наблюдатель + ручная отписка. */\nexport interface MutationObserverHandle {\n /** Живой MutationObserver (для тестов и расширенного контроля). */\n observer: MutationObserver;\n /** Отписаться от мутаций. */\n disconnect(): void;\n}\n\n/** id = tag_indexPath — в точности формула vslBuilder.toVslObject. */\nfunction elementId(el: Element, root: Element): string | null {\n const indexPath: number[] = [];\n let current: Element | null = el;\n while (current !== null && current !== root) {\n const parent: HTMLElement | null = current.parentElement;\n if (parent === null) return null; // элемент вне поддерева root\n indexPath.unshift(Array.from(parent.children).indexOf(current));\n current = parent;\n }\n if (current !== root || indexPath.length === 0) return null; // сам root\n return `${el.tagName.toLowerCase()}_${indexPath.join('_')}`;\n}\n\n/**\n * Инвалидация цели мутации: собственный id из store — удаляется; иначе\n * ближайший предок из store. Цели вне root / сам root — no-op (см. шапку).\n */\nfunction invalidateMutationTarget(node: Node, root: Element, store: CacheStore): void {\n const start = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;\n if (start === null || start === root || !root.contains(start)) return;\n\n let current: Element | null = start;\n while (current !== null && current !== root) {\n const id = elementId(current, root);\n if (id !== null && store.has(id)) {\n store.invalidate(id);\n return;\n }\n current = current.parentElement;\n }\n}\n\n/**\n * Подписывает store на мутации поддерева root (§5.2 «DOM mutation observer →\n * invalidation изменённых элементов»). Возвращает handle с disconnect().\n */\nexport function attachMutationObserver(root: Element, store: CacheStore): MutationObserverHandle {\n const observer = new MutationObserver((records) => {\n for (const record of records) {\n invalidateMutationTarget(record.target, root, store);\n }\n });\n observer.observe(root, {\n childList: true,\n attributes: true,\n characterData: true,\n subtree: true,\n });\n return { observer, disconnect: () => observer.disconnect() };\n}","/**\n * Diff Engine (T1.2.3, ROADMAP.md M1.2; формат ARCHITECTURE.md §6.2).\n *\n * diffVslDocuments(prev, next, options) сравнивает два VslDocument по ПЛОСКОМУ\n * индексу id (id = tag_indexPath глобально уникальны — база M1.1) и выдаёт\n * дифф строго в формате §6.2:\n * - added — новые объекты полными поддеревьями (tree order next);\n * - modified — id + только изменившиеся поля в порядке канона §4\n * (t→r→p→s→st→txt→act); УДАЛЁННОЕ опциональное поле передаётся явным null\n * (r/st/txt/act; решение dc_5: отсутствие в Partial амбивалентно —\n * «не изменилось» vs «удалено»); t/p/s/id никогда не null;\n * - removed — {id} в tree order prev;\n * - unchanged_refs — id неизменённых объектов (tree order next).\n *\n * Равенство объектов — по паре хэшей Cache Store (решение dc_4):\n * contentHash {t,r?,st?,txt?,act?} + coordHash {p,s}, sha256 по фиксированному\n * порядку ключей. Поле ch в сравнении НЕ участвует — diff плоский, дети\n * отслеживаются собственными id: изменение состава/содержимого детей проявляется\n * их собственными added/removed/modified записями. Canvas не диффуется —\n * сравнивается только дерево objects.\n *\n * Пустой дифф валиден: идентичные документы → все id в unchanged_refs,\n * остальные списки пусты.\n *\n * Предусловие: id уникальны в пределах каждого документа (гарантия M1.1).\n */\n\nimport { computeCoordHash, computeContentHash } from '../cache/cacheStore';\nimport type { VslDocument, VslObject, VslState, VslType } from '../types/vsl';\n\n/** Изменившийся объект: id + только изменившиеся поля (порядок канона §4). */\nexport interface VslModifiedObject {\n id: string;\n t?: VslType;\n /** null = опциональное поле r удалено в next. */\n r?: string | null;\n p?: [number, number];\n s?: [number, number];\n /** null = состояние st удалено в next. */\n st?: VslState | null;\n /** null = текст txt удалён в next. */\n txt?: string | null;\n /** null = список действий act удалён в next. */\n act?: string[] | null;\n}\n\n/** Удалённый объект: только ссылка на id (§6.2). */\nexport interface VslRemovedObject {\n id: string;\n}\n\n/** Блок changes формата §6.2. */\nexport interface VslDiffChanges {\n /** Новые объекты — полные поддеревья, tree order next. */\n added: VslObject[];\n /** Изменившиеся объекты, tree order next. */\n modified: VslModifiedObject[];\n /** Удалённые объекты, tree order prev. */\n removed: VslRemovedObject[];\n /** id неизменённых объектов, tree order next. */\n unchanged_refs: string[];\n}\n\n/** Дифф двух VslDocument (формат ARCHITECTURE.md §6.2). */\nexport interface VslDiff {\n /** Версия нового состояния (счётчик session). */\n diff_version: number;\n /** Версия базового (предыдущего) состояния. */\n base_version: number;\n /** ISO 8601. */\n timestamp: string;\n changes: VslDiffChanges;\n}\n\n/** Опции diffVslDocuments: версии заголовка обязательны (явный контракт session). */\nexport interface DiffOptions {\n diffVersion: number;\n baseVersion: number;\n /** ISO 8601; по умолчанию — текущее время (недетерминированно; в тестах фиксировать). */\n timestamp?: string;\n}\n\n/** Плоский индекс id → VslObject в tree order (обход дерева в глубину). */\nfunction flatten(objects: readonly VslObject[], map: Map<string, VslObject>): void {\n for (const object of objects) {\n map.set(object.id, object);\n flatten(object.ch ?? [], map);\n }\n}\n\n/** Равенство объектов по паре хэшей dc_4 (ch/id в сравнении не участвуют). */\nfunction objectsEqual(a: VslObject, b: VslObject): boolean {\n return (\n computeContentHash(a) === computeContentHash(b) &&\n computeCoordHash(a) === computeCoordHash(b)\n );\n}\n\nfunction tuple2Equal(\n a: [number, number] | null | undefined,\n b: [number, number] | null | undefined,\n): boolean {\n if (a === null || a === undefined) return b === null || b === undefined;\n if (b === null || b === undefined) return false;\n return a[0] === b[0] && a[1] === b[1];\n}\n\nfunction stringArrayEqual(\n a: readonly string[] | undefined,\n b: readonly string[] | undefined,\n): boolean {\n if (a === undefined || b === undefined) return a === b;\n return a.length === b.length && a.every((value, i) => value === b[i]);\n}\n\n/** Field-by-field в порядке канона §4; удалённые опциональные поля → явный null (dc_5). */\nfunction buildModified(prev: VslObject, next: VslObject): VslModifiedObject {\n const result: VslModifiedObject = { id: next.id };\n if (prev.t !== next.t) result.t = next.t;\n if (prev.r !== next.r) result.r = next.r ?? null;\n if (!tuple2Equal(prev.p, next.p)) result.p = next.p;\n if (!tuple2Equal(prev.s, next.s)) result.s = next.s;\n if (prev.st !== next.st) result.st = next.st ?? null;\n if (prev.txt !== next.txt) result.txt = next.txt ?? null;\n if (!stringArrayEqual(prev.act, next.act)) result.act = next.act ?? null;\n return result;\n}\n\n/** Сравнивает два VSL-документа и возвращает дифф формата §6.2 (см. шапку модуля). */\nexport function diffVslDocuments(\n prev: VslDocument,\n next: VslDocument,\n options: DiffOptions,\n): VslDiff {\n const prevFlat = new Map<string, VslObject>();\n const nextFlat = new Map<string, VslObject>();\n flatten(prev.objects, prevFlat);\n flatten(next.objects, nextFlat);\n\n const added: VslObject[] = [];\n const modified: VslModifiedObject[] = [];\n const unchangedRefs: string[] = [];\n\n // Tree order next (DFS): добавленные корни поддеревьев → изменённые →\n // неизменённые. Потомки добавленного корня НЕ дублируются отдельными записями\n // added — они уже перенесены внутри ch добавленного поддерева (§6.2).\n const walk = (objects: readonly VslObject[], insideAdded: boolean): void => {\n for (const nextObject of objects) {\n if (insideAdded) {\n walk(nextObject.ch ?? [], true);\n continue;\n }\n const prevObject = prevFlat.get(nextObject.id);\n if (prevObject === undefined) {\n added.push(nextObject); // корень добавленного поддерева\n walk(nextObject.ch ?? [], true);\n } else if (objectsEqual(prevObject, nextObject)) {\n unchangedRefs.push(nextObject.id);\n walk(nextObject.ch ?? [], false);\n } else {\n modified.push(buildModified(prevObject, nextObject));\n walk(nextObject.ch ?? [], false);\n }\n }\n };\n walk(next.objects, false);\n\n // Tree order prev: удалённые.\n const removed: VslRemovedObject[] = [];\n for (const id of prevFlat.keys()) {\n if (!nextFlat.has(id)) removed.push({ id });\n }\n\n return {\n diff_version: options.diffVersion,\n base_version: options.baseVersion,\n timestamp: options.timestamp ?? new Date().toISOString(),\n changes: { added, modified, removed, unchanged_refs: unchangedRefs },\n };\n}","/**\n * enrichWithVision (T1.5.4, dev_8): async-шаг конвейера ДО buildVslDocument —\n * Level 5 vision fallback (§2.2.1, ARCHITECTURE.md:L174-202).\n *\n * Отбор кандидатов: элементы, которые builder ОТБРОСИТ (t === null и\n * !isIncludedInVsl — нетипизированный, score < 3, без включённых детей).\n * Для каждого: extract (bounding box → WebP base64, кэш по hash) → classify\n * (порт VisionClassifier) → аннотация t + vf/vf_meta НА МЕСТЕ (мутирует\n * дерево, как segmentTree) — после этого builder включает элемент в VSL\n * (типизирован), а convert переносит vf/vf_meta в объект (AC[5]).\n *\n * Best-effort (vision не должен ломать снапшот): ошибки extract/classify\n * одного элемента не роняют обогащение — элемент остаётся отброшенным\n * (поведение как до vision), ошибка учитывается в failedCount.\n *\n * Кэш и AC[3]: запись FragmentCacheEntry возвращается extract по ссылке и\n * хранит classification — повторный extract того же hash даёт тот же entry,\n * поэтому классификатор вызывается один раз на фрагмент. Обход СТРОГО\n * последовательный: параллельный запуск классифицировал бы одинаковые\n * фрагменты дважды (оба extract завершились бы до первой записи classification).\n *\n * Данные изображений НЕ попадают в VslDocument (бюджет 10-100 KB) —\n * возвращаются в VisualFragmentStore (DEC-015, lazy loading §4.4) для\n * DecideInput.visualFragments.\n */\n\nimport { isIncludedInVsl } from '../builder/vslBuilder';\nimport type { VisualFragmentStore } from '../llm/types';\nimport type { SegmentedElement } from '../segmentation/segmenter';\nimport type { FragmentExtraction, FragmentExtractor } from './fragmentExtractor';\nimport type { VisionClassifier } from './types';\n\n/** Зависимости enrichWithVision (Dependency Inversion — фейки в unit-тестах). */\nexport interface EnrichWithVisionDeps {\n /** Классификатор фрагментов (LlmVisionClassifier; в тестах — фейк). */\n classifier: VisionClassifier;\n /** Извлекатель фрагментов с кэшем по hash (AC[3]). */\n extractor: FragmentExtractor;\n}\n\n/** Результат обогащения: счётчики + стор данных фрагментов (lazy, DEC-015). */\nexport interface EnrichWithVisionResult {\n /** Число спасённых элементов (аннотированы t + vf/vf_meta). */\n enrichedCount: number;\n /** Число кандидатов, пропущенных из-за ошибок extract/classify (best-effort). */\n failedCount: number;\n /** Данные фрагментов vf_id → {mediaType, data} для DecideInput.visualFragments. */\n fragments: VisualFragmentStore;\n}\n\n/**\n * Собирает vision-кандидатов обходом дерева (pre-order): элемент отбрасывается\n * builder-ом ⟺ t === null и !isIncludedInVsl (нетипизированный, score < 3,\n * без включённых детей) — ровно их спасает vision fallback.\n */\nfunction collectCandidates(elements: readonly SegmentedElement[]): SegmentedElement[] {\n const candidates: SegmentedElement[] = [];\n const visit = (element: SegmentedElement): void => {\n if (element.t === null && !isIncludedInVsl(element)) candidates.push(element);\n for (const child of element.ch) {\n visit(child);\n }\n };\n for (const element of elements) visit(element);\n return candidates;\n}\n\n/** Подсказка классификатору: контекст элемента (тег; текст, если есть). */\nfunction buildHint(element: SegmentedElement): string {\n return element.txt === null ? `tag=${element.tag}` : `tag=${element.tag}; text: ${element.txt}`;\n}\n\n/**\n * Обогащает дерево элементов vision-классификацией (Level 5 fallback).\n * МУТИРУЕТ элементы на месте (t + vf/vf_meta) — как segmentTree; после\n * обогащения buildVslDocument включает спасённые элементы в VSL.\n * Последовательный обход (см. шапку: кэш + AC[3]).\n */\nexport async function enrichWithVision(\n elements: SegmentedElement[],\n deps: EnrichWithVisionDeps,\n): Promise<EnrichWithVisionResult> {\n const fragments = new Map<string, import('../llm/types').VisualFragmentData>();\n let enrichedCount = 0;\n let failedCount = 0;\n\n for (const element of collectCandidates(elements)) {\n let extraction: FragmentExtraction | null;\n try {\n extraction = await deps.extractor.extract(element.rect);\n } catch {\n // Ошибки capture/crop не глушатся в extract (его контракт) — best-effort\n // здесь: элемент остаётся отброшенным, как до vision.\n failedCount += 1;\n continue;\n }\n if (extraction === null) continue; // пустая геометрия — не ошибка, элемент остаётся отброшенным\n\n const { entry } = extraction;\n let classification = entry.classification;\n try {\n if (classification === undefined) {\n classification = await deps.classifier.classify(entry.data, buildHint(element));\n entry.classification = classification; // sticky в кэше: повторные hash-hit без вызова модели (AC[3])\n }\n } catch {\n failedCount += 1;\n continue;\n }\n element.t = classification.type;\n element.vf = entry.vfId;\n element.vf_meta = {\n type: classification.type,\n format: entry.meta.format,\n size: entry.meta.size,\n hash: entry.meta.hash,\n cached_at: entry.meta.cached_at,\n };\n fragments.set(entry.vfId, entry.data);\n enrichedCount += 1;\n }\n\n return { enrichedCount, failedCount, fragments };\n}","/**\n * VSL Snapshot Session (T1.2.4, ROADMAP.md M1.2; ARCHITECTURE.md §2.4/§5.2/§6.3).\n *\n * Stateful-фасад над пайплайном capture→segment→build→cache→diff:\n * - первый вызов или смена URL → полный VslDocument; кэш очищается и\n * заполняется объектами документа; счётчик версий сбрасывается в 1;\n * - тот же URL → VslDiff последнего документа против нового (формат §6.2):\n * diff_version = version + 1, base_version = version;\n * - viewport изменился при том же URL → дополнительно\n * store.invalidateCoordinates() — «сброс координат» §5.2, НЕ полный сброс.\n *\n * Состояние {lastUrl, lastViewport, lastDocument, version} — в памяти\n * экземпляра; VslDocument (канон §4) версиями НЕ расширяется.\n *\n * Политика кэша — store отражает текущий документ: full → clear + заполнение;\n * diff → удалённые invalidate, добавленные (поддеревья) и изменённые set из\n * нового документа; неизменённые остаются кэшированными (после resize — с\n * обнулённым coordHash: наблюдаемый эффект invalidateCoordinates).\n *\n * input.url/viewport/timestamp/background передаются в BuildOptions ЯВНО —\n * дефолты builder (window.innerWidth/innerHeight/location.href/document.title)\n * вычислялись бы от глобалов: недетерминированны в тестах и недоступны в Node;\n * title — из input, иначе root.ownerDocument.title (портативный фолбэк — фикс\n * packaging-smoke demo:cache-diff).\n */\n\nimport { extractDomTree } from '../capture/domExtractor';\nimport { segmentTree, type SegmentedElement } from '../segmentation/segmenter';\nimport { buildVslDocument } from '../builder/vslBuilder';\nimport type { BuildOptions } from '../builder/vslBuilder';\nimport { createCacheStore } from '../cache/cacheStore';\nimport type { CacheStore } from '../cache/cacheStore';\nimport { diffVslDocuments } from '../diff/diffEngine';\nimport type { DiffOptions, VslDiff } from '../diff/diffEngine';\nimport type { VslDocument, VslObject } from '../types/vsl';\nimport { enrichWithVision, type EnrichWithVisionDeps } from '../vision/enrichWithVision';\nimport type { VisualFragmentStore } from '../llm/types';\n\n/** Результат snapshot(): полный документ (1-й вызов / URL change) или дифф. */\nexport type SnapshotResult = VslDocument | VslDiff;\n\n/** Type guard: различение диффа и полного документа (контракт T1.2.4). */\nexport function isVslDiff(result: SnapshotResult): result is VslDiff {\n return 'changes' in result;\n}\n\n/**\n * Результат snapshotWithVision (T1.5.5): снапшот (документ/дифф) + ДАННЫЕ\n * фрагментов (base64) — стора в документе нет по контракту (DEC-015):\n * документ несёт только vf/vf_meta, данные идут в decide({visualFragments}).\n */\nexport interface VisionSnapshotResult {\n snapshot: SnapshotResult;\n fragments: VisualFragmentStore;\n}\n\n/** Входные параметры снапшота; url/viewport обязательны (инвалидация §5.2). */\nexport interface SnapshotInput {\n /** URL страницы: изменение → полный сброс кэша и новый полный документ. */\n url: string;\n /** Текущий viewport: изменение при том же URL → сброс координат кэша. */\n viewport: { width: number; height: number };\n title?: string;\n /** ISO 8601; фиксировать в тестах (детерминизм canvas.timestamp и диффа). */\n timestamp?: string;\n background?: string;\n}\n\n/** Плоский индекс id → VslObject (обход дерева в глубину). */\nfunction flattenObjects(objects: readonly VslObject[], map: Map<string, VslObject>): void {\n for (const object of objects) {\n map.set(object.id, object);\n flattenObjects(object.ch ?? [], map);\n }\n}\n\n/** Сохраняет поддерево в store пообъектно (id уникальны — база M1.1). */\nfunction storeTree(store: CacheStore, objects: readonly VslObject[]): void {\n for (const object of objects) {\n store.set(object.id, object);\n storeTree(store, object.ch ?? []);\n }\n}\n\n/**\n * Session-фасад (§2.4 «первый вызов → полный JSON, последующие → дифф»).\n * Store инъекцией — тесты наблюдают coordHash-инвалидацию напрямую.\n */\nexport class VslSnapshotSession {\n private readonly store: CacheStore;\n private lastUrl: string | null = null;\n private lastViewport: { width: number; height: number } | null = null;\n private lastDocument: VslDocument | null = null;\n private version = 0;\n\n constructor(store: CacheStore = createCacheStore()) {\n this.store = store;\n }\n\n /**\n * Сбрасывает состояние сессии (URL, viewport, документ, версия).\n * Следующий вызов snapshot() вернёт полный документ (как первый вызов).\n */\n clear(): void {\n this.store.clear();\n this.lastUrl = null;\n this.lastViewport = null;\n this.lastDocument = null;\n this.version = 0;\n }\n\n /** Возвращает последний полный документ (не diff). */\n getLastDocument(): VslDocument | null {\n return this.lastDocument;\n }\n\n /** Текущая версия документа (инкрементируется при каждом diff). */\n getVersion(): number {\n return this.version;\n }\n\n /**\n * Строит VSL из DOM поддерева root (extractDomTree → segmentTree →\n * buildVslDocument) и возвращает полный документ либо дифф (см. шапку).\n */\n snapshot(root: Element, input: SnapshotInput): SnapshotResult {\n const elements = segmentTree(extractDomTree(root));\n return this.commit(elements, this.buildOptions(root, input), input);\n }\n\n /**\n * Вариант snapshot() для pre-extracted elements (MCP сервер, Node.js контекст).\n * Принимает ExtractedElement[] напрямую (без DOM Element), проходит тот же\n * pipeline: segmentTree → buildVslDocument → cache/diff.\n */\n snapshotFromElements(\n extractedElements: readonly import('../capture/domExtractor').ExtractedElement[],\n input: SnapshotInput,\n ): SnapshotResult {\n const elements = segmentTree(extractedElements as never);\n return this.commit(elements, this.buildOptionsFromInput(input), input);\n }\n\n /**\n * Vision-вариант конвейера (T1.5.5): segmentTree → enrichWithVision (Level 5\n * fallback — аннотация t + vf/vf_meta на элементах ДО сборки) → buildVslDocument\n * → cache/diff — та же логика состояния, что snapshot(). Синхронный snapshot()\n * сохранён (существующие контракты/тесты не ломаются); данные фрагментов\n * (base64) возвращаются ОТДЕЛЬНО — в документе только метаданные (DEC-015).\n */\n async snapshotWithVision(\n root: Element,\n input: SnapshotInput,\n vision: EnrichWithVisionDeps,\n ): Promise<VisionSnapshotResult> {\n const elements = segmentTree(extractDomTree(root));\n const visionResult = await enrichWithVision(elements, vision);\n const snapshot = this.commit(elements, this.buildOptions(root, input), input);\n return { snapshot, fragments: visionResult.fragments };\n }\n\n /**\n * Vision-вариант для pre-extracted elements (MCP сервер, Node.js контекст).\n * Полный pipeline: segmentTree → enrichWithVision → buildVslDocument → cache/diff.\n */\n async snapshotWithVisionFromElements(\n extractedElements: readonly import('../capture/domExtractor').ExtractedElement[],\n input: SnapshotInput,\n vision: EnrichWithVisionDeps,\n ): Promise<VisionSnapshotResult> {\n const elements = segmentTree(extractedElements as never);\n const visionResult = await enrichWithVision(elements, vision);\n const snapshot = this.commit(elements, this.buildOptionsFromInput(input), input);\n return { snapshot, fragments: visionResult.fragments };\n }\n\n /** BuildOptions из input + портативный title (логика бывшего snapshot()). */\n private buildOptions(root: Element, input: SnapshotInput): BuildOptions {\n const options: BuildOptions = {\n viewport: { width: input.viewport.width, height: input.viewport.height },\n url: input.url,\n };\n // title: из input, иначе из документа root — ПОРТАТИВНО, без глобального\n // document (дефолт builder document.title упал бы в Node — packaging-smoke).\n options.title = input.title ?? root.ownerDocument.title;\n if (input.timestamp !== undefined) options.timestamp = input.timestamp;\n if (input.background !== undefined) options.background = input.background;\n return options;\n }\n\n /**\n * BuildOptions из input без DOM Element (для snapshotFromElements).\n * Title берётся только из input (нет фолбэка на root.ownerDocument.title).\n */\n private buildOptionsFromInput(input: SnapshotInput): BuildOptions {\n const options: BuildOptions = {\n viewport: { width: input.viewport.width, height: input.viewport.height },\n url: input.url,\n };\n if (input.title !== undefined) options.title = input.title;\n if (input.timestamp !== undefined) options.timestamp = input.timestamp;\n if (input.background !== undefined) options.background = input.background;\n return options;\n }\n\n /** Сборка документа + кэш/дифф (логика бывшего snapshot(), см. шапку модуля). */\n private commit(\n elements: readonly SegmentedElement[],\n options: BuildOptions,\n input: SnapshotInput,\n ): SnapshotResult {\n const nextDocument = buildVslDocument(elements, options);\n\n const firstCall = this.lastDocument === null;\n const urlChanged = !firstCall && input.url !== this.lastUrl;\n\n if (firstCall || urlChanged) {\n this.store.clear();\n storeTree(this.store, nextDocument.objects);\n this.version = 1;\n this.lastUrl = input.url;\n this.lastViewport = { ...input.viewport };\n this.lastDocument = nextDocument;\n return nextDocument;\n }\n\n const viewportChanged =\n this.lastViewport === null ||\n input.viewport.width !== this.lastViewport.width ||\n input.viewport.height !== this.lastViewport.height;\n if (viewportChanged) this.store.invalidateCoordinates();\n\n const diffOptions: DiffOptions = {\n diffVersion: this.version + 1,\n baseVersion: this.version,\n };\n if (input.timestamp !== undefined) diffOptions.timestamp = input.timestamp;\n const diff = diffVslDocuments(this.lastDocument!, nextDocument, diffOptions);\n\n // Кэш отражает новый документ (см. шапку): неизменённые остаются,\n // изменённые/добавленные обновляются из fresh-документа, удалённые —\n // инвалидируются.\n const fresh = new Map<string, VslObject>();\n flattenObjects(nextDocument.objects, fresh);\n for (const removed of diff.changes.removed) this.store.invalidate(removed.id);\n for (const modified of diff.changes.modified) {\n const object = fresh.get(modified.id);\n if (object !== undefined) this.store.set(object.id, object);\n }\n for (const added of diff.changes.added) storeTree(this.store, [added]);\n\n this.version = diff.diff_version;\n this.lastUrl = input.url;\n this.lastViewport = { ...input.viewport };\n this.lastDocument = nextDocument;\n return diff;\n }\n}","/**\n * Типы LLM Integration (M1.3, ROADMAP.md T1.3.1–T1.3.5).\n *\n * Архитектурная база:\n * - ARCHITECTURE.md §2.5 — LLM Integration Layer, промпт-шаблон {vsl_json}+{goal};\n * - ARCHITECTURE.md §7 — Action Model, формат ответа LLM (§7.4);\n * - ARCHITECTURE.md §9.4 — API адаптеров: decide({vslJson, goal});\n * - ARCHITECTURE.md §10.3 — retry 3× exponential backoff;\n * - DESIGN_SYSTEM.md §4.4 — visual fragments (vf/vf_meta, forward-compatible);\n * - DEC-002 — model-agnostic: адаптеры провайдеров без SDK-зависимостей.\n *\n * Транспорт (нативный fetch, node>=18) инъецируется через LlmTransport —\n * тесты работают на моках без сети (решение пользователя, clarification M1.3).\n * Ноль runtime-зависимостей: официальные SDK провайдеров НЕ используются.\n */\n\nimport type { VslDiff } from '../diff/diffEngine';\nimport type { VslDocument, VslObject } from '../types/vsl';\n\n/** Вход LLM-адаптера: полный документ или дифф (§2.5, §11.2 — diff-first). */\nexport type VslInput = VslDocument | VslDiff;\n\n/** Действие LLM — формат ответа модели (ARCHITECTURE.md §7.4). */\nexport interface LlmAction {\n /** Имя действия из Action Model (§7.1–7.3; см. VALID_ACTIONS в llm/actions). */\n action: string;\n /** ID элемента-цели в VSL JSON — обязателен для действий с целью. */\n target_id?: string;\n /** Значение: текст ввода для type, option для select и т.п. */\n value?: string | null;\n /** Объяснение выбора модели (свободный текст). */\n reasoning?: string;\n}\n\n/** Нормализованное использование токенов (маппинг из usage провайдера). */\nexport interface LlmUsage {\n inputTokens?: number;\n outputTokens?: number;\n}\n\n/** Ответ провайдера на sendPrompt (T1.3.1): raw-данные БЕЗ валидации. */\nexport interface LlmResponse {\n /** Текстовое содержимое ответа (если провайдер его вернул). */\n text: string | null;\n /** Raw-объект действия из tool call (валидацию выполняет decide). */\n action?: unknown;\n /** Идентификатор модели, сформировавшей ответ. */\n model: string;\n usage?: LlmUsage;\n}\n\n/** Метаданные visual fragment (DESIGN_SYSTEM.md §4.4). */\nexport interface VslFragmentMeta {\n type?: string;\n format?: string;\n size?: [number, number];\n hash?: string;\n cached_at?: string;\n}\n\n/** Объект VSL с forward-compatible полями visual fragments (§4.4). */\nexport type VslObjectWithVf = VslObject & {\n /** Ссылка на визуальный фрагмент (например, «emb_abc123»). */\n vf?: string;\n /** Метаданные фрагмента. */\n vf_meta?: VslFragmentMeta;\n};\n\n/** Данные изображения фрагмента для мультимодальной подачи (DEC-015). */\nexport interface VisualFragmentData {\n /** MIME-тип изображения, например «image/webp». */\n mediaType: string;\n /** Base64-данные изображения (без data:-префикса). */\n data: string;\n}\n\n/** Стор данных фрагментов: ключ — vf-ссылка объекта («emb_abc123»). */\nexport type VisualFragmentStore = ReadonlyMap<string, VisualFragmentData>;\n\n/**\n * Провайдер-независимая content-часть user-сообщения (T1.5.4, вариант C):\n * адаптеры маппят её в формат провайдера (OpenAI image_url / Anthropic image\n * base64-блок); форма image-части совпадает с VisualFragmentData (DEC-015).\n */\nexport type LlmContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mediaType: string; data: string };\n\n/**\n * Провайдер-независимое определение tool: одна JSON Schema на оба провайдера\n * (паттерн schema.ts: OpenAI function.parameters / Anthropic input_schema).\n */\nexport interface LlmToolDef {\n /** Имя tool (execute_action / classify_fragment). */\n name: string;\n /** Описание для модели. */\n description: string;\n /** JSON Schema аргументов. */\n schema: Record<string, unknown>;\n}\n\n/** Опции sendPrompt сверх базовых аргументов. */\nexport interface SendPromptOptions {\n /**\n * Данные изображений для vf-ссылок входа: image-блоки включаются\n * только для ссылок, присутствующих в сторе (lazy loading, §4.4);\n * без стора подача остаётся текстовой.\n */\n visualFragments?: VisualFragmentStore;\n}\n\n/** Вход decide (ARCHITECTURE.md §9.4). */\nexport interface DecideInput {\n /** VSL JSON: VslDocument или VslDiff от VslSnapshotSession. */\n vslJson: VslInput;\n /** Цель пользователя на естественном языке. */\n goal: string;\n /** Данные изображений для vf-ссылок (см. SendPromptOptions). */\n visualFragments?: VisualFragmentStore;\n}\n\n/**\n * Инъекция HTTP-транспорта: подмножество сигнатуры fetch.\n * Прод — нативный fetch; тесты — мок (ноль runtime-зависимостей).\n */\nexport type LlmTransport = (url: string, init: RequestInit) => Promise<Response>;\n\n/** Функция задержки (инъекция для детерминированных тестов retry). */\nexport type Sleep = (ms: number) => Promise<void>;\n\n/** Конфиг адаптера провайдера (§9.4: new OpenAIAdapter({apiKey, model})). */\nexport interface LlmAdapterConfig {\n /** API-ключ провайдера. */\n apiKey: string;\n /** Модель; дефолт задаёт адаптер (gpt-4o / claude-sonnet-4-20250514). */\n model?: string;\n /** База API; по умолчанию — официальный endpoint провайдера. */\n baseUrl?: string;\n /** Попыток при сбоях API (§10.3); по умолчанию 3. */\n maxRetries?: number;\n /** Транспорт; по умолчанию — нативный fetch. */\n transport?: LlmTransport;\n /** Задержка между retry-попытками (инъекция для детерминированных тестов; §10.3). */\n sleep?: Sleep;\n}\n\n/** Абстрактный интерфейс LLM Adapter (ROADMAP.md T1.3.1). */\nexport interface LlmAdapter {\n /** Низкий уровень: VSL JSON + задача → ответ провайдера (raw action). */\n sendPrompt(\n vslJson: VslInput,\n task: string,\n options?: SendPromptOptions,\n ): Promise<LlmResponse>;\n /** Convenience (§9.4): решение для цели с валидацией действия (§7.4). */\n decide(input: DecideInput): Promise<LlmAction>;\n}\n\n/**\n * Низкоуровневый вызов провайдера (T1.5.4, вариант C): system + user content +\n * один tool → LlmResponse. Реализуют OpenAIAdapter/AnthropicAdapter (sendRaw);\n * потребитель — LlmVisionClassifier (tool «classify_fragment»). Отдельный\n * интерфейс (не extends LlmAdapter): структурная типизация, контракт LlmAdapter\n * не расширяется — существующие реализации и моки не ломаются.\n */\nexport interface RawLlmCaller {\n sendRaw(\n system: string,\n userContent: readonly LlmContentPart[],\n tool: LlmToolDef,\n ): Promise<LlmResponse>;\n}\n\n/** Базовая ошибка LLM-слоя (транспорт/HTTP/формат ответа провайдера). */\nexport class LlmError extends Error {\n /** HTTP-статус, если ошибка пришла от API (для классификации retry). */\n readonly status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.name = 'LlmError';\n this.status = status;\n }\n}\n\n/** Ответ модели не прошёл валидацию: неизвестное действие, невалидный target_id. */\nexport class LlmValidationError extends LlmError {\n constructor(message: string) {\n super(message);\n this.name = 'LlmValidationError';\n }\n}","/**\n * Action Model (ARCHITECTURE.md §7) для M1.3: канонический список действий,\n * классификация по цели и валидация ответа LLM (§7.4, T1.3.5).\n *\n * Валидация (§7.4 «Action Executor», п.1): action — валидное действие,\n * target_id существует в поданном VSL JSON. Поля value/reasoning проходят\n * сквозь с мягкой нормализацией типов (string | null).\n */\n\nimport { isVslDiff } from '../session/snapshotSession';\nimport type { VslObject } from '../types/vsl';\nimport { LlmValidationError } from './types';\nimport type { LlmAction, VslInput } from './types';\n\n/**\n * Канонический список действий Action Model (§7.1–7.3) — 24 действия:\n * базовые (10) + расширенные (9) + навигационные (4) + download (1).\n */\nexport const VALID_ACTIONS = [\n // §7.1 Базовые\n 'click',\n 'type',\n 'clear',\n 'scroll',\n 'hover',\n 'focus',\n 'blur',\n 'select',\n 'check',\n 'uncheck',\n // §7.2 Расширенные\n 'drag',\n 'drop',\n 'submit',\n 'reset',\n 'open',\n 'close',\n 'expand',\n 'collapse',\n 'wait',\n // §7.3 Навигационные\n 'navigate',\n 'go_back',\n 'go_forward',\n 'refresh',\n // §7.4 Загрузка файлов\n 'download',\n] as const;\n\n/** Имя валидного действия Action Model. */\nexport type ValidAction = (typeof VALID_ACTIONS)[number];\n\n/**\n * Действия, требующие target_id (§7.1–7.2): воздействие на элемент/форму.\n * Без цели: scroll, wait, navigate, go_back, go_forward, refresh.\n */\nexport const TARGET_ACTIONS: ReadonlySet<string> = new Set([\n 'click',\n 'type',\n 'clear',\n 'hover',\n 'focus',\n 'blur',\n 'select',\n 'check',\n 'uncheck',\n 'drag',\n 'drop',\n 'submit',\n 'reset',\n 'open',\n 'close',\n 'expand',\n 'collapse',\n 'download',\n]);\n\nconst collectFromObjects = (objects: readonly VslObject[], ids: Set<string>): void => {\n for (const object of objects) {\n ids.add(object.id);\n collectFromObjects(object.ch ?? [], ids);\n }\n};\n\n/**\n * Собирает множество валидных target_id из входа LLM (§7.4 валидация):\n * - VslDocument — все id рекурсивно;\n * - VslDiff — added (рекурсивно) + modified + unchanged_refs;\n * removed НЕ включаются: элементы удалены и не могут быть целью.\n */\nexport function collectIds(vslJson: VslInput): ReadonlySet<string> {\n const ids = new Set<string>();\n if (isVslDiff(vslJson)) {\n collectFromObjects(vslJson.changes.added, ids);\n for (const modified of vslJson.changes.modified) ids.add(modified.id);\n for (const ref of vslJson.changes.unchanged_refs) ids.add(ref);\n } else {\n collectFromObjects(vslJson.objects, ids);\n }\n return ids;\n}\n\n/**\n * Валидирует raw-объект действия из tool call модели (§7.4):\n * - action — строка из VALID_ACTIONS;\n * - для TARGET_ACTIONS target_id обязателен и должен присутствовать в validIds;\n * - value/reasoning — passthrough с мягкой нормализацией (string | null);\n * target_id у действий без цели игнорируется.\n * Бросает LlmValidationError при нарушении любого условия.\n */\nexport function validateAction(raw: unknown, validIds: ReadonlySet<string>): LlmAction {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new LlmValidationError('Model response: action must be a JSON object');\n }\n const record = raw as Record<string, unknown>;\n const action = record.action;\n if (typeof action !== 'string' || !(VALID_ACTIONS as readonly string[]).includes(action)) {\n const shown = typeof action === 'string' ? action : (JSON.stringify(action) ?? 'undefined');\n throw new LlmValidationError(`Unknown action: ${shown}`);\n }\n const result: LlmAction = { action };\n if (TARGET_ACTIONS.has(action)) {\n const targetId = record.target_id;\n if (typeof targetId !== 'string') {\n throw new LlmValidationError(`Action \"${action}\" requires a string target_id`);\n }\n if (!validIds.has(targetId)) {\n throw new LlmValidationError(`target_id \"${targetId}\" not found in VSL JSON`);\n }\n result.target_id = targetId;\n }\n const value = record.value;\n if (typeof value === 'string' || value === null) result.value = value;\n const reasoning = record.reasoning;\n if (typeof reasoning === 'string') result.reasoning = reasoning;\n return result;\n}","/**\n * Промпты LLM (T1.3.4): system prompt (VSL-формат + Action Model + 3 few-shot)\n * и user-промпт по каноническому шаблону ARCHITECTURE.md §2.5.\n *\n * Промпты провайдер-независимы: few-shot включены ТЕКСТОМ в system prompt,\n * поэтому OpenAI (function calling) и Anthropic (tool use) получают идентичную\n * инструкцию; само действие модель возвращает через tool «execute_action»\n * (см. schema.ts). Список действий рендерится из VALID_ACTIONS (actions.ts) —\n * единый источник правды для промпта и enum в JSON Schema.\n *\n * Примечание (neg note f48a9bda4bd5): llm_prompts_examples.md из корня —\n * примеры домена scene editing (Phase 4), НЕ заготовка для этого промпта;\n * текст построен с нуля из ARCHITECTURE §2.5 + §7.\n */\n\nimport { isVslDiff } from '../session/snapshotSession';\nimport { VALID_ACTIONS } from './actions';\nimport type { VslObject } from '../types/vsl';\nimport type { VslInput, VisualFragmentData, VisualFragmentStore } from './types';\n\n/** Few-shot пример: заголовок, компактный VSL-фрагмент, цель, действие. */\nexport interface FewShotExample {\n title: string;\n /** Компактный VSL JSON (иллюстративный фрагмент). */\n vsl: string;\n goal: string;\n /** Ожидаемый ответ модели — JSON действия (формат §7.4). */\n action: string;\n}\n\n/** Три few-shot примера (T1.3.4): клик по кнопке, заполнение формы, навигация. */\nexport const FEW_SHOT_EXAMPLES: readonly FewShotExample[] = [\n {\n title: 'Example 1 — click a button (full document)',\n vsl: '{\"vsl_version\":\"1.0.0\",\"objects\":[{\"id\":\"header_0\",\"t\":\"header\",\"ch\":[{\"id\":\"link_0_0\",\"t\":\"link\",\"txt\":\"Войти\",\"act\":[\"click\"]},{\"id\":\"link_0_1\",\"t\":\"link\",\"txt\":\"Регистрация\",\"act\":[\"click\"]}]}]}',\n goal: 'Войти в аккаунт',\n action: '{\"action\":\"click\",\"target_id\":\"link_0_0\",\"value\":null,\"reasoning\":\"Ссылка «Войти» открывает форму входа\"}',\n },\n {\n title: 'Example 2 — fill a form (diff input)',\n vsl: '{\"diff_version\":2,\"base_version\":1,\"changes\":{\"added\":[{\"id\":\"form_1_0\",\"t\":\"input\",\"txt\":\"Поиск\",\"act\":[\"click\",\"type\"]}],\"modified\":[],\"removed\":[],\"unchanged_refs\":[\"button_1_1\"]}}',\n goal: 'Найти «vsl json»',\n action: '{\"action\":\"type\",\"target_id\":\"form_1_0\",\"value\":\"vsl json\",\"reasoning\":\"Ввожу запрос в новое поле поиска из диффа; затем нажму button_1_1 из unchanged_refs\"}',\n },\n {\n title: 'Example 3 — navigation',\n vsl: '{\"vsl_version\":\"1.0.0\",\"objects\":[{\"id\":\"main_0\",\"t\":\"main\",\"ch\":[{\"id\":\"header_0_0\",\"t\":\"header\",\"txt\":\"Настройки\"}]}]}',\n goal: 'Открыть страницу настроек по прямой ссылке',\n action: '{\"action\":\"navigate\",\"value\":\"https://example.com/settings\",\"reasoning\":\"Целевой URL известен — прямая навигация быстрее\"}',\n },\n];\n\n/** Свод правил параметров в едином формате {action, target_id, value, reasoning}. */\nconst PARAMETER_CONVENTIONS = [\n '- click, hover, focus, blur, clear, check, uncheck, open, close, expand, collapse — target_id;',\n '- type — target_id + value (text to type);',\n '- select — target_id + value (option);',\n '- scroll — value: \"up\" | \"down\" (optional amount: \"down:300\");',\n '- drag — target_id: source element, value: destination element id;',\n '- submit, reset — target_id: form id;',\n '- wait — value: condition (e.g. a selector or \"idle\");',\n '- navigate — value: URL;',\n '- download — target_id (element to click) OR value (URL to download directly);',\n '- go_back, go_forward, refresh — no parameters.',\n].join('\\n');\n\n/**\n * Строит system prompt: описание VSL-формата (включая семантику диффа §6.2),\n * правила ответа через tool «execute_action», Action Model и 3 few-shot.\n */\nexport function buildSystemPrompt(): string {\n const actions = VALID_ACTIONS.join(' | ');\n const examples = FEW_SHOT_EXAMPLES.map(\n (example) =>\n `${example.title}\\nVSL JSON: ${example.vsl}\\nUser goal: ${example.goal}\\nAction: ${example.action}`,\n ).join('\\n\\n');\n\n return [\n \"You are a screen understanding agent. You receive a VSL JSON representation of the current screen. Analyze the structure and decide what action to take to accomplish the user's goal.\",\n '',\n '## VSL JSON format',\n '',\n 'Elements form a tree via \"ch\" (children). Element keys:',\n '- \"id\" — deterministic unique element id; the ONLY valid value for target_id;',\n '- \"t\" — semantic type: button | input | link | nav | header | main | container | image | select | textarea | modal | tab;',\n '- \"r\" — ARIA role (clarifies semantics);',\n '- \"p\" — position [x, y], relative 0.0–1.0 of the viewport;',\n '- \"s\" — size [width, height] in pixels;',\n '- \"st\" — state: checked | expanded | disabled;',\n '- \"txt\" — visible text or accessible label;',\n '- \"act\" — actions the element supports (e.g. [\"click\"]);',\n '- \"vf\" / \"vf_meta\" — optional visual fragment reference; matching image content, if provided, is attached as separate image blocks.',\n '',\n 'The input is either a full document:',\n '{\"vsl_version\": \"...\", \"canvas\": {...}, \"objects\": [...]}',\n 'or a diff (diff-first mode):',\n '{\"diff_version\": N, \"base_version\": M, \"changes\": {\"added\": [...], \"modified\": [...], \"removed\": [...], \"unchanged_refs\": [...]}}',\n '',\n 'Diff semantics:',\n '- \"added\" — new subtrees, fully described;',\n '- \"modified\" — objects carrying ONLY the changed fields;',\n '- \"removed\" — ids that no longer exist: NEVER target them;',\n '- \"unchanged_refs\" — known unchanged elements: valid targets.',\n '',\n '## Lazy text loading (M1.7)',\n '',\n 'When an element text exceeds 200 characters, the VSL JSON contains only:',\n '- \"txt_preview\" — first ~50 characters of the text (with ellipsis);',\n '- \"txt_ref\" — reference to the full text block (format: tb_xxx, e.g. tb_001).',\n '',\n 'The full text is stored in \"text_blocks\" at the document root. To retrieve it, call the MCP tool vsl_get_text_block with {block_id: \"<txt_ref value>\"}.',\n '',\n 'When to fetch full text: only when the user goal REQUIRES reading the content of a long text (e.g. \"find the paragraph about X\", \"click the link in the article about Y\"). For navigation by headings/titles — txt_preview is sufficient.',\n '',\n '## Actions',\n '',\n 'Respond with exactly ONE next action. Allowed actions:',\n actions + '.',\n '',\n 'Unified parameter format {action, target_id, value, reasoning}:',\n PARAMETER_CONVENTIONS,\n '',\n '## Response rules',\n '',\n '1. Respond ONLY by calling the execute_action tool with one object:',\n ' {\"action\": \"...\", \"target_id\": \"...\", \"value\": null, \"reasoning\": \"...\"}',\n '2. When required, target_id MUST be an id present in the provided VSL JSON (document or diff, including unchanged_refs).',\n '3. Never target removed ids; never invent ids or actions outside the allowed list.',\n '4. Prefer the smallest action that moves toward the goal; explain briefly in \"reasoning\".',\n '',\n '## Examples',\n '',\n examples,\n ].join('\\n');\n}\n\n/** Строит user-промпт по шаблону §2.5: Current screen (VSL JSON) + User goal. */\nexport function buildUserPrompt(vslJson: VslInput, goal: string): string {\n return `Current screen (VSL JSON):\\n${JSON.stringify(vslJson)}\\n\\nUser goal: ${goal}`;\n}\n\n/**\n * Собирает данные изображений для vf-ссылок входа (DESIGN_SYSTEM §4.4, AC[7]):\n * обходит VslDocument (или дифф: added рекурсивно + modified), собирает `vf`-ссылки\n * и возвращает данные только тех, что есть в сторе (lazy loading: нет данных —\n * нет image-блока). Порядок стабильный (порядок первого вхождения ссылок).\n */\nexport function collectFragmentData(\n vslJson: VslInput,\n store?: VisualFragmentStore,\n): VisualFragmentData[] {\n if (store === undefined || store.size === 0) return [];\n const refs = new Set<string>();\n const visit = (objects: readonly VslObject[]): void => {\n for (const object of objects) {\n const vf = (object as { vf?: unknown }).vf;\n if (typeof vf === 'string') refs.add(vf);\n visit(object.ch ?? []);\n }\n };\n if (isVslDiff(vslJson)) {\n visit(vslJson.changes.added);\n for (const modified of vslJson.changes.modified) {\n const vf = (modified as { vf?: unknown }).vf;\n if (typeof vf === 'string') refs.add(vf);\n }\n } else {\n visit(vslJson.objects);\n }\n const fragments: VisualFragmentData[] = [];\n for (const ref of refs) {\n const data = store.get(ref);\n if (data !== undefined) fragments.push(data);\n }\n return fragments;\n}\n","/**\n * JSON Schema tool «execute_action» (T1.3.4) — одна схема для обоих провайдеров:\n * - OpenAI function calling: tools[0].function.parameters;\n * - Anthropic tool use: tools[0].input_schema.\n * Список действий — enum из VALID_ACTIONS (actions.ts): единый источник правды\n * с system prompt. Формат аргументов — §7.4 {action, target_id, value, reasoning}.\n */\n\nimport { VALID_ACTIONS } from './actions';\n\n/** Имя tool, через которое модель возвращает действие. */\nexport const ACTION_TOOL_NAME = 'execute_action' as const;\n\n/** Описание tool для моделей. */\nexport const ACTION_TOOL_DESCRIPTION =\n 'Execute exactly one next browser action toward the user goal.';\n\n/**\n * JSON Schema аргументов tool (подмножество Draft 2020-12, совместимое\n * с OpenAI function calling и Anthropic tool use).\n */\nexport const ACTION_TOOL_SCHEMA = {\n type: 'object',\n properties: {\n action: {\n type: 'string',\n description: 'Action name from the Action Model (ARCHITECTURE.md §7.1–7.3).',\n enum: [...VALID_ACTIONS],\n },\n target_id: {\n type: 'string',\n description: 'Element id from the provided VSL JSON (required for target actions).',\n },\n value: {\n type: ['string', 'null'],\n description:\n 'Value for parameterized actions: type/select/scroll/navigate/wait/drag.',\n },\n reasoning: {\n type: 'string',\n description: 'Brief explanation of the chosen action.',\n },\n },\n required: ['action'],\n additionalProperties: false,\n};","/**\n * HTTP-транспорт и retry для LLM-адаптеров (T1.3.2/T1.3.3, ARCHITECTURE §10.1/§10.3):\n * - defaultTransport — нативный fetch (node>=18, ноль runtime-зависимостей);\n * - retryWithBackoff — до 3 попыток с exponential backoff (§10.3, 2^attempt секунд);\n * - executeJsonWithRetry — запрос + статус-проверка + парс JSON, с retry.\n *\n * Retry-политика (уточнение §10.1/§10.3): повторяются ТОЛЬКО сетевые ошибки,\n * HTTP 429 и 5xx; прочие 4xx — fast-fail (401/403 повтором не чинятся).\n * Паузы: 1s, 2s перед 2-й и 3-й попыткой (форма 2^attempt секунд из §10.3).\n * sleep инъецируется (RetryOptions.sleep / LlmAdapterConfig.sleep) —\n * тесты идут без реальных задержек.\n */\n\nimport { LlmError } from './types';\nimport type { LlmTransport, Sleep } from './types';\n\n/** Транспорт по умолчанию — нативный fetch (глобальный на момент вызова). */\nexport const defaultTransport: LlmTransport = (url, init) => fetch(url, init);\n\n/** Задержка по умолчанию — реальный setTimeout (в тестах инъекция). */\nexport const defaultSleep: Sleep = (ms) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\nexport interface RetryOptions {\n /** Всего попыток, включая первую (§10.3: 3). Значения <1 приводятся к 1. */\n maxRetries?: number;\n /** Задержка между попытками (инъекция для тестов). */\n sleep?: Sleep;\n}\n\n/** Задержка §10.3: 2^attempt секунд → 1s, 2s, 4s… */\nconst backoffDelayMs = (attempt: number): number => 2 ** attempt * 1000;\n\n/**\n * Повторяется ли ошибка: сетевые сбои (не LlmError, напр. TypeError от fetch),\n * LlmError без статуса (обёртка сетевого сбоя), HTTP 429 и 5xx.\n * Прочие 4xx — fast-fail без retry.\n */\nconst isRetryable = (error: unknown): boolean => {\n if (error instanceof LlmError) {\n return error.status === undefined || error.status === 429 || error.status >= 500;\n }\n return true;\n};\n\n/**\n * Исполняет operation с retry до maxRetries попыток и exponential backoff\n * 1s/2s/… (§10.3). После исчерпания попыток (или на неремрабатой ошибке)\n * исключение уходит наружу.\n */\nexport async function retryWithBackoff<T>(\n operation: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxRetries = Math.max(1, options.maxRetries ?? 3);\n const sleep = options.sleep ?? defaultSleep;\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await operation();\n } catch (error) {\n if (!isRetryable(error) || attempt >= maxRetries - 1) throw error;\n await sleep(backoffDelayMs(attempt));\n }\n }\n}\n\n/**\n * Выполняет HTTP-запрос через transport с retry (§10.3) и статус-проверкой,\n * возвращает распарсенное тело JSON.\n *\n * Ошибки:\n * - сетевой сбой transport → LlmError без статуса (retryable);\n * - !res.ok → LlmError со статусом (429/5xx retryable, прочие 4xx — нет);\n * - тело не JSON → LlmError (после retry — наружу).\n */\nexport async function executeJsonWithRetry<T>(\n transport: LlmTransport,\n url: string,\n init: RequestInit,\n providerLabel: string,\n options: RetryOptions = {},\n): Promise<T> {\n const response = await retryWithBackoff(async () => {\n let res: Response;\n try {\n res = await transport(url, init);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new LlmError(`${providerLabel}: network error — ${message}`);\n }\n if (!res.ok) {\n throw new LlmError(`${providerLabel}: HTTP ${res.status} ${res.statusText}`, res.status);\n }\n return res;\n }, options);\n\n try {\n return (await response.json()) as T;\n } catch {\n throw new LlmError(`${providerLabel}: response is not valid JSON`);\n }\n}","/**\n * OpenAI adapter (T1.3.2): Chat Completions API + function calling\n * (tools/tool_choice «execute_action») без официального SDK — нативный fetch\n * через инъекцию транспорта (ноль runtime-зависимостей, DEC-002).\n *\n * Retry (AC[8], §10.3): executeJsonWithRetry — до 3 попыток, только\n * сеть/429/5xx. Мультимодальность (AC[7], §4.4): vf-ссылки из входа при\n * наличии данных в сторе превращаются в content-части\n * {type: 'image_url', image_url: {url: 'data:<mediaType>;base64,<data>'}}.\n */\n\nimport { collectIds, validateAction } from './actions';\nimport { buildSystemPrompt, buildUserPrompt, collectFragmentData } from './prompt';\nimport {\n ACTION_TOOL_DESCRIPTION,\n ACTION_TOOL_NAME,\n ACTION_TOOL_SCHEMA,\n} from './schema';\nimport { defaultTransport, executeJsonWithRetry } from './transport';\nimport { LlmError } from './types';\nimport type {\n DecideInput,\n LlmAction,\n LlmAdapter,\n LlmAdapterConfig,\n LlmContentPart,\n LlmResponse,\n LlmToolDef,\n LlmTransport,\n SendPromptOptions,\n Sleep,\n VslInput,\n} from './types';\n\nconst DEFAULT_MODEL = 'gpt-4o';\nconst DEFAULT_BASE_URL = 'https://api.openai.com/v1';\n\n/** Ответ chat/completions — подмножество полей, нужных адаптеру. */\ninterface OpenAiChatResponse {\n model?: string;\n choices?: Array<{\n message?: {\n content?: string | null;\n tool_calls?: Array<{\n function?: { name?: string; arguments?: string };\n }>;\n };\n }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n}\n\n/** Один content-блок user-сообщения (текст или image_url). */\ntype OpenAiContentPart = Record<string, unknown>;\n\n/**\n * Адаптер OpenAI (gpt-4o / gpt-4o-mini): system prompt + VSL JSON (§2.5)\n * → tool call «execute_action» с действием §7.4.\n */\nexport class OpenAIAdapter implements LlmAdapter {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly baseUrl: string;\n private readonly maxRetries: number;\n private readonly transport: LlmTransport;\n private readonly sleep: Sleep | undefined;\n\n constructor(config: LlmAdapterConfig) {\n if (config.apiKey.length === 0) {\n throw new LlmError('OpenAIAdapter: apiKey is required');\n }\n this.apiKey = config.apiKey;\n this.model = config.model ?? DEFAULT_MODEL;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.maxRetries = config.maxRetries ?? 3;\n this.transport = config.transport ?? defaultTransport;\n this.sleep = config.sleep;\n }\n\n async sendPrompt(\n vslJson: VslInput,\n task: string,\n options?: SendPromptOptions,\n ): Promise<LlmResponse> {\n const userContent: LlmContentPart[] = [\n { type: 'text', text: buildUserPrompt(vslJson, task) },\n ...collectFragmentData(vslJson, options?.visualFragments).map((fragment) => ({\n type: 'image' as const,\n mediaType: fragment.mediaType,\n data: fragment.data,\n })),\n ];\n return this.sendRaw(buildSystemPrompt(), userContent, {\n name: ACTION_TOOL_NAME,\n description: ACTION_TOOL_DESCRIPTION,\n schema: ACTION_TOOL_SCHEMA,\n });\n }\n\n /**\n * Низкоуровневый вызов (T1.5.4, вариант C): провайдер-независимые аргументы\n * → Chat Completions → LlmResponse. Потребители: sendPrompt (tool\n * «execute_action») и LlmVisionClassifier (tool «classify_fragment»).\n * Retry (§10.3) и парсинг tool_calls живут здесь в одном месте.\n */\n async sendRaw(\n system: string,\n userContent: readonly LlmContentPart[],\n tool: LlmToolDef,\n ): Promise<LlmResponse> {\n const content: OpenAiContentPart[] = userContent.map((part) =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : {\n type: 'image_url',\n image_url: { url: `data:${part.mediaType};base64,${part.data}` },\n },\n );\n const body = {\n model: this.model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content },\n ],\n tools: [\n {\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.schema,\n },\n },\n ],\n tool_choice: { type: 'function', function: { name: tool.name } },\n };\n\n const data = await executeJsonWithRetry<OpenAiChatResponse>(\n this.transport,\n `${this.baseUrl}/chat/completions`,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n },\n 'OpenAI',\n { maxRetries: this.maxRetries, sleep: this.sleep },\n );\n\n const message = data.choices?.[0]?.message;\n const toolCall = message?.tool_calls?.[0];\n let rawAction: unknown;\n const args = toolCall?.function?.arguments;\n if (args !== undefined) {\n try {\n rawAction = JSON.parse(args);\n } catch {\n throw new LlmError('OpenAI: tool_call.arguments is not valid JSON');\n }\n }\n return {\n text: message?.content ?? null,\n action: rawAction,\n model: data.model ?? this.model,\n usage: {\n inputTokens: data.usage?.prompt_tokens,\n outputTokens: data.usage?.completion_tokens,\n },\n };\n }\n\n /** Convenience §9.4: решение для цели + валидация (whitelist + target_id). */\n async decide(input: DecideInput): Promise<LlmAction> {\n const response = await this.sendPrompt(input.vslJson, input.goal, {\n visualFragments: input.visualFragments,\n });\n return validateAction(response.action, collectIds(input.vslJson));\n }\n}","/**\n * Anthropic adapter (T1.3.3): Messages API (/v1/messages) + tool use\n * (tools/tool_choice «execute_action») без официального SDK — нативный fetch\n * через инъекцию транспорта (ноль runtime-зависимостей, DEC-002).\n *\n * Retry (AC[8], §10.3): executeJsonWithRetry — до 3 попыток, только\n * сеть/429/5xx. Мультимодальность (AC[7], §4.4): vf-ссылки из входа при\n * наличии данных в сторе превращаются в content-блоки\n * {type: 'image', source: {type: 'base64', media_type, data}}.\n */\n\nimport { collectIds, validateAction } from './actions';\nimport { buildSystemPrompt, buildUserPrompt, collectFragmentData } from './prompt';\nimport {\n ACTION_TOOL_DESCRIPTION,\n ACTION_TOOL_NAME,\n ACTION_TOOL_SCHEMA,\n} from './schema';\nimport { defaultTransport, executeJsonWithRetry } from './transport';\nimport { LlmError } from './types';\nimport type {\n DecideInput,\n LlmAction,\n LlmAdapter,\n LlmAdapterConfig,\n LlmContentPart,\n LlmResponse,\n LlmToolDef,\n LlmTransport,\n SendPromptOptions,\n Sleep,\n VslInput,\n} from './types';\n\nconst DEFAULT_MODEL = 'claude-sonnet-4-20250514';\nconst DEFAULT_BASE_URL = 'https://api.anthropic.com';\nconst ANTHROPIC_VERSION = '2023-06-01';\n/** Anthropic требует max_tokens в каждом запросе (§7.4-ответ компактен). */\nconst DEFAULT_MAX_TOKENS = 1024;\n\n/** Блок content-ответа /v1/messages — подмножество полей, нужных адаптеру. */\ninterface AnthropicReplyBlock {\n type?: string;\n text?: string;\n name?: string;\n input?: unknown;\n}\n\n/** Ответ /v1/messages — подмножество полей, нужных адаптеру. */\ninterface AnthropicMessagesResponse {\n model?: string;\n content?: AnthropicReplyBlock[];\n usage?: { input_tokens?: number; output_tokens?: number };\n}\n\n/** Один content-блок user-сообщения (текст или image). */\ntype AnthropicContentBlock = Record<string, unknown>;\n\n/**\n * Адаптер Anthropic (claude-sonnet-4-20250514): system prompt + VSL JSON (§2.5)\n * → tool_use «execute_action» с действием §7.4.\n */\nexport class AnthropicAdapter implements LlmAdapter {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly baseUrl: string;\n private readonly maxTokens: number;\n private readonly maxRetries: number;\n private readonly transport: LlmTransport;\n private readonly sleep: Sleep | undefined;\n\n constructor(config: LlmAdapterConfig) {\n if (config.apiKey.length === 0) {\n throw new LlmError('AnthropicAdapter: apiKey is required');\n }\n this.apiKey = config.apiKey;\n this.model = config.model ?? DEFAULT_MODEL;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.maxTokens = DEFAULT_MAX_TOKENS;\n this.maxRetries = config.maxRetries ?? 3;\n this.transport = config.transport ?? defaultTransport;\n this.sleep = config.sleep;\n }\n\n async sendPrompt(\n vslJson: VslInput,\n task: string,\n options?: SendPromptOptions,\n ): Promise<LlmResponse> {\n const userContent: LlmContentPart[] = [\n { type: 'text', text: buildUserPrompt(vslJson, task) },\n ...collectFragmentData(vslJson, options?.visualFragments).map((fragment) => ({\n type: 'image' as const,\n mediaType: fragment.mediaType,\n data: fragment.data,\n })),\n ];\n return this.sendRaw(buildSystemPrompt(), userContent, {\n name: ACTION_TOOL_NAME,\n description: ACTION_TOOL_DESCRIPTION,\n schema: ACTION_TOOL_SCHEMA,\n });\n }\n\n /**\n * Низкоуровневый вызов (T1.5.4, вариант C): провайдер-независимые аргументы\n * → Messages API → LlmResponse. Потребители: sendPrompt (tool\n * «execute_action») и LlmVisionClassifier (tool «classify_fragment»).\n * tool_use-блоки фильтруются по tool.name (ранее — константа ACTION_TOOL_NAME):\n * параметризация безопасна, sendPrompt передаёт ACTION_TOOL_NAME.\n */\n async sendRaw(\n system: string,\n userContent: readonly LlmContentPart[],\n tool: LlmToolDef,\n ): Promise<LlmResponse> {\n const content: AnthropicContentBlock[] = userContent.map((part) =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : {\n type: 'image',\n source: {\n type: 'base64',\n media_type: part.mediaType,\n data: part.data,\n },\n },\n );\n const body = {\n model: this.model,\n max_tokens: this.maxTokens,\n system,\n messages: [{ role: 'user', content }],\n tools: [\n {\n name: tool.name,\n description: tool.description,\n input_schema: tool.schema,\n },\n ],\n tool_choice: { type: 'tool', name: tool.name },\n };\n\n const data = await executeJsonWithRetry<AnthropicMessagesResponse>(\n this.transport,\n `${this.baseUrl}/v1/messages`,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': ANTHROPIC_VERSION,\n },\n body: JSON.stringify(body),\n },\n 'Anthropic',\n { maxRetries: this.maxRetries, sleep: this.sleep },\n );\n\n let text: string | null = null;\n let rawAction: unknown;\n for (const block of data.content ?? []) {\n if (block.type === 'text' && typeof block.text === 'string') {\n text = text === null ? block.text : `${text}\\n${block.text}`;\n } else if (block.type === 'tool_use' && block.name === tool.name) {\n rawAction = block.input;\n }\n }\n return {\n text,\n action: rawAction,\n model: data.model ?? this.model,\n usage: {\n inputTokens: data.usage?.input_tokens,\n outputTokens: data.usage?.output_tokens,\n },\n };\n }\n\n /** Convenience §9.4: решение для цели + валидация (whitelist + target_id). */\n async decide(input: DecideInput): Promise<LlmAction> {\n const response = await this.sendPrompt(input.vslJson, input.goal, {\n visualFragments: input.visualFragments,\n });\n return validateAction(response.action, collectIds(input.vslJson));\n }\n}","/**\n * Alibaba/Qwen adapter (DashScope API, OpenAI-compatible): Chat Completions API\n * + function calling (tools/tool_choice «execute_action») без официального SDK —\n * нативный fetch через инъекцию транспорта (ноль runtime-зависимостей, DEC-002).\n *\n * DashScope compatible-mode endpoint: https://dashscope.aliyuncs.com/compatible-mode/v1\n * Формат запроса/ответа идентичен OpenAI (chat/completions, tool_calls).\n *\n * Модель по умолчанию: qwen3.8-flash — самая быстрая и дешёвая модель Qwen (09.2026).\n * Используется для vision-backend (классификация visual fragments).\n *\n * Retry (AC[8], §10.3): executeJsonWithRetry — до 3 попыток, только\n * сеть/429/5xx. Мультимодальность (AC[7], §4.4): vf-ссылки из входа при\n * наличии данных в сторе превращаются в content-части\n * {type: 'image_url', image_url: {url: 'data:<mediaType>;base64,<data>'}}.\n */\n\nimport { collectIds, validateAction } from './actions';\nimport { buildSystemPrompt, buildUserPrompt, collectFragmentData } from './prompt';\nimport {\n ACTION_TOOL_DESCRIPTION,\n ACTION_TOOL_NAME,\n ACTION_TOOL_SCHEMA,\n} from './schema';\nimport { defaultTransport, executeJsonWithRetry } from './transport';\nimport { LlmError } from './types';\nimport type {\n DecideInput,\n LlmAction,\n LlmAdapter,\n LlmAdapterConfig,\n LlmContentPart,\n LlmResponse,\n LlmToolDef,\n LlmTransport,\n SendPromptOptions,\n Sleep,\n VslInput,\n} from './types';\n\nconst DEFAULT_MODEL = 'qwen3.8-flash';\nconst DEFAULT_BASE_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1';\n\n/** Ответ chat/completions — подмножество полей, нужных адаптеру. */\ninterface AlibabaChatResponse {\n model?: string;\n choices?: Array<{\n message?: {\n content?: string | null;\n tool_calls?: Array<{\n function?: { name?: string; arguments?: string };\n }>;\n };\n }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n}\n\n/** Один content-блок user-сообщения (текст или image_url). */\ntype AlibabaContentPart = Record<string, unknown>;\n\n/**\n * Адаптер Alibaba/Qwen (qwen3.8-flash): system prompt + VSL JSON (§2.5)\n * → tool call «execute_action» с действием §7.4.\n *\n * DashScope compatible-mode — OpenAI-compatible API, формат запроса/ответа\n * идентичен OpenAI (chat/completions, tools, tool_choice, tool_calls).\n */\nexport class AlibabaAdapter implements LlmAdapter {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly baseUrl: string;\n private readonly maxRetries: number;\n private readonly transport: LlmTransport;\n private readonly sleep: Sleep | undefined;\n\n constructor(config: LlmAdapterConfig) {\n if (config.apiKey.length === 0) {\n throw new LlmError('AlibabaAdapter: apiKey is required');\n }\n this.apiKey = config.apiKey;\n this.model = config.model ?? DEFAULT_MODEL;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.maxRetries = config.maxRetries ?? 3;\n this.transport = config.transport ?? defaultTransport;\n this.sleep = config.sleep;\n }\n\n async sendPrompt(\n vslJson: VslInput,\n task: string,\n options?: SendPromptOptions,\n ): Promise<LlmResponse> {\n const userContent: LlmContentPart[] = [\n { type: 'text', text: buildUserPrompt(vslJson, task) },\n ...collectFragmentData(vslJson, options?.visualFragments).map((fragment) => ({\n type: 'image' as const,\n mediaType: fragment.mediaType,\n data: fragment.data,\n })),\n ];\n return this.sendRaw(buildSystemPrompt(), userContent, {\n name: ACTION_TOOL_NAME,\n description: ACTION_TOOL_DESCRIPTION,\n schema: ACTION_TOOL_SCHEMA,\n });\n }\n\n /**\n * Низкоуровневый вызов (T1.5.4, вариант C): провайдер-независимые аргументы\n * → DashScope Chat Completions → LlmResponse. Потребители: sendPrompt (tool\n * «execute_action») и LlmVisionClassifier (tool «classify_fragment»).\n * Retry (§10.3) и парсинг tool_calls живут здесь в одном месте.\n */\n async sendRaw(\n system: string,\n userContent: readonly LlmContentPart[],\n tool: LlmToolDef,\n ): Promise<LlmResponse> {\n const content: AlibabaContentPart[] = userContent.map((part) =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : {\n type: 'image_url',\n image_url: { url: `data:${part.mediaType};base64,${part.data}` },\n },\n );\n const body = {\n model: this.model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content },\n ],\n tools: [\n {\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.schema,\n },\n },\n ],\n tool_choice: { type: 'function', function: { name: tool.name } },\n };\n\n const data = await executeJsonWithRetry<AlibabaChatResponse>(\n this.transport,\n `${this.baseUrl}/chat/completions`,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n },\n 'Alibaba/Qwen',\n { maxRetries: this.maxRetries, sleep: this.sleep },\n );\n\n const message = data.choices?.[0]?.message;\n const toolCall = message?.tool_calls?.[0];\n let rawAction: unknown;\n const args = toolCall?.function?.arguments;\n if (args !== undefined) {\n try {\n rawAction = JSON.parse(args);\n } catch {\n throw new LlmError('Alibaba/Qwen: tool_call.arguments is not valid JSON');\n }\n }\n return {\n text: message?.content ?? null,\n action: rawAction,\n model: data.model ?? this.model,\n usage: {\n inputTokens: data.usage?.prompt_tokens,\n outputTokens: data.usage?.completion_tokens,\n },\n };\n }\n\n /** Convenience §9.4: решение для цели + валидация (whitelist + target_id). */\n async decide(input: DecideInput): Promise<LlmAction> {\n const response = await this.sendPrompt(input.vslJson, input.goal, {\n visualFragments: input.visualFragments,\n });\n return validateAction(response.action, collectIds(input.vslJson));\n }\n}","/**\n * Типы Action Executor (ARCHITECTURE.md §7.4 «Action Executor»,\n * ROADMAP.md M1.4: T1.4.1–T1.4.3).\n *\n * Executor исполняет действия Action Model M1.3 (VALID_ACTIONS, 24 действия)\n * на живом DOM. Контракты:\n * - вход — LlmAction (llm/types.ts): {action, target_id?, value?, reasoning?};\n * - value-кодирование — строго по PARAMETER_CONVENTIONS (llm/prompt.ts),\n * единый источник правды с LLM (prompt/schema согласованы в M1.3);\n * - резолв target_id — чисто вычислительный, executor не аннотирует DOM;\n * - executeAction — soft-fail: сбои возвращаются в ActionResult (error), а не\n * бросаются, чтобы шаг агента не прерывал агентный цикл (extension background).\n */\n\n/** Информация о загруженном файле (заполняется при download действии). */\nexport interface DownloadInfo {\n /** Уникальный идентификатор загрузки. */\n downloadId: string;\n /** Имя файла. */\n filename: string;\n /** URL источника. */\n url: string;\n /** Статус загрузки: pending | completed | cancelled | failed. */\n status: 'pending' | 'completed' | 'cancelled' | 'failed';\n}\n\n/** Результат исполнения одного действия (возврат executeAction агенту). */\nexport interface ActionResult {\n /** true — действие исполнено; false — отклонено (детали в error). */\n success: boolean;\n /** Имя исполненного действия (эхо LlmAction.action). */\n action: string;\n /** target_id, если действие было с целью (эхо из входа). */\n targetId?: string;\n /** Машиночитаемое описание сбоя при success=false; обратная связь агенту. */\n error?: string;\n /** Информация о загрузке (заполняется при action='download' и success=true). */\n download?: DownloadInfo;\n}\n\n/**\n * Ошибка исполнения действия: невалидные параметры, целевой элемент не найден\n * (DOM изменился с момента снапшота) или расхождение tag при резолве id.\n * Бросается resolveTarget и внутренними шагами executeAction; снаружи\n * executeAction конвертирует её в ActionResult{success:false, error}.\n */\nexport class ActionExecutionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ActionExecutionError';\n }\n}\n\n/** Опции исполнения; все опциональны (дефолты рассчитаны на прод в браузере). */\nexport interface ExecutorOptions {\n /**\n * Корень обхода DOM при резолве target_id; по умолчанию document.body.\n * Обязан совпадать с root, переданным в extractDomTree/VslSnapshotSession:\n * indexPath в VSL id считается от корня обхода (domExtractor, §5.1).\n */\n root?: Element;\n /** Таймаут wait (мс) — для селектора или \"idle\"; по умолчанию 5000. */\n waitTimeout?: number;\n /**\n * Амплитуда скролла (px) для bare-направления (\"down\" без amount);\n * по умолчанию 300. Явный \"down:300\" перекрывает дефолт.\n */\n defaultScrollAmount?: number;\n}","/**\n * Резолв target_id → DOM-элемент (ARCHITECTURE.md §7.4 «Action Executor»,\n * ROADMAP.md M1.4, T1.4.1).\n *\n * Контракт (spec M1.4 п.4; формат id — vslBuilder.ts:100):\n * - target_id = `tag_index1_index2_...`: tag — имя тега в нижнем регистре,\n * индексы — indexPath элемента (id = `${el.tag}_${el.indexPath.join('_')}`);\n * - indexPath — индексы среди ЭЛЕМЕНТНЫХ детей каждого уровня, вычисляются по\n * структуре DOM ДО фильтрации (domExtractor.collectVisibleChildren) — позиция\n * стабильна независимо от фильтров видимости/сегментации;\n * - extractDomTree возвращает ЛЕС потомков root (сам root не включается), поэтому\n * каждый VSL id содержит ≥1 индекс, а обход стартует с root.children[index];\n * - обход от корня снапшота: ExecutorOptions.root ?? document.body. Root обязан\n * совпадать с root, переданным в extractDomTree/VslSnapshotSession, иначе\n * indexPath укажет не на тот элемент;\n * - резолв чисто вычислительный: executor не аннотирует DOM и не хранит ссылок;\n * - при невалидном формате id, отсутствии элемента по indexPath или несовпадении\n * tag бросается ActionExecutionError (DOM изменился с момента снапшота).\n * Сообщения ошибок — на английском: runtime-строки — публичный API @thinkingos/vsl-sdk и\n * вход для LLM-агента (решение 22.09.2026).\n */\n\nimport { ActionExecutionError } from './types';\nimport type { ExecutorOptions } from './types';\n\n/** Разделитель сегментов target_id (формат vslBuilder.ts:100). */\nconst ID_SEPARATOR = '_';\n\n/** Сегмент индекса: непустая последовательность десятичных цифр (без знака). */\nconst INDEX_SEGMENT_RE = /^\\d+$/;\n\n/**\n * Корень обхода по умолчанию — document.body (прод в браузере).\n * Отдельная функция: обращение к document только в момент вызова, чтобы импорт\n * модуля в plain Node (demo, packaging-smoke) не падал с ReferenceError.\n */\nfunction defaultRoot(): Element {\n const body = document.body;\n if (body === null) {\n throw new ActionExecutionError(\n 'resolveTarget: document.body is not available — pass the snapshot root via ExecutorOptions.root',\n );\n }\n return body;\n}\n\n/**\n * Разбирает target_id на tag и indexPath.\n * Строгий формат (vslBuilder порождает id всегда с indexPath): минимум один индекс;\n * сегменты индексов — только десятичные цифры (пустой сегмент и знак запрещены:\n * Number('') === 0 и приведение '-1' — ловушки парсинга).\n */\nfunction parseTargetId(targetId: string): { tag: string; indexPath: number[] } {\n const segments = targetId.split(ID_SEPARATOR);\n const tag = segments[0];\n if (tag === undefined || tag.length === 0) {\n throw new ActionExecutionError(\n `Invalid target_id \"${targetId}\": missing tag — expected format \"tag_index1_index2_...\"`,\n );\n }\n const indexSegments = segments.slice(1);\n if (indexSegments.length === 0) {\n throw new ActionExecutionError(\n `Invalid target_id \"${targetId}\": missing index path — expected format \"tag_index1_index2_...\"`,\n );\n }\n const indexPath: number[] = [];\n for (const segment of indexSegments) {\n if (!INDEX_SEGMENT_RE.test(segment)) {\n throw new ActionExecutionError(\n `Invalid target_id \"${targetId}\": index segment \"${segment}\" is not a non-negative integer`,\n );\n }\n indexPath.push(Number(segment));\n }\n return { tag, indexPath };\n}\n\n/**\n * Резолвит target_id в DOM-элемент обходом от корня снапшота (§7.4, T1.4.1).\n *\n * @param targetId — id элемента из VSL JSON, формат \"tag_index1_index2_...\".\n * @param options — ExecutorOptions; root обязан совпадать с корнем снапшота\n * (extractDomTree/§5.1), по умолчанию document.body.\n * @returns найденный DOM-элемент.\n * @throws ActionExecutionError — невалидный формат id, элемент по indexPath не\n * найден или tag не совпал (DOM изменился с момента снапшота).\n */\nexport function resolveTarget(targetId: string, options: ExecutorOptions = {}): Element {\n const root = options.root ?? defaultRoot();\n const { tag, indexPath } = parseTargetId(targetId);\n\n let current: Element = root;\n let depth = 0;\n for (const index of indexPath) {\n depth += 1;\n // Индексы считаются по ЭЛЕМЕНТНЫМ детям (Element.children) — конвенция domExtractor.\n const next = current.children[index];\n if (next === undefined) {\n throw new ActionExecutionError(\n `Failed to resolve target_id \"${targetId}\": no element child at index ${index} on depth ${depth} — DOM may have changed since the snapshot`,\n );\n }\n current = next;\n }\n\n const actualTag = current.tagName.toLowerCase();\n if (actualTag !== tag) {\n throw new ActionExecutionError(\n `Failed to resolve target_id \"${targetId}\": found <${actualTag}>, expected tag \"${tag}\" — DOM may have changed since the snapshot`,\n );\n }\n return current;\n}","/**\n * Action Executor — исполнение действий Action Model M1.3 на живом DOM\n * (ARCHITECTURE.md §7.4 «Action Executor», ROADMAP.md M1.4: T1.4.1–T1.4.3).\n *\n * Контракт (spec M1.4; value-кодирование — строго по PARAMETER_CONVENTIONS\n * llm/prompt.ts, единый источник правды с LLM — решение dc_4/dc_7):\n * - вход — LlmAction (llm/types.ts): {action, target_id?, value?, reasoning?};\n * - target_id резолвится resolveTarget (tag_indexPath → обход DOM от корня\n * снапшота: ExecutorOptions.root ?? document.body) — executor не аннотирует\n * DOM и не хранит ссылок на элементы между действиями;\n * - executeAction — soft-fail: всегда возвращает Promise<ActionResult>, не\n * бросает; ActionExecutionError (и любой неожиданный error) перехватывается\n * и отражается в result.error — сбой шага не роняет агентный цикл\n * (extension background);\n * - значения input/select/checkbox выставляются программно + dispatch\n * input/change с bubbles — React-совместимость (AC[2]): React обновляет\n * controlled-компоненты только по событиям, а не от прямой смены .value;\n * - type ЗАМЕНЯЕТ значение поля (не дополняет) — детерминизм: повторный type\n * даёт то же состояние; очистка поля — отдельным действием clear;\n * - сообщения ошибок — на английском (публичный API @thinkingos/vsl-sdk и вход для\n * LLM-агента, решение 22.09.2026).\n */\n\nimport type { LlmAction } from '../llm/types';\nimport { resolveTarget } from './resolveTarget';\nimport { ActionExecutionError } from './types';\nimport type { ActionResult, DownloadInfo, ExecutorOptions } from './types';\n\n/** Амплитуда скролла по умолчанию для bare-направления (types.ts, дефолт 300). */\nconst DEFAULT_SCROLL_AMOUNT = 300;\n\n/** Допустимые направления scroll (PARAMETER_CONVENTIONS). */\nconst SCROLL_DIRECTIONS: ReadonlySet<string> = new Set(['up', 'down', 'left', 'right']);\n\n/** Таймаут wait по умолчанию (types.ts ExecutorOptions, дефолт 5000). */\nconst WAIT_DEFAULT_TIMEOUT_MS = 5000;\n\n/** Интервал опроса условия wait (мс). */\nconst WAIT_POLL_INTERVAL_MS = 50;\n/**\n * Резолвит target_id действия через resolveTarget (§7.4). Обработчики\n * target-действий вызывают его первым шагом: отсутствие строкового target_id —\n * ActionExecutionError (PARAMETER_CONVENTIONS: target-действия требуют цель).\n */\nfunction resolveTargetOf(action: LlmAction, options: ExecutorOptions): Element {\n const targetId = action.target_id;\n if (typeof targetId !== 'string') {\n throw new ActionExecutionError(`Action \"${action.action}\" requires a string target_id`);\n }\n return resolveTarget(targetId, options);\n}\n\n/** Извлекает обязательный строковый value параметризованного действия. */\nfunction requireValue(action: LlmAction): string {\n const value = action.value;\n if (typeof value !== 'string') {\n throw new ActionExecutionError(`Action \"${action.action}\" requires a string value`);\n }\n return value;\n}\n\n/**\n * Dispatch input+change с bubbles после программного изменения значения —\n * AC[2] React-совместимость: React делегирует события на корне, поэтому смена\n * .value без событий не обновляет controlled-компоненты.\n */\nfunction dispatchValueEvents(el: Element): void {\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\n/**\n * Проверяет, что цель — текстовое поле (input/textarea; contenteditable в\n * scope M1.4 не входит: builder даёт type только input/textarea), и возвращает\n * типизированную ссылку.\n */\nfunction requireTextTarget(\n action: LlmAction,\n el: Element,\n): HTMLInputElement | HTMLTextAreaElement {\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n return el;\n }\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires an <input> or <textarea> target, got <${el.tagName.toLowerCase()}>`,\n );\n}\n\n/** type/clear: замена значения поля + input/change (контракт — в шапке файла). */\nfunction typeInto(el: HTMLInputElement | HTMLTextAreaElement, text: string): void {\n el.value = text;\n dispatchValueEvents(el);\n}\n\n/**\n * Клик: нативный click() (HTMLElement/SVGElement, HTMLOrSVGElement mixin);\n * фолбэк — синтетический bubbling MouseEvent для элементов без click().\n */\nfunction clickElement(el: Element): void {\n if (typeof (el as HTMLElement).click === 'function') {\n (el as HTMLElement).click();\n return;\n }\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n}\n\n/** Hover: синтетические mouseover (bubbles) + mouseenter (не bubbling по DOM-семантике). */\nfunction hoverElement(el: Element): void {\n el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true, cancelable: true }));\n el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: false }));\n}\n\n/**\n * Focus/blur: нативные focus()/blur() — браузер (и jsdom) сами диспетчат\n * focus/blur/focusin/focusout; элемент без focus() — ActionExecutionError.\n */\nfunction focusableOrThrow(action: LlmAction, el: Element): { focus(): void; blur(): void } {\n const htmlEl = el as HTMLElement;\n if (typeof htmlEl.focus !== 'function' || typeof htmlEl.blur !== 'function') {\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires a focusable element, got <${el.tagName.toLowerCase()}>`,\n );\n }\n return htmlEl;\n}\n\n/**\n * select: value = option value (PARAMETER_CONVENTIONS), фолбэк — совпадение по\n * видимому тексту option (dc_4); иначе ActionExecutionError. Значение\n * выставляется в value выбранного option'а + input/change (AC[2]).\n */\nfunction selectOption(el: Element, raw: string): void {\n if (!(el instanceof HTMLSelectElement)) {\n throw new ActionExecutionError(\n `Action \"select\" requires a <select> target, got <${el.tagName.toLowerCase()}>`,\n );\n }\n const wanted = raw.trim();\n for (const option of Array.from(el.options)) {\n if (option.value === raw) {\n el.value = option.value;\n dispatchValueEvents(el);\n return;\n }\n }\n for (const option of Array.from(el.options)) {\n if (option.text.trim() === wanted) {\n el.value = option.value;\n dispatchValueEvents(el);\n return;\n }\n }\n throw new ActionExecutionError(\n `Action \"select\": option \"${raw}\" not found in <select> (neither by value nor by visible text)`,\n );\n}\n\n/**\n * check/uncheck: цель — input[type=checkbox|radio] (builder defaultActions\n * даёт check/uncheck обоим типам); состояние выставляется программно +\n * input/change (AC[2]).\n */\nfunction setChecked(el: Element, action: LlmAction, checked: boolean): void {\n if (!(el instanceof HTMLInputElement) || (el.type !== 'checkbox' && el.type !== 'radio')) {\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires an <input type=\"checkbox\"> or <input type=\"radio\"> target, got <${el.tagName.toLowerCase()}>`,\n );\n }\n el.checked = checked;\n dispatchValueEvents(el);\n}\n\n/**\n * Разбор value scroll: \"dir\" либо \"dir:amount\" (PARAMETER_CONVENTIONS, dc_4);\n * bare-направление берёт amount из ExecutorOptions.defaultScrollAmount.\n * Возвращает дельту (dx, dy) для window.scrollBy.\n */\nfunction parseScrollValue(value: string, defaultAmount: number): { dx: number; dy: number } {\n const parts = value.trim().split(':');\n const dir = parts[0] ?? '';\n const amountPart = parts[1];\n if (parts.length > 2 || !SCROLL_DIRECTIONS.has(dir)) {\n throw new ActionExecutionError(\n `Invalid scroll value \"${value}\" — expected \"up\", \"down\", \"left\", \"right\" or \"dir:amount\" (e.g. \"down:300\")`,\n );\n }\n let amount = defaultAmount;\n if (amountPart !== undefined) {\n if (!/^\\d+(?:\\.\\d+)?$/.test(amountPart)) {\n throw new ActionExecutionError(\n `Invalid scroll amount in \"${value}\" — expected a positive number, e.g. \"down:300\"`,\n );\n }\n amount = Number(amountPart);\n }\n const dx = dir === 'left' ? -amount : dir === 'right' ? amount : 0;\n const dy = dir === 'up' ? -amount : dir === 'down' ? amount : 0;\n return { dx, dy };\n}\n\n/**\n * Синтетическая последовательность HTML5 drag-and-drop (dc_4: drag —\n * target_id=source, value=id destination; executor stateless — источник и цель\n * резолвятся заново на каждое действие). DragEvent может отсутствовать в средах\n * вроде jsdom — фолбэк на bubbling Event с той же семантикой.\n */\nfunction dispatchDragEvent(target: Element, type: string): void {\n if (typeof DragEvent === 'function') {\n target.dispatchEvent(new DragEvent(type, { bubbles: true, cancelable: true }));\n } else {\n target.dispatchEvent(new Event(type, { bubbles: true, cancelable: true }));\n }\n}\n\n/** Проверяет, что цель — форма (dc_4: submit/reset — target_id формы). */\nfunction requireForm(action: LlmAction, el: Element): HTMLFormElement {\n if (el instanceof HTMLFormElement) {\n return el;\n }\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires a <form> target, got <${el.tagName.toLowerCase()}>`,\n );\n}\n\n/**\n * open/close (dc_4: target_id без value): <dialog> → showModal()/close(),\n * <details> → атрибут open; у прочих элементов нет стандартной open-семантики —\n * ActionExecutionError. HTMLDialogElement может отсутствовать в jsdom — guard.\n */\nfunction setOpenState(action: LlmAction, el: Element, open: boolean): void {\n if (typeof HTMLDialogElement !== 'undefined' && el instanceof HTMLDialogElement) {\n if (open) {\n el.showModal();\n } else {\n el.close();\n }\n return;\n }\n if (el instanceof HTMLDetailsElement) {\n el.open = open;\n return;\n }\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires a <dialog> or <details> target, got <${el.tagName.toLowerCase()}>`,\n );\n}\n\n/**\n * expand/collapse (dc_4: target_id без value): <details> → атрибут open,\n * иначе элемент с aria-expanded → установка атрибута (состояние читается\n * напрямую, без событий); иначе ActionExecutionError.\n */\nfunction setExpanded(action: LlmAction, el: Element, expanded: boolean): void {\n if (el instanceof HTMLDetailsElement) {\n el.open = expanded;\n return;\n }\n if (el.hasAttribute('aria-expanded')) {\n el.setAttribute('aria-expanded', expanded ? 'true' : 'false');\n return;\n }\n throw new ActionExecutionError(\n `Action \"${action.action}\" requires a <details> or an element with aria-expanded, got <${el.tagName.toLowerCase()}>`,\n );\n}\n\n/** Пауза между опросами условия wait. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/**\n * Условие wait выполнено (dc_4/dc_7): \"idle\" → документ загрузился\n * (document.readyState === 'complete'); иначе value — CSS-селектор, элемент\n * существует. Невалидный селектор — ActionExecutionError с понятным сообщением.\n */\nfunction isWaitSatisfied(value: string): boolean {\n if (value.trim() === 'idle') {\n return document.readyState === 'complete';\n }\n try {\n return document.querySelector(value) !== null;\n } catch {\n throw new ActionExecutionError(`wait: invalid selector \"${value}\"`);\n }\n}\n\n/**\n * wait: опрос условия (селектор или \"idle\") до таймаута\n * ExecutorOptions.waitTimeout (дефолт 5000). Таймаут — ActionExecutionError\n * (soft-fail отразит его в ActionResult.error).\n */\nasync function waitForValue(value: string, options: ExecutorOptions): Promise<void> {\n const timeoutMs = options.waitTimeout ?? WAIT_DEFAULT_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n if (isWaitSatisfied(value)) {\n return;\n }\n if (Date.now() >= deadline) {\n throw new ActionExecutionError(\n `wait: condition \"${value}\" is not met within ${timeoutMs}ms`,\n );\n }\n await sleep(WAIT_POLL_INTERVAL_MS);\n }\n}\n\n/**\n * Исполняет одно действие LLM на живом DOM (§7.4, T1.4.1–T1.4.3).\n *\n * Soft-fail (решение dev_1): любые сбои — невалидные параметры, целевой\n * элемент не найден, DOM изменился с момента снапшота — возвращаются в\n * ActionResult.error, исключение агентному циклу не бросается.\n *\n * @param action — действие от LLM (после validateAction в M1.3; состав VSL\n * здесь повторно не валидируется — executor проверяет только то, что нужно\n * для исполнения).\n * @param options — ExecutorOptions; root обязан совпадать с корнем снапшота.\n * @returns ActionResult с эхом action/targetId.\n */\nexport async function executeAction(\n action: LlmAction,\n options: ExecutorOptions = {},\n): Promise<ActionResult> {\n const targetId = action.target_id;\n const echo: ActionResult = {\n success: false,\n action: action.action,\n ...(typeof targetId === 'string' ? { targetId } : {}),\n };\n try {\n switch (action.action) {\n // ——— §7.1 Базовые (T1.4.1) ———\n case 'click':\n clickElement(resolveTargetOf(action, options));\n break;\n case 'type':\n typeInto(\n requireTextTarget(action, resolveTargetOf(action, options)),\n requireValue(action),\n );\n break;\n case 'clear': {\n const field = requireTextTarget(action, resolveTargetOf(action, options));\n typeInto(field, '');\n break;\n }\n case 'scroll': {\n const amount = options.defaultScrollAmount ?? DEFAULT_SCROLL_AMOUNT;\n const { dx, dy } = parseScrollValue(requireValue(action), amount);\n window.scrollBy(dx, dy);\n break;\n }\n case 'hover':\n hoverElement(resolveTargetOf(action, options));\n break;\n case 'focus':\n focusableOrThrow(action, resolveTargetOf(action, options)).focus();\n break;\n case 'blur':\n focusableOrThrow(action, resolveTargetOf(action, options)).blur();\n break;\n case 'select':\n selectOption(resolveTargetOf(action, options), requireValue(action));\n break;\n case 'check':\n setChecked(resolveTargetOf(action, options), action, true);\n break;\n case 'uncheck':\n setChecked(resolveTargetOf(action, options), action, false);\n break;\n // ——— §7.2 Расширенные (T1.4.2) ———\n case 'drag': {\n // dc_4: target_id = source, value = id destination (второй resolveTarget).\n // Каноническая последовательность HTML5 DnD: старт у источника,\n // перетаскивание и сброс у цели, завершение у источника.\n const destinationId = requireValue(action);\n const source = resolveTargetOf(action, options);\n const destination = resolveTarget(destinationId, options);\n dispatchDragEvent(source, 'dragstart');\n dispatchDragEvent(destination, 'dragenter');\n dispatchDragEvent(destination, 'dragover');\n dispatchDragEvent(destination, 'drop');\n dispatchDragEvent(source, 'dragend');\n break;\n }\n case 'drop': {\n // Executor stateless: источник неизвестен — цель получает стандартную\n // последовательность dragover (страница разрешает drop через\n // preventDefault в своём обработчике) и drop.\n const dropTarget = resolveTargetOf(action, options);\n dispatchDragEvent(dropTarget, 'dragover');\n dispatchDragEvent(dropTarget, 'drop');\n break;\n }\n case 'submit': {\n // Синтетический cancelable submit с bubbles: нативный form.submit()\n // обходит submit-обработчики и в jsdom не реализован; делегированные\n // обработчики (React onSubmit) событие получают.\n const form = requireForm(action, resolveTargetOf(action, options));\n form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n break;\n }\n case 'reset':\n // Нативный reset(): по спецификации сам диспетчит cancelable 'reset'\n // и сбрасывает поля, если событие не отменено.\n requireForm(action, resolveTargetOf(action, options)).reset();\n break;\n case 'open':\n setOpenState(action, resolveTargetOf(action, options), true);\n break;\n case 'close':\n setOpenState(action, resolveTargetOf(action, options), false);\n break;\n case 'expand':\n setExpanded(action, resolveTargetOf(action, options), true);\n break;\n case 'collapse':\n setExpanded(action, resolveTargetOf(action, options), false);\n break;\n case 'wait':\n await waitForValue(requireValue(action), options);\n break;\n // ——— §7.3 Навигационные (T1.4.3) ———\n case 'navigate':\n // location.assign: сохраняет запись истории (в отличие от replace),\n // принимает и относительные URL (PARAMETER_CONVENTIONS: navigate=URL).\n window.location.assign(requireValue(action));\n break;\n case 'go_back':\n window.history.back();\n break;\n case 'go_forward':\n window.history.forward();\n break;\n case 'refresh':\n window.location.reload();\n break;\n case 'download': {\n // Гибридный подход (решение dc_6): target_id — клик по элементу, value — прямое скачивание по URL\n const downloadTargetId = action.target_id;\n const downloadValue = action.value;\n \n if (typeof downloadTargetId === 'string') {\n // Клик по элементу (кнопка/ссылка для скачивания)\n const el = resolveTarget(downloadTargetId, options);\n clickElement(el);\n // Браузер инициирует download event, extension context обработает его отдельно\n break;\n }\n \n if (typeof downloadValue === 'string') {\n // Прямое скачивание по URL\n const url = downloadValue;\n const a = document.createElement('a');\n a.href = url;\n a.download = ''; // Браузер сам определит filename из URL\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n // Генерируем downloadId для отслеживания\n const downloadId = `download_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const filename = url.split('/').pop() || 'download';\n const downloadInfo: DownloadInfo = {\n downloadId,\n filename,\n url,\n status: 'pending',\n };\n return {\n ...echo,\n success: true,\n download: downloadInfo,\n };\n }\n \n throw new ActionExecutionError(\n 'Action \"download\" requires either target_id (element to click) or value (URL to download)',\n );\n }\n default:\n // После validateAction (M1.3) сюда попасть нельзя — защита от прямых\n // вызовов executeAction с действием вне VALID_ACTIONS.\n throw new ActionExecutionError(\n `Unknown action: \"${action.action}\" — must be one of VALID_ACTIONS (24 actions)`,\n );\n }\n return { ...echo, success: true };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { ...echo, success: false, error: message };\n }\n}"],"mappings":"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","names":["elementId","import_node_fs","import_node_path","import_node_os","sleep","DEFAULT_MODEL","DEFAULT_BASE_URL","DEFAULT_MODEL","DEFAULT_BASE_URL","defaultRoot"]}