Skip to main content

ServerState

Struct ServerState 

Source
pub struct ServerState {
Show 22 fields pub journal_dir: PathBuf, pub sessions: Mutex<HashMap<String, Arc<ClientSession>>>, pub inference: OnceLock<Arc<InferenceEngine>>, pub host: Arc<HostState>, pub shared_memgine: Option<Arc<Mutex<MemgineEngine>>>, pub voice_sessions: Arc<VoiceSessionRegistry>, pub meetings: Arc<MeetingRegistry>, pub a2ui: A2uiSurfaceStore, pub ui_agent: Arc<UIImprovementAgent>, pub ui_agent_oscillation: Arc<OscillationDetector>, pub ui_agent_budget: Arc<IterationBudget>, pub admission: Arc<InferenceAdmission>, pub a2ui_route_auth: Mutex<HashMap<String, A2aRouteAuth>>, pub supervisor: OnceLock<Arc<Supervisor>>, pub a2a_dispatcher: OnceLock<Arc<A2aDispatcher>>, pub a2ui_subscribers: Mutex<HashMap<String, Arc<WsChannel>>>, pub auth_token: OnceLock<String>, pub attached_agents: Mutex<HashMap<String, String>>, pub agent_memgines: Mutex<HashMap<String, Arc<Mutex<MemgineEngine>>>>, pub mcp_url: OnceLock<String>, pub mcp_sessions: OnceLock<Arc<SessionMap>>, pub approval_gate: ApprovalGate, /* private fields */
}
Expand description

Global server state shared across all connections.

Fields§

§journal_dir: PathBuf§sessions: Mutex<HashMap<String, Arc<ClientSession>>>§inference: OnceLock<Arc<InferenceEngine>>§host: Arc<HostState>§shared_memgine: Option<Arc<Mutex<MemgineEngine>>>

When Some, create_session clones this handle into every new ClientSession.memgine — embedders that want a single shared memgine across all WS sessions set this. Standalone car-server leaves it None, which gives each session its own engine (preserving today’s behavior).

§voice_sessions: Arc<VoiceSessionRegistry>

Process-wide voice session registry. Each voice.transcribe_stream.start call registers its own per-client WsVoiceEventSink so events route back to the originating WS connection only.

§meetings: Arc<MeetingRegistry>

Process-wide meeting registry. Meeting ids are global; each meeting binds to the originating client’s WS for upstream events but persists transcripts to the resolved .car/meetings/<id>/ regardless of which client started it.

§a2ui: A2uiSurfaceStore

Process-wide A2UI surface store. Agent-produced surfaces are visible to every host UI subscriber, independent of the WebSocket session that applied the update.

§ui_agent: Arc<UIImprovementAgent>

In-process UI-improvement agent. Invoked from handle_a2ui_render_report with each inbound report; returned Decision::Patch envelopes are applied via the standard apply_a2ui_envelope path so all subscribers see the patch. Arc so the agent’s interior DashMap state survives across handler calls even when ServerState is cheap-cloned.

§ui_agent_oscillation: Arc<OscillationDetector>

Per-surface oscillation detector for the UI-improvement loop. Sits between the agent’s Decision::Patch and the apply path so A→B→A patch cycles get cooled down without the agent itself having to track history. neo’s review: “controllers use workqueue backoff; reconcilers stay stateless.”

§ui_agent_budget: Arc<IterationBudget>

Per-surface iteration budget. Backstop against runaway loops the oscillation detector misses — caps total agent- driven patches per surface at DEFAULT_MAX_ITERATIONS.

§admission: Arc<InferenceAdmission>

