Skip to content

Architecture ru

Hermes Agent edited this page Oct 1, 2026 · 1 revision

Архитектура

English | 中文 | 日本語 | 한국어 | Español | Português | Русский

browsa — расширение Chrome MV3 с чат-UI в боковой панели. Исходники расширения не требуют сборки — Chrome загружает каждый JS-файл напрямую; единственная сборка (build/build.mjs) бандлит вендорские библиотеки через esbuild.

Путь одного сообщения

page content scripts (MAIN-world interceptors / ISOLATED-world selection toolbar)
        │  chrome.runtime.sendMessage / port
        ▼
background.js (service worker, single message router)
        │  handle() switch — case bodies live in lib/handlers/*
        ▼
sidepanel.js (the UI orchestrator)
        ▲
        ├── browsa-nav      long-lived port: NAVIGATED / XHS pushes / SELECTION_ACTION
        ├── browsa-chat     fresh per turn: main chat stream (chunk protocol)
        └── browsa-subchat  fresh per send: detail-thread stream
  • background.js — единственный маршрутизатор chrome.runtime.onMessage. Один switch в handle() остаётся единственным экспортируемым диспетчером, который импортируют тесты; тела самых больших кейсов живут в lib/handlers/ (chat / subchat / session / семейство attach / mermaid-repair / provider-resolver / approval-relay / agent-stream-session / prompt-assembly / site-cache-store …).
  • Состояние стримов (streamPorts / streamState / chatControllers / Map-ы ожидающих одобрений …) живёт в одном экземпляре в lib/state.js, который background.js реэкспортирует — тесты, импортирующие из ../background.js, получают те же экземпляры Map.

Жизненный цикл портов: постоянный vs на каждый ход (разница намеренная)

  • browsa-nav — долгоживущий: подключается один раз при инициализации панели, переподключается после перезапусков SW по паттерну connectNavPort() (backoff 1с; у нового объекта порта должны быть заново навешены все onMessage-слушатели). Он означает «какую бы вкладку панель ни смотрела» — перерегистрация под новым tabId через NAV_FOLLOW и есть его семантика.
  • browsa-chat / browsa-subchat открываются заново на каждый ход / каждую отправку: один ход = один порт. Разрыв из-за заснувшего SW не требует самолечения — следующая отправка откроет новый. Это намеренно: жизненный цикл одного хода никогда не протекает в следующий.

Конверт ответов (историческое семейство багов — проверяйте, какой слой читаете)

  • Большинство обработчиков: успех { ok: true, data } / брошенная ошибка { ok: false, error, code, hint }.
  • Некоторые (APPROVAL_RESPOND / CLARIFY_RESPOND …) ловят сбои реле внутренне и возвращают внутренний { ok, ... } внутри data.
  • Читайте конкретный кейс, прежде чем делать предположения. Неправильное прочтение конверта — целое семейство реальных багов, найденных в общеремозиторном проходе 2026-08 (например, LOAD_SESSION возвращает -1 при промахе — 0 это легальная пустая сессия; внутренний res.data.ok — настоящий флаг успеха).

Фоновые стримы при переключении сессий (2026-09-24)

Переключение разговоров больше не отменяет идущий ход. Правило маршрутизации в одну строку: пока панель смотрит на стрим, ход пишет в ЖИВУЮ историю (bg === false); как только ход уходит в фон, он пишет в снимок ИСХОДНОЙ сессии. persistTurnEntry — единственный писатель; REASSIGN_STREAM_SESSION (автосохранение при переключении прочь) и STREAM_PEEK (переподключение при возврате) — единственные два сообщения, меняющие bg. Прерванный ход с уже стримленным текстом спасается как { interrupted: true } в исходную сессию (Esc / idle-таймаут / обрыв сети); явное уничтожение истории использует salvage: false.

Подводные камни MV3 service worker (читайте до написания SW-кода)

  • SW засыпает после ~30с простоя. Модульные Map-ы сбрасываются при каждом перезапуске — никогда не храните там долговечное состояние.
  • setTimeout внутри SW ненадёжен после возврата из обработки сообщения — используйте chrome.alarms или chrome.storage.session.
  • Регистрация слушателей по требованию — доменный стиль: три слушателя chrome.webNavigation регистрируются, только пока существует navPort, tabs.onRemoved регистрируется по требованию, будильник stream-GC существует, только пока streamState непуст — иначе каждая навигация/закрытие вкладки холодно стартуют SW (парсинг модуля 644KB) ради no-op.
  • chrome.storage.session переживает перезапуски SW в рамках одной сессии браузера (восстановление site-кэшей, ожидающие SELECTION_ACTION …).

Переключение вкладок

chrome.tabs.onActivated не должен трогать DOM — Chrome сохраняет документ боковой панели живым при переключениях вкладок. Обновляйте currentTabId и текст page-meta, отправляйте NAV_FOLLOW. Прерывания стрима адресуют СОБСТВЕННУЮ вкладку стрима (streamTabIdOf()), никогда текущую вкладку панели.

Карта модулей

Путь Ответственность
background.js SW; диспетчеризация handle() + инлайновые мелкие кейсы (ATTACH_PAGE …)
lib/handlers/* Тела больших кейсов: chat / subchat / session / attach-* / approval-relay / provider-resolver / stream-dispatch / agent-stream-session / site-cache-store / attach-store / attach-modes
lib/state.js Map-ы состояния стримов + протокол pushChunk + терминальные надгробия (tombstones)
lib/llm-client.js Слой сетевых протоколов: четыре стрима на одном скелете openSseStream()
lib/message-builder.js Формы запросов по провайдерам + ageStaleAttachments
lib/agent-turn.js / lib/image-budget.js Общий слой агентских ходов (бюджет/backfill текста/изображений)
lib/prompt-assembly.js CAPABILITY_HINTS_ENTRIES — единственный источник текста render-контракта
lib/storage.js обёртка chrome.storage.local; глобальная история + разделённые ключи сессий
lib/sidepanel/* 26 UI-модулей (конвейер рендеринга, панель сессий, detail thread, таймлайн …)
lib/content-scripts/* Сайтовые перехватчики в MAIN world + панель выделения в ISOLATED; знание по сайтам в SITES.md
lib/page-extractor.js … Слой прикрепления/экстракции (каскад reader/dom/full/auto + сайтовые fast-path + хэндоффы PDF/Office/ASR)

Авторитетные версии: Architecture (англ.) / Architecture-zh (кит.) — снимок первичного перевода ИИ, синхронизирован 2026-10-01.

Clone this wiki locally