サイト内検索

Cua Driver 0.26.1 の Computer Use 内部実装を読む — ウィンドウへの宛先固定と、操作結果・事後条件検証の分離

重岡 正 · Wed, September 9, 2026

先日、Hermes Agent v2026.9.7 の Computer Use 内部実装を読むOpen Interpreter rust-v0.0.42 の Computer Use 内部実装を読む の 2 本を書きました。どちらもエージェント側の実装であり、実際の OS 操作は外部の cua-driver に委譲していました。委譲先で何が起きているのかを追わない限り、Computer Use の全体は見えません。この記事はその続きにあたる、trycua/cua モノレポの commit b4e3caec に含まれる Cua Driver 0.26.1 を対象にしたコードリーディングノートです。

対象はモノレポ全体ではなく、主に libs/cua-driver 配下です。同じモノレポには Python の ComputerAgent など、エージェント側の実装も別に存在します(agent.py L902–941)。「cua は LLM を持たない」と一括りにすると不正確なので、この記事ではモデル非依存の native 操作基盤としての Cua Driver に絞ります。以降のコードリンクは上記 SHA に固定しています。今回は GUI 操作、driver のインストール、Rust ビルドやテスト実行は行わず、公開ソースの静的調査にとどめました。

結論を先に書くと

この版の Cua Driver の読みどころは、操作 API の種類ではなく 「どの対象へ送ったか」「何が観測できたか」「何を成功とみなせるか」を分ける設計 にあります。

節の並びとは別に、コードを追って印象に残った要点を 6 つに絞ると次のとおりです。

  • MCP・CLI・同一プロセス SDK という 3 つの入口があり、共通の native dispatch へ集約する
  • PID しか渡されていない window 操作は、候補の列挙と ambiguous_window_target 拒否で入力前に落とす
  • 要素参照は element_token として snapshot に結び付ける。同一ウィンドウの再取得で前の token は即座に失効させる
  • macOS の Accessibility API・Windows の UI Automation・Linux の AT-SPI、座標入力、ブラウザーの DevTools Protocol 経路を使い分ける
  • 入力 API が受理したことを、そのまま confirmed にしない。ActionResultverify_state を明確に分ける
  • background 操作は共通契約と OS・アプリ・入力方式の成立条件を分けて読む必要がある

以下、それぞれをソースに当てながら書きます。本文では「この版」「静的調査で確認できた範囲」を主語にして、他の cua コンポーネント(VM lifecycle を扱う Lume や Python ComputerAgent)まで一般化しないよう気を付けています。

何を固定して読んだか

調査開始時、ローカル HEAD と git ls-remote origin refs/heads/main の両方が b4e3caec を指しました。作業ツリーは変更がなく、その状態のファイルを読みました。

libs/cua-driver/rust/Cargo.toml の workspace version は 0.26.1 です。一方、公開 tag cua-driver-rs-v0.26.1 は別 SHA cc542544 を指しており、GitHub API では prerelease=truepublished_at=2026-09-10T12:15:53Z でした(Cargo.toml L19–25)。

installer 側では、stable チャネルでも明示的な version pin があればそれを優先し、なければ baked version、それもなければ API 解決へ進む分岐があります(_install-rust.sh L606–619install.ps1 L1025–1058)。したがって GitHub UI の prerelease バッジと「公式 installer で選ばれる版」は別の情報です。記事の再現性を担保したい場合、読んだソース SHA、driver バイナリの版と hash、接続した daemon の版・起動方法・permission mode を分けて記録するのが安全です。

全体像

処理経路は 2 段に分けて描くと見通しがよくなります。まず 3 つの入口が native dispatch へ集約する流れ、続いて共通 core と OS backend へ降りる流れです。

flowchart TD
    H[Hermes computer_use wrapper] --> M[cua-driver mcp]
    O[Open Interpreter cua-driver CLI] --> C[cua-driver call]
    P[Python / TypeScript SDK 利用者] --> S[CuaDriver SDK]
    M --> D{起動形態と OS}
    D -->|通常の macOS / socket 指定| X[MCP proxy - daemon]
    D -->|通常の Windows / Linux など| R[同一プロセス runtime]
    C --> X
    X --> A[SdkDaemonAdapter]
    A --> S
    S --> R
    R --> T[ToolRegistry.invoke_authorized]
    T --> N[引数正規化・認可・session namespace]
    N --> W[対象解決・platform tool]
    W --> OS[AX / UIA / AT-SPI / native input / CDP]
    OS --> F[ActionExecutionRecord から公開結果契約へ]

