@memberjunction/realtime-runtime 6.2.0-edge.3 → 6.2.0-edge.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +75 -0
  2. package/dist/channels/base-realtime-channel-client.d.ts +535 -28
  3. package/dist/channels/base-realtime-channel-client.d.ts.map +1 -1
  4. package/dist/channels/base-realtime-channel-client.js +864 -10
  5. package/dist/channels/base-realtime-channel-client.js.map +1 -1
  6. package/dist/channels/channel-action-dispatcher.d.ts +70 -0
  7. package/dist/channels/channel-action-dispatcher.d.ts.map +1 -0
  8. package/dist/channels/channel-action-dispatcher.js +136 -0
  9. package/dist/channels/channel-action-dispatcher.js.map +1 -0
  10. package/dist/channels/channel-catalog-note.d.ts +48 -0
  11. package/dist/channels/channel-catalog-note.d.ts.map +1 -0
  12. package/dist/channels/channel-catalog-note.js +119 -0
  13. package/dist/channels/channel-catalog-note.js.map +1 -0
  14. package/dist/channels/channel-contract-types.d.ts +107 -0
  15. package/dist/channels/channel-contract-types.d.ts.map +1 -0
  16. package/dist/channels/channel-contract-types.js +12 -0
  17. package/dist/channels/channel-contract-types.js.map +1 -0
  18. package/dist/channels/channel-descriptor-synthesis.d.ts +56 -0
  19. package/dist/channels/channel-descriptor-synthesis.d.ts.map +1 -0
  20. package/dist/channels/channel-descriptor-synthesis.js +90 -0
  21. package/dist/channels/channel-descriptor-synthesis.js.map +1 -0
  22. package/dist/channels/channel-perception.d.ts +82 -0
  23. package/dist/channels/channel-perception.d.ts.map +1 -0
  24. package/dist/channels/channel-perception.js +120 -0
  25. package/dist/channels/channel-perception.js.map +1 -0
  26. package/dist/channels/channel-schema-format.d.ts +17 -0
  27. package/dist/channels/channel-schema-format.d.ts.map +1 -0
  28. package/dist/channels/channel-schema-format.js +45 -0
  29. package/dist/channels/channel-schema-format.js.map +1 -0
  30. package/dist/channels/channel-state-delta.d.ts +59 -0
  31. package/dist/channels/channel-state-delta.d.ts.map +1 -0
  32. package/dist/channels/channel-state-delta.js +116 -0
  33. package/dist/channels/channel-state-delta.js.map +1 -0
  34. package/dist/channels/channel-surface-placement.d.ts +25 -0
  35. package/dist/channels/channel-surface-placement.d.ts.map +1 -0
  36. package/dist/channels/channel-surface-placement.js +29 -0
  37. package/dist/channels/channel-surface-placement.js.map +1 -0
  38. package/dist/channels/channel-visual-pump.d.ts +126 -0
  39. package/dist/channels/channel-visual-pump.d.ts.map +1 -0
  40. package/dist/channels/channel-visual-pump.js +174 -0
  41. package/dist/channels/channel-visual-pump.js.map +1 -0
  42. package/dist/hosts/IRealtimeMediaHost.d.ts +86 -34
  43. package/dist/hosts/IRealtimeMediaHost.d.ts.map +1 -1
  44. package/dist/hosts/IRealtimeMediaHost.js +2 -1
  45. package/dist/hosts/IRealtimeMediaHost.js.map +1 -1
  46. package/dist/index.d.ts +26 -3
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +28 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/session/RealtimeSessionRuntime.d.ts +984 -111
  51. package/dist/session/RealtimeSessionRuntime.d.ts.map +1 -1
  52. package/dist/session/RealtimeSessionRuntime.js +2396 -381
  53. package/dist/session/RealtimeSessionRuntime.js.map +1 -1
  54. package/dist/session/RecordingShardDrain.d.ts +59 -0
  55. package/dist/session/RecordingShardDrain.d.ts.map +1 -0
  56. package/dist/session/RecordingShardDrain.js +135 -0
  57. package/dist/session/RecordingShardDrain.js.map +1 -0
  58. package/dist/session/RecordingShardQueue.d.ts +94 -0
  59. package/dist/session/RecordingShardQueue.d.ts.map +1 -0
  60. package/dist/session/RecordingShardQueue.js +176 -0
  61. package/dist/session/RecordingShardQueue.js.map +1 -0
  62. package/dist/session/RecordingShardStore.d.ts +57 -0
  63. package/dist/session/RecordingShardStore.d.ts.map +1 -0
  64. package/dist/session/RecordingShardStore.js +184 -0
  65. package/dist/session/RecordingShardStore.js.map +1 -0
  66. package/dist/session/RecordingShardWindow.d.ts +42 -0
  67. package/dist/session/RecordingShardWindow.d.ts.map +1 -0
  68. package/dist/session/RecordingShardWindow.js +88 -0
  69. package/dist/session/RecordingShardWindow.js.map +1 -0
  70. package/dist/session/RecordingTransport.d.ts +66 -0
  71. package/dist/session/RecordingTransport.d.ts.map +1 -0
  72. package/dist/session/RecordingTransport.js +61 -0
  73. package/dist/session/RecordingTransport.js.map +1 -0
  74. package/dist/session/app-client-tool-registry.d.ts +76 -0
  75. package/dist/session/app-client-tool-registry.d.ts.map +1 -0
  76. package/dist/session/app-client-tool-registry.js +100 -0
  77. package/dist/session/app-client-tool-registry.js.map +1 -0
  78. package/dist/session/avatar-notice.d.ts +43 -0
  79. package/dist/session/avatar-notice.d.ts.map +1 -0
  80. package/dist/session/avatar-notice.js +60 -0
  81. package/dist/session/avatar-notice.js.map +1 -0
  82. package/dist/session/call-end.d.ts +21 -0
  83. package/dist/session/call-end.d.ts.map +1 -0
  84. package/dist/session/call-end.js +19 -0
  85. package/dist/session/call-end.js.map +1 -0
  86. package/dist/session/channel-exposure-preferences.d.ts +70 -0
  87. package/dist/session/channel-exposure-preferences.d.ts.map +1 -0
  88. package/dist/session/channel-exposure-preferences.js +138 -0
  89. package/dist/session/channel-exposure-preferences.js.map +1 -0
  90. package/dist/session/channel-session-scope.d.ts +142 -0
  91. package/dist/session/channel-session-scope.d.ts.map +1 -0
  92. package/dist/session/channel-session-scope.js +128 -0
  93. package/dist/session/channel-session-scope.js.map +1 -0
  94. package/dist/session/client-session-deadline.d.ts +28 -0
  95. package/dist/session/client-session-deadline.d.ts.map +1 -0
  96. package/dist/session/client-session-deadline.js +50 -0
  97. package/dist/session/client-session-deadline.js.map +1 -0
  98. package/dist/session/realtime-captures.d.ts +236 -0
  99. package/dist/session/realtime-captures.d.ts.map +1 -0
  100. package/dist/session/realtime-captures.js +383 -0
  101. package/dist/session/realtime-captures.js.map +1 -0
  102. package/dist/session/realtime-microphone.d.ts +50 -0
  103. package/dist/session/realtime-microphone.d.ts.map +1 -0
  104. package/dist/session/realtime-microphone.js +51 -0
  105. package/dist/session/realtime-microphone.js.map +1 -0
  106. package/dist/session/session-event-hub.d.ts +72 -0
  107. package/dist/session/session-event-hub.d.ts.map +1 -0
  108. package/dist/session/session-event-hub.js +151 -0
  109. package/dist/session/session-event-hub.js.map +1 -0
  110. package/dist/session/session-launcher.d.ts +128 -0
  111. package/dist/session/session-launcher.d.ts.map +1 -0
  112. package/dist/session/session-launcher.js +160 -0
  113. package/dist/session/session-launcher.js.map +1 -0
  114. package/package.json +10 -9
