[{"data":1,"prerenderedAt":5704},["ShallowReactive",2],{"navigation":3,"page-\u002Fchapters\u002Fcompaction":102,"surround-\u002Fchapters\u002Fcompaction":5699},[4,8],{"title":5,"path":6,"stem":7},"Home","\u002F","index",{"title":9,"path":10,"stem":11,"children":12,"page":101},"Chapters","\u002Fchapters","2.chapters",[13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97],{"title":14,"path":15,"stem":16},"Chapter 1. What an Agent Actually Is","\u002Fchapters\u002Fwhat-an-agent-actually-is","2.chapters\u002F01.what-an-agent-actually-is",{"title":18,"path":19,"stem":20},"Chapter 2. The Minimum Viable Loop","\u002Fchapters\u002Fminimum-viable-loop","2.chapters\u002F02.minimum-viable-loop",{"title":22,"path":23,"stem":24},"Chapter 3. Messages, Turns, and the Transcript","\u002Fchapters\u002Fmessages-turns-transcript","2.chapters\u002F03.messages-turns-transcript",{"title":26,"path":27,"stem":28},"Chapter 4. The Tool Protocol","\u002Fchapters\u002Ftool-protocol","2.chapters\u002F04.tool-protocol",{"title":30,"path":31,"stem":32},"Chapter 5. Streaming, Interruption, and Error Handling","\u002Fchapters\u002Fstreaming-interruption-errors","2.chapters\u002F05.streaming-interruption-errors",{"title":34,"path":35,"stem":36},"Chapter 6. Safe Tool Execution","\u002Fchapters\u002Fsafe-tool-execution","2.chapters\u002F06.safe-tool-execution",{"title":38,"path":39,"stem":40},"Chapter 7. The Context Window Is a Resource","\u002Fchapters\u002Fcontext-window-as-resource","2.chapters\u002F07.context-window-as-resource",{"title":42,"path":43,"stem":44},"Chapter 8. Compaction","\u002Fchapters\u002Fcompaction","2.chapters\u002F08.compaction",{"title":46,"path":47,"stem":48},"Chapter 9. External State: The Scratchpad","\u002Fchapters\u002Fscratchpad","2.chapters\u002F09.scratchpad",{"title":50,"path":51,"stem":52},"Chapter 10. Retrieval","\u002Fchapters\u002Fretrieval","2.chapters\u002F10.retrieval",{"title":54,"path":55,"stem":56},"Chapter 11. Designing Tools Models Can Actually Use","\u002Fchapters\u002Fdesigning-tools","2.chapters\u002F11.designing-tools",{"title":58,"path":59,"stem":60},"Chapter 12. The Tool Cliff and Dynamic Loading","\u002Fchapters\u002Ftool-cliff","2.chapters\u002F12.tool-cliff",{"title":62,"path":63,"stem":64},"Chapter 13. MCP: Tools From the Outside World","\u002Fchapters\u002Fmcp","2.chapters\u002F13.mcp",{"title":66,"path":67,"stem":68},"Chapter 14. Sandboxing and Permissions","\u002Fchapters\u002Fsandboxing-permissions","2.chapters\u002F14.sandboxing-permissions",{"title":70,"path":71,"stem":72},"Chapter 15. Sub-agents","\u002Fchapters\u002Fsub-agents","2.chapters\u002F15.sub-agents",{"title":74,"path":75,"stem":76},"Chapter 16. Structured Plans and Verified Completion","\u002Fchapters\u002Fplans-verified-completion","2.chapters\u002F16.plans-verified-completion",{"title":78,"path":79,"stem":80},"Chapter 17. Parallelism and Shared State","\u002Fchapters\u002Fparallelism-shared-state","2.chapters\u002F17.parallelism-shared-state",{"title":82,"path":83,"stem":84},"Chapter 18. Observability","\u002Fchapters\u002Fobservability","2.chapters\u002F18.observability",{"title":86,"path":87,"stem":88},"Chapter 19. Evals","\u002Fchapters\u002Fevals","2.chapters\u002F19.evals",{"title":90,"path":91,"stem":92},"Chapter 20. Cost Control","\u002Fchapters\u002Fcost-control","2.chapters\u002F20.cost-control",{"title":94,"path":95,"stem":96},"Chapter 21. Resumability and Durable State","\u002Fchapters\u002Fresumability","2.chapters\u002F21.resumability",{"title":98,"path":99,"stem":100},"Chapter 22. What Transfers, Where to Go","\u002Fchapters\u002Fwhat-transfers","2.chapters\u002F22.what-transfers",false,{"id":103,"title":42,"body":104,"description":117,"extension":5694,"meta":5695,"navigation":5696,"path":43,"seo":5697,"stem":44,"__hash__":5698},"content\u002F2.chapters\u002F08.compaction.md",{"type":105,"value":106,"toc":5683},"minimark",[107,111,118,121,132,135,152,155,224,227,232,251,257,264,279,281,285,300,1156,1169,1172,1181,1187,1200,1202,1206,1209,1216,1241,1244,2367,2370,2376,2382,2391,2397,2399,2403,2406,3282,3285,3287,3291,3298,3927,3938,3948,3954,3956,3960,3963,5535,5546,5549,5552,5554,5558,5561,5570,5577,5583,5586,5588,5592,5640,5644,5668,5670,5679],[108,109,42],"h1",{"id":110},"chapter-8-compaction",[112,113,114],"p",{},[115,116,117],"em",{},"Previously: the accountant sees the context window. Red state is detected but not acted upon. The transcript keeps growing until the provider refuses to accept it.",[112,119,120],{},"Compaction is the first subsystem in this book where the right answer is not obvious and the design space is genuinely contested. Claude Code compacts by summarizing older turns with a cheaper model. OpenAI's Agents SDK leaves compaction to the developer. LangGraph's graph model offers no native compaction at all. AutoGen's GroupChat scales O(n×m) with agents×turns because no one compacts. Each of these is a design position, and each has specific failure modes.",[112,122,123,124,127,128,131],{},"The research frame here is worth naming. Packer et al.'s 2024 \"MemGPT: Towards LLMs as Operating Systems\" (ICML 2024) proposed the analogy this chapter effectively implements — treat the context window as ",[115,125,126],{},"main memory"," and durable storage as ",[115,129,130],{},"disk",", and give the agent explicit operations for moving data between tiers. MemGPT's specific design (a self-managing memory controller exposed as tools the agent calls) is more ambitious than what we build here; our compactor is automatic, not agent-controlled. But the paradigm is the same: the context window is a finite, managed resource, compaction is the paging policy, and the right shape for the system is a hierarchy where fresh, token-expensive material lives in-window and older material is summarized or offloaded. Every concrete decision in this chapter — mask-before-summarize, preserve-tool-call-record, scratchpad-in-Chapter-9 — is a particular point within that paradigm.",[112,133,134],{},"This chapter builds compaction in two layers, in the order production systems evolved toward:",[136,137,138,146],"ol",{},[139,140,141,145],"li",{},[142,143,144],"strong",{},"Observation masking"," — the cheapest, reversible pass that catches most cases.",[139,147,148,151],{},[142,149,150],{},"LLM summarization"," — the lossy, expensive fallback when masking isn't enough.",[112,153,154],{},"Both fire in response to the accountant's red signal. Both preserve the integrity of the tool-call history — the single most important invariant, because losing it causes the agent to forget what it has done and repeat side effects.",[156,157,161,215],"figure",{"className":158},[159,160],"not-prose","my-8",[162,163,169,181,186,190,193,197,200,208,211],"div",{"className":164},[165,166,167,168],"flex","flex-col","items-center","gap-2",[162,170,180],{"className":171,"style":179},[172,173,174,175,176,177,178],"border","border-default","rounded-lg","py-2","px-4","text-sm","text-default","min-width:260px;text-align:center;","Context utilization check",[162,182,185],{"className":183},[184],"text-muted","↓",[162,187,189],{"className":188,"style":179},[172,173,174,175,176,177,178],"Mask older tool results (reversible)",[162,191,185],{"className":192},[184],[162,194,196],{"className":195,"style":179},[172,173,174,175,176,177,178],"Still over threshold?",[162,198,185],{"className":199},[184],[162,201,207],{"className":202,"style":179},[203,204,174,175,176,177,205,206],"border-2","border-primary","font-semibold","text-primary","Summarize (lossy)",[162,209,185],{"className":210},[184],[162,212,214],{"className":213,"style":179},[172,173,174,175,176,177,178],"Resume loop",[216,217,223],"figcaption",{"className":218},[219,184,220,221,222],"text-xs","mt-3","text-center","italic","Mask first — it's reversible. Only fall back to summarization (amber) when the transcript still doesn't fit; that step is lossy and irreversible.",[225,226],"hr",{},[228,229,231],"h2",{"id":230},"_81-the-two-ways-to-shrink-a-transcript","8.1 The Two Ways to Shrink a Transcript",[112,233,234,237,238,241,242,246,247,250],{},[142,235,236],{},"Masking"," replaces the content of a tool result with a placeholder, while leaving the tool ",[115,239,240],{},"call"," intact. After masking, the transcript says \"I called ",[243,244,245],"code",{},"read_file"," with path=\u002Fetc\u002Fpasswd\" but the content of the result is now ",[243,248,249],{},"[content elided; call id c-47; 8,143 tokens]",". If the agent needs the data again, it re-runs the tool. The agent's memory of what it did is perfect; what it saw has been paged out.",[112,252,253,256],{},[142,254,255],{},"Summarization"," replaces a stretch of the transcript with a condensed natural-language summary. \"Between turns 5 and 15, the agent read three configuration files, found a port conflict, and settled on using port 8081.\" The actual turns are gone. The summary is lossy — some detail the agent might have wanted is now unreachable — but the token reduction is much larger than masking alone can achieve.",[112,258,259,260,263],{},"The first-order rule: ",[142,261,262],{},"mask before you summarize",". Masking is reversible (re-run the tool) and precise (you know exactly what was dropped). Summarization is lossy and irreversible. A good policy tries masking first, checks whether the resulting transcript fits, and only falls back to summarization when it doesn't.",[112,265,266,267,270,271,278],{},"A complementary rule: ",[142,268,269],{},"never summarize the tool-call record",". If the agent sent an email on turn 4, the transcript must preserve the fact that the email was sent. Otherwise, the agent re-reads its summary (\"discussed notification with user\") and re-sends the email on turn 20. ",[272,273,277],"a",{"href":274,"rel":275},"https:\u002F\u002Fblog.jetbrains.com\u002Fresearch\u002F2025\u002F12\u002Fefficient-context-management\u002F",[276],"nofollow","JetBrains Research's Dec 2025 \"Smarter Context Management\" study"," found that artifact tracking — which files were modified, which calls succeeded — scores 2.19–2.45\u002F5 across every compaction method they tested; none reliably preserve procedural state. The mitigation is to structure the compactor so it can never touch the tool-call event log.",[225,280],{},[228,282,284],{"id":283},"_82-what-masking-looks-like","8.2 What Masking Looks Like",[112,286,287,288,291,292,295,296,299],{},"Every tool result in the transcript is a ",[243,289,290],{},"ToolResult"," block inside a ",[243,293,294],{},"user","-role message. Masking replaces the ",[243,297,298],{},"content"," field with a placeholder:",[301,302,307],"pre",{"className":303,"code":304,"language":305,"meta":306,"style":306},"language-python shiki shiki-themes material-theme-lighter github-light github-dark","# src\u002Fharness\u002Fcontext\u002Fmasking.py\nfrom __future__ import annotations\n\nfrom ..messages import Message, ToolResult, Transcript\n\n\nMASK_TEMPLATE = \"[tool_result elided; call_id={call_id}; original_tokens={tokens}]\"\n\n\ndef mask_older_results(\n    transcript: Transcript,\n    keep_recent: int,\n    encoder,\n) -> int:\n    \"\"\"Replace tool-result content in all but the most recent `keep_recent` turns.\n\n    Returns the number of tokens freed.\n    \"\"\"\n    results: list[tuple[int, int, ToolResult]] = []\n    for mi, message in enumerate(transcript.messages):\n        for bi, block in enumerate(message.blocks):\n            if isinstance(block, ToolResult):\n                results.append((mi, bi, block))\n\n    if len(results) \u003C= keep_recent:\n        return 0\n\n    # mask everything except the last `keep_recent`\n    to_mask = results[:-keep_recent]\n    freed = 0\n    for mi, bi, block in to_mask:\n        if block.content.startswith(\"[tool_result elided\"):\n            continue  # already masked\n        tokens = len(encoder.encode(block.content))\n        new_content = MASK_TEMPLATE.format(call_id=block.call_id, tokens=tokens)\n        new_block = ToolResult(\n            call_id=block.call_id,\n            content=new_content,\n            is_error=block.is_error,\n        )\n        new_blocks = list(transcript.messages[mi].blocks)\n        new_blocks[bi] = new_block\n        transcript.messages[mi] = Message(\n            role=transcript.messages[mi].role,\n            blocks=new_blocks,\n            created_at=transcript.messages[mi].created_at,\n            id=transcript.messages[mi].id,\n        )\n        freed += tokens - len(encoder.encode(new_content))\n    return freed\n","python","",[243,308,309,318,336,343,372,377,382,416,421,426,440,456,470,478,492,503,508,514,520,558,596,626,646,675,680,704,713,718,724,748,758,780,809,818,848,891,903,919,932,949,955,984,1002,1024,1049,1062,1087,1112,1117,1147],{"__ignoreMap":306},[310,311,314],"span",{"class":312,"line":313},"line",1,[310,315,317],{"class":316},"sutJx","# src\u002Fharness\u002Fcontext\u002Fmasking.py\n",[310,319,321,325,329,332],{"class":312,"line":320},2,[310,322,324],{"class":323},"sVHd0","from",[310,326,328],{"class":327},"s_hVV"," __future__",[310,330,331],{"class":323}," import",[310,333,335],{"class":334},"su5hD"," annotations\n",[310,337,339],{"class":312,"line":338},3,[310,340,342],{"emptyLinePlaceholder":341},true,"\n",[310,344,346,348,352,355,358,361,364,367,369],{"class":312,"line":345},4,[310,347,324],{"class":323},[310,349,351],{"class":350},"sP7_E"," ..",[310,353,354],{"class":334},"messages ",[310,356,357],{"class":323},"import",[310,359,360],{"class":334}," Message",[310,362,363],{"class":350},",",[310,365,366],{"class":334}," ToolResult",[310,368,363],{"class":350},[310,370,371],{"class":334}," Transcript\n",[310,373,375],{"class":312,"line":374},5,[310,376,342],{"emptyLinePlaceholder":341},[310,378,380],{"class":312,"line":379},6,[310,381,342],{"emptyLinePlaceholder":341},[310,383,385,388,392,396,400,404,407,410,413],{"class":312,"line":384},7,[310,386,387],{"class":327},"MASK_TEMPLATE",[310,389,391],{"class":390},"smGrS"," =",[310,393,395],{"class":394},"sjJ54"," \"",[310,397,399],{"class":398},"s_sjI","[tool_result elided; call_id=",[310,401,403],{"class":402},"srdBf","{call_id}",[310,405,406],{"class":398},"; original_tokens=",[310,408,409],{"class":402},"{tokens}",[310,411,412],{"class":398},"]",[310,414,415],{"class":394},"\"\n",[310,417,419],{"class":312,"line":418},8,[310,420,342],{"emptyLinePlaceholder":341},[310,422,424],{"class":312,"line":423},9,[310,425,342],{"emptyLinePlaceholder":341},[310,427,429,433,437],{"class":312,"line":428},10,[310,430,432],{"class":431},"sbsja","def",[310,434,436],{"class":435},"sGLFI"," mask_older_results",[310,438,439],{"class":350},"(\n",[310,441,443,447,450,453],{"class":312,"line":442},11,[310,444,446],{"class":445},"sFwrP","    transcript",[310,448,449],{"class":350},":",[310,451,452],{"class":334}," Transcript",[310,454,455],{"class":350},",\n",[310,457,459,462,464,468],{"class":312,"line":458},12,[310,460,461],{"class":445},"    keep_recent",[310,463,449],{"class":350},[310,465,467],{"class":466},"sZMiF"," int",[310,469,455],{"class":350},[310,471,473,476],{"class":312,"line":472},13,[310,474,475],{"class":445},"    encoder",[310,477,455],{"class":350},[310,479,481,484,487,489],{"class":312,"line":480},14,[310,482,483],{"class":350},")",[310,485,486],{"class":350}," ->",[310,488,467],{"class":466},[310,490,491],{"class":350},":\n",[310,493,495,499],{"class":312,"line":494},15,[310,496,498],{"class":497},"s2W-s","    \"\"\"",[310,500,502],{"class":501},"sithA","Replace tool-result content in all but the most recent `keep_recent` turns.\n",[310,504,506],{"class":312,"line":505},16,[310,507,342],{"emptyLinePlaceholder":341},[310,509,511],{"class":312,"line":510},17,[310,512,513],{"class":501},"    Returns the number of tokens freed.\n",[310,515,517],{"class":312,"line":516},18,[310,518,519],{"class":497},"    \"\"\"\n",[310,521,523,526,528,531,534,537,539,542,544,546,548,550,553,555],{"class":312,"line":522},19,[310,524,525],{"class":334},"    results",[310,527,449],{"class":350},[310,529,530],{"class":334}," list",[310,532,533],{"class":350},"[",[310,535,536],{"class":334},"tuple",[310,538,533],{"class":350},[310,540,541],{"class":466},"int",[310,543,363],{"class":350},[310,545,467],{"class":466},[310,547,363],{"class":350},[310,549,366],{"class":334},[310,551,552],{"class":350},"]]",[310,554,391],{"class":390},[310,556,557],{"class":350}," []\n",[310,559,561,564,567,569,572,575,579,582,586,589,593],{"class":312,"line":560},20,[310,562,563],{"class":323},"    for",[310,565,566],{"class":334}," mi",[310,568,363],{"class":350},[310,570,571],{"class":334}," message ",[310,573,574],{"class":323},"in",[310,576,578],{"class":577},"sptTA"," enumerate",[310,580,581],{"class":350},"(",[310,583,585],{"class":584},"slqww","transcript",[310,587,588],{"class":350},".",[310,590,592],{"class":591},"skxfh","messages",[310,594,595],{"class":350},"):\n",[310,597,599,602,605,607,610,612,614,616,619,621,624],{"class":312,"line":598},21,[310,600,601],{"class":323},"        for",[310,603,604],{"class":334}," bi",[310,606,363],{"class":350},[310,608,609],{"class":334}," block ",[310,611,574],{"class":323},[310,613,578],{"class":577},[310,615,581],{"class":350},[310,617,618],{"class":584},"message",[310,620,588],{"class":350},[310,622,623],{"class":591},"blocks",[310,625,595],{"class":350},[310,627,629,632,635,637,640,642,644],{"class":312,"line":628},22,[310,630,631],{"class":323},"            if",[310,633,634],{"class":577}," isinstance",[310,636,581],{"class":350},[310,638,639],{"class":584},"block",[310,641,363],{"class":350},[310,643,366],{"class":584},[310,645,595],{"class":350},[310,647,649,652,654,657,660,663,665,667,669,672],{"class":312,"line":648},23,[310,650,651],{"class":334},"                results",[310,653,588],{"class":350},[310,655,656],{"class":584},"append",[310,658,659],{"class":350},"((",[310,661,662],{"class":584},"mi",[310,664,363],{"class":350},[310,666,604],{"class":584},[310,668,363],{"class":350},[310,670,671],{"class":584}," block",[310,673,674],{"class":350},"))\n",[310,676,678],{"class":312,"line":677},24,[310,679,342],{"emptyLinePlaceholder":341},[310,681,683,686,689,691,694,696,699,702],{"class":312,"line":682},25,[310,684,685],{"class":323},"    if",[310,687,688],{"class":577}," len",[310,690,581],{"class":350},[310,692,693],{"class":584},"results",[310,695,483],{"class":350},[310,697,698],{"class":390}," \u003C=",[310,700,701],{"class":334}," keep_recent",[310,703,491],{"class":350},[310,705,707,710],{"class":312,"line":706},26,[310,708,709],{"class":323},"        return",[310,711,712],{"class":402}," 0\n",[310,714,716],{"class":312,"line":715},27,[310,717,342],{"emptyLinePlaceholder":341},[310,719,721],{"class":312,"line":720},28,[310,722,723],{"class":316},"    # mask everything except the last `keep_recent`\n",[310,725,727,730,733,736,739,742,745],{"class":312,"line":726},29,[310,728,729],{"class":334},"    to_mask ",[310,731,732],{"class":390},"=",[310,734,735],{"class":334}," results",[310,737,738],{"class":350},"[:",[310,740,741],{"class":390},"-",[310,743,744],{"class":334},"keep_recent",[310,746,747],{"class":350},"]\n",[310,749,751,754,756],{"class":312,"line":750},30,[310,752,753],{"class":334},"    freed ",[310,755,732],{"class":390},[310,757,712],{"class":402},[310,759,761,763,765,767,769,771,773,775,778],{"class":312,"line":760},31,[310,762,563],{"class":323},[310,764,566],{"class":334},[310,766,363],{"class":350},[310,768,604],{"class":334},[310,770,363],{"class":350},[310,772,609],{"class":334},[310,774,574],{"class":323},[310,776,777],{"class":334}," to_mask",[310,779,491],{"class":350},[310,781,783,786,788,790,792,794,797,799,802,805,807],{"class":312,"line":782},32,[310,784,785],{"class":323},"        if",[310,787,671],{"class":334},[310,789,588],{"class":350},[310,791,298],{"class":591},[310,793,588],{"class":350},[310,795,796],{"class":584},"startswith",[310,798,581],{"class":350},[310,800,801],{"class":394},"\"",[310,803,804],{"class":398},"[tool_result elided",[310,806,801],{"class":394},[310,808,595],{"class":350},[310,810,812,815],{"class":312,"line":811},33,[310,813,814],{"class":323},"            continue",[310,816,817],{"class":316},"  # already masked\n",[310,819,821,824,826,828,830,833,835,838,840,842,844,846],{"class":312,"line":820},34,[310,822,823],{"class":334},"        tokens ",[310,825,732],{"class":390},[310,827,688],{"class":577},[310,829,581],{"class":350},[310,831,832],{"class":584},"encoder",[310,834,588],{"class":350},[310,836,837],{"class":584},"encode",[310,839,581],{"class":350},[310,841,639],{"class":584},[310,843,588],{"class":350},[310,845,298],{"class":591},[310,847,674],{"class":350},[310,849,851,854,856,859,861,864,866,870,872,874,876,878,880,883,885,888],{"class":312,"line":850},35,[310,852,853],{"class":334},"        new_content ",[310,855,732],{"class":390},[310,857,858],{"class":327}," MASK_TEMPLATE",[310,860,588],{"class":350},[310,862,863],{"class":584},"format",[310,865,581],{"class":350},[310,867,869],{"class":868},"s99_P","call_id",[310,871,732],{"class":390},[310,873,639],{"class":584},[310,875,588],{"class":350},[310,877,869],{"class":591},[310,879,363],{"class":350},[310,881,882],{"class":868}," tokens",[310,884,732],{"class":390},[310,886,887],{"class":584},"tokens",[310,889,890],{"class":350},")\n",[310,892,894,897,899,901],{"class":312,"line":893},36,[310,895,896],{"class":334},"        new_block ",[310,898,732],{"class":390},[310,900,366],{"class":584},[310,902,439],{"class":350},[310,904,906,909,911,913,915,917],{"class":312,"line":905},37,[310,907,908],{"class":868},"            call_id",[310,910,732],{"class":390},[310,912,639],{"class":584},[310,914,588],{"class":350},[310,916,869],{"class":591},[310,918,455],{"class":350},[310,920,922,925,927,930],{"class":312,"line":921},38,[310,923,924],{"class":868},"            content",[310,926,732],{"class":390},[310,928,929],{"class":584},"new_content",[310,931,455],{"class":350},[310,933,935,938,940,942,944,947],{"class":312,"line":934},39,[310,936,937],{"class":868},"            is_error",[310,939,732],{"class":390},[310,941,639],{"class":584},[310,943,588],{"class":350},[310,945,946],{"class":591},"is_error",[310,948,455],{"class":350},[310,950,952],{"class":312,"line":951},40,[310,953,954],{"class":350},"        )\n",[310,956,958,961,963,965,967,969,971,973,975,977,980,982],{"class":312,"line":957},41,[310,959,960],{"class":334},"        new_blocks ",[310,962,732],{"class":390},[310,964,530],{"class":466},[310,966,581],{"class":350},[310,968,585],{"class":584},[310,970,588],{"class":350},[310,972,592],{"class":591},[310,974,533],{"class":350},[310,976,662],{"class":591},[310,978,979],{"class":350},"].",[310,981,623],{"class":591},[310,983,890],{"class":350},[310,985,987,990,992,995,997,999],{"class":312,"line":986},42,[310,988,989],{"class":334},"        new_blocks",[310,991,533],{"class":350},[310,993,994],{"class":334},"bi",[310,996,412],{"class":350},[310,998,391],{"class":390},[310,1000,1001],{"class":334}," new_block\n",[310,1003,1005,1008,1010,1012,1014,1016,1018,1020,1022],{"class":312,"line":1004},43,[310,1006,1007],{"class":334},"        transcript",[310,1009,588],{"class":350},[310,1011,592],{"class":591},[310,1013,533],{"class":350},[310,1015,662],{"class":591},[310,1017,412],{"class":350},[310,1019,391],{"class":390},[310,1021,360],{"class":584},[310,1023,439],{"class":350},[310,1025,1027,1030,1032,1034,1036,1038,1040,1042,1044,1047],{"class":312,"line":1026},44,[310,1028,1029],{"class":868},"            role",[310,1031,732],{"class":390},[310,1033,585],{"class":584},[310,1035,588],{"class":350},[310,1037,592],{"class":591},[310,1039,533],{"class":350},[310,1041,662],{"class":591},[310,1043,979],{"class":350},[310,1045,1046],{"class":591},"role",[310,1048,455],{"class":350},[310,1050,1052,1055,1057,1060],{"class":312,"line":1051},45,[310,1053,1054],{"class":868},"            blocks",[310,1056,732],{"class":390},[310,1058,1059],{"class":584},"new_blocks",[310,1061,455],{"class":350},[310,1063,1065,1068,1070,1072,1074,1076,1078,1080,1082,1085],{"class":312,"line":1064},46,[310,1066,1067],{"class":868},"            created_at",[310,1069,732],{"class":390},[310,1071,585],{"class":584},[310,1073,588],{"class":350},[310,1075,592],{"class":591},[310,1077,533],{"class":350},[310,1079,662],{"class":591},[310,1081,979],{"class":350},[310,1083,1084],{"class":591},"created_at",[310,1086,455],{"class":350},[310,1088,1090,1093,1095,1097,1099,1101,1103,1105,1107,1110],{"class":312,"line":1089},47,[310,1091,1092],{"class":868},"            id",[310,1094,732],{"class":390},[310,1096,585],{"class":584},[310,1098,588],{"class":350},[310,1100,592],{"class":591},[310,1102,533],{"class":350},[310,1104,662],{"class":591},[310,1106,979],{"class":350},[310,1108,1109],{"class":591},"id",[310,1111,455],{"class":350},[310,1113,1115],{"class":312,"line":1114},48,[310,1116,954],{"class":350},[310,1118,1120,1123,1126,1129,1131,1133,1135,1137,1139,1141,1143,1145],{"class":312,"line":1119},49,[310,1121,1122],{"class":334},"        freed ",[310,1124,1125],{"class":390},"+=",[310,1127,1128],{"class":334}," tokens ",[310,1130,741],{"class":390},[310,1132,688],{"class":577},[310,1134,581],{"class":350},[310,1136,832],{"class":584},[310,1138,588],{"class":350},[310,1140,837],{"class":584},[310,1142,581],{"class":350},[310,1144,929],{"class":584},[310,1146,674],{"class":350},[310,1148,1150,1153],{"class":312,"line":1149},50,[310,1151,1152],{"class":323},"    return",[310,1154,1155],{"class":334}," freed\n",[112,1157,1158,1159,1161,1162,1165,1166,1168],{},"The algorithm is simple: walk the transcript, find every tool result, and mask all but the ",[243,1160,744],{}," most recent. Tool ",[115,1163,1164],{},"calls"," are untouched. Tool ",[115,1167,693],{}," shrink to a pointer.",[112,1170,1171],{},"A few small but important details.",[112,1173,1174,1177,1178,1180],{},[142,1175,1176],{},"Idempotent."," A result that's already masked (starts with ",[243,1179,804],{},") isn't re-masked. This matters because compaction may run multiple times in a session.",[112,1182,1183,1186],{},[142,1184,1185],{},"Returns tokens freed."," The caller can decide whether to stop (enough was freed) or escalate (not enough; try summarization).",[112,1188,1189,1192,1193,1196,1197,1199],{},[142,1190,1191],{},"Messages are rebuilt, not mutated."," ",[243,1194,1195],{},"Message"," is a dataclass, but its ",[243,1198,623],{}," list is still a list; we rebuild the whole message to keep immutability discipline consistent with Chapter 3's frozen blocks.",[225,1201],{},[228,1203,1205],{"id":1204},"_83-what-summarization-looks-like","8.3 What Summarization Looks Like",[112,1207,1208],{},"When masking alone can't bring the transcript under the red threshold, we summarize an older prefix.",[112,1210,1211,1212,1215],{},"The tricky part is deciding ",[115,1213,1214],{},"where"," to cut. Three choices, each defensible:",[1217,1218,1219,1225,1231],"ul",{},[139,1220,1221,1224],{},[142,1222,1223],{},"Everything before turn N."," Simple. Risks cutting off mid-thought.",[139,1226,1227,1230],{},[142,1228,1229],{},"The first K% of the transcript by tokens."," Reasonable. May summarize the user's initial goal, which is usually the one thing you most want preserved verbatim.",[139,1232,1233,1240],{},[142,1234,1235,1236,1239],{},"The first K% ",[115,1237,1238],{},"after"," the first user message."," The user's initial goal stays in context; older intermediate steps get summarized.",[112,1242,1243],{},"We use option three. The initial user message is the anchor — the thing the agent is ultimately trying to satisfy — and it should stay verbatim as long as possible.",[301,1245,1247],{"className":303,"code":1246,"language":305,"meta":306,"style":306},"# src\u002Fharness\u002Fcontext\u002Fsummarizer.py\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass\n\nfrom ..messages import Message, TextBlock, ToolCall, ToolResult, Transcript\nfrom ..providers.base import Provider\n\n\nSUMMARIZER_SYSTEM = \"\"\"\\\nYou are a conversation summarizer for an AI agent session.\n\nYour job is to condense the provided conversation into a brief summary that\npreserves:\n- Key facts discovered (files read, values computed, decisions made).\n- Open questions and in-progress subtasks.\n- Which tools have been called and what they returned, in sequence.\n- Any user-expressed preferences or constraints.\n\nDO NOT:\n- Invent information not present in the transcript.\n- Omit tool calls — list each tool call with a one-line outcome.\n- Exceed 1000 words.\n\nReturn plain prose. The summary replaces the original turns in the agent's\nmemory, so it must be accurate and complete.\n\"\"\"\n\n\n@dataclass\nclass SummarizationResult:\n    summary_text: str\n    turns_replaced: int\n    input_tokens: int\n    output_tokens: int\n\n\nasync def summarize_prefix(\n    transcript: Transcript,\n    provider: Provider,\n    keep_recent_turns: int,\n) -> SummarizationResult | None:\n    \"\"\"Summarize turns before the last `keep_recent_turns`, leaving the first\n    user message intact.\"\"\"\n    if len(transcript.messages) \u003C= keep_recent_turns + 1:\n        return None\n\n    first_user = transcript.messages[0]\n    prefix_end = len(transcript.messages) - keep_recent_turns\n    prefix_to_summarize = transcript.messages[1:prefix_end]\n    if not prefix_to_summarize:\n        return None\n\n    # Render the prefix as text the summarizer can read.\n    rendered_parts: list[str] = []\n    for m in prefix_to_summarize:\n        for block in m.blocks:\n            match block:\n                case TextBlock(text=t):\n                    rendered_parts.append(f\"[{m.role}] {t}\")\n                case ToolCall(name=n, args=a):\n                    rendered_parts.append(f\"[assistant→tool] {n}({a})\")\n                case ToolResult(content=c, is_error=err):\n                    prefix = \"[tool→error]\" if err else \"[tool→result]\"\n                    rendered_parts.append(f\"{prefix} {c}\")\n    rendered = \"\\n\".join(rendered_parts)\n\n    sub_transcript = Transcript(system=SUMMARIZER_SYSTEM)\n    sub_transcript.append(Message.user_text(\n        f\"Summarize this conversation.\\n\\n{rendered}\"\n    ))\n\n    response = await provider.acomplete(sub_transcript, tools=[])\n    summary_text = response.text or \"(empty summary)\"\n\n    # Replace the prefix with a single synthetic message.\n    summary_message = Message.user_text(\n        f\"[session summary — {len(prefix_to_summarize)} turns replaced]\\n\"\n        f\"{summary_text}\"\n    )\n    transcript.messages[1:prefix_end] = [summary_message]\n\n    return SummarizationResult(\n        summary_text=summary_text,\n        turns_replaced=len(prefix_to_summarize),\n        input_tokens=response.input_tokens,\n        output_tokens=response.output_tokens,\n    )\n",[243,1248,1249,1254,1264,1268,1280,1284,1314,1333,1337,1341,1355,1360,1364,1369,1374,1379,1384,1389,1394,1398,1403,1408,1413,1418,1422,1427,1432,1437,1441,1445,1454,1465,1475,1485,1494,1503,1507,1511,1524,1534,1546,1557,1574,1581,1588,1617,1624,1628,1649,1674,1699,1712,1719,1724,1730,1751,1765,1783,1793,1813,1857,1885,1920,1948,1979,2012,2039,2044,2065,2086,2107,2113,2118,2152,2177,2182,2188,2204,2233,2249,2255,2284,2289,2298,2310,2327,2345,2362],{"__ignoreMap":306},[310,1250,1251],{"class":312,"line":313},[310,1252,1253],{"class":316},"# src\u002Fharness\u002Fcontext\u002Fsummarizer.py\n",[310,1255,1256,1258,1260,1262],{"class":312,"line":320},[310,1257,324],{"class":323},[310,1259,328],{"class":327},[310,1261,331],{"class":323},[310,1263,335],{"class":334},[310,1265,1266],{"class":312,"line":338},[310,1267,342],{"emptyLinePlaceholder":341},[310,1269,1270,1272,1275,1277],{"class":312,"line":345},[310,1271,324],{"class":323},[310,1273,1274],{"class":334}," dataclasses ",[310,1276,357],{"class":323},[310,1278,1279],{"class":334}," dataclass\n",[310,1281,1282],{"class":312,"line":374},[310,1283,342],{"emptyLinePlaceholder":341},[310,1285,1286,1288,1290,1292,1294,1296,1298,1301,1303,1306,1308,1310,1312],{"class":312,"line":379},[310,1287,324],{"class":323},[310,1289,351],{"class":350},[310,1291,354],{"class":334},[310,1293,357],{"class":323},[310,1295,360],{"class":334},[310,1297,363],{"class":350},[310,1299,1300],{"class":334}," TextBlock",[310,1302,363],{"class":350},[310,1304,1305],{"class":334}," ToolCall",[310,1307,363],{"class":350},[310,1309,366],{"class":334},[310,1311,363],{"class":350},[310,1313,371],{"class":334},[310,1315,1316,1318,1320,1323,1325,1328,1330],{"class":312,"line":384},[310,1317,324],{"class":323},[310,1319,351],{"class":350},[310,1321,1322],{"class":334},"providers",[310,1324,588],{"class":350},[310,1326,1327],{"class":334},"base ",[310,1329,357],{"class":323},[310,1331,1332],{"class":334}," Provider\n",[310,1334,1335],{"class":312,"line":418},[310,1336,342],{"emptyLinePlaceholder":341},[310,1338,1339],{"class":312,"line":423},[310,1340,342],{"emptyLinePlaceholder":341},[310,1342,1343,1346,1348,1351],{"class":312,"line":428},[310,1344,1345],{"class":327},"SUMMARIZER_SYSTEM",[310,1347,391],{"class":390},[310,1349,1350],{"class":394}," \"\"\"",[310,1352,1354],{"class":1353},"s39Yj","\\\n",[310,1356,1357],{"class":312,"line":442},[310,1358,1359],{"class":398},"You are a conversation summarizer for an AI agent session.\n",[310,1361,1362],{"class":312,"line":458},[310,1363,342],{"emptyLinePlaceholder":341},[310,1365,1366],{"class":312,"line":472},[310,1367,1368],{"class":398},"Your job is to condense the provided conversation into a brief summary that\n",[310,1370,1371],{"class":312,"line":480},[310,1372,1373],{"class":398},"preserves:\n",[310,1375,1376],{"class":312,"line":494},[310,1377,1378],{"class":398},"- Key facts discovered (files read, values computed, decisions made).\n",[310,1380,1381],{"class":312,"line":505},[310,1382,1383],{"class":398},"- Open questions and in-progress subtasks.\n",[310,1385,1386],{"class":312,"line":510},[310,1387,1388],{"class":398},"- Which tools have been called and what they returned, in sequence.\n",[310,1390,1391],{"class":312,"line":516},[310,1392,1393],{"class":398},"- Any user-expressed preferences or constraints.\n",[310,1395,1396],{"class":312,"line":522},[310,1397,342],{"emptyLinePlaceholder":341},[310,1399,1400],{"class":312,"line":560},[310,1401,1402],{"class":398},"DO NOT:\n",[310,1404,1405],{"class":312,"line":598},[310,1406,1407],{"class":398},"- Invent information not present in the transcript.\n",[310,1409,1410],{"class":312,"line":628},[310,1411,1412],{"class":398},"- Omit tool calls — list each tool call with a one-line outcome.\n",[310,1414,1415],{"class":312,"line":648},[310,1416,1417],{"class":398},"- Exceed 1000 words.\n",[310,1419,1420],{"class":312,"line":677},[310,1421,342],{"emptyLinePlaceholder":341},[310,1423,1424],{"class":312,"line":682},[310,1425,1426],{"class":398},"Return plain prose. The summary replaces the original turns in the agent's\n",[310,1428,1429],{"class":312,"line":706},[310,1430,1431],{"class":398},"memory, so it must be accurate and complete.\n",[310,1433,1434],{"class":312,"line":715},[310,1435,1436],{"class":394},"\"\"\"\n",[310,1438,1439],{"class":312,"line":720},[310,1440,342],{"emptyLinePlaceholder":341},[310,1442,1443],{"class":312,"line":726},[310,1444,342],{"emptyLinePlaceholder":341},[310,1446,1447,1451],{"class":312,"line":750},[310,1448,1450],{"class":1449},"stp6e","@",[310,1452,1453],{"class":435},"dataclass\n",[310,1455,1456,1459,1463],{"class":312,"line":760},[310,1457,1458],{"class":431},"class",[310,1460,1462],{"class":1461},"sbgvK"," SummarizationResult",[310,1464,491],{"class":350},[310,1466,1467,1470,1472],{"class":312,"line":782},[310,1468,1469],{"class":334},"    summary_text",[310,1471,449],{"class":350},[310,1473,1474],{"class":466}," str\n",[310,1476,1477,1480,1482],{"class":312,"line":811},[310,1478,1479],{"class":334},"    turns_replaced",[310,1481,449],{"class":350},[310,1483,1484],{"class":466}," int\n",[310,1486,1487,1490,1492],{"class":312,"line":820},[310,1488,1489],{"class":334},"    input_tokens",[310,1491,449],{"class":350},[310,1493,1484],{"class":466},[310,1495,1496,1499,1501],{"class":312,"line":850},[310,1497,1498],{"class":334},"    output_tokens",[310,1500,449],{"class":350},[310,1502,1484],{"class":466},[310,1504,1505],{"class":312,"line":893},[310,1506,342],{"emptyLinePlaceholder":341},[310,1508,1509],{"class":312,"line":905},[310,1510,342],{"emptyLinePlaceholder":341},[310,1512,1513,1516,1519,1522],{"class":312,"line":921},[310,1514,1515],{"class":431},"async",[310,1517,1518],{"class":431}," def",[310,1520,1521],{"class":435}," summarize_prefix",[310,1523,439],{"class":350},[310,1525,1526,1528,1530,1532],{"class":312,"line":934},[310,1527,446],{"class":445},[310,1529,449],{"class":350},[310,1531,452],{"class":334},[310,1533,455],{"class":350},[310,1535,1536,1539,1541,1544],{"class":312,"line":951},[310,1537,1538],{"class":445},"    provider",[310,1540,449],{"class":350},[310,1542,1543],{"class":334}," Provider",[310,1545,455],{"class":350},[310,1547,1548,1551,1553,1555],{"class":312,"line":957},[310,1549,1550],{"class":445},"    keep_recent_turns",[310,1552,449],{"class":350},[310,1554,467],{"class":466},[310,1556,455],{"class":350},[310,1558,1559,1561,1563,1566,1569,1572],{"class":312,"line":986},[310,1560,483],{"class":350},[310,1562,486],{"class":350},[310,1564,1565],{"class":334}," SummarizationResult ",[310,1567,1568],{"class":390},"|",[310,1570,1571],{"class":1353}," None",[310,1573,491],{"class":350},[310,1575,1576,1578],{"class":312,"line":1004},[310,1577,498],{"class":497},[310,1579,1580],{"class":501},"Summarize turns before the last `keep_recent_turns`, leaving the first\n",[310,1582,1583,1586],{"class":312,"line":1026},[310,1584,1585],{"class":501},"    user message intact.",[310,1587,1436],{"class":497},[310,1589,1590,1592,1594,1596,1598,1600,1602,1604,1606,1609,1612,1615],{"class":312,"line":1051},[310,1591,685],{"class":323},[310,1593,688],{"class":577},[310,1595,581],{"class":350},[310,1597,585],{"class":584},[310,1599,588],{"class":350},[310,1601,592],{"class":591},[310,1603,483],{"class":350},[310,1605,698],{"class":390},[310,1607,1608],{"class":334}," keep_recent_turns ",[310,1610,1611],{"class":390},"+",[310,1613,1614],{"class":402}," 1",[310,1616,491],{"class":350},[310,1618,1619,1621],{"class":312,"line":1064},[310,1620,709],{"class":323},[310,1622,1623],{"class":1353}," None\n",[310,1625,1626],{"class":312,"line":1089},[310,1627,342],{"emptyLinePlaceholder":341},[310,1629,1630,1633,1635,1638,1640,1642,1644,1647],{"class":312,"line":1114},[310,1631,1632],{"class":334},"    first_user ",[310,1634,732],{"class":390},[310,1636,1637],{"class":334}," transcript",[310,1639,588],{"class":350},[310,1641,592],{"class":591},[310,1643,533],{"class":350},[310,1645,1646],{"class":402},"0",[310,1648,747],{"class":350},[310,1650,1651,1654,1656,1658,1660,1662,1664,1666,1668,1671],{"class":312,"line":1119},[310,1652,1653],{"class":334},"    prefix_end ",[310,1655,732],{"class":390},[310,1657,688],{"class":577},[310,1659,581],{"class":350},[310,1661,585],{"class":584},[310,1663,588],{"class":350},[310,1665,592],{"class":591},[310,1667,483],{"class":350},[310,1669,1670],{"class":390}," -",[310,1672,1673],{"class":334}," keep_recent_turns\n",[310,1675,1676,1679,1681,1683,1685,1687,1689,1692,1694,1697],{"class":312,"line":1149},[310,1677,1678],{"class":334},"    prefix_to_summarize ",[310,1680,732],{"class":390},[310,1682,1637],{"class":334},[310,1684,588],{"class":350},[310,1686,592],{"class":591},[310,1688,533],{"class":350},[310,1690,1691],{"class":402},"1",[310,1693,449],{"class":350},[310,1695,1696],{"class":591},"prefix_end",[310,1698,747],{"class":350},[310,1700,1702,1704,1707,1710],{"class":312,"line":1701},51,[310,1703,685],{"class":323},[310,1705,1706],{"class":390}," not",[310,1708,1709],{"class":334}," prefix_to_summarize",[310,1711,491],{"class":350},[310,1713,1715,1717],{"class":312,"line":1714},52,[310,1716,709],{"class":323},[310,1718,1623],{"class":1353},[310,1720,1722],{"class":312,"line":1721},53,[310,1723,342],{"emptyLinePlaceholder":341},[310,1725,1727],{"class":312,"line":1726},54,[310,1728,1729],{"class":316},"    # Render the prefix as text the summarizer can read.\n",[310,1731,1733,1736,1738,1740,1742,1745,1747,1749],{"class":312,"line":1732},55,[310,1734,1735],{"class":334},"    rendered_parts",[310,1737,449],{"class":350},[310,1739,530],{"class":334},[310,1741,533],{"class":350},[310,1743,1744],{"class":466},"str",[310,1746,412],{"class":350},[310,1748,391],{"class":390},[310,1750,557],{"class":350},[310,1752,1754,1756,1759,1761,1763],{"class":312,"line":1753},56,[310,1755,563],{"class":323},[310,1757,1758],{"class":334}," m ",[310,1760,574],{"class":323},[310,1762,1709],{"class":334},[310,1764,491],{"class":350},[310,1766,1768,1770,1772,1774,1777,1779,1781],{"class":312,"line":1767},57,[310,1769,601],{"class":323},[310,1771,609],{"class":334},[310,1773,574],{"class":323},[310,1775,1776],{"class":334}," m",[310,1778,588],{"class":350},[310,1780,623],{"class":591},[310,1782,491],{"class":350},[310,1784,1786,1789,1791],{"class":312,"line":1785},58,[310,1787,1788],{"class":323},"            match",[310,1790,671],{"class":334},[310,1792,491],{"class":350},[310,1794,1796,1799,1801,1803,1806,1808,1811],{"class":312,"line":1795},59,[310,1797,1798],{"class":323},"                case",[310,1800,1300],{"class":584},[310,1802,581],{"class":350},[310,1804,1805],{"class":868},"text",[310,1807,732],{"class":390},[310,1809,1810],{"class":584},"t",[310,1812,595],{"class":350},[310,1814,1816,1819,1821,1823,1825,1828,1831,1834,1837,1839,1841,1844,1847,1849,1851,1853,1855],{"class":312,"line":1815},60,[310,1817,1818],{"class":334},"                    rendered_parts",[310,1820,588],{"class":350},[310,1822,656],{"class":584},[310,1824,581],{"class":350},[310,1826,1827],{"class":431},"f",[310,1829,1830],{"class":398},"\"[",[310,1832,1833],{"class":402},"{",[310,1835,1836],{"class":584},"m",[310,1838,588],{"class":350},[310,1840,1046],{"class":591},[310,1842,1843],{"class":402},"}",[310,1845,1846],{"class":398},"] ",[310,1848,1833],{"class":402},[310,1850,1810],{"class":584},[310,1852,1843],{"class":402},[310,1854,801],{"class":398},[310,1856,890],{"class":350},[310,1858,1860,1862,1864,1866,1869,1871,1874,1876,1879,1881,1883],{"class":312,"line":1859},61,[310,1861,1798],{"class":323},[310,1863,1305],{"class":584},[310,1865,581],{"class":350},[310,1867,1868],{"class":868},"name",[310,1870,732],{"class":390},[310,1872,1873],{"class":584},"n",[310,1875,363],{"class":350},[310,1877,1878],{"class":868}," args",[310,1880,732],{"class":390},[310,1882,272],{"class":584},[310,1884,595],{"class":350},[310,1886,1888,1890,1892,1894,1896,1898,1901,1903,1905,1907,1909,1911,1913,1915,1918],{"class":312,"line":1887},62,[310,1889,1818],{"class":334},[310,1891,588],{"class":350},[310,1893,656],{"class":584},[310,1895,581],{"class":350},[310,1897,1827],{"class":431},[310,1899,1900],{"class":398},"\"[assistant→tool] ",[310,1902,1833],{"class":402},[310,1904,1873],{"class":584},[310,1906,1843],{"class":402},[310,1908,581],{"class":398},[310,1910,1833],{"class":402},[310,1912,272],{"class":584},[310,1914,1843],{"class":402},[310,1916,1917],{"class":398},")\"",[310,1919,890],{"class":350},[310,1921,1923,1925,1927,1929,1931,1933,1936,1938,1941,1943,1946],{"class":312,"line":1922},63,[310,1924,1798],{"class":323},[310,1926,366],{"class":584},[310,1928,581],{"class":350},[310,1930,298],{"class":868},[310,1932,732],{"class":390},[310,1934,1935],{"class":584},"c",[310,1937,363],{"class":350},[310,1939,1940],{"class":868}," is_error",[310,1942,732],{"class":390},[310,1944,1945],{"class":584},"err",[310,1947,595],{"class":350},[310,1949,1951,1954,1956,1958,1961,1963,1966,1969,1972,1974,1977],{"class":312,"line":1950},64,[310,1952,1953],{"class":334},"                    prefix ",[310,1955,732],{"class":390},[310,1957,395],{"class":394},[310,1959,1960],{"class":398},"[tool→error]",[310,1962,801],{"class":394},[310,1964,1965],{"class":323}," if",[310,1967,1968],{"class":334}," err ",[310,1970,1971],{"class":323},"else",[310,1973,395],{"class":394},[310,1975,1976],{"class":398},"[tool→result]",[310,1978,415],{"class":394},[310,1980,1982,1984,1986,1988,1990,1992,1994,1996,1999,2001,2004,2006,2008,2010],{"class":312,"line":1981},65,[310,1983,1818],{"class":334},[310,1985,588],{"class":350},[310,1987,656],{"class":584},[310,1989,581],{"class":350},[310,1991,1827],{"class":431},[310,1993,801],{"class":398},[310,1995,1833],{"class":402},[310,1997,1998],{"class":584},"prefix",[310,2000,1843],{"class":402},[310,2002,2003],{"class":402}," {",[310,2005,1935],{"class":584},[310,2007,1843],{"class":402},[310,2009,801],{"class":398},[310,2011,890],{"class":350},[310,2013,2015,2018,2020,2022,2025,2027,2029,2032,2034,2037],{"class":312,"line":2014},66,[310,2016,2017],{"class":334},"    rendered ",[310,2019,732],{"class":390},[310,2021,395],{"class":394},[310,2023,2024],{"class":327},"\\n",[310,2026,801],{"class":394},[310,2028,588],{"class":350},[310,2030,2031],{"class":584},"join",[310,2033,581],{"class":350},[310,2035,2036],{"class":584},"rendered_parts",[310,2038,890],{"class":350},[310,2040,2042],{"class":312,"line":2041},67,[310,2043,342],{"emptyLinePlaceholder":341},[310,2045,2047,2050,2052,2054,2056,2059,2061,2063],{"class":312,"line":2046},68,[310,2048,2049],{"class":334},"    sub_transcript ",[310,2051,732],{"class":390},[310,2053,452],{"class":584},[310,2055,581],{"class":350},[310,2057,2058],{"class":868},"system",[310,2060,732],{"class":390},[310,2062,1345],{"class":577},[310,2064,890],{"class":350},[310,2066,2068,2071,2073,2075,2077,2079,2081,2084],{"class":312,"line":2067},69,[310,2069,2070],{"class":334},"    sub_transcript",[310,2072,588],{"class":350},[310,2074,656],{"class":584},[310,2076,581],{"class":350},[310,2078,1195],{"class":584},[310,2080,588],{"class":350},[310,2082,2083],{"class":584},"user_text",[310,2085,439],{"class":350},[310,2087,2089,2092,2095,2098,2100,2103,2105],{"class":312,"line":2088},70,[310,2090,2091],{"class":431},"        f",[310,2093,2094],{"class":398},"\"Summarize this conversation.",[310,2096,2097],{"class":327},"\\n\\n",[310,2099,1833],{"class":402},[310,2101,2102],{"class":584},"rendered",[310,2104,1843],{"class":402},[310,2106,415],{"class":398},[310,2108,2110],{"class":312,"line":2109},71,[310,2111,2112],{"class":350},"    ))\n",[310,2114,2116],{"class":312,"line":2115},72,[310,2117,342],{"emptyLinePlaceholder":341},[310,2119,2121,2124,2126,2129,2132,2134,2137,2139,2142,2144,2147,2149],{"class":312,"line":2120},73,[310,2122,2123],{"class":334},"    response ",[310,2125,732],{"class":390},[310,2127,2128],{"class":323}," await",[310,2130,2131],{"class":334}," provider",[310,2133,588],{"class":350},[310,2135,2136],{"class":584},"acomplete",[310,2138,581],{"class":350},[310,2140,2141],{"class":584},"sub_transcript",[310,2143,363],{"class":350},[310,2145,2146],{"class":868}," tools",[310,2148,732],{"class":390},[310,2150,2151],{"class":350},"[])\n",[310,2153,2155,2158,2160,2163,2165,2167,2170,2172,2175],{"class":312,"line":2154},74,[310,2156,2157],{"class":334},"    summary_text ",[310,2159,732],{"class":390},[310,2161,2162],{"class":334}," response",[310,2164,588],{"class":350},[310,2166,1805],{"class":591},[310,2168,2169],{"class":390}," or",[310,2171,395],{"class":394},[310,2173,2174],{"class":398},"(empty summary)",[310,2176,415],{"class":394},[310,2178,2180],{"class":312,"line":2179},75,[310,2181,342],{"emptyLinePlaceholder":341},[310,2183,2185],{"class":312,"line":2184},76,[310,2186,2187],{"class":316},"    # Replace the prefix with a single synthetic message.\n",[310,2189,2191,2194,2196,2198,2200,2202],{"class":312,"line":2190},77,[310,2192,2193],{"class":334},"    summary_message ",[310,2195,732],{"class":390},[310,2197,360],{"class":334},[310,2199,588],{"class":350},[310,2201,2083],{"class":584},[310,2203,439],{"class":350},[310,2205,2207,2209,2212,2214,2217,2219,2222,2224,2226,2229,2231],{"class":312,"line":2206},78,[310,2208,2091],{"class":431},[310,2210,2211],{"class":398},"\"[session summary — ",[310,2213,1833],{"class":402},[310,2215,2216],{"class":577},"len",[310,2218,581],{"class":350},[310,2220,2221],{"class":584},"prefix_to_summarize",[310,2223,483],{"class":350},[310,2225,1843],{"class":402},[310,2227,2228],{"class":398}," turns replaced]",[310,2230,2024],{"class":327},[310,2232,415],{"class":398},[310,2234,2236,2238,2240,2242,2245,2247],{"class":312,"line":2235},79,[310,2237,2091],{"class":431},[310,2239,801],{"class":398},[310,2241,1833],{"class":402},[310,2243,2244],{"class":584},"summary_text",[310,2246,1843],{"class":402},[310,2248,415],{"class":398},[310,2250,2252],{"class":312,"line":2251},80,[310,2253,2254],{"class":350},"    )\n",[310,2256,2258,2260,2262,2264,2266,2268,2270,2272,2274,2276,2279,2282],{"class":312,"line":2257},81,[310,2259,446],{"class":334},[310,2261,588],{"class":350},[310,2263,592],{"class":591},[310,2265,533],{"class":350},[310,2267,1691],{"class":402},[310,2269,449],{"class":350},[310,2271,1696],{"class":591},[310,2273,412],{"class":350},[310,2275,391],{"class":390},[310,2277,2278],{"class":350}," [",[310,2280,2281],{"class":334},"summary_message",[310,2283,747],{"class":350},[310,2285,2287],{"class":312,"line":2286},82,[310,2288,342],{"emptyLinePlaceholder":341},[310,2290,2292,2294,2296],{"class":312,"line":2291},83,[310,2293,1152],{"class":323},[310,2295,1462],{"class":584},[310,2297,439],{"class":350},[310,2299,2301,2304,2306,2308],{"class":312,"line":2300},84,[310,2302,2303],{"class":868},"        summary_text",[310,2305,732],{"class":390},[310,2307,2244],{"class":584},[310,2309,455],{"class":350},[310,2311,2313,2316,2318,2320,2322,2324],{"class":312,"line":2312},85,[310,2314,2315],{"class":868},"        turns_replaced",[310,2317,732],{"class":390},[310,2319,2216],{"class":577},[310,2321,581],{"class":350},[310,2323,2221],{"class":584},[310,2325,2326],{"class":350},"),\n",[310,2328,2330,2333,2335,2338,2340,2343],{"class":312,"line":2329},86,[310,2331,2332],{"class":868},"        input_tokens",[310,2334,732],{"class":390},[310,2336,2337],{"class":584},"response",[310,2339,588],{"class":350},[310,2341,2342],{"class":591},"input_tokens",[310,2344,455],{"class":350},[310,2346,2348,2351,2353,2355,2357,2360],{"class":312,"line":2347},87,[310,2349,2350],{"class":868},"        output_tokens",[310,2352,732],{"class":390},[310,2354,2337],{"class":584},[310,2356,588],{"class":350},[310,2358,2359],{"class":591},"output_tokens",[310,2361,455],{"class":350},[310,2363,2365],{"class":312,"line":2364},88,[310,2366,2254],{"class":350},[112,2368,2369],{},"Four things to notice.",[112,2371,2372,2375],{},[142,2373,2374],{},"The summarizer is a separate LLM call, not a model switch."," It goes through the same provider; the caller can pass a cheaper model if they want (a Haiku-grade summarizer is usually fine for Sonnet-grade agents). Chapter 20 integrates model routing; for now we use whatever provider we're handed.",[112,2377,2378,2381],{},[142,2379,2380],{},"The summary replaces the prefix in place."," The transcript's first user message survives; the summary becomes the second message; the recent turns remain at the end. The message list stays linear.",[112,2383,2384,1192,2387,2390],{},[142,2385,2386],{},"Tool calls are rendered explicitly.",[243,2388,2389],{},"[assistant→tool] calc({\"expression\": \"1+1\"})"," is in the summarizer's input. The summarizer prompt tells it to preserve tool calls one-per-line in the summary. This is how we avoid the \"lost tool history\" failure mode — not by protecting the data structure, but by instructing the summarizer to enumerate it.",[112,2392,2393,2396],{},[142,2394,2395],{},"The system prompt for the summarizer is deliberate."," It names what to preserve, what to omit, and that it must not invent. Summarizers that hallucinate are worse than no summarizer at all; a strong system prompt plus a smaller model is usually enough.",[225,2398],{},[228,2400,2402],{"id":2401},"_84-the-compactor","8.4 The Compactor",[112,2404,2405],{},"The compactor is the orchestrator: it runs masking first, checks the accountant, and escalates to summarization if needed.",[301,2407,2409],{"className":303,"code":2408,"language":305,"meta":306,"style":306},"# src\u002Fharness\u002Fcontext\u002Fcompactor.py\nfrom __future__ import annotations\n\nimport logging\nfrom dataclasses import dataclass\n\nfrom ..messages import Transcript\nfrom ..providers.base import Provider\nfrom .accountant import ContextAccountant\nfrom .masking import mask_older_results\nfrom .summarizer import summarize_prefix\n\n\nlog = logging.getLogger(__name__)\n\n\n@dataclass\nclass CompactionResult:\n    masking_tokens_freed: int = 0\n    summarization_turns_replaced: int = 0\n    summarization_tokens: int = 0\n    final_state: str = \"green\"\n\n\nclass Compactor:\n    def __init__(\n        self,\n        accountant: ContextAccountant,\n        provider: Provider,\n        keep_recent_results: int = 3,\n        keep_recent_turns_on_summary: int = 6,\n    ) -> None:\n        self.accountant = accountant\n        self.provider = provider\n        self.keep_recent_results = keep_recent_results\n        self.keep_recent_turns_on_summary = keep_recent_turns_on_summary\n\n    async def compact_if_needed(\n        self,\n        transcript: Transcript,\n        tools: list[dict],\n    ) -> CompactionResult:\n        result = CompactionResult()\n        snap = self.accountant.snapshot(transcript, tools=tools)\n        result.final_state = snap.state\n        if snap.state != \"red\":\n            return result\n\n        # Step 1: mask older tool results.\n        freed = mask_older_results(transcript, self.keep_recent_results,\n                                    self.accountant._enc)\n        result.masking_tokens_freed = freed\n        snap = self.accountant.snapshot(transcript, tools=tools)\n        result.final_state = snap.state\n        if snap.state != \"red\":\n            return result\n\n        # Step 2: summarize prefix.\n        summary = await summarize_prefix(\n            transcript, self.provider, self.keep_recent_turns_on_summary\n        )\n        if summary is not None:\n            result.summarization_turns_replaced = summary.turns_replaced\n            result.summarization_tokens = summary.output_tokens\n\n        snap = self.accountant.snapshot(transcript, tools=tools)\n        result.final_state = snap.state\n        if snap.state == \"red\":\n            log.warning(\"compaction could not bring transcript under red threshold\")\n\n        return result\n",[243,2410,2411,2416,2426,2430,2437,2447,2451,2463,2479,2494,2508,2522,2526,2530,2552,2556,2560,2566,2575,2588,2601,2614,2633,2637,2641,2650,2660,2668,2680,2691,2707,2723,2734,2748,2762,2776,2790,2794,2806,2812,2822,2839,2849,2861,2895,2915,2938,2946,2950,2955,2977,2993,3006,3036,3052,3072,3078,3082,3087,3100,3122,3126,3142,3162,3180,3184,3214,3230,3251,3272,3276],{"__ignoreMap":306},[310,2412,2413],{"class":312,"line":313},[310,2414,2415],{"class":316},"# src\u002Fharness\u002Fcontext\u002Fcompactor.py\n",[310,2417,2418,2420,2422,2424],{"class":312,"line":320},[310,2419,324],{"class":323},[310,2421,328],{"class":327},[310,2423,331],{"class":323},[310,2425,335],{"class":334},[310,2427,2428],{"class":312,"line":338},[310,2429,342],{"emptyLinePlaceholder":341},[310,2431,2432,2434],{"class":312,"line":345},[310,2433,357],{"class":323},[310,2435,2436],{"class":334}," logging\n",[310,2438,2439,2441,2443,2445],{"class":312,"line":374},[310,2440,324],{"class":323},[310,2442,1274],{"class":334},[310,2444,357],{"class":323},[310,2446,1279],{"class":334},[310,2448,2449],{"class":312,"line":379},[310,2450,342],{"emptyLinePlaceholder":341},[310,2452,2453,2455,2457,2459,2461],{"class":312,"line":384},[310,2454,324],{"class":323},[310,2456,351],{"class":350},[310,2458,354],{"class":334},[310,2460,357],{"class":323},[310,2462,371],{"class":334},[310,2464,2465,2467,2469,2471,2473,2475,2477],{"class":312,"line":418},[310,2466,324],{"class":323},[310,2468,351],{"class":350},[310,2470,1322],{"class":334},[310,2472,588],{"class":350},[310,2474,1327],{"class":334},[310,2476,357],{"class":323},[310,2478,1332],{"class":334},[310,2480,2481,2483,2486,2489,2491],{"class":312,"line":423},[310,2482,324],{"class":323},[310,2484,2485],{"class":350}," .",[310,2487,2488],{"class":334},"accountant ",[310,2490,357],{"class":323},[310,2492,2493],{"class":334}," ContextAccountant\n",[310,2495,2496,2498,2500,2503,2505],{"class":312,"line":428},[310,2497,324],{"class":323},[310,2499,2485],{"class":350},[310,2501,2502],{"class":334},"masking ",[310,2504,357],{"class":323},[310,2506,2507],{"class":334}," mask_older_results\n",[310,2509,2510,2512,2514,2517,2519],{"class":312,"line":442},[310,2511,324],{"class":323},[310,2513,2485],{"class":350},[310,2515,2516],{"class":334},"summarizer ",[310,2518,357],{"class":323},[310,2520,2521],{"class":334}," summarize_prefix\n",[310,2523,2524],{"class":312,"line":458},[310,2525,342],{"emptyLinePlaceholder":341},[310,2527,2528],{"class":312,"line":472},[310,2529,342],{"emptyLinePlaceholder":341},[310,2531,2532,2535,2537,2540,2542,2545,2547,2550],{"class":312,"line":480},[310,2533,2534],{"class":334},"log ",[310,2536,732],{"class":390},[310,2538,2539],{"class":334}," logging",[310,2541,588],{"class":350},[310,2543,2544],{"class":584},"getLogger",[310,2546,581],{"class":350},[310,2548,2549],{"class":327},"__name__",[310,2551,890],{"class":350},[310,2553,2554],{"class":312,"line":494},[310,2555,342],{"emptyLinePlaceholder":341},[310,2557,2558],{"class":312,"line":505},[310,2559,342],{"emptyLinePlaceholder":341},[310,2561,2562,2564],{"class":312,"line":510},[310,2563,1450],{"class":1449},[310,2565,1453],{"class":435},[310,2567,2568,2570,2573],{"class":312,"line":516},[310,2569,1458],{"class":431},[310,2571,2572],{"class":1461}," CompactionResult",[310,2574,491],{"class":350},[310,2576,2577,2580,2582,2584,2586],{"class":312,"line":522},[310,2578,2579],{"class":334},"    masking_tokens_freed",[310,2581,449],{"class":350},[310,2583,467],{"class":466},[310,2585,391],{"class":390},[310,2587,712],{"class":402},[310,2589,2590,2593,2595,2597,2599],{"class":312,"line":560},[310,2591,2592],{"class":334},"    summarization_turns_replaced",[310,2594,449],{"class":350},[310,2596,467],{"class":466},[310,2598,391],{"class":390},[310,2600,712],{"class":402},[310,2602,2603,2606,2608,2610,2612],{"class":312,"line":598},[310,2604,2605],{"class":334},"    summarization_tokens",[310,2607,449],{"class":350},[310,2609,467],{"class":466},[310,2611,391],{"class":390},[310,2613,712],{"class":402},[310,2615,2616,2619,2621,2624,2626,2628,2631],{"class":312,"line":628},[310,2617,2618],{"class":334},"    final_state",[310,2620,449],{"class":350},[310,2622,2623],{"class":466}," str",[310,2625,391],{"class":390},[310,2627,395],{"class":394},[310,2629,2630],{"class":398},"green",[310,2632,415],{"class":394},[310,2634,2635],{"class":312,"line":648},[310,2636,342],{"emptyLinePlaceholder":341},[310,2638,2639],{"class":312,"line":677},[310,2640,342],{"emptyLinePlaceholder":341},[310,2642,2643,2645,2648],{"class":312,"line":682},[310,2644,1458],{"class":431},[310,2646,2647],{"class":1461}," Compactor",[310,2649,491],{"class":350},[310,2651,2652,2655,2658],{"class":312,"line":706},[310,2653,2654],{"class":431},"    def",[310,2656,2657],{"class":577}," __init__",[310,2659,439],{"class":350},[310,2661,2662,2666],{"class":312,"line":715},[310,2663,2665],{"class":2664},"smCYv","        self",[310,2667,455],{"class":350},[310,2669,2670,2673,2675,2678],{"class":312,"line":720},[310,2671,2672],{"class":445},"        accountant",[310,2674,449],{"class":350},[310,2676,2677],{"class":334}," ContextAccountant",[310,2679,455],{"class":350},[310,2681,2682,2685,2687,2689],{"class":312,"line":726},[310,2683,2684],{"class":445},"        provider",[310,2686,449],{"class":350},[310,2688,1543],{"class":334},[310,2690,455],{"class":350},[310,2692,2693,2696,2698,2700,2702,2705],{"class":312,"line":750},[310,2694,2695],{"class":445},"        keep_recent_results",[310,2697,449],{"class":350},[310,2699,467],{"class":466},[310,2701,391],{"class":390},[310,2703,2704],{"class":402}," 3",[310,2706,455],{"class":350},[310,2708,2709,2712,2714,2716,2718,2721],{"class":312,"line":760},[310,2710,2711],{"class":445},"        keep_recent_turns_on_summary",[310,2713,449],{"class":350},[310,2715,467],{"class":466},[310,2717,391],{"class":390},[310,2719,2720],{"class":402}," 6",[310,2722,455],{"class":350},[310,2724,2725,2728,2730,2732],{"class":312,"line":782},[310,2726,2727],{"class":350},"    )",[310,2729,486],{"class":350},[310,2731,1571],{"class":1353},[310,2733,491],{"class":350},[310,2735,2736,2738,2740,2743,2745],{"class":312,"line":811},[310,2737,2665],{"class":327},[310,2739,588],{"class":350},[310,2741,2742],{"class":591},"accountant",[310,2744,391],{"class":390},[310,2746,2747],{"class":334}," accountant\n",[310,2749,2750,2752,2754,2757,2759],{"class":312,"line":820},[310,2751,2665],{"class":327},[310,2753,588],{"class":350},[310,2755,2756],{"class":591},"provider",[310,2758,391],{"class":390},[310,2760,2761],{"class":334}," provider\n",[310,2763,2764,2766,2768,2771,2773],{"class":312,"line":850},[310,2765,2665],{"class":327},[310,2767,588],{"class":350},[310,2769,2770],{"class":591},"keep_recent_results",[310,2772,391],{"class":390},[310,2774,2775],{"class":334}," keep_recent_results\n",[310,2777,2778,2780,2782,2785,2787],{"class":312,"line":893},[310,2779,2665],{"class":327},[310,2781,588],{"class":350},[310,2783,2784],{"class":591},"keep_recent_turns_on_summary",[310,2786,391],{"class":390},[310,2788,2789],{"class":334}," keep_recent_turns_on_summary\n",[310,2791,2792],{"class":312,"line":905},[310,2793,342],{"emptyLinePlaceholder":341},[310,2795,2796,2799,2801,2804],{"class":312,"line":921},[310,2797,2798],{"class":431},"    async",[310,2800,1518],{"class":431},[310,2802,2803],{"class":435}," compact_if_needed",[310,2805,439],{"class":350},[310,2807,2808,2810],{"class":312,"line":934},[310,2809,2665],{"class":2664},[310,2811,455],{"class":350},[310,2813,2814,2816,2818,2820],{"class":312,"line":951},[310,2815,1007],{"class":445},[310,2817,449],{"class":350},[310,2819,452],{"class":334},[310,2821,455],{"class":350},[310,2823,2824,2827,2829,2831,2833,2836],{"class":312,"line":957},[310,2825,2826],{"class":445},"        tools",[310,2828,449],{"class":350},[310,2830,530],{"class":334},[310,2832,533],{"class":350},[310,2834,2835],{"class":466},"dict",[310,2837,2838],{"class":350},"],\n",[310,2840,2841,2843,2845,2847],{"class":312,"line":986},[310,2842,2727],{"class":350},[310,2844,486],{"class":350},[310,2846,2572],{"class":334},[310,2848,491],{"class":350},[310,2850,2851,2854,2856,2858],{"class":312,"line":1004},[310,2852,2853],{"class":334},"        result ",[310,2855,732],{"class":390},[310,2857,2572],{"class":584},[310,2859,2860],{"class":350},"()\n",[310,2862,2863,2866,2868,2871,2873,2875,2877,2880,2882,2884,2886,2888,2890,2893],{"class":312,"line":1026},[310,2864,2865],{"class":334},"        snap ",[310,2867,732],{"class":390},[310,2869,2870],{"class":327}," self",[310,2872,588],{"class":350},[310,2874,2742],{"class":591},[310,2876,588],{"class":350},[310,2878,2879],{"class":584},"snapshot",[310,2881,581],{"class":350},[310,2883,585],{"class":584},[310,2885,363],{"class":350},[310,2887,2146],{"class":868},[310,2889,732],{"class":390},[310,2891,2892],{"class":584},"tools",[310,2894,890],{"class":350},[310,2896,2897,2900,2902,2905,2907,2910,2912],{"class":312,"line":1051},[310,2898,2899],{"class":334},"        result",[310,2901,588],{"class":350},[310,2903,2904],{"class":591},"final_state",[310,2906,391],{"class":390},[310,2908,2909],{"class":334}," snap",[310,2911,588],{"class":350},[310,2913,2914],{"class":591},"state\n",[310,2916,2917,2919,2921,2923,2926,2929,2931,2934,2936],{"class":312,"line":1064},[310,2918,785],{"class":323},[310,2920,2909],{"class":334},[310,2922,588],{"class":350},[310,2924,2925],{"class":591},"state",[310,2927,2928],{"class":390}," !=",[310,2930,395],{"class":394},[310,2932,2933],{"class":398},"red",[310,2935,801],{"class":394},[310,2937,491],{"class":350},[310,2939,2940,2943],{"class":312,"line":1089},[310,2941,2942],{"class":323},"            return",[310,2944,2945],{"class":334}," result\n",[310,2947,2948],{"class":312,"line":1114},[310,2949,342],{"emptyLinePlaceholder":341},[310,2951,2952],{"class":312,"line":1119},[310,2953,2954],{"class":316},"        # Step 1: mask older tool results.\n",[310,2956,2957,2959,2961,2963,2965,2967,2969,2971,2973,2975],{"class":312,"line":1149},[310,2958,1122],{"class":334},[310,2960,732],{"class":390},[310,2962,436],{"class":584},[310,2964,581],{"class":350},[310,2966,585],{"class":584},[310,2968,363],{"class":350},[310,2970,2870],{"class":327},[310,2972,588],{"class":350},[310,2974,2770],{"class":591},[310,2976,455],{"class":350},[310,2978,2979,2982,2984,2986,2988,2991],{"class":312,"line":1701},[310,2980,2981],{"class":327},"                                    self",[310,2983,588],{"class":350},[310,2985,2742],{"class":591},[310,2987,588],{"class":350},[310,2989,2990],{"class":591},"_enc",[310,2992,890],{"class":350},[310,2994,2995,2997,2999,3002,3004],{"class":312,"line":1714},[310,2996,2899],{"class":334},[310,2998,588],{"class":350},[310,3000,3001],{"class":591},"masking_tokens_freed",[310,3003,391],{"class":390},[310,3005,1155],{"class":334},[310,3007,3008,3010,3012,3014,3016,3018,3020,3022,3024,3026,3028,3030,3032,3034],{"class":312,"line":1721},[310,3009,2865],{"class":334},[310,3011,732],{"class":390},[310,3013,2870],{"class":327},[310,3015,588],{"class":350},[310,3017,2742],{"class":591},[310,3019,588],{"class":350},[310,3021,2879],{"class":584},[310,3023,581],{"class":350},[310,3025,585],{"class":584},[310,3027,363],{"class":350},[310,3029,2146],{"class":868},[310,3031,732],{"class":390},[310,3033,2892],{"class":584},[310,3035,890],{"class":350},[310,3037,3038,3040,3042,3044,3046,3048,3050],{"class":312,"line":1726},[310,3039,2899],{"class":334},[310,3041,588],{"class":350},[310,3043,2904],{"class":591},[310,3045,391],{"class":390},[310,3047,2909],{"class":334},[310,3049,588],{"class":350},[310,3051,2914],{"class":591},[310,3053,3054,3056,3058,3060,3062,3064,3066,3068,3070],{"class":312,"line":1732},[310,3055,785],{"class":323},[310,3057,2909],{"class":334},[310,3059,588],{"class":350},[310,3061,2925],{"class":591},[310,3063,2928],{"class":390},[310,3065,395],{"class":394},[310,3067,2933],{"class":398},[310,3069,801],{"class":394},[310,3071,491],{"class":350},[310,3073,3074,3076],{"class":312,"line":1753},[310,3075,2942],{"class":323},[310,3077,2945],{"class":334},[310,3079,3080],{"class":312,"line":1767},[310,3081,342],{"emptyLinePlaceholder":341},[310,3083,3084],{"class":312,"line":1785},[310,3085,3086],{"class":316},"        # Step 2: summarize prefix.\n",[310,3088,3089,3092,3094,3096,3098],{"class":312,"line":1795},[310,3090,3091],{"class":334},"        summary ",[310,3093,732],{"class":390},[310,3095,2128],{"class":323},[310,3097,1521],{"class":584},[310,3099,439],{"class":350},[310,3101,3102,3105,3107,3109,3111,3113,3115,3117,3119],{"class":312,"line":1815},[310,3103,3104],{"class":584},"            transcript",[310,3106,363],{"class":350},[310,3108,2870],{"class":327},[310,3110,588],{"class":350},[310,3112,2756],{"class":591},[310,3114,363],{"class":350},[310,3116,2870],{"class":327},[310,3118,588],{"class":350},[310,3120,3121],{"class":591},"keep_recent_turns_on_summary\n",[310,3123,3124],{"class":312,"line":1859},[310,3125,954],{"class":350},[310,3127,3128,3130,3133,3136,3138,3140],{"class":312,"line":1887},[310,3129,785],{"class":323},[310,3131,3132],{"class":334}," summary ",[310,3134,3135],{"class":390},"is",[310,3137,1706],{"class":390},[310,3139,1571],{"class":1353},[310,3141,491],{"class":350},[310,3143,3144,3147,3149,3152,3154,3157,3159],{"class":312,"line":1922},[310,3145,3146],{"class":334},"            result",[310,3148,588],{"class":350},[310,3150,3151],{"class":591},"summarization_turns_replaced",[310,3153,391],{"class":390},[310,3155,3156],{"class":334}," summary",[310,3158,588],{"class":350},[310,3160,3161],{"class":591},"turns_replaced\n",[310,3163,3164,3166,3168,3171,3173,3175,3177],{"class":312,"line":1950},[310,3165,3146],{"class":334},[310,3167,588],{"class":350},[310,3169,3170],{"class":591},"summarization_tokens",[310,3172,391],{"class":390},[310,3174,3156],{"class":334},[310,3176,588],{"class":350},[310,3178,3179],{"class":591},"output_tokens\n",[310,3181,3182],{"class":312,"line":1981},[310,3183,342],{"emptyLinePlaceholder":341},[310,3185,3186,3188,3190,3192,3194,3196,3198,3200,3202,3204,3206,3208,3210,3212],{"class":312,"line":2014},[310,3187,2865],{"class":334},[310,3189,732],{"class":390},[310,3191,2870],{"class":327},[310,3193,588],{"class":350},[310,3195,2742],{"class":591},[310,3197,588],{"class":350},[310,3199,2879],{"class":584},[310,3201,581],{"class":350},[310,3203,585],{"class":584},[310,3205,363],{"class":350},[310,3207,2146],{"class":868},[310,3209,732],{"class":390},[310,3211,2892],{"class":584},[310,3213,890],{"class":350},[310,3215,3216,3218,3220,3222,3224,3226,3228],{"class":312,"line":2041},[310,3217,2899],{"class":334},[310,3219,588],{"class":350},[310,3221,2904],{"class":591},[310,3223,391],{"class":390},[310,3225,2909],{"class":334},[310,3227,588],{"class":350},[310,3229,2914],{"class":591},[310,3231,3232,3234,3236,3238,3240,3243,3245,3247,3249],{"class":312,"line":2046},[310,3233,785],{"class":323},[310,3235,2909],{"class":334},[310,3237,588],{"class":350},[310,3239,2925],{"class":591},[310,3241,3242],{"class":390}," ==",[310,3244,395],{"class":394},[310,3246,2933],{"class":398},[310,3248,801],{"class":394},[310,3250,491],{"class":350},[310,3252,3253,3256,3258,3261,3263,3265,3268,3270],{"class":312,"line":2067},[310,3254,3255],{"class":334},"            log",[310,3257,588],{"class":350},[310,3259,3260],{"class":584},"warning",[310,3262,581],{"class":350},[310,3264,801],{"class":394},[310,3266,3267],{"class":398},"compaction could not bring transcript under red threshold",[310,3269,801],{"class":394},[310,3271,890],{"class":350},[310,3273,3274],{"class":312,"line":2088},[310,3275,342],{"emptyLinePlaceholder":341},[310,3277,3278,3280],{"class":312,"line":2109},[310,3279,709],{"class":323},[310,3281,2945],{"class":334},[112,3283,3284],{},"The compactor is deliberately modest. It has two levers, it tries them in order, and it reports what it did. If both levers together can't bring the transcript under red, it logs and gives up — the next turn may fail at the provider level, which is information the operator needs.",[225,3286],{},[228,3288,3290],{"id":3289},"_85-wiring-into-the-loop","8.5 Wiring Into the Loop",[112,3292,3293,3294,3297],{},"The ",[243,3295,3296],{},"if snapshot.state == \"red\": pass"," placeholder from Chapter 7 becomes:",[301,3299,3301],{"className":303,"code":3300,"language":305,"meta":306,"style":306},"# src\u002Fharness\u002Fagent.py (updated)\nfrom .context.compactor import Compactor\n\n\nasync def arun(\n    provider: Provider,\n    registry: ToolRegistry,\n    user_message: str,\n    transcript: Transcript | None = None,\n    system: str | None = None,\n    on_event: \"callable | None\" = None,\n    on_tool_call: \"callable | None\" = None,\n    on_tool_result: \"callable | None\" = None,\n    on_snapshot: \"callable | None\" = None,\n    accountant: ContextAccountant | None = None,\n    compactor: Compactor | None = None,\n) -> str:\n    if transcript is None:\n        transcript = Transcript(system=system)\n    transcript.append(Message.user_text(user_message))\n    accountant = accountant or ContextAccountant()\n    compactor = compactor or Compactor(accountant, provider)\n\n    for _ in range(MAX_ITERATIONS):\n        snapshot = accountant.snapshot(transcript, tools=registry.schemas())\n        if on_snapshot is not None:\n            on_snapshot(snapshot)\n\n        if snapshot.state == \"red\":\n            result = await compactor.compact_if_needed(transcript, registry.schemas())\n            log.info(\"compacted: masked=%d, summarized_turns=%d, new_state=%s\",\n                     result.masking_tokens_freed,\n                     result.summarization_turns_replaced,\n                     result.final_state)\n            # Re-snapshot so observers see the post-compaction state\n            # in the same iteration. Otherwise the last visible frame\n            # is red and the compaction's effect is invisible — which\n            # matters if the next turn is the one that returns a final\n            # answer (there's no iteration after that to re-fire).\n            if on_snapshot is not None:\n                on_snapshot(accountant.snapshot(transcript, tools=registry.schemas()))\n\n        # ... rest of the turn, unchanged\n",[243,3302,3303,3308,3327,3331,3335,3346,3356,3368,3379,3398,3418,3438,3457,3476,3495,3515,3535,3545,3558,3577,3600,3617,3641,3645,3664,3699,3714,3725,3729,3750,3782,3816,3827,3837,3847,3852,3857,3862,3867,3872,3886,3918,3922],{"__ignoreMap":306},[310,3304,3305],{"class":312,"line":313},[310,3306,3307],{"class":316},"# src\u002Fharness\u002Fagent.py (updated)\n",[310,3309,3310,3312,3314,3317,3319,3322,3324],{"class":312,"line":320},[310,3311,324],{"class":323},[310,3313,2485],{"class":350},[310,3315,3316],{"class":334},"context",[310,3318,588],{"class":350},[310,3320,3321],{"class":334},"compactor ",[310,3323,357],{"class":323},[310,3325,3326],{"class":334}," Compactor\n",[310,3328,3329],{"class":312,"line":338},[310,3330,342],{"emptyLinePlaceholder":341},[310,3332,3333],{"class":312,"line":345},[310,3334,342],{"emptyLinePlaceholder":341},[310,3336,3337,3339,3341,3344],{"class":312,"line":374},[310,3338,1515],{"class":431},[310,3340,1518],{"class":431},[310,3342,3343],{"class":435}," arun",[310,3345,439],{"class":350},[310,3347,3348,3350,3352,3354],{"class":312,"line":379},[310,3349,1538],{"class":445},[310,3351,449],{"class":350},[310,3353,1543],{"class":334},[310,3355,455],{"class":350},[310,3357,3358,3361,3363,3366],{"class":312,"line":384},[310,3359,3360],{"class":445},"    registry",[310,3362,449],{"class":350},[310,3364,3365],{"class":334}," ToolRegistry",[310,3367,455],{"class":350},[310,3369,3370,3373,3375,3377],{"class":312,"line":418},[310,3371,3372],{"class":445},"    user_message",[310,3374,449],{"class":350},[310,3376,2623],{"class":466},[310,3378,455],{"class":350},[310,3380,3381,3383,3385,3388,3390,3392,3394,3396],{"class":312,"line":423},[310,3382,446],{"class":445},[310,3384,449],{"class":350},[310,3386,3387],{"class":334}," Transcript ",[310,3389,1568],{"class":390},[310,3391,1571],{"class":1353},[310,3393,391],{"class":390},[310,3395,1571],{"class":1353},[310,3397,455],{"class":350},[310,3399,3400,3403,3405,3407,3410,3412,3414,3416],{"class":312,"line":428},[310,3401,3402],{"class":445},"    system",[310,3404,449],{"class":350},[310,3406,2623],{"class":466},[310,3408,3409],{"class":390}," |",[310,3411,1571],{"class":1353},[310,3413,391],{"class":390},[310,3415,1571],{"class":1353},[310,3417,455],{"class":350},[310,3419,3420,3423,3425,3427,3430,3432,3434,3436],{"class":312,"line":442},[310,3421,3422],{"class":445},"    on_event",[310,3424,449],{"class":350},[310,3426,395],{"class":394},[310,3428,3429],{"class":398},"callable | None",[310,3431,801],{"class":394},[310,3433,391],{"class":390},[310,3435,1571],{"class":1353},[310,3437,455],{"class":350},[310,3439,3440,3443,3445,3447,3449,3451,3453,3455],{"class":312,"line":458},[310,3441,3442],{"class":445},"    on_tool_call",[310,3444,449],{"class":350},[310,3446,395],{"class":394},[310,3448,3429],{"class":398},[310,3450,801],{"class":394},[310,3452,391],{"class":390},[310,3454,1571],{"class":1353},[310,3456,455],{"class":350},[310,3458,3459,3462,3464,3466,3468,3470,3472,3474],{"class":312,"line":472},[310,3460,3461],{"class":445},"    on_tool_result",[310,3463,449],{"class":350},[310,3465,395],{"class":394},[310,3467,3429],{"class":398},[310,3469,801],{"class":394},[310,3471,391],{"class":390},[310,3473,1571],{"class":1353},[310,3475,455],{"class":350},[310,3477,3478,3481,3483,3485,3487,3489,3491,3493],{"class":312,"line":480},[310,3479,3480],{"class":445},"    on_snapshot",[310,3482,449],{"class":350},[310,3484,395],{"class":394},[310,3486,3429],{"class":398},[310,3488,801],{"class":394},[310,3490,391],{"class":390},[310,3492,1571],{"class":1353},[310,3494,455],{"class":350},[310,3496,3497,3500,3502,3505,3507,3509,3511,3513],{"class":312,"line":494},[310,3498,3499],{"class":445},"    accountant",[310,3501,449],{"class":350},[310,3503,3504],{"class":334}," ContextAccountant ",[310,3506,1568],{"class":390},[310,3508,1571],{"class":1353},[310,3510,391],{"class":390},[310,3512,1571],{"class":1353},[310,3514,455],{"class":350},[310,3516,3517,3520,3522,3525,3527,3529,3531,3533],{"class":312,"line":505},[310,3518,3519],{"class":445},"    compactor",[310,3521,449],{"class":350},[310,3523,3524],{"class":334}," Compactor ",[310,3526,1568],{"class":390},[310,3528,1571],{"class":1353},[310,3530,391],{"class":390},[310,3532,1571],{"class":1353},[310,3534,455],{"class":350},[310,3536,3537,3539,3541,3543],{"class":312,"line":510},[310,3538,483],{"class":350},[310,3540,486],{"class":350},[310,3542,2623],{"class":466},[310,3544,491],{"class":350},[310,3546,3547,3549,3552,3554,3556],{"class":312,"line":516},[310,3548,685],{"class":323},[310,3550,3551],{"class":334}," transcript ",[310,3553,3135],{"class":390},[310,3555,1571],{"class":1353},[310,3557,491],{"class":350},[310,3559,3560,3563,3565,3567,3569,3571,3573,3575],{"class":312,"line":522},[310,3561,3562],{"class":334},"        transcript ",[310,3564,732],{"class":390},[310,3566,452],{"class":584},[310,3568,581],{"class":350},[310,3570,2058],{"class":868},[310,3572,732],{"class":390},[310,3574,2058],{"class":584},[310,3576,890],{"class":350},[310,3578,3579,3581,3583,3585,3587,3589,3591,3593,3595,3598],{"class":312,"line":560},[310,3580,446],{"class":334},[310,3582,588],{"class":350},[310,3584,656],{"class":584},[310,3586,581],{"class":350},[310,3588,1195],{"class":584},[310,3590,588],{"class":350},[310,3592,2083],{"class":584},[310,3594,581],{"class":350},[310,3596,3597],{"class":584},"user_message",[310,3599,674],{"class":350},[310,3601,3602,3605,3607,3610,3613,3615],{"class":312,"line":598},[310,3603,3604],{"class":334},"    accountant ",[310,3606,732],{"class":390},[310,3608,3609],{"class":334}," accountant ",[310,3611,3612],{"class":390},"or",[310,3614,2677],{"class":584},[310,3616,2860],{"class":350},[310,3618,3619,3622,3624,3627,3629,3631,3633,3635,3637,3639],{"class":312,"line":628},[310,3620,3621],{"class":334},"    compactor ",[310,3623,732],{"class":390},[310,3625,3626],{"class":334}," compactor ",[310,3628,3612],{"class":390},[310,3630,2647],{"class":584},[310,3632,581],{"class":350},[310,3634,2742],{"class":584},[310,3636,363],{"class":350},[310,3638,2131],{"class":584},[310,3640,890],{"class":350},[310,3642,3643],{"class":312,"line":648},[310,3644,342],{"emptyLinePlaceholder":341},[310,3646,3647,3649,3652,3654,3657,3659,3662],{"class":312,"line":677},[310,3648,563],{"class":323},[310,3650,3651],{"class":334}," _ ",[310,3653,574],{"class":323},[310,3655,3656],{"class":577}," range",[310,3658,581],{"class":350},[310,3660,3661],{"class":577},"MAX_ITERATIONS",[310,3663,595],{"class":350},[310,3665,3666,3669,3671,3674,3676,3678,3680,3682,3684,3686,3688,3691,3693,3696],{"class":312,"line":682},[310,3667,3668],{"class":334},"        snapshot ",[310,3670,732],{"class":390},[310,3672,3673],{"class":334}," accountant",[310,3675,588],{"class":350},[310,3677,2879],{"class":584},[310,3679,581],{"class":350},[310,3681,585],{"class":584},[310,3683,363],{"class":350},[310,3685,2146],{"class":868},[310,3687,732],{"class":390},[310,3689,3690],{"class":584},"registry",[310,3692,588],{"class":350},[310,3694,3695],{"class":584},"schemas",[310,3697,3698],{"class":350},"())\n",[310,3700,3701,3703,3706,3708,3710,3712],{"class":312,"line":706},[310,3702,785],{"class":323},[310,3704,3705],{"class":334}," on_snapshot ",[310,3707,3135],{"class":390},[310,3709,1706],{"class":390},[310,3711,1571],{"class":1353},[310,3713,491],{"class":350},[310,3715,3716,3719,3721,3723],{"class":312,"line":715},[310,3717,3718],{"class":584},"            on_snapshot",[310,3720,581],{"class":350},[310,3722,2879],{"class":584},[310,3724,890],{"class":350},[310,3726,3727],{"class":312,"line":720},[310,3728,342],{"emptyLinePlaceholder":341},[310,3730,3731,3733,3736,3738,3740,3742,3744,3746,3748],{"class":312,"line":726},[310,3732,785],{"class":323},[310,3734,3735],{"class":334}," snapshot",[310,3737,588],{"class":350},[310,3739,2925],{"class":591},[310,3741,3242],{"class":390},[310,3743,395],{"class":394},[310,3745,2933],{"class":398},[310,3747,801],{"class":394},[310,3749,491],{"class":350},[310,3751,3752,3755,3757,3759,3762,3764,3767,3769,3771,3773,3776,3778,3780],{"class":312,"line":750},[310,3753,3754],{"class":334},"            result ",[310,3756,732],{"class":390},[310,3758,2128],{"class":323},[310,3760,3761],{"class":334}," compactor",[310,3763,588],{"class":350},[310,3765,3766],{"class":584},"compact_if_needed",[310,3768,581],{"class":350},[310,3770,585],{"class":584},[310,3772,363],{"class":350},[310,3774,3775],{"class":584}," registry",[310,3777,588],{"class":350},[310,3779,3695],{"class":584},[310,3781,3698],{"class":350},[310,3783,3784,3786,3788,3791,3793,3795,3798,3801,3804,3806,3809,3812,3814],{"class":312,"line":760},[310,3785,3255],{"class":334},[310,3787,588],{"class":350},[310,3789,3790],{"class":584},"info",[310,3792,581],{"class":350},[310,3794,801],{"class":394},[310,3796,3797],{"class":398},"compacted: masked=",[310,3799,3800],{"class":402},"%d",[310,3802,3803],{"class":398},", summarized_turns=",[310,3805,3800],{"class":402},[310,3807,3808],{"class":398},", new_state=",[310,3810,3811],{"class":402},"%s",[310,3813,801],{"class":394},[310,3815,455],{"class":350},[310,3817,3818,3821,3823,3825],{"class":312,"line":782},[310,3819,3820],{"class":584},"                     result",[310,3822,588],{"class":350},[310,3824,3001],{"class":591},[310,3826,455],{"class":350},[310,3828,3829,3831,3833,3835],{"class":312,"line":811},[310,3830,3820],{"class":584},[310,3832,588],{"class":350},[310,3834,3151],{"class":591},[310,3836,455],{"class":350},[310,3838,3839,3841,3843,3845],{"class":312,"line":820},[310,3840,3820],{"class":584},[310,3842,588],{"class":350},[310,3844,2904],{"class":591},[310,3846,890],{"class":350},[310,3848,3849],{"class":312,"line":850},[310,3850,3851],{"class":316},"            # Re-snapshot so observers see the post-compaction state\n",[310,3853,3854],{"class":312,"line":893},[310,3855,3856],{"class":316},"            # in the same iteration. Otherwise the last visible frame\n",[310,3858,3859],{"class":312,"line":905},[310,3860,3861],{"class":316},"            # is red and the compaction's effect is invisible — which\n",[310,3863,3864],{"class":312,"line":921},[310,3865,3866],{"class":316},"            # matters if the next turn is the one that returns a final\n",[310,3868,3869],{"class":312,"line":934},[310,3870,3871],{"class":316},"            # answer (there's no iteration after that to re-fire).\n",[310,3873,3874,3876,3878,3880,3882,3884],{"class":312,"line":951},[310,3875,631],{"class":323},[310,3877,3705],{"class":334},[310,3879,3135],{"class":390},[310,3881,1706],{"class":390},[310,3883,1571],{"class":1353},[310,3885,491],{"class":350},[310,3887,3888,3891,3893,3895,3897,3899,3901,3903,3905,3907,3909,3911,3913,3915],{"class":312,"line":957},[310,3889,3890],{"class":584},"                on_snapshot",[310,3892,581],{"class":350},[310,3894,2742],{"class":584},[310,3896,588],{"class":350},[310,3898,2879],{"class":584},[310,3900,581],{"class":350},[310,3902,585],{"class":584},[310,3904,363],{"class":350},[310,3906,2146],{"class":868},[310,3908,732],{"class":390},[310,3910,3690],{"class":584},[310,3912,588],{"class":350},[310,3914,3695],{"class":584},[310,3916,3917],{"class":350},"()))\n",[310,3919,3920],{"class":312,"line":986},[310,3921,342],{"emptyLinePlaceholder":341},[310,3923,3924],{"class":312,"line":1004},[310,3925,3926],{"class":316},"        # ... rest of the turn, unchanged\n",[112,3928,3929,3930,3933,3934,3937],{},"Think of ",[243,3931,3932],{},"on_snapshot"," as firing once per ",[115,3935,3936],{},"state transition"," rather than once per iteration. The pre-compaction snapshot is the \"decision\" frame — it's why the loop took the branch at all. The post-compaction snapshot is the \"effect\" frame — it's what the compactor did. Observers that only redraw on change (the common pattern for live progress bars and TUIs) get the drop from red-to-yellow-or-green naturally; observers that render every frame see a paired before\u002Fafter. §8.6's demo is pitched on watching the utilization walk back down after compaction fires, and without this second call the walk-down isn't visible — on short sessions the compaction often happens on the second-to-last turn, the model answers on the next turn, the loop exits, and the last thing the bar ever showed was \"red.\"",[112,3939,3940,3941,3943,3944,3947],{},"The same hook is what makes the compactor visible to observability tooling later. Chapter 18 routes ",[243,3942,3932],{}," into OpenTelemetry spans; with both frames emitted, you get paired pre\u002Fpost context-size data points on a ",[243,3945,3946],{},"compaction"," span for free, without the compactor itself needing to know anything about tracing.",[112,3949,3950,3951,3953],{},"Five lines added, the placeholder is gone, and the compactor is now visible to any observer already hooked on ",[243,3952,3932],{},". The loop owns the policy decision (\"compact when red\"); the compactor owns the mechanism; observability owns nothing it didn't already.",[225,3955],{},[228,3957,3959],{"id":3958},"_86-a-scenario-worth-running","8.6 A Scenario Worth Running",[112,3961,3962],{},"Compaction is invisible in small examples. The behavior shows up when you push the agent hard enough to get into red state and watch it recover.",[301,3964,3966],{"className":303,"code":3965,"language":305,"meta":306,"style":306},"# examples\u002Fch08_long_session.py\n#\n# Drives the agent through enough tool calls that compaction fires.\n# On macOS the book's Linux-specific paths (\u002Fproc\u002Fcpuinfo, \u002Fvar\u002Flog\u002Fdmesg)\n# don't exist; we use mac-available equivalents and tighten the context\n# budget so the red threshold is crossed within a single session.\nimport asyncio\nimport json\nimport logging\n\nfrom harness.agent import arun\nfrom harness.context.accountant import ContextAccountant, ContextBudget\nfrom harness.context.compactor import Compactor\nfrom harness.messages import ToolCall, ToolResult\nfrom harness.providers.base import TextDelta\nfrom harness.providers.local import LocalProvider\nfrom harness.tools.registry import ToolRegistry\nfrom harness.tools.std import bash, calc, read_file\n\n\nTASK = (\n    \"Investigate this machine. Read the files \u002Fetc\u002Fhosts, \u002Fetc\u002Fshells, \"\n    \"\u002Fetc\u002Fpasswd, and \u002FSystem\u002FLibrary\u002FCoreServices\u002FSystemVersion.plist. \"\n    \"Run `df -h`, `uptime`, and `ls -la \u002FUsers`. Then summarize in about \"\n    \"150 words what you learned about this machine.\"\n)\n\n\ndef show(snap) -> None:\n    filled = int(snap.utilization * 40)\n    bar = \"█\" * filled + \"░\" * (40 - filled)\n    color = {\"green\": \"\\033[92m\", \"yellow\": \"\\033[93m\", \"red\": \"\\033[91m\"}[snap.state]\n    reset = \"\\033[0m\"\n    print(f\"{color}[ctx {bar}] {snap.utilization * 100:5.1f}% {snap.state}{reset}\")\n\n\ndef stream_to_stdout(event) -> None:\n    if isinstance(event, TextDelta):\n        print(event.text, end=\"\", flush=True)\n\n\ndef on_tool_call(call: ToolCall) -> None:\n    args = json.dumps(call.args, ensure_ascii=False)\n    if len(args) > 120:\n        args = args[:117] + \"...\"\n    print(f\"  ⚙ {call.name}({args})\", flush=True)\n\n\ndef on_tool_result(result: ToolResult) -> None:\n    marker = \"✗\" if result.is_error else \"✓\"\n    preview = result.content.replace(\"\\n\", \" \").strip()\n    if len(preview) > 100:\n        preview = preview[:97] + \"...\"\n    print(f\"  {marker} {preview}\", flush=True)\n\n\nasync def main() -> None:\n    logging.basicConfig(\n        level=logging.INFO,\n        format=\"\\033[96m%(levelname)s %(name)s: %(message)s\\033[0m\",\n    )\n\n    provider = LocalProvider()\n    registry = ToolRegistry(tools=[calc, read_file, bash])\n\n    # Deliberately tight budget so the red threshold is reachable on a\n    # single investigation. A real 200K-token window wouldn't produce\n    # visible compaction in a 5-tool-call demo.\n    budget = ContextBudget(\n        window_size=8_000,\n        headroom=1_000,\n        yellow_threshold=0.60,\n        red_threshold=0.80,\n    )\n    accountant = ContextAccountant(budget=budget)\n    compactor = Compactor(\n        accountant,\n        provider,\n        keep_recent_results=2,\n        keep_recent_turns_on_summary=4,\n    )\n\n    print(\"--- agent run start ---\\n\", flush=True)\n    try:\n        final = await arun(\n            provider=provider,\n            registry=registry,\n            user_message=TASK,\n            accountant=accountant,\n            compactor=compactor,\n            on_event=stream_to_stdout,\n            on_snapshot=show,\n            on_tool_call=on_tool_call,\n            on_tool_result=on_tool_result,\n        )\n    except Exception as e:\n        print(f\"\\n--- arun raised: {type(e).__name__}: {e} ---\", flush=True)\n        raise\n    print(f\"\\n\\n--- agent run finished ---\\n[final text, {len(final)} chars]:\\n{final}\")\n\n\nasyncio.run(main())\n",[243,3967,3968,3973,3978,3983,3988,3993,3998,4005,4012,4018,4022,4039,4062,4080,4099,4118,4138,4158,4188,4192,4196,4206,4216,4225,4234,4243,4247,4251,4255,4275,4301,4344,4421,4437,4508,4512,4516,4536,4553,4588,4592,4596,4619,4655,4675,4701,4742,4746,4750,4774,4807,4847,4866,4891,4927,4931,4935,4953,4965,4983,5017,5021,5025,5037,5069,5073,5078,5083,5088,5100,5112,5124,5136,5148,5152,5171,5181,5187,5193,5204,5215,5219,5223,5248,5255,5268,5279,5290,5301,5313,5326,5339,5351,5364,5377,5382,5399,5452,5458,5507,5512,5517],{"__ignoreMap":306},[310,3969,3970],{"class":312,"line":313},[310,3971,3972],{"class":316},"# examples\u002Fch08_long_session.py\n",[310,3974,3975],{"class":312,"line":320},[310,3976,3977],{"class":316},"#\n",[310,3979,3980],{"class":312,"line":338},[310,3981,3982],{"class":316},"# Drives the agent through enough tool calls that compaction fires.\n",[310,3984,3985],{"class":312,"line":345},[310,3986,3987],{"class":316},"# On macOS the book's Linux-specific paths (\u002Fproc\u002Fcpuinfo, \u002Fvar\u002Flog\u002Fdmesg)\n",[310,3989,3990],{"class":312,"line":374},[310,3991,3992],{"class":316},"# don't exist; we use mac-available equivalents and tighten the context\n",[310,3994,3995],{"class":312,"line":379},[310,3996,3997],{"class":316},"# budget so the red threshold is crossed within a single session.\n",[310,3999,4000,4002],{"class":312,"line":384},[310,4001,357],{"class":323},[310,4003,4004],{"class":334}," asyncio\n",[310,4006,4007,4009],{"class":312,"line":418},[310,4008,357],{"class":323},[310,4010,4011],{"class":334}," json\n",[310,4013,4014,4016],{"class":312,"line":423},[310,4015,357],{"class":323},[310,4017,2436],{"class":334},[310,4019,4020],{"class":312,"line":428},[310,4021,342],{"emptyLinePlaceholder":341},[310,4023,4024,4026,4029,4031,4034,4036],{"class":312,"line":442},[310,4025,324],{"class":323},[310,4027,4028],{"class":334}," harness",[310,4030,588],{"class":350},[310,4032,4033],{"class":334},"agent ",[310,4035,357],{"class":323},[310,4037,4038],{"class":334}," arun\n",[310,4040,4041,4043,4045,4047,4049,4051,4053,4055,4057,4059],{"class":312,"line":458},[310,4042,324],{"class":323},[310,4044,4028],{"class":334},[310,4046,588],{"class":350},[310,4048,3316],{"class":334},[310,4050,588],{"class":350},[310,4052,2488],{"class":334},[310,4054,357],{"class":323},[310,4056,2677],{"class":334},[310,4058,363],{"class":350},[310,4060,4061],{"class":334}," ContextBudget\n",[310,4063,4064,4066,4068,4070,4072,4074,4076,4078],{"class":312,"line":472},[310,4065,324],{"class":323},[310,4067,4028],{"class":334},[310,4069,588],{"class":350},[310,4071,3316],{"class":334},[310,4073,588],{"class":350},[310,4075,3321],{"class":334},[310,4077,357],{"class":323},[310,4079,3326],{"class":334},[310,4081,4082,4084,4086,4088,4090,4092,4094,4096],{"class":312,"line":480},[310,4083,324],{"class":323},[310,4085,4028],{"class":334},[310,4087,588],{"class":350},[310,4089,354],{"class":334},[310,4091,357],{"class":323},[310,4093,1305],{"class":334},[310,4095,363],{"class":350},[310,4097,4098],{"class":334}," ToolResult\n",[310,4100,4101,4103,4105,4107,4109,4111,4113,4115],{"class":312,"line":494},[310,4102,324],{"class":323},[310,4104,4028],{"class":334},[310,4106,588],{"class":350},[310,4108,1322],{"class":334},[310,4110,588],{"class":350},[310,4112,1327],{"class":334},[310,4114,357],{"class":323},[310,4116,4117],{"class":334}," TextDelta\n",[310,4119,4120,4122,4124,4126,4128,4130,4133,4135],{"class":312,"line":505},[310,4121,324],{"class":323},[310,4123,4028],{"class":334},[310,4125,588],{"class":350},[310,4127,1322],{"class":334},[310,4129,588],{"class":350},[310,4131,4132],{"class":334},"local ",[310,4134,357],{"class":323},[310,4136,4137],{"class":334}," LocalProvider\n",[310,4139,4140,4142,4144,4146,4148,4150,4153,4155],{"class":312,"line":510},[310,4141,324],{"class":323},[310,4143,4028],{"class":334},[310,4145,588],{"class":350},[310,4147,2892],{"class":334},[310,4149,588],{"class":350},[310,4151,4152],{"class":334},"registry ",[310,4154,357],{"class":323},[310,4156,4157],{"class":334}," ToolRegistry\n",[310,4159,4160,4162,4164,4166,4168,4170,4173,4175,4178,4180,4183,4185],{"class":312,"line":516},[310,4161,324],{"class":323},[310,4163,4028],{"class":334},[310,4165,588],{"class":350},[310,4167,2892],{"class":334},[310,4169,588],{"class":350},[310,4171,4172],{"class":334},"std ",[310,4174,357],{"class":323},[310,4176,4177],{"class":334}," bash",[310,4179,363],{"class":350},[310,4181,4182],{"class":334}," calc",[310,4184,363],{"class":350},[310,4186,4187],{"class":334}," read_file\n",[310,4189,4190],{"class":312,"line":522},[310,4191,342],{"emptyLinePlaceholder":341},[310,4193,4194],{"class":312,"line":560},[310,4195,342],{"emptyLinePlaceholder":341},[310,4197,4198,4201,4203],{"class":312,"line":598},[310,4199,4200],{"class":327},"TASK",[310,4202,391],{"class":390},[310,4204,4205],{"class":350}," (\n",[310,4207,4208,4211,4214],{"class":312,"line":628},[310,4209,4210],{"class":394},"    \"",[310,4212,4213],{"class":398},"Investigate this machine. Read the files \u002Fetc\u002Fhosts, \u002Fetc\u002Fshells, ",[310,4215,415],{"class":394},[310,4217,4218,4220,4223],{"class":312,"line":648},[310,4219,4210],{"class":394},[310,4221,4222],{"class":398},"\u002Fetc\u002Fpasswd, and \u002FSystem\u002FLibrary\u002FCoreServices\u002FSystemVersion.plist. ",[310,4224,415],{"class":394},[310,4226,4227,4229,4232],{"class":312,"line":677},[310,4228,4210],{"class":394},[310,4230,4231],{"class":398},"Run `df -h`, `uptime`, and `ls -la \u002FUsers`. Then summarize in about ",[310,4233,415],{"class":394},[310,4235,4236,4238,4241],{"class":312,"line":682},[310,4237,4210],{"class":394},[310,4239,4240],{"class":398},"150 words what you learned about this machine.",[310,4242,415],{"class":394},[310,4244,4245],{"class":312,"line":706},[310,4246,890],{"class":350},[310,4248,4249],{"class":312,"line":715},[310,4250,342],{"emptyLinePlaceholder":341},[310,4252,4253],{"class":312,"line":720},[310,4254,342],{"emptyLinePlaceholder":341},[310,4256,4257,4259,4262,4264,4267,4269,4271,4273],{"class":312,"line":726},[310,4258,432],{"class":431},[310,4260,4261],{"class":435}," show",[310,4263,581],{"class":350},[310,4265,4266],{"class":445},"snap",[310,4268,483],{"class":350},[310,4270,486],{"class":350},[310,4272,1571],{"class":1353},[310,4274,491],{"class":350},[310,4276,4277,4280,4282,4284,4286,4288,4290,4293,4296,4299],{"class":312,"line":750},[310,4278,4279],{"class":334},"    filled ",[310,4281,732],{"class":390},[310,4283,467],{"class":466},[310,4285,581],{"class":350},[310,4287,4266],{"class":584},[310,4289,588],{"class":350},[310,4291,4292],{"class":591},"utilization",[310,4294,4295],{"class":390}," *",[310,4297,4298],{"class":402}," 40",[310,4300,890],{"class":350},[310,4302,4303,4306,4308,4310,4313,4315,4317,4320,4322,4324,4327,4329,4331,4334,4337,4339,4342],{"class":312,"line":760},[310,4304,4305],{"class":334},"    bar ",[310,4307,732],{"class":390},[310,4309,395],{"class":394},[310,4311,4312],{"class":398},"█",[310,4314,801],{"class":394},[310,4316,4295],{"class":390},[310,4318,4319],{"class":334}," filled ",[310,4321,1611],{"class":390},[310,4323,395],{"class":394},[310,4325,4326],{"class":398},"░",[310,4328,801],{"class":394},[310,4330,4295],{"class":390},[310,4332,4333],{"class":350}," (",[310,4335,4336],{"class":402},"40",[310,4338,1670],{"class":390},[310,4340,4341],{"class":334}," filled",[310,4343,890],{"class":350},[310,4345,4346,4349,4351,4353,4355,4357,4359,4361,4363,4366,4369,4371,4373,4375,4378,4380,4382,4384,4386,4389,4391,4393,4395,4397,4399,4401,4403,4405,4408,4410,4413,4415,4417,4419],{"class":312,"line":782},[310,4347,4348],{"class":334},"    color ",[310,4350,732],{"class":390},[310,4352,2003],{"class":350},[310,4354,801],{"class":394},[310,4356,2630],{"class":398},[310,4358,801],{"class":394},[310,4360,449],{"class":350},[310,4362,395],{"class":394},[310,4364,4365],{"class":327},"\\033",[310,4367,4368],{"class":398},"[92m",[310,4370,801],{"class":394},[310,4372,363],{"class":350},[310,4374,395],{"class":394},[310,4376,4377],{"class":398},"yellow",[310,4379,801],{"class":394},[310,4381,449],{"class":350},[310,4383,395],{"class":394},[310,4385,4365],{"class":327},[310,4387,4388],{"class":398},"[93m",[310,4390,801],{"class":394},[310,4392,363],{"class":350},[310,4394,395],{"class":394},[310,4396,2933],{"class":398},[310,4398,801],{"class":394},[310,4400,449],{"class":350},[310,4402,395],{"class":394},[310,4404,4365],{"class":327},[310,4406,4407],{"class":398},"[91m",[310,4409,801],{"class":394},[310,4411,4412],{"class":350},"}[",[310,4414,4266],{"class":334},[310,4416,588],{"class":350},[310,4418,2925],{"class":591},[310,4420,747],{"class":350},[310,4422,4423,4426,4428,4430,4432,4435],{"class":312,"line":811},[310,4424,4425],{"class":334},"    reset ",[310,4427,732],{"class":390},[310,4429,395],{"class":394},[310,4431,4365],{"class":327},[310,4433,4434],{"class":398},"[0m",[310,4436,415],{"class":394},[310,4438,4439,4442,4444,4446,4448,4450,4453,4455,4458,4460,4463,4465,4467,4469,4471,4473,4475,4477,4480,4483,4485,4488,4490,4492,4494,4496,4499,4502,4504,4506],{"class":312,"line":820},[310,4440,4441],{"class":577},"    print",[310,4443,581],{"class":350},[310,4445,1827],{"class":431},[310,4447,801],{"class":398},[310,4449,1833],{"class":402},[310,4451,4452],{"class":584},"color",[310,4454,1843],{"class":402},[310,4456,4457],{"class":398},"[ctx ",[310,4459,1833],{"class":402},[310,4461,4462],{"class":584},"bar",[310,4464,1843],{"class":402},[310,4466,1846],{"class":398},[310,4468,1833],{"class":402},[310,4470,4266],{"class":584},[310,4472,588],{"class":350},[310,4474,4292],{"class":591},[310,4476,4295],{"class":390},[310,4478,4479],{"class":402}," 100",[310,4481,4482],{"class":431},":5.1f",[310,4484,1843],{"class":402},[310,4486,4487],{"class":398},"% ",[310,4489,1833],{"class":402},[310,4491,4266],{"class":584},[310,4493,588],{"class":350},[310,4495,2925],{"class":591},[310,4497,4498],{"class":402},"}{",[310,4500,4501],{"class":584},"reset",[310,4503,1843],{"class":402},[310,4505,801],{"class":398},[310,4507,890],{"class":350},[310,4509,4510],{"class":312,"line":850},[310,4511,342],{"emptyLinePlaceholder":341},[310,4513,4514],{"class":312,"line":893},[310,4515,342],{"emptyLinePlaceholder":341},[310,4517,4518,4520,4523,4525,4528,4530,4532,4534],{"class":312,"line":905},[310,4519,432],{"class":431},[310,4521,4522],{"class":435}," stream_to_stdout",[310,4524,581],{"class":350},[310,4526,4527],{"class":445},"event",[310,4529,483],{"class":350},[310,4531,486],{"class":350},[310,4533,1571],{"class":1353},[310,4535,491],{"class":350},[310,4537,4538,4540,4542,4544,4546,4548,4551],{"class":312,"line":921},[310,4539,685],{"class":323},[310,4541,634],{"class":577},[310,4543,581],{"class":350},[310,4545,4527],{"class":584},[310,4547,363],{"class":350},[310,4549,4550],{"class":584}," TextDelta",[310,4552,595],{"class":350},[310,4554,4555,4558,4560,4562,4564,4566,4568,4571,4573,4576,4578,4581,4583,4586],{"class":312,"line":934},[310,4556,4557],{"class":577},"        print",[310,4559,581],{"class":350},[310,4561,4527],{"class":584},[310,4563,588],{"class":350},[310,4565,1805],{"class":591},[310,4567,363],{"class":350},[310,4569,4570],{"class":868}," end",[310,4572,732],{"class":390},[310,4574,4575],{"class":394},"\"\"",[310,4577,363],{"class":350},[310,4579,4580],{"class":868}," flush",[310,4582,732],{"class":390},[310,4584,4585],{"class":1353},"True",[310,4587,890],{"class":350},[310,4589,4590],{"class":312,"line":951},[310,4591,342],{"emptyLinePlaceholder":341},[310,4593,4594],{"class":312,"line":957},[310,4595,342],{"emptyLinePlaceholder":341},[310,4597,4598,4600,4603,4605,4607,4609,4611,4613,4615,4617],{"class":312,"line":986},[310,4599,432],{"class":431},[310,4601,4602],{"class":435}," on_tool_call",[310,4604,581],{"class":350},[310,4606,240],{"class":445},[310,4608,449],{"class":350},[310,4610,1305],{"class":334},[310,4612,483],{"class":350},[310,4614,486],{"class":350},[310,4616,1571],{"class":1353},[310,4618,491],{"class":350},[310,4620,4621,4624,4626,4629,4631,4634,4636,4638,4640,4643,4645,4648,4650,4653],{"class":312,"line":1004},[310,4622,4623],{"class":334},"    args ",[310,4625,732],{"class":390},[310,4627,4628],{"class":334}," json",[310,4630,588],{"class":350},[310,4632,4633],{"class":584},"dumps",[310,4635,581],{"class":350},[310,4637,240],{"class":584},[310,4639,588],{"class":350},[310,4641,4642],{"class":591},"args",[310,4644,363],{"class":350},[310,4646,4647],{"class":868}," ensure_ascii",[310,4649,732],{"class":390},[310,4651,4652],{"class":1353},"False",[310,4654,890],{"class":350},[310,4656,4657,4659,4661,4663,4665,4667,4670,4673],{"class":312,"line":1026},[310,4658,685],{"class":323},[310,4660,688],{"class":577},[310,4662,581],{"class":350},[310,4664,4642],{"class":584},[310,4666,483],{"class":350},[310,4668,4669],{"class":390}," >",[310,4671,4672],{"class":402}," 120",[310,4674,491],{"class":350},[310,4676,4677,4680,4682,4684,4686,4689,4691,4694,4696,4699],{"class":312,"line":1051},[310,4678,4679],{"class":334},"        args ",[310,4681,732],{"class":390},[310,4683,1878],{"class":334},[310,4685,738],{"class":350},[310,4687,4688],{"class":402},"117",[310,4690,412],{"class":350},[310,4692,4693],{"class":390}," +",[310,4695,395],{"class":394},[310,4697,4698],{"class":398},"...",[310,4700,415],{"class":394},[310,4702,4703,4705,4707,4709,4712,4714,4716,4718,4720,4722,4724,4726,4728,4730,4732,4734,4736,4738,4740],{"class":312,"line":1064},[310,4704,4441],{"class":577},[310,4706,581],{"class":350},[310,4708,1827],{"class":431},[310,4710,4711],{"class":398},"\"  ⚙ ",[310,4713,1833],{"class":402},[310,4715,240],{"class":584},[310,4717,588],{"class":350},[310,4719,1868],{"class":591},[310,4721,1843],{"class":402},[310,4723,581],{"class":398},[310,4725,1833],{"class":402},[310,4727,4642],{"class":584},[310,4729,1843],{"class":402},[310,4731,1917],{"class":398},[310,4733,363],{"class":350},[310,4735,4580],{"class":868},[310,4737,732],{"class":390},[310,4739,4585],{"class":1353},[310,4741,890],{"class":350},[310,4743,4744],{"class":312,"line":1089},[310,4745,342],{"emptyLinePlaceholder":341},[310,4747,4748],{"class":312,"line":1114},[310,4749,342],{"emptyLinePlaceholder":341},[310,4751,4752,4754,4757,4759,4762,4764,4766,4768,4770,4772],{"class":312,"line":1119},[310,4753,432],{"class":431},[310,4755,4756],{"class":435}," on_tool_result",[310,4758,581],{"class":350},[310,4760,4761],{"class":445},"result",[310,4763,449],{"class":350},[310,4765,366],{"class":334},[310,4767,483],{"class":350},[310,4769,486],{"class":350},[310,4771,1571],{"class":1353},[310,4773,491],{"class":350},[310,4775,4776,4779,4781,4783,4786,4788,4790,4793,4795,4797,4800,4802,4805],{"class":312,"line":1149},[310,4777,4778],{"class":334},"    marker ",[310,4780,732],{"class":390},[310,4782,395],{"class":394},[310,4784,4785],{"class":398},"✗",[310,4787,801],{"class":394},[310,4789,1965],{"class":323},[310,4791,4792],{"class":334}," result",[310,4794,588],{"class":350},[310,4796,946],{"class":591},[310,4798,4799],{"class":323}," else",[310,4801,395],{"class":394},[310,4803,4804],{"class":398},"✓",[310,4806,415],{"class":394},[310,4808,4809,4812,4814,4816,4818,4820,4822,4825,4827,4829,4831,4833,4835,4837,4839,4842,4845],{"class":312,"line":1701},[310,4810,4811],{"class":334},"    preview ",[310,4813,732],{"class":390},[310,4815,4792],{"class":334},[310,4817,588],{"class":350},[310,4819,298],{"class":591},[310,4821,588],{"class":350},[310,4823,4824],{"class":584},"replace",[310,4826,581],{"class":350},[310,4828,801],{"class":394},[310,4830,2024],{"class":327},[310,4832,801],{"class":394},[310,4834,363],{"class":350},[310,4836,395],{"class":394},[310,4838,395],{"class":394},[310,4840,4841],{"class":350},").",[310,4843,4844],{"class":584},"strip",[310,4846,2860],{"class":350},[310,4848,4849,4851,4853,4855,4858,4860,4862,4864],{"class":312,"line":1714},[310,4850,685],{"class":323},[310,4852,688],{"class":577},[310,4854,581],{"class":350},[310,4856,4857],{"class":584},"preview",[310,4859,483],{"class":350},[310,4861,4669],{"class":390},[310,4863,4479],{"class":402},[310,4865,491],{"class":350},[310,4867,4868,4871,4873,4876,4878,4881,4883,4885,4887,4889],{"class":312,"line":1721},[310,4869,4870],{"class":334},"        preview ",[310,4872,732],{"class":390},[310,4874,4875],{"class":334}," preview",[310,4877,738],{"class":350},[310,4879,4880],{"class":402},"97",[310,4882,412],{"class":350},[310,4884,4693],{"class":390},[310,4886,395],{"class":394},[310,4888,4698],{"class":398},[310,4890,415],{"class":394},[310,4892,4893,4895,4897,4899,4902,4904,4907,4909,4911,4913,4915,4917,4919,4921,4923,4925],{"class":312,"line":1726},[310,4894,4441],{"class":577},[310,4896,581],{"class":350},[310,4898,1827],{"class":431},[310,4900,4901],{"class":398},"\"  ",[310,4903,1833],{"class":402},[310,4905,4906],{"class":584},"marker",[310,4908,1843],{"class":402},[310,4910,2003],{"class":402},[310,4912,4857],{"class":584},[310,4914,1843],{"class":402},[310,4916,801],{"class":398},[310,4918,363],{"class":350},[310,4920,4580],{"class":868},[310,4922,732],{"class":390},[310,4924,4585],{"class":1353},[310,4926,890],{"class":350},[310,4928,4929],{"class":312,"line":1732},[310,4930,342],{"emptyLinePlaceholder":341},[310,4932,4933],{"class":312,"line":1753},[310,4934,342],{"emptyLinePlaceholder":341},[310,4936,4937,4939,4941,4944,4947,4949,4951],{"class":312,"line":1767},[310,4938,1515],{"class":431},[310,4940,1518],{"class":431},[310,4942,4943],{"class":435}," main",[310,4945,4946],{"class":350},"()",[310,4948,486],{"class":350},[310,4950,1571],{"class":1353},[310,4952,491],{"class":350},[310,4954,4955,4958,4960,4963],{"class":312,"line":1785},[310,4956,4957],{"class":334},"    logging",[310,4959,588],{"class":350},[310,4961,4962],{"class":584},"basicConfig",[310,4964,439],{"class":350},[310,4966,4967,4970,4972,4975,4977,4981],{"class":312,"line":1795},[310,4968,4969],{"class":868},"        level",[310,4971,732],{"class":390},[310,4973,4974],{"class":584},"logging",[310,4976,588],{"class":350},[310,4978,4980],{"class":4979},"swQdS","INFO",[310,4982,455],{"class":350},[310,4984,4985,4988,4990,4992,4994,4997,5000,5003,5006,5009,5011,5013,5015],{"class":312,"line":1815},[310,4986,4987],{"class":868},"        format",[310,4989,732],{"class":390},[310,4991,801],{"class":394},[310,4993,4365],{"class":327},[310,4995,4996],{"class":398},"[96m",[310,4998,4999],{"class":402},"%(levelname)s",[310,5001,5002],{"class":402}," %(name)s",[310,5004,5005],{"class":398},": ",[310,5007,5008],{"class":402},"%(message)s",[310,5010,4365],{"class":327},[310,5012,4434],{"class":398},[310,5014,801],{"class":394},[310,5016,455],{"class":350},[310,5018,5019],{"class":312,"line":1859},[310,5020,2254],{"class":350},[310,5022,5023],{"class":312,"line":1887},[310,5024,342],{"emptyLinePlaceholder":341},[310,5026,5027,5030,5032,5035],{"class":312,"line":1922},[310,5028,5029],{"class":334},"    provider ",[310,5031,732],{"class":390},[310,5033,5034],{"class":584}," LocalProvider",[310,5036,2860],{"class":350},[310,5038,5039,5042,5044,5046,5048,5050,5052,5054,5057,5059,5062,5064,5066],{"class":312,"line":1950},[310,5040,5041],{"class":334},"    registry ",[310,5043,732],{"class":390},[310,5045,3365],{"class":584},[310,5047,581],{"class":350},[310,5049,2892],{"class":868},[310,5051,732],{"class":390},[310,5053,533],{"class":350},[310,5055,5056],{"class":584},"calc",[310,5058,363],{"class":350},[310,5060,5061],{"class":584}," read_file",[310,5063,363],{"class":350},[310,5065,4177],{"class":584},[310,5067,5068],{"class":350},"])\n",[310,5070,5071],{"class":312,"line":1981},[310,5072,342],{"emptyLinePlaceholder":341},[310,5074,5075],{"class":312,"line":2014},[310,5076,5077],{"class":316},"    # Deliberately tight budget so the red threshold is reachable on a\n",[310,5079,5080],{"class":312,"line":2041},[310,5081,5082],{"class":316},"    # single investigation. A real 200K-token window wouldn't produce\n",[310,5084,5085],{"class":312,"line":2046},[310,5086,5087],{"class":316},"    # visible compaction in a 5-tool-call demo.\n",[310,5089,5090,5093,5095,5098],{"class":312,"line":2067},[310,5091,5092],{"class":334},"    budget ",[310,5094,732],{"class":390},[310,5096,5097],{"class":584}," ContextBudget",[310,5099,439],{"class":350},[310,5101,5102,5105,5107,5110],{"class":312,"line":2088},[310,5103,5104],{"class":868},"        window_size",[310,5106,732],{"class":390},[310,5108,5109],{"class":402},"8_000",[310,5111,455],{"class":350},[310,5113,5114,5117,5119,5122],{"class":312,"line":2109},[310,5115,5116],{"class":868},"        headroom",[310,5118,732],{"class":390},[310,5120,5121],{"class":402},"1_000",[310,5123,455],{"class":350},[310,5125,5126,5129,5131,5134],{"class":312,"line":2115},[310,5127,5128],{"class":868},"        yellow_threshold",[310,5130,732],{"class":390},[310,5132,5133],{"class":402},"0.60",[310,5135,455],{"class":350},[310,5137,5138,5141,5143,5146],{"class":312,"line":2120},[310,5139,5140],{"class":868},"        red_threshold",[310,5142,732],{"class":390},[310,5144,5145],{"class":402},"0.80",[310,5147,455],{"class":350},[310,5149,5150],{"class":312,"line":2154},[310,5151,2254],{"class":350},[310,5153,5154,5156,5158,5160,5162,5165,5167,5169],{"class":312,"line":2179},[310,5155,3604],{"class":334},[310,5157,732],{"class":390},[310,5159,2677],{"class":584},[310,5161,581],{"class":350},[310,5163,5164],{"class":868},"budget",[310,5166,732],{"class":390},[310,5168,5164],{"class":584},[310,5170,890],{"class":350},[310,5172,5173,5175,5177,5179],{"class":312,"line":2184},[310,5174,3621],{"class":334},[310,5176,732],{"class":390},[310,5178,2647],{"class":584},[310,5180,439],{"class":350},[310,5182,5183,5185],{"class":312,"line":2190},[310,5184,2672],{"class":584},[310,5186,455],{"class":350},[310,5188,5189,5191],{"class":312,"line":2206},[310,5190,2684],{"class":584},[310,5192,455],{"class":350},[310,5194,5195,5197,5199,5202],{"class":312,"line":2235},[310,5196,2695],{"class":868},[310,5198,732],{"class":390},[310,5200,5201],{"class":402},"2",[310,5203,455],{"class":350},[310,5205,5206,5208,5210,5213],{"class":312,"line":2251},[310,5207,2711],{"class":868},[310,5209,732],{"class":390},[310,5211,5212],{"class":402},"4",[310,5214,455],{"class":350},[310,5216,5217],{"class":312,"line":2257},[310,5218,2254],{"class":350},[310,5220,5221],{"class":312,"line":2286},[310,5222,342],{"emptyLinePlaceholder":341},[310,5224,5225,5227,5229,5231,5234,5236,5238,5240,5242,5244,5246],{"class":312,"line":2291},[310,5226,4441],{"class":577},[310,5228,581],{"class":350},[310,5230,801],{"class":394},[310,5232,5233],{"class":398},"--- agent run start ---",[310,5235,2024],{"class":327},[310,5237,801],{"class":394},[310,5239,363],{"class":350},[310,5241,4580],{"class":868},[310,5243,732],{"class":390},[310,5245,4585],{"class":1353},[310,5247,890],{"class":350},[310,5249,5250,5253],{"class":312,"line":2300},[310,5251,5252],{"class":323},"    try",[310,5254,491],{"class":350},[310,5256,5257,5260,5262,5264,5266],{"class":312,"line":2312},[310,5258,5259],{"class":334},"        final ",[310,5261,732],{"class":390},[310,5263,2128],{"class":323},[310,5265,3343],{"class":584},[310,5267,439],{"class":350},[310,5269,5270,5273,5275,5277],{"class":312,"line":2329},[310,5271,5272],{"class":868},"            provider",[310,5274,732],{"class":390},[310,5276,2756],{"class":584},[310,5278,455],{"class":350},[310,5280,5281,5284,5286,5288],{"class":312,"line":2347},[310,5282,5283],{"class":868},"            registry",[310,5285,732],{"class":390},[310,5287,3690],{"class":584},[310,5289,455],{"class":350},[310,5291,5292,5295,5297,5299],{"class":312,"line":2364},[310,5293,5294],{"class":868},"            user_message",[310,5296,732],{"class":390},[310,5298,4200],{"class":577},[310,5300,455],{"class":350},[310,5302,5304,5307,5309,5311],{"class":312,"line":5303},89,[310,5305,5306],{"class":868},"            accountant",[310,5308,732],{"class":390},[310,5310,2742],{"class":584},[310,5312,455],{"class":350},[310,5314,5316,5319,5321,5324],{"class":312,"line":5315},90,[310,5317,5318],{"class":868},"            compactor",[310,5320,732],{"class":390},[310,5322,5323],{"class":584},"compactor",[310,5325,455],{"class":350},[310,5327,5329,5332,5334,5337],{"class":312,"line":5328},91,[310,5330,5331],{"class":868},"            on_event",[310,5333,732],{"class":390},[310,5335,5336],{"class":584},"stream_to_stdout",[310,5338,455],{"class":350},[310,5340,5342,5344,5346,5349],{"class":312,"line":5341},92,[310,5343,3718],{"class":868},[310,5345,732],{"class":390},[310,5347,5348],{"class":584},"show",[310,5350,455],{"class":350},[310,5352,5354,5357,5359,5362],{"class":312,"line":5353},93,[310,5355,5356],{"class":868},"            on_tool_call",[310,5358,732],{"class":390},[310,5360,5361],{"class":584},"on_tool_call",[310,5363,455],{"class":350},[310,5365,5367,5370,5372,5375],{"class":312,"line":5366},94,[310,5368,5369],{"class":868},"            on_tool_result",[310,5371,732],{"class":390},[310,5373,5374],{"class":584},"on_tool_result",[310,5376,455],{"class":350},[310,5378,5380],{"class":312,"line":5379},95,[310,5381,954],{"class":350},[310,5383,5385,5388,5391,5394,5397],{"class":312,"line":5384},96,[310,5386,5387],{"class":323},"    except",[310,5389,5390],{"class":466}," Exception",[310,5392,5393],{"class":323}," as",[310,5395,5396],{"class":334}," e",[310,5398,491],{"class":350},[310,5400,5402,5404,5406,5408,5410,5412,5415,5417,5420,5422,5425,5427,5429,5431,5433,5435,5437,5439,5442,5444,5446,5448,5450],{"class":312,"line":5401},97,[310,5403,4557],{"class":577},[310,5405,581],{"class":350},[310,5407,1827],{"class":431},[310,5409,801],{"class":398},[310,5411,2024],{"class":327},[310,5413,5414],{"class":398},"--- arun raised: ",[310,5416,1833],{"class":402},[310,5418,5419],{"class":466},"type",[310,5421,581],{"class":350},[310,5423,5424],{"class":584},"e",[310,5426,4841],{"class":350},[310,5428,2549],{"class":327},[310,5430,1843],{"class":402},[310,5432,5005],{"class":398},[310,5434,1833],{"class":402},[310,5436,5424],{"class":584},[310,5438,1843],{"class":402},[310,5440,5441],{"class":398}," ---\"",[310,5443,363],{"class":350},[310,5445,4580],{"class":868},[310,5447,732],{"class":390},[310,5449,4585],{"class":1353},[310,5451,890],{"class":350},[310,5453,5455],{"class":312,"line":5454},98,[310,5456,5457],{"class":323},"        raise\n",[310,5459,5461,5463,5465,5467,5469,5471,5474,5476,5479,5481,5483,5485,5488,5490,5492,5495,5497,5499,5501,5503,5505],{"class":312,"line":5460},99,[310,5462,4441],{"class":577},[310,5464,581],{"class":350},[310,5466,1827],{"class":431},[310,5468,801],{"class":398},[310,5470,2097],{"class":327},[310,5472,5473],{"class":398},"--- agent run finished ---",[310,5475,2024],{"class":327},[310,5477,5478],{"class":398},"[final text, ",[310,5480,1833],{"class":402},[310,5482,2216],{"class":577},[310,5484,581],{"class":350},[310,5486,5487],{"class":584},"final",[310,5489,483],{"class":350},[310,5491,1843],{"class":402},[310,5493,5494],{"class":398}," chars]:",[310,5496,2024],{"class":327},[310,5498,1833],{"class":402},[310,5500,5487],{"class":584},[310,5502,1843],{"class":402},[310,5504,801],{"class":398},[310,5506,890],{"class":350},[310,5508,5510],{"class":312,"line":5509},100,[310,5511,342],{"emptyLinePlaceholder":341},[310,5513,5515],{"class":312,"line":5514},101,[310,5516,342],{"emptyLinePlaceholder":341},[310,5518,5520,5523,5525,5528,5530,5533],{"class":312,"line":5519},102,[310,5521,5522],{"class":334},"asyncio",[310,5524,588],{"class":350},[310,5526,5527],{"class":584},"run",[310,5529,581],{"class":350},[310,5531,5532],{"class":584},"main",[310,5534,3698],{"class":350},[112,5536,5537,5538,5541,5542,5545],{},"Three things the example is doing on purpose. The ",[243,5539,5540],{},"ContextBudget"," is tight (8K window, 60\u002F80% thresholds) because on a real 200K Anthropic budget you'd need to read a 100K file to even tickle the yellow threshold, and a 5-step demo never gets there. Tightening the budget synthesises the pressure a long session produces in production. The prompt asks for seven sources plus a summary — enough tool calls that history blows past the red threshold before the final answer. And ",[243,5543,5544],{},"LocalProvider"," is the default here because the whole point is watching the context bar, not burning API credits on an API-assisted demo; point it at any OpenAI-compatible local server (Ollama, vLLM, llama.cpp) and you can rerun the session as many times as you like with no cost.",[112,5547,5548],{},"Run this. On a typical Linux machine, you'll watch the utilization walk from 5% to 65% to 82% to — the moment the first compaction fires — back down to something in the 50s. The subsequent turns pick up where they left off, with the older tool results now placeholders. If the agent needs a file it already read, it re-reads it. The final summary in the assistant's response is not visibly different from a version without compaction, which is the goal.",[112,5550,5551],{},"If you turn up the logging, you'll see the compactor's decisions in order: first masking (usually enough), occasionally summarization (when masking alone won't do it on a long session). You now have a loop that can run forty, fifty, eighty turns without falling over.",[225,5553],{},[228,5555,5557],{"id":5556},"_87-what-breaks-and-why-its-good-that-it-breaks","8.7 What Breaks, and Why It's Good That It Breaks",[112,5559,5560],{},"Two failure modes you will hit if you push this hard enough.",[112,5562,5563,5566,5567,5569],{},[142,5564,5565],{},"Masking elides something the agent needed."," The agent reads a file, does other work, and two compaction cycles later the file content is a placeholder. The agent says \"let me re-read that file\" — and if you're feeling clever — it re-runs ",[243,5568,245],{},", the result is fresh, everything continues. Good: the mask was reversible, the agent used that reversibility.",[112,5571,5572,5573,5576],{},"Sometimes, though, the agent needs something it can't re-derive. It had the output of a tool that isn't idempotent — maybe it ran ",[243,5574,5575],{},"last -n 20"," ten minutes ago and the list has changed. It can re-call the tool, but the result now differs from what it was working with. This is a real limitation. Chapter 9's scratchpad pattern is how we rescue the agent in that case: the agent explicitly writes important findings to durable state before they can be masked away.",[112,5578,5579,5582],{},[142,5580,5581],{},"Summarization loses detail the agent silently needed."," A file path appeared in a summarized turn, the summary paraphrased (\"read a config file\"), the agent later needs to reference the exact path and can't. This is why the summarizer's system prompt is specific about preserving file paths, tool arguments, and decisions. Even so, loss is inevitable; you will tune what your summarizer preserves based on what your agent fails on.",[112,5584,5585],{},"Both of these are tuning decisions, not bugs. Chapter 19 builds the evaluation harness that lets you tune them empirically instead of by hunches.",[225,5587],{},[228,5589,5591],{"id":5590},"_88-commit","8.8 Commit",[301,5593,5597],{"className":5594,"code":5595,"language":5596,"meta":306,"style":306},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","git add -A && git commit -m \"ch08: compaction — masking + summarization\"\ngit tag ch08-compaction\n","bash",[243,5598,5599,5630],{"__ignoreMap":306},[310,5600,5601,5604,5607,5611,5614,5617,5620,5623,5625,5628],{"class":312,"line":313},[310,5602,5603],{"class":1461},"git",[310,5605,5606],{"class":398}," add",[310,5608,5610],{"class":5609},"stzsN"," -A",[310,5612,5613],{"class":350}," &&",[310,5615,5616],{"class":1461}," git",[310,5618,5619],{"class":398}," commit",[310,5621,5622],{"class":5609}," -m",[310,5624,395],{"class":394},[310,5626,5627],{"class":398},"ch08: compaction — masking + summarization",[310,5629,415],{"class":394},[310,5631,5632,5634,5637],{"class":312,"line":320},[310,5633,5603],{"class":1461},[310,5635,5636],{"class":398}," tag",[310,5638,5639],{"class":398}," ch08-compaction\n",[228,5641,5643],{"id":5642},"_89-try-it-yourself","8.9 Try It Yourself",[136,5645,5646,5656,5662],{},[139,5647,5648,5651,5652,5655],{},[142,5649,5650],{},"Deliberately under-provision and watch."," Set ",[243,5653,5654],{},"budget.window_size = 20_000"," (the accountant will start flagging red on short sessions) and run a medium prompt. Observe the compactor firing. Does the final output degrade? By how much? Can you see the degradation without being told to look for it?",[139,5657,5658,5661],{},[142,5659,5660],{},"Write a compaction that's worse than ours."," Implement a compactor that just truncates the first half of the transcript on red state. Run it against the scenario above. How does the agent's output differ? What does it forget? This is the \"naive truncation\" baseline your evals should beat.",[139,5663,5664,5667],{},[142,5665,5666],{},"Experiment with the summarizer's system prompt."," Add a rule: \"preserve every numerical value verbatim.\" Rerun a prompt where the agent computes intermediate numbers. Does the rule help? Does it make the summary too long? You're discovering what compaction tuning actually feels like in practice.",[225,5669],{},[5671,5672,5673,5676],"what-you-understand",{},[112,5674,5675],{},"Compaction is a two-layer subsystem in your harness: masking catches the easy cases reversibly, summarization handles the long-session cases lossily, and the compactor orchestrates them on the accountant's red signal. The tool-call record is preserved end-to-end because the summarizer's prompt says to. Your loop can run much longer than Chapter 2's could.",[112,5677,5678],{},"What's still missing. Masked content is reversible only because the agent can re-run the tool. Summarized content is gone. In both cases, some important state might be something the agent actively discovered and wants to keep — a decision it made, a plan it committed to, a fact it confirmed. That kind of state belongs outside the transcript entirely, in durable storage the agent writes to and reads from on demand. Chapter 9 is the scratchpad pattern: how the agent keeps what it learns without letting it eat context.",[5680,5681,5682],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sFwrP, html code.shiki .sFwrP{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s2W-s, html code.shiki .s2W-s{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#032F62;--shiki-default-font-style:inherit;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit}html pre.shiki code .sithA, html code.shiki .sithA{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#032F62;--shiki-default-font-style:inherit;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stp6e, html code.shiki .stp6e{--shiki-light:#39ADB5;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .smCYv, html code.shiki .smCYv{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .swQdS, html code.shiki .swQdS{--shiki-light:#E53935;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":306,"searchDepth":320,"depth":320,"links":5684},[5685,5686,5687,5688,5689,5690,5691,5692,5693],{"id":230,"depth":320,"text":231},{"id":283,"depth":320,"text":284},{"id":1204,"depth":320,"text":1205},{"id":2401,"depth":320,"text":2402},{"id":3289,"depth":320,"text":3290},{"id":3958,"depth":320,"text":3959},{"id":5556,"depth":320,"text":5557},{"id":5590,"depth":320,"text":5591},{"id":5642,"depth":320,"text":5643},"md",{},null,{"title":42,"description":117},"swILjYI5DpjPXMO1YWt3p9a546DtaUZIW_S8_PRZJ_A",[5700,5702],{"title":38,"path":39,"stem":40,"description":5701,"children":-1},"Previously: the registry validates arguments and detects loops. All five breaks from Chapter 2 are handled — except Break 5, which is now the subject of the next five chapters. A tool that returns 200KB of JSON still poisons the loop on turn four.",{"title":46,"path":47,"stem":48,"description":5703,"children":-1},"Previously: compaction. Older tool results get masked; the prefix gets summarized when masking isn't enough. The transcript survives long sessions. But anything the agent wanted to keep verbatim is at the mercy of the compactor.",1776848984225]