Diffs

diffs は、簡潔な組み込みシステムガイダンスとコンパニオンスキルを備えたオプションのプラグインツールで、変更内容を読み取り専用の diff アーティファクトとしてエージェントに提供します。

入力として以下のいずれかを受け付けます:

  • beforeafter テキスト
  • 統一形式の patch

出力として以下を返せます:

  • キャンバス表示用の Gateway ビューア URL
  • メッセージ配信用のレンダリングファイルパス(PNG または PDF)
  • 1 回の呼び出しで両方の出力

有効にすると、プラグインはシステムプロンプトに簡潔な使い方のガイダンスを追加し、エージェントがより詳細な指示を必要とする場合のために詳細なスキルも公開します。

クイックスタート

  1. プラグインを有効にする。
  2. キャンバス優先のフローには diffsmode: "view" で呼び出す。
  3. チャットファイル配信フローには diffsmode: "file" で呼び出す。
  4. 両方のアーティファクトが必要な場合は diffsmode: "both" で呼び出す。

プラグインを有効にする

{
  plugins: {
    entries: {
      diffs: {
        enabled: true,
      },
    },
  },
}

組み込みシステムガイダンスを無効にする

diffs ツールは有効にしたまま組み込みシステムプロンプトガイダンスを無効にしたい場合は、plugins.entries.diffs.hooks.allowPromptInjectionfalse に設定:

{
  plugins: {
    entries: {
      diffs: {
        enabled: true,
        hooks: {
          allowPromptInjection: false,
        },
      },
    },
  },
}

これにより diffs プラグインの before_prompt_build フックがブロックされますが、プラグイン、ツール、コンパニオンスキルは引き続き利用可能です。

ガイダンスとツールの両方を無効にしたい場合は、プラグイン自体を無効にしてください。

エージェントの典型的なワークフロー

  1. エージェントが diffs を呼び出す。
  2. エージェントが details フィールドを読み取る。
  3. エージェントが以下のいずれかを実行:
    • details.viewerUrlcanvas present で開く
    • details.filePathmessagepath または filePath で送信
    • 両方を実行

入力例

before と after:

{
  "before": "# Hello\n\nOne",
  "after": "# Hello\n\nTwo",
  "path": "docs/example.md",
  "mode": "view"
}

パッチ:

{
  "patch": "diff --git a/src/example.ts b/src/example.ts\n--- a/src/example.ts\n+++ b/src/example.ts\n@@ -1 +1 @@\n-const x = 1;\n+const x = 2;\n",
  "mode": "both"
}

ツール入力リファレンス

特記のない限り、すべてのフィールドは任意です:

  • beforestring): 元のテキスト。patch を省略する場合は after と共に必須。
  • afterstring): 更新後のテキスト。patch を省略する場合は before と共に必須。
  • patchstring): 統一 diff テキスト。beforeafter とは排他的。
  • pathstring): before/after モード用の表示ファイル名。
  • langstring): before/after モード用の言語オーバーライドヒント。
  • titlestring): ビューアタイトルのオーバーライド。
  • mode"view" | "file" | "both"): 出力モード。プラグインデフォルトの defaults.mode に従う。
  • theme"light" | "dark"): ビューアテーマ。プラグインデフォルトの defaults.theme に従う。
  • layout"unified" | "split"): diff レイアウト。プラグインデフォルトの defaults.layout に従う。
  • expandUnchangedboolean): 完全なコンテキストが利用可能な場合に未変更セクションを展開。呼び出しごとのオプションのみ(プラグインデフォルトキーではない)。
  • fileFormat"png" | "pdf"): レンダリングファイル形式。プラグインデフォルトの defaults.fileFormat に従う。
  • fileQuality"standard" | "hq" | "print"): PNG または PDF レンダリングの品質プリセット。
  • fileScalenumber): デバイススケールオーバーライド(14)。
  • fileMaxWidthnumber): CSS ピクセルでの最大レンダリング幅(6402400)。
  • ttlSecondsnumber): ビューアアーティファクトの TTL(秒)。デフォルト 1800、最大 21600。
  • baseUrlstring): ビューア URL オリジンのオーバーライド。http または https でなければならず、クエリ/ハッシュは不可。