@@ -1,8 +1,15 @@
1
- import type { Observable } from 'rxjs';
1
+ import { Observable } from 'rxjs';
2
2
  import { IMetadataProvider } from '@memberjunction/core';
3
- import { JSONValue, RealtimeToolDefinition, RealtimeTrackDescriptor, RealtimeTrackDirection } from '@memberjunction/ai';
4
- import type { BaseRealtimeClient } from '@memberjunction/ai-realtime-client';
5
- import type { AppContextSnapshot } from '@memberjunction/ai-core-plus';
3
+ import { JSONObject, JSONValue, RealtimeToolDefinition, RealtimeTrackDescriptor, RealtimeTrackDirection } from '@memberjunction/ai';
4
+ import { ChannelInboundVideoBridge, type BaseRealtimeClient, type IChannelFrameProvider, type MediaPlacement, type MediaVideoSource, type VideoSourceState } from '@memberjunction/ai-realtime-client';
5
+ import { type AppContextSnapshot, type RealtimeChannelActor, type RealtimeChannelDescriptor, type RealtimeChannelExposure, type RealtimeChannelVerb } from '@memberjunction/ai-core-plus';
6
+ import type { RealtimeChannelEvent, RealtimeChannelOutput, RealtimeChannelVerbResult, RealtimeContextActionRequest, RealtimeContextActionResult } from './channel-contract-types.js';
7
+ import type { RealtimeSessionStreamEvent } from '../session/session-event-hub.js';
8
+ import { type ChannelPerceptionOptions } from './channel-perception.js';
9
+ import { type ChannelSurfacePlacement } from './channel-surface-placement.js';
10
+ import type { RealtimeCaptureKind, RealtimeCaptureState, RealtimeCaptureStates } from '../session/realtime-captures.js';
11
+ import type { RealtimeConnectionState } from '../session/RealtimeSessionRuntime.js';
12
+ import type { ParsedDelegationArtifact } from '../session/delegation-result-parser.js';
6
13
  /**
7
14
  * A UI framework's component-class reference, as far as this runtime is concerned.
8
15
  *
@@ -63,7 +70,7 @@ export interface RealtimeChannelContext {
63
70
  RequestSave(stateJson: string): void;
64
71
  /**
65
72
  * Requests (or releases) the FOCUS layout for this channel's surface: the overlay
66
- * collapses the main call column so the surface owns the screen, with a compact floating
73
+ * collapses the main call column and fills its stage with the surface, with a compact floating
67
74
  * call pill keeping mute / thread / end reachable. Any channel may request it; the host
68
75
  * tracks which channel holds focus and routes the pill's "exit" back to it via
69
76
  * {@link BaseRealtimeChannelClient.RequestFocusExit}.
@@ -140,10 +147,47 @@ export interface RealtimeChannelContext {
140
147
  ErrorMessage?: string;
141
148
  }>;
142
149
  /**
143
- * OPTIONAL — sends a visual frame into the live session's inbound video track
144
- * (e.g. from Whiteboard or Remote Browser video bridges). No-op when the session
145
- * has not established an inbound video track or is not live.
150
+ * OPTIONAL — runs a CHANNEL-ADDRESSED `ContextTool` call: `{ action, params, target: { channel, instance? } }`.
151
+ * The headless `ClientContextChannel`'s proxy routes a call here when the model named a target; the
152
+ * host runtime resolves the channel, validates `params` against the verb's declared schema (a failure
153
+ * comes back as a structured, model-recoverable error — never a half-applied mutation), enforces
154
+ * `InvokableBy`, and opens an `on-demand` channel when the action is `'open'`. Never throws.
146
155
  *
156
+ * Absent on hosts that do not scope channels; the proxy then reports that channel addressing is
157
+ * unavailable instead of guessing.
158
+ *
159
+ * @param request The addressed call.
160
+ * @returns A structured outcome the proxy serializes back to the model.
161
+ */
162
+ DispatchContextAction?(request: RealtimeContextActionRequest): Promise<RealtimeContextActionResult>;
163
+ /**
164
+ * The resolved per-channel configuration for THIS channel: the host's defaults beneath the agent's
165
+ * and the app's `channels.config[<key>]` (see `ResolveRealtimeChannelScope`). Empty (`{}`) when none
166
+ * was configured. A channel reads its policy knobs from here (e.g. an identity channel's allowed
167
+ * email domains); it never reads the cascade itself.
168
+ *
169
+ * Treat it as untrusted, validated input: it is JSON authored by an operator, not typed code.
170
+ */
171
+ ChannelConfig?: Readonly<JSONObject>;
172
+ /**
173
+ * OPTIONAL — the events the SERVER publishes to this session (identity verification today; app-defined
174
+ * types too), hot with no replay. A channel that reacts to them (an identity channel learning that the
175
+ * user verified) subscribes in {@link BaseRealtimeChannelClient.OnInitialize} and unsubscribes at
176
+ * {@link BaseRealtimeChannelClient.Dispose}. Absent on hosts whose session has no event transport.
177
+ */
178
+ SessionEvents$?: Observable<RealtimeSessionStreamEvent>;
179
+ /**
180
+ * OPTIONAL — sends a visual frame into the live session's inbound video track, as this channel's source at the
181
+ * session's `VideoSourceArbiter` (the id its own frame bridge uses, {@link ChannelVideoSourceID}). The frame reaches
182
+ * the model only while the arbiter picks this channel, and the model is told when that changes; nothing is sent while
183
+ * the channel's exposure is below `pixels`. No-op when the session has not established an inbound video track or is
184
+ * not live.
185
+ *
186
+ * @deprecated Use a `ChannelInboundVideoBridge` (`@memberjunction/ai-realtime-client`), or
187
+ * {@link BaseRealtimeChannelClient.EnableVisualPerception}, which runs one for the channel: the bridge lists the
188
+ * source before its first frame, removes it when stopped, and says whether each frame was sent. The context's
189
+ * {@link Client} still bypasses the arbiter: a frame sent with its own `SendVideoFrame` overrides the source the
190
+ * arbiter picked, and the model is not told.
147
191
  * @param base64Image The image data (base64-encoded JPEG/PNG).
148
192
  * @param mimeType The image MIME type (defaults to 'image/jpeg').
149
193
  */
