技術 約12分で読めます

Cloudflare Agents Week 2026のSandboxes GA・Durable Object Facets・統合CLI

いけさん目次

Cloudflareが4月13日から「Agents Week 2026」と銘打った一連の発表を行った。AIエージェントがCloudflareのインフラ上で開発・実行・永続化まで完結できる環境を一気に整えた格好だ。
主要な発表は3つ。エージェント向け隔離実行環境のSandboxes GA、AI生成コードに専用SQLiteを割り当てるDurable Object Facets、全Cloudflare APIを一貫操作する統合CLI cf とLocal Explorer。

Sandboxes GAでエージェントに永続的な開発環境を提供

コード実行場所の問題

LLMベースのエージェントにコードを書かせるとき、そのコードを実行する場所の問題は思いのほか厄介だ。
毎回クリーンなコンテナを起動すれば安全だが、git clonenpm installで30秒かかる。既存のコンテナを使い回せばセッション間で状態が汚染される。認証情報をエージェントに渡せば情報漏洩のリスクがある。

Cloudflareはこの問題をプラットフォームのプリミティブとして解決することにした。2025年6月のベータ公開から約9ヶ月、Sandboxesが一般提供(GA)に到達した。

基本的な使い方は単純で、サンドボックスを名前で要求するだけだ。

import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";

export default {
  async fetch(request: Request, env: Env) {
    const sandbox = getSandbox(env.Sandbox, "agent-session-47");

    await sandbox.gitCheckout("https://github.com/org/repo", {
      targetDir: "/workspace",
      depth: 1,
    });

    return sandbox.exec("npm", ["test"], { stream: true });
  },
};

実行中なら取得、停止中なら起動、アイドル時は自動でスリープして次のリクエストで再起動する。同じIDを渡せば世界のどこからでも同じサンドボックスに到達できる。

GAで追加された主要機能

セキュアな認証情報インジェクション

エージェントに認証情報を渡さずにプライベートサービスへアクセスさせる仕組み。ネットワーク層で認証情報インジェクションとして実装され、プログラマブルな出口プロキシ(egress proxy)として動作する。エージェントはトークン本体を一切見ない。

class OpenCodeInABox extends Sandbox {
  static outboundByHost = {
    "my-internal-vcs.dev": (request, env, ctx) => {
      const headersWithAuth = new Headers(request.headers);
      headersWithAuth.set("x-auth-token", env.SECRET);
      return fetch(request, { headers: headersWithAuth });
    }
  }
}

env.SECRETはWorkers bindingsを経由して安全に渡される。Identity-aware(ユーザー識別ベース)のインジェクションや動的ルール変更も可能。

PTY(擬似端末)サポート

エージェントがシェルにアクセスする初期の実装は、コマンドを投げてレスポンスを待つリクエスト-レスポンスループだった。
人間がターミナルを使うときはストリーミング出力を見ながら割り込み、後でセッションに再接続して続きをやる。2月に実装されたPTYサポートはこのギャップを埋める。

WebSocket越しにプロキシされる擬似端末セッションで、xterm.jsと互換性がある。ターミナルセッションはそれぞれ独立したシェルと作業ディレクトリを持ち、出力はサーバー側でバッファリングされるため、再接続時に見逃した出力をリプレイできる。

export default {
  async fetch(request: Request, env: Env) {
    const url = new URL(request.url);
    if (url.pathname === "/terminal") {
      const sandbox = getSandbox(env.Sandbox, "my-session");
      return sandbox.terminal(request, { cols: 80, rows: 24 });
    }
    return new Response("Not found", { status: 404 });
  },
};

永続コードインタープリタ

データ分析やスクリプティング向けに、状態を保持するコード実行コンテキストが提供される。
多くのコードインタープリタ実装ではスニペットごとに独立した実行環境が立ち上がるため、ある呼び出しでセットした変数を次の呼び出しで参照できない。Sandboxesの永続コンテキストはJupyterノートブックのように状態を維持する。

const ctx = await sandbox.createCodeContext({ language: "python" });

