Skip to content

runLoop and context compaction

runLoop is the session’s ReAct outer loop: it decides whether this turn handles a subtask first, compacts first, or asks the model again. When context grows, the system uses compaction to create a summary message, then filterCompacted truncates older raw text before the next model call. Separately, SessionSummary does not call the model and only tallies workspace diffs — do not conflate the two.

flowchart TD
Prompt[SessionPrompt.prompt] --> User[createUserMessage]
User --> Loop[Runner.ensureRunning]
Loop --> RL[runLoop while]
RL --> Next{Next action}
Next --> Sub[subtask]
Next --> Comp[compaction]
Next --> LLM[processor + LLM]

One session’s Runner ensures two runLoops do not run concurrently. See Instance boundaries and concurrency.

  1. Read filterCompacted history and find anchors (lastUser / lastAssistant / pending tasks).
  2. If the assistant finished cleanly and no tool-call is pending, exit.
  3. Consume tasks (LIFO): prefer subtask, then compaction.
  4. If overflow prediction is true, create a compaction task and continue.
  5. Otherwise enter a normal model turn: resolve tools, Processor consumes the stream.
flowchart TD
A[Read history + anchors] --> B{Finished?}
B -->|yes| Z[Return]
B -->|no| C{Has task?}
C -->|subtask| S[handleSubtask] --> A
C -->|compaction| P[compaction.process] --> A
C -->|none| O{Overflow?}
O -->|yes| N[Create compaction] --> A
O -->|no| L[LLM turn] --> A
SourceMechanism
Overflow predictionInside runLoop, context about to overflow → create compaction
Processor signalStep end or context-overflow error → return compact → create compaction
ManualSession summarize / compaction API
QueueUser messages that already carry a compaction Part, consumed by the outer loop

compaction.process calls the model with a dedicated compaction role and prompt to produce a text summary (tools forbidden), then writes an assistant message with summary=true. In automatic mode it may append a synthetic user message so execution can continue. Returns continue or stop (including failure exit when context is still too large).

flowchart LR
C[compaction produces summary assistant] --> U[User message carries compaction Part]
U --> F[Next turn filterCompacted]
F --> V[Model-facing view shortens]

The summary keeps a semantic anchor; older raw text is not expanded. Message-model side: Message model and cross-turn memory.

On finish-step, Processor may trigger SessionSummary: tally adds/deletes and files from snapshot diffs, write session-level summary fields. It does not call an LLM and does not create a filterCompacted boundary. It is work accounting, not meeting-minutes-style compaction.

  • Compaction costs an extra model call and latency; on failure the outer loop may stop instead of stuffing over-limit context.
  • Summary quality is bounded by the compaction prompt and model; a bad summary pollutes later turns — use session replay and human review to locate it.
  • Structured output (json_schema) can end the outer loop early; that path is independent of compaction.

Related reading: