ProtectedContextThe host context, available from Initialize until Dispose.
null outside that window — guard with ?. in any code that can run early/late.
ProtectedSessionProtectedSessionMax 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).
ReadonlyStateThe board's state of record — created fresh with the plugin (one per session).
The channel definition name — MUST match the MJ: AI Agent Channels row's Name
(e.g. 'Whiteboard'). Used as the persistence key for SerializeState saves
and as the channel tab's stable key.
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.
Font Awesome icon class for the channel's tab (e.g. 'fa-solid fa-chalkboard').
Label for the channel's tab on the overlay's surface panel (e.g. 'Whiteboard').
The shared name prefix of every tool this channel exposes (e.g. 'Whiteboard_').
The host registers ONE local-execution route per plugin: tool calls whose name starts
with this prefix go to ApplyAgentTool instead of the server relay.
Executes one Whiteboard_* tool call LOCALLY. Prefers the live bound host (board
mutation + UI garnish); falls back to the pure engine function when no surface is
bound so the channel keeps working with the pane collapsed.
Wires the dynamically-created board host: inputs (shared state engine + agent name) are set BEFORE the component's first change detection, and the perception/garnish outputs are subscribed back into the host context — the overlay never sees any of it.
Tears the plugin down at session end: release the surface binding, unsubscribe
state-engine subscriptions, then drop the context. Subclasses overriding this MUST
call super.Dispose(). Any final state save has already been flushed by the host
(the debounced RealtimeChannelContext.RequestSave pipeline) before disposal.
First-run intro shown the first time the user opens the Whiteboard tab (once per user).
The Angular component the overlay creates dynamically as this channel's tab pane, or null
for a server-only channel that renders no MJ surface (its surface, if any, lives on the
external platform — e.g. a bridge-contributed native whiteboard or meeting-controls channel).
When this returns null, the overlay renders NO tab for the channel and never calls
BindSurface/UnbindSurface — but the channel's tools (GetToolDefinitions /
ApplyAgentTool) and perception (RealtimeChannelContext.SendContextNote) still run.
A created surface instance is handed straight back via BindSurface; the host treats it as
opaque.
Default: null (server-only). A channel with a rendered surface overrides this to return its
component type.
The channel's CLIENT-EXECUTED tool declarations, aggregated by the session service
into the clientTools set declared to the realtime model at session mint. The server
only DECLARES these — execution stays in the browser via ApplyAgentTool.
Whether this channel has a rendered MJ surface (GetSurfaceComponent returns non-null).
The overlay uses this to decide whether to register a surface tab; server-only channels are
false. Override only if surface availability must be decided WITHOUT constructing the type
(the default calls GetSurfaceComponent once).
Binds the host context and invokes the OnInitialize hook. Called exactly once per session, right after ClassFactory instantiation and before any tool call or surface bind.
ProtectedOnPersist the board (host-debounced) on EVERY board mutation — user edits AND agent tools.
Exit focus mode THROUGH the bound host (its own Focus button state stays in sync; it
re-emits FocusModeChange(false) → Context.SetFocusMode(false)). When no surface is
bound the overlay's defensive flag clear covers it.
ProtectedResolveResolves 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).
Rehydrates a prior session's saved board into THIS session's state engine (in place —
the State instance and its subscriptions are preserved). Returns true on
success; malformed / incompatible JSON returns false and the board stays fresh
(never throws — WhiteboardState.LoadFromJSON is tolerant by contract).
The board's serialized state of record (persisted under ChannelName).
Called by the host when the surface component is being destroyed (tab panel collapsed / overlay torn down). Drop the instance reference and unsubscribe any output subscriptions — after this, ApplyAgentTool runs in its no-surface mode. Default: no-op.
The LIVE WHITEBOARD as a pluggable interactive channel — the canonical BaseRealtimeChannelClient implementation, resolved from the
MJ: AI Agent Channelsregistry row whoseClientPluginClassis'RealtimeWhiteboardChannel'.One instance per session (created via ClassFactory at session start). It owns the board's WhiteboardState engine and contributes the channel's full contract:
Whiteboard_*client-executed tool set (WHITEBOARD_TOOL_DEFINITIONS); ApplyAgentTool prefers the BOUND host component (board mutation + violet pop-in / toast / presence-cursor garnish) and falls back to the pure ApplyWhiteboardAgentTool engine call when no surface is bound (e.g. the surface panel is collapsed) — the channel keeps working, just without the garnish.SceneDeltastream and pipes each delta into the live model's context as a[whiteboard]background note; the agent-undo toast click flows the same way.Context.SetFocusModeso the shell can collapse/restore the main call column.'Whiteboard'— the host debounces and flushes at teardown.A PRIOR session's persisted board is restored through RestoreState (invoked by the session host after Initialize, before any surface binding): the saved JSON is rehydrated IN PLACE into the same WhiteboardState instance, so the save subscription and any later surface binding keep pointing at one engine. Malformed or incompatible payloads are tolerated — the board simply starts fresh.