fix(server): cover imported and failed shell output

This commit is contained in:
Mohamed Boudra
2026-07-12 16:08:15 +02:00
parent 928c124356
commit 8fdc41f06c
4 changed files with 74 additions and 1 deletions

View File

@@ -473,6 +473,27 @@ describe("target coalesced behavior", () => {
}
});
test("bounds failed shell output carried in the error", async () => {
const harness = createHarness();
try {
const { agentId, session } = await createManagedSession(harness);
const content = `${"a".repeat(512 * 1024)}${"z".repeat(512 * 1024)}`;
const expectedItem = toolCall({
status: "failed",
error: { content: "a".repeat(TOOL_CALL_CONTENT_MAX_LENGTH) },
});
session.pushEvent(timelineEvent(toolCall({ status: "failed", error: { content } })));
await waitForSessionEventQueue();
expect(getTimelineItems(await harness.manager.getTimelineRows(agentId))).toEqual([
expectedItem,
]);
} finally {
harness.cleanup();
}
});
test(`coalesces a same-tick assistant burst after the ${COALESCE_WINDOW_MS}ms window`, async () => {
vi.useFakeTimers();
const harness = createHarness();

View File

@@ -2314,6 +2314,22 @@ test("importProviderSession imports the selected session without listing and pub
item: { type: "assistant_message" as const, text: "Done" },
timestamp: "2026-01-02T00:00:01.000Z",
},
{
item: {
type: "tool_call" as const,
callId: "large-shell-result",
name: "shell",
status: "completed" as const,
error: null,
detail: {
type: "shell" as const,
command: "print output",
output: "x".repeat(1024 * 1024),
exitCode: 0,
},
},
timestamp: "2026-01-02T00:00:02.000Z",
},
],
};
}
@@ -2343,6 +2359,19 @@ test("importProviderSession imports the selected session without listing and pub
expect(manager.getTimeline(imported.id)).toEqual([
{ type: "user_message", text: "Trace provider imports" },
{ type: "assistant_message", text: "Done" },
{
type: "tool_call",
callId: "large-shell-result",
name: "shell",
status: "completed",
error: null,
detail: {
type: "shell",
command: "print output",
output: "x".repeat(64 * 1024),
exitCode: 0,
},
},
]);
expect(events).toHaveLength(1);
expect(events[0]).toMatchObject({

View File

@@ -494,7 +494,7 @@ function buildImportedTimelineRows(entries: readonly ImportedTimelineEntry[]): A
rows.push({
seq: rows.length + 1,
timestamp: entry.timestamp ?? new Date().toISOString(),
item: entry.item,
item: limitAgentTimelineItemContent(entry.item),
});
}
return rows;

View File

@@ -2,7 +2,30 @@ import type { AgentTimelineItem } from "./agent-sdk-types.js";
const TOOL_CALL_CONTENT_MAX_LENGTH = 64 * 1024;
function limitFailedShellError(item: AgentTimelineItem): AgentTimelineItem {
if (
item.type !== "tool_call" ||
item.detail.type !== "shell" ||
item.status !== "failed" ||
typeof item.error !== "object" ||
item.error === null ||
!("content" in item.error) ||
typeof item.error.content !== "string" ||
item.error.content.length <= TOOL_CALL_CONTENT_MAX_LENGTH
) {
return item;
}
return {
...item,
error: {
...item.error,
content: item.error.content.slice(0, TOOL_CALL_CONTENT_MAX_LENGTH),
},
};
}
export function limitAgentTimelineItemContent(item: AgentTimelineItem): AgentTimelineItem {
item = limitFailedShellError(item);
if (
item.type !== "tool_call" ||
item.detail.type !== "shell" ||