Yesterday's v0.26→v0.29 interactive terminal made it possible to drive a loomcycle agent for hours from the Web UI — close the tab, come back, the run is still alive. The natural next problem: a multi-hour conversation eventually crowds the model's context window. v0.32.0 ships the answer as three coordinated triggers around one shared summarizer. Manual: a one-click Compact button in the run terminal header that calls POST /v1/runs/{run_id}/compact (scope runs:create), gated to a safe boundary — a live interactive run must be parked at awaiting_input, mid-turn returns 409 (the same iteration-boundary discipline F41 cooperative , the steering work's drainSteer, and snapshot all share). Auto: at the top of each iteration the loop checks the previous turn's context footprint against the per-agent autocompact_at_pct (50..95, off by default); if crossed, the loop summarizes inline + replaces, debounced by a +1-iteration guard so the now-smaller request can't loop, and skipped when the window is unknown (e.g. Ollama). Self: a new Context op=compact tool that sets the loop's compact-request flag, summarized at the next safe boundary — useful for a long autonomous run that filled its window without an operator watching. To make self-compact decisions conscious rather than guessed, Context op=self now reports a context object: {used_tokens, max_tokens, used_pct} alongside the resolved compaction settings, so an agent can do "used_pct >= compaction.autocompact_at_pct → call op=compact now". used_tokens is computed as input + cache_read + cache_creation (the same true-prompt-footprint formula the v0.29.0 gauge uses, not just input_tokens which undercounts under prompt caching). Per-agent settings (enabled, target_percentage 10..50, keep_last_n, keep_first, autocompact_at_pct 50..95, model — a cheaper same-provider summary model for cost) round-trip through every AgentDef mirror (mergedDef + applyOverlay + lookup.SubstrateAgentDef + content-identifying so a fork that changes a compaction field mints a new content_sha256; omitempty + normalize-collapse ensures a no-compaction agent hashes byte-identical to pre-feature rows), and through per-run override on POST /v1/runs (MergeCompaction is per-field). Spawn inheritance is the asymmetric design call: compaction settings flow DOWN the spawn tree (unlike memory_scopes / sampling, which are each agent's own) — a parent that knows it needs aggressive compaction wants its fan-out children compacted too. Precedence: per-spawn override on Agent.spawn (and Agent.parallel_spawn) > parent's effective policy > child def's own settings — parent-set wins, child def fills gaps the parent left unset, per-spawn override wins over both, all re-stamped on subCtx so grandchildren inherit recursively. The compacted form is keep-last-N + keep-first, not brutal drop-everything: a CompactionSplit helper snaps the cut to a clean user-turn boundary so a tool_use/tool_results pair is never split (same provider-protocol discipline as the steering work). The pinned task survives verbatim (keep_first preserves the original instruction). The summary replaces the middle. The recent tail (keep_last_n messages) stays verbatim. One summarizer drives all three triggers — loop.Summarize is one-shot, no-tools, target-percentage-parameterized, shared by manual/auto/self; an earlier draft had a separate server.summarizeConversation for the manual path; #461 consolidated to one implementation so a future improvement lands once. Persisted EventContextCompaction marker with trigger/keep_n/keep_first/before/after; replayTranscript rebuilds the [pinned + summary + last-N] form on resume/crash-recovery/continuation, so the durable transcript captures the compacted shape (the full transcript is retained — non-destructive audit); OTEL adds a context.compaction span event. Plus the operator-side polish bundled into v0.32.0: a "Stop" button restyled white-on-dark-red so the destructive cancel reads at a glance, distinct from accent-colored primary actions, and a Claude-Desktop-style composer card for the terminal input (rounded elevated card with footer row showing the live serving model name and "MCP (N)" count derived from mcp____ calls in the transcript). The clean-boundary discipline this is the third subsystem to insist on: cooperative (F41/RFC X), mid-run steering (drainSteer top-of-iteration, never between tool_use and tool_results), and now compaction. The LLM message graph has structural constraints the runtime has to respect; the provider's protocol is part of the runtime's contract, not just the model's preference. Why this matters: long interactive sessions (yesterday's interactive terminal) stop dying at the context wall, autonomous long-running agents (exp6 self-evolving with snapshot/resume) stop hitting context-length-exceeded mid-task, fan-out orchestrators (exp5 agent ensembles) can let their sub-agents inherit the parent's compaction discipline. And the bit that took the most thinking to get right: the agent participates. Context op=self shows the gauge; the prompt instructs the agent to check it; Context op=compact lets the agent act. Compaction stops being something the runtime does to the agent and becomes something the agent does with the runtime.
Budgets, costs, and encrypted credentials (v1.9.0 to v1.11.1)