@@ -153,9 +197,48 @@ export interface RealtimeChannelContext {
153
197
  */
154
198
  IsTrackEstablished?(modality: string, direction: RealtimeTrackDirection): boolean;
155
199
  /**
156
- * OPTIONAL — the underlying {@link BaseRealtimeClient} driving the media and transport planes.
200
+ * OPTIONAL — the underlying {@link BaseRealtimeClient} driving the media and transport planes. Send video through a
201
+ * `ChannelInboundVideoBridge` over it, not through its own `SendVideoFrame`, which bypasses the session's
202
+ * `VideoSourceArbiter`.
157
203
  */
158
204
  Client?: BaseRealtimeClient | null;
205
+ /**
206
+ * OPTIONAL — the session's camera and screen share, now and on every change (the runtime's `Captures$`). A channel that
207
+ * fronts a capture ({@link BaseRealtimeChannelClient.CaptureKind}) follows it here.
208
+ */
209
+ Captures$?: Observable<RealtimeCaptureStates>;
210
+ /**
211
+ * OPTIONAL — starts the camera or a screen share for the user's click on the channel's surface: the runtime's
212
+ * `StartCamera` or `StartScreenShare`, under the same policy. Resolves with the capture's state; a failure is a state,
213
+ * never a throw.
214
+ */
215
+ StartCapture?(kind: RealtimeCaptureKind): Promise<RealtimeCaptureState>;
216
+ /** OPTIONAL — stops the camera or the screen share (the runtime's `StopCamera` / `StopScreenShare`). */
217
+ StopCapture?(kind: RealtimeCaptureKind): void;
218
+ /**
219
+ * OPTIONAL — the session's video sources, now and on every change (the runtime's `VideoSources$`): each source the
220
+ * video source arbiter knows, whether it is on (`Enabled`) and whether the model is being sent its frames (`Active`).
221
+ * On a model that takes one video stream, a capture that is on can still be one the model is not sent. A channel that
222
+ * fronts a capture finds its source by `REALTIME_CAPTURE_SOURCE_IDS`, and says the agent sees the capture only while
223
+ * that source is both.
224
+ */
225
+ VideoSources$?: Observable<readonly VideoSourceState[]>;
226
+ /**
227
+ * OPTIONAL — the agent's video while the model sends it, `null` otherwise (the runtime's `AgentVideo$`). A channel that
228
+ * shows the agent, one that sinks outbound video ({@link BaseRealtimeChannelClient.GetSunkTracks}), follows it here.
229
+ */
230
+ AgentVideo$?: Observable<MediaVideoSource | null>;
231
+ /**
232
+ * OPTIONAL — the call's state, now and on every change (the runtime's `ConnectionState$`): connecting, listening,
233
+ * speaking, thinking, an error, or closed. A channel that shows the agent follows the agent's turn here.
234
+ */
235
+ ConnectionState$?: Observable<RealtimeConnectionState>;
236
+ /**
237
+ * OPTIONAL — whether the call is resuming on a new provider connection mid-call, now and on every change (the runtime's
238
+ * `Resuming$`). The agent's video can stop for a few seconds while it does, so a channel that shows the agent holds the
239
+ * last frame through the gap rather than showing that the video has stalled.
240
+ */
241
+ Resuming$?: Observable<boolean>;
159
242
  }
160
243
  /**
161
244
  * The first-run INTRO content for an interactive channel — the concise "what is this surface
@@ -210,10 +293,13 @@ export interface ChannelOnboardingDetails {
210
293
  *
211
294
  * ### Lifecycle — ONE INSTANCE PER SESSION (not a singleton)
212
295
  * `ClassFactory.CreateInstance` → {@link Initialize}(ctx) → zero or more
213
- * {@link BindSurface}/{@link UnbindSurface} cycles (the surface pane is created/destroyed
214
- * with the overlay's tab panel, e.g. collapse/expand) → {@link Dispose} at teardown.
296
+ * {@link BindSurface}/{@link UnbindSurface} cycles (the Angular overlay creates the surface the
297
+ * first time it is shown and keeps it until the channel leaves the session; another host may
298
+ * recreate it), each telling the plugin when its surface goes in and out of sight
299
+ * ({@link OnSurfaceVisibilityChange}) and where it is placed ({@link OnSurfacePlacementChange})
300
+ * → {@link Dispose} at teardown.
215
301
  * {@link ApplyAgentTool} MUST work with NO surface bound (apply to the state engine
216
- * directly; skip the UI garnish) — tool calls can arrive while the panel is collapsed.
302
+ * directly; skip the UI garnish) — tool calls can arrive before the surface is first shown.
217
303
  *
218
304
  * @typeParam TSurface The plugin's Angular surface component type. The host only ever
219
305
  * sees the default (`object`) — the typed parameter exists so concrete plugins get a
@@ -235,12 +321,16 @@ export declare abstract class BaseRealtimeChannelClient<TSurface extends object
235
321
  * The shared name prefix of every tool this channel exposes (e.g. `'Whiteboard_'`).
236
322
  * The host registers ONE local-execution route per plugin: tool calls whose name starts
237
323
  * with this prefix go to {@link ApplyAgentTool} instead of the server relay.
324
+ *
325
+ * Default `''` — a v2 channel with NO native tools (it is reached only through the `ContextTool`
326
+ * proxy). The host registers no route for an empty prefix: an empty prefix would match *every*
327
+ * tool name and hijack the server relay.
238
328
  */
