Skip to main content

SessionState

Struct SessionState 

Source
pub struct SessionState {
Show 32 fields pub canonical_checkpoint: Option<Box<CanonicalSession>>, pub save_gate: Arc<SaveGate>, pub last_save_failed: bool, pub id: String, pub app_session_id: Option<String>, pub version: u32, pub started_at: DateTime<Utc>, pub updated_at: DateTime<Utc>, pub project_root: Option<String>, pub shell_cwd: Option<String>, pub task: Option<TaskInfo>, pub findings: Vec<Finding>, pub decisions: Vec<Decision>, pub handoff_context: Vec<KnowledgeFact>, pub files_touched: Vec<FileTouched>, pub test_results: Option<TestSnapshot>, pub progress: Vec<ProgressEntry>, pub next_steps: Vec<String>, pub evidence: Vec<EvidenceRecord>, pub intents: Vec<IntentRecord>, pub active_structured_intent: Option<StructuredIntent>, pub stats: SessionStats, pub terse_mode: bool, pub compression_level: String, pub last_consolidate_ts: Option<DateTime<Utc>>, pub last_aaak_hash: Option<String>, pub extra_roots: Vec<String>, pub live_zone: LiveZoneSessionState, pub playbook: Playbook, pub last_semantic_query: Option<String>, pub taint: InputTaint, pub last_flush: Option<Instant>,
}
Expand description

Persistent session state tracking task, findings, files, decisions, and stats.

Fields§

§canonical_checkpoint: Option<Box<CanonicalSession>>

Present only after admitted canonical adoption or validated local reload. Public legacy serializers remain projections, not a second authority.

§save_gate: Arc<SaveGate>§last_save_failed: bool§id: String§app_session_id: Option<String>

The launching app’s session id (LEAN_CTX_SESSION_ID) when this session was opened under one: attribution for session stats.

§version: u32§started_at: DateTime<Utc>§updated_at: DateTime<Utc>§project_root: Option<String>§shell_cwd: Option<String>§task: Option<TaskInfo>§findings: Vec<Finding>§decisions: Vec<Decision>§handoff_context: Vec<KnowledgeFact>§files_touched: Vec<FileTouched>§test_results: Option<TestSnapshot>§progress: Vec<ProgressEntry>§next_steps: Vec<String>§evidence: Vec<EvidenceRecord>§intents: Vec<IntentRecord>§active_structured_intent: Option<StructuredIntent>§stats: SessionStats§terse_mode: bool

When true, resume / compaction prompts encourage concise model replies.

§compression_level: String

Unified compression level label (off/lite/standard/max).

§last_consolidate_ts: Option<DateTime<Utc>>

Watermark: timestamp of last auto-consolidation to prevent duplicate knowledge entries.

§last_aaak_hash: Option<String>

Retired, kept so stored sessions keep their shape: no delivery path reads or writes it. The project memory in the initialize instructions is always given in full (see instructions.rs).

§extra_roots: Vec<String>

Extra project roots for multi-root workspaces. Populated from config extra_roots and/or MCP roots/list.

§live_zone: LiveZoneSessionState

Explicit live-zone freeze state. This is intentionally process-local: provider cache prefixes must be re-established after a restart.

§playbook: Playbook

