Skip to main content

ClientSession

Struct ClientSession 

Source
pub struct ClientSession {
Show 25 fields pub client_id: String, pub runtime: Arc<Runtime>, pub channel: Arc<WsChannel>, pub host: Arc<HostState>, pub memgine: Arc<Mutex<MemgineEngine>>, pub browser: BrowserSessionSlot, pub authenticated: Arc<AtomicBool>, pub negotiated_protocol_version: AtomicU32, pub negotiated_capabilities: Arc<RwLock<BTreeSet<String>>>, pub inference_control: Arc<InferenceRegistry>, pub is_host: AtomicBool, pub agent_id: Arc<Mutex<Option<String>>>, pub agent_method_allowlist: Arc<RwLock<Option<BTreeSet<String>>>>, pub callback_tool_schema_digests: Arc<RwLock<HashMap<String, String>>>, pub memory_namespace: Mutex<Option<String>>, pub bound_memgine: Mutex<Option<Arc<Mutex<MemgineEngine>>>>, pub current_run_id: Mutex<Option<String>>, pub run_lifecycle_guard: Arc<Mutex<()>>, pub permission_gate: Arc<RwLock<PermissionGate>>, pub halted: Arc<AtomicBool>, pub evolution_guard: CycleGuard, pub last_chat_turn: Mutex<Option<LastChatTurn>>, pub chat_inflight: AtomicUsize, pub tenant: Mutex<Option<String>>, pub tool_stream_subscribed: Arc<AtomicBool>,
}
Expand description

Per-client session.

Fields§

§client_id: String§runtime: Arc<Runtime>§channel: Arc<WsChannel>§host: Arc<HostState>§memgine: Arc<Mutex<MemgineEngine>>

Memgine handle. Wrapped in tokio::sync::Mutex so dispatcher handlers can hold the lock across .await points without risking poisoning. Migrated from std::sync::Mutex in the car-server-core extraction (U1) per the “one-wrapper rule”.

§browser: BrowserSessionSlot

Lazy browser session — first browser.run call launches Chromium, subsequent calls reuse it so element IDs resolve across invocations within the same WebSocket connection.

§authenticated: Arc<AtomicBool>

Per-connection auth state. Starts false; flips to true after a successful session.auth handshake. Always considered authenticated when ServerState::auth_token is unset (auth disabled). Closes Parslee-ai/car-releases#32.

§negotiated_protocol_version: AtomicU32

Negotiated daemon JSON-RPC protocol for this WebSocket connection. Starts at 0 (unnegotiated) and is set to car_proto::PROTOCOL_VERSION only after an exact-version server.handshake. The state is connection-scoped by construction, so a reconnect always has to negotiate again before using handshake-gated host/auth methods.

§negotiated_capabilities: Arc<RwLock<BTreeSet<String>>>

Capability names negotiated by this connection’s authenticated server.handshake. Connection-scoped for the same reason as the wire version: a reconnect cannot inherit another socket’s proof.

§inference_control: Arc<InferenceRegistry>

Bounded lifecycle state for inferences started on this exact socket. Keeping it per-session prevents an authenticated peer from probing IDs owned by another connection.

§is_host: AtomicBool

Host-management role (Parslee-ai/car#254). Starts false; flips to true only when the connection presents the per-launch host token via session.auth { host_token } (validated against ServerState::host_token). authorize_run_access requires this for cross-agent run-trace reads — being merely host.subscribed is no longer sufficient, which is what closes the self-elevation hole. Cleared implicitly when the connection drops.

§agent_id: Arc<Mutex<Option<String>>>

Bound agent identity (#169). Some(id) once a lifecycle-agent child has called session.auth { token, agent_id } and the supervisor confirmed agent_id is supervised + token matches. Used by agents.list to surface which managed agents have actually attached vs. just being marked Running at the process level. Cleared at disconnect by remove_session.

§agent_method_allowlist: Arc<RwLock<Option<BTreeSet<String>>>>

Optional daemon-method allowlist bound by successful supervised-agent authentication. None is the backward-compatible unrestricted state; Some(empty) denies every application method. The dispatcher reads it once before any method-specific handler or notification interceptor.

§callback_tool_schema_digests: Arc<RwLock<HashMap<String, String>>>

Canonical schema digests for the narrow reverse-callback tools this client registered. Server-owned registry entries never land here.

§memory_namespace: Mutex<Option<String>>

Bound memory namespace (session.auth { memory_namespace }, #79/#80).

The namespace identity has to survive the handshake, not just the lookup that binds bound_memgine. Without it the daemon knows which graph a session is using but not what to call it, so nothing can write that graph back to ~/.car/memory/memory-namespaces/<encoded-ns>.json — which is why namespace memory was durable only until the next restart (car-releases#82). Paired with bound_memgine exactly as agent_id is.

§bound_memgine: Mutex<Option<Arc<Mutex<MemgineEngine>>>>

Bound persistent memgine (#170). Some after session.auth successfully attaches the connection to a daemon-owned per-agent memgine (paired with agent_id). Memory handlers route through ClientSession::effective_memgine which returns this when set, falling back to the ephemeral memgine field for browser/host/CLI connections.

§current_run_id: Mutex<Option<String>>

The run currently bracketed on this connection (agent run tracing, U1). Set by runs.start before that handler responds, so the per-turn recorder (U2) always reads the run_id the bracket established — no race across the concurrently-spawned dispatch tasks (KTD3). Cleared by runs.complete. On disconnect with a still-set current run and no recorded terminal, the daemon marks it Incomplete (R5).

§run_lifecycle_guard: Arc<Mutex<()>>

Serializes the authenticated run bracket on one WebSocket: start, proposal lifecycle, complete, and disconnect terminalization cannot interleave and produce ambiguous journal ordering.

§permission_gate: Arc<RwLock<PermissionGate>>

Per-session permission-tier gate (survey §3.4.3/§5.2.5). Backs the permission.* JSON-RPC methods: the session holds the granted standing tier + risk classifier. Defaults to SandboxEdit, the tier docs/websocket-protocol.md publishes as the session default.

Arc, not a bare lock, because this is also the gate crate::permission_gate::PermissionAdmissionGate enforces at proposal admission (Parslee-ai/car#890). ONE gate object behind both: a second instance would let the advisory permission.evaluate and the enforcement point disagree about the same action, which is precisely the bug class the admission gate was added to close.

NOTE (kernel review C1): approval records do NOT live here. The gate’s embedded ledger is deliberately unused in the daemon — every fingerprint-keyed HITL read/write goes through the SHARED ServerState::approval_ledger (journal-backed), so an approval recorded on one connection is visible to every other and survives restart. Tier state stays per-session; the approval store is daemon-wide.

§halted: Arc<AtomicBool>

Connection-local fail-stop latch. A typed terminal tool callback sets it after its proposal has aborted and rolled back; the admission gate sharing this exact atomic rejects every later proposal on this socket. It is deliberately not durable and is discarded on reconnect.

§evolution_guard: CycleGuard

Non-overlap guard for evolution.run on this session (kernel review S3): the dispatcher spawns requests concurrently even on one connection, and two interleaved cycles would double-dispatch the evolution mechanisms. A second evolution.run while one is in flight errs instead of overlapping.

§last_chat_turn: Mutex<Option<LastChatTurn>>

Last assistant turn produced on this session’s chat path, so the next user turn can be scored as a conversation outcome (Part B). None until the first chat reply; only set for declared-chat inference.

§chat_inflight: AtomicUsize

In-flight chat-infer count. Concurrent infers on one session have no well-defined turn adjacency (the dispatcher spawns a task per frame), so the outcome scorer only records a turn that began while the session was idle — otherwise it would score one infer’s trace against an unrelated concurrent user turn (an active mislabel of routing stats). neo #1.

§tenant: Mutex<Option<String>>

Tenant identity bound at session.auth { tenant_id } (linus review C-4). Once bound, every tenant-scoped handler uses THIS identity: a per-request tenant_id param may restate it but a mismatch is rejected — an authenticated connection cannot hop into another tenant’s namespace by editing request params. None (unbound) preserves the legacy per-request behavior.

§tool_stream_subscribed: Arc<AtomicBool>

Whether this connection has a live tools.stream.event forwarder task (C2). Set by tools.stream.subscribe; a concurrent second subscribe is a no-op instead of spawning a duplicate forwarder (which would double every notification). Arc so the forwarder task can RESET it on exit (write timeout on a stalled-but- recovering socket, broadcast closed) — a later re-subscribe then spawns a fresh forwarder rather than silently no-op’ing (Q2).

Implementations§

Source§

impl ClientSession

Source

pub async fn effective_memgine(&self) -> Arc<Mutex<MemgineEngine>>

Returns the memgine handle the memory.* handlers should use: the bound per-agent memgine when this session attached via session.auth { agent_id } (#169 + #170), otherwise the ephemeral per-WS memgine. Cheap (one async lock + Arc clone).

Source

pub async fn bind_run_journal(&self, run_id: &str) -> Result<(), String>

Source

pub async fn require_run_journal_binding( &self, run_id: &str, ) -> Result<(), String>

Source

pub async fn clear_run_journal_binding( &self, run_id: &str, ) -> Result<(), String>

Source

pub async fn append_run_terminal_event( &self, ended: &RunEnded, ) -> Result<(), String>

Source

pub async fn append_run_cancellation_requested_event( &self, requested: &RunCancellationRequested, ) -> Result<(), String>

Source

pub async fn append_run_cancellation_result_event( &self, result: &RunCancelResponse, ) -> Result<(), String>

Source

pub async fn append_run_started_event( &self, started: &RunStarted, ) -> Result<(), String>

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<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

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<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

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, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

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