mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 14:38:25 +00:00
fix: preserve inter-session input provenance (thanks @anbecker)
This commit is contained in:
@@ -4,6 +4,10 @@ import type { TSchema } from "@sinclair/typebox";
|
||||
import { EventEmitter } from "node:events";
|
||||
import type { TranscriptPolicy } from "../transcript-policy.js";
|
||||
import { registerUnhandledRejectionHandler } from "../../infra/unhandled-rejections.js";
|
||||
import {
|
||||
hasInterSessionUserProvenance,
|
||||
normalizeInputProvenance,
|
||||
} from "../../sessions/input-provenance.js";
|
||||
import {
|
||||
downgradeOpenAIReasoningBlocks,
|
||||
isCompactionFailureError,
|
||||
@@ -44,6 +48,7 @@ const GOOGLE_SCHEMA_UNSUPPORTED_KEYWORDS = new Set([
|
||||
"maxProperties",
|
||||
]);
|
||||
const ANTIGRAVITY_SIGNATURE_RE = /^[A-Za-z0-9+/]+={0,2}$/;
|
||||
const INTER_SESSION_PREFIX_BASE = "[Inter-session message]";
|
||||
|
||||
function isValidAntigravitySignature(value: unknown): value is string {
|
||||
if (typeof value !== "string") {
|
||||
@@ -119,6 +124,85 @@ export function sanitizeAntigravityThinkingBlocks(messages: AgentMessage[]): Age
|
||||
return touched ? out : messages;
|
||||
}
|
||||
|
||||
function buildInterSessionPrefix(message: AgentMessage): string {
|
||||
const provenance = normalizeInputProvenance((message as { provenance?: unknown }).provenance);
|
||||
if (!provenance) {
|
||||
return INTER_SESSION_PREFIX_BASE;
|
||||
}
|
||||
const details = [
|
||||
provenance.sourceSessionKey ? `sourceSession=${provenance.sourceSessionKey}` : undefined,
|
||||
provenance.sourceChannel ? `sourceChannel=${provenance.sourceChannel}` : undefined,
|
||||
provenance.sourceTool ? `sourceTool=${provenance.sourceTool}` : undefined,
|
||||
].filter(Boolean);
|
||||
if (details.length === 0) {
|
||||
return INTER_SESSION_PREFIX_BASE;
|
||||
}
|
||||
return `${INTER_SESSION_PREFIX_BASE} ${details.join(" ")}`;
|
||||
}
|
||||
|
||||
function annotateInterSessionUserMessages(messages: AgentMessage[]): AgentMessage[] {
|
||||
let touched = false;
|
||||
const out: AgentMessage[] = [];
|
||||
for (const msg of messages) {
|
||||
if (!hasInterSessionUserProvenance(msg as { role?: unknown; provenance?: unknown })) {
|
||||
out.push(msg);
|
||||
continue;
|
||||
}
|
||||
const prefix = buildInterSessionPrefix(msg);
|
||||
const user = msg as Extract<AgentMessage, { role: "user" }>;
|
||||
if (typeof user.content === "string") {
|
||||
if (user.content.startsWith(prefix)) {
|
||||
out.push(msg);
|
||||
continue;
|
||||
}
|
||||
touched = true;
|
||||
out.push({
|
||||
...(msg as unknown as Record<string, unknown>),
|
||||
content: `${prefix}\n${user.content}`,
|
||||
} as AgentMessage);
|
||||
continue;
|
||||
}
|
||||
if (!Array.isArray(user.content)) {
|
||||
out.push(msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
const textIndex = user.content.findIndex(
|
||||
(block) =>
|
||||
block &&
|
||||
typeof block === "object" &&
|
||||
(block as { type?: unknown }).type === "text" &&
|
||||
typeof (block as { text?: unknown }).text === "string",
|
||||
);
|
||||
|
||||
if (textIndex >= 0) {
|
||||
const existing = user.content[textIndex] as { type: "text"; text: string };
|
||||
if (existing.text.startsWith(prefix)) {
|
||||
out.push(msg);
|
||||
continue;
|
||||
}
|
||||
const nextContent = [...user.content];
|
||||
nextContent[textIndex] = {
|
||||
...existing,
|
||||
text: `${prefix}\n${existing.text}`,
|
||||
};
|
||||
touched = true;
|
||||
out.push({
|
||||
...(msg as unknown as Record<string, unknown>),
|
||||
content: nextContent,
|
||||
} as AgentMessage);
|
||||
continue;
|
||||
}
|
||||
|
||||
touched = true;
|
||||
out.push({
|
||||
...(msg as unknown as Record<string, unknown>),
|
||||
content: [{ type: "text", text: prefix }, ...user.content],
|
||||
} as AgentMessage);
|
||||
}
|
||||
return touched ? out : messages;
|
||||
}
|
||||
|
||||
function findUnsupportedSchemaKeywords(schema: unknown, path: string): string[] {
|
||||
if (!schema || typeof schema !== "object") {
|
||||
return [];
|
||||
@@ -358,13 +442,18 @@ export async function sanitizeSessionHistory(params: {
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
});
|
||||
const sanitizedImages = await sanitizeSessionMessagesImages(params.messages, "session:history", {
|
||||
sanitizeMode: policy.sanitizeMode,
|
||||
sanitizeToolCallIds: policy.sanitizeToolCallIds,
|
||||
toolCallIdMode: policy.toolCallIdMode,
|
||||
preserveSignatures: policy.preserveSignatures,
|
||||
sanitizeThoughtSignatures: policy.sanitizeThoughtSignatures,
|
||||
});
|
||||
const withInterSessionMarkers = annotateInterSessionUserMessages(params.messages);
|
||||
const sanitizedImages = await sanitizeSessionMessagesImages(
|
||||
withInterSessionMarkers,
|
||||
"session:history",
|
||||
{
|
||||
sanitizeMode: policy.sanitizeMode,
|
||||
sanitizeToolCallIds: policy.sanitizeToolCallIds,
|
||||
toolCallIdMode: policy.toolCallIdMode,
|
||||
preserveSignatures: policy.preserveSignatures,
|
||||
sanitizeThoughtSignatures: policy.sanitizeThoughtSignatures,
|
||||
},
|
||||
);
|
||||
const sanitizedThinking = policy.normalizeAntigravityThinkingBlocks
|
||||
? sanitizeAntigravityThinkingBlocks(sanitizedImages)
|
||||
: sanitizedImages;
|
||||
|
||||
Reference in New Issue
Block a user