# Действия страницы через run_js

Основной Chat Agent 2.3.0 предоставляет модели один инструмент страницы:
`run_js({code})`. `code` — тело async-функции с явным JSON-возвратом.
История остаётся общей для сайта, выполнение привязано к исходной вкладке и документу.

```mermaid
flowchart LR
  Model[Модель] -->|"run_js: code"| Agent
  Agent -->|"существующая доставка NATS"| LocalMcp
  LocalMcp --> Background
  Background -->|"Chrome userScripts.execute / documentId"| Script[USER_SCRIPT]
  Script <-->|"Chrome messages: dom.find/get/set/act"| DOM[Общий DOM runtime / collector]
  Script -->|"JSON: ok, value / error"| Background
  Background --> LocalMcp
  LocalMcp -->|"browser_tool_result"| Agent
  Agent --> Model
```

## Включение

Нужен Chrome 138+. Обновите основной пакет по прежнему пути и включите
«Разрешить пользовательские скрипты» в настройках расширения. Нажатие значка
на HTTP/HTTPS-странице выдаёт временный activeTab доступ именно этой странице.
Объявленные optional host permissions не выдают доступ всем сайтам.
Смена CSP страницы не нужна. Проверка доступности — «Проверить JavaScript»
в шестерёнке панели; она не вызывает модель и не меняет страницу.

## DOM API

Все четыре метода асинхронные. Ссылки выдаёт общий ограниченный реестр collector;
удалённая ссылка никогда не назначается другому узлу.

| Метод | Назначение |
|---|---|
| `dom.find({selector,text,role,scope,limit,offset,include_hidden})` | Порция элементов и признак продолжения; условия соединяются И |
| `dom.get([ref], [property], {limit,offset})` | Нужные свойства, constraints, контекст и порции options |
| `dom.set([{ref,property,value}])` | Предварительная проверка, native setters, события и фактический результат |
| `dom.act(ref, 'click' / 'scroll')` | Ограниченное действие над найденным элементом |

Например, изменить единственный h1, сохранив заголовок вкладки:

```js
const found = await dom.find({selector: 'h1', limit: 2});
if (found.items.length !== 1 || found.has_more) {
  return {ok: false, reason: 'Нужен один однозначный заголовок'};
}
return await dom.set([{ref: found.items[0].ref, property: 'textContent', value: 'Новый заголовок'}]);
```

Формы используют `value`, `checked`, `selected_values`; заголовок вкладки —
`{ref:'@document', property:'title'}`. Метаданные доступны через `attr:name`,
`attr:property`, `attr:content`, стили — `style:<property>` и `computed:<property>`.
Helper обходит доступные iframe и открытый/закрытый Shadow DOM. Новая поддержка
кросс-доменных iframe в этот выпуск не входит. Ручной «Собрать поля» сохранён.

## Инструменты приложения

`await pageTools.list()` возвращает JSON-каталог текущего документа с `name`,
`description`, `inputSchema`. `await pageTools.call(name, args)` вызывает
инструмент с JSON-объектом аргументов. Сначала читайте каталог, затем используйте
имя и схему из него; не разбирайте имя на модуль и метод самостоятельно.

```js
const tools = await pageTools.list();
if (!tools.some(tool => tool.name === 'Measure.GetActive')) return {available: false};
return {active: await pageTools.call('Measure.GetActive', {})};
```

Непустой каталог `document.modelContext` текущего документа имеет приоритет.
Нативный вызов выполняется в USER_SCRIPT; формат аргументов выбирается до вызова:
Chrome до 155 получает JSON-строку, 155+ — объект. Строковая схема каталога
преобразуется в JSON-объект. Инструменты дочерних документов не включаются.

Если нативный каталог пуст или API отсутствует, фиксированная операция читает
`window.webMcp` в MAIN. Используется существующий `chrome.userScripts.execute`
и тот же валидатор JSON, без eval/Function, изменения CSP и новых разрешений.
В MAIN передаются только операция, имя и JSON-аргументы; код модели остаётся
в USER_SCRIPT. `moduleName/apiName` берутся из актуального каталога страницы.

Поддерживаются JSON, Promise и void (возвращается `null`). Observable выдаёт
явную ошибку: автоматические подписки не создаются. Ошибка вызова не приводит
к повтору через другой API или DOM. Каталог и результаты не кэшируются между
вызовами. Действуют существующие deadline и привязка к исходному документу;
отмена прекращает ожидание, но не обещает остановку уже запущенной функции.

На SPPI API включается штатно: «Настройки карты → Feature flags →
webMcpEnabled». Дождитесь загрузки каталога. Это настройка SPPI в браузере,
расширение не включает её автоматически.

## Результаты и пределы

Исполнитель возвращает `{ok:true,value}` либо `{ok:false,error,message,effect}`.
Нужно проверить также внутренние `value.ok` и `value.errors`: выполненный JS
сам по себе не означает успешное изменение поля. JSON и код раскрываются в панели;
просмотр истории и Monitor ничего не исполняет.

Пределы: код 16 000 символов, поиск 20 элементов по умолчанию / максимум 100,
запись 100 изменений, реестр 1000 ссылок. Результат клиента ограничен 96 000 байт
с запасом под серверный конверт 98 304 байта. На вопрос: 12 вызовов модели,
10 инструментов, 120 секунд. Ожидание JS — 4,5 секунды, серверное — 5 секунд.

Один незавершённый запуск на документ. Повторная доставка того же tool_call_id
возвращает прежний исход. Отметка chrome.storage.session переживает остановку
background. При неизвестном исходе нужна перезагрузка документа. Тайм-аут и
отмена прекращают ожидание и дальнейшие шаги Agent; произвольный JS принудительно
не останавливается, уже сделанные изменения не откатываются автоматически.

Каталог сервера принимает только run_js; локальные message/cancel остаются
служебными командами. Результаты доступны внутри вопроса и не сохраняются
между независимыми вопросами. Таблица widget_cache хранит каталог по
(session_id, context_id); старый столбец results и история физически сохранены.

Источники реализации: `extensions/chrome/src/runJs.ts`, `domRuntime.ts`,
`form-collector.js`, `src_agent/widget_requests.py`, `src_shared/browser_tools.py`.
Отдельный аудит и измерения: [RUN_JS_AUDIT_2026-10-28](</experiments/voice-ai/archive/w/DOCS/RUN_JS_AUDIT_2026-10-28.md>).
