Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
85 changes: 85 additions & 0 deletions apps/web/src/session-logic.command-output.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { EventId, TurnId, type OrchestrationThreadActivity } from "@t3tools/contracts";
import { describe, expect, it } from "vite-plus/test";

import { deriveWorkLogEntries } from "./session-logic";

function makeCommandActivity(
id: string,
payload: Record<string, unknown>,
): OrchestrationThreadActivity {
return {
id: EventId.make(id),
createdAt: "2026-07-17T10:00:00.000Z",
kind: "tool.completed",
summary: "Ran command",
tone: "tool",
payload,
turnId: TurnId.make("turn-1"),
};
}

describe("deriveWorkLogEntries command output", () => {
it("uses Codex aggregated output instead of repeating the command", () => {
const [entry] = deriveWorkLogEntries([
makeCommandActivity("codex-command", {
itemType: "command_execution",
title: "Ran command",
detail: "/bin/zsh -lc \"printf 'hello\\n'\"",
data: {
item: {
type: "commandExecution",
command: "/bin/zsh -lc \"printf 'hello\\n'\"",
commandActions: [{ command: "printf 'hello\\n'", type: "unknown" }],
aggregatedOutput: "hello\n<exited with exit code 0>",
status: "completed",
},
},
}),
]);

expect(entry).toMatchObject({
command: "printf 'hello\\n'",
rawCommand: "/bin/zsh -lc \"printf 'hello\\n'\"",
detail: "hello",
});
});

it("uses Claude ACP stdout instead of repeating the command", () => {
const [entry] = deriveWorkLogEntries([
makeCommandActivity("claude-command", {
itemType: "command_execution",
title: "Ran command",
detail: "printf hello",
data: {
kind: "execute",
command: "printf hello",
rawOutput: {
stdout: "hello from claude\n",
},
},
}),
]);

expect(entry).toMatchObject({
command: "printf hello",
detail: "hello from claude",
});
});

it("drops duplicated command detail when the command has no output", () => {
const [entry] = deriveWorkLogEntries([
makeCommandActivity("empty-command", {
itemType: "command_execution",
title: "Ran command",
detail: "true",
data: {
kind: "execute",
command: "true",
},
}),
]);

expect(entry?.command).toBe("true");
expect(entry?.detail).toBeUndefined();
});
});
93 changes: 91 additions & 2 deletions apps/web/src/session-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,70 @@ function summarizeToolRawOutput(payload: Record<string, unknown> | null): string
return null;
}

function extractAcpTextContent(value: unknown): string | null {
if (!Array.isArray(value)) {
return null;
}

const chunks: string[] = [];
for (const entryValue of value) {
const entry = asRecord(entryValue);
if (entry?.type !== "content") {
continue;
}
const content = asRecord(entry.content);
if (content?.type !== "text") {
continue;
}
const text = asTrimmedString(content.text);
if (text) {
chunks.push(text);
}
}

return chunks.length > 0 ? chunks.join("\n") : null;
}

function extractToolOutput(payload: Record<string, unknown> | null): string | null {
const data = asRecord(payload?.data);
const item = asRecord(data?.item);
const itemResult = asRecord(item?.result);
const rawOutput = asRecord(data?.rawOutput);

const outputStreams: string[] = [];
const stdout = asTrimmedString(rawOutput?.stdout);
const stderr = asTrimmedString(rawOutput?.stderr);
if (stdout) {
outputStreams.push(stdout);
}
if (stderr) {
outputStreams.push(stderr);
}

const candidates: unknown[] = [
item?.aggregatedOutput,
itemResult?.content,
data?.rawOutput,
rawOutput?.content,
outputStreams.length > 0 ? outputStreams.join("\n") : null,
rawOutput?.output,
extractAcpTextContent(data?.content),
];

for (const candidate of candidates) {
const text = asTrimmedString(candidate);
if (!text) {
continue;
}
const output = stripTrailingExitCode(text).output;
if (output) {
return output;
}
}

return null;
}
Comment thread
cursor[bot] marked this conversation as resolved.

function isCommandToolDetail(payload: Record<string, unknown> | null, heading: string): boolean {
const data = asRecord(payload?.data);
const kind = asTrimmedString(data?.kind)?.toLowerCase();
Expand All @@ -1402,12 +1466,37 @@ function extractToolDetail(
const detail = rawDetail ? stripTrailingExitCode(rawDetail).output : null;
const normalizedHeading = normalizePreviewForComparison(heading);
const normalizedDetail = normalizePreviewForComparison(detail);
const commandTool = isCommandToolDetail(payload, heading);
const commandPreview = commandTool
? extractToolCommand(payload)
: { command: null, rawCommand: null };
const command = commandPreview.command;
const normalizedCommand = normalizePreviewForComparison(command);
const normalizedRawCommand = normalizePreviewForComparison(commandPreview.rawCommand);

if (detail && normalizedHeading !== normalizedDetail) {
if (
detail &&
normalizedHeading !== normalizedDetail &&
(!commandTool ||
(normalizedCommand !== normalizedDetail && normalizedRawCommand !== normalizedDetail))
) {
return detail;
}

if (isCommandToolDetail(payload, heading)) {
if (commandTool) {
if (!command) {
return null;
}

const output = extractToolOutput(payload);
const normalizedOutput = normalizePreviewForComparison(output);
if (
output &&
normalizedOutput !== normalizedHeading &&
normalizedOutput !== normalizedCommand
) {
Comment thread
cursor[bot] marked this conversation as resolved.
return output;
}
return null;
}

Expand Down
Loading