239
- abstract get ToolNamePrefix(): string;
240
- /** Label for the channel's tab on the overlay's surface panel (e.g. `'Whiteboard'`). */
241
- abstract get TabTitle(): string;
242
- /** Font Awesome icon class for the channel's tab (e.g. `'fa-solid fa-chalkboard'`). */
243
- abstract get TabIcon(): string;
329
+ get ToolNamePrefix(): string;
330
+ /** Label for the channel's tab on the overlay's surface panel (e.g. `'Whiteboard'`). Defaults to {@link ChannelName}. */
331
+ get TabTitle(): string;
332
+ /** Font Awesome icon class for the channel's tab (e.g. `'fa-solid fa-chalkboard'`). Defaults to a generic puzzle-piece. */
333
+ get TabIcon(): string;
244
334
  /**
245
335
  * OPTIONAL accent color for the channel's tab (a CSS color string, e.g. an `hsl()` /
246
336
  * token). When a plugin supplies one, the overlay paints the tab's dot + active underline
@@ -253,18 +343,26 @@ export declare abstract class BaseRealtimeChannelClient<TSurface extends object
253
343
  * The channel's CLIENT-EXECUTED tool declarations, aggregated by the session service
254
344
  * into the `clientTools` set declared to the realtime model at session mint. The server
255
345
  * only DECLARES these — execution stays in the browser via {@link ApplyAgentTool}.
346
+ *
347
+ * Default `[]` — a v2 channel whose verbs are reached through the proxy only.
256
348
  */
257
- abstract GetToolDefinitions(): RealtimeToolDefinition[];
349
+ GetToolDefinitions(): RealtimeToolDefinition[];
258
350
  /**
259
351
  * Executes ONE agent tool call locally (the ACTION direction) and returns the result
260
352
  * JSON string fed back to the model as the `tool_response`. Called for every tool whose
261
353
  * name starts with {@link ToolNamePrefix}. Must work both WITH a bound surface (apply +
262
- * UI garnish) and WITHOUT one (apply to the state engine directly — the tab pane may not
263
- * exist, e.g. the surface panel is collapsed). Should not throw: return a
354
+ * UI garnish) and WITHOUT one (apply to the state engine directly — the surface may not
355
+ * exist yet, e.g. it has not been shown). Should not throw: return a
264
356
  * `{ success: false, error }` payload so the model can narrate the failure (the host
265
357
  * additionally wraps anything thrown).
358
+ *
359
+ * **This is the LEGACY entry point.** Channels written against the v2 contract override
360
+ * {@link ApplyVerb} instead and inherit this method: the default maps the tool name back to its verb,
361
+ * calls {@link ApplyVerb} as the agent, and serializes the structured result. Channels written
362
+ * against v1 override THIS and inherit {@link ApplyVerb}, which adapts it. A channel must override
363
+ * at least one of the two — overriding neither is reported as a failure result, never a recursion.
266
364
  */
267
- abstract ApplyAgentTool(toolName: string, argsJson: string): string | Promise<string>;
365
+ ApplyAgentTool(toolName: string, argsJson: string): string | Promise<string>;
268
366
  /**
269
367
  * The Angular component the overlay creates dynamically as this channel's tab pane, or `null`
270
368
  * for a **server-only** channel that renders no MJ surface (its surface, if any, lives on the
@@ -305,16 +403,57 @@ export declare abstract class BaseRealtimeChannelClient<TSurface extends object
305
403
  * The plugin — which knows its own component type — sets inputs (state engine, agent
306
404
  * name, …) and subscribes outputs here, wiring perception/garnish flows back through
307
405
  * {@link Context}. May be called again with a NEW instance after an
308
- * {@link UnbindSurface} (the pane is destroyed/recreated with the tab panel).
406
+ * {@link UnbindSurface} (the host recreated the surface).
309
407
  */
310
- abstract BindSurface(instance: TSurface): void;
408
+ BindSurface(_instance: TSurface): void;
311
409
  /**
312
- * Called by the host when the surface component is being destroyed (tab panel
313
- * collapsed / overlay torn down). Drop the instance reference and unsubscribe any
410
+ * Called by the host when the surface component is being destroyed (the channel left
411
+ * the session / the overlay was torn down). Drop the instance reference and unsubscribe any
314
412
  * output subscriptions — after this, {@link ApplyAgentTool} runs in its no-surface
315
413
  * mode. Default: no-op.
316
414
  */
317
415
  UnbindSurface(): void;
416
+ /**
417
+ * Called by the host when the bound surface comes into sight or goes out of it (the panel collapsed or hidden,
418
+ * another tab active, the call minimized). The surface stays bound and keeps its state; a channel can pause work
419
+ * nobody sees, such as a screenshot poll, and resume it when the surface is shown again. The host calls it right
420
+ * after {@link BindSurface} with the surface's current visibility, then on every change, and not after
421
+ * {@link UnbindSurface}. Default: no-op, so a surface keeps running out of sight.
422
+ */
423
+ OnSurfaceVisibilityChange(_visible: boolean): void;
424
+ /**
425
+ * Called by the host when the bound surface moves: to the stage (it fills the call), to its tab, or out of sight
426
+ * because the user hid it. A channel can adapt its own chrome, such as a "Move to stage" button that has nothing to
427
+ * do once the surface is on the stage. Called right after {@link BindSurface} with the current placement, then on
428
+ * every move, and not after {@link UnbindSurface}. Default: no-op.
429
+ */
430
+ OnSurfacePlacementChange(_placement: MediaPlacement): void;
431
+ private surfacePlacement;
432
+ /**
433
+ * Where this channel's surface shows when a call starts, and where the user may move it: from the channel's registry
434
+ * row (`UIConfig.Placement` and `UIConfig.AllowedPlacements`). A channel without a row, or whose row says nothing,
435
+ * starts on its tab and can go anywhere.
436
+ */
437
+ get SurfacePlacement(): ChannelSurfacePlacement;
438
+ /**
439
+ * Sets {@link SurfacePlacement}. The runtime calls it with the registry row's placement when it builds the channel, before
440
+ * the channel mounts, as it applies exposure policy with {@link ApplyExposure}.
441
+ */
442
+ ApplySurfacePlacement(placement: ChannelSurfacePlacement): void;
443
+ /**
444
+ * Whether the user may share this channel's surface on its own: a host that offers it lists the surface, named by
445
+ * {@link TabTitle} with {@link TabIcon}, under "This panel" in its Share menu while the surface is on screen, and the
446
+ * share then shows only that surface. Default `false`, so a channel opts in with this getter. A surface that shows the
447
+ * agent its own call (the camera, the screen share, the agent's video) never should.
448
+ */
449
+ get SurfaceShareable(): boolean;
450
+ /**
451
+ * The runtime capture this channel fronts (`'camera'` or `'screen'`), or `null`. Such a channel is the capture's policy:
452
+ * the runtime starts the capture only while the channel is in the session and the session's policy lets the agent see
453
+ * pixels through it, and the user's "agent can see" choice for the channel decides whether the capture's frames reach
454
+ * the model. Default `null`.
455
+ */
456
+ get CaptureKind(): RealtimeCaptureKind | null;
318
457
  /**
319
458
  * Binds the host context and invokes the {@link OnInitialize} hook. Called exactly once
320
459
  * per session, right after ClassFactory instantiation and before any tool call or
@@ -329,11 +468,21 @@ export declare abstract class BaseRealtimeChannelClient<TSurface extends object
329
468
  protected OnInitialize(): void;
330
469
  /**
331
470
  * Subclass hook invoked once the realtime session is connected and live (the client driver
332
- * is created, connected, and media tracks negotiated). Channels that establish media bridges
333
- * (e.g. video streaming) can start them here when `Context.Client` is available.
334
- * Default: no-op.
471
+ * is created, connected, and media tracks negotiated), or when the channel is opened in a live
472
+ * session. Channels that establish media bridges (e.g. video streaming) can start them here when
473
+ * `Context.Client` is available.
474
+ * Default: a channel with visual perception adds the inbound video track it sources when the call
475
+ * has none (it was opened mid-call) and brings its frame bridge up; any other channel does nothing.
476
+ * An override that keeps visual perception calls `super.OnSessionStarted()`.
335
477
  */
