Two ways to forget
Context compaction has become the memory manager of every long-running agent. The two leading frameworks have made opposite choices about whether it destroys the record or merely hides it, and the standard meant to make it observable has settled on a flag that can never be false.
The argumentCompaction is the agent runtime's memory manager, the leading frameworks disagree on whether it destroys the transcript or merely hides it, and the only standard signal for it is a flag that can never be false.
Ten items. That is the threshold at which the OpenAI Agents SDK decides your agent has accumulated more history than it needs, and does something about it. The constant is right there in the source — DEFAULT_COMPACTION_THRESHOLD = 10 — and the function that reads it counts only what it calls compaction candidates, which is everything in the transcript except the user's own messages and the summaries of previous compactions. Tool calls. Tool output. The model's reasoning. The record of what the agent actually did, in other words, is the part eligible for deletion, and the part where a person typed something is the part that survives.
What happens at item eleven depends on a library choice nobody writes down in an architecture decision record. In the OpenAI SDK, a compaction-aware session calls a server-side endpoint, responses.compact, to produce a summary, and then calls clear_session() on whatever store is underneath and writes the summary in its place. What that endpoint does inside the provider is not visible from the client, and nothing below is claimed about it; everything here is read from the libraries' own source and the text of the specification. The pre-compaction history is held for the duration of that call in a local variable named previous_items, so that a failure or a cancellation can put it back. If the replacement succeeds, that variable goes out of scope. The history is gone from the store.
Google's Agent Development Kit does the opposite. Compaction there produces an EventCompaction — a start timestamp, an end timestamp, and the summarised content — which is appended to the session as an event in its own right. The raw events it covers stay exactly where they were. When ADK assembles the history to send to the model, _process_compaction_events filters out the events a summary covers; there is even a function whose job is to reach back and recover a function call that compaction had hidden, for the case where a long-running call is resumed after the range containing it was summarised. In ADK, compaction is a view. In the OpenAI SDK, it is a write.
Both are defensible. Neither is wrong. But they are opposite answers to a question of real consequence — does the system retain what it decided to stop reading? — and the question is being settled at the level of which package you imported. That is the argument here: compaction has become the memory manager of every long-running agent, the frameworks disagree about whether it destroys the record, and the industry has standardised the fact that it happened without standardising anything about how much it cost.
Start with how invisible it is. The OpenAI SDK has a substantial tracing subsystem; span_data.py defines thirteen span types. Agents get a span. Turns get a span. Individual function calls get spans, as do handoffs, guardrails, transcription, speech, and — this is the one worth sitting with — listing the tools available on an MCP server. Enumerating tools is considered an event worth a span. Replacing the conversation with a summary of itself is not. There is no compaction span anywhere in that module. On the success path, the operation announces itself through logger.debug twice: once at start, once at done. The only message that rises to logger.warning is the one emitted when compaction is skipped because the session changed underneath it. Run at the default log level, which is what production does, and the most destructive operation in the framework is the quietest thing it does.
ADK made the better choice and it is worth saying so. It emits a span for compaction — operation name compact_events — with attributes for the trigger, the summariser type, the number of events involved, the token threshold, the retention size, the overlap, and the identifier of the resulting event. Somebody there thought carefully about what an operator would need to know. The difficulty is that every one of those attribute names is gen_ai.compaction.*, and no specification defines any of them. They sit in a namespace the conventions own, describing a concept the conventions did not have. Two agent frameworks, two philosophies of forgetting, and no vocabulary in which to compare them.
That vocabulary is arriving, and its shape is the most revealing detail of all. The OpenTelemetry GenAI conventions have moved out of the main semantic-conventions repository into one of their own — the old docs/gen-ai/README.md is now a forwarding notice — and in that new repository, merged to the main branch but not yet in any release, is a first attempt: gen_ai.conversation.compacted. It is a boolean. Its brief reads "indicates whether the effective conversation context used for this operation is a compacted view of a prior conversation." Its note instructs instrumentations to set it to true only when they can reliably determine that compaction was applied, and then adds this: instrumentations should not set it to false; they should leave it unset otherwise.
Consider what that produces. A flag whose only legal value is true. Absence means either that nothing was compacted or that the instrumentation could not tell, and there is nothing in the signal to distinguish those two. You cannot build a rate from it, because you have no denominator. You cannot alert on a change in it, because it has no baseline. It records that forgetting occurred, for those cases where somebody was in a position to notice, and it records nothing whatsoever about how much.
The accompanying message-schema change is the same story in a different register. A CompactionPart can now appear in the recorded input and output messages, which is genuinely useful: the summary text itself can be captured, when available. Look at the required fields, though. There is exactly one, type, whose value is the constant "compaction". The identifier is optional. The content is optional. And there is no field at all — not optional, not recommended, not reserved — for what the summary replaced. No count of the items removed, no token figure before and after, no time range of the sort ADK has been recording in EventCompaction all along. The schema can represent the summary. It cannot represent the loss.
Here is where an experienced engineer will push back, and the objection is a good one. Caches have always been lossy and heuristic, and we have never demanded proofs from them. A summariser has no ground truth: there is no oracle that says which of the discarded tokens would have mattered on turn forty, so asking for a miss rate is asking for a number nobody can compute. Semantic conventions are also right to be minimal. Attributes marked development are deliberately cheap to change, an over-specified signal is one nobody emits, and a boolean that instrumentations will actually set beats a rich schema they ignore. On this reading, a flag is a sensible first brick and magnitude follows once implementations agree.
Most of that I accept. But the cache analogy is doing more work than it can bear, and the place it breaks is precisely the interesting place. Caches are permitted to be lossy because a miss is detectable and recoverable: you notice, you go to the source of truth, you pay latency instead of correctness. Compaction in the OpenAI SDK has neither property. There is no miss signal, and after the replace there is no source of truth to fall back to — that is what clear_session() means. It is not a cache. It is a lossy write to the only copy, performed automatically, mid-task, by a model chosen by default rather than by you. The compaction model defaults to gpt-4.1; the class validates that it is an OpenAI model and otherwise leaves it alone. So the summary of your most capable model's work is, unless you intervene, written by a different and cheaper one.
And the minimality defence concedes the smaller claim while missing the larger. Nobody is asking the conventions to quantify semantic loss, which is genuinely hard. They are being asked for the count of items dropped and the token totals either side — numbers both frameworks already hold at the moment of the call. ADK puts event_count and token_threshold on its span today. The OpenAI SDK adds the compaction request's own token usage to the run's billing totals, which means it has the figures and spends them on cost accounting rather than on telling you what you lost. The magnitude is not missing because it is unknowable. It is missing because no one decided it was the interesting part.
Which leaves the practical question of what the record of an autonomous system is. When an agent run goes wrong across six hours and four hundred tool calls, the artefact you reach for is the transcript, and by then the transcript may be a document the system wrote about itself — accurate in the way a summary is accurate, silent about whatever the summariser judged unimportant, and bearing no mark of where the seams are. ADK's design survives that moment because the events are still there. The other does not, and the difference will not appear in any diagram, any conformance test, or any dashboard. It appears once, in an incident review, when somebody asks what the agent saw before it decided and the honest answer is that nobody kept it.
The reasonable inference for anyone building on this is not to avoid compaction, which is unavoidable once runs outgrow a window. It is to stop treating the transcript as the durable state of the task. Write what matters to a store you can read — files, a task record, a database — and let the context window be what it actually is, a register file whose contents are expendable. Prefer the framework that keeps the log and compacts as a view. Set the trigger on tokens rather than on a count of ten items, which fires identically whether those items are one line each or a thousand. Choose the summarising model deliberately. And if you are instrumenting any of this, know that the standard will soon let you record that your system forgot something, and still will not let you say what.
We built a memory hierarchy and gave it an eviction policy written in prose. Then we agreed on a way to note that eviction had occurred, and stopped there. Every other cache in the history of this profession was born with a counter attached, because the first question anyone asks of a cache is how often it is wrong. This is the first one we have shipped at scale that cannot be asked.
What this is argued from
Reporting and primary material the piece rests on, dated at the time of writing. The interpretation is mine; the facts belong to these.
- openai-agents-python — src/agents/memory/openai_responses_compaction_session.py (read at commit fbd2dbc)
- openai-agents-python — src/agents/tracing/span_data.py (read at commit fbd2dbc)
- adk-python — src/google/adk/events/event_actions.py, EventCompaction (read at commit f2fe475)
- adk-python — src/google/adk/apps/_configs.py, EventsCompactionConfig (read at commit f2fe475)
- adk-python — src/google/adk/telemetry/tracing.py, compaction span attributes (read at commit f2fe475)
- OpenTelemetry GenAI semantic conventions — gen_ai.conversation.compacted in model/gen-ai/registry.yaml (read at commit 0c87594)
- OpenTelemetry GenAI semantic conventions — CompactionPart in model/gen-ai/gen-ai-input-messages.json (read at commit 0c87594)
- OpenTelemetry semantic-conventions — docs/gen-ai/README.md, notice that GenAI conventions have moved (read at commit 22b6cbb)
Editorials on this site are written to be argued with. If you think the reading is wrong, it probably is in some particular way, and that is the useful part.