Diffs

使用 optional Diffs plugin 将 before and after text 或 unified patches 渲染为 gateway-hosted diff view、file(PNG 或 PDF)或两者。

Diffs

diffs 是一个 optional plugin tool,将 change content 转换为 read-only diff artifact 供 agents 使用。

它接受:

  • beforeafter text
  • unified patch

它可以返回:

  • canvas presentation 的 gateway viewer URL
  • message delivery 的 rendered file path(PNG 或 PDF)
  • 一次调用中的 both outputs

Quick start

  1. Enable the plugin。
  2. 调用 diffs with mode: "view" 用于 canvas-first flows。
  3. 调用 diffs with mode: "file" 用于 chat file delivery flows。
  4. 调用 diffs with mode: "both" 当你需要 both artifacts。

Enable the plugin

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

Typical agent workflow

  1. Agent 调用 diffs
  2. Agent 读取 details fields。
  3. Agent 要么:
    • canvas present 打开 details.viewerUrl
    • message 发送 details.filePath 使用 pathfilePath
    • 两者都做

Input examples

Before and after:

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

Patch:

{
  "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"
}

Tool input reference

所有 fields 是 optional 除非注明:

  • beforestring):original text。当 patch omitted 时与 after 一起 required。
  • afterstring):updated text。当 patch omitted 时与 before 一起 required。
  • patchstring):unified diff text。与 beforeafter 互斥。
  • pathstring):before and after mode 的 display filename。
  • langstring):before and after mode 的 language override hint。
  • titlestring):viewer title override。
  • mode"view" | "file" | "both"):output mode。默认为 plugin default defaults.mode
  • theme"light" | "dark"):viewer theme。默认为 plugin default defaults.theme
  • layout"unified" | "split"):diff layout。默认为 plugin default defaults.layout
  • expandUnchangedboolean):当 full context available 时 expand unchanged sections。仅 per-call option(不是 plugin default key)。
  • fileFormat"png" | "pdf"):rendered file format。默认为 plugin default defaults.fileFormat
  • fileQuality"standard" | "hq" | "print"):PNG 或 PDF rendering 的 quality preset。
  • fileScalenumber):device scale override(1-4)。
  • fileMaxWidthnumber):max render width in CSS pixels(640-2400)。
  • ttlSecondsnumber):viewer artifact TTL in seconds。默认 1800,max 21600。
  • baseUrlstring):viewer URL origin override。必须是 httphttps,no query/hash。

Validation and limits:

  • beforeafter 每个 max 512 KiB。
  • patch max 2 MiB。
  • path max 2048 bytes。
  • lang max 128 bytes。
  • title max 1024 bytes。
  • Patch complexity cap:max 128 files 和 120000 total lines。
  • patchbeforeafter together 被 rejected。
  • Rendered file safety limits(适用于 PNG 和 PDF):
    • fileQuality: "standard":max 8 MP(8,000,000 rendered pixels)。
    • fileQuality: "hq":max 14 MP(14,000,000 rendered pixels)。
    • fileQuality: "print":max 24 MP(24,000,000 rendered pixels)。
    • PDF also has a max of 50 pages。

Output details contract

Tool 在 details 下返回 structured metadata。

为创建 viewer 的 modes 共享 fields:

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

当 PNG 或 PDF rendered 时的 File fields:

  • filePath
  • path(与 filePath 相同值,用于 message tool compatibility)
  • fileBytes
  • fileFormat
  • fileQuality
  • fileScale
  • fileMaxWidth

Mode behavior summary:

  • mode: "view":仅 viewer fields。
  • mode: "file":仅 file fields,no viewer artifact。
  • mode: "both":viewer fields plus file fields。如果 file rendering fails,viewer 仍然返回 with fileError

Collapsed unchanged sections

  • Viewer 可以显示 rows like N unmodified lines
  • 那些 rows 上的 Expand controls 是 conditional 且不 guaranteed for every input kind。
  • 当 rendered diff has expandable context data 时 Expand controls appear,这在 before and after input 中是 typical。
  • 对于许多 unified patch inputs,omitted context bodies 在 parsed patch hunks 中不可用,所以 row 可以 appear without expand controls。这是 expected behavior。
  • expandUnchanged 仅当 expandable context exists 时适用。

Plugin defaults

~/.openclaw/openclaw.json 中设置 plugin-wide defaults:

{
  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",
          },
        },
      },
    },
  },
}

Supported defaults:

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

Explicit tool parameters override these defaults。

Security config

  • security.allowRemoteViewerboolean,默认 false
    • false:non-loopback requests to viewer routes are denied。
    • true:remote viewers are allowed if tokenized path is valid。

示例:

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

Artifact lifecycle and storage

  • Artifacts stored under the temp subfolder:$TMPDIR/openclaw-diffs
  • Viewer artifact metadata contains:
    • random artifact ID(20 hex chars)
    • random token(48 hex chars)
    • createdAtexpiresAt
    • stored viewer.html path
  • Default viewer TTL is 30 minutes when not specified。
  • Maximum accepted viewer TTL is 6 hours。
  • Cleanup runs opportunistically after artifact creation。
  • Expired artifacts are deleted。
  • Fallback cleanup removes stale folders older than 24 hours when metadata is missing。

Viewer URL and network behavior

Viewer route:

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

Viewer assets:

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

URL construction behavior:

  • 如果提供 baseUrl,它在 strict validation 后使用。
  • 没有 baseUrl,viewer URL defaults to loopback 127.0.0.1
  • 如果 gateway bind mode 是 customgateway.customBindHost is set,that host is used。

baseUrl rules:

  • Must be http:// or https://
  • Query and hash are rejected。
  • Origin plus optional base path is allowed。

Security model

Viewer hardening:

  • Loopback-only by default。
  • Tokenized viewer paths with strict ID and token validation。
  • Viewer response CSP:
    • default-src 'none'
    • scripts and assets only from self
    • no outbound connect-src
  • Remote miss throttling when remote access is enabled:
    • 40 failures per 60 seconds
    • 60 second lockout(429 Too Many Requests

File rendering hardening:

  • Screenshot browser request routing is deny-by-default。
  • Only local viewer assets from http://127.0.0.1/plugins/diffs/assets/* are allowed。
  • External network requests are blocked。

File mode 的 Browser requirements

mode: "file"mode: "both" 需要 Chromium-compatible browser。

Resolution order:

  1. OpenClaw config 中的 browser.executablePath
  2. Environment variables:
    • OPENCLAW_BROWSER_EXECUTABLE_PATH
    • BROWSER_EXECUTABLE_PATH
    • PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
  3. Platform command/path discovery fallback。

Common failure text:

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

通过安装 Chrome、Chromium、Edge 或 Brave 修复,或设置上述 executable path options 之一。

Troubleshooting

Input validation errors:

  • Provide patch or both before and after text.
    • Include both before and after,或 provide patch
  • Provide either patch or before/after input, not both.
    • Do not mix input modes。
  • Invalid baseUrl: ...
    • Use http(s) origin with optional path,no query/hash。
  • {field} exceeds maximum size (...)
    • Reduce payload size。
  • Large patch rejection
    • Reduce patch file count or total lines。

Viewer accessibility issues:

  • Viewer URL resolves to 127.0.0.1 by default。
  • For remote access scenarios,要么:
    • pass baseUrl per tool call,或
    • use gateway.bind=custom and gateway.customBindHost
  • Enable security.allowRemoteViewer only when you intend external viewer access。

Unmodified-lines row has no expand button:

  • This can happen for patch input when the patch does not carry expandable context。
  • This is expected and does not indicate a viewer failure。

Artifact not found:

  • Artifact expired due TTL。
  • Token or path changed。
  • Cleanup removed stale data。

Operational guidance

  • Prefer mode: "view" for local interactive reviews in canvas。
  • Prefer mode: "file" for outbound chat channels that need an attachment。
  • Keep allowRemoteViewer disabled unless your deployment requires remote viewer URLs。
  • Set explicit short ttlSeconds for sensitive diffs。
  • Avoid sending secrets in diff input when not required。
  • If your channel compresses images aggressively(例如 Telegram 或 WhatsApp),prefer PDF output(fileFormat: "pdf")。

Diff rendering engine:

Related docs