Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions actions/setup/js/log_parser_shared.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 : {};
Expand Down Expand Up @@ -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;
Expand All @@ -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) }],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The buildToolInput(data) call here uses data from a tool.execution_complete event (orphaned completion path). Real SDK completion events do not carry a command field, so this will always return {} — the logic is harmless but could mislead future maintainers.

💡 Suggestion

Consider making the intent explicit with an inline comment or a plain {}:

// Orphaned completion: no matching start event; command is unavailable
content: [{ type: "tool_use", id: resolvedToolId, name: toolName, input: {} }],

A small test for the orphaned-completion path would also document this behaviour as a spec.

@copilot please address this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1538d63. The orphaned completion fallback now preserves structured input while explicitly skipping command synthesis, since completion payloads cannot reliably recover the original command.

},
});
}
Expand Down
17 changes: 17 additions & 0 deletions actions/setup/js/parse_copilot_log.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The regression test covers the happy path (command present on tool.execution_start), but misses the merge case: when both data.input (or data.parameters) and data.command are present and base.command is undefined. Adding a test for that branch would prevent a silent regression if the merge logic changes.

💡 Suggested additional test
it('merges data.command into existing data.input when base.command is absent', () => {
  const eventsLog = [
    '{"type":"user.message","data":{}}',
    '{"type":"tool.execution_start","data":{"toolName":"bash","mcpServerName":"","input":{"cwd":"/tmp"},"command":"ls"}}',
    '{"type":"tool.execution_complete","data":{"toolName":"bash","mcpServerName":"","success":true,"result":{"content":"out"}}}',
    '{"type":"assistant.message","data":{"content":"Done"}}',
  ].join('\n');

  const result = parseCopilotLog(eventsLog);
  expect(result.markdown).toContain('ls');
  expect(result.markdown).toContain('/tmp'); // cwd from input preserved
});

@copilot please address this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1538d63. Added regression coverage for the merge path where structured input is preserved and data.command is merged in when input.command is absent.

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":{}}',
Expand Down
Loading