fix(coding-agent/debug): Sanitized debug log rendering
- Sanitized debug log lines by stripping ANSI/control codes, replacing tabs, and truncating to terminal width. - Added formatting helper and unit tests covering sanitization, tab handling, and truncation.
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
|
||||
- Fixed `session.abort()` not clearing `promptInFlight` flag due to microtask ordering, which blocked subsequent prompts
|
||||
|
||||
### Fixed
|
||||
- Sanitized debug log display to strip control codes, normalize tabs, and trim width
|
||||
|
||||
## [12.4.0] - 2026-02-14
|
||||
### Changed
|
||||
|
||||
@@ -69,6 +72,7 @@
|
||||
- Improved error reporting in fetch tool to include HTTP status codes when URL fetching fails
|
||||
- Fixed fetch tool to preserve actual response metadata (finalUrl, contentType) instead of defaults when requests fail
|
||||
|
||||
||||||| parent of a70a34c8b (fix(coding-agent/debug): Sanitized debug log rendering)
|
||||
## [12.1.0] - 2026-02-13
|
||||
|
||||
### Added
|
||||
|
||||
@@ -11,6 +11,7 @@ import { DynamicBorder } from "../modes/components/dynamic-border";
|
||||
import { getSelectListTheme, getSymbolTheme, theme } from "../modes/theme/theme";
|
||||
import type { InteractiveModeContext } from "../modes/types";
|
||||
import { openPath } from "../utils/open";
|
||||
import { formatDebugLogLine } from "./log-formatting";
|
||||
import { generateHeapSnapshotData, type ProfilerSession, startCpuProfile } from "./profiler";
|
||||
import { clearArtifactCache, createReportBundle, getArtifactCacheStats, getRecentLogs } from "./report-bundle";
|
||||
import { collectSystemInfo, formatSystemInfo } from "./system-info";
|
||||
@@ -284,10 +285,12 @@ export class DebugSelectorComponent extends Container {
|
||||
this.ctx.chatContainer.addChild(new Spacer(1));
|
||||
|
||||
// Display logs with dim styling
|
||||
const maxWidth = Math.max(1, this.ctx.ui.terminal.columns - 2);
|
||||
const lines = logs.split("\n").slice(-50);
|
||||
for (const line of lines) {
|
||||
if (line.trim()) {
|
||||
this.ctx.chatContainer.addChild(new Text(theme.fg("dim", line), 1, 0));
|
||||
const formatted = formatDebugLogLine(line, maxWidth);
|
||||
if (formatted.trim()) {
|
||||
this.ctx.chatContainer.addChild(new Text(theme.fg("dim", formatted), 1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { sanitizeText } from "@oh-my-pi/pi-utils";
|
||||
import { replaceTabs, truncateToWidth } from "../tools/render-utils";
|
||||
|
||||
export function formatDebugLogLine(line: string, maxWidth: number): string {
|
||||
const sanitized = sanitizeText(line);
|
||||
const normalized = replaceTabs(sanitized);
|
||||
const width = Math.max(1, maxWidth);
|
||||
return truncateToWidth(normalized, width);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { formatDebugLogLine } from "../../src/debug/log-formatting";
|
||||
|
||||
describe("formatDebugLogLine", () => {
|
||||
it("strips ANSI codes and carriage returns", () => {
|
||||
const input = "\u001b[31merror\r\u001b[0m";
|
||||
const result = formatDebugLogLine(input, 80);
|
||||
expect(result).toBe("error");
|
||||
});
|
||||
|
||||
it("replaces tabs with spaces", () => {
|
||||
const input = "col1\tcol2";
|
||||
const result = formatDebugLogLine(input, 80);
|
||||
expect(result).toBe("col1 col2");
|
||||
});
|
||||
|
||||
it("removes unsafe control characters", () => {
|
||||
const input = "ok\u0007bad";
|
||||
const result = formatDebugLogLine(input, 80);
|
||||
expect(result).toBe("okbad");
|
||||
});
|
||||
|
||||
it("truncates long lines", () => {
|
||||
const input = "0123456789ABCDEFGHIJ";
|
||||
const result = formatDebugLogLine(input, 10);
|
||||
expect(Bun.stringWidth(result)).toBeLessThanOrEqual(10);
|
||||
expect(result.startsWith("012345")).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user