Process-wide concurrency gate for inference RPC handlers. Sized from host RAM at startup, overridable via crate::admission::ENV_MAX_CONCURRENT. Without this, N concurrent users multiply KV-cache and activation memory and take the host out (#114-adjacent: filed alongside the daemon always-on rework). The semaphore lives on ServerState so it is shared across every WebSocket session in the same process.

§a2ui_route_auth: Mutex<HashMap<String, A2aRouteAuth>>

Server-side A2A continuation auth keyed by A2UI surface id. Kept out of A2uiSurface.owner so host renderers never see bearer/API-key material.

§supervisor: OnceLock<Arc<Supervisor>>

Lifecycle-managed agents — declarative manifest at ~/.car/agents.json driving spawn/restart/stop. Closes Parslee-ai/car-releases#27. Lazy-initialized so embedders that don’t want process supervision don’t pay the disk-touch cost at server start.

§a2a_dispatcher: OnceLock<Arc<A2aDispatcher>>

In-core A2A dispatcher — embedders that consume car-server-core get A2A reachability “for free” without standing up a separate HTTP listener. Closes Parslee-ai/car-releases#28. Lazy-init so the embedder can override the runtime / task store / agent card via ServerStateConfig::with_a2a_runtime etc. before the first dispatch.

§a2ui_subscribers: Mutex<HashMap<String, Arc<WsChannel>>>

WS clients subscribed to A2UI envelope events. After every successful a2ui.apply / a2ui.ingest, the resulting A2uiApplyResult is broadcast to every subscriber as an a2ui.event JSON-RPC notification. Closes Parslee-ai/car-releases#29. Subscribers register via the a2ui/subscribe method and are auto-cleaned on WS disconnect.

§auth_token: OnceLock<String>

Per-launch auth token. When Some, the WS dispatcher rejects non-auth methods on unauthenticated sessions until the client calls session.auth with the matching value. When None, auth is disabled and every connection works as before. Set at startup by car-server unless --no-auth is passed (default flipped 2026-05); embedders that want to enable auth call ServerState::install_auth_token. Closes Parslee-ai/car-releases#32.

§attached_agents: Mutex<HashMap<String, String>>

agent_id -> client_id map of currently-attached lifecycle agents (#169). Populated by the session.auth handler when a supervised child presents its agent_id + per-agent token; drained on disconnect by remove_session. Single-claim: a second connection presenting the same agent_id is rejected so the daemon-side per-agent state stays unambiguous.

§agent_memgines: Mutex<HashMap<String, Arc<Mutex<MemgineEngine>>>>

agent_id -> persistent memgine map (#170). Lazy-loaded on first connection per id from ~/.car/memory/agents/<id>.jsonl, retained across daemon restart, surviving any single disconnect/reconnect of the supervised child. Connections that auth without an agent_id (browser, host, ad-hoc CLI) keep the per-WS ephemeral memgine on ClientSession.memgine — no behaviour change.

§mcp_url: OnceLock<String>

Bound MCP HTTP-streamable URL (e.g. "http://127.0.0.1:9102/mcp") — car-server installs this after binding the listener. Used by the agents.invoke_external handler to default InvokeOptions.mcp_endpoint so external agents (Claude Code today) load the daemon’s CAR namespace via --mcp-config automatically. None when MCP isn’t bound (e.g. --mcp-bind disabled).

§mcp_sessions: OnceLock<Arc<SessionMap>>

Registry of connected MCP SSE sessions. Populated alongside [mcp_url] when car-server boots the MCP listener. Public so handlers can call crate::mcp::push_to_session to send server-initiated requests to a specific MCP-connected client (MCP-3 foundation; MCP-3b will wire host-owned tool dispatch through this).

§approval_gate: ApprovalGate

Approval gate for high-risk WS methods (audit 2026-05). The gate intercepts automation.run_applescript, automation.shortcuts.run, messages.send, mail.send, and vision.ocr before they dispatch, raises a host.create_approval for the user to act on, and waits (with a timeout) for host.resolve_approval. Approve → dispatch continues; deny / timeout → JSON-RPC error code -32003. The set of gated methods and the wait timeout are embedder-overridable via ServerStateConfig::with_approval_gate.

Implementations§

Source§

impl ServerState

Source

pub fn standalone(journal_dir: PathBuf) -> Self

Constructor for the standalone car-server binary. Each WS connection gets its own per-session memgine — matches the pre-extraction default and is correct for a single-process daemon serving one user at a time.

Embedders must not call this. It silently leaves shared_memgine = None, which re-introduces the dual-memgine bug U7 was created to prevent (one engine in the embedder, a fresh one inside every WS session). Embedders use ServerState::embedded instead, which makes the shared engine handle a required argument so it cannot be forgotten.

Source

pub fn embedded( journal_dir: PathBuf, shared_memgine: Arc<Mutex<MemgineEngine>>, ) -> Self

Constructor for embedders (e.g. tokhn-daemon). The shared memgine handle is required: every WS session created by this state will reuse the same engine, preventing the dual-memgine bug.

For embedders that also want to inject a pre-warmed inference engine or other advanced wiring, build a ServerStateConfig directly and call ServerState::with_config.

Source

pub fn with_config(cfg: ServerStateConfig) -> Self

Build a ServerState from a ServerStateConfig — the path embedders use when they need to inject a shared memgine and a pre-warmed inference engine, or any other advanced wiring the convenience constructors don’t cover.

Source

pub fn install_auth_token(&self, token: String) -> Result<(), String>

Enable the per-launch auth handshake. After this call, every new WS connection must call session.auth with token as the first frame; otherwise the connection is closed. Called by car-server at startup unless --no-auth is set (default flipped 2026-05); embedders supply their own token if they want the same posture. Returns Err(token) when auth was already installed.

Source

pub fn install_mcp_url(&self, url: String) -> Result<(), String>

Install the bound MCP URL after car-server’s listener is up. Idempotent on the first call; subsequent calls are accepted silently (matches the supervisor / a2a_dispatcher install idiom). Returns Err(()) when an MCP URL was already installed — embedders should treat this as “another component beat us to it” and use whichever value is now set.

Source

pub fn install_mcp_sessions( &self, sessions: Arc<SessionMap>, ) -> Result<(), Arc<SessionMap>>

Install the MCP SSE session registry. Pairs with [install_mcp_url] — both come from the same start_mcp call and either both get installed or neither does (the daemon binds them together).

Source

pub fn supervisor(&self) -> Result<Arc<Supervisor>, String>

Lazy-initialize and return the agent supervisor. The first call constructs a car_registry::supervisor::Supervisor backed by ~/.car/agents.json + ~/.car/logs/. Embedders that need a non-default location should call ServerState::install_supervisor before any handler runs.

Source

pub fn install_supervisor( &self, supervisor: Arc<Supervisor>, ) -> Result<(), Arc<Supervisor>>

Replace the lazy default with a caller-supplied supervisor. Returns Err(()) when a supervisor was already installed. Used by the standalone car-server binary to call start_all() on a known-good handle without paying the lazy-init lookup cost.

Source

pub async fn a2a_dispatcher(&self) -> Arc<A2aDispatcher>

Lazy-initialize and return the in-core A2A dispatcher. The first call constructs an car_a2a::A2aDispatcher from either the embedder’s overrides (set via ServerStateConfig::with_a2a_runtime / with_a2a_store / with_a2a_card_source) or sensible defaults: a fresh Runtime with register_agent_basics registered, an InMemoryTaskStore, and a card built from the runtime’s tool schemas advertising ws://127.0.0.1:9100/ as the public URL. Closes Parslee-ai/car-releases#28.

Source

pub async fn create_session( &self, client_id: &str, channel: Arc<WsChannel>, ) -> Arc<ClientSession>

Source

pub async fn remove_session( &self, client_id: &str, ) -> Option<Arc<ClientSession>>

Remove a per-client session from the registry on disconnect. Returns the removed session if present so callers can drop any remaining strong refs (e.g. drain pending tool callbacks). Fix for MULTI-4 / WS-3 — without this, state.sessions retains Arc<ClientSession> for every connection that ever existed.

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> 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: 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: 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> 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 = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

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