336
478
  OnSessionStarted(): void;
479
+ /**
480
+ * Adds the inbound video track this channel sources ({@link GetSourcedTracks}) when the call is live without one. A
481
+ * channel open when the call connected had its track negotiated then, so this does nothing for it. A channel opened
482
+ * mid-call (`on-demand`) was not part of that negotiation: without its own track it could only send on a track a camera
483
+ * or screen share added, which goes when that capture stops. Only on a model that takes inbound video.
484
+ */
485
+ private addVisualTrackMidCall;
337
486
  /**
338
487
  * Max time {@link ResolveAgentSessionId} waits for the session id to bind before giving up, and the
339
488
  * poll interval it re-checks on. Protected so tests can shrink the wait; production keeps the
@@ -389,8 +538,326 @@ export declare abstract class BaseRealtimeChannelClient<TSurface extends object
389
538
  /**
390
539
  * Media tracks this client channel can SINK — samples flowing from the model OUT.
391
540
  * Default `[]`.
541
+ *
542
+ * The session requests them when it connects, so the driver establishes the ones the model supports. A channel that
543
+ * sinks outbound video shows the agent's video: it follows {@link RealtimeChannelContext.AgentVideo$}, and counts as
544
+ * used once the video arrives, so the host shows its surface.
392
545
  */
393
546
  GetSunkTracks(): readonly RealtimeTrackDescriptor[];
