Compaction keeps an agent’s context small by replacing large, already-processed tool data with lightweight references. The agent receives the minimum context needed to continue and can call a tool again if it needs the original data. This is an in-run context-management technique, not a way to preserve every detail of a conversation. section 6.2.4 section 6.1.3
Compact both sides of a tool interaction:
- For a write operation such as create_file, replace large content in the ToolCall arguments after it has been saved. Keep a reference indicating that the content is in the file. section 6.2.4
- For a read operation such as read_file or search_web, replace the ToolResult body after the agent has processed it. Keep enough information—such as a file path or query—to retrieve it again. section 6.2.4
An implementation can walk the request contents once. Record each ToolCall’s arguments under its call ID; because the call precedes its result, those arguments are available when compacting the matching ToolResult. Apply rules by tool name so the replacement is predictable rather than an LLM-generated summary. section 6.2.4 section 6.2.5
Listing 6.5 Compaction-strategy implementation
# Tools to compress ToolCall arguments
TOOLCALL_COMPACTION_RULES = {
"create_file": "[Content saved to file]",
}
# Tools to compress ToolResult content
TOOLRESULT_COMPACTION_RULES = {
"read_file": "File content from {file_path}. Re-read if needed.",
"search_web": "Search results processed. Query: {query}. Re-search if needed.",
"tavily_search": "Search results processed. Query: {query}. Re-search if needed.",
}
def apply_compaction(context: ExecutionContext, request: LlmRequest) -> None:
"""Compress tool calls and results into reference messages"""
tool_call_args: Dict[str, Dict] = {}
compacted = []
for item in request.contents:
if isinstance(item, ToolCall):
# Save arguments (for use when compressing ToolResult later)
tool_call_args[item.tool_call_id] = item.arguments
# If the ToolCall itself is a compression target (create_file, etc.)
if item.name in TOOLCALL_COMPACTION_RULES:
compressed_args = {
k: TOOLCALL_COMPACTION_RULES[item.name] if k == "content" else v
for k, v in item.arguments.items()
}
compacted.append(ToolCall(
tool_call_id=item.tool_call_id,
name=item.name,
arguments=compressed_args
))
else:
compacted.append(item)
elif isinstance(item, ToolResult):
# If ToolResult is a compression target (read_file, search_web, etc.)
if item.name in TOOLRESULT_COMPACTION_RULES:
args = tool_call_args.get(item.tool_call_id, {})
template = TOOLRESULT_COMPACTION_RULES[item.name]
compressed_content = template.format(
file_path=args.get("file_path", args.get("path", "unknown")),
query=args.get("query", "unknown")
)
compacted.append(ToolResult(
tool_call_id=item.tool_call_id,
name=item.name,
status=item.status,
content=[compressed_content]
))
else:
compacted.append(item)
else:
compacted.append(item)
request.contents = compacted
The implementation defines separate replacement rules for ToolCalls and ToolResults, then tracks call arguments while traversing the request. section 6.2.4
A report followed by a search
An agent saves a 10,000-character report with create_file, then processes search results. Instead of carrying the report body and full results into later requests, it retains a file reference and a query reference. If either source becomes necessary again, it calls the corresponding tool. section 6.2.4
Do not compact merely because a replacement is possible. Re-fetching can add fees and latency, and a repeated web search may return different results. Changing an earlier message can also make subsequent prompt content miss the cache. A practical optimizer counts tokens first, skips changes below a threshold, compacts when needed, and uses summarization only if the request remains too large. Unlike compaction, summarization cannot restore discarded reasoning by calling a tool. section 6.2.4 section 6.2.6 section 6.2.5




