Skip to content

feat(ai-chat): add sanitizeMessageForPersistence hook and Anthropic tool payload truncation#1150

Merged
threepointone merged 3 commits intomainfrom
santize-message-hook
Mar 23, 2026
Merged

feat(ai-chat): add sanitizeMessageForPersistence hook and Anthropic tool payload truncation#1150
threepointone merged 3 commits intomainfrom
santize-message-hook

Conversation

@threepointone
Copy link
Contributor

@threepointone threepointone commented Mar 22, 2026

Summary

Adds a user-overridable hook for customizing message sanitization before persistence, and built-in truncation of large Anthropic server-side tool payloads. Closes #1118.

Problem

  1. Anthropic's server-side tools (code_execution, text_editor) produce tool parts where input and output can contain 200KB+ of text. After a few exchanges, messages balloon to 1MB+ of dead weight — the model has already consumed the results.
  2. No extensibility_sanitizeMessageForPersistence was private, so users had no way to strip provider-specific or domain-specific bloat from persisted messages.

Solution

Built-in Anthropic truncation: For any tool part with providerExecuted === true, large string values in input and output are recursively truncated to 500 characters with a size marker (e.g. "… [truncated, original length: 10000]"). This only applies to provider-executed tools — user-defined tool outputs are untouched.

User-overridable hook: A new protected sanitizeMessageForPersistence(message) method that subclasses can override. It runs after all built-in sanitization, so the framework's own cleanup is never bypassable. The default implementation is identity (returns the message unchanged).

The full sanitization pipeline is now:

  1. Strip OpenAI ephemeral metadata (itemId, reasoningEncryptedContent)
  2. Truncate provider-executed tool payloads (new)
  3. Filter empty reasoning parts
  4. Call user hook (new)

Usage

class MyAgent extends AIChatAgent<Env> {
  protected sanitizeMessageForPersistence(message: UIMessage): UIMessage {
    return {
      ...message,
      parts: message.parts.map(part => {
        if ("output" in part && typeof part.output === "string"
            && part.output.length > 2000) {
          return { ...part, output: "[redacted]" };
        }
        return part;
      })
    };
  }
}

Changes

  • packages/ai-chat/src/index.ts — Added sanitizeMessageForPersistence protected hook, _truncateProviderExecutedToolPayloads and _truncateLargeStrings static helpers, updated _sanitizeMessageForPersistence pipeline and JSDoc
  • packages/ai-chat/src/tests/sanitize-messages.test.ts — 4 new tests: provider-executed tool truncation, no truncation for user tools, custom hook invocation, hook ordering with built-in sanitization
  • packages/ai-chat/src/tests/worker.ts — Added CustomSanitizeAgent test agent
  • packages/ai-chat/src/tests/wrangler.jsonc — Binding + migration for CustomSanitizeAgent
  • docs/chat-agents.md — Documented sanitizeMessageForPersistence in Storage Management section, updated the context-vs-storage table
  • .changeset/sanitize-message-hook.md — Minor changeset for @cloudflare/ai-chat

Test plan

  • Existing 314 tests pass (no regressions)
  • New test: provider-executed tool parts with large input.file_text and output.content are truncated, short fields preserved
  • New test: user-defined tool outputs (no providerExecuted) are not truncated
  • New test: CustomSanitizeAgent override is called and transforms output
  • New test: user hook runs after built-in OpenAI stripping (both apply)
  • npm run check passes (sherif, exports, oxfmt, oxlint, typecheck)

Made with Cursor


Open with Devin

Introduce a protected sanitizeMessageForPersistence(message) hook on AIChatAgent to allow custom per-message transforms before persistence (defaults to identity). Add server-side truncation of large string values in provider-executed tool parts (e.g. Anthropic code_execution / text_editor) to avoid persisting huge payloads; truncation appends a marker with the original length and uses a 500-char cutoff. Update docs to describe the hook and built-in sanitization steps, add tests covering truncation and the custom hook, register a CustomSanitizeAgent for testing, and include a changeset entry. Closes #1118.
@changeset-bot
Copy link

changeset-bot bot commented Mar 22, 2026

🦋 Changeset detected

Latest commit: 20d7798

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/ai-chat Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 22, 2026

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@1150

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@1150

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@1150

hono-agents

npm i https://pkg.pr.new/hono-agents@1150

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@1150

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@1150

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@1150

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@1150

commit: 20d7798

Change _truncateLargeStrings to reserve space for the truncation marker so the total length (content + marker) stays within PROVIDER_TOOL_MAX_STRING_LENGTH. Use a computed contentLength (clamped to >=0) so re-running truncation on already-truncated strings is a no-op. Update tests to assert lengths are <= threshold (500) and add an idempotency test that re-persisting truncated messages does not alter stored content.
@threepointone threepointone merged commit 81a8710 into main Mar 23, 2026
2 checks passed
@threepointone threepointone deleted the santize-message-hook branch March 23, 2026 11:18
@github-actions github-actions bot mentioned this pull request Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: add public way to extend AIChatAgent._sanitizeMessageForPersistence

1 participant