pub struct Session {Show 17 fields
pub id: String,
pub title: String,
pub title_source: TitleSource,
pub messages: Vec<Message>,
pub branches: Vec<Branch>,
pub system_prompt: String,
pub project_path: PathBuf,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub usage: Usage,
pub cost: SessionCost,
pub edit_stats: EditStats,
pub checkpoint: Option<CheckpointState>,
pub last_rewind_report: Option<String>,
pub turn_context: Option<String>,
pub instruction_dirs: BTreeSet<PathBuf>,
pub stream_rules: StreamRuleLedger,
/* private fields */
}Expand description
A conversation session
Fields§
§id: String§title: String§title_source: TitleSourceHow title was chosen. Gates auto-title so manual renames stick.
messages: Vec<Message>The active path: the transcript shown and sent to the model.
branches: Vec<Branch>Every other stored path of this conversation (/branch, /tree).
Compaction, spill and undo work on messages only.
system_prompt: String§project_path: PathBuf§created_at: DateTime<Utc>§updated_at: DateTime<Utc>§usage: UsageToken usage across every turn, as the provider reported it.
#[serde(default)] so sessions exported before this existed still load.
cost: SessionCostEstimated USD for this run (/cost). Not stored in the database, so a
resumed session starts from zero.
edit_stats: EditStatsLines added / removed by this session’s write / edit /
apply_patch results (/info). Rebuilt from the transcript on load.
checkpoint: Option<CheckpointState>Active exploratory checkpoint (conversation only; not a file snapshot).
last_rewind_report: Option<String>Last rewind report, used to reject a second rewind with no new checkpoint.
turn_context: Option<String>Model-facing context for the current user turn (memory recall).
Appended to the latest user message at request time, never stored in
messages, so the system prompt and history stay byte-stable for the
provider prompt cache. Not persisted.
instruction_dirs: BTreeSet<PathBuf>Directories below the project whose instruction files (AGENTS.md
…) were already attached to a tool result this session. Not persisted:
a resumed session sees each once more.
stream_rules: StreamRuleLedgerStream rules that fired this session (repeat limits). Not stored in
the database, so a resumed session may fire each once rule again.
Implementations§
Source§impl Session
impl Session
Sourcepub fn from_imported(
project_path: PathBuf,
messages: Vec<Message>,
title_hint: Option<&str>,
) -> Self
pub fn from_imported( project_path: PathBuf, messages: Vec<Message>, title_hint: Option<&str>, ) -> Self
Build a session from imported messages (new id, current project).
Sourcepub fn fork(&self) -> Self
pub fn fork(&self) -> Self
Copy of this conversation under a new id, so it can diverge while the
original stays as saved. Usage restarts at zero (the history’s tokens
were spent by the original); the title gets a (fork) suffix.
Sourcepub fn set_title_manual(&mut self, title: impl Into<String>)
pub fn set_title_manual(&mut self, title: impl Into<String>)
User-facing rename; locks the title against further auto updates.
Sourcepub fn apply_heuristic_title(&mut self, first_user_text: &str) -> bool
pub fn apply_heuristic_title(&mut self, first_user_text: &str) -> bool
Instant offline title from the first user prompt (if still default).
Returns true when the title changed.
Sourcepub fn maybe_upgrade_title_from_history(&mut self) -> bool
pub fn maybe_upgrade_title_from_history(&mut self) -> bool
Upgrade a placeholder title from the transcript’s first user message.
Used on resume and when listing sessions so legacy New session - …
rows (and unfinished project-ab placeholders) pick up a real name
without waiting for another turn.
Returns true when the title changed.
Sourcepub fn apply_generated_title(&mut self, title: impl Into<String>) -> bool
pub fn apply_generated_title(&mut self, title: impl Into<String>) -> bool
Apply a small-model title when still auto-titleable.
Returns true when the title changed.
Sourcepub fn user_message_count(&self) -> usize
pub fn user_message_count(&self) -> usize
Count of user-role messages (used to gate first-turn auto-title).
Sourcepub fn first_user_text(&self) -> Option<String>
pub fn first_user_text(&self) -> Option<String>
Text of the first user message, if any.
Sourcepub fn first_assistant_snippet(&self, max_chars: usize) -> Option<String>
pub fn first_assistant_snippet(&self, max_chars: usize) -> Option<String>
Short assistant excerpt for title refine (first text block, capped).
Sourcepub fn add_usage(&mut self, usage: &Usage)
pub fn add_usage(&mut self, usage: &Usage)
Fold a turn’’s reported usage into the session total.
Sourcepub fn record_edit_results(
&mut self,
calls: &[ToolCall],
results: &[ToolResult],
)
pub fn record_edit_results( &mut self, calls: &[ToolCall], results: &[ToolResult], )
Fold the line counts of file-mutating tool results into
Self::edit_stats; each result is matched to its call by id.
Sourcepub fn add_user_message(&mut self, content: &str)
pub fn add_user_message(&mut self, content: &str)
Add a user message
Sourcepub fn add_agent_note(&mut self, content: &str)
pub fn add_agent_note(&mut self, content: &str)
Append a user-role note the agent wrote itself (recovery hint, reminder, job notice). Compaction never treats it as the user’s turn.
Sourcepub fn add_user_message_blocks(&mut self, blocks: Vec<ContentBlock>)
pub fn add_user_message_blocks(&mut self, blocks: Vec<ContentBlock>)
Add a user message with structured content blocks (text + images, etc.).
Sourcepub fn add_assistant_message(&mut self, blocks: Vec<ContentBlock>)
pub fn add_assistant_message(&mut self, blocks: Vec<ContentBlock>)
Add an assistant message with content blocks
Sourcepub fn add_tool_results(&mut self, results: Vec<ToolResult>)
pub fn add_tool_results(&mut self, results: Vec<ToolResult>)
Add tool results (oversized bodies are capped for context economy).
Sourcepub fn build_request(
&self,
tools: impl Into<Arc<[ToolDefinition]>>,
max_tokens: Option<u32>,
temperature: Option<f32>,
_thinking: Option<bool>,
) -> LlmRequest
pub fn build_request( &self, tools: impl Into<Arc<[ToolDefinition]>>, max_tokens: Option<u32>, temperature: Option<f32>, _thinking: Option<bool>, ) -> LlmRequest
Build an LLM request from the current conversation
Sourcepub fn info(&self) -> SessionInfo
pub fn info(&self) -> SessionInfo
Get session info
Sourcepub fn format_exit_summary(
&self,
duration: Duration,
model_label: &str,
binary: &str,
) -> String
pub fn format_exit_summary( &self, duration: Duration, model_label: &str, binary: &str, ) -> String
Exit summary printed after the terminal is restored.
model_label is typically provider/model. binary is the CLI name
used in the resume hint (e.g. "whycodes").
Sourcepub fn conversation_text(&self) -> String
pub fn conversation_text(&self) -> String
Get conversation as a readable string (for display)
Sourcepub fn set_system_prompt(&mut self, prompt: &str)
pub fn set_system_prompt(&mut self, prompt: &str)
Update the system prompt
Sourcepub fn token_count(&self) -> usize
pub fn token_count(&self) -> usize
Estimate token count (Unicode chars / 4, same family as the LLM fallback).
Not provider BPE — good enough for compaction thresholds and the context meter when usage has not been reported yet. O(1) when the running cache is valid; rebuilds after load or bulk mutation.
Sourcepub fn token_count_cached(&mut self) -> usize
pub fn token_count_cached(&mut self) -> usize
Like [token_count] but refreshes the running cache when stale.
Prefer this on mutating paths (agent loop) so a prune/shake that rebuilt the cache stays O(1) for the rest of the step.
Sourcepub fn truncate_large_tool_results(&mut self) -> usize
pub fn truncate_large_tool_results(&mut self) -> usize
Cap oversized tool result bodies already in the transcript.
Returns how many messages were modified.
Sourcepub fn prune_old_tool_results(&mut self) -> usize
pub fn prune_old_tool_results(&mut self) -> usize
Compact the conversation toward a token budget.
- Cap oversized tool results in place.
- If still over
¾ · max_tokens, drop oldest messages until under budget or only [MIN_KEEP_MESSAGES] remain in the tail. - Prepend a short stub summary of what was dropped.
When already under budget after tool caps, the message list is not
reshuffled (avoids churn on /compact for small sessions).
Shrink older tool outputs more aggressively.
Keeps the last [PRUNE_KEEP_RECENT_TOOLS] tool-role messages at the
normal cap; everything older is cut to TOOL_RESULT_PRUNE_CHARS.
Sourcepub fn shake_old_tool_results(&mut self) -> usize
pub fn shake_old_tool_results(&mut self) -> usize
Shrink older tool results to TOOL_RESULT_SHAKE_CHARS when prune was
not enough to relieve context pressure.
Sourcepub fn spill_old_tool_results(
&mut self,
store: &mut dyn FnMut(&str, &str) -> Option<String>,
) -> usize
pub fn spill_old_tool_results( &mut self, store: &mut dyn FnMut(&str, &str) -> Option<String>, ) -> usize
Move old tool bodies longer than TOOL_RESULT_PRUNE_CHARS out of
the transcript through store(id, body) -> Some(pointer): the message
keeps the first SPILL_HEAD_CHARS chars plus a pointer line, so the
model can read the full output instead of re-running the tool. The
last [PRUNE_KEEP_RECENT_TOOLS] tool messages, short bodies, bodies
already spilled and bodies store declines are left alone. Returns
how many bodies were spilled.
Sourcepub fn prepend_compact_summary(&mut self, summary: &str)
pub fn prepend_compact_summary(&mut self, summary: &str)
Insert or replace the leading compact summary user message (LLM path).
Sourcepub fn transcript_for_compact_summary(&self, max_chars: usize) -> String
pub fn transcript_for_compact_summary(&self, max_chars: usize) -> String
Concatenate older messages for LLM summarization (capped).
Prefer CompactOutcome::dropped_transcript from a just-run
Session::compact — that captures the actual dropped prefix.
This helper is a fallback when no drop has happened yet.
Sourcepub fn transcript_for_full_summary(&self, max_chars: usize) -> String
pub fn transcript_for_full_summary(&self, max_chars: usize) -> String
Whole-session transcript for full-replace compact.
Sourcepub fn compaction_prefix_end(&self) -> Option<usize>
pub fn compaction_prefix_end(&self) -> Option<usize>
Where Self::apply_full_replace would cut now: the index of the
last real user turn, or None when there is none.
Sourcepub fn prefix_transcript_for_summary(&self) -> Option<(usize, String)>
pub fn prefix_transcript_for_summary(&self) -> Option<(usize, String)>
The messages a full-replace compaction would summarize (everything
before the last real user turn), rendered like
Self::transcript_for_full_summary, with the index that bounds
them. None when nothing precedes that turn.
Sourcepub fn local_full_replace_summary(&self) -> String
pub fn local_full_replace_summary(&self) -> String
Local stub used when the compact LLM is off or fails.
Sourcepub fn apply_full_replace(&mut self, summary: &str) -> CompactOutcome
pub fn apply_full_replace(&mut self, summary: &str) -> CompactOutcome
Replace history with last user query + recent tail + summary carrier.
Full-replace: the model-visible conversation becomes
[last_user?, recent…, continuation_summary]. System prompt stays
on the session, not in messages.
Sourcepub fn native_compaction_prefix(&self) -> Option<(usize, Vec<Message>)>
pub fn native_compaction_prefix(&self) -> Option<(usize, Vec<Message>)>
Where provider-native compaction would cut, with the messages it
would summarize: everything before the last real user turn. None
unless something precedes that turn and the message just before it
is an assistant reply without a tool call: the summarized range must
not end on an open call, and the first kept message (the user turn)
must differ in role from the last summarized one, or the provider
merges them and kept thinking no longer matches.
Sourcepub fn apply_native_compaction(
&mut self,
cut: usize,
block: ContentBlock,
) -> CompactOutcome
pub fn apply_native_compaction( &mut self, cut: usize, block: ContentBlock, ) -> CompactOutcome
Replace the messages before cut (from
Self::native_compaction_prefix) with one assistant message that
holds the provider’s compaction block. The kept turns follow it
unchanged, thinking included, as the provider requires.
Sourcepub fn compact_full_replace_local(&mut self) -> CompactOutcome
pub fn compact_full_replace_local(&mut self) -> CompactOutcome
Full-replace compact without an LLM (local stub summary).
pub fn compact(&mut self, max_tokens: usize) -> CompactOutcome
Sourcepub fn save_to_db(&self, db: &Database) -> Result<()>
pub fn save_to_db(&self, db: &Database) -> Result<()>
Persist this session and all messages to the SQLite database.
Session metadata (including provider-reported token usage) is upserted. Messages are replaced as a set so repeated saves do not duplicate rows.
Sourcepub fn load_from_db(db: &Database, id: &str) -> Result<Option<Self>>
pub fn load_from_db(db: &Database, id: &str) -> Result<Option<Self>>
Load a session and its messages from the SQLite database by session id.
Sourcepub fn mark_checkpoint(&mut self, goal: impl Into<String>)
pub fn mark_checkpoint(&mut self, goal: impl Into<String>)
Record a conversation checkpoint after the current last message.
Sourcepub fn apply_rewind(&mut self, report: &str) -> bool
pub fn apply_rewind(&mut self, report: &str) -> bool
Collapse messages after the active checkpoint and keep report.
Returns false when no checkpoint is active.
Source§impl Session
impl Session
Export the session as a shareable JSON file. Writes to .whycodes/shares/{session_id}.json and returns the file path.
Sourcepub fn export_markdown(&self) -> String
pub fn export_markdown(&self) -> String
Export conversation as Markdown for sharing / /export.
Sourcepub fn revert_to(&mut self, message_index: usize) -> usize
pub fn revert_to(&mut self, message_index: usize) -> usize
Revert the session to a previous state by removing all messages after the given index. Returns the number of messages removed.
Sourcepub fn set_messages(&mut self, messages: Vec<Message>)
pub fn set_messages(&mut self, messages: Vec<Message>)
Replace the entire message list (used by undo/redo).
Sourcepub fn undo_last_turn(&mut self) -> usize
pub fn undo_last_turn(&mut self) -> usize
Undo the last user turn: remove from the last user message to the end. Returns the number of messages removed, or 0 if nothing to undo.
Source§impl Session
impl Session
Sourcepub fn mark_branch(&mut self, name: &str) -> Result<String, BranchError>
pub fn mark_branch(&mut self, name: &str) -> Result<String, BranchError>
Mark the current point under name (or branch N). Returns the name.
Sourcepub fn tree_rows(&self) -> Vec<TreeRow>
pub fn tree_rows(&self) -> Vec<TreeRow>
The conversation as a tree of user turns, active path first.
Sourcepub fn switch_branch(&mut self, target: TreeTarget) -> Result<bool, BranchError>
pub fn switch_branch(&mut self, target: TreeTarget) -> Result<bool, BranchError>
Make target the active leaf. The old active path is kept as a
branch unless another path already holds all of it. Returns false
when target already is the active path.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Session
impl<'de> Deserialize<'de> for Session
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for Session
impl RefUnwindSafe for Session
impl Send for Session
impl Sync for Session
impl Unpin for Session
impl UnsafeUnpin for Session
impl UnwindSafe for Session
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more