fix: preserve inter-session input provenance (thanks @anbecker)

This commit is contained in:
Peter Steinberger
2026-02-13 02:01:53 +01:00
parent 7081dee1af
commit 85409e401b
25 changed files with 415 additions and 12 deletions

View File

@@ -113,6 +113,10 @@ function extractToolResultId(msg: Extract<AgentMessage, { role: "toolResult" }>)
export function installSessionToolResultGuard(
sessionManager: SessionManager,
opts?: {
/**
* Optional transform applied to any message before persistence.
*/
transformMessageForPersistence?: (message: AgentMessage) => AgentMessage;
/**
* Optional, synchronous transform applied to toolResult messages *before* they are
* persisted to the session transcript.
@@ -133,6 +137,10 @@ export function installSessionToolResultGuard(
} {
const originalAppend = sessionManager.appendMessage.bind(sessionManager);
const pending = new Map<string, string | undefined>();
const persistMessage = (message: AgentMessage) => {
const transformer = opts?.transformMessageForPersistence;
return transformer ? transformer(message) : message;
};
const persistToolResult = (
message: AgentMessage,
@@ -152,7 +160,7 @@ export function installSessionToolResultGuard(
for (const [id, name] of pending.entries()) {
const synthetic = makeMissingToolResult({ toolCallId: id, toolName: name });
originalAppend(
persistToolResult(synthetic, {
persistToolResult(persistMessage(synthetic), {
toolCallId: id,
toolName: name,
isSynthetic: true,
@@ -186,7 +194,7 @@ export function installSessionToolResultGuard(
}
// Apply hard size cap before persistence to prevent oversized tool results
// from consuming the entire context window on subsequent LLM calls.
const capped = capToolResultSize(nextMessage);
const capped = capToolResultSize(persistMessage(nextMessage));
return originalAppend(
persistToolResult(capped, {
toolCallId: id ?? undefined,
@@ -212,7 +220,7 @@ export function installSessionToolResultGuard(
}
}
const result = originalAppend(nextMessage as never);
const result = originalAppend(persistMessage(nextMessage) as never);
const sessionFile = (
sessionManager as { getSessionFile?: () => string | null }