Hermes と Open Interpreter からの矢印は、前 2 記事が扱った版を基にした接続位置の確認です。今回それらの最新版を再調査したという意味ではありません。

責務を分けると次のようになります。

領域主な責務
libs/cua-driver/rust/crates/cua-driverCLI・MCP・daemon
…/cua-driver-sdknative runtime の生成・接続、SDK 境界
…/cua-driver-coretool registry、認可、session、token、検証、browser 共通実装
…/cua-driver-contracttyped input / output、公開契約
…/platform-macosAX、ScreenCaptureKit、CGEvent
…/platform-windowsUIA / MSAA、Windows 入力、capture
…/platform-linuxAT-SPI、X11、Wayland

mcp が常に daemon proxy とは限らない

mcp_uses_direct_runtime_for() は起動 flag と OS で経路を分けます(main.rs L389–425)。

  • --direct--socket の同時指定はエラー
  • --direct なら direct runtime
  • embedded host なのに private socket 指定がなければ拒否
  • 通常の macOS では proxy を使い、LaunchServices と TCC の帰属を保つ
  • macOS 以外で socket 指定も history preview 条件もない通常経路は direct runtime

「MCP は必ず daemon へ転送する」と描くと、Windows や Linux の通常経路とずれます。proxy 側の tools/list は daemon から取得した inventory を返し、tools/callDaemonRequest へ変換し、tool エラーと transport エラーを区別します(proxy.rs L697–829)。

CLI の call は service-backed

run_call() は毎回独立した native runtime を作らず、既存 daemon への接続を確認したうえで版の互換性を検査してからリクエストを送ります。コメントには policy・session state・cache・platform identity の適用点を一つにする意図が明記されています。前回の Open Interpreter 記事で触れた「シェル経由でも状態がどこに残るのか」の続きに直結する挙動です(cli.rs L2255–2395)。

1 回の要求:get_window_state から click まで

説明用の要求例です。PID、window ID、token、ラベルは実機観測で置き換える前提で、このまま実行して成功した記録ではありません。まず list_windows などで対象を選び、次のように状態を取得します。

{
  "pid": 1234,
  "window_id": 5678,
  "session": "article-research",
  "include_screenshot": true
}

tool 名は get_window_state で、返ってきた要素の element_token をそのまま次の click に使います。

{
  "pid": 1234,
  "window_id": 5678,
  "session": "article-research",
  "element_token": "s00000001:12",
  "delivery_mode": "background"
}

element_token は形式を理解しても自作しません。上の文字列は説明用のダミーです。呼び出しの大筋はこうなります。

  1. CLI / MCP / SDK が要求を native runtime へ渡す
  2. ToolRegistry::invoke_authorized() が予約済み内部引数を除去し、alias や typed target を正規化する
  3. 認可 context、policy、manifest、失効状態を検査する
  4. public session 名を内部 namespace に変換する
  5. wrapper / platform tool が対象ウィンドウと要素参照を解決する
  6. macOS なら cached AX element へ action を送るか、window-local pixel 入力を行う
  7. native 結果から実行記録を作り、公開 ActionResult へ射影する
  8. 呼び出し側が結果を読み、必要なら verify_state や fresh capture を明示的に呼ぶ
  9. ハーネスがタスクの終了・再観測・別経路への移行を判断する

ここで重要なのは、click の成功結果だけで verify_state が自動的に走る、という説明にはしないことです(tool.rs L1035–1158tool.rs L1538–1630)。

対象ウィンドウの曖昧さを、入力前に落とす

共通の WindowTargetGuard は、window-scoped tool について PID しか渡されていない場合の候補を列挙します(window_target.rs L104–159)。

対象候補処理
0 件window_target_not_found
1 件その window_id を補って dispatch
複数ambiguous_window_target と candidate 一覧を返して拒否

明示的な window ID、element token、desktop scope 指定は、この PID 補完とは別の解決経路に進みます。「適当な前面ウィンドウを選ぶ」実装ではないので、複数候補ならハーネスが再選択できます。テストも ambiguous ケースで内部 tool が呼ばれないことまで確認しており、単なるエラーテキスト生成ではなく 副作用前の拒否 という位置が重要です(window_target.rs L201–265)。テストは今回ソースの存在を確認しただけで、実行はしていません。