バリデーションと制限:

  • beforeafter はそれぞれ最大 512 KiB。
  • patch は最大 2 MiB。
  • path は最大 2048 バイト。
  • lang は最大 128 バイト。
  • title は最大 1024 バイト。
  • パッチの複雑さの上限: 最大 128 ファイル、合計 120000 行。
  • patchbefore または after の同時指定は拒否される。
  • レンダリングファイルの安全制限(PNG と PDF に適用):
    • fileQuality: "standard": 最大 8 MP(8,000,000 レンダリングピクセル)。
    • fileQuality: "hq": 最大 14 MP(14,000,000 レンダリングピクセル)。
    • fileQuality: "print": 最大 24 MP(24,000,000 レンダリングピクセル)。
    • PDF は最大 50 ページの制限あり。

出力 details の仕様

ツールは details 配下に構造化メタデータを返します。

ビューアを作成するモードの共通フィールド:

  • artifactId
  • viewerUrl
  • viewerPath
  • title
  • expiresAt
  • inputKind
  • fileCount
  • mode

PNG または PDF レンダリング時のファイルフィールド:

  • filePath
  • pathfilePath と同じ値、message ツール互換用)
  • fileBytes
  • fileFormat
  • fileQuality
  • fileScale
  • fileMaxWidth

モード別の動作:

  • mode: "view": ビューアフィールドのみ。
  • mode: "file": ファイルフィールドのみ、ビューアアーティファクトなし。
  • mode: "both": ビューアフィールド+ファイルフィールド。ファイルレンダリングが失敗してもビューアは fileError 付きで返される。

折りたたまれた未変更セクション

  • ビューアは N unmodified lines のような行を表示できる。
  • それらの行の展開コントロールは条件付きであり、すべての入力種別で保証されるわけではない。
  • 展開コントロールは、レンダリングされた diff に展開可能なコンテキストデータがある場合に表示される(before/after 入力では一般的)。
  • 多くの統一パッチ入力では、省略されたコンテキスト本体がパースされたパッチハンクに含まれないため、展開コントロールなしで行が表示されることがある。これは期待通りの動作。
  • expandUnchanged は展開可能なコンテキストが存在する場合にのみ適用される。

プラグインデフォルト

~/.openclaw/openclaw.json でプラグイン全体のデフォルトを設定:

{
  plugins: {
    entries: {
      diffs: {
        enabled: true,
        config: {
          defaults: {
            fontFamily: "Fira Code",
            fontSize: 15,
            lineSpacing: 1.6,
            layout: "unified",
            showLineNumbers: true,
            diffIndicators: "bars",
            wordWrap: true,
            background: true,
            theme: "dark",
            fileFormat: "png",
            fileQuality: "standard",
            fileScale: 2,
            fileMaxWidth: 960,
            mode: "both",
          },
        },
      },
    },
  },
}

設定可能なデフォルト:

  • fontFamily
  • fontSize
  • lineSpacing
  • layout
  • showLineNumbers
  • diffIndicators
  • wordWrap
  • background
  • theme
  • fileFormat
  • fileQuality
  • fileScale
  • fileMaxWidth
  • mode

明示的なツールパラメータはこれらのデフォルトをオーバーライドします。

セキュリティ設定

  • security.allowRemoteViewerboolean、デフォルト false
    • false: 非ループバックリクエストのビューアルートを拒否。
    • true: トークン付きパスが有効であればリモートビューアを許可。

例:

{
  plugins: {
    entries: {
      diffs: {
        enabled: true,
        config: {
          security: {
            allowRemoteViewer: false,
          },
        },
      },
    },
  },
}

アーティファクトのライフサイクルとストレージ

  • アーティファクトは一時サブフォルダ $TMPDIR/openclaw-diffs に保存。
  • ビューアアーティファクトのメタデータには以下を含む:
    • ランダムなアーティファクト ID(16 進数 20 文字)
    • ランダムなトークン(16 進数 48 文字)
    • createdAtexpiresAt
    • 保存された viewer.html のパス
  • 未指定時のデフォルトビューア TTL は 30 分。
  • ビューア TTL の最大値は 6 時間。
  • クリーンアップはアーティファクト作成後に日和見的に実行。
  • 期限切れのアーティファクトは削除される。
  • メタデータが欠落している場合、24 時間以上古いフォルダはフォールバッククリーンアップで削除。

