Member Junction
    Preparing search index...

    The REMOTE BROWSER as a pluggable interactive channel — a BaseRealtimeChannelClient resolved from the MJ: AI Agent Channels registry row whose ClientPluginClass is 'RealtimeRemoteBrowserChannel'. One instance per session (ClassFactory-created at session start).

    Topology (CLIENT-DIRECT, like the whiteboard — the browser just happens to live on the server):

    • Action: the browser_* client-executed tool set (REMOTE_BROWSER_TOOL_DEFINITIONS). ApplyAgentTool maps a tool call → a normalized RemoteBrowserAction (MapToolToAction) and awaits the ExecuteRemoteBrowserAction GraphQL mutation (via RealtimeChannelContext.ExecuteServerAction) to drive the server browser, returning a concise JSON result for the model. Never throws — argument and transport failures map to a { success: false, error } payload.
    • Perception: after a successful action, a [browser] context note carries the new page URL so the agent perceives where the page went; the surface independently polls a live screenshot.
    • Surface: RemoteBrowserSurfaceComponent, created dynamically in the channel tab; the plugin hands it the session's provider + id in BindSurface so it can poll RemoteBrowserSnapshot. HUMAN TAKEOVER is enabled by default (Collaborative): the user can click/type into the live canvas and those events relay to the server browser via RELAY_HUMAN_INPUT_MUTATION (server-gated on the backend's HumanTakeover capability).

    The channel keeps NO client-side state of record — the browser's state lives entirely on the server — so SerializeState / RestoreState use the base no-op behavior.

    Hierarchy (View Summary)

    Index

    Constructors

    Properties

    Context: RealtimeChannelContext | null = null

    The host context, available from Initialize until Dispose. null outside that window — guard with ?. in any code that can run early/late.

    GoalPollIntervalMs: number = 2500

    Poll cadence + deadline for pollGoalResult (the async browser_AchieveGoal result poll). Protected so tests can shrink them; production polls every 2.5s for up to 5 minutes (a goal loop past 5 min keeps running server-side, the model just gets a "still running" note).

    GoalPollTimeoutMs: number = ...
    SessionIdWaitIntervalMs: number = 200
    SessionIdWaitTimeoutMs: number = 8000

    Max time ResolveAgentSessionId waits for the session id to bind before giving up, and the poll interval it re-checks on. Protected so tests can shrink the wait; production keeps the defaults (the real mint race is sub-second, 8s is generous headroom).

    Accessors

    • get TabColor(): string | null

      OPTIONAL accent color for the channel's tab (a CSS color string, e.g. an hsl() / token). When a plugin supplies one, the overlay paints the tab's dot + active underline with it; when omitted (the default null), the overlay derives a stable, deterministic color from the ChannelName so every channel still reads as a distinct, colored surface. A channel only overrides this to enforce a specific brand accent.

      Returns string | null

    Methods

    • Wires the dynamically-created surface: hands it a snapshot fetcher closing over the channel context (session id + provider live there), so the surface stays transport- agnostic. Set BEFORE the surface's first change detection (the pane binds synchronously), so its ngOnInit poll has the fetcher.

      Also asks the server to start a live screencast. When the backend supports ScreenStreaming the server starts PUSHING frames and reports Streaming: true; we flip the surface to canvas mode (its poll is then skipped) and OnScreencastFrame paints each pushed frame. When the backend lacks the capability (Streaming: false) the surface keeps the snapshot poll already running — graceful fallback, no further action.

      HUMAN TAKEOVER is enabled BY DEFAULT (Collaborative): the surface is flipped Interactive = true and its HumanInput output subscribed, forwarding each pointer/keyboard event to the server via RELAY_HUMAN_INPUT_MUTATION. Takeover only takes effect on the canvas (screencast) path; the <img> poll fallback stays view-only. The server gates the actual routing on the backend's HumanTakeover capability, so this is safe to enable unconditionally.

      Parameters

      Returns void

    • Forwards one PUSHED screencast frame to the bound surface's canvas. Called by the session service when a RemoteBrowserScreencastFrame arrives on the push-status stream for THIS session. No-op when the channel isn't streaming or has no bound surface (e.g. the tab pane is collapsed).

      Under streaming the surface's snapshot poll is stopped, so this is the ONLY thing that sees the page while frames are being pushed — which is why the frame now carries the URL (#3496). Without it, a user navigating during a screencast changed the picture and nothing else: the agent kept describing the page it last opened, and the pixels proving otherwise were right there on screen.

      Parameters

      • dataBase64: string

        The frame image as raw base64 JPEG (no data: prefix).

      • OptionalcurrentUrl: string | null

        The browser's URL when the frame was captured; absent from older servers.

      Returns void

    • Resolves the live RealtimeChannelContext.AgentSessionID, briefly WAITING for it when it isn't bound yet rather than giving up instantly. AgentSessionID is a live getter over the session service's current id: it reads null in the window BEFORE the session mints (the realtime model can fire a tool call the very first beat it connects, before mintSession resolves) and again AFTER teardown. Server-backed tool paths (e.g. the Remote Browser channel's browser_* tools) call this instead of reading Context?.AgentSessionID synchronously, so a tool invoked a beat early WAITS for the session to come live — defense-in-depth against the "session id missing" race — instead of returning a hard failure to the model.

      Returns the id as soon as it's non-null (the common path resolves immediately, no delay), or null if it's still unbound after SessionIdWaitTimeoutMs — or the channel was Disposed in the meantime (Context goes null, so we stop waiting on a torn-down session).

      Returns Promise<string | null>