Skip to main content

BrowserViewRegistry

Struct BrowserViewRegistry 

Source
pub struct BrowserViewRegistry { /* private fields */ }
Expand description

Every browser the drawer can reach, keyed by conversation/agent-session — plus the one standing session shared by every conversation that has no agent-attached browser of its own (controller ruling R6).

Implementations§

Source§

impl BrowserViewRegistry

Source

pub fn new(root: PathBuf) -> Self

Source

pub fn set_signin_attention(&self, attention: Arc<dyn SignInAttention>)

Install the operator-attention sink relayed views report sign-in waits through. Called once, by ServerState::with_config; a second call is a silent no-op.

Source

pub async fn standing(&self) -> Arc<BrowserView>

The standing user session, created on first reference.

Creating it does NOT launch Chromium — BrowserTools launches lazily, and for this view the trigger is the user’s first navigation (see BrowserTools::user_navigate). Opening the drawer on an empty standing session therefore costs nothing and shows the empty state, which is exactly what the design asks for.

Source

pub async fn register( &self, conversation_id: impl Into<String>, tools: Arc<BrowserTools>, ) -> Arc<BrowserView>

Publish an agent-attached browser under a conversation/agent-session key, so the drawer can watch that conversation specifically.

This is the producer API. Anything that builds an assistant runtime hands the runtime’s own BrowserTools (AssistantRuntime::browser) in here; the registry never creates an agent’s browser itself. Registering costs nothing for a run that never browses — BrowserTools launches Chromium lazily.

Replacement is the lifetime bound. A conversation’s view outlives the run that created it (see BrowserView::note_run_ended), so the thing that eventually releases the old browser is a NEW run registering for the SAME key. That makes the standing cost one idle Chromium per conversation whose agent actually browsed, not one per run.

Subscribers and the event cursor are handed over to the new view, so a drawer watching this conversation follows it to the new browser without re-subscribing — and, critically, without the cursor going backwards, which would break gap detection far worse than a gap does.

Source

pub async fn register_relay( &self, conversation_id: impl Into<String>, producer: Arc<RelayProducer>, ) -> Arc<BrowserView>

Publish a SUPERVISED AGENT PROCESS’s browser under a conversation key.

The relay twin of Self::register, with one difference that matters: the same process re-claiming its own conversation is a no-op. A supervised agent registers on every turn, and churning the view each time would reset its cursor and drop the drawer’s stream for no reason. A DIFFERENT process claiming the key — the supervisor restarted it — replaces the view through the ordinary adopt path, so a drawer that never unsubscribed follows the agent across without the cursor moving backwards.

Source

pub async fn producer_for( &self, client_id: &str, agent_id: &str, channel: &Arc<WsChannel>, ) -> Arc<RelayProducer>

The producer for an agent connection, created on its first registration.

Source

pub async fn broadcast_host_connected(&self, connected: bool)

Tell every supervised agent process that host connectivity changed.

They cache the answer — a supervised process has no read of the daemon’s session set — and it decides whether browser_await_signin points the user at the drawer or tells them to open the CAR app.

Source

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

The producer a given connection registered, if any. The gate on inbound pushes: a connection that never registered has none.

Source

pub async fn conversation_owner(&self, conversation_id: &str) -> Option<String>

The agent entitled to publish a conversation, and how that gets recorded. See browser_relay::authorize_conversation_claim.

Source

pub async fn bind_conversation(&self, conversation_id: &str, agent_id: &str)

Source

pub async fn note_producer_disconnected(&self, client_id: &str)

An agent connection dropped — its browser went with the process. Its views stay registered, reporting an empty browser, so a restarted process can replace them and carry the drawer across.

Source

pub async fn release_if_idle(&self, view: &Arc<BrowserView>) -> bool

Release a view whose run has ended and that nobody is subscribed to: drop it from the map (and with it the last reference to the browser it was serving) and stop its capture. Reports whether it went.

This is the eviction path the lifetime bound needs. Replacement — “a NEW run registering for the SAME key” — is the documented bound, and it is genuinely unreachable for a producer that mints a fresh key per run: the in-daemon assistant_start path keys on mcp-run-<uuid>, so before this, every assistant run that browsed left one idle Chromium registered for the daemon’s whole lifetime.

It does NOT weaken “a view outlives the run that created it”. A subscribed view is never released, so the drawer still keeps showing the last page exactly as the agent left it, with agent-opened tabs usable, for as long as anything is actually watching. What goes is a finished run’s browser that no drawer ever attached to — which nobody can observe, and which is precisely the leak.

Identity-checked, never key-checked: a successor may already hold the key by the time an asynchronous run-end signal arrives, and removing by key would take a LIVE run’s browser down. Same reasoning as BrowserView::note_run_ended being a method on the view.

Source

pub async fn pending_signins(&self) -> Vec<BrowserSignInSnapshot>

Durable state returned by host.subscribe, independent of the bounded host-event backlog. Relay producers are deduplicated because every view they back shows the same one process-owned browser.

Source

pub async fn get( &self, conversation_id: Option<&str>, ) -> Option<Arc<BrowserView>>

Source

pub async fn drop_subscriptions_for_client(&self, client_id: &str)

Disconnect cleanup: drop this connection’s subscriptions everywhere, and start the control grace period on any view it was driving.

Trait Implementations§

Source§

impl Default for BrowserViewRegistry

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

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