本文へ移動
AI開発 AI生成 文責 blackstrawberry

OpenCode 2.0 を Durable Object で動かす、実験版 harness の導入条件

OpenCode 2.0 を Durable Object で動かす実験版 OpenCodeHarness の導入条件を整理する。現行ガイドの依存バージョン、Wrangler 設定、使えないローカルツールと再開の範囲を確認する。

OpenCode 2.0 のエージェントを Cloudflare の Durable Object 内で動かすのが、実験版の OpenCodeHarness だ。公式ガイドでは、Agents SDK 0.28.1以降と OpenCode パッケージに加え、AI binding、SQLite を使う Durable Object、nodejs_compat が前提になる。パッケージを入れるだけでは導入は終わらない。

セッションを SQLite に保存し、eviction で処理が中断したら Lifecycle のジョブが再開を支える。ただし、ローカルのファイル操作や shell がそのまま使える構成ではない。既存のコーディングエージェントを移すなら、実行ツールの置き換えと中断後の結果処理まで含めて設計する必要がある。

依存バージョンと Wrangler の準備

告知時点と現行ガイドでは最低版が違う。2026年10月12日の changelog は Agents SDK 0.28.0以降としていたが、現行ガイドは 0.28.1以降を指定している。OpenCode の依存範囲は @opencode/sdk と @opencode/plugin のどちらも ^2.0.24 だ。以下は現行ガイドの条件に合わせたインストール例で、記事内で実行確認した手順ではない。

npm i "agents@^0.28.1" "@opencode/sdk@^2.0.24" "@opencode/plugin@^2.0.24"

Wrangler では AI binding を AI という名前で定義し、Durable Object binding と SQLite 用 migration を設定する。通常の Durable Object の宣言だけで済ませず、ガイドの new_sqlite_classes と nodejs_compat を含む設定例に合わせる。既存プロジェクトでは migration の履歴も自分の構成に合わせて扱う。

さらに ./worker-tools の実装も必要で、公式ガイドの例ではローカルファイルの読み書き・検索や shell など workerd で使えない標準ツールを外し、独自ツールを追加している。ファイル編集やコード実行が必要なら、その処理を担う別の実行先とツールを用意することになる。

Durable Object 側の接続

次は changelog の TypeScript 例をもとに、現行ガイドの config.default_agent を加えた接続部分だ。Env の binding 型や workerTools の本体、Wrangler 設定を省略しているため、このファイル単独では動かない。全体を組み立てるときはガイドの一式を参照する。

import { DurableObject } from "cloudflare:workers";
import { Lifecycle } from "agents/lifecycle";
import { OpenCodeWorkerd } from "@opencode/sdk/workerd";
import { OpenCodeHarness } from "agents/harness/opencode";
import { createAI } from "agents/models/opencode";
import workerTools from "./worker-tools";

export class Assistant extends DurableObject<Env> {
  ai = createAI({ binding: this.env.AI });
  harness = new OpenCodeHarness({
    opencode: ({ storage }) =>
      OpenCodeWorkerd.create({
        storage,
        models: { fetch: false },
        config: { default_agent: "build" },
        plugins: [this.ai.plugin, workerTools],
      }),
    defaults: {
      model: this.ai("@cf/moonshotai/kimi-k2.7-code", {
        reasoningEffort: "low",
      }),
      agent: "build",
    },
  });

  lifecycle = Lifecycle.install(this).use(this.harness);

  async ask(prompt: string) {
    const { status, text, reason } = await this.harness.prompt(prompt);
    return { status, text, reason };
  }
}

createAI({ binding: this.env.AI }) が Workers AI を接続し、this.ai.plugin を OpenCode 側へ登録する。defaults.model は既定モデル、defaults.agent は呼び出すエージェントの指定だ。例のモデル名を使う場合も、利用環境での提供状況と料金を確認したい。

harness から渡される storage は、OpenCode のセッション状態を Durable Object の SQLite に保存するためのものだ。また models: { fetch: false } は外部モデルカタログの更新を止める設定で、モデル推論そのものを無効にする意味ではない。Workers AI の provider とは役割が異なる。

再開できる状態と、返り値を分けて扱う

OpenCode の Cloudflare SDK 文書は、セッションやメッセージ、durable event を SQLite に残し、workerd で使えないファイルシステム・プロセス機能を置き換える構成として説明している。継続の土台はこの保存状態であり、任意の shell コマンドや外部への書き込みが中断なしに完了するという保証ではない。

Lifecycle.install(this).use(this.harness) は、実行中セッションのジョブを追跡し、中断した処理の再開を担う。返り値の確認も必要だ。呼び出し元は harness.prompt() の終了を待つだけで成功扱いにせず、返ってきた status を見る。現行ガイドでは回答できた場合が done、回答できなかった場合が unanswered で、後者の理由は reason に入る。

text が返る前提で表示処理を組むと、失敗や中断時に扱いが欠ける。結果の状態と理由を記録し、回答がない場合の表示や再実行方針を呼び出し側に設ける。外部ツールに副作用があるなら、再開時に同じ操作を繰り返しても問題がないかも別途確認する。

プラグインを足すときに確認する範囲

拡張の入口は OpenCode のプラグインで、独自ツール、リモート MCP サーバー、skills を組み込める。ただし Workers にバンドルするプラグインと、ローカルのファイルやプロセスを前提とするプラグインは区別が必要だ。「OpenCode 対応」という名前だけで移植可能と判断せず、そのツールが必要とする実行環境を見る。

公式の動作例へのリンクは、ガイド末尾にある。最初はその構成を基準にし、binding、永続化、モデル接続、ツール呼び出しの順に自分の環境との差を確かめると、どこまで動いたかを切り分けやすい。

OpenCodeHarness は experimental なので、導入した依存バージョンと lockfile を残しておく。更新時には changelog とガイドを確認し、通常の応答だけでなく、途中で中断した場合の復旧と失敗結果も試す。この記事は公式文書の構成と制約を整理したもので、実環境での耐障害性を検証した報告ではない。

出典 種別について