Diffs
diffs는 짧은 내장 시스템 안내와 함께, 변경 내용을 읽기 전용 diff 아티팩트로 바꿔 에이전트가 사용할 수 있게 해주는 companion skill을 제공하는 선택적 plugin 도구입니다.
다음 중 하나를 받을 수 있습니다.
before및after텍스트- 통합
patch
- canvas 표시용 gateway 뷰어 URL
- 메시지 전달용 렌더링된 파일 경로(PNG 또는 PDF)
- 한 번의 호출로 두 출력 모두
빠른 시작
- plugin을 활성화합니다.
- canvas 우선 흐름에는
mode: "view"로diffs를 호출합니다. - 채팅 파일 전달 흐름에는
mode: "file"로diffs를 호출합니다. - 두 아티팩트가 모두 필요하면
mode: "both"로diffs를 호출합니다.
plugin 활성화
내장 시스템 안내 비활성화
diffs 도구는 계속 활성화한 채 내장 system-prompt 안내만 비활성화하려면 plugins.entries.diffs.hooks.allowPromptInjection을 false로 설정하세요.
before_prompt_build hook만 차단됩니다.
안내와 도구를 모두 비활성화하려면 대신 plugin 자체를 비활성화하세요.
일반적인 에이전트 워크플로
- 에이전트가
diffs를 호출합니다. - 에이전트가
details필드를 읽습니다. - 에이전트는 다음 중 하나를 수행합니다.
canvas present로details.viewerUrl을 엽니다path또는filePath를 사용해message로details.filePath를 보냅니다- 둘 다 수행합니다
입력 예시
이전과 이후:도구 입력 참조
별도 표시가 없는 한 모든 필드는 선택 사항입니다.before(string): 원본 텍스트.patch가 없을 때after와 함께 필요합니다.after(string): 업데이트된 텍스트.patch가 없을 때before와 함께 필요합니다.patch(string): 통합 diff 텍스트.before및after와 상호 배타적입니다.path(string): 이전/이후 모드에서 표시할 파일명입니다.lang(string): 이전/이후 모드의 언어 재정의 힌트입니다. 알 수 없는 값은 일반 텍스트로 대체됩니다.title(string): 뷰어 제목 재정의입니다.mode("view" | "file" | "both"): 출력 모드입니다. 기본값은 plugin 기본값defaults.mode입니다. 사용 중단된 별칭:"image"는"file"처럼 동작하며 이전 버전 호환을 위해 여전히 허용됩니다.theme("light" | "dark"): 뷰어 테마입니다. 기본값은 plugin 기본값defaults.theme입니다.layout("unified" | "split"): diff 레이아웃입니다. 기본값은 plugin 기본값defaults.layout입니다.expandUnchanged(boolean): 전체 컨텍스트를 사용할 수 있을 때 변경되지 않은 섹션을 펼칩니다. 호출별 옵션으로만 제공되며 plugin 기본값 키는 아닙니다.fileFormat("png" | "pdf"): 렌더링된 파일 형식입니다. 기본값은 plugin 기본값defaults.fileFormat입니다.fileQuality("standard" | "hq" | "print"): PNG 또는 PDF 렌더링용 품질 프리셋입니다.fileScale(number): 디바이스 스케일 재정의입니다(1-4).fileMaxWidth(number): 최대 렌더링 너비(CSS 픽셀)입니다(640-2400).ttlSeconds(number): 뷰어 및 독립형 파일 출력의 아티팩트 TTL(초)입니다. 기본값은 1800, 최대값은 21600입니다.baseUrl(string): 뷰어 URL origin 재정의입니다. plugin의viewerBaseUrl을 재정의합니다.http또는https여야 하며 query/hash는 허용되지 않습니다.
format->fileFormatimageFormat->fileFormatimageQuality->fileQualityimageScale->fileScaleimageMaxWidth->fileMaxWidth
before와after는 각각 최대 512 KiB입니다.patch는 최대 2 MiB입니다.path는 최대 2048바이트입니다.lang는 최대 128바이트입니다.title은 최대 1024바이트입니다.- patch 복잡도 상한: 최대 128개 파일, 총 120000줄입니다.
patch와before또는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 아래에 반환합니다.
뷰어를 생성하는 모드의 공통 필드:
artifactIdviewerUrlviewerPathtitleexpiresAtinputKindfileCountmodecontext(agentId,sessionId,messageChannel,agentAccountId를 사용할 수 있을 때 포함)
artifactIdexpiresAtfilePathpath(filePath와 같은 값, message 도구 호환용)fileBytesfileFormatfileQualityfileScalefileMaxWidth
format(fileFormat와 같은 값)imagePath(filePath와 같은 값)imageBytes(fileBytes와 같은 값)imageQuality(fileQuality와 같은 값)imageScale(fileScale와 같은 값)imageMaxWidth(fileMaxWidth와 같은 값)
mode: "view": 뷰어 필드만 반환합니다.mode: "file": 파일 필드만 반환하며 뷰어 아티팩트는 생성하지 않습니다.mode: "both": 뷰어 필드와 파일 필드를 함께 반환합니다. 파일 렌더링이 실패해도 뷰어는fileError및 호환성 별칭imageError와 함께 계속 반환됩니다.
접힌 변경 없음 섹션
- 뷰어는
N unmodified lines와 같은 행을 표시할 수 있습니다. - 해당 행의 펼치기 컨트롤은 조건부이며 모든 입력 종류에서 항상 보장되지는 않습니다.
- 렌더링된 diff에 펼칠 수 있는 컨텍스트 데이터가 있을 때 펼치기 컨트롤이 나타나며, 이는 보통 이전/이후 입력에서 일반적입니다.
- 많은 통합 patch 입력에서는 생략된 컨텍스트 본문이 파싱된 patch hunk에 없으므로, 펼치기 버튼 없이 해당 행만 나타날 수 있습니다. 이는 예상된 동작입니다.
expandUnchanged는 펼칠 수 있는 컨텍스트가 있을 때만 적용됩니다.
plugin 기본값
plugin 전체 기본값은~/.openclaw/openclaw.json에서 설정하세요.
fontFamilyfontSizelineSpacinglayoutshowLineNumbersdiffIndicatorswordWrapbackgroundthemefileFormatfileQualityfileScalefileMaxWidthmode
viewerBaseUrl(string, 선택 사항)- 도구 호출에서
baseUrl을 전달하지 않았을 때 반환되는 뷰어 링크의 plugin 소유 대체값입니다. http또는https여야 하며 query/hash는 허용되지 않습니다.
- 도구 호출에서
보안 구성
security.allowRemoteViewer(boolean, 기본값false)false: 뷰어 경로에 대한 loopback 이외 요청은 거부됩니다.true: 토큰화된 경로가 유효하면 원격 뷰어가 허용됩니다.
아티팩트 수명 주기 및 저장소
- 아티팩트는 임시 하위 폴더
$TMPDIR/openclaw-diffs아래에 저장됩니다. - 뷰어 아티팩트 메타데이터에는 다음이 포함됩니다.
- 무작위 아티팩트 ID(16진수 20자)
- 무작위 토큰(16진수 48자)
createdAt및expiresAt- 저장된
viewer.html경로
- 지정하지 않으면 기본 아티팩트 TTL은 30분입니다.
- 허용되는 최대 뷰어 TTL은 6시간입니다.
- 정리는 아티팩트 생성 후 기회가 있을 때 수행됩니다.
- 만료된 아티팩트는 삭제됩니다.
- 메타데이터가 없을 때는 24시간보다 오래된 폴더를 제거하는 대체 정리가 수행됩니다.
뷰어 URL 및 네트워크 동작
뷰어 경로:/plugins/diffs/view/{artifactId}/{token}
/plugins/diffs/assets/viewer.js/plugins/diffs/assets/viewer-runtime.js
baseUrl 경로 접두사도 에셋 요청에 그대로 유지됩니다.
URL 구성 동작:
- 도구 호출의
baseUrl이 제공되면 엄격한 검증 후 사용됩니다. - 그렇지 않고 plugin
viewerBaseUrl이 구성되어 있으면 그것이 사용됩니다. - 두 재정의 모두 없으면 뷰어 URL은 기본적으로 loopback
127.0.0.1을 사용합니다. - gateway bind 모드가
custom이고gateway.customBindHost가 설정되어 있으면 해당 호스트가 사용됩니다.
baseUrl 규칙:
- 반드시
http://또는https://여야 합니다. - query 및 hash는 거부됩니다.
- origin과 선택적 기본 경로는 허용됩니다.
보안 모델
뷰어 강화:- 기본적으로 loopback 전용입니다.
- 엄격한 ID 및 토큰 검증이 있는 토큰화된 뷰어 경로를 사용합니다.
- 뷰어 응답 CSP:
default-src 'none'- script와 에셋은 self에서만 허용
- 외부
connect-src없음
- 원격 액세스가 활성화된 경우 원격 miss 제한:
- 60초 동안 실패 40회
- 60초 잠금 (
429 Too Many Requests)
- 스크린샷 브라우저 요청 라우팅은 기본 거부입니다.
http://127.0.0.1/plugins/diffs/assets/*의 로컬 뷰어 에셋만 허용됩니다.- 외부 네트워크 요청은 차단됩니다.
파일 모드용 브라우저 요구 사항
mode: "file" 및 mode: "both"에는 Chromium 호환 브라우저가 필요합니다.
확인 순서:
- OpenClaw 구성의
browser.executablePath. - 환경 변수:
OPENCLAW_BROWSER_EXECUTABLE_PATHBROWSER_EXECUTABLE_PATHPLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
- 플랫폼 명령/경로 검색 대체 방식.
Diff PNG/PDF rendering requires a Chromium-compatible browser...
문제 해결
입력 검증 오류:Provide patch or both before and after text.before와after를 모두 포함하거나patch를 제공하세요.
Provide either patch or before/after input, not both.- 입력 모드를 혼합하지 마세요.
Invalid baseUrl: ...- query/hash 없이 선택적 경로가 있는
http(s)origin을 사용하세요.
- query/hash 없이 선택적 경로가 있는
{field} exceeds maximum size (...)- 페이로드 크기를 줄이세요.
- 큰 patch 거부
- patch 파일 수 또는 총 줄 수를 줄이세요.
- 뷰어 URL은 기본적으로
127.0.0.1로 확인됩니다. - 원격 액세스 시나리오에서는 다음 중 하나를 사용하세요.
- plugin
viewerBaseUrl설정 - 도구 호출마다
baseUrl전달 gateway.bind=custom및gateway.customBindHost사용
- plugin
gateway.trustedProxies에 동일 호스트 프록시(예: Tailscale Serve)를 위한 loopback이 포함되면, 전달된 클라이언트 IP 헤더가 없는 원시 loopback 뷰어 요청은 설계상 fail closed됩니다.- 이 프록시 토폴로지에서는:
- 첨부 파일만 필요하면
mode: "file"또는mode: "both"를 우선 사용하거나 - 공유 가능한 뷰어 URL이 필요하면 의도적으로
security.allowRemoteViewer를 활성화하고 pluginviewerBaseUrl을 설정하거나 프록시/공용baseUrl을 전달하세요
- 첨부 파일만 필요하면
- 외부 뷰어 액세스가 정말 필요할 때만
security.allowRemoteViewer를 활성화하세요.
- patch 입력에 확장 가능한 컨텍스트가 포함되지 않으면 이런 일이 발생할 수 있습니다.
- 이는 예상된 동작이며 뷰어 실패를 의미하지 않습니다.
- 아티팩트가 TTL 만료로 삭제되었습니다.
- 토큰 또는 경로가 변경되었습니다.
- 정리 작업이 오래된 데이터를 제거했습니다.
운영 지침
- canvas에서 로컬 대화형 검토에는
mode: "view"를 우선 사용하세요. - 첨부 파일이 필요한 외부 채팅 채널에는
mode: "file"을 우선 사용하세요. - 배포 환경에서 원격 뷰어 URL이 필요하지 않으면
allowRemoteViewer는 비활성화 상태로 유지하세요. - 민감한 diff에는 명시적으로 짧은
ttlSeconds를 설정하세요. - 필요하지 않으면 diff 입력에 비밀 정보를 포함하지 마세요.
- 채널이 이미지를 강하게 압축하는 경우(예: Telegram 또는 WhatsApp) PDF 출력(
fileFormat: "pdf")을 우선 사용하세요.
- Diffs 기반입니다.