macOS では WindowServer 側の owner も調べる

get_window_state は AX tree walk の前後で、指定 window ID の owner PID を検査します。Open / Save panel のように別プロセスが所有する画面が背景にあり、アプリの PID と見えている window の owner が一致しないケースを想定した処理です。別 surface の tree を正常結果として混ぜないための対策で、ソースコメントで動機が明記されています(get_window_state.rs L175–374)。

element_token:番号ではなく snapshot への参照

要素番号 12 だけでは、再取得後も同じボタンを指す保証がありません。この版は element_token(または snapshot_id + element_index)を使います。

  • token は snapshot ID と element index を含むが、公開利用者は opaque 参照として扱う
  • registry の key は runtime scope と PID
  • 同じ window の新 snapshot を登録すると、前の snapshot は即座に失効する
  • 別 window の snapshot も保持するが、runtime / PID ごとに最大 8 件に制限する
  • stale token、別 runtime generation、token と明示 window / index の衝突はエラー
  • このリゾルバは bare element_index 単独を snapshot_id_required で拒否する

根拠は register_snapshot_entry()lane.retain(...)resolve_element_args_wide() の分岐にあります。冒頭コメントには歴史的な説明が残っているため、現行挙動は関数本体とテストで確認しました(element_token.rs L142–234element_token.rs L445–532element_token.rs L625–870)。

ただし token が保証するのは、driver が持つ snapshot 参照の整合性です。画面の状態変化を自動検出して全 token を失効させるわけではありませんし、runtime / PID namespace は session ごとの完全な GUI 隔離ではありません。同じ runtime 内の別呼び出しによる再取得が先の token を失効させることもあります。記事的な要約としては「古い番号を新しい tree の別要素に黙って読み替えない仕組み」と説明するのが適切です。

capture:tree と画像の両方、ただし完全な同時 snapshot ではない

macOS の get_window_state は既定で tree とスクリーンショットを返します。capture_mode は deprecated で、include_accessibility_treeinclude_screenshot が取得対象の切替に使われます。両方 false は無意味な要求として扱われ、screenshot_out_file には別途 capture を強制する扱いもあります(get_window_state.rs L175–374)。

AX walk には 20000 ms の待機制限、要素数や深さの cap があります。ただし spawn_blocking の handle を drop しても native AX 呼び出し自体はキャンセルできない点がコメントに明記されています。「20000 ms で OS 処理も必ず停止する」とは書けません。tree 取得、cache 更新、画像取得の順で進むため、tree と画像は同じ window に結び付きますが、同一瞬間の原子的 snapshot ではありません。

macOS の window capture の主経路は ScreenCaptureKit です。window filter と configuration のプランを短時間再利用し、SCScreenshotManager によって新しい画像を取得します。失敗時は screencapture -l ... の互換 fallback があります。キャッシュは TTL 2000 ms・capacity 32 の capture プラン であって、古い画像を返し続ける frame cache ではありません。この区別は性能説明で間違えやすい点です。実測のレイテンシー改善率は今回測っていません(capture.rs L349–443capture.rs L625–749)。

Retina・resize と入力座標

返す画像が縮小されていたら、resize ratio を PID と window ID に対応付けて記録します。pixel click はその倍率を戻し、window の origin と backing scale を使って screen 座標へ変換します。概念的には次の順です。

返された画像上の pixel
  → resize 前の window screenshot pixel
  → backing scale を除いた window-local point
  → window origin を足した screen point

frame が得られないときに勝手に screen-absolute 座標へ読み替えず、capture の縦横と window bounds から妥当な scale を検査し、background 入力では window 外の点も拒否します。前回の Hermes 記事で扱った bounds_scale と重ねる場合、要素の bounds と画像上 pixel を区別し、上流でも下流でも同じ倍率を掛ける二重変換の案内にしないよう注意します(get_window_state.rs L376–464click.rs L800–857px_frame.rs L1–142)。

background の実体を OS ごとに読む

macOS:AX action と targeted event は別の経路

要素指定の click には AX action を実行する経路があります。perform_ax_action() は AX API の返値を扱いますが、API が成功を返しても実際の UI 変化を保証するものではありません(ax_actions.rs L215–230)。

