mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
test: cover durable compaction activity
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { persistCodexContextCompactionActivity } from "./context-compaction-activity.js";
|
||||
|
||||
const appendMessage = vi.hoisted(() => vi.fn());
|
||||
const publishUpdate = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/session-transcript-runtime", () => ({
|
||||
appendSessionTranscriptMessageByIdentity: appendMessage,
|
||||
publishSessionTranscriptUpdateByIdentity: publishUpdate,
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
appendMessage.mockReset();
|
||||
publishUpdate.mockReset();
|
||||
});
|
||||
|
||||
describe("persistCodexContextCompactionActivity", () => {
|
||||
it("publishes one model-excluded activity and leaves replay deduplication to transcript identity", async () => {
|
||||
appendMessage
|
||||
.mockImplementationOnce(async (params: { message: unknown }) => ({
|
||||
appended: true,
|
||||
message: params.message,
|
||||
messageId: "activity-message",
|
||||
}))
|
||||
.mockResolvedValueOnce({
|
||||
appended: false,
|
||||
message: {},
|
||||
messageId: "activity-message",
|
||||
});
|
||||
const params = {
|
||||
run: {
|
||||
runId: "run-1",
|
||||
workspaceDir: "/workspace",
|
||||
sessionTarget: {
|
||||
agentId: "main",
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:dashboard:session-1",
|
||||
storePath: "/state/openclaw-agent.sqlite",
|
||||
},
|
||||
},
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "compact-1",
|
||||
timestamp: 123,
|
||||
} as Parameters<typeof persistCodexContextCompactionActivity>[0];
|
||||
|
||||
await persistCodexContextCompactionActivity(params);
|
||||
await persistCodexContextCompactionActivity(params);
|
||||
|
||||
expect(appendMessage).toHaveBeenCalledTimes(2);
|
||||
expect(appendMessage.mock.calls[0]?.[0]).toMatchObject({
|
||||
eventId: "codex-context-compaction:thread-1:turn-1:compact-1",
|
||||
message: {
|
||||
role: "custom",
|
||||
customType: "openclaw.context-compaction",
|
||||
content: "Context compacted",
|
||||
display: true,
|
||||
excludeFromContext: true,
|
||||
idempotencyKey: "codex-context-compaction:thread-1:turn-1:compact-1",
|
||||
},
|
||||
});
|
||||
expect(publishUpdate).toHaveBeenCalledOnce();
|
||||
expect(publishUpdate.mock.calls[0]?.[0]).toMatchObject({
|
||||
update: {
|
||||
messageId: "activity-message",
|
||||
runId: "run-1",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -133,6 +133,19 @@ function toolResultEntry(
|
||||
}
|
||||
|
||||
describe("buildSessionContext", () => {
|
||||
it("keeps display-only custom activity out of model input", () => {
|
||||
const activity = {
|
||||
role: "custom" as const,
|
||||
customType: "openclaw.context-compaction",
|
||||
content: "Context compacted",
|
||||
display: true,
|
||||
excludeFromContext: true,
|
||||
timestamp: Date.parse(timestamp),
|
||||
};
|
||||
|
||||
expect(convertToLlm([activity])).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps private shell executions in history without projecting them into context", () => {
|
||||
const hiddenEntry = bashEntry("hidden", "initial", "private shell output", true);
|
||||
const visibleEntry = bashEntry("visible", "hidden", "visible shell output", false);
|
||||
|
||||
@@ -86,12 +86,18 @@ export function groupMessages(items: ChatItem[]): Array<ChatItem | MessageGroup>
|
||||
currentGroup?.role === "assistant" &&
|
||||
isKeyedAssistantStreamFallbackMessage(currentGroup.messages[0]?.message) !==
|
||||
isKeyedAssistantStreamFallbackMessage(item.message);
|
||||
const splitsRuntimeActivity =
|
||||
role === "assistant" &&
|
||||
currentGroup?.role === "assistant" &&
|
||||
isContextCompactionActivity(currentGroup.messages[0]?.message) !==
|
||||
isContextCompactionActivity(item.message);
|
||||
|
||||
if (
|
||||
!currentGroup ||
|
||||
startsProjectedTurn ||
|
||||
currentGroup.role !== role ||
|
||||
splitsAssistantCommentary ||
|
||||
splitsRuntimeActivity ||
|
||||
(shouldSplitBySender &&
|
||||
(currentGroup.senderLabel !== senderLabel ||
|
||||
senderIdentityKey(currentGroup.sender) !== senderIdentityKey(sender)))
|
||||
@@ -542,14 +548,15 @@ export function assistantGroupCanOwnActiveRunStatus(group: MessageGroup): boolea
|
||||
return (
|
||||
group.role.toLowerCase() === "assistant" &&
|
||||
!assistantGroupIsForwardedBoundary(group) &&
|
||||
!group.messages.every(
|
||||
({ message }) =>
|
||||
asRecord(asRecord(message)?.["__openclaw"])?.runtimeActivityKind === "context_compaction",
|
||||
) &&
|
||||
!group.messages.every(({ message }) => isContextCompactionActivity(message)) &&
|
||||
groupHasVisibleReplyContent(group)
|
||||
);
|
||||
}
|
||||
|
||||
function isContextCompactionActivity(message: unknown): boolean {
|
||||
return asRecord(asRecord(message)?.["__openclaw"])?.runtimeActivityKind === "context_compaction";
|
||||
}
|
||||
|
||||
// History carries no final-vs-commentary marker (commentary exists only as
|
||||
// live stream segments), so the last assistant group with visible content
|
||||
// stands in for the final reply. Turns whose last content is commentary
|
||||
|
||||
@@ -757,6 +757,35 @@ describe("collapseCompletedTurnWork", () => {
|
||||
expect(requireGroup(items[2]).role).toBe("assistant");
|
||||
});
|
||||
|
||||
it("keeps durable context compaction inside completed work instead of treating it as the reply", () => {
|
||||
const items = collapsedItems({
|
||||
messages: [
|
||||
userMessage("do it", 1_000),
|
||||
{
|
||||
role: "custom",
|
||||
customType: "openclaw.context-compaction",
|
||||
content: "Context compacted",
|
||||
display: true,
|
||||
excludeFromContext: true,
|
||||
timestamp: 2_000,
|
||||
},
|
||||
assistantMessage("All done.", 3_000),
|
||||
],
|
||||
});
|
||||
|
||||
expect(items.map((item) => item.kind)).toEqual(["group", "work-group", "group"]);
|
||||
const work = requireWorkGroup(items[1]);
|
||||
expect(work.groups).toHaveLength(1);
|
||||
expect(work.groups[0]?.messages[0]?.message).toMatchObject({
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "Context compacted" }],
|
||||
__openclaw: { runtimeActivityKind: "context_compaction" },
|
||||
});
|
||||
expect(requireGroup(items[2]).messages[0]?.message).toMatchObject({
|
||||
content: "All done.",
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
role: "assistant",
|
||||
|
||||
Reference in New Issue
Block a user