547
+ /**
548
+ * Whether this channel shows the agent's video: it sinks outbound video ({@link GetSunkTracks}). The runtime marks it
549
+ * as used when the video arrives, and a host presents its surface as the agent (the call overlay puts it in the agent's
550
+ * place in the call rather than in the focus layout).
551
+ */
552
+ get ShowsAgentVideo(): boolean;
553
+ /**
554
+ * The channel's stable primary-instance id. A single-instance channel has exactly one; a
555
+ * {@link RealtimeChannelDescriptor.MultiInstance} channel owns further instance ids itself and
556
+ * routes by the `instanceId` argument of {@link ApplyVerb}. Appears in every structured note
557
+ * (`[channel:<Key>#<instance>] …`) and every {@link RealtimeChannelEvent}.
558
+ */
559
+ get InstanceId(): string;
560
+ /**
561
+ * The channel's SELF-DESCRIPTION — what it is, what it holds, what it accepts, how it behaves in a
562
+ * session. This is the contract an agent reads to operate a channel it has never seen, and what the
563
+ * runtime reads to scope, advertise and validate. Pure data; called often, so keep it cheap.
564
+ *
565
+ * Default: a descriptor SYNTHESIZED from the v1 members (tools → verbs, intro → instructions),
566
+ * stamped with the legacy contract version — so a channel that implements nothing new is still
567
+ * describable and works exactly as before. A v2 channel overrides this with an authored descriptor.
568
+ * (Build verbs from {@link GetToolDefinitions} with `BuildToolBackedVerbs` when they are the
569
+ * native tools, so a subclass that adds a tool does not leave the descriptor lying.)
570
+ */
571
+ GetDescriptor(): RealtimeChannelDescriptor;
572
+ /**
573
+ * A snapshot of the channel's things (the descriptor's nouns), keyed by noun name. Perception notes
574
+ * are computed as deltas between successive snapshots, so this must be a pure read.
575
+ *
576
+ * Default: the parsed {@link SerializeState} when that is a JSON object, else `{}` — which makes a
577
+ * v1 channel's state of record its (single, opaque) snapshot with no code change.
578
+ */
579
+ GetState(): JSONObject;
580
+ /**
581
+ * Checks the current {@link GetState} snapshot against the descriptor's noun schemas and returns
582
+ * every violation (empty when valid). Useful in a channel's own tests; the runtime also runs it on
583
+ * every perception note when {@link StrictStateChecks} is on.
584
+ */
585
+ ValidateState(): string[];
586
+ /**
587
+ * Whether state snapshots are validated against the descriptor's noun schemas as notes are built —
588
+ * a development aid that surfaces a channel whose state drifted from its own declaration. On by
589
+ * default only when a Node-style environment reports a non-production `NODE_ENV` (tests, tooling);
590
+ * a browser production build never pays for it. Hosts may set it explicitly.
591
+ */
592
+ static StrictStateChecks: boolean;
593
+ /**
594
+ * Runs one verb of this channel — the contract-v2 entry point, reached through the `ContextTool`
595
+ * proxy (and, via {@link ApplyAgentTool}, by native tool calls).
596
+ *
597
+ * The runtime has ALREADY resolved the verb against the descriptor, enforced `InvokableBy`, and
598
+ * validated `args` against the verb's parameter schema before calling this, so an override can
599
+ * trust its inputs' shape. It must work with no surface bound, and should not throw — return a
600
+ * failure result so the model can recover.
601
+ *
602
+ * Default: adapts the legacy {@link ApplyAgentTool} (the verb's native tool name, args as JSON),
603
+ * so every v1 channel is reachable through the proxy with no change. A v2 channel overrides this.
604
+ *
605
+ * @param verb The verb name (as declared in the descriptor).
606
+ * @param args The validated parameters.
607
+ * @param actor Who is acting: the agent (a model call) or the user (their own interaction).
608
+ * @param instanceId Which instance of a multi-instance channel; `undefined` for the primary instance.
609
+ */
610
+ ApplyVerb(verb: string, args: JSONObject, actor: RealtimeChannelActor, instanceId?: string): RealtimeChannelVerbResult | Promise<RealtimeChannelVerbResult>;
611
+ /**
612
+ * Maps a native tool name back to its verb: the descriptor's `NativeToolName` match first, then the
613
+ * name minus {@link ToolNamePrefix}. Protected so a channel with an unusual naming scheme can
614
+ * override it.
615
+ */
616
+ protected ResolveVerbForTool(toolName: string): string;
617
+ /** Maps a verb to the native tool name it is reachable as (inverse of {@link ResolveVerbForTool}). */
618
+ protected ResolveNativeToolName(verb: string): string;
619
+ /**
620
+ * The descriptor verb a native tool call runs, or `undefined` when the tool is not one of the channel's verbs
621
+ * (the runtime uses it to apply {@link RefuseVerbForExposure} on the native-tool route as well as the `ContextTool` one).
622
+ *
623
+ * @param toolName The native tool name the model called.
624
+ */
625
+ FindVerbForNativeTool(toolName: string): RealtimeChannelVerb | undefined;
626
+ /**
627
+ * Whether the agent may be given this verb's result at the channel's current exposure. A verb that declares
628
+ * `ReturnsChannelData` is refused whole when exposure is below that level, because its result would hand the agent
629
+ * what the user or a zero-data-retention policy held back from it. Policy, not redaction: the result is never filtered.
630
+ *
631
+ * Applies to the AGENT (the dispatcher and the native-tool route call it); a user acting through the surface is not restricted.
632
+ *
633
+ * @param verb The verb the agent is calling.
634
+ * @returns The sentence to give the agent, or `null` when the call may proceed.
635
+ */
636
+ RefuseVerbForExposure(verb: Pick<RealtimeChannelVerb, 'Name' | 'ReturnsChannelData'>): string | null;
637
+ /** Why a channel that overrides neither entry point cannot run anything. */
638
+ private implementsNeitherMessage;
639
+ private readonly channelEventsSubject;
640
+ private readonly channelOutputSubject;
641
+ /**
642
+ * Everything this channel streams out, as typed events: state changes, discrete user actions, frames
643
+ * pushed, opened/completed. Hosts subscribe (the embeddable element re-emits these as DOM events);
644
+ * the model is told about a change through a coalesced note instead — see {@link RecordChange}.
645
+ * Completes at {@link Dispose}.
646
+ */
647
+ readonly Events$: Observable<RealtimeChannelEvent>;
648
+ /** What the channel handed back when it completed (see {@link Complete}). Completes at {@link Dispose}. */
649
+ readonly Output$: Observable<RealtimeChannelOutput>;
650
+ /**
651
+ * Opens the channel — mounts it for use and seeds it with `inputs`. Called by the runtime when the
652
+ * agent (or user) opens an `on-demand` channel, after the channel has been initialized.
653
+ *
654
+ * Validates `inputs` against the descriptor's `Inputs` schema (a violation is a structured
655
+ * `'invalid_params'` failure, never a half-opened channel), runs the {@link OnOpen} hook, then tells
656
+ * the model the channel is open and what it currently holds (`opened` note with a state snapshot) —
657
+ * and takes that snapshot as the baseline later changes are described against.
658
+ *
659
+ * @param inputs What the agent asked to seed the channel with.
660
+ */
661
+ Open(inputs?: JSONObject): Promise<RealtimeChannelVerbResult>;
662
+ /**
663
+ * Subclass hook for {@link Open}: apply the (already validated) seed `inputs`. Return a failure
664
+ * result to refuse the open; return nothing for success. Default: nothing to do.
665
+ */
666
+ protected OnOpen(_inputs: JSONObject): void | RealtimeChannelVerbResult | Promise<void | RealtimeChannelVerbResult>;
667
+ /**
668
+ * The channel finished and is handing something back (a submitted form, a chosen option, a
669
+ * finished game). Emits on {@link Output$} and {@link Events$} and tells the model with a
670
+ * `completed` note. The output is validated against the descriptor's `Output` schema when
671
+ * {@link StrictStateChecks} is on; a mismatch is logged, never swallowed into silence or thrown.
672
+ *
673
+ * @param output What the channel hands back.
674
+ * @param instanceId The instance that completed, for a multi-instance channel (default: the primary instance).
675
+ */
676
+ Complete(output: JSONObject, instanceId?: string): void;
677
+ /** Monotonic id of the latest state change; tags state events and the frames that describe them. */
678
+ private channelChangeSeq;
679
+ /** Re-entrancy guard for the default {@link ApplyVerb} ↔ {@link ApplyAgentTool} bridge. */
680
+ private bridgingVerbAndNativeTool;
681
+ private channelPerception;
682
+ /** Dev-check messages already logged, so a drifting state warns once, not on every note. */
683
+ private readonly warnedStateIssueKeys;
684
+ /** Tuning for the perception coalescer (debounce window, max note size). Override to change it. */
685
+ protected PerceptionOptions: ChannelPerceptionOptions;
686
+ /** The id of the latest state change (0 before any). */
687
+ protected get CurrentChangeId(): number;
688
+ /**
689
+ * Records that the channel's state changed. Call this on EVERY mutation, whoever caused it.
690
+ *
691
+ * It (1) assigns the change a monotonic id, (2) emits a `state_changed` event on {@link Events$}
692
+ * immediately (observers see every change), and (3) — unless `Perceive` is `false` — schedules a
693
+ * COALESCED structured note to the model: a burst of N changes inside the debounce window produces
694
+ * ONE `[channel:<Key>#<instance>] state_changed {"changeId":…,"changes":N,"delta":{…}}` note carrying
695
+ * what differs from what the model was last told (the first note is a full snapshot). Notes that
696
+ * would be too large degrade to a list of changed paths.
697
+ *
698
+ * @param options `Author` — who made the change (carried on the event); `Perceive` — whether the
699
+ * model should be told (default `true`; a channel that already feeds the model its own perception
700
+ * — like the Whiteboard's scene deltas — passes `false`).
701
+ * @returns The change's id.
702
+ */
703
+ protected RecordChange(options?: {
704
+ Author?: RealtimeChannelActor | 'system';
705
+ Perceive?: boolean;
706
+ }): number;
707
+ /**
708
+ * Emits a typed event on {@link Events$} (NOT to the model — use {@link RecordChange} for state the
709
+ * model should perceive, or send an explicit note for a discrete event it must hear about).
710
+ *
711
+ * @param name The event name (declare it in the descriptor's `Events`).
712
+ * @param payload The event payload.
713
+ * @param changeId The change id the event relates to, when it relates to one.
714
+ * @param instanceId The instance the event is about, for a multi-instance channel (default: the primary instance).
715
+ */
716
+ protected EmitChannelEvent(name: string, payload?: JSONObject, changeId?: number, instanceId?: string): void;
717
+ /** Sends any pending coalesced perception note now (e.g. before a surface is torn down). */
718
+ protected FlushPerception(): void;
719
+ /** The perception coalescer, created on first use. */
720
+ private ensurePerception;
721
+ /** {@link GetState}, with the development-time schema check applied when {@link StrictStateChecks} is on. */
722
+ private getStateChecked;
723
+ /** Logs a state problem once per channel instance — these checks run on every perception note. */
724
+ private warnStateIssueOnce;
725
+ /**
726
+ * Whether this channel wants to be offered the artifacts a delegated run just produced (the agent asked
727
+ * Skip or Sage to build something, and it came back as artifacts). A channel that hosts artifacts says yes
728
+ * for the ones it can show — and for an artifact it ALREADY shows, so a newer version replaces it in place.
729
+ *
730
+ * Asked of every channel in the session, mounted or merely advertised. For an advertised channel it is
731
+ * asked before the channel has been initialized, so it must not depend on {@link Context}; the channel's
732
+ * resolved configuration is passed in for exactly that reason. Saying yes for an advertised channel
733
+ * mounts it, so only answer yes when you will do something with the artifacts.
734
+ *
735
+ * Default: `false` — a channel that does not host artifacts is never bothered.
736
+ *
737
+ * @param artifacts What the delegated run produced.
738
+ * @param config This channel's resolved configuration (host defaults beneath agent/app config).
739
+ */
740
+ AcceptsDelegationArtifacts(_artifacts: readonly ParsedDelegationArtifact[], _config: JSONObject): boolean;
741
+ /**
742
+ * A delegated run produced artifacts this channel said it wants ({@link AcceptsDelegationArtifacts}). The
743
+ * channel is mounted and initialized by now. Never throw into the runtime: handle your own failures and
744
+ * report them (the runtime logs anything that escapes, but cannot recover the work).
745
+ *
746
+ * Default: nothing.
747
+ *
748
+ * @param artifacts What the delegated run produced.
749
+ */
750
+ OnDelegationArtifacts(_artifacts: readonly ParsedDelegationArtifact[]): void | Promise<void>;
751
+ /** The exposure the SERVER's policy allows (agent cap, zero data retention); `undefined` until the runtime applies one. */
752
+ private policyExposure;
753
+ /** The user's own choice for this channel; `undefined` when they made none. */
754
+ private userExposure;
755
+ /** Why exposure is below the channel's ceiling, as sentences the agent can be told. */
756
+ private exposureReasons;
757
+ /**
758
+ * How much of this channel the model may perceive without asking: the lowest of the channel's own ceiling
759
+ * (`GetDescriptor().MaxExposure`), the server policy and the user's choice. A channel nobody has applied a
760
+ * policy to simply has its ceiling, which is what every channel had before exposure policy existed.
761
+ *
762
+ * - `'none'`: no state notes and no frames.
763
+ * - `'state'`: structured state notes; no frames.
764
+ * - `'pixels'`: state notes and frames.
765
+ *
766
+ * It governs what flows to the model UNPROMPTED (perception notes, the contents of `opened` / `completed`
767
+ * notes, frames). It does not REDACT what a verb's result contains: a verb is all-or-nothing. A verb whose
768
+ * result would show the model something above this level declares `ReturnsChannelData` on the verb, and
769
+ * when this level is below it the verb is refused whole (`exposure_restricted`) and listed to the model as
770
+ * unavailable; see {@link RefuseVerbForExposure}. A verb that does not declare it returns what the channel
771
+ * says it returns, so a channel whose verbs reveal sensitive data must either declare it or lower its own
772
+ * `MaxExposure`.
773
+ */
774
+ get Exposure(): RealtimeChannelExposure;
775
+ /** Why {@link Exposure} is below the channel's ceiling; empty when it is not. */
776
+ get ExposureReasons(): readonly string[];
777
+ /**
778
+ * Whether the server policy (not the user) leaves room for pixels. Deliberately does not consult the
779
+ * descriptor: a synthesized descriptor derives its `MaxExposure` from {@link GetSourcedTracks}, which asks
780
+ * this, so reading it here would recurse. A channel that enabled visual perception has declared pixels.
781
+ */
782
+ private policyAllowsPixels;
783
+ /**
784
+ * Applies exposure policy: the server's decision and the user's own choice. The runtime calls this when
785
+ * it mounts the channel and again whenever the user changes their choice.
786
+ *
787
+ * Withdrawing exposure takes effect immediately: a pending perception note is dropped, a pending frame
788
+ * is dropped, and the channel's video source stops being forwarded. Restoring it re-baselines (the
789
+ * model's picture of the channel is stale, so the next note is a full snapshot) and sends a fresh frame.
790
+ * When the call is live the model is told about the change, with the reason, so it never assumes it
791
+ * can still see what it can't.
792
+ *
793
+ * @param settings `Policy` — the server-decided exposure; `User` — the user's choice (omit for none);
794
+ * `Reasons` — why the policy lowered it, from the server's limits.
795
+ */
796
+ ApplyExposure(settings: ChannelExposureSettings): void;
797
+ /** Makes a change in exposure real: stops what is no longer allowed, restarts what is again. */
798
+ private applyExposureEffects;
799
+ /** Emits the change on {@link Events$} and, when the call is live, tells the model what it can now perceive and why. */
800
+ private announceExposureChange;
801
+ /** The frame source the visual pump captures from, once {@link EnableVisualPerception} ran. */
802
+ private visualFrameProvider;
803
+ private visualPerceptionOptions;
804
+ private visualFramePump;
805
+ /**
806
+ * The shared frame bridge that carries frames to the model's inbound video track. Deliberately NOT named
807
+ * `videoBridge`: channels that predate the pump (the Remote Browser, app channels) declare their own
808
+ * private `videoBridge`, and a second private of the same name in the base class would stop them compiling.
809
+ */
810
+ protected VisualVideoBridge: ChannelInboundVideoBridge | null;
811
+ /** Stops refreshing the model's picture when the bridge's source is sent again; set while the bridge exists. */
812
+ private stopRefreshOnSourceActivated;
813
+ /** Default frame cadence when the track negotiated none (1 fps — the Gemini Live ceiling). */
814
+ private static readonly DEFAULT_VISUAL_CADENCE_MS;
815
+ /** Floor for the negotiated cadence (4 fps) so a fast model never makes a channel spin. */
816
+ private static readonly MIN_VISUAL_CADENCE_MS;
817
+ /**
818
+ * Opts this channel into VISUAL perception: its surface can be shown to a video-capable model as
819
+ * frames. Call it from {@link OnInitialize} with the channel's frame source.
820
+ *
821
+ * The channel then needs only to say *when* its picture changed — {@link NotifyVisualChange} after a
822
+ * user edit, {@link ConfirmVisualChange} after a successful agent edit — and the base class does the
823
+ * rest: change-driven (no idle heartbeat), paced to the negotiated cadence, a trailing settle so the
824
+ * model ends on the final state of a burst, dedupe of identical frames, and a single confirmation
825
+ * frame (with a "do not narrate" note) after an agent edit. Every frame is tagged with the change id
826
+ * of the state it was captured at (`frame_pushed` events), so state and pixels can be cross-checked.
827
+ *
828
+ * Also makes {@link GetSourcedTracks} report the inbound video track, which is what gets it
829
+ * negotiated. Idempotent: calling it again replaces the provider and options.
830
+ *
831
+ * @param provider Renders the channel's current picture as a base64 JPEG (or `null`).
832
+ * @param options See {@link VisualPerceptionOptions}.
833
+ */
834
+ protected EnableVisualPerception(provider: IChannelFrameProvider, options?: VisualPerceptionOptions): void;
835
+ /**
836
+ * The channel's picture changed because of a USER action (or a scene replacement): push a frame,
837
+ * respecting the cadence. Resolves when the decision has been carried out; never rejects. A no-op
838
+ * until {@link EnableVisualPerception} ran and the session has an inbound video track.
839
+ */
840
+ protected NotifyVisualChange(): Promise<void>;
841
+ /**
842
+ * An AGENT action succeeded and changed the channel's picture: push exactly one confirmation frame
843
+ * now and tell the model not to narrate its own change. Call only on SUCCESS — confirming a failed
844
+ * action would assert an edit landed that did not. A no-op until {@link EnableVisualPerception} ran.
845
+ */
846
+ protected ConfirmVisualChange(): Promise<void>;
847
+ /**
848
+ * Returns the shared frame bridge, creating it if needed and (only when the channel opted into the
849
+ * legacy poller) starting its fixed-rate poll once the video track is up. `null` when no context is bound.
850
+ */
851
+ protected EnsureVideoBridge(): ChannelInboundVideoBridge | null;
852
+ /** Builds the pump, wiring it to this channel's context, bridge and event stream. */
853
+ private createVisualPump;
854
+ /** The "do not narrate your own change" note sent with a confirmation frame. */
855
+ private confirmationNote;
856
+ /**
857
+ * The frame cadence in ms: the rate the inbound video track negotiated (`1000 / Rate`), clamped to
858
+ * a 250ms floor; 1000ms when nothing was negotiated.
859
+ */
860
+ private getNegotiatedVisualCadenceMs;
394
861
  /**
395
862
  * Tears the plugin down at session end: release the surface binding, unsubscribe
396
863
  * state-engine subscriptions, then drop the context. Subclasses overriding this MUST
@@ -399,4 +866,44 @@ export declare abstract class BaseRealtimeChannelClient<TSurface extends object
399
866
  */