ClickTool は結果を 3 種に分けます。selection readback で確認できた場合は confirmed、action を advertise しない要素などでは suspected_noop、一般的に効果を読めない場合は unverifiable です。selection のような例外があるので「クリックは常に検証不能」とも書けません(click.rs L686–746)。

pixel / keyboard 側には SkyLight SPI bridge があり、シンボルを動的解決します。Chromium・Catalyst 向けの event 処理があり、利用できない場合は public な post_to_pid へ fallback する経路も見えます。macOS backend を「Accessibility API だけ」と説明すると取りこぼしがあります。SPI が使えるか、実際に対象が受け付けるかは OS・アプリごとの検証が必要です(skylight.rs L1–85mouse.rs L438–499)。

Windows:UIA・PostMessage・入力注入を分ける

Windows の click 周辺には semantic action、targeted injection、PostMessageW などの分岐があります。background element click の内部記録は、実際に選んだ transport と fallback を持ちます。画面に現れた結果を独立して読めなければ unverifiable を返します(impl_.rs L2951–3053)。

PostMessageW の mouse 経路と、SendInput を使う keyboard 経路は別の関数に実装されており、高 integrity の対象、foreground 取得、入力したイベント数などを扱います。「Windows は全部 PostMessage で背景操作できる」「UIA があるから前面化は不要」といった一般化はできません(mouse.rs L84–150keyboard.rs L408–481)。

注目したい分岐は finish_pixel_uia_attempt() です。UIA point invocation が Miss を返した場合のみ次経路へ進み、Busy / Timeout / Unavailable では fallback 入力を送りません。応答待ちの失敗と操作未実施を同一視して再送しないための例として使えます。ただし全 tool・全 transport の exactly-once 保証まで拡張はできません。

Windows の capture も一律 PrintWindow ではありません。現行関数は最小化 window をまず拒否します。既知の XAML / WinUI / UWP 対象では Windows.Graphics.Capture を先に試し、失敗時は screen-region BitBlt、その後 PrintWindow へ進む分岐があります。他の経路では PrintWindow が黒くなった場合の WGC / BitBlt fallback もあります。screen-region capture は上に重なった別 window の影響を受けうるため、occlusion 情報を扱います。WGC と screen-region capture を同じ「背景スクリーンショット」とまとめず、取得元を区別する必要があります(capture.rs L404–480capture.rs L612–674)。

Linux:X11 と Wayland を一括りにしない

X11 の window capture は MIT-SHM、persistent XGetImage、ImageMagick fallback という経路を持ちます(capture.rs L1–102)。X11 の pixel click には、利用可能なら MPX / uinput の virtual pointer を使い、条件に応じて XSendEvent へ進む実装があります。ただし uinput_unavailable の一部エラーはそのまま返すため、「あらゆる失敗を XSendEvent で救済」とは書けません(impl_.rs L2033–2083)。

一般的な Wayland 入力では、未 focus の特定 window へ同じ方法で送れるとは限りません。一方、この版には Hyprland の専用経路を使う条件分岐もあります。したがって「Wayland では背景操作が絶対不可能」とは断言せず、compositor・plugin・surface を固定して検証する必要があります。今回 Linux は主要分岐の補足確認までで、全 compositor を追ったものではありません(impl_.rs L1694–1716)。

ActionResult と verify_state の分離

この版の Cua Driver で最も面白いのはここです。「成功」を層で分けています。

段階何が分かるか分からないこと
transport / tool envelope要求が届いたか、tool がエラーを返したか操作の効果やタスク完了
ActionResult操作 route、delivery、効果に対する根拠の強さ利用者の目的全体を達成したか
VerifyStateOutput呼び出し側が渡した事後条件が満たされたか条件が本当にタスク全体を代表するか

proxy も tool failure と transport failure を区別し、tool がエラーでなくても effect=unverifiable はありえます(action-result-contract.md L1–132)。

公開 action contract は effectroute を必須とし、delivery、evidence、escalation を追加します。

effect読み方
confirmed公開可能な readback や window-change evidence がある
partial配信の一部を確認した。delivered count を伴う
unverifiable実行側で効果を確認できない。成功にも失敗にも自動変換しない
suspected_noop効果がなかった可能性を示す
refused拒否。delivery / evidence を成功したかのように付けない

