Skip to main content

Session

Struct Session 

Source
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: TitleSource

How 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: Usage

Token usage across every turn, as the provider reported it.

#[serde(default)] so sessions exported before this existed still load.

§cost: SessionCost

Estimated USD for this run (/cost). Not stored in the database, so a resumed session starts from zero.

§edit_stats: EditStats

Lines 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: StreamRuleLedger

Stream 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

Source

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).

Source

pub fn new(project_path: PathBuf, system_prompt: String) -> Self

Create a new session

Source

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.

Source

pub fn set_title_manual(&mut self, title: impl Into<String>)

User-facing rename; locks the title against further auto updates.

Source

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.

Source

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.

Source

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.

Source

pub fn user_message_count(&self) -> usize

Count of user-role messages (used to gate first-turn auto-title).

Source

pub fn first_user_text(&self) -> Option<String>

Text of the first user message, if any.

Source

pub fn first_assistant_snippet(&self, max_chars: usize) -> Option<String>

Short assistant excerpt for title refine (first text block, capped).

Source

pub fn add_usage(&mut self, usage: &Usage)

Fold a turn’’s reported usage into the session total.

Source

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.

Source

pub fn add_user_message(&mut self, content: &str)

Add a user message

Source

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.

Source

pub fn add_user_message_blocks(&mut self, blocks: Vec<ContentBlock>)

Add a user message with structured content blocks (text + images, etc.).

Source

pub fn add_assistant_message(&mut self, blocks: Vec<ContentBlock>)

Add an assistant message with content blocks

Source

pub fn add_tool_results(&mut self, results: Vec<ToolResult>)

Add tool results (oversized bodies are capped for context economy).

Source

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

Source

pub fn info(&self) -> SessionInfo

Get session info

Source

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").

Source

pub fn conversation_text(&self) -> String

Get conversation as a readable string (for display)

Source

pub fn set_system_prompt(&mut self, prompt: &str)

Update the system prompt

Source

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.

Source

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.

Source

pub fn truncate_large_tool_results(&mut self) -> usize

Cap oversized tool result bodies already in the transcript.

Returns how many messages were modified.

Source

pub fn prune_old_tool_results(&mut self) -> usize

Compact the conversation toward a token budget.

  1. Cap oversized tool results in place.
  2. If still over ¾ · max_tokens, drop oldest messages until under budget or only [MIN_KEEP_MESSAGES] remain in the tail.
  3. 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.

Source

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.

Source

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.

Source

pub fn prepend_compact_summary(&mut self, summary: &str)

Insert or replace the leading compact summary user message (LLM path).

Source

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.

Source

pub fn transcript_for_full_summary(&self, max_chars: usize) -> String

Whole-session transcript for full-replace compact.

Source

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.

Source

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.

Source

pub fn local_full_replace_summary(&self) -> String

Local stub used when the compact LLM is off or fails.

Source

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.

Source

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.

Source

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.

Source

pub fn compact_full_replace_local(&mut self) -> CompactOutcome

Full-replace compact without an LLM (local stub summary).

Source

pub fn compact(&mut self, max_tokens: usize) -> CompactOutcome

Source

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.

Source

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.

Source

pub fn mark_checkpoint(&mut self, goal: impl Into<String>)

Record a conversation checkpoint after the current last message.

Source

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

Source

pub fn export_share(&self) -> Result<String>

Export the session as a shareable JSON file. Writes to .whycodes/shares/{session_id}.json and returns the file path.

Source

pub fn export_markdown(&self) -> String

Export conversation as Markdown for sharing / /export.

Source

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.

Source

pub fn set_messages(&mut self, messages: Vec<Message>)

Replace the entire message list (used by undo/redo).

Source

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

Source

pub fn mark_branch(&mut self, name: &str) -> Result<String, BranchError>

Mark the current point under name (or branch N). Returns the name.

Source

pub fn tree_rows(&self) -> Vec<TreeRow>

The conversation as a tree of user turns, active path first.

Source

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 Clone for Session

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Session

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Session

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for Session

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more