Struct SessionStore
pub struct SessionStore {
pub session_queue: Arc<SessionActorQueue>,
/* private fields */
}Fields§
§session_queue: Arc<SessionActorQueue>Implementations§
Source§impl SessionStore
impl SessionStore
pub fn new(max_sessions: usize, session_queue: Arc<SessionActorQueue>) -> Self
pub async fn insert( &self, id: String, session: RpcSession, ) -> Result<u64, &'static str>
Sourcepub async fn insert_admitted(
&self,
admission: &SessionGuard,
id: String,
session: RpcSession,
) -> Result<u64, &'static str>
pub async fn insert_admitted( &self, admission: &SessionGuard, id: String, session: RpcSession, ) -> Result<u64, &'static str>
Publish a session while the caller already owns the session’s
admission permit. handle_session_new drives the whole incarnation —
predecessor wait, transcript load, build, publish, history restore —
under ONE permit acquisition: the transcript is only read after the
predecessor turn has fully finalized, and no prompt can be admitted
against the successor until the caller drops its guard after the
history restore. Re-acquiring the permit internally (as
SessionStore::insert does) would deadlock against
the caller’s guard.
This REPLACES any live incarnation already present under id:
rehydration legitimately swaps a same-ID successor for the published
session while holding the permit, so absence is not required here.
The session/new external boundary, which must never overwrite a
concurrent incarnation, uses
SessionStore::insert_admitted_if_absent
instead.
The caller must hold admission for exactly id (debug-asserted)
and must keep it alive until the published session is fully restored.
Sourcepub async fn insert_admitted_if_absent(
&self,
admission: &SessionGuard,
id: String,
session: RpcSession,
) -> Result<u64, &'static str>
pub async fn insert_admitted_if_absent( &self, admission: &SessionGuard, id: String, session: RpcSession, ) -> Result<u64, &'static str>
Absence-safe variant of SessionStore::insert_admitted
for the session/new external boundary, combining the admission
permit contract with
SessionStore::insert_if_absent’s refusal
to overwrite a live incarnation: under the caller’s permit the slot
was cleared (or never occupied) before the rebuild began, so a live
entry here means a concurrent resume won the race. Two concurrent
resume requests therefore cannot silently replace one another.
The caller must hold admission for exactly id (debug-asserted)
and must keep it alive until the published session is fully restored.
Sourcepub async fn insert_if_absent(
&self,
id: String,
session: RpcSession,
) -> Result<(), &'static str>
pub async fn insert_if_absent( &self, id: String, session: RpcSession, ) -> Result<(), &'static str>
Publish a newly constructed session only when no live incarnation is
already present. session/new uses this at the external boundary so
two concurrent resume requests cannot replace one another, and so a
session/new can never replace (and thereby hijack) an existing
session’s agent, whoever owns it.
Sourcepub async fn insert_for_prompt(
&self,
id: String,
session: RpcSession,
cancel_generation: u64,
) -> Result<(), &'static str>
pub async fn insert_for_prompt( &self, id: String, session: RpcSession, cancel_generation: u64, ) -> Result<(), &'static str>
Publish a rehydrated session and bind the admitted prompt’s cancel token to the new incarnation before the session becomes observable.
pub async fn get_agent(&self, id: &str) -> Option<Arc<Mutex<Agent>>>
Sourcepub async fn get_generation(&self, id: &str) -> Option<u64>
pub async fn get_generation(&self, id: &str) -> Option<u64>
Return the current generation for the session with id, or None if
the session is absent. Provider-refresh callers capture this value
before building a provider box and thread it through
apply_model_provider so stale work targeting a replaced session
becomes a no-op.
Sourcepub async fn generation_and_mode(&self, id: &str) -> Option<(u64, ChatMode)>
pub async fn generation_and_mode(&self, id: &str) -> Option<(u64, ChatMode)>
Snapshot the live session identity under one map lock.
Sourcepub async fn owner_generation_and_mode(
&self,
id: &str,
) -> Option<(Option<String>, u64, ChatMode)>
pub async fn owner_generation_and_mode( &self, id: &str, ) -> Option<(Option<String>, u64, ChatMode)>
The owning principal and generation of the LIVE incarnation under
id, read together under the store lock. Ownership is a property of
one incarnation: an operation that authorized against generation g
must find this exact pair again at its admission boundary, or the
record it authorized is not the record it is about to act on.
Sourcepub async fn matches_generation_and_mode(
&self,
id: &str,
expected: &(u64, ChatMode),
) -> bool
pub async fn matches_generation_and_mode( &self, id: &str, expected: &(u64, ChatMode), ) -> bool
Return whether id still names the captured live incarnation.
pub async fn touch(&self, id: &str)
pub async fn set_overrides( &self, id: &str, patch: SessionOverrides, ) -> Option<SessionOverrides>
Sourcepub async fn set_overrides_gated(
&self,
id: &str,
generation: u64,
patch: SessionOverrides,
) -> Option<SessionOverrides>
pub async fn set_overrides_gated( &self, id: &str, generation: u64, patch: SessionOverrides, ) -> Option<SessionOverrides>
Like set_overrides, but validates generation before mutating the
session entry or its agent. Returns None if the session is absent
or if it was replaced under the same ID — both cases mean the caller
captured a stale generation and the work must be discarded.
pub async fn preview_overrides( &self, id: &str, patch: &SessionOverrides, ) -> Option<SessionOverrides>
Sourcepub async fn migrate_model_provider_override(
&self,
id: &str,
generation: u64,
new_ref: String,
) -> bool
pub async fn migrate_model_provider_override( &self, id: &str, generation: u64, new_ref: String, ) -> bool
Rewrite a session’s in-memory model_provider override to a new
reference. SessionOverrides is transient session state rather than
config, so a provider alias rename does not reach it through the
config cascade. Without this migration the override would keep naming
an alias that no longer exists, and the next resolution would fall
back to compatibility values or fail while rebuilding the old
reference. Called from the live-refresh publication step while that
session’s model_provider_update guard is held.
generation must match the session’s current generation (same
contract as apply_model_provider). A same-ID successor inserted
by session/new or ACP rehydration while this refresh was in flight
would have a different generation, and the migration must be rejected
for it: the successor never requested an alias rename, and a stale
refresh must not write its override. Returns true when the migration
applied, false when the session was absent or its generation did not
match.
pub async fn get_overrides(&self, id: &str) -> Option<SessionOverrides>
Sourcepub async fn with_session_uploads<R>(
&self,
session_id: &str,
f: impl FnOnce(&mut HashMap<String, UploadEntry>) -> R,
) -> Option<R>
pub async fn with_session_uploads<R>( &self, session_id: &str, f: impl FnOnce(&mut HashMap<String, UploadEntry>) -> R, ) -> Option<R>
Run f on the upload index of the live session session_id, holding
the session map for the whole call. None, without calling f, when
no live session has that id.
f is synchronous, so the dedup lookup, the file write it guards, and
the index insert happen with no session change in between.
Sourcepub async fn with_incarnation_uploads<R>(
&self,
session_id: &str,
generation: u64,
owner: Option<&str>,
f: impl FnOnce(&mut HashMap<String, UploadEntry>) -> R,
) -> Option<R>
pub async fn with_incarnation_uploads<R>( &self, session_id: &str, generation: u64, owner: Option<&str>, f: impl FnOnce(&mut HashMap<String, UploadEntry>) -> R, ) -> Option<R>
Self::with_session_uploads for one exact incarnation: f runs
only while session_id still names the live session of generation
owned by owner. A session closed or deleted, recreated under the same
id, or re-owned since the caller resolved it yields None and f is
not called.
Sourcepub async fn live_incarnation(
&self,
session_id: &str,
) -> Option<(u64, Option<String>, String)>
pub async fn live_incarnation( &self, session_id: &str, ) -> Option<(u64, Option<String>, String)>
The live incarnation of session_id, read in one snapshot: its
generation, owning principal, and agent alias. None if no live
session has that id.
Sourcepub async fn get_workspace_dir(&self, session_id: &str) -> Option<String>
pub async fn get_workspace_dir(&self, session_id: &str) -> Option<String>
Get the workspace directory for a session.
Sourcepub async fn get_agent_alias(&self, session_id: &str) -> Option<String>
pub async fn get_agent_alias(&self, session_id: &str) -> Option<String>
Get the agent alias bound to a session, if known. Used by the dispatcher to route uploads to the agent’s own workspace dir rather than to the user’s session cwd (which is often a git repo we shouldn’t be writing into).
pub async fn seed_history(&self, id: &str, msgs: &[ChatMessage])
Whether the session’s in-memory agent currently believes its history
starts with the synthetic trim breadcrumb. None if the session is
unknown. Callers persist this alongside the transcript so a later
restore never has to infer provenance from message text.
Restore the session’s own canonical breadcrumb-provenance record onto its in-memory agent. No-op if the session is unknown.
pub async fn seed_history_with_event( &self, id: &str, msgs: &[ChatMessage], ) -> Option<TurnEvent>
Sourcepub async fn set_plan(&self, id: &str, entries: Vec<PlanEntry>)
pub async fn set_plan(&self, id: &str, entries: Vec<PlanEntry>)
Replace the session’s execution plan wholesale (TodoWrite whole-list semantics). No-op if the session is unknown.
Sourcepub async fn get_plan(&self, id: &str) -> Option<Vec<PlanEntry>>
pub async fn get_plan(&self, id: &str) -> Option<Vec<PlanEntry>>
Current stored plan for a session. None if the session is
unknown; Some(empty) if the session exists with no/cleared plan.
pub async fn seed_conversation_history( &self, id: &str, msgs: Vec<ConversationMessage>, )
pub async fn seed_conversation_history_with_event( &self, id: &str, msgs: Vec<ConversationMessage>, ) -> Option<TurnEvent>
pub async fn chat_mode(&self, id: &str) -> Option<ChatMode>
pub async fn history_len(&self, id: &str) -> Option<usize>
pub async fn history_slice_from( &self, id: &str, from: usize, ) -> Option<Vec<ConversationMessage>>
pub async fn remove(&self, id: &str) -> bool
Sourcepub async fn remove_generation(&self, id: &str, generation: u64) -> bool
pub async fn remove_generation(&self, id: &str, generation: u64) -> bool
Remove the live incarnation under id only if it is still the one
with generation. A successor installed under the same id after the
caller authorized its predecessor is left untouched, and the caller
learns the removal did not happen.
pub async fn evict_same_mode_sibling( &self, tui_id: &str, chat_mode: &ChatMode, except_id: &str, ) -> Vec<(String, String)>
Sourcepub async fn session_owner_tui_id(
&self,
session_id: &str,
) -> Option<Option<String>>
pub async fn session_owner_tui_id( &self, session_id: &str, ) -> Option<Option<String>>
Read the owner_tui_id stamp from a session. Returns None if the
session doesn’t exist, Some(None) if it exists but is unowned (e.g.
created by an anonymous connection), Some(Some(id)) if owned by id.
Sourcepub async fn session_owner_principal(
&self,
session_id: &str,
) -> Option<Option<String>>
pub async fn session_owner_principal( &self, session_id: &str, ) -> Option<Option<String>>
Read the owning-principal stamp from a LIVE session. Same tri-state
contract as Self::session_owner_tui_id.
Sourcepub async fn session_access_snapshot(
&self,
session_id: &str,
) -> Option<(u64, Option<String>)>
pub async fn session_access_snapshot( &self, session_id: &str, ) -> Option<(u64, Option<String>)>
Capture the incarnation and owner checked by a session-scoped request. Callers that wait on another ordering boundary can compare this with a later snapshot so an authorized request cannot act on a same-ID successor.
pub async fn list_ids(&self) -> Vec<String>
pub async fn list_ids_for_owner(&self, owner_tui_id: &str) -> Vec<String>
pub fn register_cancel_token(&self, id: &str, token: CancellationToken) -> u64
pub fn remove_cancel_token(&self, id: &str, generation: u64)
Sourcepub async fn cancel_all_inflight_and_wait(&self, cause: CancelCause)
pub async fn cancel_all_inflight_and_wait(&self, cause: CancelCause)
Cancel every active turn and wait until each owner has removed its registration. Earlier cancellation causes remain authoritative.
pub fn cancel_session(&self, id: &str) -> bool
Sourcepub fn abort_session(&self, id: &str) -> bool
pub fn abort_session(&self, id: &str) -> bool
Interrupt the session’s running turn on an operator’s behalf. Unlike
Self::cancel_session it is not tied to the owning client.
Sourcepub fn abort_session_for_generation(
&self,
id: &str,
session_generation: Option<u64>,
) -> bool
pub fn abort_session_for_generation( &self, id: &str, session_generation: Option<u64>, ) -> bool
Self::abort_session bound to the live session incarnation the
caller was authorized against: a turn belonging to any other
incarnation under the same id (a same-id successor) is left running.
Sourcepub async fn signal_cancellation_for_incarnation(
&self,
id: &str,
expected_generation: Option<u64>,
cause: CancelCause,
) -> Option<bool>
pub async fn signal_cancellation_for_incarnation( &self, id: &str, expected_generation: Option<u64>, cause: CancelCause, ) -> Option<bool>
Signal only the session incarnation observed by the caller.
Holding the session map lock across the generation check and token signal prevents a same-ID replacement from receiving a stale request’s cancellation.
Sourcepub fn signal_session_removal(&self, id: &str) -> bool
pub fn signal_session_removal(&self, id: &str) -> bool
Signal an in-flight turn before a close/delete handler waits for the session admission permit. The handler removes the session only after the admitted prompt has finalized under its original incarnation.
Sourcepub fn signal_session_removal_for_generation(
&self,
id: &str,
session_generation: Option<u64>,
) -> bool
pub fn signal_session_removal_for_generation( &self, id: &str, session_generation: Option<u64>, ) -> bool
Signal removal only when the in-flight turn belongs to the captured live session incarnation. A stale close must not cancel a same-ID successor that registered its prompt before the close acquired admission.
Sourcepub fn signal_session_kill_for_generation(
&self,
id: &str,
session_generation: Option<u64>,
) -> bool
pub fn signal_session_kill_for_generation( &self, id: &str, session_generation: Option<u64>, ) -> bool
Signal an administrative kill only when the in-flight turn belongs to the captured live session incarnation.
Sourcepub fn signal_session_kill(&self, id: &str) -> bool
pub fn signal_session_kill(&self, id: &str) -> bool
Signal an in-flight turn before an administrative kill waits for the session admission permit.
Sourcepub fn steer_session_for_generation(
&self,
id: &str,
session_generation: Option<u64>,
input: SteeringInput,
) -> SteerOutcome
pub fn steer_session_for_generation( &self, id: &str, session_generation: Option<u64>, input: SteeringInput, ) -> SteerOutcome
Queue a steering message for the session’s running turn, but only if that turn belongs to the live session incarnation the caller was authorized against. The turn’s registration (which records the incarnation it runs in) and its steering sender are read and the message is queued in one critical section, so a same-id successor that replaced the authorized incarnation never receives it.
Sourcepub fn has_inflight_turn(&self, id: &str) -> bool
pub fn has_inflight_turn(&self, id: &str) -> bool
Returns true if a cancel token is registered — i.e. a turn is in flight.
Sourcepub fn inflight_turn_generation(&self, id: &str) -> Option<u64>
pub fn inflight_turn_generation(&self, id: &str) -> Option<u64>
Generation of the runtime-owned turn currently executing for a session. This is the authoritative live-turn identity for RPC status; persisted session metadata is not updated on every RPC turn.
pub async fn kill_session(&self, id: &str) -> bool
Sourcepub async fn kill_session_generation(&self, id: &str, generation: u64) -> bool
pub async fn kill_session_generation(&self, id: &str, generation: u64) -> bool
Self::kill_session bound to one incarnation: kills only if the
live record under id still carries generation.
Sourcepub fn record_cancel_cause(&self, id: &str, cause: CancelCause)
pub fn record_cancel_cause(&self, id: &str, cause: CancelCause)
Record the cause for an imminent cancel-token fire. Call immediately before firing so the verdict site can attribute the cancel.
Sourcepub fn record_cancel_cause_if_absent(&self, id: &str, cause: CancelCause)
pub fn record_cancel_cause_if_absent(&self, id: &str, cause: CancelCause)
Record a fallback cause without overwriting a more specific cancel that won the race (for example an explicit client cancel immediately before the transport disconnects).
Sourcepub fn take_cancel_cause(&self, id: &str) -> Option<CancelCause>
pub fn take_cancel_cause(&self, id: &str) -> Option<CancelCause>
Drain the recorded cancel cause for a session. Returns None only
when no cancel actually fired (clean completion); every firing path
records before token.cancel(), so Some(_) after a fired token is
the invariant the verdict audit relies on.
pub async fn count(&self) -> usize
Sourcepub async fn count_by_agent(&self) -> HashMap<String, usize>
pub async fn count_by_agent(&self) -> HashMap<String, usize>
Count active sessions grouped by agent alias.
Auto Trait Implementations§
impl !Freeze for SessionStore
impl !RefUnwindSafe for SessionStore
impl !UnwindSafe for SessionStore
impl Send for SessionStore
impl Sync for SessionStore
impl Unpin for SessionStore
impl UnsafeUnpin for SessionStore
Blanket Implementations§
§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
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
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> 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