Skip to main content
Providers compact a conversation within a single request. Anthropic’s context editing clears old tool-use blocks server-side, and OpenAI’s stored responses keep the thread server-side so you only send the delta. Neither changes what your agent has accumulated, so on its own the next turn re-sends the whole transcript again and the token saving is lost. This is the gap this page closes. After each turn, mirror what the provider compacted into the agent’s stored history with chat.history.set(), so the next turn is derived from the already-reduced conversation. And because a native handle is provider-specific, this page also shows how a provider-agnostic Trigger.dev compaction summary lets you fall back between providers without re-expanding the context.
The full runnable example is triggerdotdev/resilient-chat-example. See native-persist.ts for the Anthropic persistence flow and resilient-chat.ts for OpenAI stored responses plus provider fallback.

Two kinds of compaction

They are not competing; they compose. Native compaction is the per-turn optimization, and Trigger.dev compaction is the durable, portable checkpoint.

Persist Anthropic native context editing

Anthropic’s contextManagement clears old tool-use/tool-result blocks server-side per request, and reports how many it cleared in providerMetadata.anthropic.contextManagement.appliedEdits (clearedToolUses, clearedInputTokens). It does not touch your accumulated history, so on its own the next turn still re-sends everything. The fix: read the appliedEdits counts as they stream in onStepFinish, then after the turn mirror that clearing into stored history with chat.history.set(). No custom summarizer is involved, since the provider’s native editing drives what gets persisted.
/trigger/native-persist.ts
Turn 1 sends the user message and accumulates six tool results. Anthropic clears four of them server-side. onTurnComplete prunes those four from stored history, so turn 2 re-sends the smaller conversation (one tool result, not six) instead of the full transcript.
onTurnComplete is where persistence happens. Action turns fire onAction only, and a chat.history.set() inside run() is overwritten by the accumulator at turn end. See Persistence and replay.

Persist OpenAI stored responses

OpenAI’s store: true keeps the thread server-side and returns a responseId. Pass that back as previousResponseId on the next turn and send only the messages since the last assistant reply; everything before it lives on OpenAI’s side.
/trigger/openai-store.ts
Turn 1 stores the thread and sends all three messages. Turn 2 sends only the new user message (1/3), because OpenAI already has the rest.

Fall back between providers without losing history

A native handle is a per-provider cache. An OpenAI previousResponseId means nothing to Anthropic, and Anthropic’s server-side edits don’t exist on OpenAI. So when a provider is down and you fall back to another, the native optimization is a cache miss, and a naive fallback re-sends the entire raw transcript to the new provider. Trigger.dev’s compaction is the portable checkpoint that closes this gap. summarize returns a plain string and compactModelMessages returns neutral ModelMessage[], so the summary survives any provider switch. Tag each native handle with the provider that produced it. On a switch it’s a cache miss, and you rebuild from the summary instead of re-expanding the context.
/trigger/resilient-chat.ts
When Anthropic is down, the loop falls through to OpenAI. Because compaction has already reduced messages to a summary plus the last couple of exchanges, the switch sends the portable baseline, not megabytes of raw transcript.
Fallback here retries a turn that hasn’t started streaming yet. Once a response is streaming to the client, a mid-stream provider failure can’t be swapped transparently. Surface the error and let the frontend regenerate the turn. See Error handling.

Production notes

  • Persist the handles. The Maps above (nativeStore, clearedByChat) work in the example because the run stays alive across turns, but they don’t survive a run boundary. Store native handles and summaries in your database keyed by chatId, alongside your message persistence.
  • No cross-provider translation. Native compaction from one provider never transfers to another. The Trigger.dev compaction summary is the only portable baseline across a switch.
  • Native compaction is opt-in per turn. It applies only for the provider whose providerOptions you set on that turn’s streamText call.

See also

  • Compaction: the provider-agnostic compaction option, onCompacted, and manual chat.compact().
  • Prompt caching: the other per-turn token optimization, and how it interacts with a growing history.
  • Database persistence: where to store native handles and summaries for real.
  • Lifecycle hooks: onTurnComplete and onCompacted in the broader hook taxonomy.