mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(compaction): skip replay-unsafe session sends (#93293)
* fix(compaction): skip replay-unsafe session sends * fix(compaction): preserve safe text around session sends * fix(compaction): narrow replay filtering to sessions_send Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * test(compaction): use review-safe key fixtures Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * test(compaction): use placeholder API key Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * test(compaction): limit fixture cleanup to new cases Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * fix(compaction): preserve completed sessions_send history Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * fix(compaction): preserve unfinished delegated work Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * fix(compaction): detect completion before replay filtering Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * fix(compaction): retain unresolved send status Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * fix(compaction): preserve inert send outcomes Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * fix(compaction): narrow terminal tail type Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> * fix(compaction): guard replay input lookup Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com> --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
co-authored by
Peter Steinberger
parent
120632e6fc
commit
f771aa54e8
@@ -2537,6 +2537,369 @@ describe("compaction-safeguard double-compaction guard", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("does not replay inter-session sessions_send branch turns as fallback history", async () => {
|
||||
mockSummarizeInStages.mockReset();
|
||||
mockSummarizeInStages.mockResolvedValue("branch summary");
|
||||
|
||||
const now = Date.now();
|
||||
const sessionManager = {
|
||||
...stubSessionManager(),
|
||||
getBranch: () => [
|
||||
{
|
||||
type: "message",
|
||||
id: "user-1",
|
||||
parentId: null,
|
||||
timestamp: new Date(now).toISOString(),
|
||||
message: {
|
||||
role: "user",
|
||||
content: "say bee",
|
||||
provenance: {
|
||||
kind: "inter_session",
|
||||
sourceSessionKey: "agent:pm",
|
||||
sourceTool: "sessions_send",
|
||||
},
|
||||
timestamp: now,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "assistant-1",
|
||||
parentId: "user-1",
|
||||
timestamp: new Date(now + 1).toISOString(),
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "bee reply" }],
|
||||
timestamp: now + 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as ExtensionContext["sessionManager"];
|
||||
const model = createAnthropicModelFixture();
|
||||
setCompactionSafeguardRuntime(sessionManager, { model, recentTurnsPreserve: 0 });
|
||||
|
||||
const mockEvent = {
|
||||
preparation: {
|
||||
messagesToSummarize: [] as AgentMessage[],
|
||||
turnPrefixMessages: [] as AgentMessage[],
|
||||
firstKeptEntryId: "entry-7",
|
||||
tokensBefore: 38085,
|
||||
fileOps: { read: [], edited: [], written: [] },
|
||||
settings: { reserveTokens: 4000 },
|
||||
isSplitTurn: true,
|
||||
},
|
||||
customInstructions: "",
|
||||
signal: new AbortController().signal,
|
||||
};
|
||||
const { result, getApiKeyAndHeadersMock } = await runCompactionScenario({
|
||||
sessionManager,
|
||||
event: mockEvent,
|
||||
apiKey: "dummy",
|
||||
});
|
||||
|
||||
const compaction = expectCompactionResult(result);
|
||||
expect(compaction.summary).toContain("No prior history.");
|
||||
expect(mockSummarizeInStages).not.toHaveBeenCalled();
|
||||
expect(getApiKeyAndHeadersMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ toolName: "read", expectedRoles: ["user", "assistant", "toolResult"] },
|
||||
{ toolName: "functions.sessions_send", expectedRoles: ["user", "assistant"] },
|
||||
])("preserves unfinished inter-session work after a $toolName result", async (scenario) => {
|
||||
mockSummarizeInStages.mockReset();
|
||||
mockSummarizeInStages.mockResolvedValue("unfinished branch summary");
|
||||
|
||||
const now = Date.now();
|
||||
const sessionManager = {
|
||||
...stubSessionManager(),
|
||||
getBranch: () => [
|
||||
{
|
||||
type: "message",
|
||||
id: "user-1",
|
||||
parentId: null,
|
||||
timestamp: new Date(now).toISOString(),
|
||||
message: {
|
||||
role: "user",
|
||||
content: "continue the delegated task",
|
||||
provenance: {
|
||||
kind: "inter_session",
|
||||
sourceSessionKey: "agent:pm",
|
||||
sourceTool: "sessions_send",
|
||||
},
|
||||
timestamp: now,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "assistant-1",
|
||||
parentId: "user-1",
|
||||
timestamp: new Date(now + 1).toISOString(),
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "toolCall",
|
||||
id: "call-tool",
|
||||
name: scenario.toolName,
|
||||
arguments: {},
|
||||
},
|
||||
],
|
||||
timestamp: now + 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "tool-1",
|
||||
parentId: "assistant-1",
|
||||
timestamp: new Date(now + 2).toISOString(),
|
||||
message: {
|
||||
role: "toolResult",
|
||||
toolCallId: "call-tool",
|
||||
toolName: scenario.toolName,
|
||||
content: [{ type: "text", text: "partial evidence" }],
|
||||
timestamp: now + 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as ExtensionContext["sessionManager"];
|
||||
const model = createAnthropicModelFixture();
|
||||
setCompactionSafeguardRuntime(sessionManager, { model, recentTurnsPreserve: 0 });
|
||||
|
||||
const mockEvent = {
|
||||
preparation: {
|
||||
messagesToSummarize: [] as AgentMessage[],
|
||||
turnPrefixMessages: [] as AgentMessage[],
|
||||
firstKeptEntryId: "entry-8",
|
||||
tokensBefore: 38085,
|
||||
fileOps: { read: [], edited: [], written: [] },
|
||||
settings: { reserveTokens: 4000 },
|
||||
isSplitTurn: true,
|
||||
},
|
||||
customInstructions: "",
|
||||
signal: new AbortController().signal,
|
||||
};
|
||||
const { result } = await runCompactionScenario({
|
||||
sessionManager,
|
||||
event: mockEvent,
|
||||
apiKey: "dummy",
|
||||
});
|
||||
|
||||
const compaction = expectCompactionResult(result);
|
||||
expect(compaction.summary).toContain("unfinished branch summary");
|
||||
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
|
||||
const summarizeCall = requireRecord(mockCallArg(mockSummarizeInStages));
|
||||
const messages = requireArray(summarizeCall.messages);
|
||||
expect(messages.map((message) => requireRecord(message).role)).toEqual(scenario.expectedRoles);
|
||||
});
|
||||
|
||||
it("keeps source-session sends as inert status history", async () => {
|
||||
mockSummarizeInStages.mockReset();
|
||||
mockSummarizeInStages.mockResolvedValue("completed send summary");
|
||||
|
||||
const now = Date.now();
|
||||
const sessionManager = {
|
||||
...stubSessionManager(),
|
||||
getBranch: () => [
|
||||
{
|
||||
type: "message",
|
||||
id: "user-1",
|
||||
parentId: null,
|
||||
timestamp: new Date(now).toISOString(),
|
||||
message: {
|
||||
role: "user",
|
||||
content: "ask the bee session to respond",
|
||||
timestamp: now,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "assistant-1",
|
||||
parentId: "user-1",
|
||||
timestamp: new Date(now + 1).toISOString(),
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "toolCall",
|
||||
id: "call-1",
|
||||
name: "functions.sessions_send",
|
||||
arguments: { sessionKey: "agent:bee", message: "say bee" },
|
||||
},
|
||||
{
|
||||
type: "toolCall",
|
||||
id: "call-2",
|
||||
name: "tools/sessions_send",
|
||||
arguments: { sessionKey: "agent:wasp", message: "say wasp" },
|
||||
},
|
||||
{
|
||||
type: "toolCall",
|
||||
id: "call-3",
|
||||
name: "sessions_send",
|
||||
arguments: { sessionKey: "agent:ant", message: "say ant" },
|
||||
},
|
||||
],
|
||||
timestamp: now + 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "tool-1",
|
||||
parentId: "assistant-1",
|
||||
timestamp: new Date(now + 2).toISOString(),
|
||||
message: {
|
||||
role: "toolResult",
|
||||
toolCallId: "call-1",
|
||||
toolName: "functions.sessions_send",
|
||||
content: [{ type: "text", text: '{"status":"ok","reply":"bee replied"}' }],
|
||||
timestamp: now + 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "tool-3",
|
||||
parentId: "tool-1",
|
||||
timestamp: new Date(now + 3).toISOString(),
|
||||
message: {
|
||||
role: "toolResult",
|
||||
toolCallId: "call-3",
|
||||
toolName: "sessions_send",
|
||||
content: [{ type: "text", text: '{"status":"error","error":"ant unavailable"}' }],
|
||||
timestamp: now + 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as ExtensionContext["sessionManager"];
|
||||
const model = createAnthropicModelFixture();
|
||||
setCompactionSafeguardRuntime(sessionManager, { model, recentTurnsPreserve: 0 });
|
||||
|
||||
const mockEvent = {
|
||||
preparation: {
|
||||
messagesToSummarize: [] as AgentMessage[],
|
||||
turnPrefixMessages: [] as AgentMessage[],
|
||||
firstKeptEntryId: "entry-8",
|
||||
tokensBefore: 38085,
|
||||
fileOps: { read: [], edited: [], written: [] },
|
||||
settings: { reserveTokens: 4000 },
|
||||
isSplitTurn: true,
|
||||
},
|
||||
customInstructions: "",
|
||||
signal: new AbortController().signal,
|
||||
};
|
||||
const { result } = await runCompactionScenario({
|
||||
sessionManager,
|
||||
event: mockEvent,
|
||||
apiKey: "dummy",
|
||||
});
|
||||
|
||||
const compaction = expectCompactionResult(result);
|
||||
expect(compaction.summary).toContain("completed send summary");
|
||||
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
|
||||
const summarizeCall = requireRecord(mockCallArg(mockSummarizeInStages));
|
||||
const messages = requireArray(summarizeCall.messages);
|
||||
expect(messages.map((message) => requireRecord(message).role)).toEqual(["user", "assistant"]);
|
||||
expect(JSON.stringify(messages)).toContain("sessions_send result received");
|
||||
expect(JSON.stringify(messages)).toContain("sessions_send result missing");
|
||||
expect(JSON.stringify(messages)).toContain("bee replied");
|
||||
expect(JSON.stringify(messages)).toContain("ant unavailable");
|
||||
expect(JSON.stringify(messages)).toContain("agent:wasp");
|
||||
expect(JSON.stringify(messages)).toContain("say wasp");
|
||||
expect(JSON.stringify(messages)).not.toContain("sessions_send completed");
|
||||
expect(JSON.stringify(messages)).not.toContain("functions.sessions_send");
|
||||
expect(JSON.stringify(messages)).not.toContain("tools/sessions_send");
|
||||
});
|
||||
|
||||
it("preserves completed historical inter-session turns outside the active tail", async () => {
|
||||
mockSummarizeInStages.mockReset();
|
||||
mockSummarizeInStages.mockResolvedValue("historical branch summary");
|
||||
|
||||
const now = Date.now();
|
||||
const sessionManager = {
|
||||
...stubSessionManager(),
|
||||
getBranch: () => [
|
||||
{
|
||||
type: "message",
|
||||
id: "user-1",
|
||||
parentId: null,
|
||||
timestamp: new Date(now).toISOString(),
|
||||
message: {
|
||||
role: "user",
|
||||
content: "historical inter-session request",
|
||||
provenance: {
|
||||
kind: "inter_session",
|
||||
sourceSessionKey: "agent:pm",
|
||||
sourceTool: "sessions_send",
|
||||
},
|
||||
timestamp: now,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "assistant-1",
|
||||
parentId: "user-1",
|
||||
timestamp: new Date(now + 1).toISOString(),
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "historical reply" }],
|
||||
timestamp: now + 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "user-2",
|
||||
parentId: "assistant-1",
|
||||
timestamp: new Date(now + 2).toISOString(),
|
||||
message: { role: "user", content: "later user request", timestamp: now + 2 },
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "assistant-2",
|
||||
parentId: "user-2",
|
||||
timestamp: new Date(now + 3).toISOString(),
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "later reply" }],
|
||||
timestamp: now + 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as ExtensionContext["sessionManager"];
|
||||
const model = createAnthropicModelFixture();
|
||||
setCompactionSafeguardRuntime(sessionManager, { model, recentTurnsPreserve: 0 });
|
||||
|
||||
const mockEvent = {
|
||||
preparation: {
|
||||
messagesToSummarize: [] as AgentMessage[],
|
||||
turnPrefixMessages: [] as AgentMessage[],
|
||||
firstKeptEntryId: "entry-8",
|
||||
tokensBefore: 38085,
|
||||
fileOps: { read: [], edited: [], written: [] },
|
||||
settings: { reserveTokens: 4000 },
|
||||
isSplitTurn: true,
|
||||
},
|
||||
customInstructions: "",
|
||||
signal: new AbortController().signal,
|
||||
};
|
||||
const { result } = await runCompactionScenario({
|
||||
sessionManager,
|
||||
event: mockEvent,
|
||||
apiKey: "dummy",
|
||||
});
|
||||
|
||||
const compaction = expectCompactionResult(result);
|
||||
expect(compaction.summary).toContain("historical branch summary");
|
||||
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
|
||||
const summarizeCall = requireRecord(mockCallArg(mockSummarizeInStages));
|
||||
const messages = requireArray(summarizeCall.messages);
|
||||
expect(messages.map((message) => requireRecord(message).role)).toEqual([
|
||||
"user",
|
||||
"assistant",
|
||||
"user",
|
||||
"assistant",
|
||||
]);
|
||||
expect(JSON.stringify(messages)).toContain("historical inter-session request");
|
||||
expect(JSON.stringify(messages)).toContain("historical reply");
|
||||
});
|
||||
|
||||
it("recovers user and assistant branch turns when compaction preparation has only tool output", async () => {
|
||||
mockSummarizeInStages.mockReset();
|
||||
mockSummarizeInStages.mockResolvedValue("branch summary with visible turns");
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
getCompactionProvider,
|
||||
type CompactionProvider,
|
||||
} from "../../plugins/compaction-provider.js";
|
||||
import { normalizeInputProvenance } from "../../sessions/input-provenance.js";
|
||||
import { normalizeAcceptedSessionSpawnResult } from "../accepted-session-spawn.js";
|
||||
import {
|
||||
buildHistoryPrunePlanWithWorker,
|
||||
@@ -74,6 +75,7 @@ const DEFAULT_QUALITY_GUARD_MAX_RETRIES = 1;
|
||||
const MAX_RECENT_TURNS_PRESERVE = 12;
|
||||
const MAX_QUALITY_GUARD_MAX_RETRIES = 3;
|
||||
const MAX_RECENT_TURN_TEXT_CHARS = 600;
|
||||
const TOOL_CALL_BLOCK_TYPES = new Set(["toolCall", "toolUse", "functionCall"]);
|
||||
const PREVIOUS_SUMMARY_REDISTILL_PREFIX =
|
||||
"Previous compaction summary to re-distill with the current conversation. " +
|
||||
"Prune stale, duplicate, or superseded details instead of preserving it verbatim.";
|
||||
@@ -178,6 +180,154 @@ function collectSessionBranchMessages(sessionManager: unknown): AgentMessage[] {
|
||||
.filter((message): message is AgentMessage => Boolean(message));
|
||||
}
|
||||
|
||||
function isReplayUnsafeInterSessionInput(message: AgentMessage): boolean {
|
||||
if ((message as { role?: unknown }).role !== "user") {
|
||||
return false;
|
||||
}
|
||||
const provenance = normalizeInputProvenance((message as { provenance?: unknown }).provenance);
|
||||
return provenance?.kind === "inter_session" && provenance.sourceTool === "sessions_send";
|
||||
}
|
||||
|
||||
function isSessionsSendToolName(value: unknown): boolean {
|
||||
if (typeof value !== "string") {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/^(?:functions?|tools?)[./_-]/, "") === "sessions_send"
|
||||
);
|
||||
}
|
||||
|
||||
function sanitizeSourceSessionSends(messages: AgentMessage[]): AgentMessage[] {
|
||||
const sendCallIds = new Set<string>();
|
||||
const resolvedCallIds = new Set<string>();
|
||||
const resultTextByCallId = new Map<string, string>();
|
||||
|
||||
for (const message of messages) {
|
||||
if (message.role !== "assistant" || !Array.isArray(message.content)) {
|
||||
continue;
|
||||
}
|
||||
for (const block of message.content) {
|
||||
if (!block || typeof block !== "object") {
|
||||
continue;
|
||||
}
|
||||
const record = block as { type?: unknown; id?: unknown; name?: unknown };
|
||||
if (
|
||||
typeof record.type === "string" &&
|
||||
TOOL_CALL_BLOCK_TYPES.has(record.type) &&
|
||||
isSessionsSendToolName(record.name) &&
|
||||
typeof record.id === "string" &&
|
||||
record.id.trim()
|
||||
) {
|
||||
sendCallIds.add(record.id.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const message of messages) {
|
||||
if (message.role !== "toolResult") {
|
||||
continue;
|
||||
}
|
||||
const callId = extractToolResultId(message);
|
||||
if (!callId || !sendCallIds.has(callId)) {
|
||||
continue;
|
||||
}
|
||||
resolvedCallIds.add(callId);
|
||||
const resultText = extractMessageText(message) || formatNonTextPlaceholder(message.content);
|
||||
if (resultText) {
|
||||
resultTextByCallId.set(callId, resultText);
|
||||
}
|
||||
}
|
||||
|
||||
return messages.flatMap((message) => {
|
||||
if (message.role === "assistant" && Array.isArray(message.content)) {
|
||||
let replaced = false;
|
||||
const content = message.content.map((block) => {
|
||||
if (!block || typeof block !== "object") {
|
||||
return block;
|
||||
}
|
||||
const record = block as {
|
||||
type?: unknown;
|
||||
id?: unknown;
|
||||
name?: unknown;
|
||||
arguments?: unknown;
|
||||
};
|
||||
if (
|
||||
typeof record.type !== "string" ||
|
||||
!TOOL_CALL_BLOCK_TYPES.has(record.type) ||
|
||||
!isSessionsSendToolName(record.name)
|
||||
) {
|
||||
return block;
|
||||
}
|
||||
replaced = true;
|
||||
const callId = typeof record.id === "string" ? record.id.trim() : "";
|
||||
const resultText = callId ? resultTextByCallId.get(callId) : undefined;
|
||||
const resolved = Boolean(callId && resolvedCallIds.has(callId));
|
||||
const requestText = JSON.stringify({ callId: callId || undefined, args: record.arguments });
|
||||
return {
|
||||
type: "text",
|
||||
text:
|
||||
resolved && resultText
|
||||
? `sessions_send result received; delivery call omitted from replay.\nRequest: ${requestText}\nResult: ${resultText}`
|
||||
: resolved
|
||||
? `sessions_send result received; delivery call omitted from replay.\nRequest: ${requestText}\nResult: [empty]`
|
||||
: `sessions_send result missing; delivery call omitted from replay.\nRequest: ${requestText}`,
|
||||
};
|
||||
});
|
||||
return replaced ? [{ ...message, content } as AgentMessage] : [message];
|
||||
}
|
||||
if (message.role === "toolResult") {
|
||||
const callId = extractToolResultId(message);
|
||||
if ((callId && sendCallIds.has(callId)) || isSessionsSendToolName(message.toolName)) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [message];
|
||||
});
|
||||
}
|
||||
|
||||
function filterReplayUnsafeSessionBranchMessages(messages: AgentMessage[]): AgentMessage[] {
|
||||
const sanitizedMessages = sanitizeSourceSessionSends(messages);
|
||||
let turnStart = sanitizedMessages.length;
|
||||
while (turnStart > 0) {
|
||||
const role = (sanitizedMessages[turnStart - 1] as { role?: unknown }).role;
|
||||
if (role !== "assistant" && role !== "toolResult") {
|
||||
break;
|
||||
}
|
||||
turnStart -= 1;
|
||||
}
|
||||
|
||||
const tailMessage = messages.at(-1);
|
||||
const endsWithTerminalAssistantText =
|
||||
tailMessage !== undefined &&
|
||||
tailMessage.role === "assistant" &&
|
||||
Boolean(extractMessageText(tailMessage).trim()) &&
|
||||
(!Array.isArray(tailMessage.content) ||
|
||||
!tailMessage.content.some((block) => {
|
||||
if (!block || typeof block !== "object") {
|
||||
return false;
|
||||
}
|
||||
const type = (block as { type?: unknown }).type;
|
||||
return typeof type === "string" && TOOL_CALL_BLOCK_TYPES.has(type);
|
||||
}));
|
||||
const activeInput = sanitizedMessages[turnStart - 1];
|
||||
|
||||
// A completed sessions_send target run is already delivered to its caller.
|
||||
// Require terminal text so compaction after tool output can still recover unfinished work.
|
||||
if (
|
||||
endsWithTerminalAssistantText &&
|
||||
turnStart < sanitizedMessages.length &&
|
||||
turnStart > 0 &&
|
||||
activeInput !== undefined &&
|
||||
isReplayUnsafeInterSessionInput(activeInput)
|
||||
) {
|
||||
return sanitizedMessages.slice(0, turnStart - 1);
|
||||
}
|
||||
return sanitizedMessages;
|
||||
}
|
||||
|
||||
function containsRealConversation(messages: AgentMessage[]): boolean {
|
||||
return messages.some((message, index, allMessages) =>
|
||||
isRealConversationMessage(message, allMessages, index),
|
||||
@@ -899,8 +1049,8 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
|
||||
let hasRealSummarizable = containsRealConversation(baseMessagesToSummarize);
|
||||
let hasRealTurnPrefix = containsRealConversation(baseTurnPrefixMessages);
|
||||
if (!hasRealSummarizable && !hasRealTurnPrefix) {
|
||||
const branchMessages = stripRuntimeContextCustomMessages(
|
||||
collectSessionBranchMessages(ctx.sessionManager),
|
||||
const branchMessages = filterReplayUnsafeSessionBranchMessages(
|
||||
stripRuntimeContextCustomMessages(collectSessionBranchMessages(ctx.sessionManager)),
|
||||
);
|
||||
if (containsRealConversation(branchMessages)) {
|
||||
log.info(
|
||||
|
||||
Reference in New Issue
Block a user