openclaw/src/agents/pi-embedded-helpers.ts
eveiljuice 6bee86e618 fix(agents): add cross-turn dedup cache for embedded assistant text
shouldSkipAssistantText() only deduplicated within the same
assistantMessageIndex. After context compaction the index resets, so
when the model re-emits the same text from the compaction summary it
bypasses the dedup check and gets delivered as a new message.

Added recentDeliveredTexts — a bounded rolling cache (max 20 entries,
1h TTL) that persists across assistant message turns within a session.
Before delivering text, normalized characters are compared against
recently delivered hashes. Matches are skipped.

Includes unit tests covering hash building, cross-turn duplicate
detection, TTL expiration, whitespace/casing normalization, cache
eviction and capacity limits.

Closes #37702
Related: #38434, #33308, #33453, #33592, #37697, #30316
2026-03-12 03:44:51 -04:00

76 lines
2.3 KiB
TypeScript

export {
buildBootstrapContextFiles,
DEFAULT_BOOTSTRAP_MAX_CHARS,
DEFAULT_BOOTSTRAP_PROMPT_TRUNCATION_WARNING_MODE,
DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS,
ensureSessionHeader,
resolveBootstrapMaxChars,
resolveBootstrapPromptTruncationWarningMode,
resolveBootstrapTotalMaxChars,
stripThoughtSignatures,
} from "./pi-embedded-helpers/bootstrap.js";
export {
BILLING_ERROR_USER_MESSAGE,
formatBillingErrorMessage,
classifyFailoverReason,
classifyFailoverReasonFromHttpStatus,
formatRawAssistantErrorForUi,
formatAssistantErrorText,
getApiErrorPayloadFingerprint,
isAuthAssistantError,
isAuthErrorMessage,
isAuthPermanentErrorMessage,
isModelNotFoundErrorMessage,
isBillingAssistantError,
parseApiErrorInfo,
sanitizeUserFacingText,
isBillingErrorMessage,
isCloudflareOrHtmlErrorPage,
isCloudCodeAssistFormatError,
isCompactionFailureError,
isContextOverflowError,
isLikelyContextOverflowError,
isFailoverAssistantError,
isFailoverErrorMessage,
isImageDimensionErrorMessage,
isImageSizeError,
isOverloadedErrorMessage,
isRawApiErrorPayload,
isRateLimitAssistantError,
isRateLimitErrorMessage,
isTransientHttpError,
isTimeoutErrorMessage,
parseImageDimensionError,
parseImageSizeError,
} from "./pi-embedded-helpers/errors.js";
export { isGoogleModelApi, sanitizeGoogleTurnOrdering } from "./pi-embedded-helpers/google.js";
export {
downgradeOpenAIFunctionCallReasoningPairs,
downgradeOpenAIReasoningBlocks,
} from "./pi-embedded-helpers/openai.js";
export {
isEmptyAssistantMessageContent,
sanitizeSessionMessagesImages,
} from "./pi-embedded-helpers/images.js";
export {
isMessagingToolDuplicate,
isMessagingToolDuplicateNormalized,
normalizeTextForComparison,
isRecentlyDelivered,
recordDeliveredText,
} from "./pi-embedded-helpers/messaging-dedupe.js";
export type { RecentDeliveredEntry } from "./pi-embedded-helpers/messaging-dedupe.js";
export { pickFallbackThinkingLevel } from "./pi-embedded-helpers/thinking.js";
export {
mergeConsecutiveUserTurns,
validateAnthropicTurns,
validateGeminiTurns,
} from "./pi-embedded-helpers/turns.js";
export type { EmbeddedContextFile, FailoverReason } from "./pi-embedded-helpers/types.js";
export type { ToolCallIdMode } from "./tool-call-id.js";
export { isValidCloudCodeAssistToolId, sanitizeToolCallId } from "./tool-call-id.js";