Skip to main content

ServerState

Struct ServerState 

Source
pub struct ServerState {
Show 25 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 observer_manifest_path: OnceLock<PathBuf>, pub a2a_dispatcher: OnceLock<Arc<A2aDispatcher>>, pub a2ui_subscribers: Mutex<HashMap<String, Arc<WsChannel>>>, pub auth_token: OnceLock<String>, pub parslee_session: OnceLock<ParsleeSession>, pub attached_agents: Mutex<HashMap<String, String>>, pub agent_memgines: Mutex<HashMap<String, Arc<Mutex<MemgineEngine>>>>, pub chat_sessions: Mutex<HashMap<String, ChatSession>>, 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.

§observer_manifest_path: OnceLock<PathBuf>

Manifest path this daemon is observing but does NOT own. Set by car-server when boot-time supervisor construction fails with car_registry::supervisor::SupervisorError::AlreadyRunning — another car-server process on the host holds the exclusive lock on this manifest. In that state, supervisor() returns a clear “observe-only” error so mutation handlers refuse (preventing the duplicate-spawn bug from Parslee-ai/car-releases#44), while read-only handlers (agents.list, agents.health) fall back to car_registry::supervisor::Supervisor::list_from_manifest / car_registry::supervisor::Supervisor::health_from_manifest so operators can still inspect what the primary daemon is supervising.

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

§parslee_session: OnceLock<ParsleeSession>

Parslee cloud identity loaded from the user’s OS keychain at daemon startup when car auth login has been completed.

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

§chat_sessions: Mutex<HashMap<String, ChatSession>>

In-flight agents.chat sessions keyed by session_id. See ChatSession for shape. Populated by agents.chat, cleared on terminal agent.chat.event or agents.chat.cancel. Disconnect cleanup happens in remove_session — any in-flight session bound to either the disconnecting host or agent client is dropped so subsequent stray notifications from a respawned agent fall on the floor rather than racing into a stale stream.

§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_parslee_session( &self, session: ParsleeSession, ) -> Result<(), ParsleeSession>

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.

In observer mode (set via [install_observer_manifest]), returns a clear error mentioning the manifest path the primary daemon owns. This prevents the second daemon from re-attempting user_default() (which would also fail with AlreadyRunning) on every WS call, and gives mutation handlers a stable refusal path. Read-only handlers (agents.list, agents.health) should call Self::observer_manifest_path first and fall back to car_registry::supervisor::Supervisor::list_from_manifest / health_from_manifest when set. Closes Parslee-ai/car-releases#44.

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 fn supervisor_if_installed(&self) -> Option<Arc<Supervisor>>

Non-acquiring read of the currently-installed supervisor. Unlike supervisor, this does NOT lazy- init via user_default() — it returns None instead of constructing a fresh Supervisor and acquiring the <manifest>.lock as a side effect. Use this from read-only metadata paths (host.subscribe identity, status surfaces) where causing lock acquisition on observation would be a Heisenberg subscribe — the act of asking “do you own the lock?” must not be the act of taking it.

Source

pub fn install_observer_manifest(&self, path: PathBuf) -> Result<(), PathBuf>

Mark this daemon as observing a manifest owned by another car-server process. After this call, supervisor() returns an “observe-only” error and read-only handlers (agents.list, agents.health) fall back to the static Supervisor::list_from_manifest / health_from_manifest paths. Idempotent — subsequent calls with the same path are no-ops; a different path returns Err(()). Closes Parslee-ai/car-releases#44.

Source

pub fn observer_manifest_path(&self) -> Option<&PathBuf>

Path of the manifest this daemon is observing but not supervising. None when this daemon owns the supervisor (the normal case) or when no manifest is configured at all (no HOME, embedder didn’t install one).

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,