Diffs
diffs is an optional plugin tool and companion skill that turns change content into a read-only diff artifact for agents.
It accepts either:
beforeandaftertext- a unified
patch
- a gateway viewer URL for canvas presentation
- a rendered file path (PNG or PDF) for message delivery
- both outputs in one call
Quick start
- Enable the plugin.
- Call
diffswithmode: "view"for canvas-first flows. - Call
diffswithmode: "file"for chat file delivery flows. - Call
diffswithmode: "both"when you need both artifacts.
Enable the plugin
Typical agent workflow
- Agent calls
diffs. - Agent reads
detailsfields. - Agent either:
- opens
details.viewerUrlwithcanvas present - sends
details.filePathwithmessageusingpathorfilePath - does both
- opens
Input examples
Before and after:Tool input reference
All fields are optional unless noted:before(string): original text. Required withafterwhenpatchis omitted.after(string): updated text. Required withbeforewhenpatchis omitted.patch(string): unified diff text. Mutually exclusive withbeforeandafter.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 defaultdefaults.mode.theme("light" | "dark"): viewer theme. Defaults to plugin defaultdefaults.theme.layout("unified" | "split"): diff layout. Defaults to plugin defaultdefaults.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 defaultdefaults.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 behttporhttps, no query/hash.
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
The tool returns structured metadata underdetails.
Shared fields for modes that create a viewer:
artifactIdviewerUrlviewerPathtitleexpiresAtinputKindfileCountmode
filePathpath(same value asfilePath, for message tool compatibility)fileBytesfileFormatfileQualityfileScalefileMaxWidth
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 withfileError.
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
Set plugin-wide defaults in~/.openclaw/openclaw.json:
fontFamilyfontSizelineSpacinglayoutshowLineNumbersdiffIndicatorswordWrapbackgroundthemefileFormatfileQualityfileScalefileMaxWidthmode
Security config
security.allowRemoteViewer(boolean, defaultfalse)false: non-loopback requests to viewer routes are denied.true: remote viewers are allowed if tokenized path is valid.
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 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}
/plugins/diffs/assets/viewer.js/plugins/diffs/assets/viewer-runtime.js
- If
baseUrlis provided, it is used after strict validation. - Without
baseUrl, viewer URL defaults to loopback127.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
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)
- 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:
browser.executablePathin OpenClaw config.- Environment variables:
OPENCLAW_BROWSER_EXECUTABLE_PATHBROWSER_EXECUTABLE_PATHPLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
- Platform command/path discovery fallback.
Diff PNG/PDF rendering requires a Chromium-compatible browser...
Troubleshooting
Input validation errors:Provide patch or both before and after text.- Include both
beforeandafter, or providepatch.
- Include both
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.
- Use
{field} exceeds maximum size (...)- Reduce payload size.
- Large patch rejection
- Reduce patch file count or total lines.
- Viewer URL resolves to
127.0.0.1by default. - For remote access scenarios, either:
- pass
baseUrlper tool call, or - use
gateway.bind=customandgateway.customBindHost
- pass
- Enable
security.allowRemoteVieweronly when you intend external viewer access.
- 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 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
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").
- Powered by Diffs.