





















1+import fs from "node:fs/promises";
2+import os from "node:os";
3+import path from "node:path";
4+import { SessionManager } from "openclaw/plugin-sdk/agent-sessions";
5+import { afterEach, describe, expect, it } from "vitest";
6+import { makeAgentAssistantMessage } from "../test-helpers/agent-message-fixtures.js";
7+import { rotateTranscriptAfterCompaction } from "./compaction-successor-transcript.js";
8+import { readTranscriptFileState } from "./transcript-file-state.js";
9+10+let tmpDir: string | undefined;
11+afterEach(async () => {
12+if (tmpDir) {
13+await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => undefined);
14+tmpDir = undefined;
15+}
16+});
17+18+function makeAssistant(text: string, timestamp: number) {
19+return makeAgentAssistantMessage({ content: [{ type: "text", text }], timestamp });
20+}
21+22+function readUserTexts(entries: { type: string; message?: unknown }[]): string[] {
23+return entries
24+.filter(
25+(entry) =>
26+entry.type === "message" &&
27+(entry.message as { role?: unknown } | undefined)?.role === "user",
28+)
29+.map((entry) => {
30+const content = (entry.message as { content?: unknown } | undefined)?.content;
31+if (typeof content === "string") {
32+return content;
33+}
34+if (Array.isArray(content)) {
35+return content.map((block) => (block as { text?: string })?.text ?? "").join("");
36+}
37+return "";
38+});
39+}
40+41+const PROMPT = "Run the deployment script for staging now";
42+43+describe("rotateTranscriptAfterCompaction post-compaction duplicate", () => {
44+it("keeps a real post-compaction user turn that repeats a kept-tail prompt", async () => {
45+tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "successor-dup-post-"));
46+const manager = SessionManager.create(tmpDir, tmpDir);
47+48+manager.appendMessage({ role: "user", content: "set up the project", timestamp: 1000 });
49+manager.appendMessage(makeAssistant("Project ready.", 1001));
50+51+const firstKeptId = manager.appendMessage({
52+role: "user",
53+content: PROMPT,
54+timestamp: 2000,
55+});
56+manager.appendMessage(makeAssistant("Deploying to staging.", 2001));
57+manager.appendCompaction("Summary of project setup.", firstKeptId, 2050);
58+59+manager.appendMessage({ role: "user", content: PROMPT, timestamp: 2080 });
60+manager.appendMessage(makeAssistant("Redeploying to staging (second run).", 2081));
61+62+const sessionFile = manager.getSessionFile();
63+if (!sessionFile) {
64+throw new Error("no session file");
65+}
66+67+const result = await rotateTranscriptAfterCompaction({ sessionManager: manager, sessionFile });
68+expect(result.rotated).toBe(true);
69+const successorFile = result.sessionFile;
70+if (!successorFile) {
71+throw new Error("no successor file");
72+}
73+74+const successor = await readTranscriptFileState(successorFile);
75+const userPromptTexts = readUserTexts(
76+successor.getEntries() as { type: string; message?: unknown }[],
77+);
78+79+const occurrences = userPromptTexts.filter((text) => text.includes(PROMPT)).length;
80+expect(occurrences).toBe(2);
81+});
82+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。