400
867
  Dispose(): void;
401
868
  }
869
+ /**
870
+ * The id of a channel's video source at the session's `VideoSourceArbiter`: `<channel key>#<instance id>`. The channel's
871
+ * own frame bridge and the runtime's {@link RealtimeChannelContext.SendVideoFrame} register under it, so a channel is
872
+ * one source however it sends, and the "agent can see" control lists it once.
873
+ *
874
+ * @param channelKey The channel's descriptor key.
875
+ * @param instanceId The channel instance.
876
+ */
877
+ export declare function ChannelVideoSourceID(channelKey: string, instanceId: string): string;
878
+ /**
879
+ * Settings for {@link BaseRealtimeChannelClient.ApplyExposure}.
880
+ */
881
+ export interface ChannelExposureSettings {
882
+ /** The exposure the server's policy allows (agent cap and zero-data-retention requirement). Omit when no server policy applies. */
883
+ Policy?: RealtimeChannelExposure;
884
+ /** The user's own choice for this channel. Omit when they made none. */
885
+ User?: RealtimeChannelExposure;
886
+ /** Why the policy lowered exposure, as sentences the agent can be told. */
887
+ Reasons?: readonly string[];
888
+ }
889
+ /**
890
+ * Options for {@link BaseRealtimeChannelClient.EnableVisualPerception}.
891
+ */
892
+ export interface VisualPerceptionOptions {
893
+ /**
894
+ * The note sent after a confirmation frame — a literal string, or a function of the change id.
895
+ * Default: a structured `frame_confirmed` note carrying a do-not-narrate instruction. The Whiteboard
896
+ * passes its original wording to stay byte-for-byte what it was.
897
+ */
898
+ ConfirmationNote?: string | ((changeId: number) => string);
899
+ /**
900
+ * Also start the shared bridge's fixed-rate poller (1 fps) once the video track is up.
901
+ *
902
+ * Default `false`: the pump is purely change-driven. This exists ONLY so the Whiteboard keeps the
903
+ * behavior it had before the pump was lifted: it has always started the bridge's poller (which
904
+ * re-sends the board every second regardless of change) despite its own comments and design
905
+ * stating it is heartbeat-free. Nothing new should set this.
906
+ */
907
+ StartBridgePoller?: boolean;
908
+ }
402
909
  //# sourceMappingURL=base-realtime-channel-client.d.ts.map