From 7858376202b1a230e181ecf47149d74ace3a0cf9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:35:51 +0000 Subject: [PATCH 1/2] Initial plan From 1fd48465ed56ee2a089362f1cc771252df1bb610 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:47:43 +0000 Subject: [PATCH 2/2] Wrap agent log rendering in default open section - Add wrapAgentLogInSection function to log_parser_shared.cjs - Update log_parser_bootstrap.cjs to wrap agent logs in details/summary - Add comprehensive tests for the new wrapper function - Update existing tests to expect wrapped output - All JavaScript tests passing Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/log_parser_bootstrap.cjs | 20 +++++-- .../setup/js/log_parser_bootstrap.test.cjs | 4 +- actions/setup/js/log_parser_shared.cjs | 23 +++++++ actions/setup/js/log_parser_shared.test.cjs | 60 +++++++++++++++++++ 4 files changed, 101 insertions(+), 6 deletions(-) diff --git a/actions/setup/js/log_parser_bootstrap.cjs b/actions/setup/js/log_parser_bootstrap.cjs index 172125c4f83..782da3d536a 100644 --- a/actions/setup/js/log_parser_bootstrap.cjs +++ b/actions/setup/js/log_parser_bootstrap.cjs @@ -1,7 +1,7 @@ // @ts-check /// -const { generatePlainTextSummary, generateCopilotCliStyleSummary, formatSafeOutputsPreview } = require("./log_parser_shared.cjs"); +const { generatePlainTextSummary, generateCopilotCliStyleSummary, wrapAgentLogInSection, formatSafeOutputsPreview } = require("./log_parser_shared.cjs"); const { getErrorMessage } = require("./error_helpers.cjs"); /** @@ -157,8 +157,14 @@ async function runLogParser(options) { parserName, }); + // Wrap the agent log in a details/summary section (open by default) + const wrappedAgentLog = wrapAgentLogInSection(copilotCliStyleMarkdown, { + parserName, + open: true, + }); + // Add safe outputs preview to step summary - let fullMarkdown = copilotCliStyleMarkdown; + let fullMarkdown = wrappedAgentLog; if (safeOutputsContent) { const safeOutputsMarkdown = formatSafeOutputsPreview(safeOutputsContent, { isPlainText: false }); if (safeOutputsMarkdown) { @@ -179,8 +185,14 @@ async function runLogParser(options) { } } - // Write original markdown to step summary if available - let fullMarkdown = markdown; + // Wrap the original markdown in a details/summary section (open by default) + const wrappedAgentLog = wrapAgentLogInSection(markdown, { + parserName, + open: true, + }); + + // Write wrapped markdown to step summary if available + let fullMarkdown = wrappedAgentLog; if (safeOutputsContent) { const safeOutputsMarkdown = formatSafeOutputsPreview(safeOutputsContent, { isPlainText: false }); if (safeOutputsMarkdown) { diff --git a/actions/setup/js/log_parser_bootstrap.test.cjs b/actions/setup/js/log_parser_bootstrap.test.cjs index 9007de6e788..57c08a8ba29 100644 --- a/actions/setup/js/log_parser_bootstrap.test.cjs +++ b/actions/setup/js/log_parser_bootstrap.test.cjs @@ -45,7 +45,7 @@ describe("log_parser_bootstrap.cjs", () => { (runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }), expect(mockParseLog).toHaveBeenCalledWith("Test log content"), expect(mockCore.info).toHaveBeenCalledWith("TestParser log parsed successfully"), - expect(mockCore.summary.addRaw).toHaveBeenCalledWith("## Parsed Log\n\nSuccess!"), + expect(mockCore.summary.addRaw).toHaveBeenCalledWith("
\n🤖 TestParser CLI Session\n\n## Parsed Log\n\nSuccess!\n
"), expect(mockCore.summary.write).toHaveBeenCalled(), fs.unlinkSync(logFile), fs.rmdirSync(tmpDir)); @@ -57,7 +57,7 @@ describe("log_parser_bootstrap.cjs", () => { const mockParseLog = vi.fn().mockReturnValue({ markdown: "## Result\n", mcpFailures: [], maxTurnsHit: !1 }); (runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }), expect(mockCore.info).toHaveBeenCalledWith("TestParser log parsed successfully"), - expect(mockCore.summary.addRaw).toHaveBeenCalledWith("## Result\n"), + expect(mockCore.summary.addRaw).toHaveBeenCalledWith("
\n🤖 TestParser CLI Session\n\n## Result\n\n
"), expect(mockCore.setFailed).not.toHaveBeenCalled(), fs.unlinkSync(logFile), fs.rmdirSync(tmpDir)); diff --git a/actions/setup/js/log_parser_shared.cjs b/actions/setup/js/log_parser_shared.cjs index 7fa71f9c96f..f6c918bff69 100644 --- a/actions/setup/js/log_parser_shared.cjs +++ b/actions/setup/js/log_parser_shared.cjs @@ -1414,6 +1414,28 @@ function generateCopilotCliStyleSummary(logEntries, options = {}) { return lines.join("\n"); } +/** + * Wraps agent log markdown in a details/summary section + * @param {string} markdown - The agent log markdown content + * @param {Object} options - Configuration options + * @param {string} [options.parserName="Agent"] - Name of the parser (e.g., "Copilot", "Claude") + * @param {boolean} [options.open=true] - Whether the section should be open by default + * @returns {string} Wrapped markdown in details/summary tags + */ +function wrapAgentLogInSection(markdown, options = {}) { + const { parserName = "Agent", open = true } = options; + + if (!markdown || markdown.trim().length === 0) { + return ""; + } + + const openAttr = open ? " open" : ""; + const emoji = "🤖"; + const title = `${emoji} ${parserName} CLI Session`; + + return `\n${title}\n\n${markdown}\n`; +} + /** * Formats safe outputs preview for display in logs * @param {string} safeOutputsContent - The raw JSONL content from safe outputs file @@ -1591,6 +1613,7 @@ module.exports = { formatToolCallAsDetails, generatePlainTextSummary, generateCopilotCliStyleSummary, + wrapAgentLogInSection, formatSafeOutputsPreview, wrapLogParser, createEngineLogParser, diff --git a/actions/setup/js/log_parser_shared.test.cjs b/actions/setup/js/log_parser_shared.test.cjs index f1a8b8e5d9a..6b157c27b47 100644 --- a/actions/setup/js/log_parser_shared.test.cjs +++ b/actions/setup/js/log_parser_shared.test.cjs @@ -2087,6 +2087,66 @@ describe("log_parser_shared.cjs", () => { }); }); + describe("wrapAgentLogInSection", () => { + it("should wrap markdown in details/summary with default open attribute", async () => { + const { wrapAgentLogInSection } = await import("./log_parser_shared.cjs"); + + const markdown = "```\nConversation:\n\nAgent: Hello\n```"; + const result = wrapAgentLogInSection(markdown, { parserName: "Copilot" }); + + expect(result).toContain("
"); + expect(result).toContain("🤖 Copilot CLI Session"); + expect(result).toContain(markdown); + expect(result).toContain("
"); + }); + + it("should support custom parser names", async () => { + const { wrapAgentLogInSection } = await import("./log_parser_shared.cjs"); + + const markdown = "Test content"; + const result = wrapAgentLogInSection(markdown, { parserName: "Claude" }); + + expect(result).toContain("🤖 Claude CLI Session"); + }); + + it("should allow closed state when open is false", async () => { + const { wrapAgentLogInSection } = await import("./log_parser_shared.cjs"); + + const markdown = "Test content"; + const result = wrapAgentLogInSection(markdown, { parserName: "Copilot", open: false }); + + expect(result).toContain("
"); + expect(result).not.toContain("
"); + }); + + it("should default to Agent parser name when not provided", async () => { + const { wrapAgentLogInSection } = await import("./log_parser_shared.cjs"); + + const markdown = "Test content"; + const result = wrapAgentLogInSection(markdown); + + expect(result).toContain("🤖 Agent CLI Session"); + }); + + it("should return empty string for empty or undefined markdown", async () => { + const { wrapAgentLogInSection } = await import("./log_parser_shared.cjs"); + + expect(wrapAgentLogInSection("")).toBe(""); + expect(wrapAgentLogInSection(" ")).toBe(""); + }); + + it("should properly escape markdown content", async () => { + const { wrapAgentLogInSection } = await import("./log_parser_shared.cjs"); + + const markdown = "Content with and `code`"; + const result = wrapAgentLogInSection(markdown, { parserName: "Copilot" }); + + expect(result).toContain(markdown); + expect(result).toContain("
"); + expect(result).toContain("
"); + }); + }); + describe("wrapLogParser", () => { it("should call parser function and return result on success", async () => { const { wrapLogParser } = await import("./log_parser_shared.cjs");