mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 21:48:27 +00:00
Cap sessions_history payloads to prevent context overflow (#10000)
* Cap sessions_history payloads to prevent context overflow * fix: harden sessions_history payload caps * fix: cap sessions_history payloads to prevent context overflow (#10000) (thanks @gut-puncture) --------- Co-authored-by: Shailesh Rana <shaileshrana@ShaileshMM.local> Co-authored-by: George Pickett <gpickett00@gmail.com>
This commit is contained in:
@@ -181,6 +181,128 @@ describe("sessions tools", () => {
|
||||
expect(withToolsDetails.messages).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("sessions_history caps oversized payloads and strips heavy fields", async () => {
|
||||
callGatewayMock.mockReset();
|
||||
const oversized = Array.from({ length: 80 }, (_, idx) => ({
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `${String(idx)}:${"x".repeat(5000)}`,
|
||||
},
|
||||
{
|
||||
type: "thinking",
|
||||
thinking: "y".repeat(7000),
|
||||
thinkingSignature: "sig".repeat(4000),
|
||||
},
|
||||
],
|
||||
details: {
|
||||
giant: "z".repeat(12000),
|
||||
},
|
||||
usage: {
|
||||
input: 1,
|
||||
output: 1,
|
||||
},
|
||||
}));
|
||||
callGatewayMock.mockImplementation(async (opts: unknown) => {
|
||||
const request = opts as { method?: string };
|
||||
if (request.method === "chat.history") {
|
||||
return { messages: oversized };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "sessions_history");
|
||||
expect(tool).toBeDefined();
|
||||
if (!tool) {
|
||||
throw new Error("missing sessions_history tool");
|
||||
}
|
||||
|
||||
const result = await tool.execute("call4b", {
|
||||
sessionKey: "main",
|
||||
includeTools: true,
|
||||
});
|
||||
const details = result.details as {
|
||||
messages?: Array<Record<string, unknown>>;
|
||||
truncated?: boolean;
|
||||
droppedMessages?: boolean;
|
||||
contentTruncated?: boolean;
|
||||
bytes?: number;
|
||||
};
|
||||
expect(details.truncated).toBe(true);
|
||||
expect(details.droppedMessages).toBe(true);
|
||||
expect(details.contentTruncated).toBe(true);
|
||||
expect(typeof details.bytes).toBe("number");
|
||||
expect((details.bytes ?? 0) <= 80 * 1024).toBe(true);
|
||||
expect(details.messages && details.messages.length > 0).toBe(true);
|
||||
|
||||
const first = details.messages?.[0] as
|
||||
| {
|
||||
details?: unknown;
|
||||
usage?: unknown;
|
||||
content?: Array<{
|
||||
type?: string;
|
||||
text?: string;
|
||||
thinking?: string;
|
||||
thinkingSignature?: string;
|
||||
}>;
|
||||
}
|
||||
| undefined;
|
||||
expect(first?.details).toBeUndefined();
|
||||
expect(first?.usage).toBeUndefined();
|
||||
const textBlock = first?.content?.find((block) => block.type === "text");
|
||||
expect(typeof textBlock?.text).toBe("string");
|
||||
expect((textBlock?.text ?? "").length <= 4015).toBe(true);
|
||||
const thinkingBlock = first?.content?.find((block) => block.type === "thinking");
|
||||
expect(thinkingBlock?.thinkingSignature).toBeUndefined();
|
||||
});
|
||||
|
||||
it("sessions_history enforces a hard byte cap even when a single message is huge", async () => {
|
||||
callGatewayMock.mockReset();
|
||||
callGatewayMock.mockImplementation(async (opts: unknown) => {
|
||||
const request = opts as { method?: string };
|
||||
if (request.method === "chat.history") {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "ok" }],
|
||||
extra: "x".repeat(200_000),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "sessions_history");
|
||||
expect(tool).toBeDefined();
|
||||
if (!tool) {
|
||||
throw new Error("missing sessions_history tool");
|
||||
}
|
||||
|
||||
const result = await tool.execute("call4c", {
|
||||
sessionKey: "main",
|
||||
includeTools: true,
|
||||
});
|
||||
const details = result.details as {
|
||||
messages?: Array<Record<string, unknown>>;
|
||||
truncated?: boolean;
|
||||
droppedMessages?: boolean;
|
||||
contentTruncated?: boolean;
|
||||
bytes?: number;
|
||||
};
|
||||
expect(details.truncated).toBe(true);
|
||||
expect(details.droppedMessages).toBe(true);
|
||||
expect(details.contentTruncated).toBe(false);
|
||||
expect(typeof details.bytes).toBe("number");
|
||||
expect((details.bytes ?? 0) <= 80 * 1024).toBe(true);
|
||||
expect(details.messages).toHaveLength(1);
|
||||
expect(details.messages?.[0]?.content).toContain(
|
||||
"[sessions_history omitted: message too large]",
|
||||
);
|
||||
});
|
||||
|
||||
it("sessions_history resolves sessionId inputs", async () => {
|
||||
callGatewayMock.mockReset();
|
||||
const sessionId = "sess-group";
|
||||
|
||||
Reference in New Issue
Block a user