From 5309e00b0746d20440cb7cb4d3b1191847f1059f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 09:13:27 +0000 Subject: [PATCH 1/5] fix(copilot-parser): render bash command from data.command in SDK events Copilot SDK tool.execution_start events carry the executed command as a top-level data.command field, not nested under data.input/data.parameters. The events->legacy normalizer only read data.input || data.parameters, so bash commands were dropped from the rendered step-summary (tool name and output showed, but not the command that ran). Add a buildToolInput helper that falls back to (and merges in) data.command, and a regression test covering a bash tool.execution_start with data.command. Co-Authored-By: Claude Opus 4.8 (1M context) --- actions/setup/js/log_parser_shared.cjs | 23 +++++++++++++++++++-- actions/setup/js/parse_copilot_log.test.cjs | 17 +++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/actions/setup/js/log_parser_shared.cjs b/actions/setup/js/log_parser_shared.cjs index e07d4cc6bc4..03ce7c408b7 100644 --- a/actions/setup/js/log_parser_shared.cjs +++ b/actions/setup/js/log_parser_shared.cjs @@ -840,6 +840,25 @@ function convertCopilotEventsToLegacyLogEntries(logEntries) { return ""; }; + // Builds the tool_use `input` object for a Copilot SDK tool event. + // Copilot SDK bash events carry the executed command as a top-level `data.command` + // field rather than nesting it inside `data.input`/`data.parameters`, so fall back + // to it (and merge it in when structured input lacks a command) to avoid dropping + // the command from the rendered summary. + const buildToolInput = data => { + const base = data.input || data.parameters; + if (base && typeof base === "object" && !Array.isArray(base)) { + if (base.command === undefined && typeof data.command === "string") { + return { ...base, command: data.command }; + } + return base; + } + if (typeof data.command === "string") { + return { command: data.command }; + } + return {}; + }; + for (const entry of logEntries) { if (!entry || typeof entry !== "object") continue; const data = entry.data && typeof entry.data === "object" ? entry.data : {}; @@ -900,7 +919,7 @@ function convertCopilotEventsToLegacyLogEntries(logEntries) { normalizedEntries.push({ type: "assistant", message: { - content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: data.input || data.parameters || {} }], + content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: buildToolInput(data) }], }, }); break; @@ -926,7 +945,7 @@ function convertCopilotEventsToLegacyLogEntries(logEntries) { normalizedEntries.push({ type: "assistant", message: { - content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: data.input || data.parameters || {} }], + content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: buildToolInput(data) }], }, }); } diff --git a/actions/setup/js/parse_copilot_log.test.cjs b/actions/setup/js/parse_copilot_log.test.cjs index 89f5c991d1b..c3823162df4 100644 --- a/actions/setup/js/parse_copilot_log.test.cjs +++ b/actions/setup/js/parse_copilot_log.test.cjs @@ -138,6 +138,23 @@ describe("parse_copilot_log.cjs", () => { expect(result.markdown).toContain("file1.txt"); }); + it("renders the bash command from data.command in Copilot CLI events.jsonl", () => { + // Real Copilot SDK events carry the executed command as a top-level data.command + // field on tool.execution_start, not nested inside data.input/data.parameters. + const eventsLog = [ + '{"type":"user.message","timestamp":"2026-06-05T00:44:01.367Z","data":{}}', + '{"type":"tool.execution_start","timestamp":"2026-06-05T00:44:04.520Z","data":{"toolName":"bash","mcpServerName":"","command":"cat /tmp/gh-aw/agent/candidates.txt"}}', + '{"type":"tool.execution_complete","timestamp":"2026-06-05T00:44:04.700Z","data":{"toolName":"bash","mcpServerName":"","success":true,"result":{"content":"candidate-list-output"}}}', + '{"type":"assistant.message","timestamp":"2026-06-05T00:44:59.769Z","data":{"content":"Done"}}', + ].join("\n"); + + const result = parseCopilotLog(eventsLog); + + expect(result.markdown).toContain("bash"); + expect(result.markdown).toContain("cat /tmp/gh-aw/agent/candidates.txt"); + expect(result.markdown).toContain("candidate-list-output"); + }); + it("renders tool output preview from array-based result.content in Copilot CLI events.jsonl", () => { const eventsLog = [ '{"type":"user.message","timestamp":"2026-06-05T00:44:01.367Z","data":{}}', From 6019463aef3292724fbaba2905df48cf6a346907 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:19:32 +0000 Subject: [PATCH 2/5] fix(copilot-parser): address review feedback Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/log_parser_shared.cjs | 2 +- actions/setup/js/parse_copilot_log.test.cjs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/actions/setup/js/log_parser_shared.cjs b/actions/setup/js/log_parser_shared.cjs index 03ce7c408b7..c4ad487faf7 100644 --- a/actions/setup/js/log_parser_shared.cjs +++ b/actions/setup/js/log_parser_shared.cjs @@ -945,7 +945,7 @@ function convertCopilotEventsToLegacyLogEntries(logEntries) { normalizedEntries.push({ type: "assistant", message: { - content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: buildToolInput(data) }], + content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: {} }], }, }); } diff --git a/actions/setup/js/parse_copilot_log.test.cjs b/actions/setup/js/parse_copilot_log.test.cjs index c3823162df4..8f5b347624a 100644 --- a/actions/setup/js/parse_copilot_log.test.cjs +++ b/actions/setup/js/parse_copilot_log.test.cjs @@ -155,6 +155,21 @@ describe("parse_copilot_log.cjs", () => { expect(result.markdown).toContain("candidate-list-output"); }); + it("merges data.command into existing data.input when command is absent there", () => { + const eventsLog = [ + '{"type":"user.message","timestamp":"2026-06-05T00:44:01.367Z","data":{}}', + '{"type":"tool.execution_start","timestamp":"2026-06-05T00:44:04.520Z","data":{"toolName":"bash","mcpServerName":"","input":{"cwd":"/tmp"},"command":"ls"}}', + '{"type":"tool.execution_complete","timestamp":"2026-06-05T00:44:04.700Z","data":{"toolName":"bash","mcpServerName":"","success":true,"result":{"content":"file1.txt\\nfile2.txt"}}}', + '{"type":"assistant.message","timestamp":"2026-06-05T00:44:59.769Z","data":{"content":"Done"}}', + ].join("\n"); + + const result = parseCopilotLog(eventsLog); + + expect(result.markdown).toContain("ls"); + expect(result.markdown).toContain("/tmp"); + expect(result.markdown).toContain("file1.txt"); + }); + it("renders tool output preview from array-based result.content in Copilot CLI events.jsonl", () => { const eventsLog = [ '{"type":"user.message","timestamp":"2026-06-05T00:44:01.367Z","data":{}}', From 6b5adc5583c4f4eb161ceea60fd2a8a0f1c7558f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:22:02 +0000 Subject: [PATCH 3/5] docs(copilot-parser): clarify orphaned completion input Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/log_parser_shared.cjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actions/setup/js/log_parser_shared.cjs b/actions/setup/js/log_parser_shared.cjs index c4ad487faf7..bf1ffe4d93c 100644 --- a/actions/setup/js/log_parser_shared.cjs +++ b/actions/setup/js/log_parser_shared.cjs @@ -945,6 +945,8 @@ function convertCopilotEventsToLegacyLogEntries(logEntries) { normalizedEntries.push({ type: "assistant", message: { + // Orphaned completion events have no corresponding start event, so the + // executed command cannot be recovered from SDK payloads here. content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: {} }], }, }); From 014a5689e1a3c3bf653dc66dd98f8da2a0dd0381 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:24:51 +0000 Subject: [PATCH 4/5] fix(copilot-parser): preserve orphaned completion input Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/log_parser_shared.cjs | 13 +++++++------ actions/setup/js/parse_copilot_log.test.cjs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/actions/setup/js/log_parser_shared.cjs b/actions/setup/js/log_parser_shared.cjs index bf1ffe4d93c..28c705ea5ef 100644 --- a/actions/setup/js/log_parser_shared.cjs +++ b/actions/setup/js/log_parser_shared.cjs @@ -845,15 +845,16 @@ function convertCopilotEventsToLegacyLogEntries(logEntries) { // field rather than nesting it inside `data.input`/`data.parameters`, so fall back // to it (and merge it in when structured input lacks a command) to avoid dropping // the command from the rendered summary. - const buildToolInput = data => { + const buildToolInput = (data, options = {}) => { + const { includeCommand = true } = options; const base = data.input || data.parameters; if (base && typeof base === "object" && !Array.isArray(base)) { - if (base.command === undefined && typeof data.command === "string") { + if (includeCommand && base.command === undefined && typeof data.command === "string") { return { ...base, command: data.command }; } return base; } - if (typeof data.command === "string") { + if (includeCommand && typeof data.command === "string") { return { command: data.command }; } return {}; @@ -945,9 +946,9 @@ function convertCopilotEventsToLegacyLogEntries(logEntries) { normalizedEntries.push({ type: "assistant", message: { - // Orphaned completion events have no corresponding start event, so the - // executed command cannot be recovered from SDK payloads here. - content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: {} }], + // Orphaned completion events have no corresponding start event, so keep + // structured input but do not synthesize a command from completion data. + content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: buildToolInput(data, { includeCommand: false }) }], }, }); } diff --git a/actions/setup/js/parse_copilot_log.test.cjs b/actions/setup/js/parse_copilot_log.test.cjs index 8f5b347624a..e03da170ce8 100644 --- a/actions/setup/js/parse_copilot_log.test.cjs +++ b/actions/setup/js/parse_copilot_log.test.cjs @@ -170,6 +170,20 @@ describe("parse_copilot_log.cjs", () => { expect(result.markdown).toContain("file1.txt"); }); + it("preserves structured input for orphaned completion events without inventing a command", () => { + const eventsLog = [ + '{"type":"user.message","timestamp":"2026-06-05T00:44:01.367Z","data":{}}', + '{"type":"tool.execution_complete","timestamp":"2026-06-05T00:44:04.700Z","data":{"toolName":"bash","mcpServerName":"","input":{"cwd":"/tmp"},"success":true,"result":{"content":"file1.txt\\nfile2.txt"}}}', + '{"type":"assistant.message","timestamp":"2026-06-05T00:44:59.769Z","data":{"content":"Done"}}', + ].join("\n"); + + const result = parseCopilotLog(eventsLog); + + expect(result.markdown).toContain("bash"); + expect(result.markdown).toContain("/tmp"); + expect(result.markdown).toContain("file1.txt"); + }); + it("renders tool output preview from array-based result.content in Copilot CLI events.jsonl", () => { const eventsLog = [ '{"type":"user.message","timestamp":"2026-06-05T00:44:01.367Z","data":{}}', From 1538d63db448ddc2a3fd44b31f44bca290623272 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:27:20 +0000 Subject: [PATCH 5/5] docs(copilot-parser): document includeCommand option Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/log_parser_shared.cjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actions/setup/js/log_parser_shared.cjs b/actions/setup/js/log_parser_shared.cjs index 28c705ea5ef..ab93a9cc7a6 100644 --- a/actions/setup/js/log_parser_shared.cjs +++ b/actions/setup/js/log_parser_shared.cjs @@ -845,6 +845,8 @@ function convertCopilotEventsToLegacyLogEntries(logEntries) { // field rather than nesting it inside `data.input`/`data.parameters`, so fall back // to it (and merge it in when structured input lacks a command) to avoid dropping // the command from the rendered summary. + // Pass { includeCommand: false } when normalizing orphaned completion events that + // may still carry structured input but cannot reliably recover the original command. const buildToolInput = (data, options = {}) => { const { includeCommand = true } = options; const base = data.input || data.parameters;