これは closed contract であり、旧 verified boolean、座標、selector、内部 transport 診断などは公開 action 結果に含めない設計です。

platform の JSON をそのまま読まない

macOS や Windows の tool 本体には、まだ verifiedpath・旧 escalation.recommended などを組み立てるコードがあります。しかしそれがそのまま公開結果にはなりません。共通 registry は platform 結果の後に ActionExecutionRecord::from_legacy() などを経由し、publish_action_result() で公開 contract へ射影し、typed output を検査します。内部に "confirmed" と書いてあっても、信頼できる公開 evidence がなければ unverifiable に下げる処理があります(tool.rs L1538–1630action_record.rs L501–514)。

flowchart LR
    A[OS API の戻り値・内部診断] --> B[ActionExecutionRecord]
    B --> C[公開 ActionResult]
    C --> H[ハーネスの判断]
    H -->|条件を指定| V[verify_state]
    V --> S[satisfied / unsatisfied / unknown]
    S --> H
    H -->|必要時| I[fresh screenshot をモデルが読む]

「ソース内に verified が残っているから廃止されていない」とは結論しません。producer の内部表現と、外部へ出す contract を分けて追うことが必要です。

verify_state は小さな述語評価器

契約には、window の存在・bounds、element selector の role / label、value / enabled / selected などがあります。1 個から 8 個の述語を AND で評価します。既定 timeout は 5000 ms、設定上限は 10000 ms、安定確認は既定 2 sample・指定範囲 1 から 5 です。説明用の例です。

{
  "pid": 1234,
  "window_id": 5678,
  "session": "article-research",
  "expect": [
    {
      "element": {
        "selector": {"role": "AXTextField", "label_contains": "Result"},
        "value_equals": "42"
      }
    }
  ],
  "timeout_ms": 5000,
  "stable_samples": 2,
  "include_screenshot": true
}

loop は観測、述語評価、連続成功回数の確認を行い、必要なら 100 ms を上限とする待機を挟みます。終了時点で条件が一度だけ満たされても必要 sample 数に届いていなければ、unknown / stability_unproven にします。ここで注意したいのは、この関数が provider.observe(...).await 全体を同じ deadline の timeout で包んでいないことです。設定の「最大 10000 ms」は polling 予算であって、native observation を含む厳密な wall-clock 上限とは書けません(expectation.rs L224–334verification.rs L115–187)。

信頼できない source、観測不足、値判定での複数一致などは unknown になります。全要素を網羅した tree とは限らないため、element.exists=false の入力を拒否します。見つからないことから「存在しない」を安易に証明しない設計です。単に存在確認だけなら複数一致でも成立する場合があるので、「複数一致は必ず unknown」ともしません(expectation.rs L493–610)。

include_screenshot=true の画像は、述語結果を作った後で別の観測として追加します。driver はその画像を解釈しません。画像取得の失敗が、すでに得られた述語結果を必ずエラーに変えるわけでもありません。「画像付きなら画像で成功を検証済み」とは書けません。

escalation は助言

公開 contract の escalation.targetpixel / foreground / page / session です。ハーネスが再観測して、その一段へ進むかを決めます。OS backend 内の低レベル fallback と、ハーネスによる次の操作は別の話です。unverifiable を「失敗したので同じ click を再送」に直結させると、二重入力の危険があります。次の観測を挟む判断が必要になりますが、その運用を強制する LLM loop は Cua Driver 自身ではありません。

session と権限:3 層を混ぜない

run_call() は明示的な非 default session 名があると cli-explicit という transport ownership namespace を使い、session 名がなければ cli-<UUID> を作って応答後に session_end を送ります。連続 CLI 操作では、同じ public session label を一貫して渡すことが重要になります。共通 registry は public session label をそのまま全内部 state の key にせず、認可後に runtime の namespace へ変換し、終了済み session への操作は session_ended で拒否します。

ただし、これを「session 名さえ違えば同じデスクトップ上で入力が完全に独立する」とは表現しません。画面・OS 入力装置・対象アプリは共有されえます。共通 registry には同一 PID への text mutation の競合を拒否する仕組みもあり、同時実行の成立範囲は tool や platform に依存します。

権限は次の 3 層で読みます。

