ANTIGRAVITY LABEN
記事一覧/Agents & Manager
Agents & Manager/2026-05-26中級

Antigravity の Browser Sub-Agent が SPA を空ページと誤認する原因と待機戦略

Antigravity の Browser Sub-Agent に SPA ダッシュボードを読ませると、本文が描画される前に get_page_text が返ってきて空ページ扱いされる。原因と、私の運用で安定した3つの待機パターンを整理しました。

antigravity436browser-sub-agent3spatroubleshooting88agents90wait-strategy

Antigravity の Browser Sub-Agent に AdMob 管理コンソールを読ませた朝、get_page_text の戻り値が「Sign in」と「Loading…」だけだったことがあります。ブラウザの URL バーは確かにダッシュボードに進んでいるのに、エージェントから見ると本文がまだ何もない状態でした。タスクは「先週から eCPM が下がっているネットワークを抽出して」だったので、エージェントは空のテキストを根拠に「該当なし」と回答してしまいます。

2014年から累計5,000万ダウンロードのアプリ群を一人で運用してきた中で、AdMob・App Store Connect・Stripe Dashboard のような SPA ダッシュボードを Sub-Agent に読ませる場面が増えてきました。最初は同じ失敗を週に何度か繰り返していましたが、原因を分解してみると待機戦略のミスマッチに集約できることが分かってきました。Dolice Labs の4サイト並行運用でもこの設計は転用できているので、私の運用で落ち着いた診断手順と、3つの安定した待機パターンを書き残しておきます。

「空ページ」に見える3種類の症状を切り分ける

最初に大事なのは、Browser Sub-Agent が返してくる「空ページ」が実は3つの別々の状態だという点です。これを切り分けないまま wait の秒数だけ伸ばしても、安定はしません。

1つ目は、SPA のシェルだけが届いている状態です。HTML には <div id="root"></div> と最低限のメタタグしかなく、本文はクライアント JS が後から差し込みます。get_page_text の結果が極端に短く、Loading…Sign in to continue のような汎用文字列しか含まないときはこの状態です。

2つ目は、認証ガードに弾かれてログインページに戻っている状態です。document.title がアプリ名のままでも、本文は「Sign in」フォームだけになっています。先週ログインしたつもりでもセッションクッキーが切れているケースで、AdMob では7日、App Store Connect では8時間ほどで切れることが多いです。

3つ目は、本文の DOM は生成済みなのに、データ部分だけが API レスポンス待ちで空になっている状態です。テーブルのヘッダーは見えていて、行が「No data」または空セルになっています。これはネットワーク待ちなので待機を増やせば直ります。

切り分けの一歩目は、必ず HTML の構造を見ることです。get_page_text だけでなく get_page_html も併用すると、上記3つは数秒で判別できます。

原因は「DOMContentLoaded」と「ハイドレーション完了」のズレ

Browser Sub-Agent のデフォルト動作は、navigate 直後に「ページがロードされた」と判断します。この判定はおおむね load イベント基準で、HTML と外部リソースの取得が終わった時点です。一方で React・Vue・Next.js の App Router のような SPA は、load のあとに JS バンドルを評価し、ハイドレーションを行い、初回のデータフェッチを終えてから初めて意味のあるテキストが画面に出ます。

つまり Sub-Agent が「読めるよ」と判断する瞬間と、人間がブラウザで「ダッシュボードが見えた」と感じる瞬間の間には、SPA では2〜6秒のギャップがあります。AdMob ダッシュボードの場合、私の手元ではメディアン3.2秒、95パーセンタイルで5.8秒ほどでした。固定の sleep で済まそうとすると、95% を救うには6秒以上必要で、その間ずっとエージェントの実行時間とトークンを浪費することになります。

これを避けるには、ページが「準備できた」ことを構造的に検知する仕組みが必要です。以下、私の運用で安定した3つのパターンを順に紹介します。

待機パターン①: 期待するセレクタが描画されるまで待つ

一番堅実で、誤検知も少ないのが「このセレクタが描画されたら本文が揃ったとみなす」明示的な待機です。Antigravity の Browser Sub-Agent でも javascript_tool 経由で MutationObserver を仕掛けるか、シンプルにポーリングで十分です。

// Browser Sub-Agent から javascript_tool で実行
async function waitForSelector(selector, timeoutMs = 10000) {
  const start = Date.now();
  while (Date.now() - start < timeoutMs) {
    const el = document.querySelector(selector);
    if (el && el.offsetParent !== null) return true;
    await new Promise(r => setTimeout(r, 200));
  }
  throw new Error(`Selector not visible: ${selector}`);
}
 
// AdMob の Mediation テーブルが描画されるのを待つ
await waitForSelector('table[data-test="mediation-waterfall"] tbody tr');

ポイントは offsetParent !== null で「DOM 上に存在し、かつ display:none ではない」ことを確認している点です。SPA は要素を先に挿入してから可視化することがあるので、これを省くと早すぎる成功判定が起きます。AdMob だと tbody tr の存在を見ていますが、行が0件の状態を「描画完了かつデータゼロ」と判別したいときは、tbody tr, [data-empty-state] のように空状態用のセレクタも OR で含めます。

待機パターン②: ネットワークアイドル + 構造的検証