ACE delta playbook (#541): incremental, stable-ID checkpoint entries — grown by ctx_compress, never rewritten (anti context-collapse).

§last_semantic_query: Option<String>

Last ctx_semantic_search query (#542): fallback query source for query-conditioned IB compression when no explicit task is set.

§taint: InputTaint

The untrusted origins this session has read (pz21 §1.7). Written through by session::taint at the release point; an older session file without it reads untainted, which is safe (the taint only restricts).

§last_flush: Option<Instant>

#717: in-memory timestamp of the last flush to disk. Backs the time-based save trigger so a slow trickle of changes (below the 5-change batch) still reaches disk — the dashboard reads session JSONs and otherwise showed “idle” for the whole batch window.

Implementations§

Source§

impl SessionState

Source

pub fn canonical_checkpoint_for_export( id: &str, ) -> Result<(CanonicalCheckpoint, String), String>

Export only a validated committed checkpoint, never a pending view or legacy projection.

Source

pub fn from_storage_json(json: &str) -> Result<Self, String>

Decode the current canonical storage envelope or the historical flat shape. This is local state, never signer admission or a policy/entitlement grant.

Source§

impl SessionState

Source

pub fn format_compact(&self) -> String

Formats the session state as a compact multi-line summary for agent context.

Source

pub fn format_compact_delivered(&self) -> String

Self::format_compact as the reply of a tool that declares its sources (ctx_session status). Findings publish their sources or mark the call’s capture themselves (Self::finding_served); every other session text in the summary — task, decisions, touched files, test results, next steps, playbook — was typed or derived without a source binding, so a summary that carries any of it marks the capture incomplete. What remains unmarked is fixed text, the session counters and the project root the request is bound to.

Source

pub fn build_compaction_snapshot(&self) -> String

Builds a size-limited XML snapshot of session state for context compaction.

Source

pub fn save_compaction_snapshot(&self) -> Result<String, String>

Writes the compaction snapshot to disk and returns the snapshot string.

Source

pub fn load_compaction_snapshot(session_id: &str) -> Option<String>

Loads a previously saved compaction snapshot by session ID.

Source

pub fn load_latest_snapshot() -> Option<String>

Loads the most recently modified compaction snapshot from disk.

When a project root can be derived from CWD, only snapshots whose embedded session data matches the project root are considered. This prevents cross-project snapshot leakage.

Source

pub fn build_resume_block(&self) -> String

Build a compact resume block for post-compaction injection. Max ~500 tokens. Includes task, decisions, files, and archive references.

Source§

impl SessionState

Source

pub fn add_finding_for_file( &mut self, file: Option<&str>, line: Option<u32>, summary: &str, )

Self::add_finding for a finding an agent records for file: the finding is bound to that file as the admitted reader returns it now. No file, a file that cannot be bound (missing, outside the project, not permitted, restricted) or a session without a project root: the finding is recorded unbound.

Source

pub fn add_finding_from_call( &mut self, file: Option<&str>, summary: &str, call_sources: Option<&ArchiveAuthority>, )

Self::add_finding for a finding extracted from the output of the current tool call. call_sources is the call’s captured source set and is passed only when the tool declares every source: the finding is then bound to exactly those files. None, or an incomplete set: recorded unbound.

Source

pub fn finding_served(&self, finding: &Finding) -> bool

Whether finding of this session may be delivered now, for every renderer that prints a finding (archive::bindings::serve): a finding bound to sources is re-authorized for the session’s project — false means it must be left out — and, served, publishes its files into the call’s capture; a finding without a binding marks the capture incomplete and is delivered as before. Without protection every finding is delivered and nothing is published.

Source§

impl SessionState

Source

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

Return the current project root only when it is an absolute, non-broad root normalized with the same path rules as scoped session loading.

Source

pub fn decision_count_this_week() -> u64

Counts locally recorded decisions from the trailing seven days.

Source

pub fn save(&mut self) -> Result<(), String>

Serializes and writes the session state to disk synchronously.

Source

pub fn save_new(&mut self) -> Result<(), String>

Import publication never replaces an existing session archive. A failure after primary publication may retain a new recovery candidate, not erase the previous live session or source archive.

Source

pub fn prepare_save(&mut self) -> Result<PreparedSave, String>

Take a detached copy of the session for a later save. Safe to call while holding the live session lock: it validates the ID and copies memory — no file, configuration or policy input is read and nothing here can wait on a policy refresh (#1783). The unsaved counter is retained until a write is acknowledged.

The copy is not admitted for storage. Project-root checks, storage admission, serialization and the commit happen in PreparedSave::write_to_disk(), which belongs after the lock is released (a background thread, or save_shared). A session whose project root is unsafe, deleted or unreadable therefore prepares here and is refused there, with nothing written.

Source

pub fn load_recent_for_project_root( project_root: &str, limit: usize, ) -> Vec<Self>

Load the bounded warm-history set for one safe project root.

There is intentionally no legacy full-store fallback: cache warming is optional, while scanning every persisted session on each MCP launch is not acceptable under concurrent agent load. New saves populate the index; legacy sessions remain available through explicit session tools.

Source

pub fn load_latest() -> Option<Self>

Loads the most recent session matching the current working directory’s project root.

Returns None (a fresh session) rather than falling back to the global latest.json pointer: that unconditional fallback bypassed project-root matching and was the root cause of cross-project session leakage — one project’s findings/decisions/knowledge bleeding into another project’s first session. The correct project session is loaded later from the MCP roots handshake (load_latest_for_project_root).

Also refuses to scope to a broad/unsafe cwd (e.g. the MCP daemon’s HOME), which would otherwise resurrect the contaminated “HOME mega-session”.

Source

pub fn load_global_latest_pointer() -> Option<Self>

Loads the session referenced by the global latest.json pointer, regardless of project. Intended only for explicit, cross-project UX (e.g. lean-ctx session status from an arbitrary directory) — never for injecting knowledge into a new project’s context. Prefer load_latest.

Source

pub fn load_latest_for_project_root(project_root: &str) -> Option<Self>

Loads the most recent session matching a specific project root.

A valid per-project index is the only nominal path: one index read and one session read, independent of the global session-store cardinality. A missing, corrupt, or stale index is repaired by one exceptional scan.

Source

pub fn load_by_id_for_project_root( id: &str, expected_root: &str, ) -> Result<Option<Self>, SessionScopeError>

Loads a specific session only when its persisted root already matches the receiving root; legacy normalization runs after that check.

Source

pub fn load_by_id(id: &str) -> Option<Self>

Loads a specific session from disk by its unique ID.

Source

pub fn delete_session(id: &str) -> Result<bool, String>

Deletes a saved session and its compaction snapshot.

If the deleted session is the global latest pointer, the pointer is moved to the newest remaining session or removed when none remain.

Source

pub fn list_sessions() -> Vec<SessionSummary>

Lists all saved sessions as summaries, sorted by most recently updated.

Source

pub fn load_attributed(id: &str) -> Vec<Self>

The saved sessions id names: the session with that id and every session opened under the app session id id (app_session_id), oldest first (then by id).

Source

pub fn doctor_quarantine_unsafe_roots( apply: bool, ) -> (Vec<(String, String)>, usize)

Scans all saved sessions for contaminated ones — those rooted at a broad/unsafe path (HOME, filesystem root, agent sandbox dir) without a real project marker, i.e. the historic “HOME mega-session” artifact.

Returns (found, quarantined) where found is (id, root) pairs. When apply is true, each offending session file is moved to a sessions/quarantine/ subdirectory (non-destructive) instead of being loaded into any project’s context.

Source

pub fn cleanup_old_sessions(max_age_days: i64) -> u32

Deletes sessions older than max_age_days, preserving the most recent session for every project root. Returns the count removed.

This is an explicit retention operation, so its single store scan stays off the command hot path.

Source§

impl SessionState

Source

pub async fn save_shared(owner: Arc<RwLock<Self>>) -> Result<(), String>

Flush the shared live owner without holding its state lock during disk I/O. The per-owner lane covers preparation through acknowledgement, not state.

Source

pub fn save_shared_sync( owner: &Arc<RwLock<Self>>, ) -> Result<SharedSaveReceipt, String>

Synchronous MCP adapter with the same save lane, detached projection, version check, and owner acknowledgement as save_shared.

Source

pub async fn save_shared_logged(owner: Arc<RwLock<Self>>)

Source

pub async fn save_shared_at_shutdown(owner: Arc<RwLock<Self>>) -> bool

The save of a server that is ending: the same save, behind the same lane. Background followers are told to yield (SaveGate); a save already in flight is waited for up to SHUTDOWN_SAVE_LANE_WAIT.

true: the owner’s current version is acknowledged as durable. false: it is not, and the log says why. A save that failed (lane not free in time, admission refused, authority changed, commit failed) is not repeated blindly — its changes stay pending as after any failed save, and there is no later save in this process to pick them up. A session that CHANGED while it was being saved is saved once more.

Source§

impl SessionState

Source

pub fn new() -> Self

Creates a new session with a unique ID and current timestamp.

Source

pub fn refresh_handoff_context(&mut self)

Source

pub fn increment(&mut self)

Bumps the version counter and marks the session as dirty.

Source

pub fn should_save(&self) -> bool

Returns true if enough changes have accumulated to warrant a disk save — or, since #717, if any change has waited longer than SESSION_FLUSH_INTERVAL: the 5-change batch alone left slow sessions invisible to the dashboard (stuck “idle”) for the whole batch window. A fresh in-memory session flushes its first change immediately so new activity surfaces at once.

Source

pub fn set_task(&mut self, description: &str, intent: Option<&str>)

Sets the active task and infers a structured intent from the description.

Source

pub fn auto_infer_task(&mut self)

Auto-infers the task from available context (plans, git diff, file patterns). Never overwrites a stated task; an earlier auto-inferred one is re-derived, so a label from a past session or a since-committed working tree does not linger as “the task” forever.

Source

pub fn add_finding( &mut self, file: Option<&str>, line: Option<u32>, summary: &str, )

Records a finding (discovery or observation) in the session log.

Source

pub fn add_decision(&mut self, summary: &str, rationale: Option<&str>)

Records a design or implementation decision with optional rationale.

Source

pub fn touch_file( &mut self, path: &str, file_ref: Option<&str>, mode: &str, tokens: usize, )

Records a file read/access in the session, incrementing its read count.

Source

pub fn mark_modified(&mut self, path: &str)

Marks a previously touched file as modified (written to).

Source

pub fn set_file_summary(&mut self, path: &str, summary: &str)

Sets a one-line content summary for a touched file (max 80 chars).

Source

pub fn record_tool_call(&mut self, tokens_saved: u64, tokens_input: u64)

Increments the tool call counter and accumulates token savings.

Source

pub fn record_intent(&mut self, intent: IntentRecord)

Records an inferred or explicit intent, coalescing consecutive duplicates.

Source

pub fn record_tool_receipt( &mut self, tool: &str, action: Option<&str>, input_md5: &str, output_md5: &str, agent_id: Option<&str>, client_name: Option<&str>, )

Appends an auditable evidence record for a tool invocation.

Source

pub fn record_tool_receipt_with_task( &mut self, tool: &str, action: Option<&str>, input_md5: &str, output_md5: &str, agent_id: Option<&str>, client_name: Option<&str>, task_id: Option<&str>, )

Records a tool receipt with optional MCP task lineage.

Source

pub fn record_manual_evidence(&mut self, key: &str, value: Option<&str>)

Appends a manual (non-tool) evidence record to the audit log.

Source

pub fn has_evidence_key(&self, key: &str) -> bool

Returns true if an evidence record with the given key exists.

Source

pub fn record_cache_hit(&mut self)

Increments the session-level cache hit counter.

Source

pub fn record_command(&mut self)

Increments the session-level command counter.

Source

pub fn effective_cwd(&self, explicit_cwd: Option<&str>) -> String

Returns the effective working directory for shell commands. Priority: explicit cwd arg > session shell_cwd > project_root > process cwd. Explicit CWD and stored shell_cwd are jail-checked against the project root to prevent MCP clients from escaping the workspace.

Source

pub fn effective_cwd_checked( &self, explicit_cwd: Option<&str>, ) -> (String, Option<String>)

Like Self::effective_cwd, but also reports why an explicit cwd request was rejected by the project-root jail and silently replaced with the project root (#629).

The jail itself is deliberate sandboxing (it stops MCP clients escaping the workspace) and must stay — the only gap was that the substitution was silent, so a caller running pwd && ls in what they think is dir A actually ran in the project root with no indication why. Callers that surface output to a human/agent (e.g. ctx_shell) use the returned Option<String> reason to append a one-line hint instead of letting the swap pass unnoticed; effective_cwd keeps the original lossless behaviour.

Source

pub fn execution_cwd( &self, explicit_cwd: Option<&str>, ) -> Result<String, String>

The directory a command is to RUN in, or the reason there is none.

Unlike Self::effective_cwd_checked nothing is substituted: an explicit cwd the project jail rejects is an error, so the command is not executed, backgrounded or cached under a directory the caller did not name. The stored shell cwd is held to the same jail — it was accepted when it was stored, but the project root may have changed since. Without a project root there is no jail to hold it to, and the stored value is used as before.

Source

pub fn note_explicit_cwd(&mut self, cwd: &str)

Persist an explicit, jail-accepted cwd argument as the live shell cwd (#707). update_shell_cwd only tracks cd inside the command text, but clients that switch checkouts mid-session (Claude Code after EnterWorktree) pass the new directory as the cwd param of every subsequent call — without persisting it, the worktree-divergence detection in path resolution never sees the switch. Callers must pass a cwd that already passed the project-root jail.

Source

pub fn update_shell_cwd(&mut self, command: &str)

Updates shell_cwd by detecting cd in the command. Handles: cd /abs/path, cd rel/path (relative to current cwd), cd .., and chained commands like cd foo && .... The new CWD is jail-checked against the project root.

Trait Implementations§

Source§

impl Clone for SessionState

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 SessionState

Source§

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

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

impl Default for SessionState

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for SessionState

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 SessionState

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

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

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

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