AI coding agent や sandbox-based な開発フローを触っていると、実行環境は安全で、すぐに作れて、壊しても惜しくない。一方で、そこで育っていた workspace の状態は、実行が終わるたびに途切れやすい。たとえば途中まで書いたメモ、生成した補助ファイル、少しずつ整えていた作業用ディレクトリなどです。
もちろん、それ自体は Sandbox の良さでもあります。ephemeral だからこそ、試行錯誤しやすい。しかし、agent に何度か作業をまたがせたり、sandbox を作り直しながら同じ仕事を続けたりし始めると、「安全な一時環境」とは別に、「workspace が続いていてほしい」という欲求が出てきます。
そこで作ったのが giselles-ai/sandbox-volume です。この記事では、機能一覧を説明するというより、Vercel Sandbox の一時環境に、workspace の継続性をどう足したかったのか、その考え方を書いてみます。
一時環境の気楽さと、workspace が途切れる不便さ
Vercel Sandbox は、安全で ephemeral な実行環境を作るための選択肢としてとても魅力的です。検証や自動化のために sandbox を立ち上げ、処理が終わったら閉じる。このリズムは軽やかです。
とはいえ、実務では「一回ごとに全部忘れてくれること」が、そのまま嬉しさになるわけでもありません。特に AI agent と組み合わせると、次のような場面が出てきます。
- 前回の実行で整えた workspace を次の実行でも使いたい
- sandbox を作り直しても、途中までの成果物は引き継ぎたい
- 実行環境そのものではなく、ファイルの状態だけを継続したい
ここで欲しいのは VM 全体の再開ではなく、もっと薄いものだと思います。つまり、workspace のファイル群だけが、実行の外側に少し残っていてくれることです。
sandbox-volume は snapshot ではなく、workspace sync として考えた
sandbox-volume を作るときに最初から意識していたのは、これを「sandbox の完全な保存」にしないことでした。
このライブラリが扱うのは、プロセスやメモリの状態ではなく、workspace 内のファイルです。しかも、単純なファイルコピーの雰囲気とも少し違います。README では transactional workspace sync という言葉を使いました。少し大げさにも見えるのですが、今のところこの言い方が一番しっくりきています。
考え方としては、だいたい次の流れです。
- 外部ストレージに保存してある workspace のファイル群を sandbox に hydrate する
- sandbox の中で作業する
- 最後に差分を見て、必要なものだけ commit する
つまり、「毎回まるごと snapshot を戻す」のではなく、「workspace の状態を、実行単位で丁寧に同期する」ための層です。ここが sandbox-volume の性格をかなり決めています。
sandbox-volumeでできること・できないこと・向いてること・向いていないこと
- できること: 実行をまたいで workspace files を引き継ぐ
- やらないこと: sandbox 全体の状態を丸ごと復元する
- 向いていること: agent の反復作業、生成物の継続利用、作業途中のファイル群の保持
- 向いていないこと: 実行中プロセスの再開、完全な VM snapshot の代替
Vercel Sandbox の Filesystem Snapshot とどう違うのか
Vercel Sandbox には、 Snapshots の仕組みがあります。公式ドキュメントでは、snapshot は「running sandbox の state を、filesystem と installed packages を含めて capture し、subsequent runs の setup time を skip する」ためのものとして説明されています。また、snapshot を作ると元の sandbox は自動的に停止します。
これはとても便利です。依存関係のインストールが重い環境や、長めのセットアップを済ませた状態をそのまま再利用したい時には、まず snapshot を検討するのが自然だと思います。
一方で、sandbox-volume が解こうとしているのは少し別の問題です。
- Snapshot: sandbox 全体の実行状態に近いスタート地点を保存して、次回の起動を速くする
sandbox-volume: 実行の外側に workspace files を逃がして、別の sandbox でも同じ作業状態を引き継ぎやすくする
違いをもう少し具体的に書くと、こんな感じです。
- Snapshot は「環境を再開しやすくする」仕組み
sandbox-volumeは「workspace を持ち運びやすくする」仕組み- Snapshot は dependency installation や environment setup を飛ばしたい時に向いている
sandbox-volumeはメモ、生成物、途中成果物のような file state を実行間で残したい時に向いている
なので、両者は競合するというより、見ているレイヤーが違います。setup 済みの sandbox を起点にしたいなら snapshot がよく、sandbox 自体は作り直しても workspace のファイル群は持ち越したいなら sandbox-volume が合います。
言い換えると、snapshot は「同じ環境から始めたい」という欲求に応える道具で、sandbox-volume は「同じ作業の続きから始めたい」という欲求に応える道具です。似ているようで、ここは少し違います。
ざっくり使い方
たとえば Vercel Blob を保存先にして使うなら、概ね以下のような形です。
import { Sandbox } from "@vercel/sandbox";
import { SandboxVolume, createVercelBlobStorageAdapter } from "@giselles-ai/sandbox-volume";
const adapter = createVercelBlobStorageAdapter({
token: process.env.BLOB_READ_WRITE_TOKEN,
namespace: "my-team",
});
const volume = await SandboxVolume.create({
key: "repos/example-app",
adapter,
include: ["src/**", "package.json"],
exclude: ["dist/**", ".sandbox/**/*"],
});
const sandbox = await Sandbox.create();
await volume.mount(sandbox, async (sandbox) => {
await sandbox.runCommand("sh", ["-lc", "echo hello > /vercel/sandbox/workspace/notes.md"]);
});
mount() が「読み込んで、作業して、成功時に commit する」という処理をラップしており、ファイル永続化の責務がアプリケーションコードにベタっと漏れにくい形にしたかったので、このくらいの単位で包むのがよさそうだと考えました。
また、include / exclude で同期対象を絞ることができます。
実際に使う時の検討事項
sandbox-volumeは薄く作っているので、利用するアプリケーション側で対応する責務がそれなりにあります。
1. include / exclude を変えた時の整合性
過去に広いルールで保存していたものを、あとから狭いルールに変えた場合、以前のスコープ外ファイルはすぐには消えません。こういうのは地味ですが大事です。
そのため、対象ルールを変えた時に rewrite() や resync() で基準状態を作り直す、という運用は頭に入れておくと安心です。
2. ロックや保存先の設計は、アプリケーション側の責務が残る
sandbox-volume 自体は薄い層なので、保存先の選定や lock の設計まで全部引き受けるわけではありません。複数 writer がありうるなら、競合時の振る舞いをどうするかは、利用側の設計が必要です。
どういう用途に向いていそうか
現時点では、次のような用途と相性がよさそうです。
- AI coding agent が複数回の実行をまたいで同じ workspace を扱う
- sandbox を作り直しながら、作業途中のファイル群を引き継ぎたい
- Vercel Blob のような外部ストレージに、workspace state を薄く退避したい(Sandboxを起動せずにファイル一覧を表示したりダウンロードできる)
逆に、「完全に同じ実行状態を戻したい」「live に共有される network filesystem がほしい」という期待で入ると、少し違うものに見えるかもしれません。
おわりに
sandbox-volume は、Vercel Sandbox の価値を置き換えるものではなく、その上にもう一枚だけ薄いレイヤーを足すつもりで作りました。安全で ephemeral な実行環境はそのままに、workspace だけは少し続いていてほしい。その欲求は、AI agent や自動化を試していると、思ったより早く出てきます。
一時環境の気軽さと、作業状態の継続性は、必ずしも二者択一ではないのかもしれません。もし今、sandbox-based な開発フローの中で「毎回ゼロからは少しつらい」と感じているなら、こういう薄い同期レイヤーから試してみるのはよさそうです。
リポジトリはこちらです。