Skip to content

Architecture ja

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

アーキテクチャ

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

browsa はサイドパネル型チャット UI を持つ Chrome MV3 拡張。拡張のソースにはビルド工程がない — すべての JS ファイルを Chrome が直接読み込む。唯一のビルド(build/build.mjs)はベンダーライブラリを esbuild でバンドルするもの。

1 つのメッセージの旅

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 の唯一のルータ。1 つの handle() switch が、テストが import する単一の export ディスパッチャとして残っており、最大級の case 本体は 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 に 1 回だけ定義され、background.js が再 export する — ../background.js から import するテストは同じ Map インスタンスを受け取る。

ポートのライフサイクル: 恒久 vs ターン毎(意図的に異なる)

  • browsa-nav は長寿命: パネルの init 時に 1 回接続し、SW 再起動の後は connectNavPort() パターンで再接続する(1 秒バックオフ。新しいポートオブジェクトには onMessage リスナーをすべて付け直す必要がある)。意味するところは「パネルが監視しているタブ」 — NAV_FOLLOW で新しい tabId の下に再登録するのがまさにそのセマンティクス。
  • browsa-chat / browsa-subchat はターン毎/送信毎に新規オープン: 1 ターン = 1 ポート。SW のスリープに起因する切断に自己修復は不要 — 次の送信が新しいポートを開ける。意図的: ターンのライフサイクルが次のターンへ漏出しない。

レスポンスエンベロープ(過去のバグファミリ — どの層を読んでいるか確認すること)

  • ほとんどのハンドラ: 成功 { ok: true, data } / 送出されたエラー { ok: false, error, code, hint }。
  • 一部(APPROVAL_RESPOND / CLARIFY_RESPOND など)はリレー失敗を内部で捕捉し、data の内側に内層の { ok, ... } を返す。
  • 決めつける前に実際の case を読む。エンベロープの読み違えは、2026-08 の全リポジトリ sweep で見つかった現実のバグファミリそのもの(例: LOAD_SESSION はミス時に -1 を返す — 0 は合法的な空セッション。真の成功フラグは内層の res.data.ok)。

セッション切替をまたぐバックグラウンドストリーム(2026-09-24)

会話の切替は、実行中のターンをもうキャンセルしない。ルーティング規則は 1 行で: パネルがストリームを見ている間、ターンは LIVE 履歴に書き込み(bg === false)、バックグラウンドに回った時点で ORIGIN セッションのスナップショットに書き込む。 書き手は persistTurnEntry の 1 か所だけ。bg を変更するメッセージは、REASSIGN_STREAM_SESSION(切替時の自動保存)と STREAM_PEEK(切替戻り時の再アタッチ)の 2 つだけ。ストリーム済みテキストのある中断ターンは { interrupted: true } として origin に救済される(Esc / アイドルタイムアウト / ネットワーク断)。明示的な履歴破棄は salvage: false を使う。

MV3 service worker の注意点(SW 側コードを書く前に読むこと)

  • SW は約 30 秒のアイドルでスリープする。モジュールレベルの Map は再起動のたびにリセットされる — 恒久的な状態をそこに置かないこと。
  • SW 内の setTimeout は、メッセージ処理が返った後は信頼できない — chrome.alarms か chrome.storage.session を使う。
  • オンデマンドのリスナー登録がハウススタイル: chrome.webNavigation の 3 リスナーは navPort が存在する間だけ登録され、tabs.onRemoved は必要時に登録され、ストリーム GC アラームは streamState が空でない間だけ存在する — さもないとすべてのナビゲーション/タブクローズが、何もしないだけのために SW をコールドスタートさせる(644KB のモジュールパース)。
  • chrome.storage.session は、ブラウザセッション内なら SW 再起動をまたいで生き残る(サイトキャッシュ復元、保留中の SELECTION_ACTION …)。

タブ切替

chrome.tabs.onActivated は DOM に触れてはならない — Chrome はタブ切替をまたいでサイドパネルのドキュメントを生かし続ける。currentTabId とページメタテキストを更新し、NAV_FOLLOW を送る。ストリームの中断はストリーム自身のタブ(streamTabIdOf())に向ける。パネルの現在タブに向けてはならない。

モジュールマップ

パス 責務
background.js SW。handle() ディスパッチ + インラインの小さな case(ATTACH_PAGE …)
lib/handlers/* 大きい case 本体: 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 プロトコル + 終端トゥームストーン
lib/llm-client.js ワイヤプロトコル層: 1 つの openSseStream() 骨格の上に 4 ストリーム
lib/message-builder.js プロバイダ毎のリクエスト形状 + ageStaleAttachments
lib/agent-turn.js / lib/image-budget.js 共有エージェントターン層(テキスト/画像のバジェット/バックフィル)
lib/prompt-assembly.js CAPABILITY_HINTS_ENTRIES — 描画契約テキストの唯一のソース
lib/storage.js chrome.storage.local ラッパ。グローバル履歴 + 分割セッションキー
lib/sidepanel/* UI 側 26 モジュール(描画パイプライン、セッションドロワー、ディテールスレッド、タイムライン …)
lib/content-scripts/* MAIN ワールドのサイトインターセプタ + ISOLATED 選択ツールバー。サイト毎の知識は SITES.md
lib/page-extractor.js … 添付・抽出層(reader/dom/full/auto カスケード + サイト高速パス + PDF/Office/ASR ハンドオフ)

権威版: Architecture(英語) / Architecture-zh(中文) — AI による初翻スナップショット、同期日 2026-10-01。

Clone this wiki locally