# In-call AI-агент, права пользователя и spotlight-подсказки

В звонке у пользователя есть ассистент: можно попросить «включи запись», «открой
чат», «как поменять камеру?» — и агент либо **выполняет действие**, либо
**подсвечивает нужную кнопку** (затемняет интерфейс и показывает, куда нажать).
Агент действует **строго с правами пользователя**: он физически не может
сделать больше, чем разрешено ролью/политикой тенанта.

## 1. Что умеет

| Действие | Tool | Требует |
|---|---|---|
| Начать/остановить запись | `start_recording`, `stop_recording` | фича `recording` + роль host/moderator |
| Микрофон/камера/рука | `toggle_mic`, `toggle_camera`, `raise_hand` | без отдельной фичи |
| Демонстрация экрана | `toggle_screen_share` | `screenShare` |
| Открыть чат/файлы/участников/заметки/настройки | `open_chat`, `open_files`, `open_participants`, `open_notes`, `open_settings` | соответствующая фича (`chat`, `fileShare`, `participantList`, `meetingNotes`, `deviceSettings`) |
| Блюр фона | `set_blur` | `backgroundBlur` |
| Свернуть/развернуть/фуллскрин/новая вкладка | `minimize`, `restore`, `request_fullscreen`, `open_new_tab` | `fullscreen` (для фуллскрина) |
| Скопировать приглашение | `copy_invite_link` | — |
| Подсказка «куда нажать» | spotlight (не действие) | `session.inCallAgent.spotlight` |

Полный список — `packages/protocol/src/assistant.ts` (`assistantTools`,
`assistantToolRequirements`). Клиент и сервер используют **одну и ту же**
функцию прав `isAssistantToolAllowed`/`filterAssistantActions`: LLM не может
предложить действие, которое пользователю недоступно (отфильтровывается), а
сервер повторно проверяет права и аудирует.

## 2. Режимы

`TenantConfig.session.inCallAgent`:

| Поле | Смысл |
|---|---|
| `enabled` | включён ли ассистент (по умолчанию да) |
| `mode` | `local` (офлайн-правила, без LLM), `external`, `hybrid` (LLM + локальный fallback) |
| `endpoint`, `apiKeyEnv`, `timeoutMs` | внешний LLM-агент (HTTPS + HMAC; ключ только в env) |
| `systemPrompt`, `model` | параметры внешней модели |
| `spotlight` | разрешить подсветку элементов UI |
| `allowedTools` | какие инструменты вообще доступны в тенанте |
| `maxActionsPerReply` | лимит действий на один ответ (1..10) |
| `audit` | писать `assistant.usage` в аудит |
| `denyMessage` | текст при отказе по правам |

**local (по умолчанию)** — детерминированный разбор фраз (ru/en), без сети:
понимает «включи запись», «открой чат», «где микрофон», «как включить фон» и
строит spotlight-tour. Работает автономно, нулевая стоимость, ноль утечек.

**external** — запрос уходит в корпоративный LLM-агент (любая нейросеть за
прокси, см. `docs/llm-integrations.md`), который возвращает текст, действия и
подсказки. Ответ валидируется zod, действия фильтруются правами.

## 3. Spotlight (подсветка «куда нажать»)

Ассистент возвращает `spotlight: [{target, text, step?}]`. Виджет:

1. затемняет весь интерфейс (overlay, `backdrop` + dim);
2. находит элемент по логическому имени (`mic`, `camera`, `screen`, `chat`,
   `files`, `participants`, `notes`, `settings`, `blur`, `recording`,
   `minimize`, `fullscreen`, `newtab`, `invite`) или CSS-селектору;
3. рисует подсветку (кольцо/пульс) и подсказку-стрелку с текстом;
4. для многошаговых туров — кнопки «Далее/Назад/Понятно», шаги `step`.

Работает и как тур по функциям («покажи, как включить запись»). A11y: overlay
закрывается Esc, фокус на подсказке, `aria-live` для текста, не блокирует звонок.

## 4. Wire-протокол

Ассистент вызывается из виджета:
`POST /v1/rooms/:roomId/assistant` (`authorization: Bearer <join token>`,
тело `{text, locale?, state?}`) → `AssistantReply {text, actions, spotlight, source}`.
Сервер:

1. проверяет токен/комнату/фичу `session.inCallAgent.enabled` и rate-limit
   (10/мин на участника);
2. `mode=local` → `501 assistant_local_only` с подсказкой (виджет исполняет
   локальный движок сам, без сети);
3. `external/hybrid` → `POST endpoint` с `InCallAssistantRequest`
   (`participant.permissions`, `tools`, `state`, `systemPrompt`, HMAC-подпись),
   ответ `InCallAssistantResponse` валидируется `parseInCallAssistantResponse`;
4. отбрасывает действия, не проходящие `filterAssistantActions`, обрезает до
   `maxActionsPerReply`, пишет аудит `assistant.usage`, возвращает reply.

Виджет исполняет действия только после проверки `isAssistantToolAllowed` (UX),
сервер — источник истины (enforcement).

## 5. Безопасность

- **Нет эскалации прав:** LLM не может записать/поделиться экраном/открыть
  данные, если у пользователя нет фичи; инъекция в промпт («игнорируй права»)
  не проходит серверный фильтр.
- **Нет скрытых действий:** все исполняемые действия видимы пользователю
  (кнопка/панель меняет состояние), запись — с consent-диалогом.
- **Prompt injection:** пользовательский текст и транскрипт — данные, не
  инструкции; ответы только по схеме, без цепочек tool-calling с произвольными
  параметрами (args ограничены и не исполняются сервером).
- **Данные:** наружу уходят только имя/роль/permissions и компактный state (без
  аудио/видео/чата); ключ LLM — только env; аудит всех вызовов.
- **Отключение:** `enabled:false` или пустой `allowedTools` — ассистента нет;
  `spotlight:false` — только текстовые ответы.

## 6. Примеры

- «Включи запись» → `start_recording` + подсветка кнопки записи (если роль host).
- «Где чат?» → текст + spotlight на кнопку чата.
- «Покажи, как включить размытие фона» → тур: шаг 1 — кнопка фона, шаг 2 —
  настройки фона.
- «Открой заметки и найди, что решили по бюджету» → `open_notes` (затем
  knowledge-ассистент отвечает по транскрипту).
- «Начни запись и пригласи Аню» → `start_recording` + `copy_invite_link`.
