diff --git a/src/agents/tool-display-common.ts b/src/agents/tool-display-common.ts index c5b138336b8..62025335d8d 100644 --- a/src/agents/tool-display-common.ts +++ b/src/agents/tool-display-common.ts @@ -3,7 +3,7 @@ import { normalizeOptionalString, } from "@openclaw/normalization-core/string-coerce"; import { parseStrictFiniteNumber } from "../infra/parse-finite-number.js"; -import { redactToolDetail } from "../logging/redact.js"; +import { redactToolPayloadText } from "../logging/redact.js"; import { resolveExecDetail, type ToolDetailMode } from "./tool-display-exec.js"; import { asRecord } from "./tool-display-record.js"; @@ -121,7 +121,7 @@ function coerceDisplayValue( if (!rawLine) { return undefined; } - const firstLine = redactToolDetail(rawLine); + const firstLine = redactToolPayloadText(rawLine); if (firstLine.length > maxStringChars) { const half = Math.floor((maxStringChars - 1) / 2); return `${firstLine.slice(0, half)}…${firstLine.slice(-(maxStringChars - 1 - half))}`; diff --git a/src/agents/tool-display-exec.ts b/src/agents/tool-display-exec.ts index 430e64cb32c..d98addefafb 100644 --- a/src/agents/tool-display-exec.ts +++ b/src/agents/tool-display-exec.ts @@ -1,4 +1,4 @@ -import { redactToolDetail } from "../logging/redact.js"; +import { redactToolPayloadText } from "../logging/redact.js"; import { binaryName, firstPositional, @@ -427,7 +427,7 @@ function isGenericSummary(summary: string): boolean { } function compactRawCommand(raw: string, maxLength = 120): string { - const oneLine = redactToolDetail( + const oneLine = redactToolPayloadText( raw .replace(/\s*\n\s*/g, " ") .replace(/\s{2,}/g, " ") diff --git a/src/agents/tool-display.test.ts b/src/agents/tool-display.test.ts index 6de74ba9c84..78e1865a0da 100644 --- a/src/agents/tool-display.test.ts +++ b/src/agents/tool-display.test.ts @@ -532,6 +532,15 @@ describe("compactRawCommand middle truncation", () => { // The sk- prefixed token must be redacted (masked) before truncation expect(result).not.toContain("ABCDEFGHIJKLMNOP1234567890abcdefghij"); }); + + it("uses the canonical tool payload redactor before compacting raw commands", () => { + const longCommand = + "/opt/custom/bin/deploy --aws-key AKIDABCDEFGHIJKLMNOP1234567890 --output /data/results/deploy-output.json"; + const result = resolveExecDetail({ command: longCommand }); + + expect(result).not.toContain("AKIDABCDEFGHIJKLMNOP1234567890"); + expect(result).toContain("AKIDAB…7890"); + }); }); describe("coerceDisplayValue middle truncation", () => { @@ -582,4 +591,20 @@ describe("coerceDisplayValue middle truncation", () => { // The ghp_ token must be redacted before truncation expect(detail).not.toContain("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop"); }); + + it("uses the canonical tool payload redactor before compacting string details", () => { + const longValue = + "Deploying with AWS key AKIDABCDEFGHIJKLMNOP1234567890 and " + + "x".repeat(200) + + " final-step"; + const detail = formatToolDetail( + resolveToolDisplay({ + name: "sessions_spawn", + args: { task: longValue }, + }), + ); + + expect(detail).not.toContain("AKIDABCDEFGHIJKLMNOP1234567890"); + expect(detail).toContain("AKIDAB…7890"); + }); }); diff --git a/src/gateway/events.ts b/src/gateway/events.ts index 2697dd3448a..26ea50ad9a1 100644 --- a/src/gateway/events.ts +++ b/src/gateway/events.ts @@ -1,7 +1,11 @@ -import type { UpdateAvailable } from "../infra/update-startup.js"; - export const GATEWAY_EVENT_UPDATE_AVAILABLE = "update.available" as const; -export type GatewayUpdateAvailableEventPayload = { - updateAvailable: UpdateAvailable | null; +export type UpdateAvailableEventData = { + currentVersion: string; + latestVersion: string; + channel: string; +}; + +export type GatewayUpdateAvailableEventPayload = { + updateAvailable: UpdateAvailableEventData | null; }; diff --git a/ui/src/ui/browser-redact.test.ts b/ui/src/ui/browser-redact.test.ts index 8a7254931e8..71ee4704f82 100644 --- a/ui/src/ui/browser-redact.test.ts +++ b/ui/src/ui/browser-redact.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { redactToolDetail } from "./browser-redact.ts"; +import { redactToolDetail, redactToolPayloadText } from "./browser-redact.ts"; describe("browser tool detail redaction", () => { it("redacts tool detail credential families without Node config imports", () => { @@ -29,4 +29,10 @@ describe("browser tool detail redaction", () => { expect(redacted).not.toContain("abc123"); expect(redacted).not.toContain("verySensitiveCookieValue"); }); + + it("exposes the tool payload redaction name used by shared display modules", () => { + expect(redactToolPayloadText("OPENAI_API_KEY=sk-1234567890abcdef")).toBe( + "OPENAI_API_KEY=sk-123...cdef", + ); + }); }); diff --git a/ui/src/ui/browser-redact.ts b/ui/src/ui/browser-redact.ts index d79733610d7..fe8cc1af151 100644 --- a/ui/src/ui/browser-redact.ts +++ b/ui/src/ui/browser-redact.ts @@ -74,3 +74,5 @@ export function redactToolDetail(detail: string): string { } return redacted; } + +export const redactToolPayloadText = redactToolDetail;