セレクタが事前に決まらないページ、例えばエージェントが「画面上の最新エラーを拾って」のような曖昧な指示を実行する場合は、ネットワークが落ち着くまで待ってから構造で検証します。

// ネットワークが N ミリ秒間 idle になるまで待つ
async function waitForNetworkIdle(idleMs = 800, timeoutMs = 12000) {
  return new Promise((resolve, reject) => {
    const start = Date.now();
    let lastActivity = Date.now();
    let pending = 0;
 
    const origFetch = window.fetch;
    window.fetch = async function (...args) {
      pending++;
      lastActivity = Date.now();
      try {
        return await origFetch.apply(this, args);
      } finally {
        pending--;
        lastActivity = Date.now();
      }
    };
 
    const tick = setInterval(() => {
      const idle = Date.now() - lastActivity;
      if (pending === 0 && idle >= idleMs) {
        clearInterval(tick);
        window.fetch = origFetch;
        resolve(true);
      } else if (Date.now() - start > timeoutMs) {
        clearInterval(tick);
        window.fetch = origFetch;
        reject(new Error("Network idle timeout"));
      }
    }, 100);
  });
}
 
await waitForNetworkIdle(800);
 
// 構造的検証: 本文に最低限の見出しと表が揃っているか
const ok = document.querySelectorAll('h1, h2, h3').length > 0
        && document.querySelectorAll('table, [role="grid"]').length > 0;
if (!ok) throw new Error("Page structure not ready");

fetch のラップは XHR の検出を取りこぼすので、画面が XHR ベースの古い実装に依存しているなら XMLHttpRequest.prototype.send も同じ要領でフックします。私は App Store Connect の一部画面で XHR フックを追加して安定しました。

待機パターン③: リトライ前提のエージェントプロンプト

仕組み側で頑張りすぎないという選択肢もあります。Browser Sub-Agent への指示テンプレートに「get_page_text の結果が短い・キーワードを含まない場合は3秒待ってリトライ。3回失敗したら状況を要約して報告」を組み込んでおく方法です。

# Sub-Agent への共通プロンプト断片
 
ページを読み取るときは次のルールに従ってください。
 
1. navigate の直後に get_page_text を呼ぶ。
2. 戻り値の長さが 200 文字未満、または "Loading" / "Sign in" のみを含む場合は、
   3秒待って再度 get_page_text を呼ぶ。最大3回まで。
3. 3回失敗した場合は、現在の URL・タイトル・先頭500文字を添えて
   「未描画の可能性」と報告し、人間の判断を仰ぐ。
4. 期待するキーワード(例: "Mediation", "Waterfall")が含まれていれば成功と判断する。

このパターンは実装コストがほぼゼロですが、トークン消費は増えます。私の運用では、頻度の高い AdMob・App Store Connect・Stripe Dashboard はパターン①のセレクタ待機、月に一度しか開かない管理画面はパターン③のリトライ前提、という形で使い分けています。

再発防止のために運用に組み込んでおくこと

待機パターンを整えるだけでなく、運用側で2点だけ習慣化すると安定度がぐっと上がります。1点目は、Sub-Agent の戻り値に対する「明らかに短い」「キーワードが完全に欠落している」の判定を必ずエージェントの責務にすることです。生のテキスト長を見るだけでなく、ドメインごとに「このページなら必ず含まれるはずのラベル」を定義しておくと誤判定がほぼゼロになります。

2点目は、セッションクッキーの寿命を運用カレンダーに書き出しておくことです。AdMob は7日、App Store Connect は8時間、Stripe Dashboard は CSR 経由だと2週間ほど、Cloudflare Dashboard は MFA を有効にすると12時間です。Sub-Agent が「読み取れない」と訴える原因の半分はここなので、ログインが必要な画面を扱う運用フローでは、毎週月曜日にまとめてセッション更新する時間を取るだけで、無駄な待機トラブルが消えます。

ここまでが、Browser Sub-Agent と SPA の相性問題を運用に組み込むときに、私が積み重ねてきた診断と対処の全体像です。お読みいただきありがとうございました。次のステップとして、今ご自身が一番頻繁に読ませているダッシュボード1つを選び、まず「成功条件のセレクタ」を1行だけ書き出してみてください。それだけでも、エージェントの戻り値の信頼度がはっきり変わるはずです。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Agents & Manager2026-07-15
ls には見えるのに、エージェントが開けない — 日本語ファイル名が macOS と Linux で別物になるとき
エージェントが「そのファイルは存在しません」と言い、ターミナルの ls には確かに写っている。三日かけて同期を疑った末に辿り着いたのは、濁点が一文字ではなかったという事実でした。検出スクリプトと三層ゲートまで残します。
Agents & Manager2026-05-30
Antigravity のエージェントが node や python を『command not found』にするときの原因と対処
自分のターミナルでは動くのに、Antigravity のエージェントがコマンドを実行すると command not found になる。PATH 継承の仕組みから、nvm・pyenv・Homebrew・WSL 環境ごとの具体的な解決手順と、直ったかどうかの確かめ方までまとめます。
Agents & Manager2026-05-09
Antigravityのエージェントから.envが読まれないときに最初に疑うべき3つの伝搬経路
Antigravityのエージェント実行時だけ.envが読まれずMissing API_KEYで止まる現象を、GUI起動・AGENTS.md誤解・dotenv未ロードの3経路に分けて整理し、確実に伝搬させる4ステップを示します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →