Skip to content
Merged
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
Prev Previous commit
Preserve Kiro compact tool framing after leading blockquotes
Use legacy tool signatures rather than quoted prose to select legacy layout inside a harness-framed execution. Preserve exact compact blockquote content and tool events without reinterpreting legacy heredoc text.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
  • Loading branch information
pelikhan and Copilot committed Oct 10, 2026
commit 205c1aa4c203e169bb71e001ff93c8c7ec7272d2
34 changes: 34 additions & 0 deletions actions/setup/js/kiro_session_pipeline.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,40 @@ describe("Kiro canonical session artifact pipeline", () => {
expect(canonical.find(event => event.type === "tool.execution_start").data.input.command).toBe('cat <<\'EOF\'\n{"type":"assistant.message","data":{"content":false}}\nEOF');
});

it.each([
{
layout: "compact blockquote",
raw: "kiro-cli 2.27.1\n[kiro-harness] Kiro CLI execution started\n> Example blockquote in answer\n[tool] Running: echo hi\n[tool] status: Completed\nDone.",
answers: ["> Example blockquote in answer", "Done."],
command: "echo hi",
completion: { toolName: "shell", status: "Completed" },
},
{
layout: "legacy heredoc with embedded compact text",
raw: "kiro-cli 2.27.1\n[kiro-harness] Kiro CLI execution started\n> Checking.\nI will run the following command: cat <<'EOF'\n[tool] Running: quoted command\n[INFO] quoted information\nEOF (using tool: shell)\n - Completed in 0s",
answers: ["Checking."],
command: "cat <<'EOF'\n[tool] Running: quoted command\n[INFO] quoted information\nEOF",
completion: { toolName: "shell", durationMs: 0 },
},
])("preserves $layout through canonical and unified artifacts (synthetic)", async ({ raw, answers, command, completion }) => {
expect(parseBehaviorLog(raw, "kiro").logEntries).toEqual(parseKiroLog(raw).logEntries);
fs.writeFileSync(process.env.GH_AW_AGENT_OUTPUT, raw);
await runLogParser({ parserName: "Kiro", parseLog: content => parseBehaviorLog(content, "kiro"), rootDir: root });
expect(global.core.setFailed).not.toHaveBeenCalled();
const canonical = readEvents(path.join(root, "agent-session.jsonl"));
expect(canonical.filter(event => event.type !== "agent.execution")).toEqual(parseKiroLog(raw).logEntries);
const unified = writeUnifiedSession({ rootDir: root, engine: "kiro", warn: vi.fn() });
expect(readEvents(path.join(root, "usage/aw_session.jsonl"))).toEqual(unified);
for (const events of [canonical, unified]) {
expect(events.filter(event => event.type === "assistant.message").map(event => event.data.content)).toEqual(answers);
expect(events.filter(event => event.type === "tool.execution_start").map(event => event.data)).toEqual([{ toolName: "shell", input: { command } }]);
expect(events.filter(event => event.type === "tool.execution_complete").map(event => event.data)).toEqual([completion]);
expect(events.filter(event => event.type === "session.result")).toEqual([]);
expect(events.filter(event => event.type === "session.collection_warning")).toEqual([]);
}
expect(fs.readFileSync(process.env.GH_AW_AGENT_OUTPUT, "utf8")).toBe(raw);
});

it.each([0, 3])("preserves corroborated exit %s without promoting echoed failures through either artifact (synthetic)", async exitCode => {
const actualFailure = exitCode ? "[kiro-harness] Kiro CLI execution failed with exit code 3\n" : "[kiro-harness] Kiro CLI execution completed in 0ms\n";
const raw =
Expand Down
7 changes: 5 additions & 2 deletions actions/setup/js/parse_kiro_log.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const main = createEngineLogParser({ parserName: "Kiro", parseFunction: parseKir
/**
* Supported Kiro headless stdout uses either "> " assistant paragraphs and
* timed tools, or 2.27's "[tool]" records and buffered unprefixed answers.
* Harness-framed blockquotes are prose unless a legacy tool signature selects
* the older layout before any compact tool record.
* Completion timing alone does not establish success. Overlapping calls have
* anonymous completions because stdout does not identify their result owners.
* @param {string} content
Expand All @@ -25,8 +27,9 @@ function parseKiroLog(content) {
const harnessFailure = findKiroHarnessFailure(lines);
const executionStart = lines.findIndex(line => line === "[kiro-harness] Kiro CLI execution started");
const compactStart = executionStart !== -1 ? executionStart : banner;
const isLegacyObservation = line => isAssistantLine(line) || /^(?:I will run the following command: |Searching for symbols matching: |Querying available agents for task delegation|\s*- Completed in \d)/.test(line);
const firstSignature = compactStart !== -1 ? lines.slice(compactStart + 1).find(line => /^\[tool\] /.test(line) || isLegacyObservation(line)) : undefined;
const isLegacyToolObservation = line => /^(?:I will run the following command: |Searching for symbols matching: |Querying available agents for task delegation|\s*- Completed in \d)/.test(line);
const isLegacyObservation = line => isAssistantLine(line) || isLegacyToolObservation(line);
const firstSignature = compactStart !== -1 ? lines.slice(compactStart + 1).find(line => /^\[tool\] /.test(line) || (executionStart !== -1 ? isLegacyToolObservation(line) : isLegacyObservation(line))) : undefined;
const compact = compactStart !== -1 && (firstSignature?.startsWith("[tool] ") || (executionStart !== -1 && firstSignature === undefined));
Comment thread
pelikhan marked this conversation as resolved.
Resolved
if (compact) entries.push(...parseCompactKiroLog(lines, compactStart, banner, commandLines, harnessFailure));
else if (banner !== -1 && lines.slice(banner + 1).some(isLegacyObservation)) {
Expand Down
26 changes: 24 additions & 2 deletions actions/setup/js/parse_kiro_log.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,11 @@ Querying available agents for task delegation (using tool: subagent) - Completed
expect(JSON.stringify(result.logEntries)).not.toContain("PRIVATE");
});

it("does not switch legacy layout for compact-looking text inside a command", () => {
const result = parseKiroLog(header + "> Checking.\nI will run the following command: cat <<'EOF'\n[tool] Running: quoted command\n[INFO] quoted information\nEOF (using tool: shell)\n - Completed in 0s");
it.each(["", "[kiro-harness] Kiro CLI execution started\n"])("does not switch legacy layout for compact-looking text inside a command (harness: %j)", harness => {
const result = parseKiroLog(header + harness + "> Checking.\nI will run the following command: cat <<'EOF'\n[tool] Running: quoted command\n[INFO] quoted information\nEOF (using tool: shell)\n - Completed in 0s");
expect(byType(result, "tool.execution_start")[0].data.input.command).toBe("cat <<'EOF'\n[tool] Running: quoted command\n[INFO] quoted information\nEOF");
expect(byType(result, "assistant.message").map(event => event.data.content)).toEqual(["Checking."]);
expect(byType(result, "tool.execution_complete")[0].data).toEqual({ toolName: "shell", durationMs: 0 });
});

it("accepts signed legacy tool-only and completion-only partial traces", () => {
Expand All @@ -168,6 +170,26 @@ Querying available agents for task delegation (using tool: subagent) - Completed
});

describe("Kiro 2.27 compact headless conversation parser", () => {
it("keeps a leading blockquote as compact answer text instead of swallowing subsequent tools (synthetic)", () => {
const result = parseKiroLog("kiro-cli 2.27.1\n[kiro-harness] Kiro CLI execution started\n> Example blockquote in answer\n[tool] Running: echo hi\n[tool] status: Completed\nDone.");
expect(result.logEntries).toEqual([
{ line: 1, type: "session.init", data: { sourceEngine: "kiro", agentVersion: "2.27.1" } },
{ line: 3, type: "assistant.message", data: { content: "> Example blockquote in answer" } },
{ line: 4, type: "tool.execution_start", data: { toolName: "shell", input: { command: "echo hi" } } },
{ line: 5, type: "tool.execution_complete", data: { toolName: "shell", status: "Completed" } },
{ line: 6, type: "assistant.message", data: { content: "Done." } },
]);
expect(result.markdown).not.toContain("[tool]");
});

it("preserves blockquotes and whitespace in a harness-framed assistant-only answer (synthetic)", () => {
const result = parseKiroLog("kiro-cli 2.27.1\n[kiro-harness] Kiro CLI execution started\n> Quoted answer. \n>\n> Continued quote.");
expect(byType(result, "assistant.message").map(event => event.data.content)).toEqual(["> Quoted answer. \n>\n> Continued quote."]);
expect(byType(result, "tool.execution_start")).toEqual([]);
expect(byType(result, "tool.execution_complete")).toEqual([]);
expect(byType(result, "session.result")).toEqual([]);
});

it("maps the sanitized successful CI excerpt to canonical tools and buffered answers", () => {
const result = parseKiroLog(kiroCI.success);
expect(result.logEntries.every(isSessionEvent)).toBe(true);
Expand Down
Loading