// 1回目: データをロード
await sandbox.runCode(`
  import pandas as pd
  df = pd.read_csv('/workspace/sales.csv')
  df['margin'] = (df['revenue'] - df['cost']) / df['revenue']
`, { context: ctx });

// 2回目: dfがまだ生きている
const result = await sandbox.runCode(`
  df.groupby('region')['margin'].mean().sort_values(ascending=False)
`, { context: ctx, onStdout: (line) => console.log(line.text) });

Python、JavaScript、TypeScriptに対応。結果はmatplotlibチャート、構造化JSON、HTML形式のPandasテーブルとして返ってくる。

バックグラウンドプロセスとファイルシステム監視

開発サーバーをバックグラウンドで起動してパブリックURLを発行できる。waitForPort()waitForLog()で実際のシグナルを待てるのが重要で、sleep(2000)に比べて確実性が違う。

const server = await sandbox.startProcess("npm run dev", {
  cwd: "/workspace",
});
await server.waitForLog(/Local:.*localhost:(\d+)/);
const { url } = await sandbox.exposePort(3000);

3月に実装されたsandbox.watch()はLinuxカーネルのinotifyを利用したSSEストリームを返す。ファイル変更でビルドを走らせる、設定変更でサーバーを再起動するといった、人間の開発者が当然のように行うイベント駆動の開発ループをエージェントも使えるようになる。

const stream = await sandbox.watch('/workspace/src', {
  recursive: true,
  include: ['*.ts', '*.tsx']
});

for await (const event of parseSSEStream<FileWatchSSEEvent>(stream)) {
  if (event.type === 'modify' && event.path.endsWith('.ts')) {
    await sandbox.exec('npx tsc --noEmit', { cwd: '/workspace' });
  }
}

スナップショット(近日公開)

エージェントがgit cloneしてnpm installしてコードを書いた後、コードレビュー待ちでセッションを止めたい。コンテナを起動したままなら課金は続き、コンテナイメージから再スタートならnpm installのやり直しで時間がかかる。

スナップショットはディスクの完全な状態を保存する。OS設定、インストール済みの依存関係、変更されたファイル、データファイルまで含めて保存し、高速に復元する。

class AgentDevEnvironment extends Sandbox {
  sleepAfter = "5m";
  persistAcrossSessions = { type: "disk" };
}

手動でのスナップショット取得とフォーク(同一状態から複数インスタンスを並行起動)も可能。現在は近日公開予定で、先行してbackup/restoreメソッドが利用可能。npm installで30秒かかるところをバックアップからの復元なら2秒に短縮できる。

将来のリリースではメモリの状態も保存対象になり、ターミナルやエディタが最後に閉じた状態そのままで再開できるようになる予定だ。

料金モデルの変更

アクティブなCPUサイクルのみに課金する「Active CPU Pricing」が導入された。エージェントがLLMのレスポンスを待っているアイドル時間は課金されない。

インスタンスタイプ同時実行数上限
lite15,000
basic6,000
大型インスタンス1,000以上

ベータから関わったFigma Makeでは、ユーザーが書いたエージェントコードをCloudflare Containersで実行している。「信頼できないエージェントおよびユーザー作成コードを実行できる、信頼性の高いハイスケールなサンドボックスが必要だった」(Figma AI and Developer Platforms担当)というユースケースだ。

SDKのバージョンは0.8.9で、npm i @cloudflare/sandbox@latestでインストールできる。

Durable Object FacetsでAI生成コードに専用SQLiteを割り当てる

Dynamic Workersの制約

通常のCloudflare Workersはwranglerでデプロイして設定ファイルにバインディングを列挙する。一方Dynamic WorkersはAPI経由でコードをランタイムに流し込み、V8アイソレートとして即時実行する。

アイソレートはコンテナとは根本的に異なる。Linuxコンテナが起動に数百ミリ秒と数百MBのメモリを使うのに対し、アイソレートは数ミリ秒・数MBで動作する。100倍速く、メモリ消費は10分の1。この軽量さがあるからこそ、AIが生成したコードを即時実行するモデルが現実的になる。Cloudflareの計測では、通常のツール逐次呼び出しと比べてトークン使用量を81%削減できるという。

