- core/compaction.ts: compactionCutIndex (backwards ~20k-token walk, NEVER
separates a tool result from its call — the cut extends to the call's
assistant message; a pending pose always survives), estimateTokens
(chars/4, pi's method), newCompactionMarker (header + summary; details
carry firstKeptMessageId/tokensBefore/model/at), serializeForSummary
(labeled text, tool results truncated ~2k), planTreeToText (verbatim
tree with transcript-derived statuses), buildCompactionPrompt (the four
ruled sections: learner understanding + evolution, teaching arc, plan
tree verbatim, current moment)
- toLlmMessages honors the cut: the ACTIVE (newest) marker renders ONCE
as the head; everything before firstKeptMessageId drops; corrupt cut
degrades honestly (everything renders, never throw)
- constants: KEEP_RECENT 20k, RESERVE 16k, DEFAULT_THRESHOLD 150k
- 16 test pins