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: boolWhen true, resume / compaction prompts encourage concise model replies.
compression_level: StringUnified 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: LiveZoneSessionStateExplicit live-zone freeze state. This is intentionally process-local: provider cache prefixes must be re-established after a restart.
playbook: PlaybookACE 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: InputTaintThe 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
impl SessionState
Sourcepub fn canonical_checkpoint_for_export(
id: &str,
) -> Result<(CanonicalCheckpoint, String), String>
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.
Sourcepub fn from_storage_json(json: &str) -> Result<Self, String>
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
impl SessionState
Sourcepub fn format_compact(&self) -> String
pub fn format_compact(&self) -> String
Formats the session state as a compact multi-line summary for agent context.
Sourcepub fn format_compact_delivered(&self) -> String
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.
Sourcepub fn build_compaction_snapshot(&self) -> String
pub fn build_compaction_snapshot(&self) -> String
Builds a size-limited XML snapshot of session state for context compaction.
Sourcepub fn save_compaction_snapshot(&self) -> Result<String, String>
pub fn save_compaction_snapshot(&self) -> Result<String, String>
Writes the compaction snapshot to disk and returns the snapshot string.
Sourcepub fn load_compaction_snapshot(session_id: &str) -> Option<String>
pub fn load_compaction_snapshot(session_id: &str) -> Option<String>
Loads a previously saved compaction snapshot by session ID.
Sourcepub fn load_latest_snapshot() -> Option<String>
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.
Sourcepub fn build_resume_block(&self) -> String
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
impl SessionState
Sourcepub fn add_finding_for_file(
&mut self,
file: Option<&str>,
line: Option<u32>,
summary: &str,
)
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.
Sourcepub fn add_finding_from_call(
&mut self,
file: Option<&str>,
summary: &str,
call_sources: Option<&ArchiveAuthority>,
)
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.
Sourcepub fn finding_served(&self, finding: &Finding) -> bool
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
impl SessionState
Sourcepub fn strict_project_root(&self) -> Option<String>
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.
Sourcepub fn decision_count_this_week() -> u64
pub fn decision_count_this_week() -> u64
Counts locally recorded decisions from the trailing seven days.
Sourcepub fn save(&mut self) -> Result<(), String>
pub fn save(&mut self) -> Result<(), String>
Serializes and writes the session state to disk synchronously.
Sourcepub fn save_new(&mut self) -> Result<(), String>
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.
Sourcepub fn prepare_save(&mut self) -> Result<PreparedSave, String>
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.
Sourcepub fn load_recent_for_project_root(
project_root: &str,
limit: usize,
) -> Vec<Self>
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.
Sourcepub fn load_latest() -> Option<Self>
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”.
Sourcepub fn load_global_latest_pointer() -> Option<Self>
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.
Sourcepub fn load_latest_for_project_root(project_root: &str) -> Option<Self>
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.
Sourcepub fn load_by_id_for_project_root(
id: &str,
expected_root: &str,
) -> Result<Option<Self>, SessionScopeError>
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.
Sourcepub fn load_by_id(id: &str) -> Option<Self>
pub fn load_by_id(id: &str) -> Option<Self>
Loads a specific session from disk by its unique ID.
Sourcepub fn delete_session(id: &str) -> Result<bool, String>
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.
Sourcepub fn list_sessions() -> Vec<SessionSummary>
pub fn list_sessions() -> Vec<SessionSummary>
Lists all saved sessions as summaries, sorted by most recently updated.
Sourcepub fn load_attributed(id: &str) -> Vec<Self>
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).
Sourcepub fn doctor_quarantine_unsafe_roots(
apply: bool,
) -> (Vec<(String, String)>, usize)
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.
Sourcepub fn cleanup_old_sessions(max_age_days: i64) -> u32
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
impl SessionState
Flush the shared live owner without holding its state lock during disk I/O. The per-owner lane covers preparation through acknowledgement, not state.
Synchronous MCP adapter with the same save lane, detached projection, version check, and owner acknowledgement as save_shared.
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
impl SessionState
pub fn refresh_handoff_context(&mut self)
Sourcepub fn should_save(&self) -> bool
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.
Sourcepub fn set_task(&mut self, description: &str, intent: Option<&str>)
pub fn set_task(&mut self, description: &str, intent: Option<&str>)
Sets the active task and infers a structured intent from the description.
Sourcepub fn auto_infer_task(&mut self)
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.
Sourcepub fn add_finding(
&mut self,
file: Option<&str>,
line: Option<u32>,
summary: &str,
)
pub fn add_finding( &mut self, file: Option<&str>, line: Option<u32>, summary: &str, )
Records a finding (discovery or observation) in the session log.
Sourcepub fn add_decision(&mut self, summary: &str, rationale: Option<&str>)
pub fn add_decision(&mut self, summary: &str, rationale: Option<&str>)
Records a design or implementation decision with optional rationale.
Sourcepub fn touch_file(
&mut self,
path: &str,
file_ref: Option<&str>,
mode: &str,
tokens: usize,
)
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.
Sourcepub fn mark_modified(&mut self, path: &str)
pub fn mark_modified(&mut self, path: &str)
Marks a previously touched file as modified (written to).
Sourcepub fn set_file_summary(&mut self, path: &str, summary: &str)
pub fn set_file_summary(&mut self, path: &str, summary: &str)
Sets a one-line content summary for a touched file (max 80 chars).
Sourcepub fn record_tool_call(&mut self, tokens_saved: u64, tokens_input: u64)
pub fn record_tool_call(&mut self, tokens_saved: u64, tokens_input: u64)
Increments the tool call counter and accumulates token savings.
Sourcepub fn record_intent(&mut self, intent: IntentRecord)
pub fn record_intent(&mut self, intent: IntentRecord)
Records an inferred or explicit intent, coalescing consecutive duplicates.
Sourcepub 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>,
)
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.
Sourcepub 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>,
)
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.
Sourcepub fn record_manual_evidence(&mut self, key: &str, value: Option<&str>)
pub fn record_manual_evidence(&mut self, key: &str, value: Option<&str>)
Appends a manual (non-tool) evidence record to the audit log.
Sourcepub fn has_evidence_key(&self, key: &str) -> bool
pub fn has_evidence_key(&self, key: &str) -> bool
Returns true if an evidence record with the given key exists.
Sourcepub fn record_cache_hit(&mut self)
pub fn record_cache_hit(&mut self)
Increments the session-level cache hit counter.
Sourcepub fn record_command(&mut self)
pub fn record_command(&mut self)
Increments the session-level command counter.
Sourcepub fn effective_cwd(&self, explicit_cwd: Option<&str>) -> String
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.
Sourcepub fn effective_cwd_checked(
&self,
explicit_cwd: Option<&str>,
) -> (String, Option<String>)
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.
Sourcepub fn execution_cwd(
&self,
explicit_cwd: Option<&str>,
) -> Result<String, String>
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.
Sourcepub fn note_explicit_cwd(&mut self, cwd: &str)
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.
Sourcepub fn update_shell_cwd(&mut self, command: &str)
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
impl Clone for SessionState
Source§impl Debug for SessionState
impl Debug for SessionState
Source§impl Default for SessionState
impl Default for SessionState
Source§impl<'de> Deserialize<'de> for SessionState
impl<'de> Deserialize<'de> for SessionState
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 !RefUnwindSafe for SessionState
impl !UnwindSafe for SessionState
impl Forget for SessionState
impl Freeze for SessionState
impl Move for SessionState
impl Send for SessionState
impl Sync for SessionState
impl Unpin for SessionState
impl UnsafeUnpin for SessionState
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
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>,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
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