pub trait History: Send + Sync {
// Required methods
fn append(&self, msg: Message);
fn snapshot(&self) -> Vec<Message>;
fn replace(&self, messages: Vec<Message>);
fn splice_prefix(&self, drop_count: usize, summary: Message) -> usize;
fn len(&self) -> usize;
fn truncate(&self, len: usize);
fn record_input_tokens(&self, tokens: u64);
fn token_estimate(&self) -> Option<u64>;
// Provided method
fn is_empty(&self) -> bool { ... }
}Expand description
Abstraction over message history — pure storage + token accounting.
Compaction is not handled here: summarization requires calling the LLM, which the
storage abstraction cannot reach.
Compaction is orchestrated in the turn main loop (session/turn/compact.rs) — it
reads History::snapshot, calls the LLM for a summary, then writes back the
computed new message list via History::replace. This trait is only responsible
for: appending, snapshotting, wholesale replacement, and providing the main loop with
an estimate of “how many tokens the current history is worth.”
Token estimation strategy (see VecHistory): use the actual input token count
reported by the last LLM call as a baseline, then add a character-heuristic
increment for messages appended after that baseline; when no real baseline is
available, fall back to a pure character-heuristic estimate for the entire history.
The turn main loop compares this estimate against the compaction threshold.
Required Methods§
Sourcefn snapshot(&self) -> Vec<Message>
fn snapshot(&self) -> Vec<Message>
A snapshot of the current history, to be fed into the next LLM call.
Sourcefn replace(&self, messages: Vec<Message>)
fn replace(&self, messages: Vec<Message>)
Replace the entire message list after compression. The turn main loop calls this to write back the new list consisting of a summary plus the retained tail. The implementation should also reset the token estimation baseline, since the old actual token counts no longer apply to the new list.
Sourcefn splice_prefix(&self, drop_count: usize, summary: Message) -> usize
fn splice_prefix(&self, drop_count: usize, summary: Message) -> usize
Prefix splice: replaces the first drop_count messages in the current list
with the single summary message, preserving everything after them. Returns the
actual number of messages dropped (drop_count is clamped to the current length).
This is the primitive for background compression write-back: a background task
computes drop_count (= the prefix length to summarize) and summary from a
snapshot taken at some point, but while the summarization LLM call is in flight,
the foreground turn may still be appending to the tail. Writing back with
replace(entire list) would discard any tail messages added during that time.
splice_prefix only touches the first drop_count messages of the current
list, preserving everything from drop_count.. onward (including tail messages
added in the meantime), so the write-back is correct.
Concurrency invariant (must be maintained): drop_count is computed from an
old snapshot and remains valid for the current list provided that during the
flight only tail appends (append) and in-place content replacements
(micro-compression replace with same-length rebuild) occur — no insertion or
deletion of middle messages. The only operation that removes middle messages is
compression itself, and compression runs solo (at most one in flight at a
time), so the invariant holds.
Like Self::replace, resets the token estimation baseline after write-back (the
true token count of the new prefix is unknown).
Sourcefn len(&self) -> usize
fn len(&self) -> usize
Number of messages currently held. Used to record a rollback boundary before a turn
appends its prompt, so Self::truncate can undo it if the turn fails permanently.
Sourcefn truncate(&self, len: usize)
fn truncate(&self, len: usize)
Truncates the message list to at most len messages, dropping any tail beyond it.
A no-op when len >= current length.
Used to roll back a permanently-failed turn: the user prompt (and any hook feedback)
appended at the start of the turn must not linger in history once the turn errors
out, otherwise it would be replayed on reload and re-sent to the model on the next
request. Like Self::replace, resets the token estimation baseline since the
dropped messages may have contributed to the delta estimate.
Sourcefn record_input_tokens(&self, tokens: u64)
fn record_input_tokens(&self, tokens: u64)
Records the actual input token count from the last LLM call
(input + cache_read + cache_creation). Serves as the precise baseline for
Self::token_estimate; subsequent Self::append messages are accumulated
incrementally using a character heuristic.
Sourcefn token_estimate(&self) -> Option<u64>
fn token_estimate(&self) -> Option<u64>
Estimates the token count for the current history. None indicates the history is
empty or no estimate is available.
Provided Methods§
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".