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:
beforeandaftertext- 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
Section titled “Quick start”Install the plugin
Terminal window openclaw plugins install diffsEnable the plugin
{plugins: {entries: {diffs: {enabled: true,},},},}Pick a mode
Canvas-first flows: agents call
diffswithmode: "view"and opendetails.viewerUrlwithcanvas present.Chat file delivery: agents call
diffswithmode: "file"and senddetails.filePathwithmessageusingpathorfilePath.Combined: agents call
diffswithmode: "both"to get both artifacts in one call.
Disable built-in system guidance
Section titled “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:
{ 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
Section titled “Typical agent workflow”Call diffs
Agent calls the
diffstool with input.Read details
Agent reads
detailsfields from the response.Present
Agent either opens
details.viewerUrlwithcanvas present, sendsdetails.filePathwithmessageusingpathorfilePath, or does both.
Input examples
Section titled “Input examples”{ "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"}Tool input reference
Section titled “Tool input reference”All fields are optional unless noted.
Legacy input aliases
Still accepted for backward compatibility:
format->fileFormatimageFormat->fileFormatimageQuality->fileQualityimageScale->fileScaleimageMaxWidth->fileMaxWidth
Validation and limits
beforeandaftereach max 512 KiB.patchmax 2 MiB.pathmax 2048 bytes.langmax 128 bytes.titlemax 1024 bytes.- Patch complexity cap: max 128 files and 120000 total lines.
patchandbeforeoraftertogether 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
Section titled “Output details contract”The tool returns structured metadata under details.
Viewer fields
Shared fields for modes that create a viewer:
artifactIdviewerUrlviewerPathtitleexpiresAtinputKindfileCountmodecontext(agentId,sessionId,messageChannel,agentAccountIdwhen available)
File fields
File fields when PNG or PDF is rendered:
artifactIdexpiresAtfilePathpath(same value asfilePath, for message tool compatibility)fileBytesfileFormatfileQualityfileScalefileMaxWidth
Compatibility aliases
Also returned for existing callers:
format(same value asfileFormat)imagePath(same value asfilePath)imageBytes(same value asfileBytes)imageQuality(same value asfileQuality)imageScale(same value asfileScale)imageMaxWidth(same value asfileMaxWidth)
Mode behavior summary:
| Mode | What is returned |
|---|---|
"view" | Viewer fields only. |
"file" | File fields only, no viewer artifact. |
"both" | Viewer fields plus file fields. If file rendering fails, viewer still returns with fileError and imageError alias. |
Collapsed unchanged sections
Section titled “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.
expandUnchangedapplies only when expandable context exists.
Plugin defaults
Section titled “Plugin defaults”Set plugin-wide defaults in ~/.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", ttlSeconds: 21600, }, }, }, }, },}Supported defaults:
fontFamilyfontSizelineSpacinglayoutshowLineNumbersdiffIndicatorswordWrapbackgroundthemefileFormatfileQualityfileScalefileMaxWidthmodettlSeconds
Explicit tool parameters override these defaults.
Persistent viewer URL config
Section titled “Persistent viewer URL config”{ plugins: { entries: { diffs: { enabled: true, config: { viewerBaseUrl: "https://gateway.example.com/openclaw", }, }, }, },}Security config
Section titled “Security config”{ plugins: { entries: { diffs: { enabled: true, config: { security: { allowRemoteViewer: false, }, }, }, }, },}Artifact lifecycle and storage
Section titled “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)
createdAtandexpiresAt- stored
viewer.htmlpath
- Default artifact 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
Section titled “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
The viewer document resolves those assets relative to the viewer URL, so an optional baseUrl path prefix is preserved for both asset requests too.
URL construction behavior:
- If tool-call
baseUrlis provided, it is used after strict validation. - Else if plugin
viewerBaseUrlis configured, it is used. - Without either override, viewer URL defaults to loopback
127.0.0.1. - If gateway bind mode is
customandgateway.customBindHostis set, that host is used.
baseUrl rules:
- Must be
http://orhttps://. - Query and hash are rejected.
- Origin plus optional base path is allowed.
Security model
Section titled “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
Section titled “Browser requirements for file mode”mode: "file" and mode: "both" need a Chromium-compatible browser.
Resolution order:
Config
browser.executablePathin OpenClaw config.Environment variables
OPENCLAW_BROWSER_EXECUTABLE_PATHBROWSER_EXECUTABLE_PATHPLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
Platform fallback
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
Section titled “Troubleshooting”Input validation errors
Provide patch or both before and after text.— include bothbeforeandafter, or providepatch.Provide either patch or before/after input, not both.— do not mix input modes.Invalid baseUrl: ...— usehttp(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
- Viewer URL resolves to
127.0.0.1by default. - For remote access scenarios, either:
- set plugin
viewerBaseUrl, or - pass
baseUrlper tool call, or - use
gateway.bind=customandgateway.customBindHost
- set plugin
- If
gateway.trustedProxiesincludes loopback for a same-host proxy (for example Tailscale Serve), raw loopback viewer requests without forwarded client-IP headers fail closed by design. - For that proxy topology:
- prefer
mode: "file"ormode: "both"when you only need an attachment, or - intentionally enable
security.allowRemoteViewerand set pluginviewerBaseUrlor pass a proxy/publicbaseUrlwhen you need a shareable viewer URL
- prefer
- Enable
security.allowRemoteVieweronly 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
Section titled “Operational guidance”- Prefer
mode: "view"for local interactive reviews in canvas. - Prefer
mode: "file"for outbound chat channels that need an attachment. - Keep
allowRemoteViewerdisabled unless your deployment requires remote viewer URLs. - Set explicit short
ttlSecondsfor 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").