LLM-driven context compaction for long sessions.
Uses token estimation (chars / 4) to find optimal cut points rather than
fixed event counts. Walks backwards from the newest events, accumulating
tokens until keep_recent_tokens is reached. The cut always lands on a
user or assistant message boundary — never mid-tool-result.
Summary
Types
@type compact_result() :: %{ summary: String.t(), tokens_before: non_neg_integer(), kept_events: [Vibe.Trajectory.t()], details: map() }
Functions
@spec compact(keyword()) :: {:ok, compact_result()} | {:error, term()}
@spec compact( [Vibe.Trajectory.t()], keyword() ) :: {:ok, compact_result()} | {:error, term()}
@spec find_cut_point_for_test([Vibe.Trajectory.t()], non_neg_integer()) :: {[Vibe.Trajectory.t()], [Vibe.Trajectory.t()]}
@spec snap_to_boundary_for_test([Vibe.Trajectory.t()]) :: [Vibe.Trajectory.t()]
@spec summarize([Vibe.Trajectory.t()], String.t() | nil, keyword()) :: {:ok, String.t()} | {:error, term()}
@spec turn_prefix_summary( [Vibe.Trajectory.t()], keyword() ) :: {:ok, String.t()} | {:error, term()}