File size: 11,144 Bytes
fc93158 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | ---
title: "Diffs"
summary: "Read-only diff viewer and file renderer for agents (optional plugin tool)"
description: "Use the optional Diffs plugin to render before and after text or unified patches as a gateway-hosted diff view, a file (PNG or PDF), or both."
read_when:
- You want agents to show code or markdown edits as diffs
- You want a canvas-ready viewer URL or a rendered diff file
- You need controlled, temporary diff artifacts with secure defaults
---
# Diffs
`diffs` is an optional plugin tool with short built-in system guidance and a companion skill that turns change content into a read-only diff artifact for agents.
It accepts either:
- `before` and `after` text
- a unified `patch`
It can return:
- a gateway viewer URL for canvas presentation
- a rendered file path (PNG or PDF) for message delivery
- both outputs in one call
When enabled, the plugin prepends concise usage guidance into system-prompt space and also exposes a detailed skill for cases where the agent needs fuller instructions.
## Quick start
1. Enable the plugin.
2. Call `diffs` with `mode: "view"` for canvas-first flows.
3. Call `diffs` with `mode: "file"` for chat file delivery flows.
4. Call `diffs` with `mode: "both"` when you need both artifacts.
## Enable the plugin
```json5
{
plugins: {
entries: {
diffs: {
enabled: true,
},
},
},
}
```
## Disable built-in system guidance
If you want to keep the `diffs` tool enabled but disable its built-in system-prompt guidance, set `plugins.entries.diffs.hooks.allowPromptInjection` to `false`:
```json5
{
plugins: {
entries: {
diffs: {
enabled: true,
hooks: {
allowPromptInjection: false,
},
},
},
},
}
```
This blocks the diffs plugin's `before_prompt_build` hook while keeping the plugin, tool, and companion skill available.
If you want to disable both the guidance and the tool, disable the plugin instead.
## Typical agent workflow
1. Agent calls `diffs`.
2. Agent reads `details` fields.
3. Agent either:
- opens `details.viewerUrl` with `canvas present`
- sends `details.filePath` with `message` using `path` or `filePath`
- does both
## Input examples
Before and after:
```json
{
"before": "# Hello\n\nOne",
"after": "# Hello\n\nTwo",
"path": "docs/example.md",
"mode": "view"
}
```
Patch:
```json
{
"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
All fields are optional unless noted:
- `before` (`string`): original text. Required with `after` when `patch` is omitted.
- `after` (`string`): updated text. Required with `before` when `patch` is omitted.
- `patch` (`string`): unified diff text. Mutually exclusive with `before` and `after`.
- `path` (`string`): display filename for before and after mode.
- `lang` (`string`): language override hint for before and after mode.
- `title` (`string`): viewer title override.
- `mode` (`"view" | "file" | "both"`): output mode. Defaults to plugin default `defaults.mode`.
- `theme` (`"light" | "dark"`): viewer theme. Defaults to plugin default `defaults.theme`.
- `layout` (`"unified" | "split"`): diff layout. Defaults to plugin default `defaults.layout`.
- `expandUnchanged` (`boolean`): expand unchanged sections when full context is available. Per-call option only (not a plugin default key).
- `fileFormat` (`"png" | "pdf"`): rendered file format. Defaults to plugin default `defaults.fileFormat`.
- `fileQuality` (`"standard" | "hq" | "print"`): quality preset for PNG or PDF rendering.
- `fileScale` (`number`): device scale override (`1`-`4`).
- `fileMaxWidth` (`number`): max render width in CSS pixels (`640`-`2400`).
- `ttlSeconds` (`number`): viewer artifact TTL in seconds. Default 1800, max 21600.
- `baseUrl` (`string`): viewer URL origin override. Must be `http` or `https`, no query/hash.
Validation and limits:
- `before` and `after` each 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 and 120000 total lines.
- `patch` and `before` or `after` together are rejected.
- Rendered file safety limits (apply to PNG and 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
The tool returns structured metadata under `details`.
Shared fields for modes that create a viewer:
- `artifactId`
- `viewerUrl`
- `viewerPath`
- `title`
- `expiresAt`
- `inputKind`
- `fileCount`
- `mode`
File fields when PNG or PDF is rendered:
- `filePath`
- `path` (same value as `filePath`, for message tool compatibility)
- `fileBytes`
- `fileFormat`
- `fileQuality`
- `fileScale`
- `fileMaxWidth`
Mode behavior summary:
- `mode: "view"`: viewer fields only.
- `mode: "file"`: file fields only, no viewer artifact.
- `mode: "both"`: viewer fields plus file fields. If file rendering fails, viewer still returns with `fileError`.
## Collapsed unchanged sections
- The viewer can show rows like `N unmodified lines`.
- Expand controls on those rows are conditional and not guaranteed for every input kind.
- Expand controls appear when the rendered diff has expandable context data, which is typical for before and after input.
- For many unified patch inputs, omitted context bodies are not available in the parsed patch hunks, so the row can appear without expand controls. This is expected behavior.
- `expandUnchanged` applies only when expandable context exists.
## Plugin defaults
Set plugin-wide defaults in `~/.openclaw/openclaw.json`:
```json5
{
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.allowRemoteViewer` (`boolean`, default `false`)
- `false`: non-loopback requests to viewer routes are denied.
- `true`: remote viewers are allowed if tokenized path is valid.
Example:
```json5
{
plugins: {
entries: {
diffs: {
enabled: true,
config: {
security: {
allowRemoteViewer: false,
},
},
},
},
},
}
```
## Artifact lifecycle and storage
- Artifacts are stored under the temp subfolder: `$TMPDIR/openclaw-diffs`.
- Viewer artifact metadata contains:
- random artifact ID (20 hex chars)
- random token (48 hex chars)
- `createdAt` and `expiresAt`
- 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:
- If `baseUrl` is provided, it is used after strict validation.
- Without `baseUrl`, viewer URL defaults to loopback `127.0.0.1`.
- If gateway bind mode is `custom` and `gateway.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.
## Browser requirements for file mode
`mode: "file"` and `mode: "both"` need a Chromium-compatible browser.
Resolution order:
1. `browser.executablePath` in OpenClaw config.
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...`
Fix by installing Chrome, Chromium, Edge, or Brave, or setting one of the executable path options above.
## Troubleshooting
Input validation errors:
- `Provide patch or both before and after text.`
- Include both `before` and `after`, or 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, either:
- pass `baseUrl` per tool call, or
- 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 (for example Telegram or WhatsApp), prefer PDF output (`fileFormat: "pdf"`).
Diff rendering engine:
- Powered by [Diffs](https://diffs.com).
## Related docs
- [Tools overview](/tools)
- [Plugins](/tools/plugin)
- [Browser](/tools/browser)
|