ただし初期のDynamic Workersには制約があった。コードを実行して終わり、永続的な状態を持てない。

Durable ObjectsとSQLite

Durable ObjectsはWorkersの拡張で、グローバルにユニークな識別子を持つオブジェクトだ。同じIDのオブジェクトは世界に1インスタンスしか存在しない。各Durable ObjectにローカルのSQLiteデータベースが付随している。

特性Cloudflare KVDurable Objects SQLite
整合性結果整合性強整合性(トランザクション)
アクセスパターンキー単位任意のSQLクエリ
レイテンシ低い(エッジキャッシュ)超低(同一マシン上)
同時書き込み複数Workerから可1インスタンスがシリアライズ
状態の保持グローバル共有オブジェクト固有

SQLiteはDurable Objectが動いているマシン上にローカル保存されているため、ネットワーク越しのDB呼び出しが発生しない。以前取り上げたリアルタイム分析基盤の事例でも「KVの結果整合性はデデュプリケーション用途には十分だが、正確なカウントにはDurable Objectsが必要」という教訓が語られていた。

なぜDynamic WorkersとDurable Objectsの相性が悪かったか

AIが生成したコードにDurable Objectsを使わせようとすると、従来は以下の手順が必要だった。

  1. DurableObjectを継承するクラスを書く
  2. WorkerのメインモジュールからExportする
  3. Cloudflare API経由でストレージをプロビジョニングする
  4. wrangler.tomlにネームスペースバインディングを設定する

「AIが動的にコードを生成して即実行」というモデルとは相性が悪い。加えて、動的コードにDurable Objectのネームスペースを直接渡すと、AIが生成したコードが無制限にオブジェクトを作成したり、ログを残さずにストレージを操作したりする可能性がある。

Facetsの親子構造

Facetsはこの問題を「親子構造」で解決する。

flowchart TD
    A[ユーザーの依頼] --> B[AIがコードを生成]
    B --> C{実行モード}
    C -- 単発処理 --> D[Dynamic Worker<br/>アイソレート起動]
    D --> E[コード実行・結果返却]
    E --> F[アイソレート破棄]
    C -- 永続アプリ --> G[AppRunner<br/>親Durable Object]
    G --> H[Dynamic Worker Loader<br/>コードを読み込む]
    H --> I[ctx.facets.get]
    I --> J{初回アクセス?}
    J -- Yes --> K[Facet初期化<br/>SQLite新規作成]
    J -- No --> L[既存Facet再利用<br/>状態を復元]
    K --> M[Facetが処理実行<br/>専用SQLiteに永続化]
    L --> M

親(AppRunner)は通常どおりデプロイする標準的なDurable Objectだ。Facetの作成権限や使用量の記録といったプラットフォームレベルの制御はここに入る。

子(Facet)はDynamic Worker Loaderで読み込まれた動的コードからDurableObjectを継承するクラスをエクスポートすることで生成される。ctx.facets.get()の第一引数がFacetの名前で、同じ名前なら既存のインスタンスを再利用、初アクセスなら初期化コールバックが走る。

ひとつのDurable Objectインスタンスが複数のFacetを名前で管理できる。アプリAとアプリBが同じAppRunnerインスタンスに属しながら、それぞれ独立したSQLiteを持つ構成も可能だ。

// wrangler.jsonc
{
  "durable_objects": {
    "bindings": [
      {
        "name": "APP_RUNNER",
        "class_name": "AppRunner",
        "sqlite_database": "SqliteDb"
      }
    ]
  },
  "worker_loaders": [{ "binding": "LOADER" }]
}

動的コードが触れるのは自分専用のSQLiteだけで、親のDBには手が届かない。ストレージの割り当て上限もDurable Object単位で管理できる。ログ・監査・課金は親側に集約でき、子は自分の処理に集中する。エンタープライズのAIプラットフォームを構築する上で必須のマルチテナント分離だ。

