feat: implemented token tracking and metadata options for compaction summaries
- Added `CompactionSummaryMessageOptions` interface to support optional fields and metadata in compaction summary messages. - Included compaction method labels and before-after amount badge rendering in compaction views. - Updated session maintenance and compaction methods to track and compute post-compaction token counts. - Updated calls across agent and coding-agent packages to use the new compaction options object.
This commit is contained in:
@@ -160,9 +160,10 @@ describe("AgentSession snapcompact frame dead-end rescue", () => {
|
||||
"stale snapcompact archive",
|
||||
userEntryId,
|
||||
150_000,
|
||||
{ readFiles: ["src/a.ts"], modifiedFiles: ["src/b.ts"] },
|
||||
false,
|
||||
makeArchivePreserveData(options.frameCount),
|
||||
{
|
||||
details: { readFiles: ["src/a.ts"], modifiedFiles: ["src/b.ts"] },
|
||||
preserveData: makeArchivePreserveData(options.frameCount),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const SUMMARY = "Earlier the user fixed the login TTL bug.";
|
||||
|
||||
function makeComponent(images?: ImageContent[]): CompactionSummaryMessageComponent {
|
||||
return new CompactionSummaryMessageComponent(
|
||||
createCompactionSummaryMessage(SUMMARY, 84000, new Date().toISOString(), undefined, undefined, images),
|
||||
createCompactionSummaryMessage(SUMMARY, 84000, new Date().toISOString(), { images }),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,29 @@ describe("CompactionSummaryMessageComponent", () => {
|
||||
expect(rule).not.toContain(SUMMARY);
|
||||
});
|
||||
|
||||
it("names the compaction method and the before → after amounts on the divider", () => {
|
||||
const component = new CompactionSummaryMessageComponent(
|
||||
createCompactionSummaryMessage(SUMMARY, 256_000, new Date().toISOString(), {
|
||||
method: "remote",
|
||||
tokensAfter: 20_000,
|
||||
}),
|
||||
);
|
||||
const rule = Bun.stripANSI(component.render(80)[1]);
|
||||
expect(rule).toContain("remote-compacted");
|
||||
expect(rule).toContain("256K→20K");
|
||||
expect(rule).toContain("ctrl+o");
|
||||
});
|
||||
|
||||
it("labels a handoff-method compaction as handed-off", () => {
|
||||
const component = new CompactionSummaryMessageComponent(
|
||||
createCompactionSummaryMessage(SUMMARY, 84_000, new Date().toISOString(), { method: "handoff" }),
|
||||
);
|
||||
const rule = Bun.stripANSI(component.render(80)[1]);
|
||||
expect(rule).toContain("handed-off");
|
||||
// No tokensAfter recorded → no amount badge.
|
||||
expect(rule).not.toContain("→");
|
||||
});
|
||||
|
||||
it("expanded: reveals the summary (and snapcompact frame count) below the divider", () => {
|
||||
const component = makeComponent([{ type: "image", data: "ZmFrZQ==", mimeType: "image/png" }]);
|
||||
component.setExpanded(true);
|
||||
|
||||
@@ -39,7 +39,7 @@ describe("handoff summary divider", () => {
|
||||
|
||||
expect(component).toBeInstanceOf(HandoffSummaryMessageComponent);
|
||||
const collapsed = Bun.stripANSI(component!.render(80).join("\n"));
|
||||
expect(collapsed).toContain("handoff");
|
||||
expect(collapsed).toContain("handed-off");
|
||||
expect(collapsed).toContain("ctrl+o");
|
||||
expect(collapsed).not.toContain("[handoff]");
|
||||
expect(collapsed).not.toContain("Continue the resize fix");
|
||||
|
||||
@@ -82,15 +82,9 @@ describe("large session memory guards", () => {
|
||||
textTail: "frame",
|
||||
},
|
||||
};
|
||||
const firstCompactionId = session.appendCompaction(
|
||||
firstSummary,
|
||||
undefined,
|
||||
firstKeptEntryId,
|
||||
1000,
|
||||
undefined,
|
||||
undefined,
|
||||
firstPreserve,
|
||||
);
|
||||
const firstCompactionId = session.appendCompaction(firstSummary, undefined, firstKeptEntryId, 1000, {
|
||||
preserveData: firstPreserve,
|
||||
});
|
||||
const rewindId = session.appendMessage({ role: "user", content: "between compactions", timestamp: 3 });
|
||||
session.appendCompaction(secondSummary, undefined, rewindId, 2000);
|
||||
await session.flush();
|
||||
@@ -188,15 +182,9 @@ describe("large session memory guards", () => {
|
||||
|
||||
const branchACompactionSummary = `branch-a-${"x".repeat(1024)}`;
|
||||
const branchAPreserve = { openaiRemoteCompaction: { provider: "anthropic", replacementHistory: [] } };
|
||||
session.appendCompaction(
|
||||
branchACompactionSummary,
|
||||
undefined,
|
||||
rootId,
|
||||
1000,
|
||||
undefined,
|
||||
undefined,
|
||||
branchAPreserve,
|
||||
);
|
||||
session.appendCompaction(branchACompactionSummary, undefined, rootId, 1000, {
|
||||
preserveData: branchAPreserve,
|
||||
});
|
||||
const branchACompactionId = session.getLeafId();
|
||||
if (!branchACompactionId) throw new Error("Expected branch A compaction id");
|
||||
|
||||
|
||||
@@ -219,10 +219,13 @@ describe("AgentSession shake", () => {
|
||||
seedHeavyToolResult("X".repeat(20_000));
|
||||
const firstKeptEntryId = sessionManager.getBranch()[0]?.id;
|
||||
if (!firstKeptEntryId) throw new Error("Expected seeded branch");
|
||||
sessionManager.appendCompaction("remote summary", undefined, firstKeptEntryId, 10_000, {}, false, {
|
||||
openaiRemoteCompaction: {
|
||||
provider: "openai",
|
||||
replacementHistory: [],
|
||||
sessionManager.appendCompaction("remote summary", undefined, firstKeptEntryId, 10_000, {
|
||||
details: {},
|
||||
preserveData: {
|
||||
openaiRemoteCompaction: {
|
||||
provider: "openai",
|
||||
replacementHistory: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
sessionManager.appendMessage({
|
||||
|
||||
Reference in New Issue
Block a user