主な役割この記事での位置
エージェント側利用者の意図、command / tool 実行の承認、再試行判断前 2 記事の対象。driver 認可とは別
Cua Drivermode、manifest、policy、session 認可、保護対象の grantnative dispatch の共通境界で適用
OS・デスクトップ環境macOS TCC、Windows integrity / UIAccess、Linux input や compositor の制約driver の mode だけでは代替できない

authorize_tool_call_with_context() は hard invariant、期限切れ、policy、risk classification、manifest などを検査します。MCP だけでチェックして同一プロセス SDK が抜け道になる構成を避けるため、native registry を共通の適用点にしています。mode は standard / bounded / unrestricted の 3 種で、standard は通常 automation の promptless default、bounded は reviewed manifest の範囲に限定、unrestricted は trusted startup での明示的な bypass 設定が必要です。OS 制約まで消えるという意味ではありません(authorization.rs L1121–1205authorization.rs L1207–1312)。

macOS で通常の MCP 経路が daemon に寄る理由の一つは、TCC の責任主体を保つためです。app bundle・embedded host・direct MCP の起動形態を混同しない、というのは実装上の注意点として重要です。

前 2 記事からの差分と、未検証で残したこと

Hermes / OI 側の記述は、それぞれの記事執筆時点の版に基づく比較です。今回、上流の最新版を再調査したわけではありません。

観点Hermes 記事Open Interpreter 記事今回の Cua Driver 調査
読んだ境界専用 Computer Use wrapperQA skill と汎用 command executionnative runtime、共通 core、OS backend
driver への入口MCP を backend が利用外部 CLI を利用MCP / CLI / in-process SDK が合流
対象の保持wrapper の sticky targetCLI 引数・session の扱いwindow guard、runtime / session namespace、snapshot token
観測wrapper が画像と要素を整形CLI 結果と画像表示の橋渡しwindow tree、native capture、座標 frame
成功判断wrapper が verdict を形成skill が verify 手順を指示action fact と caller-defined postcondition を分離
次の行動Hermes 側 loopOI 側モデルと command loopdriver は結果・助言を返し、ハーネスが決める

今回の版へ写す前に確認しておきたい点は次のとおりです。

  • Hermes 記事の effect + verified を、現行 driver の公開 contract にそのまま当てはめない。今は ActionResultVerifyStateOutput に分離されている
  • escalation.recommended="px" と公開 contract の escalation.target="pixel" を混同しない
  • 現行リゾルバは bare element_index 単独を受け付けない。上流 adapter が element token / snapshot ID をどう渡すか要確認
  • capture_mode の歴史的説明と、現在の include_* による取得制御を分ける
  • 通常の macOS MCP proxy と Windows / Linux direct runtime の違いを反映する

これらは API 差分として確認できますが、「Hermes v2026.9.7 は現行 driver で必ず壊れる」という結論にはしていません。上流 adapter の capability 追従や fallback もあるため、組合せを固定した別検証が必要です。

次に実機で確かめたいこと

コード上の結論と runtime 挙動を混同しないために、次はエージェント経由ではなくホストの端末から追試するつもりです。デイリースタンドアップやチームミーティングで結果を共有して、次に検証する人が、未確認の挙動と再現条件を把握できるように残しておきます。

  • 同一 PID で複数 window の状態で PID のみの click 要求を投げ、ambiguous_window_target が入力前に起きるか
  • snapshot A から B へ切り替えたあと、A の token で stale 拒否が返ることと、別要素へ誤解決しないこと
  • Open / Save panel などで owner PID が違うケースの拒否手順
  • 背面電卓を別 window を前面にしたまま操作した場合の前面 window・cursor・対象の変化
  • generic click で tool 成功と unverifiable が併存することの確認
  • selection / set value のような readback 可能な UI で confirmed の根拠と verify_state の関係
  • Retina・resize が異なる scale の設定で画像から選んだ pixel が同じ位置へ届くか
  • Windows XAML の被覆・非被覆・最小化と WGC / fallback の条件
  • 同一 session 名を明示した連続 CLI と省略した場合の lifecycle state 差
  • 副作用直後に応答が失われる条件を安全なテストアプリで再現し、再送されないことを確認
  • 日本語 IME で type_text と物理 key input の違い

以上、Cua Driver 0.26.1 のソースを固定して読み、native dispatch への集約・window guard・element_token・macOS / Windows / Linux の background 入力・ActionResult と verify_state の分離・permission mode の 3 層、という設計上の分かれ道を整理した、現場からお送りしました。

参考情報