pi-mono/packages/coding-agent/test/compaction-serialization.test.ts
Mario Zechner 15e7bdc14c fix(coding-agent,durable): omit thinking from compaction summaries
Replayed thinking is often not part of the live context (for example,
radius drops it, and Anthropic drops blocks that do not match their
request), but serializeConversation included all of it. A Pi Mobile
session at ~996K of 1M context tokens produced 1.38M-token summarization
requests that failed on every attempt. Providers only return summaries of
the reasoning anyway; visible text, tool calls, and tool results record
what happened.
2026-10-11 15:45:13 +00:00

109 lines
2.8 KiB
TypeScript

import type { Message } from "@earendil-works/pi-ai";
import { describe, expect, it } from "vitest";
import { serializeConversation } from "../src/core/compaction/utils.ts";
describe("serializeConversation", () => {
it("should truncate long tool results", () => {
const longContent = "x".repeat(5000);
const messages: Message[] = [
{
role: "toolResult",
toolCallId: "tc1",
toolName: "read",
content: [{ type: "text", text: longContent }],
isError: false,
timestamp: Date.now(),
},
];
const result = serializeConversation(messages);
expect(result).toContain("[Tool result]:");
expect(result).toContain("[... 3000 more characters truncated]");
expect(result).not.toContain("x".repeat(3000));
// First 2000 chars should be present
expect(result).toContain("x".repeat(2000));
});
it("should not truncate short tool results", () => {
const shortContent = "x".repeat(1500);
const messages: Message[] = [
{
role: "toolResult",
toolCallId: "tc1",
toolName: "read",
content: [{ type: "text", text: shortContent }],
isError: false,
timestamp: Date.now(),
},
];
const result = serializeConversation(messages);
expect(result).toBe(`[Tool result]: ${shortContent}`);
expect(result).not.toContain("truncated");
});
it("should not truncate assistant or user messages", () => {
const longText = "y".repeat(5000);
const messages: Message[] = [
{
role: "user",
content: [{ type: "text", text: longText }],
timestamp: Date.now(),
},
{
role: "assistant",
content: [{ type: "text", text: longText }],
api: "anthropic",
provider: "anthropic",
model: "test",
usage: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 0,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
},
stopReason: "stop",
timestamp: Date.now(),
},
];
const result = serializeConversation(messages);
expect(result).not.toContain("truncated");
expect(result).toContain(longText);
});
it("should omit thinking", () => {
const messages: Message[] = [
{
role: "assistant",
content: [
{ type: "thinking", thinking: "private reasoning", thinkingSignature: "sig" },
{ type: "text", text: "visible reply" },
{ type: "toolCall", id: "tc1", name: "read", arguments: { path: "a.ts" } },
],
api: "anthropic",
provider: "anthropic",
model: "test",
usage: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 0,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
},
stopReason: "toolUse",
timestamp: Date.now(),
},
];
const result = serializeConversation(messages);
expect(result).toBe('[Assistant]: visible reply\n\n[Assistant tool calls]: read(path="a.ts")');
});
});