オープンベータはWorkers有料プランのみ。以前取り上げたvinext(Next.jsのCloudflare向け再実装)でも「Durable ObjectsやAI bindingsをワークアラウンドなしで使えるのが従来のOpenNextと根本的に違う」と触れていたが、FacetsはそのDurable Objectsをさらに動的コード向けに拡張した次の層だ。

統合CLI cf とLocal Explorer

WranglerがCloudflare全体をカバーできなかった理由

Cloudflareは現在100以上のプロダクトを持ち、HTTP APIオペレーション数は約3,000に上る。API SDKやTerraformプロバイダー、MCPサーバーはOpenAPIスキーマから自動生成しているが、CLIコマンド・Workers Bindings・wrangler.jsonc設定・Agent Skills・ドキュメントは手動メンテナンスだった。

WranglerはもともとWorkers中心のCLIとして設計されており、他の多くの製品はWranglerにコマンドを持っていない。開発チームが数百人規模になると、CLIの命名規則を手動レビューで統一するのは穴だらけになる。あるコマンドが get を使い、別のコマンドが info を使う、といった揺れが積み重なると、AIエージェントが存在しないコマンドを呼び出すミスが頻発する。

TypeScriptスキーマによるコード生成

新しいTypeScriptスキーマは「インタラクティブなCLIコマンドとその引数」「ローカル開発とAPIリクエストの連携」「Workers BindingsのRPCスタイルAPI」「Agent Skills」「ドキュメントの文脈」を一括して定義できる。OpenAPIスキーマへの逆出力にも対応しており、一貫性ルールをスキーマ層で強制できるようになった。

対象正しい書き方禁止
情報取得getinfo, show, describe
確認スキップ--force--skip-confirmations, --yes
JSON出力--json--format, --output

この3つのルールだけでも、100製品・3,000オペレーションに一貫したCLIを提供する際の差は大きい。

今回公開されたテクニカルプレビューはまだ一部の製品のみサポートしている。Cloudflare内部ではAPI全体をカバーするバージョンのテストが進んでいるが、「公開の場でフィードバックを得ながら開発する」方針で早期リリースした。最終的には既存Wranglerの機能と統合されて次世代Wranglerになる予定だ。

# グローバルインストール
npm install -g cf

# または毎回最新版を実行
npx cf

ローカル開発とリモートの「一貫性の罠」

Wranglerの特殊な点として、D1・R2・KV・Durable Objects・Workflowsのローカルシミュレーション機能がある。Miniflare(ローカル開発プラットフォームエミュレーター)がWorkers runtimeと同一のAPIをローカルでも提供し、バックエンドにはローカルのSQLiteを使う。

ネットワーク不要・オフライン動作・高速テストというメリットがある一方、落とし穴もあった。エージェントがリモートのD1データベースを変更しているつもりで、実はローカルのシミュレーションデータを変更していて、その変更がローカル開発サーバーへのリクエストに反映されない、というすれ違いが起きうる。
新バージョンのCLIでは --local フラグで操作対象を明示できるようになり、コマンド出力でも操作対象が明記される。

Local Explorerでローカルデータを可視化

ローカル開発中に「今D1に何が入っているか」を確認するには、.wrangler/stateディレクトリの中身を逆算するか、サードパーティツールを使うしかなかった。

Local ExplorerはWranglerまたはCloudflare Vite pluginで起動したアプリにキーボードショートカット e で呼び出せるローカルUIだ。現在のWorkerにアタッチされているバインディングと、各バインディングに格納されているデータを確認・操作できる。KV・R2・D1・Durable Objects・Workflowsが対象。

# Wrangler起動後 → e キーでLocal Explorerを開く
wrangler dev
# → [e] to open Local Explorer

Local ExplorerのAPIは /cdn-cgi/explorer/api で公開されており、OpenAPI仕様を持つ。エージェントをこのアドレスに向けるだけで、ローカルリソースの管理をエージェントが自律的に行える。スキーマの確認・テストデータのシード・DROP TABLEによる初期化など、リモートのCloudflare APIダッシュボードでできることがそのままローカルでも使える。
リモートAPIとローカルAPIのAPIシェイプを一致させているので、--localフラグを渡すだけで同一コマンドがローカルAPIに切り替わる。