ビューア URL とネットワーク動作

ビューアルート:

  • /plugins/diffs/view/{artifactId}/{token}

ビューアアセット:

  • /plugins/diffs/assets/viewer.js
  • /plugins/diffs/assets/viewer-runtime.js

URL 構築の動作:

  • baseUrl が指定された場合、厳密なバリデーション後に使用。
  • baseUrl なしの場合、ビューア URL はデフォルトでループバック 127.0.0.1
  • Gateway のバインドモードが customgateway.customBindHost が設定されている場合、そのホストを使用。

baseUrl のルール:

  • http:// または https:// でなければならない。
  • クエリとハッシュは拒否される。
  • オリジン+任意のベースパスが許可される。

セキュリティモデル

ビューアの堅牢化:

  • デフォルトでループバック専用。
  • 厳密な ID とトークンバリデーション付きのトークン化ビューアパス。
  • ビューアレスポンス CSP:
    • default-src 'none'
    • スクリプトとアセットは self からのみ
    • アウトバウンド connect-src なし
  • リモートアクセス有効時のリモートミススロットリング:
    • 60 秒間に 40 回の失敗
    • 60 秒間のロックアウト(429 Too Many Requests

ファイルレンダリングの堅牢化:

  • スクリーンショットブラウザのリクエストルーティングはデフォルトで拒否。
  • http://127.0.0.1/plugins/diffs/assets/* からのローカルビューアアセットのみ許可。
  • 外部ネットワークリクエストはブロック。

ファイルモードのブラウザ要件

mode: "file"mode: "both" には Chromium 互換ブラウザが必要です。

解決順序:

  1. OpenClaw 設定の browser.executablePath
  2. 環境変数:
    • OPENCLAW_BROWSER_EXECUTABLE_PATH
    • BROWSER_EXECUTABLE_PATH
    • PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
  3. プラットフォームのコマンド/パス検出によるフォールバック。

よくあるエラーメッセージ:

  • Diff PNG/PDF rendering requires a Chromium-compatible browser...

Chrome、Chromium、Edge、Brave のいずれかをインストールするか、上記の実行パスオプションを設定して解決してください。

トラブルシューティング

入力バリデーションエラー:

  • Provide patch or both before and after text.
    • beforeafter の両方を含めるか、patch を提供。
  • Provide either patch or before/after input, not both.
    • 入力モードを混在させない。
  • Invalid baseUrl: ...
    • http(s) オリジン+任意のパスを使用、クエリ/ハッシュは不可。
  • {field} exceeds maximum size (...)
    • ペイロードサイズを削減。
  • 大きなパッチの拒否
    • パッチのファイル数または合計行数を削減。

ビューアのアクセス問題:

  • ビューア URL はデフォルトで 127.0.0.1 に解決。
  • リモートアクセスが必要な場合:
    • ツール呼び出しごとに baseUrl を指定するか、
    • gateway.bind=customgateway.customBindHost を使用
  • 外部ビューアアクセスを意図する場合のみ security.allowRemoteViewer を有効化。

未変更行に展開ボタンがない:

  • パッチ入力にて展開可能なコンテキストがない場合に発生する可能性がある。
  • これは期待通りの動作であり、ビューアの障害ではない。

アーティファクトが見つからない:

  • TTL により期限切れ。
  • トークンまたはパスが変更された。
  • クリーンアップにより古いデータが削除された。

運用ガイダンス

  • キャンバスでのローカル対話レビューには mode: "view" を推奨。
  • 添付ファイルが必要なアウトバウンドチャットチャンネルには mode: "file" を推奨。
  • デプロイメントがリモートビューア URL を必要とする場合を除き、allowRemoteViewer は無効のまま。
  • 機密性の高い diff には短い ttlSeconds を明示的に設定。
  • 不要な場合は diff 入力にシークレットを含めない。
  • チャンネルが画像を強く圧縮する場合(例: Telegram や WhatsApp)、PDF 出力(fileFormat: "pdf")を推奨。

diff レンダリングエンジン:

  • Diffs により提供。

関連ドキュメント