Redact home paths in transcript views

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-03-11 20:56:47 -05:00
parent 3b9da0ee95
commit ee3d8c1890
6 changed files with 146 additions and 44 deletions

View File

@@ -1,3 +1,4 @@
import { redactHomePathUserSegments, redactTranscriptEntryPaths } from "@paperclipai/adapter-utils";
import type { TranscriptEntry, StdoutLineParser } from "./types";
export type RunLogChunk = { ts: string; stream: "stdout" | "stderr" | "system"; chunk: string };
@@ -26,11 +27,11 @@ export function buildTranscript(chunks: RunLogChunk[], parser: StdoutLineParser)
for (const chunk of chunks) {
if (chunk.stream === "stderr") {
entries.push({ kind: "stderr", ts: chunk.ts, text: chunk.chunk });
entries.push({ kind: "stderr", ts: chunk.ts, text: redactHomePathUserSegments(chunk.chunk) });
continue;
}
if (chunk.stream === "system") {
entries.push({ kind: "system", ts: chunk.ts, text: chunk.chunk });
entries.push({ kind: "system", ts: chunk.ts, text: redactHomePathUserSegments(chunk.chunk) });
continue;
}
@@ -40,14 +41,14 @@ export function buildTranscript(chunks: RunLogChunk[], parser: StdoutLineParser)
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
appendTranscriptEntries(entries, parser(trimmed, chunk.ts));
appendTranscriptEntries(entries, parser(trimmed, chunk.ts).map(redactTranscriptEntryPaths));
}
}
const trailing = stdoutBuffer.trim();
if (trailing) {
const ts = chunks.length > 0 ? chunks[chunks.length - 1]!.ts : new Date().toISOString();
appendTranscriptEntries(entries, parser(trailing, ts));
appendTranscriptEntries(entries, parser(trailing, ts).map(redactTranscriptEntryPaths));
}
return entries;

View File

@@ -59,6 +59,7 @@ import { Input } from "@/components/ui/input";
import { AgentIcon, AgentIconPicker } from "../components/AgentIconPicker";
import { RunTranscriptView, type TranscriptMode } from "../components/transcript/RunTranscriptView";
import { isUuidLike, type Agent, type HeartbeatRun, type HeartbeatRunEvent, type AgentRuntimeState, type LiveEvent } from "@paperclipai/shared";
import { redactHomePathUserSegments, redactHomePathUserSegmentsInValue } from "@paperclipai/adapter-utils";
import { agentRouteRef } from "../lib/utils";
const runStatusIcons: Record<string, { icon: typeof CheckCircle2; color: string }> = {
@@ -92,11 +93,11 @@ function redactEnvValue(key: string, value: unknown): string {
}
if (shouldRedactSecretValue(key, value)) return REDACTED_ENV_VALUE;
if (value === null || value === undefined) return "";
if (typeof value === "string") return value;
if (typeof value === "string") return redactHomePathUserSegments(value);
try {
return JSON.stringify(value);
return JSON.stringify(redactHomePathUserSegmentsInValue(value));
} catch {
return String(value);
return redactHomePathUserSegments(String(value));
}
}
@@ -2023,7 +2024,7 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
const adapterInvokePayload = useMemo(() => {
const evt = events.find((e) => e.eventType === "adapter.invoke");
return asRecord(evt?.payload ?? null);
return redactHomePathUserSegmentsInValue(asRecord(evt?.payload ?? null));
}, [events]);
const adapter = useMemo(() => getUIAdapter(adapterType), [adapterType]);
@@ -2096,8 +2097,8 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
<div className="text-xs text-muted-foreground mb-1">Prompt</div>
<pre className="bg-neutral-100 dark:bg-neutral-950 rounded-md p-2 text-xs overflow-x-auto whitespace-pre-wrap">
{typeof adapterInvokePayload.prompt === "string"
? adapterInvokePayload.prompt
: JSON.stringify(adapterInvokePayload.prompt, null, 2)}
? redactHomePathUserSegments(adapterInvokePayload.prompt)
: JSON.stringify(redactHomePathUserSegmentsInValue(adapterInvokePayload.prompt), null, 2)}
</pre>
</div>
)}
@@ -2105,7 +2106,7 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
<div>
<div className="text-xs text-muted-foreground mb-1">Context</div>
<pre className="bg-neutral-100 dark:bg-neutral-950 rounded-md p-2 text-xs overflow-x-auto whitespace-pre-wrap">
{JSON.stringify(adapterInvokePayload.context, null, 2)}
{JSON.stringify(redactHomePathUserSegmentsInValue(adapterInvokePayload.context), null, 2)}
</pre>
</div>
)}
@@ -2189,14 +2190,14 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
{run.error && (
<div className="text-xs text-red-600 dark:text-red-200">
<span className="text-red-700 dark:text-red-300">Error: </span>
{run.error}
{redactHomePathUserSegments(run.error)}
</div>
)}
{run.stderrExcerpt && run.stderrExcerpt.trim() && (
<div>
<div className="text-xs text-red-700 dark:text-red-300 mb-1">stderr excerpt</div>
<pre className="bg-red-50 dark:bg-neutral-950 rounded-md p-2 text-xs overflow-x-auto whitespace-pre-wrap text-red-800 dark:text-red-100">
{run.stderrExcerpt}
{redactHomePathUserSegments(run.stderrExcerpt)}
</pre>
</div>
)}
@@ -2204,7 +2205,7 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
<div>
<div className="text-xs text-red-700 dark:text-red-300 mb-1">adapter result JSON</div>
<pre className="bg-red-50 dark:bg-neutral-950 rounded-md p-2 text-xs overflow-x-auto whitespace-pre-wrap text-red-800 dark:text-red-100">
{JSON.stringify(run.resultJson, null, 2)}
{JSON.stringify(redactHomePathUserSegmentsInValue(run.resultJson), null, 2)}
</pre>
</div>
)}
@@ -2212,7 +2213,7 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
<div>
<div className="text-xs text-red-700 dark:text-red-300 mb-1">stdout excerpt</div>
<pre className="bg-red-50 dark:bg-neutral-950 rounded-md p-2 text-xs overflow-x-auto whitespace-pre-wrap text-red-800 dark:text-red-100">
{run.stdoutExcerpt}
{redactHomePathUserSegments(run.stdoutExcerpt)}
</pre>
</div>
)}
@@ -2238,7 +2239,11 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
{evt.stream ? `[${evt.stream}]` : ""}
</span>
<span className={cn("break-all", color)}>
{evt.message ?? (evt.payload ? JSON.stringify(evt.payload) : "")}
{evt.message
? redactHomePathUserSegments(evt.message)
: evt.payload
? JSON.stringify(redactHomePathUserSegmentsInValue(evt.payload))
: ""}
</span>
</div>
);