@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.
- package/README.md +75 -0
- package/dist/channels/base-realtime-channel-client.d.ts +535 -28
- package/dist/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/channels/base-realtime-channel-client.js +864 -10
- package/dist/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/channels/channel-action-dispatcher.d.ts +70 -0
- package/dist/channels/channel-action-dispatcher.d.ts.map +1 -0
- package/dist/channels/channel-action-dispatcher.js +136 -0
- package/dist/channels/channel-action-dispatcher.js.map +1 -0
- package/dist/channels/channel-catalog-note.d.ts +48 -0
- package/dist/channels/channel-catalog-note.d.ts.map +1 -0
- package/dist/channels/channel-catalog-note.js +119 -0
- package/dist/channels/channel-catalog-note.js.map +1 -0
- package/dist/channels/channel-contract-types.d.ts +107 -0
- package/dist/channels/channel-contract-types.d.ts.map +1 -0
- package/dist/channels/channel-contract-types.js +12 -0
- package/dist/channels/channel-contract-types.js.map +1 -0
- package/dist/channels/channel-descriptor-synthesis.d.ts +56 -0
- package/dist/channels/channel-descriptor-synthesis.d.ts.map +1 -0
- package/dist/channels/channel-descriptor-synthesis.js +90 -0
- package/dist/channels/channel-descriptor-synthesis.js.map +1 -0
- package/dist/channels/channel-perception.d.ts +82 -0
- package/dist/channels/channel-perception.d.ts.map +1 -0
- package/dist/channels/channel-perception.js +120 -0
- package/dist/channels/channel-perception.js.map +1 -0
- package/dist/channels/channel-schema-format.d.ts +17 -0
- package/dist/channels/channel-schema-format.d.ts.map +1 -0
- package/dist/channels/channel-schema-format.js +45 -0
- package/dist/channels/channel-schema-format.js.map +1 -0
- package/dist/channels/channel-state-delta.d.ts +59 -0
- package/dist/channels/channel-state-delta.d.ts.map +1 -0
- package/dist/channels/channel-state-delta.js +116 -0
- package/dist/channels/channel-state-delta.js.map +1 -0
- package/dist/channels/channel-surface-placement.d.ts +25 -0
- package/dist/channels/channel-surface-placement.d.ts.map +1 -0
- package/dist/channels/channel-surface-placement.js +29 -0
- package/dist/channels/channel-surface-placement.js.map +1 -0
- package/dist/channels/channel-visual-pump.d.ts +126 -0
- package/dist/channels/channel-visual-pump.d.ts.map +1 -0
- package/dist/channels/channel-visual-pump.js +174 -0
- package/dist/channels/channel-visual-pump.js.map +1 -0
- package/dist/hosts/IRealtimeMediaHost.d.ts +86 -34
- package/dist/hosts/IRealtimeMediaHost.d.ts.map +1 -1
- package/dist/hosts/IRealtimeMediaHost.js +2 -1
- package/dist/hosts/IRealtimeMediaHost.js.map +1 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/session/RealtimeSessionRuntime.d.ts +984 -111
- package/dist/session/RealtimeSessionRuntime.d.ts.map +1 -1
- package/dist/session/RealtimeSessionRuntime.js +2396 -381
- package/dist/session/RealtimeSessionRuntime.js.map +1 -1
- package/dist/session/RecordingShardDrain.d.ts +59 -0
- package/dist/session/RecordingShardDrain.d.ts.map +1 -0
- package/dist/session/RecordingShardDrain.js +135 -0
- package/dist/session/RecordingShardDrain.js.map +1 -0
- package/dist/session/RecordingShardQueue.d.ts +94 -0
- package/dist/session/RecordingShardQueue.d.ts.map +1 -0
- package/dist/session/RecordingShardQueue.js +176 -0
- package/dist/session/RecordingShardQueue.js.map +1 -0
- package/dist/session/RecordingShardStore.d.ts +57 -0
- package/dist/session/RecordingShardStore.d.ts.map +1 -0
- package/dist/session/RecordingShardStore.js +184 -0
- package/dist/session/RecordingShardStore.js.map +1 -0
- package/dist/session/RecordingShardWindow.d.ts +42 -0
- package/dist/session/RecordingShardWindow.d.ts.map +1 -0
- package/dist/session/RecordingShardWindow.js +88 -0
- package/dist/session/RecordingShardWindow.js.map +1 -0
- package/dist/session/RecordingTransport.d.ts +66 -0
- package/dist/session/RecordingTransport.d.ts.map +1 -0
- package/dist/session/RecordingTransport.js +61 -0
- package/dist/session/RecordingTransport.js.map +1 -0
- package/dist/session/app-client-tool-registry.d.ts +76 -0
- package/dist/session/app-client-tool-registry.d.ts.map +1 -0
- package/dist/session/app-client-tool-registry.js +100 -0
- package/dist/session/app-client-tool-registry.js.map +1 -0
- package/dist/session/avatar-notice.d.ts +43 -0
- package/dist/session/avatar-notice.d.ts.map +1 -0
- package/dist/session/avatar-notice.js +60 -0
- package/dist/session/avatar-notice.js.map +1 -0
- package/dist/session/call-end.d.ts +21 -0
- package/dist/session/call-end.d.ts.map +1 -0
- package/dist/session/call-end.js +19 -0
- package/dist/session/call-end.js.map +1 -0
- package/dist/session/channel-exposure-preferences.d.ts +70 -0
- package/dist/session/channel-exposure-preferences.d.ts.map +1 -0
- package/dist/session/channel-exposure-preferences.js +138 -0
- package/dist/session/channel-exposure-preferences.js.map +1 -0
- package/dist/session/channel-session-scope.d.ts +142 -0
- package/dist/session/channel-session-scope.d.ts.map +1 -0
- package/dist/session/channel-session-scope.js +128 -0
- package/dist/session/channel-session-scope.js.map +1 -0
- package/dist/session/client-session-deadline.d.ts +28 -0
- package/dist/session/client-session-deadline.d.ts.map +1 -0
- package/dist/session/client-session-deadline.js +50 -0
- package/dist/session/client-session-deadline.js.map +1 -0
- package/dist/session/realtime-captures.d.ts +236 -0
- package/dist/session/realtime-captures.d.ts.map +1 -0
- package/dist/session/realtime-captures.js +383 -0
- package/dist/session/realtime-captures.js.map +1 -0
- package/dist/session/realtime-microphone.d.ts +50 -0
- package/dist/session/realtime-microphone.d.ts.map +1 -0
- package/dist/session/realtime-microphone.js +51 -0
- package/dist/session/realtime-microphone.js.map +1 -0
- package/dist/session/session-event-hub.d.ts +72 -0
- package/dist/session/session-event-hub.d.ts.map +1 -0
- package/dist/session/session-event-hub.js +151 -0
- package/dist/session/session-event-hub.js.map +1 -0
- package/dist/session/session-launcher.d.ts +128 -0
- package/dist/session/session-launcher.d.ts.map +1 -0
- package/dist/session/session-launcher.js +160 -0
- package/dist/session/session-launcher.js.map +1 -0
- package/package.json +10 -9
|
@@ -1,11 +1,45 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
2
|
import { IMetadataProvider } from '@memberjunction/core';
|
|
3
|
-
import { ClientRealtimeSessionConfig, RealtimeToolDefinition, RealtimeTrackDirection } from '@memberjunction/ai';
|
|
4
|
-
import { AppContextSnapshot } from '@memberjunction/ai-core-plus';
|
|
5
|
-
import { BaseRealtimeClient, RealtimeAudioActivity } from '@memberjunction/ai-realtime-client';
|
|
3
|
+
import { ClientRealtimeSessionConfig, JSONObject, RealtimeToolDefinition, RealtimeTrackDirection } from '@memberjunction/ai';
|
|
4
|
+
import { AppContextSnapshot, type IdentityVerifiedEventPayload, type RealtimeChannelExposure, type ResolvedRealtimeChannel } from '@memberjunction/ai-core-plus';
|
|
5
|
+
import { BaseRealtimeClient, RealtimeAudioActivity, RealtimeClientError, type DisplayCaptureOptions, type MediaVideoSource, type VideoSourceState } from '@memberjunction/ai-realtime-client';
|
|
6
|
+
import { type IRealtimeSessionLauncher } from './session-launcher.js';
|
|
7
|
+
import { type RealtimeAvatarNotice } from './avatar-notice.js';
|
|
8
|
+
import { type RealtimeCaptureOffers, type RealtimeCaptureState, type RealtimeCaptureStates } from './realtime-captures.js';
|
|
9
|
+
import { type RealtimeMicrophoneState } from './realtime-microphone.js';
|
|
10
|
+
import { type IRealtimeSessionEventSource, type RealtimeSessionStreamEvent } from './session-event-hub.js';
|
|
6
11
|
import { ParsedDelegationArtifact } from './delegation-result-parser.js';
|
|
7
12
|
import { BaseRealtimeChannelClient } from '../channels/base-realtime-channel-client.js';
|
|
8
13
|
import { IRealtimeMediaHost } from '../hosts/IRealtimeMediaHost.js';
|
|
14
|
+
import type { RealtimeContextActionResult } from '../channels/channel-contract-types.js';
|
|
15
|
+
import { type AppClientToolRegistration } from './app-client-tool-registry.js';
|
|
16
|
+
import { type IChannelExposurePreferences } from './channel-exposure-preferences.js';
|
|
17
|
+
import { type RealtimeSessionStartOptions } from './channel-session-scope.js';
|
|
18
|
+
import { type IRecordingShardStore } from './RecordingShardStore.js';
|
|
19
|
+
import type { IRecordingTransport } from './RecordingTransport.js';
|
|
20
|
+
/**
|
|
21
|
+
* How long teardown waits for the server to acknowledge the recording's terminal request, measured
|
|
22
|
+
* from after `CloseAgentSession`. The terminal is `keepalive`, so it completes even if the page goes
|
|
23
|
+
* away; the wait only keeps `SavingRecording$` (and so a host's "don't close yet" guard) up while the
|
|
24
|
+
* server has not confirmed. Bounded so a dead server can never hold the End of a call hostage —
|
|
25
|
+
* unacknowledged shards stay in the local store and the server janitor assembles what arrived.
|
|
26
|
+
*/
|
|
27
|
+
export declare const RECORDING_TERMINAL_ACK_TIMEOUT_MS = 3000;
|
|
28
|
+
/**
|
|
29
|
+
* How long teardown waits for the recorder to stop and flush its encoder. `Stop()` runs BEFORE
|
|
30
|
+
* `CloseAgentSession`, so a wedged encoder must not be able to hold the End of a call (and the
|
|
31
|
+
* server session) open; past this the recording ends at the packets already encoded.
|
|
32
|
+
*/
|
|
33
|
+
export declare const RECORDER_STOP_TIMEOUT_MS = 2000;
|
|
34
|
+
/** Options of {@link RealtimeSessionRuntime.SendRecordingTerminal}. */
|
|
35
|
+
export interface RecordingTerminalOptions {
|
|
36
|
+
/**
|
|
37
|
+
* False when the call may continue — the host's `visibilitychange → hidden` handler. The server then
|
|
38
|
+
* stamps the counts but assembles only after its grace period, so switching tabs does not rewrite the
|
|
39
|
+
* recording each time. Defaults to true (`pagehide`: the page is going away).
|
|
40
|
+
*/
|
|
41
|
+
Final?: boolean;
|
|
42
|
+
}
|
|
9
43
|
/**
|
|
10
44
|
* `MJ: User Settings` key for the per-user "record this voice call" consent toggle. Stored as
|
|
11
45
|
* the literal string `'true'`/`'false'` (read with `=== 'true'`), cross-device via
|
|
@@ -130,6 +164,7 @@ export interface StartRealtimeClientSessionResult {
|
|
|
130
164
|
ConversationId: string | null;
|
|
131
165
|
Provider: string;
|
|
132
166
|
Model: string;
|
|
167
|
+
/** The provider credential the client driver presents. Empty on a relay session, which has {@link RelayUrl} instead. */
|
|
133
168
|
EphemeralToken: string;
|
|
134
169
|
ExpiresAt: string;
|
|
135
170
|
/** JSON.stringify of the provider session config (instructions + tools) to apply at connect. */
|
|
@@ -150,6 +185,31 @@ export interface StartRealtimeClientSessionResult {
|
|
|
150
185
|
* last session left off.
|
|
151
186
|
*/
|
|
152
187
|
PriorChannelStatesJson: string | null;
|
|
188
|
+
/**
|
|
189
|
+
* OPTIONAL — the server's resolved `RealtimeSessionClientPolicy` (from `@memberjunction/ai-core-plus`) as JSON: which of the channel
|
|
190
|
+
* candidates the client reported at mint are in this session (agent/app scoping and the registry's
|
|
191
|
+
* kill switch already applied), their resolved display and config, and the app/static client-tool
|
|
192
|
+
* tiers. Absent from a server that predates channel scoping and from a host that mints through its
|
|
193
|
+
* own proxy; the runtime then resolves the scope locally from code defaults and host declarations.
|
|
194
|
+
*/
|
|
195
|
+
ClientPolicyJson?: string | null;
|
|
196
|
+
/**
|
|
197
|
+
* OPTIONAL — the session's live-avatar status (`RealtimeAvatarStatus` from `@memberjunction/ai`) as JSON: whether the
|
|
198
|
+
* voiced agent asked for an avatar, whether the model renders it, and why not. The runtime reads it after connecting
|
|
199
|
+
* to publish {@link RealtimeSessionRuntime.AvatarNotice$}. Absent from a server that predates it, when the agent asked
|
|
200
|
+
* for no avatar, and from a host that mints for itself and does not pass it on; the call then shows no notice.
|
|
201
|
+
*/
|
|
202
|
+
AvatarStatusJson?: string | null;
|
|
203
|
+
/**
|
|
204
|
+
* OPTIONAL — how the browser reaches the provider: `'direct'` or `'relay'` (`RealtimeClientTransport` from
|
|
205
|
+
* `@memberjunction/ai`). Absent, null or unknown means direct: a direct session, or a server that predates it.
|
|
206
|
+
*/
|
|
207
|
+
Transport?: string | null;
|
|
208
|
+
/**
|
|
209
|
+
* OPTIONAL — where a relay session's client driver connects (MJAPI's realtime relay). It carries the session's ticket,
|
|
210
|
+
* so it is a credential: never log it. A host that mints for itself must pass it on for a relay session.
|
|
211
|
+
*/
|
|
212
|
+
RelayUrl?: string | null;
|
|
153
213
|
}
|
|
154
214
|
/**
|
|
155
215
|
* Host-supplied inputs that accompany an already-minted session on
|
|
@@ -215,12 +275,13 @@ export declare class RealtimeSessionRuntime {
|
|
|
215
275
|
protected readonly mediaHost: IRealtimeMediaHost;
|
|
216
276
|
/**
|
|
217
277
|
* @param mediaHost the platform's media capabilities. The runtime never touches `navigator`,
|
|
218
|
-
* `Blob` or `FileReader` itself — microphone acquisition and audio
|
|
219
|
-
* are the host's, because both are platform-specific
|
|
220
|
-
*
|
|
278
|
+
* `Blob` or `FileReader` itself — microphone acquisition and audio capture/encoding
|
|
279
|
+
* are the host's, because both are platform-specific (permission UX, which
|
|
280
|
+
* encoder the platform offers).
|
|
221
281
|
*/
|
|
222
282
|
constructor(mediaHost: IRealtimeMediaHost);
|
|
223
283
|
private _connectionState$;
|
|
284
|
+
private readonly _resuming$;
|
|
224
285
|
private _captions$;
|
|
225
286
|
private _active$;
|
|
226
287
|
private _delegationProgress$;
|
|
@@ -230,6 +291,7 @@ export declare class RealtimeSessionRuntime {
|
|
|
230
291
|
private _agentName$;
|
|
231
292
|
private _modelName$;
|
|
232
293
|
private _minimized$;
|
|
294
|
+
private _savingRecording$;
|
|
233
295
|
private _activeChannels$;
|
|
234
296
|
private _channelFocus$;
|
|
235
297
|
private _sessionStarted$;
|
|
@@ -237,6 +299,16 @@ export declare class RealtimeSessionRuntime {
|
|
|
237
299
|
private _channelActivity$;
|
|
238
300
|
/** Current connection / turn state. */
|
|
239
301
|
readonly ConnectionState$: Observable<RealtimeConnectionState>;
|
|
302
|
+
/**
|
|
303
|
+
* Whether the call is resuming: moving to a new connection with its provider mid-call. `true` from a `'connecting'` the
|
|
304
|
+
* driver reports while the call is live, until the call is live again (`'listening'`, `'speaking'` or `'thinking'`),
|
|
305
|
+
* fails (`'error'`) or ends (`'closed'`). A call's first connect is not a resume, so it is `false` until the call has
|
|
306
|
+
* been live, and outside a call. Gemini Live resumes when Google ends a connection (`goAway`) and after a dropped
|
|
307
|
+
* socket, and the agent's video can stop for a few seconds meanwhile: a host can show that as a pause rather than as
|
|
308
|
+
* the agent leaving. It changes before {@link ConnectionState$} does, so a resume's `'connecting'` arrives with it
|
|
309
|
+
* already `true`.
|
|
310
|
+
*/
|
|
311
|
+
readonly Resuming$: Observable<boolean>;
|
|
240
312
|
/** Live captions for both sides of the conversation. */
|
|
241
313
|
readonly Captions$: Observable<RealtimeCaption[]>;
|
|
242
314
|
/** True while a session is open (mic button active, overlay shown). */
|
|
@@ -273,6 +345,17 @@ export declare class RealtimeSessionRuntime {
|
|
|
273
345
|
* pure presentation state, reset to `false` at session start and teardown.
|
|
274
346
|
*/
|
|
275
347
|
readonly Minimized$: Observable<boolean>;
|
|
348
|
+
/**
|
|
349
|
+
* True while the end-of-call recording is being saved: from the first statement of a teardown
|
|
350
|
+
* that holds a recorder until that teardown settles — which includes waiting (at most
|
|
351
|
+
* {@link RECORDING_TERMINAL_ACK_TIMEOUT_MS}) for the server to acknowledge the recording's terminal
|
|
352
|
+
* request. False at construction and for unrecorded sessions. Exists for #5195; the runtime is
|
|
353
|
+
* platform-free, so hosts own the reaction (a "saving" indicator, a disabled navigation button,
|
|
354
|
+
* etc.).
|
|
355
|
+
*/
|
|
356
|
+
readonly SavingRecording$: Observable<boolean>;
|
|
357
|
+
/** Synchronous read of {@link SavingRecording$}. */
|
|
358
|
+
get IsSavingRecording(): boolean;
|
|
276
359
|
/**
|
|
277
360
|
* The session's ACTIVE interactive-channel plugins, resolved from the `MJ: AI Agent
|
|
278
361
|
* Channels` registry at session start (one instance per session, per channel). Emits
|
|
@@ -280,6 +363,59 @@ export declare class RealtimeSessionRuntime {
|
|
|
280
363
|
* one surface tab per plugin — it never knows any concrete channel type.
|
|
281
364
|
*/
|
|
282
365
|
readonly ActiveChannels$: Observable<BaseRealtimeChannelClient[]>;
|
|
366
|
+
private readonly _videoSources$;
|
|
367
|
+
/**
|
|
368
|
+
* The video sources the agent is perceiving or could perceive right now (a whiteboard, a remote browser, a
|
|
369
|
+
* shared screen), with whether each is on and whether its frames are reaching the model. Empty when no
|
|
370
|
+
* session is live or nothing has offered a frame source. This is what the "agent can see" control renders;
|
|
371
|
+
* toggle one with {@link SetVideoSourceEnabled}.
|
|
372
|
+
*/
|
|
373
|
+
readonly VideoSources$: Observable<readonly VideoSourceState[]>;
|
|
374
|
+
private readonly _captures$;
|
|
375
|
+
/**
|
|
376
|
+
* The user's camera and screen share: off, starting, on (with the stream to show the user) or failed (with the
|
|
377
|
+
* reason). Both off outside a session. Start and stop them with {@link StartCamera}, {@link StartScreenShare},
|
|
378
|
+
* {@link StopCamera} and {@link StopScreenShare}; while one is on, it is also a source on {@link VideoSources$}.
|
|
379
|
+
*/
|
|
380
|
+
readonly Captures$: Observable<RealtimeCaptureStates>;
|
|
381
|
+
private readonly _captureOffers$;
|
|
382
|
+
/**
|
|
383
|
+
* Which captures the call offers, now and on every change. A capture is offered while the call is connected, the model
|
|
384
|
+
* takes inbound video, the host can open it (a camera controller; screen sharing) and the call's policy admits it (its
|
|
385
|
+
* channel is in the call and may show the agent pixels). A host shows its Camera and Share controls from this. Neither
|
|
386
|
+
* is offered outside a call.
|
|
387
|
+
*/
|
|
388
|
+
readonly CaptureOffers$: Observable<RealtimeCaptureOffers>;
|
|
389
|
+
private readonly _microphone$;
|
|
390
|
+
/**
|
|
391
|
+
* The call's microphone, now and on every change: the one in use and the ones the user can move it to with
|
|
392
|
+
* {@link SwitchMicrophone}, as the host's camera-and-microphone controller lists them. Offered from the moment the call
|
|
393
|
+
* connects until it ends, and kept current as devices come and go. When the call loses its microphone (the one in use
|
|
394
|
+
* fails and so does the fallback to the default, or the last one goes away), its `Failure` says why: the agent hears
|
|
395
|
+
* nothing until the user picks a microphone, which opens it again. {@link REALTIME_MICROPHONE_NONE} outside a call and
|
|
396
|
+
* on a host without a controller, which opens the microphone itself (`IRealtimeMediaHost.AcquireMicrophone`).
|
|
397
|
+
*/
|
|
398
|
+
readonly Microphone$: Observable<RealtimeMicrophoneState>;
|
|
399
|
+
private readonly _agentVideo$;
|
|
400
|
+
/**
|
|
401
|
+
* The agent's video (an avatar) while the model sends it: a live stream, or a player that owns the `<video>` element.
|
|
402
|
+
* `null` until the video arrives, for a model that sends none, and outside a call. The driver hands it over once it is
|
|
403
|
+
* live, and a newer one replaces it. A host shows it with `AttachVideoSource`; a channel that sinks outbound video
|
|
404
|
+
* follows it through its context, and counts as used once it arrives, so the host shows that channel's surface.
|
|
405
|
+
*/
|
|
406
|
+
readonly AgentVideo$: Observable<MediaVideoSource | null>;
|
|
407
|
+
private readonly _avatarNotice$;
|
|
408
|
+
/**
|
|
409
|
+
* Why the call shows no avatar the agent asked for: set once per call, after it connects, when the model renders none,
|
|
410
|
+
* the persona has no face for it, the app shows no agent video (`host`) or the browser can't play it (`browser`); with
|
|
411
|
+
* no `Reason` when the mint gave one this version doesn't know, such as a newer server's. `null` when there is nothing
|
|
412
|
+
* to say (no avatar asked for, the avatar shows, or the mint reported no status) and after the call ends. A provider
|
|
413
|
+
* resume inside the call does not set it again. A host turns the reason into words once, for example "Audio only: this
|
|
414
|
+
* voice model can't show an avatar".
|
|
415
|
+
*/
|
|
416
|
+
readonly AvatarNotice$: Observable<RealtimeAvatarNotice | null>;
|
|
417
|
+
/** Synchronous access to {@link AvatarNotice$}'s current value. */
|
|
418
|
+
get CurrentAvatarNotice(): RealtimeAvatarNotice | null;
|
|
283
419
|
/**
|
|
284
420
|
* Channel requests to enter / leave the FOCUS layout (see
|
|
285
421
|
* {@link RealtimeChannelFocusEvent}). Fired when a plugin calls its host context's
|
|
@@ -341,7 +477,11 @@ export declare class RealtimeSessionRuntime {
|
|
|
341
477
|
*/
|
|
342
478
|
/** Conversation id the SERVER created for this session (null when the host supplied one). */
|
|
343
479
|
private createdConversationId;
|
|
344
|
-
/**
|
|
480
|
+
/**
|
|
481
|
+
* The session's conversation id (supplied or server-created). While a start's mint runs, the
|
|
482
|
+
* conversation the start asked for; null when it asked the server to create one. Null after
|
|
483
|
+
* teardown.
|
|
484
|
+
*/
|
|
345
485
|
private sessionConversationId;
|
|
346
486
|
/** First final user utterance of the live session (the naming seed). */
|
|
347
487
|
private firstUserTranscript;
|
|
@@ -367,8 +507,28 @@ export declare class RealtimeSessionRuntime {
|
|
|
367
507
|
SetMinimized(minimized: boolean): void;
|
|
368
508
|
/** The provider-direct realtime client driving the live session (ClassFactory-resolved). */
|
|
369
509
|
private client;
|
|
370
|
-
/**
|
|
510
|
+
/**
|
|
511
|
+
* The mic capture stream — acquired here (permission UX) and handed to the client. A microphone the controller opens
|
|
512
|
+
* again after the call lost one replaces it.
|
|
513
|
+
*/
|
|
371
514
|
private localStream;
|
|
515
|
+
/**
|
|
516
|
+
* Whether the user muted the call's microphone, as {@link ToggleMute} last reported it. While the call has no microphone
|
|
517
|
+
* track there is nothing to mute, and a microphone opened again after the call lost one starts this way.
|
|
518
|
+
*/
|
|
519
|
+
private microphoneMuted;
|
|
520
|
+
/** The host's camera-and-microphone controller for this session, when the host offers one. */
|
|
521
|
+
private localMedia;
|
|
522
|
+
/** Follows the controller's microphone, so a swapped-in track or a reopened microphone reaches the driver and the recorder. */
|
|
523
|
+
private localMediaSubscription;
|
|
524
|
+
/**
|
|
525
|
+
* Whether the call offers its microphone for switching ({@link Microphone$}, {@link SwitchMicrophone}): from the moment
|
|
526
|
+
* the client is connected until the call ends. Not before: a driver still connecting may have bound the old track.
|
|
527
|
+
*/
|
|
528
|
+
private microphoneOffered;
|
|
529
|
+
/** The live session's camera and screen share; created once the client is connected. */
|
|
530
|
+
private captures;
|
|
531
|
+
private capturesSubscription;
|
|
372
532
|
private agentSessionId;
|
|
373
533
|
/**
|
|
374
534
|
* The application the active session runs in (sources the server-side app config cascade +
|
|
@@ -402,18 +562,54 @@ export declare class RealtimeSessionRuntime {
|
|
|
402
562
|
private narrationTemplate;
|
|
403
563
|
/**
|
|
404
564
|
* The active session's audio recorder (mic + agent mix), or `null` when the user didn't
|
|
405
|
-
* consent or the browser can't record. Created after the client connects;
|
|
406
|
-
* at teardown.
|
|
565
|
+
* consent or the browser can't record. Created after the client connects; its packets are
|
|
566
|
+
* shipped as 5 s shards during the call, and it is stopped at teardown.
|
|
407
567
|
*/
|
|
408
568
|
private recorder;
|
|
409
569
|
/** ISO timestamp of when recording started — sent to the server on session start. */
|
|
410
570
|
private recordingStartedAtIso;
|
|
411
|
-
/** Interval that
|
|
412
|
-
private
|
|
413
|
-
/**
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
571
|
+
/** Interval that cuts and uploads a shard every {@link recordingShardFlushMs} during a recording. */
|
|
572
|
+
private shardFlushTimer;
|
|
573
|
+
/**
|
|
574
|
+
* The current recording's shard pipeline (window → store → queue → transport), or null when not
|
|
575
|
+
* recording. A fresh pipeline per recording keeps a flush still in flight from a previous session
|
|
576
|
+
* off this session's indexes. Its queue is closed when the recording ends; anything it had not
|
|
577
|
+
* delivered stays in the local store for the next-load drain.
|
|
578
|
+
*/
|
|
579
|
+
private recordingShards;
|
|
580
|
+
/** How often packets are cut into shards: the 5 s shard window, so each tick seals about one window. */
|
|
581
|
+
private static readonly recordingShardFlushMs;
|
|
582
|
+
/**
|
|
583
|
+
* Bytes of `keepalive` request bodies one terminal may use, the tail included. Browsers share about
|
|
584
|
+
* 64 KiB of in-flight keepalive bodies per page; the brief reserves 48 KiB of it for the recording,
|
|
585
|
+
* leaving room for a host's own close beacon (Caliber sends one from `pagehide` too).
|
|
586
|
+
*/
|
|
587
|
+
private static readonly recordingKeepaliveBudgetBytes;
|
|
588
|
+
/**
|
|
589
|
+
* Bytes of this page's recording `keepalive` requests still in flight. The browser's keepalive budget
|
|
590
|
+
* is per page, and closing a tab fires `visibilitychange` and `pagehide` back to back, so each dispatch
|
|
591
|
+
* budgets against what the previous one has not finished sending.
|
|
592
|
+
*/
|
|
593
|
+
private keepaliveBytesInFlight;
|
|
594
|
+
/** `sessionId/index` of shards whose `keepalive` copy is still in flight; never sent twice at once. */
|
|
595
|
+
private readonly keepaliveShardsInFlight;
|
|
596
|
+
/**
|
|
597
|
+
* Most runs one drain call makes (each run is itself bounded): it runs again when it was re-armed while
|
|
598
|
+
* running, or when a run deleted shards the server refused for good, which frees the page for the
|
|
599
|
+
* shards behind them.
|
|
600
|
+
*/
|
|
601
|
+
private static readonly maxDrainRunsPerCall;
|
|
602
|
+
/** The durable local shard store, created on first use and shared by every recording of this runtime. */
|
|
603
|
+
private shardStore;
|
|
604
|
+
/**
|
|
605
|
+
* True once a drain ran with a transport; the drain then does nothing until a recording ends and
|
|
606
|
+
* re-arms it ({@link rearmPendingShardDrain}).
|
|
607
|
+
*/
|
|
608
|
+
private pendingShardsDrained;
|
|
609
|
+
/** Bumped by every re-arm, so a drain already in flight cannot mark itself done over a re-arm. */
|
|
610
|
+
private drainGeneration;
|
|
611
|
+
/** The drain in flight, so overlapping callers share it instead of uploading the same shards twice. */
|
|
612
|
+
private drainInFlight;
|
|
417
613
|
/**
|
|
418
614
|
* Interval that tells the server this session is still in use, or `null` when no session is
|
|
419
615
|
* running. See {@link startLivenessPulse} for why the server cannot work this out itself.
|
|
@@ -430,12 +626,33 @@ export declare class RealtimeSessionRuntime {
|
|
|
430
626
|
* Recording-relative ms offset at which the IN-FLIGHT (not-yet-finalized) turn's audio
|
|
431
627
|
* actually BEGAN — captured the moment that turn's audio/text starts flowing (its first
|
|
432
628
|
* interim transcript), NOT inherited from the previous turn's end. `null` before the first
|
|
433
|
-
* turn / between turns (until the next turn's audio starts)
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
* the
|
|
629
|
+
* turn / between turns (until the next turn's audio starts), and it stays `null` for a turn
|
|
630
|
+
* whose start was never measured. Sent as `utteranceStartMs` on the turn's final transcript
|
|
631
|
+
* AS-IS — `null` is relayed as `null`, never replaced with 0 — so per-turn timing lines up
|
|
632
|
+
* with the recording even when a tool-call / silence gap sits between turns (the
|
|
633
|
+
* inherit-previous-end model mis-stamped the post-gap turn at the pre-gap offset). See
|
|
634
|
+
* {@link markTurnAudioStart}. A user speech onset signalled by the client does NOT land here;
|
|
635
|
+
* it has its own slot, {@link userSpeechStartMs}.
|
|
437
636
|
*/
|
|
438
637
|
private currentTurnStartMs;
|
|
638
|
+
/**
|
|
639
|
+
* Recording-relative ms offset of the first user speech onset the client signalled
|
|
640
|
+
* (`OnUserSpeechStart`) since the last final user transcript, or `null` when none was signalled.
|
|
641
|
+
* It has its own slot, apart from {@link currentTurnStartMs}, so a user onset never claims the
|
|
642
|
+
* assistant turn's start: OpenAI user transcripts arrive asynchronously, often after the
|
|
643
|
+
* assistant's interims (or its final) for the reply have already flowed. See
|
|
644
|
+
* {@link markUserSpeechStart}.
|
|
645
|
+
*/
|
|
646
|
+
private userSpeechStartMs;
|
|
647
|
+
/**
|
|
648
|
+
* `true` once an assistant turn has finalized after {@link userSpeechStartMs} was stamped BEFORE
|
|
649
|
+
* that turn began. The held onset may still be claimed by a late user transcript (OpenAI
|
|
650
|
+
* transcribes asynchronously), but the NEXT onset replaces it: an onset no transcript ever claimed
|
|
651
|
+
* (a VAD false positive, a failed transcription) must not become the start of the next real user
|
|
652
|
+
* turn. A barge-in onset (stamped during the assistant turn) is not superseded by that turn's
|
|
653
|
+
* final. An EMPTY user transcript releases the onset outright (see onClientTranscript).
|
|
654
|
+
*/
|
|
655
|
+
private userSpeechStartSuperseded;
|
|
439
656
|
/**
|
|
440
657
|
* Wall-anchor of the SESSION clock (#3832): `performance.now()` at the moment the call went
|
|
441
658
|
* live, or `null` before any call has. Read only through {@link nowTurnOffsetMs}.
|
|
@@ -486,6 +703,11 @@ export declare class RealtimeSessionRuntime {
|
|
|
486
703
|
private pendingUsageInput;
|
|
487
704
|
/** Accumulated output-token delta since the last flush. */
|
|
488
705
|
private pendingUsageOutput;
|
|
706
|
+
/**
|
|
707
|
+
* Accumulated per-modality usage since the last flush (input and output detail blocks, avatar
|
|
708
|
+
* video seconds included), relayed in full; `null` when no update carried any.
|
|
709
|
+
*/
|
|
710
|
+
private pendingUsageDetails;
|
|
489
711
|
/** Pending debounced usage flush; also force-flushed at teardown. */
|
|
490
712
|
private usageFlushTimer;
|
|
491
713
|
/** Active push-status subscription that feeds delegation progress; cleared on teardown. */
|
|
@@ -528,7 +750,20 @@ export declare class RealtimeSessionRuntime {
|
|
|
528
750
|
*/
|
|
529
751
|
private teardownInFlight;
|
|
530
752
|
/**
|
|
531
|
-
*
|
|
753
|
+
* The latest start's opening of the host's microphone, until {@link IRealtimeMediaHost.ReleaseMicrophone} hands it
|
|
754
|
+
* back; `null` when no opening is owed a release, so a session that ends before asking for the microphone releases
|
|
755
|
+
* nothing.
|
|
756
|
+
*
|
|
757
|
+
* The host gets one release per opening, after the opening returns. A release while the microphone is still
|
|
758
|
+
* opening undoes nothing: the React Native host puts iOS into the call audio category inside `AcquireMicrophone`,
|
|
759
|
+
* after the permission prompt. So {@link runTeardown} releases an opening that has returned and leaves one still
|
|
760
|
+
* opening to its start, which releases it once it returns ({@link unwindAbandonedStart}), unless a newer start has
|
|
761
|
+
* opened the microphone since; that start's own end releases it. Whoever releases clears this first, so no
|
|
762
|
+
* opening is released twice.
|
|
763
|
+
*/
|
|
764
|
+
private microphoneLease;
|
|
765
|
+
/**
|
|
766
|
+
* Why the latest session start failed, or `null` when it has not. Each start clears it ({@link beginSessionStart}).
|
|
532
767
|
*
|
|
533
768
|
* The runtime reports failure as `'error'` on {@link ConnectionState$}, which is enough to show
|
|
534
769
|
* *that* something went wrong but not *what* — and the difference matters at exactly one point:
|
|
@@ -537,6 +772,11 @@ export declare class RealtimeSessionRuntime {
|
|
|
537
772
|
* runtime's own try/catch, so the host never sees that rejection itself.
|
|
538
773
|
*/
|
|
539
774
|
private lastStartError;
|
|
775
|
+
/**
|
|
776
|
+
* The fatal error the call's client reported, or `null` when it has reported none. Each start clears it
|
|
777
|
+
* ({@link beginSessionStart}). See {@link LastSessionError}.
|
|
778
|
+
*/
|
|
779
|
+
private lastSessionError;
|
|
540
780
|
/** Debounce window for persisting a channel's state of record after a change burst. */
|
|
541
781
|
private static readonly channelSaveDebounceMs;
|
|
542
782
|
/**
|
|
@@ -561,8 +801,82 @@ export declare class RealtimeSessionRuntime {
|
|
|
561
801
|
*/
|
|
562
802
|
get Provider(): IMetadataProvider;
|
|
563
803
|
set Provider(value: IMetadataProvider | null);
|
|
804
|
+
private _launcher;
|
|
805
|
+
/**
|
|
806
|
+
* How this runtime mints a session — the stock `StartRealtimeClientSession` mutation unless a host
|
|
807
|
+
* installs its own {@link IRealtimeSessionLauncher} (a guest-session exchange, a deployment-specific
|
|
808
|
+
* mutation). Everything after the mint — channel scoping, the driver, transcripts, teardown — is
|
|
809
|
+
* unchanged. Assign `null` to restore the default.
|
|
810
|
+
*/
|
|
811
|
+
get Launcher(): IRealtimeSessionLauncher;
|
|
812
|
+
set Launcher(value: IRealtimeSessionLauncher | null);
|
|
813
|
+
/**
|
|
814
|
+
* Loads channel code on demand. Called, with the `ClientPluginClass` keys of every channel about to be
|
|
815
|
+
* resolved (the registry's rows plus the host's declarations), after the registry is read and before any
|
|
816
|
+
* plugin is built — so a host can register a heavy channel's class only when the session could use it.
|
|
817
|
+
* It must resolve once the classes it knows are registered; a rejection is logged, never fatal.
|
|
818
|
+
*/
|
|
819
|
+
ChannelClassLoader: ((clientPluginClasses: readonly string[]) => Promise<void>) | null;
|
|
820
|
+
private readonly _sessionEvents$;
|
|
821
|
+
/**
|
|
822
|
+
* Every event the server publishes to THIS session, for the session's life (identity verification
|
|
823
|
+
* today; apps add their own types). Hot, with no replay — subscribe before the session starts, once,
|
|
824
|
+
* and it keeps working across sessions. The runtime has already acted on the events it understands
|
|
825
|
+
* (see {@link handleIdentityVerified}) by the time one reaches here; this stream is for the host UI.
|
|
826
|
+
*
|
|
827
|
+
* Silent when the session's provider has no subscription transport (see {@link createSessionEventSource}).
|
|
828
|
+
*/
|
|
829
|
+
readonly SessionEvents$: Observable<RealtimeSessionStreamEvent>;
|
|
830
|
+
/** Keeps the live session's event stream open; null between sessions. */
|
|
831
|
+
private sessionEventHub;
|
|
832
|
+
/** Per-type handlers the runtime runs (before publishing on {@link SessionEvents$}) for events it understands. */
|
|
833
|
+
private readonly sessionEventHandlers;
|
|
834
|
+
/**
|
|
835
|
+
* What the agent should SAY the moment the user's identity is verified, or `null` (the default) to
|
|
836
|
+
* say nothing — the agent is still told, silently, through a context note. A string is used as the
|
|
837
|
+
* instruction verbatim; a function receives the verified payload (e.g. to greet by name) and returns
|
|
838
|
+
* the instruction, or `null` to stay silent for this verification.
|
|
839
|
+
*/
|
|
840
|
+
IdentityVerifiedSpokenResponse: string | ((payload: IdentityVerifiedEventPayload) => string | null) | null;
|
|
841
|
+
private readonly clientDeadline;
|
|
842
|
+
/**
|
|
843
|
+
* The client's copy of the session's absolute deadline (`null` when none is known). A host that
|
|
844
|
+
* knows the session's cap at start sets it with {@link SetSessionDeadline}; verification extends it
|
|
845
|
+
* from the server's `identity.verified` payload. The SERVER enforces the real deadline — this exists
|
|
846
|
+
* so the host can end the call gracefully first and show a countdown. It only ever moves later by
|
|
847
|
+
* the server's word, so nothing a client does can extend the real one.
|
|
848
|
+
*/
|
|
849
|
+
get SessionDeadline$(): Observable<Date | null>;
|
|
850
|
+
/** Synchronous access to {@link SessionDeadline$}. */
|
|
851
|
+
get SessionDeadline(): Date | null;
|
|
852
|
+
/** Sets the baseline deadline a host knows at session start (`null` clears). See {@link SessionDeadline$}. */
|
|
853
|
+
SetSessionDeadline(deadline: Date | null): void;
|
|
854
|
+
/**
|
|
855
|
+
* Builds the transport that reads the session's events. The default rides the session's GraphQL
|
|
856
|
+
* provider; it returns `null` — "no session events here" — for a provider with no subscription
|
|
857
|
+
* support (a test double, a host on another transport), which the runtime tolerates. Override to
|
|
858
|
+
* supply another transport.
|
|
859
|
+
*/
|
|
860
|
+
protected createSessionEventSource(): IRealtimeSessionEventSource | null;
|
|
861
|
+
/** Opens the live session's event stream. Best-effort: never disturbs the call. */
|
|
862
|
+
private startSessionEvents;
|
|
863
|
+
private stopSessionEvents;
|
|
864
|
+
/** Routes one event to its per-type handler, then publishes it. An event for another session is dropped. */
|
|
865
|
+
private routeSessionEvent;
|
|
866
|
+
/**
|
|
867
|
+
* `identity.verified`: keep the client's deadline in step with the server's, tell the model — as a
|
|
868
|
+
* silent, structured note — who it is now talking to, and, when the host configured it, have it say
|
|
869
|
+
* something about that. The payload is validated again here (it crossed a wire); the name rides as
|
|
870
|
+
* JSON so user-entered text can never break out of the note's frame.
|
|
871
|
+
*/
|
|
872
|
+
private handleIdentityVerified;
|
|
564
873
|
/** True when a session is currently open. */
|
|
565
874
|
get IsActive(): boolean;
|
|
875
|
+
/**
|
|
876
|
+
* True when a session is open or starting for `conversationId`. Ids are compared without
|
|
877
|
+
* regard to case.
|
|
878
|
+
*/
|
|
879
|
+
IsActiveFor(conversationId: string | null | undefined): boolean;
|
|
566
880
|
/**
|
|
567
881
|
* Start a client-direct voice session fronting `targetAgentId`.
|
|
568
882
|
*
|
|
@@ -593,15 +907,22 @@ export declare class RealtimeSessionRuntime {
|
|
|
593
907
|
* pickers, and never synthesize overrides beyond what the user explicitly chose.
|
|
594
908
|
* Omit/`null` for the server's defaults (today's behavior).
|
|
595
909
|
* @param recordingConsent Optional EXPLICIT "record this call" consent for THIS session. When
|
|
596
|
-
* `true`, the browser records a mic + agent-audio mix and uploads it
|
|
910
|
+
* `true`, the browser records a mic + agent-audio mix and uploads it as 5 s Opus shards during the call. When
|
|
597
911
|
* omitted/`null`, the per-user persisted preference (`mj.realtimeVoice.recordingConsent.v1`
|
|
598
912
|
* via {@link UserInfoEngine}) is read as the default. `false` never records.
|
|
599
913
|
* @param mediaCollectionId Optional per-session media-kit override (`MJ: Collections.ID`). When set,
|
|
600
914
|
* the server-side Media channel resolves THIS collection as the agent's media kit for the session,
|
|
601
915
|
* taking precedence over the agent's `DefaultMediaCollectionID`. The server UUID-validates it
|
|
602
916
|
* (malformed ⇒ ignored, the agent default applies). Omit/`null` to use the agent default kit.
|
|
917
|
+
* @param applicationId Optional application the session runs in (sources the app config cascade,
|
|
918
|
+
* including `Application.AgentSettings.Realtime.Channels`).
|
|
919
|
+
* @param appContext Optional live app-context snapshot injected into the companion prompt at mint.
|
|
920
|
+
* @param options Optional per-start extras — chiefly the channels the HOST brings
|
|
921
|
+
* ({@link RealtimeSessionStartOptions.HostChannels}), the way a connect-only embed with no
|
|
922
|
+
* registry gets channels at all, and the fork the session starts on
|
|
923
|
+
* ({@link RealtimeSessionStartOptions.ConversationBranchID}; omitted is Main, where its transcript is written).
|
|
603
924
|
*/
|
|
604
|
-
StartRealtimeSession(targetAgentId: string, conversationId?: string | null, lastSessionId?: string | null, agentName?: string | null, preferredModelId?: string | null, clientTools?: RealtimeToolDefinition[] | null, coAgentId?: string | null, configOverridesJson?: string | null, recordingConsent?: boolean | null, mediaCollectionId?: string | null, applicationId?: string | null, appContext?: AppContextSnapshot | null): Promise<void>;
|
|
925
|
+
StartRealtimeSession(targetAgentId: string, conversationId?: string | null, lastSessionId?: string | null, agentName?: string | null, preferredModelId?: string | null, clientTools?: RealtimeToolDefinition[] | null, coAgentId?: string | null, configOverridesJson?: string | null, recordingConsent?: boolean | null, mediaCollectionId?: string | null, applicationId?: string | null, appContext?: AppContextSnapshot | null, options?: RealtimeSessionStartOptions | null): Promise<void>;
|
|
605
926
|
/**
|
|
606
927
|
* Declares which provider keys this host can actually carry audio for.
|
|
607
928
|
*
|
|
@@ -641,42 +962,173 @@ export declare class RealtimeSessionRuntime {
|
|
|
641
962
|
* owns that half too.
|
|
642
963
|
*
|
|
643
964
|
* @param result The minted session — the same ten fields the `StartRealtimeClientSession`
|
|
644
|
-
* mutation returns. `EphemeralToken` and `Provider` are what actually open the call
|
|
965
|
+
* mutation returns. `EphemeralToken` and `Provider` are what actually open the call; a relay
|
|
966
|
+
* session opens with its `Transport` and `RelayUrl` instead, so a host must pass those on.
|
|
645
967
|
* @param options Host-side inputs the result cannot carry; see {@link RealtimeSessionRunOptions}.
|
|
646
968
|
* Every field defaults exactly as its {@link StartRealtimeSession} counterpart does.
|
|
647
969
|
*/
|
|
648
970
|
StartRealtimeSessionFromResult(result: StartRealtimeClientSessionResult, options?: RealtimeSessionRunOptions): Promise<void>;
|
|
649
971
|
/**
|
|
650
|
-
* Start prologue shared by both entry points:
|
|
651
|
-
* per-session state, and flip the session live (which is ALSO what
|
|
652
|
-
* suppress duplicate starts while the mint is still in flight — hence it
|
|
653
|
-
* after). Returns the resolved recording consent, which the mint half
|
|
654
|
-
* the run half uses to decide whether to record.
|
|
972
|
+
* Start prologue shared by both entry points: clear the last start's failure, bind the app layer,
|
|
973
|
+
* publish the agent name, reset per-session state, and flip the session live (which is ALSO what
|
|
974
|
+
* makes the `IsActive` guard suppress duplicate starts while the mint is still in flight — hence it
|
|
975
|
+
* runs before minting, not after). Returns the resolved recording consent, which the mint half
|
|
976
|
+
* reports to the server and the run half uses to decide whether to record.
|
|
655
977
|
*/
|
|
656
978
|
private beginSessionStart;
|
|
657
979
|
/**
|
|
658
980
|
* The RUN half of a session start, shared by both entry points: consume the minted result, open
|
|
659
981
|
* the provider connection, and go live. `inputConversationId` is the conversation the START asked
|
|
660
|
-
* for (null ⇒ "server, make me one") — the result alone can't distinguish the two.
|
|
982
|
+
* for (null ⇒ "server, make me one") — the result alone can't distinguish the two. `cameraCheck` is
|
|
983
|
+
* whether the host shows a camera check ({@link RealtimeSessionStartOptions.CameraCheck}).
|
|
661
984
|
*/
|
|
662
985
|
private runMintedSession;
|
|
986
|
+
/**
|
|
987
|
+
* Tells the host, once per call, why it shows no avatar ({@link AvatarNotice$}): from the mint's status, whether this
|
|
988
|
+
* session asked for the agent's video, and whether that track is live now that the call is connected.
|
|
989
|
+
*/
|
|
990
|
+
private publishAvatarNotice;
|
|
663
991
|
/**
|
|
664
992
|
* Releases everything a start acquired after the host had already ended the session.
|
|
665
993
|
*
|
|
666
994
|
* Reached only when {@link teardown} ran while this start was awaiting the microphone or the
|
|
667
|
-
* provider connection
|
|
668
|
-
*
|
|
669
|
-
*
|
|
995
|
+
* provider connection, whether that wait then succeeded or failed: the host has ended the call,
|
|
996
|
+
* so a failure there is not reported. Teardown could not release what did not exist yet, so this
|
|
997
|
+
* start owns the cleanup: stop the microphone, hand it back to the host when teardown left that
|
|
998
|
+
* to it ({@link microphoneLease}), close the provider connection, and close the server-side
|
|
999
|
+
* session row that the mint created when no teardown has closed it or is closing it.
|
|
1000
|
+
*
|
|
1001
|
+
* @param lease This start's opening of the microphone.
|
|
670
1002
|
*/
|
|
671
1003
|
private unwindAbandonedStart;
|
|
672
1004
|
/**
|
|
673
|
-
*
|
|
1005
|
+
* Opens the user's microphone: through the host's controller when it offers one, otherwise through
|
|
1006
|
+
* {@link IRealtimeMediaHost.AcquireMicrophone}. A failure is returned rather than thrown, so the caller can
|
|
1007
|
+
* first check whether the start was abandoned meanwhile (a teardown disposes the controller, which fails a
|
|
1008
|
+
* start still in flight) and unwind quietly instead of reporting an error nobody is waiting for.
|
|
1009
|
+
*/
|
|
1010
|
+
private openMicrophone;
|
|
1011
|
+
/**
|
|
1012
|
+
* Opens the provider connection. As with {@link openMicrophone}, a failure is returned rather than thrown, so the
|
|
1013
|
+
* caller can first check whether the start was abandoned meanwhile and unwind quietly instead of reporting an error
|
|
1014
|
+
* nobody is waiting for: a teardown disconnects the client, which with most drivers fails a connection still opening.
|
|
1015
|
+
*/
|
|
1016
|
+
private connectClient;
|
|
1017
|
+
/**
|
|
1018
|
+
* Moves the driver and the recorder onto the call's microphone whenever the controller gives it a new track. A device
|
|
1019
|
+
* switch, or a lost device replaced by the default, swaps a track into the same stream, and the controller carries the
|
|
1020
|
+
* old track's mute over. A microphone opened again after the call lost one comes in a new stream: its track takes the
|
|
1021
|
+
* call's mute, and the stream becomes the one {@link ToggleMute} and the teardown act on. Mid-swap the stream holds no
|
|
1022
|
+
* track, so only a finished swap is followed. Every report also keeps {@link Microphone$} current once the call offers
|
|
1023
|
+
* it.
|
|
1024
|
+
*/
|
|
1025
|
+
private followMicrophone;
|
|
1026
|
+
/** The client is connected: the call offers its microphone on {@link Microphone$}, and {@link SwitchMicrophone} moves it. */
|
|
1027
|
+
private offerMicrophone;
|
|
1028
|
+
/** Publishes the call's microphone from the controller's state while the call offers it, when it names a change. */
|
|
1029
|
+
private publishMicrophone;
|
|
1030
|
+
/** Stops following the controller and disposes it, which releases its devices; the call no longer offers a microphone. */
|
|
1031
|
+
private closeLocalMedia;
|
|
1032
|
+
/**
|
|
1033
|
+
* Creates the session's camera and screen share and mirrors their state on {@link Captures$}. With `cameraCheck`, the
|
|
1034
|
+
* call's first camera start waits for {@link ConfirmCamera}.
|
|
1035
|
+
*/
|
|
1036
|
+
private openCaptures;
|
|
1037
|
+
/**
|
|
1038
|
+
* The host's frame sampler for the captures ({@link IRealtimeMediaHost.CreateFrameSampler}), or `undefined` when it offers
|
|
1039
|
+
* none and the captures use the DOM default.
|
|
1040
|
+
*/
|
|
1041
|
+
private hostFrameSampler;
|
|
1042
|
+
/** Stops the camera and screen share, if any, and reports both off. */
|
|
1043
|
+
private closeCaptures;
|
|
1044
|
+
/** Works out which captures the call offers ({@link CaptureOffers$}) and publishes a change. */
|
|
1045
|
+
private refreshCaptureOffers;
|
|
1046
|
+
/** Whether the host can open a capture and the call's policy admits it. */
|
|
1047
|
+
private canOffer;
|
|
1048
|
+
/** What a capture reports when there is no live session to show it to. */
|
|
1049
|
+
private noSessionCapture;
|
|
1050
|
+
/**
|
|
1051
|
+
* The session's policy for a capture. The channel that fronts it (its `CaptureKind`) must be in the session, open or
|
|
1052
|
+
* advertised, and the server's policy must let the agent see pixels through it (the agent's configuration and any
|
|
1053
|
+
* zero-data-retention rule). The user's own "agent can see" choice for the channel does not refuse a start: it decides
|
|
1054
|
+
* whether the frames reach the model.
|
|
1055
|
+
*/
|
|
1056
|
+
private admitCapture;
|
|
1057
|
+
/** The channel in the session, open or advertised, that fronts a capture. */
|
|
1058
|
+
private findCaptureChannel;
|
|
1059
|
+
/**
|
|
1060
|
+
* Brings up the channel that fronts a capture once the capture is starting, so the host shows its surface: an advertised
|
|
1061
|
+
* channel is opened (and announces itself to the agent), an open one counts as used.
|
|
1062
|
+
*/
|
|
1063
|
+
private revealCaptureChannel;
|
|
1064
|
+
/**
|
|
1065
|
+
* Starts the user's camera and shows it to the agent. Call it from the user's click: it may ask for camera
|
|
1066
|
+
* permission. Resolves with the camera's state; a failure (no session, a model that takes no video, a refused
|
|
1067
|
+
* permission) is a state with a message, never a throw. While the camera is on, it is a source on
|
|
1068
|
+
* {@link VideoSources$}, and it stops by itself if the device goes away.
|
|
1069
|
+
*
|
|
1070
|
+
* When the host shows a camera check ({@link RealtimeSessionStartOptions.CameraCheck}), the call's first start
|
|
1071
|
+
* resolves still starting, with `Checking` set and the stream for the host to preview. The agent sees the camera
|
|
1072
|
+
* once the host calls {@link ConfirmCamera}; {@link StopCamera} is the user's "not now".
|
|
1073
|
+
*
|
|
1074
|
+
* @param deviceId The camera to open; the system default when absent.
|
|
1075
|
+
*/
|
|
1076
|
+
StartCamera(deviceId?: string): Promise<RealtimeCaptureState>;
|
|
1077
|
+
/** Stops the user's camera. Safe to call when it is off. */
|
|
1078
|
+
StopCamera(): void;
|
|
1079
|
+
/**
|
|
1080
|
+
* Moves the user's camera to another device while it is open (being checked or on): one of the camera's `Devices` on
|
|
1081
|
+
* {@link Captures$}. The stream stays the same. Resolves with the camera's state; nothing changes while the camera is
|
|
1082
|
+
* off.
|
|
1083
|
+
*
|
|
1084
|
+
* @param deviceId The camera to move to.
|
|
1085
|
+
*/
|
|
1086
|
+
SwitchCamera(deviceId: string): Promise<RealtimeCaptureState>;
|
|
1087
|
+
/**
|
|
1088
|
+
* Moves the call's microphone to another device: one of the `Devices` on {@link Microphone$}. The host's controller
|
|
1089
|
+
* swaps the new track into the same stream and carries the mute over, and the driver and the call's recording move to
|
|
1090
|
+
* it, so the agent keeps hearing the user. When the new microphone cannot open, the controller goes back to the one in
|
|
1091
|
+
* use. While the call has no microphone open (it lost the one in use and the default, or the last one went away), the
|
|
1092
|
+
* pick opens one again: the driver and the recording move to it, and it keeps the call's mute. Resolves with the
|
|
1093
|
+
* microphone's state once the switch is over. Nothing changes outside a call or on a host without a controller.
|
|
1094
|
+
*
|
|
1095
|
+
* @param deviceId The microphone to move to.
|
|
1096
|
+
*/
|
|
1097
|
+
SwitchMicrophone(deviceId: string): Promise<RealtimeMicrophoneState>;
|
|
1098
|
+
/**
|
|
1099
|
+
* The user checked the camera and turned it on: the agent sees it from now on, and later camera starts in this call
|
|
1100
|
+
* skip the check. Returns the camera's state; unless the camera is waiting for its check (see {@link StartCamera}),
|
|
1101
|
+
* nothing changes.
|
|
1102
|
+
*/
|
|
1103
|
+
ConfirmCamera(): RealtimeCaptureState;
|
|
1104
|
+
/**
|
|
1105
|
+
* Asks the user for a screen, window or browser tab (or one panel of the page) and shows it to the agent. Call it
|
|
1106
|
+
* from the user's click: the browser's picker needs one. Resolves with the share's state; a failure (no session,
|
|
1107
|
+
* a model that takes no video, a closed picker, a host that cannot share) is a state with a message. The share
|
|
1108
|
+
* also stops when the user ends it from the browser's own bar.
|
|
1109
|
+
*
|
|
1110
|
+
* @param options What the picker offers first, or the panel to share and its name (`PanelLabel`), which the share's
|
|
1111
|
+
* state carries and the agent sees as "<name> (shared panel)".
|
|
1112
|
+
*/
|
|
1113
|
+
StartScreenShare(options?: DisplayCaptureOptions): Promise<RealtimeCaptureState>;
|
|
1114
|
+
/** Stops the user's screen share. Safe to call when nothing is shared. */
|
|
1115
|
+
StopScreenShare(): void;
|
|
1116
|
+
/**
|
|
1117
|
+
* Why the latest session start failed, or `null` when it has not failed or none has run.
|
|
674
1118
|
*
|
|
675
1119
|
* Read it when {@link ConnectionState$} reports `'error'`, to tell a denied microphone apart from
|
|
676
|
-
* a provider or backend failure and show copy the user can act on.
|
|
677
|
-
*
|
|
1120
|
+
* a provider or backend failure and show copy the user can act on. Each start clears it before it
|
|
1121
|
+
* reports `'connecting'`, so it never describes an earlier start.
|
|
678
1122
|
*/
|
|
679
1123
|
get LastStartError(): Error | null;
|
|
1124
|
+
/**
|
|
1125
|
+
* The fatal error the call's client reported, or `null` when it has reported none.
|
|
1126
|
+
*
|
|
1127
|
+
* Read it when {@link ConnectionState$} reports `'error'` on a call that was live, to tell a call that reached its time
|
|
1128
|
+
* limit (`Code` `'time-limit'`; {@link DescribeCallEnd} words it) from a dropped connection. Each start clears it before
|
|
1129
|
+
* it reports `'connecting'`.
|
|
1130
|
+
*/
|
|
1131
|
+
get LastSessionError(): RealtimeClientError | null;
|
|
680
1132
|
/**
|
|
681
1133
|
* The single failure path for a session start (mint half or run half): report it, latch the
|
|
682
1134
|
* overlay into 'error', and unwind whatever the half-built session already opened.
|
|
@@ -701,7 +1153,11 @@ export declare class RealtimeSessionRuntime {
|
|
|
701
1153
|
* No-op when no session is open / the control channel isn't ready, or when the text is empty.
|
|
702
1154
|
*/
|
|
703
1155
|
SendText(text: string): void;
|
|
704
|
-
/**
|
|
1156
|
+
/**
|
|
1157
|
+
* Mute / unmute the local microphone track. Returns the new muted state. While the call has no microphone track (a
|
|
1158
|
+
* switch is putting a new one in, or the call lost its microphone) there is nothing to mute: nothing changes, and it
|
|
1159
|
+
* returns the call's mute, which the next microphone keeps. Outside a call it returns `false`.
|
|
1160
|
+
*/
|
|
705
1161
|
ToggleMute(): boolean;
|
|
706
1162
|
/**
|
|
707
1163
|
* Registers a handler for CLIENT-EXECUTED UI tools whose names start with `toolNamePrefix`
|
|
@@ -748,9 +1204,17 @@ export declare class RealtimeSessionRuntime {
|
|
|
748
1204
|
*/
|
|
749
1205
|
get Client(): BaseRealtimeClient | null;
|
|
750
1206
|
/**
|
|
751
|
-
* Relays a video frame to the underlying realtime client if
|
|
1207
|
+
* Relays a video frame straight to the underlying realtime client, if a session is live.
|
|
1208
|
+
*
|
|
1209
|
+
* It bypasses the session's {@link VideoSourceArbiter}: the frame overrides the source the arbiter picked, and the
|
|
1210
|
+
* model is not told what it is looking at. Channels no longer send through it (their context's `SendVideoFrame` goes
|
|
1211
|
+
* through the arbiter); a host that shows the model something registers a source there instead, as
|
|
1212
|
+
* `ChannelInboundVideoBridge` does.
|
|
1213
|
+
*
|
|
1214
|
+
* @returns `true` when the client accepted and sent the frame; `false` when no session is live, the
|
|
1215
|
+
* client cannot take video frames, or the client dropped the frame.
|
|
752
1216
|
*/
|
|
753
|
-
SendVideoFrame(base64Image: string, mimeType?: string):
|
|
1217
|
+
SendVideoFrame(base64Image: string, mimeType?: string): boolean;
|
|
754
1218
|
/**
|
|
755
1219
|
* Checks whether a media track is established on the active realtime client.
|
|
756
1220
|
*/
|
|
@@ -767,10 +1231,60 @@ export declare class RealtimeSessionRuntime {
|
|
|
767
1231
|
* nothing (the recorder disables itself).
|
|
768
1232
|
*/
|
|
769
1233
|
private startRecording;
|
|
770
|
-
/**
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1234
|
+
/**
|
|
1235
|
+
* Builds the transport for shard uploads and terminal requests. The default posts to MJAPI's REST
|
|
1236
|
+
* recording routes, derived from the session's GraphQL provider configuration, with credentials
|
|
1237
|
+
* read per request so a refreshed token is used. Returns null — shards then wait in the local
|
|
1238
|
+
* store — when the provider has no URL (a test double, a provider that is not GraphQL, or one not
|
|
1239
|
+
* configured yet). Override to supply another transport.
|
|
1240
|
+
*/
|
|
1241
|
+
protected createRecordingTransport(): IRecordingTransport | null;
|
|
1242
|
+
/**
|
|
1243
|
+
* Builds the durable local shard store. The default is IndexedDB where the host has it and a no-op
|
|
1244
|
+
* store elsewhere (Node, some React Native runtimes). Called once per runtime. Override to supply
|
|
1245
|
+
* another store.
|
|
1246
|
+
*/
|
|
1247
|
+
protected createRecordingShardStore(): IRecordingShardStore;
|
|
1248
|
+
/** The provider's signed-in user, or null (no provider yet, or an anonymous page). */
|
|
1249
|
+
private signedInUserID;
|
|
1250
|
+
/** The runtime's shard store, created on first use. */
|
|
1251
|
+
private ensureShardStore;
|
|
1252
|
+
/**
|
|
1253
|
+
* Re-uploads shards an earlier, interrupted call left in the local store (crash, closed tab, dead
|
|
1254
|
+
* network), each under its OWN session id and index, oldest first, at most
|
|
1255
|
+
* {@link MAX_DRAINED_SHARDS_PER_LOAD} per runtime. A shard is deleted only on the server's ack.
|
|
1256
|
+
*
|
|
1257
|
+
* Runs once per runtime instance, re-armed whenever a recording ends (which may leave shards behind
|
|
1258
|
+
* for the next session start on the same page). When there is no transport yet (no provider configuration, e.g.
|
|
1259
|
+
* before login) it returns 0 WITHOUT marking itself done, so a later call — session start — drains
|
|
1260
|
+
* instead of the shards being skipped for this page. Concurrent calls share one run. Never rejects.
|
|
1261
|
+
*
|
|
1262
|
+
* @returns the number of shards the server acknowledged in this call.
|
|
1263
|
+
*/
|
|
1264
|
+
DrainPendingRecordingShards(): Promise<number>;
|
|
1265
|
+
/** Makes the next {@link DrainPendingRecordingShards} call run again, even one already in flight. */
|
|
1266
|
+
private rearmPendingShardDrain;
|
|
1267
|
+
/**
|
|
1268
|
+
* The body of {@link DrainPendingRecordingShards}, up to {@link maxDrainRunsPerCall} runs. It runs
|
|
1269
|
+
* again after a re-arm while a run is in flight (a recovery after an outage gave up on more shards),
|
|
1270
|
+
* because the running pass listed the store before those shards were there; and after a run deleted
|
|
1271
|
+
* refused shards, because those filled the page that the shards behind them now get.
|
|
1272
|
+
*/
|
|
1273
|
+
private drainPendingShards;
|
|
1274
|
+
/** One drain pass over the signed-in user's stored shards (see {@link DrainStoredRecordingShardsOnce}). */
|
|
1275
|
+
private drainStoredShardsOnce;
|
|
1276
|
+
/** Builds this recording's shard pipeline and starts the 5 s flush. */
|
|
1277
|
+
private startShardFlushing;
|
|
1278
|
+
/**
|
|
1279
|
+
* Uploads succeed again after failing: re-sends, from the local store, the shards the queue gave up
|
|
1280
|
+
* on during the outage. The queue's attempts run out after about 10 s of a dead network (or an
|
|
1281
|
+
* expired token), and without this those shards would wait for a later page load — which a widget
|
|
1282
|
+
* guest never makes — leaving a permanent gap. Runs in the background, outside any page-lifecycle
|
|
1283
|
+
* handler, so the IndexedDB read is allowed. Never rejects.
|
|
1284
|
+
*/
|
|
1285
|
+
private recoverStoredShardsAfterOutage;
|
|
1286
|
+
/** Stops the periodic shard flush. */
|
|
1287
|
+
private stopShardFlushing;
|
|
774
1288
|
/**
|
|
775
1289
|
* Starts telling the server this session is still in use (#3533).
|
|
776
1290
|
*
|
|
@@ -802,51 +1316,285 @@ export declare class RealtimeSessionRuntime {
|
|
|
802
1316
|
*/
|
|
803
1317
|
private pulseLiveness;
|
|
804
1318
|
/**
|
|
805
|
-
*
|
|
806
|
-
*
|
|
1319
|
+
* One 5 s tick: cuts the packets encoded since the last tick into shards, stores each one, then
|
|
1320
|
+
* uploads them. Best-effort — never disturbs the call and never rejects. Overlapping ticks are
|
|
1321
|
+
* safe: `Append` and `SealTail` are synchronous, so indexes are assigned in order, and the queue
|
|
1322
|
+
* bounds how many flushes upload at once. A failed shard is retained by the queue and resent under
|
|
1323
|
+
* its own index on later ticks (#5197), and stays in the store until acknowledged.
|
|
1324
|
+
*
|
|
1325
|
+
* Every tick seals whatever it took, even under a full window (the window's 250-packet and byte caps
|
|
1326
|
+
* still split a larger batch). Capture starts slightly after this timer, so a tick almost never
|
|
1327
|
+
* holds exactly 250 packets; sealing only full windows would leave nearly 5 s of encoded audio in
|
|
1328
|
+
* memory at every moment, unstored and unsent, which a killed tab loses.
|
|
1329
|
+
*/
|
|
1330
|
+
private flushRecordingShards;
|
|
1331
|
+
/** Writes shards to the local store, in order, before their upload starts. Never rejects. */
|
|
1332
|
+
private storeShards;
|
|
1333
|
+
/** Removes an acknowledged shard from the local store. Never rejects. */
|
|
1334
|
+
private deleteStoredShard;
|
|
1335
|
+
/**
|
|
1336
|
+
* Tells the server, right now, how much of the recording exists — for a host's `pagehide` and
|
|
1337
|
+
* `visibilitychange → hidden` handlers, where the page may be gone a moment later. Synchronous:
|
|
1338
|
+
* it seals the pending partial shard and fires the `keepalive` terminal with it, then re-sends
|
|
1339
|
+
* with `keepalive` whatever the server has not acknowledged yet (see
|
|
1340
|
+
* {@link dispatchRecordingTerminal}), and returns without waiting. Never stops the recorder: after
|
|
1341
|
+
* a `hidden` the call may well continue, and later audio simply starts the next shard index (the
|
|
1342
|
+
* tail is sealed, so no index is ever re-sent with different bytes). Also works while End is still
|
|
1343
|
+
* stopping the recorder, which is exactly when a host that does not await End navigates away.
|
|
1344
|
+
* No-op when nothing is recording, there is no session id or no transport, or nothing was
|
|
1345
|
+
* captured. Never throws.
|
|
1346
|
+
*
|
|
1347
|
+
* Deliberately opens NO IndexedDB transaction inside the handler: a `pagehide` handler gets a few
|
|
1348
|
+
* milliseconds, and a store write started there could not complete during unload anyway. The
|
|
1349
|
+
* shards it sealed are written to the store from a `setTimeout(0)` task after the handler returns:
|
|
1350
|
+
* on a page that is merely hidden (the call goes on) that keeps the durable copy until the server
|
|
1351
|
+
* acknowledges it; on a page that is unloading the task just never runs.
|
|
1352
|
+
*/
|
|
1353
|
+
SendRecordingTerminal(options?: RecordingTerminalOptions): void;
|
|
1354
|
+
/**
|
|
1355
|
+
* Fires the terminal request, then re-sends with `keepalive` every shard the server has not
|
|
1356
|
+
* acknowledged, then stores the shards sealed for this terminal. Shared by End and the page-lifecycle
|
|
1357
|
+
* handlers. Everything that needs the network is dispatched synchronously, before any storage work,
|
|
1358
|
+
* because the page may close at any moment from here on: an ordinary `fetch` is aborted on unload,
|
|
1359
|
+
* and a `keepalive` one is not.
|
|
1360
|
+
*
|
|
1361
|
+
* - The terminal goes first so it always gets the keepalive budget. The server waits for the shards
|
|
1362
|
+
* the terminal says exist, and assembles as soon as the last missing one arrives.
|
|
1363
|
+
* - The re-sent shards are the queue's unacknowledged ones (on the wire, waiting in a batch, or
|
|
1364
|
+
* retained after a failure) plus `sealedNow`, oldest first, within
|
|
1365
|
+
* {@link recordingKeepaliveBudgetBytes} together with the tail. A `sealedNow` shard that does not
|
|
1366
|
+
* fit, or whose keepalive upload fails, goes through the ordinary queue instead (it may still
|
|
1367
|
+
* land if the page stays). The queue keeps its own shards either way; a duplicate is acked as one.
|
|
1368
|
+
* - `sealedNow` and the tail are stored after the dispatch, from a `setTimeout(0)` task when called
|
|
1369
|
+
* from a page-lifecycle handler (`deferStore`). A shard's stored copy is deleted on its ack, after
|
|
1370
|
+
* the store write, so a fast ack can never leave an orphan row.
|
|
1371
|
+
*
|
|
1372
|
+
* Resolves true when the terminal was acknowledged. Never rejects.
|
|
1373
|
+
*/
|
|
1374
|
+
private dispatchRecordingTerminal;
|
|
1375
|
+
/** Counts `bytes` against the page's keepalive budget until `request` settles. */
|
|
1376
|
+
private trackKeepalive;
|
|
1377
|
+
/**
|
|
1378
|
+
* The `keepalive` re-send part of {@link dispatchRecordingTerminal}, budgeted against everything this
|
|
1379
|
+
* page still has in flight (the terminal just sent included). A shard whose `keepalive` copy is already
|
|
1380
|
+
* on the wire is not sent again. Never throws.
|
|
1381
|
+
*/
|
|
1382
|
+
private resendUnacknowledgedWithKeepalive;
|
|
1383
|
+
/**
|
|
1384
|
+
* Stores shards from a `setTimeout(0)` task rather than now — the path for shards sealed inside a
|
|
1385
|
+
* page-lifecycle handler, which must not open an IndexedDB transaction itself. Resolves once they
|
|
1386
|
+
* are stored (never, if the page unloads first). Never rejects.
|
|
1387
|
+
*/
|
|
1388
|
+
private storeShardsLater;
|
|
1389
|
+
/**
|
|
1390
|
+
* Sends the terminal request for a pipeline whose tail is already sealed. Resolves true on a server
|
|
1391
|
+
* ack and logs the outcome with the session id. Never rejects.
|
|
1392
|
+
*/
|
|
1393
|
+
private sendRecordingTerminal;
|
|
1394
|
+
/**
|
|
1395
|
+
* Ends the recording at teardown and STARTS its terminal request: stops the recorder (flushing the
|
|
1396
|
+
* encoder), cuts the remaining packets into shards with the tail trimmed to the recorder's captured
|
|
1397
|
+
* sample count, and dispatches the `keepalive` terminal plus `keepalive` re-sends of everything not
|
|
1398
|
+
* yet acknowledged, before storing anything ({@link dispatchRecordingTerminal}). Teardown awaits the
|
|
1399
|
+
* returned terminal only after `CloseAgentSession` ({@link awaitRecordingTerminal}), so the small,
|
|
1400
|
+
* critical close never waits behind the recording (#5195).
|
|
1401
|
+
*
|
|
1402
|
+
* The recorder and pipeline stay visible to {@link SendRecordingTerminal} until the recorder has
|
|
1403
|
+
* stopped: a `pagehide` in that window (a host that navigates away without awaiting End) still
|
|
1404
|
+
* sends what was captured, and End then continues at the next index.
|
|
1405
|
+
*
|
|
1406
|
+
* Returns null when nothing was recording or nothing was captured (no terminal is sent then).
|
|
1407
|
+
* Best-effort: never throws, never blocks teardown on the network.
|
|
1408
|
+
*/
|
|
1409
|
+
private finishRecording;
|
|
1410
|
+
/**
|
|
1411
|
+
* Stops the recorder, bounded by {@link RECORDER_STOP_TIMEOUT_MS}. Resolves the captured sample
|
|
1412
|
+
* count, or null when the recorder threw or did not finish in time (logged) — the caller then ends
|
|
1413
|
+
* the recording at the packets already encoded. Never rejects.
|
|
1414
|
+
*/
|
|
1415
|
+
private stopRecorder;
|
|
1416
|
+
/**
|
|
1417
|
+
* Waits for the terminal {@link finishRecording} started, bounded by
|
|
1418
|
+
* {@link RECORDING_TERMINAL_ACK_TIMEOUT_MS}, logs the outcome, then closes the recording's queue
|
|
1419
|
+
* (anything it had not delivered stays in the local store for the next-load drain). Never throws.
|
|
1420
|
+
*/
|
|
1421
|
+
private awaitRecordingTerminal;
|
|
1422
|
+
/** Whether the channel catalog note still has to be sent (see {@link flushChannelCatalogNote}). */
|
|
1423
|
+
private catalogNotePending;
|
|
1424
|
+
/** Plugins resolved for the current start but not yet initialized. Consumed by {@link activateChannels}. */
|
|
1425
|
+
private preparedChannels;
|
|
1426
|
+
/** The scope the browser resolves on its own — the fallback when no server policy comes back. */
|
|
1427
|
+
private localChannelScope;
|
|
1428
|
+
/** `on-demand` channels that are in the session but not opened yet (initialized only when opened). */
|
|
1429
|
+
private advertisedChannels;
|
|
1430
|
+
/** What the policy resolved for each in-session channel, keyed by normalized channel key. */
|
|
1431
|
+
private readonly resolvedChannels;
|
|
1432
|
+
/** The prior session's saved channel states, kept so an `on-demand` channel opened later is restored too. */
|
|
1433
|
+
private priorChannelStates;
|
|
1434
|
+
/** The app/static client-tool tiers the server resolved for this session. */
|
|
1435
|
+
private sessionClientTools;
|
|
1436
|
+
/** Validates and routes channel-addressed `ContextTool` calls. */
|
|
1437
|
+
private readonly channelDispatcher;
|
|
1438
|
+
/**
|
|
1439
|
+
* The resolved behavior of an in-session channel (display, exposure ceiling, config, what put it
|
|
1440
|
+
* in the session), or `null` when the channel is not in this session. Hosts and overlays read the
|
|
1441
|
+
* resolved display from here rather than from the channel's code default.
|
|
1442
|
+
*
|
|
1443
|
+
* @param channelName The channel's key (case-insensitive).
|
|
1444
|
+
*/
|
|
1445
|
+
GetResolvedChannel(channelName: string): ResolvedRealtimeChannel | null;
|
|
1446
|
+
/** Where the user's per-channel "how much can the agent see" choices are kept. Replace with {@link SetExposurePreferences}. */
|
|
1447
|
+
private exposurePreferences;
|
|
1448
|
+
/**
|
|
1449
|
+
* Replaces the store the user's per-channel exposure choices are kept in. The default persists them in
|
|
1450
|
+
* the signed-in user's settings (`mj.realtime.visualPerception.v1`) and keeps them in memory for an
|
|
1451
|
+
* anonymous principal or a connect-only embed; a host that remembers them somewhere else supplies its own.
|
|
1452
|
+
*/
|
|
1453
|
+
SetExposurePreferences(preferences: IChannelExposurePreferences): void;
|
|
1454
|
+
/** The store the user's per-channel exposure choices are currently kept in (so a host can wrap it). */
|
|
1455
|
+
get ExposurePreferences(): IChannelExposurePreferences;
|
|
1456
|
+
/** Whether user settings can be written: a signed-in user on a provider that has entity metadata. */
|
|
1457
|
+
private canPersistUserSettings;
|
|
1458
|
+
/**
|
|
1459
|
+
* How much of a channel the model may perceive right now: the channel's ceiling, lowered by the server's
|
|
1460
|
+
* policy and the user's choice. `null` when the channel is not in this session.
|
|
1461
|
+
*
|
|
1462
|
+
* @param channelName The channel's key (case-insensitive).
|
|
807
1463
|
*/
|
|
808
|
-
|
|
1464
|
+
GetChannelExposure(channelName: string): RealtimeChannelExposure | null;
|
|
809
1465
|
/**
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
1466
|
+
* The user's own choice of how much of a channel the agent may perceive. Takes effect immediately
|
|
1467
|
+
* (frames and notes the new level forbids stop, and the model is told), and is remembered per channel key
|
|
1468
|
+
* for next time. The level can only LOWER what the server's policy allows; choosing `'pixels'` for a channel
|
|
1469
|
+
* the agent's policy capped at `'state'` changes nothing.
|
|
1470
|
+
*
|
|
1471
|
+
* @param channelName The channel's key (case-insensitive).
|
|
1472
|
+
* @param level The user's choice; `undefined` clears it (back to what policy allows).
|
|
1473
|
+
* @returns `false` when the channel is not in this session (nothing is applied, but the choice is still remembered).
|
|
1474
|
+
*/
|
|
1475
|
+
SetUserChannelExposure(channelName: string, level: RealtimeChannelExposure | undefined): boolean;
|
|
1476
|
+
/** Pushes a channel's current exposure inputs (server policy, user choice) into the channel. */
|
|
1477
|
+
private applyChannelExposure;
|
|
1478
|
+
/**
|
|
1479
|
+
* Sends a frame from a channel's context ({@link RealtimeChannelContext.SendVideoFrame}, deprecated) through the
|
|
1480
|
+
* session's {@link VideoSourceArbiter}, as the channel's own source: it reaches the model only while the arbiter picks
|
|
1481
|
+
* that source, and the arbiter tells the model when it switches. The source registers with the first frame, under the
|
|
1482
|
+
* id the channel's own frame bridge uses, switched on only while the channel's exposure allows pixels.
|
|
1483
|
+
*/
|
|
1484
|
+
private sendChannelVideoFrame;
|
|
1485
|
+
/**
|
|
1486
|
+
* Keeps a channel's video source at the arbiter, when it has one, in step with the channel's exposure: on while the
|
|
1487
|
+
* agent may see its pixels, off otherwise. Quiet, because the channel tells the model about its own exposure change.
|
|
1488
|
+
*/
|
|
1489
|
+
private syncChannelVideoSource;
|
|
1490
|
+
/** Follows the session client's video-source arbiter so {@link VideoSources$} reflects it. */
|
|
1491
|
+
private watchVideoSources;
|
|
1492
|
+
/** The channel whose surface the user is looking at, kept so it applies to an arbiter created after it was set. */
|
|
1493
|
+
private focusedChannelKey;
|
|
1494
|
+
/**
|
|
1495
|
+
* Tells the session which channel's surface the user is looking at (`null` for none, e.g. the activity tab).
|
|
1496
|
+
* When the model can see only one video source and several are live, the one the user is looking at is the one
|
|
1497
|
+
* it sees (after an explicit pick, and after a camera or screen share the user started). Safe to call before the
|
|
1498
|
+
* session is live: it applies when the connection comes up.
|
|
1499
|
+
*
|
|
1500
|
+
* @param channelKey The focused channel's key, as it appears on {@link VideoSources$} entries' `ChannelKey`.
|
|
813
1501
|
*/
|
|
814
|
-
|
|
1502
|
+
SetFocusedChannel(channelKey: string | null): void;
|
|
1503
|
+
/** Stops following the arbiter and clears {@link VideoSources$}. Safe to call when nothing is watched. */
|
|
1504
|
+
private unwatchVideoSources;
|
|
1505
|
+
private stopWatchingVideoSources;
|
|
815
1506
|
/**
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
1507
|
+
* Turns the agent's view of one video source on or off — what the "agent can see" control calls.
|
|
1508
|
+
*
|
|
1509
|
+
* A source that belongs to a channel goes through {@link SetUserChannelExposure}, so the choice is
|
|
1510
|
+
* remembered per channel and the channel itself tells the model. Any other source (a camera or screen
|
|
1511
|
+
* share that is not a channel) is switched at the arbiter, which tells the model.
|
|
1512
|
+
*
|
|
1513
|
+
* @param sourceId The source's id (from {@link VideoSources$}).
|
|
1514
|
+
* @param enabled Whether the agent may see it.
|
|
1515
|
+
* @returns `false` when there is no such source.
|
|
819
1516
|
*/
|
|
820
|
-
|
|
1517
|
+
SetVideoSourceEnabled(sourceId: string, enabled: boolean): boolean;
|
|
821
1518
|
/**
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
1519
|
+
* Picks the video source the agent sees: what the "agent can see" control calls when more sources are on than the
|
|
1520
|
+
* model takes. The pick beats every other rule (a camera or screen share the user started, the surface the user is
|
|
1521
|
+
* looking at) until it is cleared, or its source is turned off or leaves. The pick shows on {@link VideoSources$}
|
|
1522
|
+
* (`Picked`), and the model is told when what it sees changes.
|
|
1523
|
+
*
|
|
1524
|
+
* @param sourceId The source's id (from {@link VideoSources$}), or `null` to let the call decide again.
|
|
1525
|
+
* @returns `false` with no live session, or for a source that is not there or is turned off.
|
|
1526
|
+
*/
|
|
1527
|
+
SelectVideoSource(sourceId: string | null): boolean;
|
|
1528
|
+
/**
|
|
1529
|
+
* The `on-demand` channels that are in the session but not open yet — what the agent can open
|
|
1530
|
+
* through `ContextTool`. A fresh array; empty before a session starts and after teardown.
|
|
1531
|
+
*/
|
|
1532
|
+
get AdvertisedChannels(): readonly BaseRealtimeChannelClient[];
|
|
1533
|
+
/**
|
|
1534
|
+
* Resolves, initializes and mounts the session's channels in one step using the scope the browser
|
|
1535
|
+
* can decide alone (code defaults + host declarations), and returns the native tools to declare at
|
|
1536
|
+
* mint. This is the single-step composition of {@link prepareChannelScope} + {@link activateChannels}
|
|
1537
|
+
* that a session start spreads across the mint; it is kept as a unit because it is the cleanest seam
|
|
1538
|
+
* for exercising the plugin plumbing without a mint.
|
|
827
1539
|
*/
|
|
828
1540
|
private startChannels;
|
|
829
1541
|
/**
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
* the
|
|
833
|
-
*
|
|
834
|
-
*
|
|
1542
|
+
* Stage 1: constructs the channel plugins (registry rows and host declarations), reads their
|
|
1543
|
+
* descriptors, resolves the scope the browser can decide alone, and builds what the mint needs —
|
|
1544
|
+
* the native tools to declare and the candidates to report. Starts nothing.
|
|
1545
|
+
*
|
|
1546
|
+
* @param hostChannels Channels the host brings to this session.
|
|
1547
|
+
* @returns The native tools to declare at mint, the candidates as JSON (`null` when there are none,
|
|
1548
|
+
* so a channel-less session sends exactly the mint it always did), and whether the host could show the agent's
|
|
1549
|
+
* video ({@link PreparedChannelsShowAgentVideo}).
|
|
835
1550
|
*/
|
|
836
|
-
private
|
|
1551
|
+
private prepareChannelScope;
|
|
837
1552
|
/**
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
|
|
842
|
-
|
|
1553
|
+
* Constructs a plugin for every active-or-inactive registry row and every host declaration. An
|
|
1554
|
+
* INACTIVE row is constructed too — only to learn its key so the kill switch can name it — and is
|
|
1555
|
+
* never initialized. A plugin whose descriptor cannot be read is skipped (logged), never fatal.
|
|
1556
|
+
*/
|
|
1557
|
+
private prepareChannels;
|
|
1558
|
+
/**
|
|
1559
|
+
* Gives a host that loads channel code on demand (a widget that keeps a heavy channel in its own download)
|
|
1560
|
+
* the chance to load it before the plugins are built. Never fatal: a loader that throws is logged and the
|
|
1561
|
+
* channel simply is not available, exactly as if its class had never been registered.
|
|
1562
|
+
*
|
|
1563
|
+
* @param keys The `ClientPluginClass` keys of every channel about to be resolved (blank/absent ones included).
|
|
1564
|
+
*/
|
|
1565
|
+
private loadChannelClasses;
|
|
1566
|
+
/** Whether a prepared plugin's descriptor can be read; logs and rejects one that throws. */
|
|
1567
|
+
private canDescribe;
|
|
1568
|
+
/** Builds a plugin from a host declaration; `null` (logged) when it names nothing buildable. */
|
|
1569
|
+
private createHostChannelPlugin;
|
|
1570
|
+
/**
|
|
1571
|
+
* Stage 3: initializes the channels the resolved policy puts in the session. `open-on-start` and
|
|
1572
|
+
* `headless` channels are mounted and published on {@link ActiveChannels$}; `on-demand` channels
|
|
1573
|
+
* are held until opened. Prepared plugins the policy left out are dropped without ever having run.
|
|
1574
|
+
*/
|
|
1575
|
+
private activateChannels;
|
|
1576
|
+
/**
|
|
1577
|
+
* Applies the mint's outcome to the channel set: the server's policy when it sent a usable one,
|
|
1578
|
+
* else the scope the browser resolved on its own. A policy can only SELECT among the plugins this
|
|
1579
|
+
* host prepared — it cannot add one.
|
|
1580
|
+
*/
|
|
1581
|
+
private applySessionClientPolicy;
|
|
1582
|
+
/** Drops plugins that were prepared for a start that never mounted them. They were never initialized, so there is nothing to release. */
|
|
1583
|
+
private discardUnmountedChannels;
|
|
1584
|
+
/**
|
|
1585
|
+
* Loads the `MJ: AI Agent Channels` registry rows (ACTIVE AND INACTIVE — an inactive row is the master
|
|
1586
|
+
* kill switch, so the scope decision has to see it). With entity metadata: from {@link AIEngineBase}'s
|
|
1587
|
+
* cached `AgentChannels` (provider-scoped engine instance, lazy `Config` — no RunView round-trip; the
|
|
1588
|
+
* engine's BaseEntity-event reactivity keeps the registry fresh). On a connect-only provider: one
|
|
1589
|
+
* `RunDynamicView` query ({@link fetchChannelDefinitionsOverGraphQL}). Failures are logged and degrade to an
|
|
1590
|
+
* empty list — channel availability must never block the voice session.
|
|
843
1591
|
*/
|
|
844
1592
|
private fetchChannelDefinitions;
|
|
845
1593
|
/**
|
|
846
|
-
* The connect-only path of {@link fetchChannelDefinitions}: the same
|
|
847
|
-
*
|
|
848
|
-
* typed RunView from. Same tolerance as the engine path — a failure is
|
|
849
|
-
* channels", never a blocked session.
|
|
1594
|
+
* The connect-only path of {@link fetchChannelDefinitions}: the same `MJ: AI Agent Channels` rows, active
|
|
1595
|
+
* and inactive (an inactive row is the kill switch), read with a dynamic view because a connect-only client
|
|
1596
|
+
* has no entity metadata to build a typed RunView from. Same tolerance as the engine path — a failure is
|
|
1597
|
+
* logged and means "no channels", never a blocked session.
|
|
850
1598
|
*/
|
|
851
1599
|
private fetchChannelDefinitionsOverGraphQL;
|
|
852
1600
|
/**
|
|
@@ -854,45 +1602,108 @@ export declare class RealtimeSessionRuntime {
|
|
|
854
1602
|
* checked first, exactly like the realtime-client drivers) and instantiates a fresh
|
|
855
1603
|
* per-session plugin. Returns `null` (logged) when no plugin is registered for the key
|
|
856
1604
|
* — e.g. its Load function was never called or the package isn't included client-side.
|
|
1605
|
+
* An inactive row is resolved quietly: it only needs to be named, and a missing plugin for a
|
|
1606
|
+
* channel nobody wants is not worth a warning.
|
|
857
1607
|
*/
|
|
858
1608
|
private resolveChannelPlugin;
|
|
859
1609
|
/**
|
|
860
|
-
* Wires one plugin into the session: hands it its host context
|
|
861
|
-
* prefix-routed local tool executor (so `<ToolNamePrefix>*`
|
|
862
|
-
* through {@link BaseRealtimeChannelClient.ApplyAgentTool}, never the
|
|
1610
|
+
* Wires one plugin into the session: hands it its host context (carrying its resolved config) and,
|
|
1611
|
+
* when it has native tools, registers its prefix-routed local tool executor (so `<ToolNamePrefix>*`
|
|
1612
|
+
* calls run in the browser through {@link BaseRealtimeChannelClient.ApplyAgentTool}, never the
|
|
1613
|
+
* server relay). A channel with no tool prefix has no native route — it is reached through
|
|
1614
|
+
* `ContextTool` — and registering an empty prefix would match EVERY tool name.
|
|
1615
|
+
*/
|
|
1616
|
+
private mountChannel;
|
|
1617
|
+
/** Records that the agent acted on a channel (the overlay's first-use reveal signal). */
|
|
1618
|
+
private noteChannelActivity;
|
|
1619
|
+
/**
|
|
1620
|
+
* Mounts an advertised `on-demand` channel when the agent opens it: initializes it, restores its
|
|
1621
|
+
* prior-session state, publishes it on {@link ActiveChannels$} (so the overlay can give it a tab)
|
|
1622
|
+
* and, if the call is already connected, tells it so. If initialization throws the channel goes
|
|
1623
|
+
* back to being advertised, so the agent can retry or move on.
|
|
1624
|
+
*/
|
|
1625
|
+
private mountAdvertisedChannel;
|
|
1626
|
+
/** Tells a channel the session is connected, containing anything it throws. */
|
|
1627
|
+
private notifyChannelSessionStarted;
|
|
1628
|
+
/** Finds a session channel (open or merely advertised) for the dispatcher. */
|
|
1629
|
+
private findDispatchableChannel;
|
|
1630
|
+
/** The keys the agent can address, for "unknown channel" messages — channels with nothing to address are left out. */
|
|
1631
|
+
private listAddressableChannelKeys;
|
|
1632
|
+
/**
|
|
1633
|
+
* Runs a channel-addressed `ContextTool` call (the {@link RealtimeChannelContext.DispatchContextAction}
|
|
1634
|
+
* implementation): validates it, opens an `on-demand` channel when asked, runs the verb, and on
|
|
1635
|
+
* success records the channel as used so the overlay reveals its tab. Never throws.
|
|
1636
|
+
*/
|
|
1637
|
+
private dispatchContextAction;
|
|
1638
|
+
/**
|
|
1639
|
+
* Opens (and seeds) a channel from the HOST — the same path the agent's `open` action takes: the channel
|
|
1640
|
+
* is mounted if it was only advertised, its `Inputs` schema validates `inputs`, and it announces itself to
|
|
1641
|
+
* the model. A channel that is already open is re-seeded. Never throws; a refusal is a structured result.
|
|
1642
|
+
*
|
|
1643
|
+
* @param channelKey The channel to open (case-insensitive).
|
|
1644
|
+
* @param inputs Seed inputs, validated against the channel's descriptor.
|
|
863
1645
|
*/
|
|
864
|
-
|
|
1646
|
+
OpenChannel(channelKey: string, inputs?: JSONObject): Promise<RealtimeContextActionResult>;
|
|
1647
|
+
/**
|
|
1648
|
+
* Tells the model which channels exist and how to use them — rendered from the channels' own
|
|
1649
|
+
* descriptors, so a new channel needs no prompt change. Sent ONCE, the first moment the control
|
|
1650
|
+
* channel is usable — a context note sent before then is dropped, and `Connect` can resolve before the
|
|
1651
|
+
* provider reports `listening`, so this runs at start and again on each state change until it has gone.
|
|
1652
|
+
* A session whose only channels describe themselves through native tools sends nothing.
|
|
1653
|
+
*/
|
|
1654
|
+
private flushChannelCatalogNote;
|
|
1655
|
+
/** One catalog entry: a channel's descriptor, whether it is open, and whether its tools were declared natively. */
|
|
1656
|
+
private catalogEntry;
|
|
865
1657
|
/** Builds the host-services context one channel plugin sees (its only line to the session). */
|
|
866
1658
|
private buildChannelContext;
|
|
867
1659
|
/**
|
|
868
1660
|
* Host registry of surface CLIENT TOOLS (Name → handler), fed by the host (Explorer) from the
|
|
869
|
-
* active surface's `SetAgentClientTools
|
|
870
|
-
* executes against this via {@link executeAppClientTool}.
|
|
871
|
-
*
|
|
1661
|
+
* active surface's `SetAgentClientTools` and from its always-on globals. The headless
|
|
1662
|
+
* ClientContextChannel's `ContextTool` proxy executes against this via {@link executeAppClientTool}.
|
|
1663
|
+
* Owner-keyed — see {@link AppClientToolRegistry}.
|
|
1664
|
+
*/
|
|
1665
|
+
private readonly appToolRegistry;
|
|
1666
|
+
/**
|
|
1667
|
+
* Registers the tools ONE source of the host can run for the realtime `ContextTool`, replacing only
|
|
1668
|
+
* that source's previous set — other owners' tools are untouched. A host with two sources (Explorer's
|
|
1669
|
+
* always-available globals and the active surface's tools) registers each under its own owner key, so
|
|
1670
|
+
* refreshing the surface's tools can neither drop the globals nor leave the previous surface's tools
|
|
1671
|
+
* behind. Passing `[]` clears just that owner. When two owners register the same tool name, the
|
|
1672
|
+
* later-registered owner wins.
|
|
1673
|
+
*
|
|
1674
|
+
* Calling it with one argument (the original form) registers under {@link DEFAULT_APP_TOOL_OWNER}, so
|
|
1675
|
+
* a host with a single source behaves exactly as before.
|
|
1676
|
+
*
|
|
1677
|
+
* @param tools The owner's complete current set (name + handler; description/schema optional).
|
|
1678
|
+
* @param owner A stable key naming the source (e.g. `'explorer.surface'`). Defaults to a shared owner.
|
|
872
1679
|
*/
|
|
873
|
-
|
|
1680
|
+
RegisterAppClientTools(tools: ReadonlyArray<AppClientToolRegistration>, owner?: string): void;
|
|
1681
|
+
/** Removes one owner's tools (and its place in the collision order). No-op when it has none. */
|
|
1682
|
+
UnregisterAppClientTools(owner: string): void;
|
|
1683
|
+
/** Removes every owner's tools. */
|
|
1684
|
+
ClearAppClientTools(): void;
|
|
874
1685
|
/**
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
*
|
|
879
|
-
*
|
|
880
|
-
* app-context manifest separately; only the executable handler is needed here.
|
|
1686
|
+
* The client tools in effect right now, resolved through the same unified resolver the server uses
|
|
1687
|
+
* (`override → session → app → static`, first match wins): the host's registered tools (described
|
|
1688
|
+
* by the manifest the host streams when it did not describe them itself) first, then the app and
|
|
1689
|
+
* agent tiers the server resolved at mint. Only the host-registered tools can RUN here; the
|
|
1690
|
+
* other tiers describe what the app declares.
|
|
881
1691
|
*/
|
|
882
|
-
|
|
883
|
-
Name: string;
|
|
884
|
-
Handler: (params: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
885
|
-
}>): void;
|
|
1692
|
+
private resolveAppClientTools;
|
|
886
1693
|
/**
|
|
887
1694
|
* Executes a host-registered surface client tool by name (the {@link RealtimeChannelContext.ExecuteClientTool}
|
|
888
1695
|
* implementation). Tolerant: an unknown tool or a thrown handler resolves to a structured
|
|
889
|
-
* `Success: false` result the channel narrates — never throws.
|
|
1696
|
+
* `Success: false` result the channel narrates — never throws. A tool the app declares but the host has
|
|
1697
|
+
* not registered a handler for says so, instead of reading like a typo. A handler that returns an object with
|
|
1698
|
+
* `Success: false` also gives `Success: false`, with the handler's `ErrorMessage` and its object as `Result`.
|
|
890
1699
|
*
|
|
891
1700
|
* @param name The tool name (the model's `action`).
|
|
892
1701
|
* @param params The tool parameters.
|
|
893
1702
|
* @returns A structured result for the channel to serialize back to the model.
|
|
894
1703
|
*/
|
|
895
1704
|
private executeAppClientTool;
|
|
1705
|
+
/** The model-readable reason a client tool could not run. */
|
|
1706
|
+
private describeMissingClientTool;
|
|
896
1707
|
/**
|
|
897
1708
|
* Runs a channel-specific GraphQL operation through the live session's provider (the
|
|
898
1709
|
* {@link RealtimeChannelContext.ExecuteServerAction} implementation). Best-effort: any
|
|
@@ -914,6 +1725,8 @@ export declare class RealtimeSessionRuntime {
|
|
|
914
1725
|
* never affected.
|
|
915
1726
|
*/
|
|
916
1727
|
private applyPriorChannelStates;
|
|
1728
|
+
/** Offers one plugin its prior-session state, if there is one. Rejections and throws are logged, never fatal. */
|
|
1729
|
+
private restorePriorChannelState;
|
|
917
1730
|
/**
|
|
918
1731
|
* Persists a channel's state as a first-class versioned artifact (`MJ: Artifacts`) via the
|
|
919
1732
|
* `SaveSessionChannelArtifact` mutation — the channel-context capability behind e.g. the
|
|
@@ -945,8 +1758,10 @@ export declare class RealtimeSessionRuntime {
|
|
|
945
1758
|
private createRealtimeClient;
|
|
946
1759
|
/**
|
|
947
1760
|
* Builds the client-direct session config the realtime client connects with.
|
|
948
|
-
* Aggregates tracks
|
|
949
|
-
* can negotiate them (e.g.,
|
|
1761
|
+
* Aggregates the tracks active channels source and sink under {@link REQUESTED_TRACKS_SESSION_KEY} so the
|
|
1762
|
+
* driver can negotiate them (e.g., inbound video for Whiteboard / RemoteBrowser, outbound video for a
|
|
1763
|
+
* channel that shows the agent's video). A track the model does not support resolves to `'unsupported'`.
|
|
1764
|
+
* The session's transport and relay URL go to the driver as minted (an unknown transport is left out: direct).
|
|
950
1765
|
*/
|
|
951
1766
|
BuildClientConfig(session: StartRealtimeClientSessionResult): ClientRealtimeSessionConfig;
|
|
952
1767
|
/** @deprecated Use {@link BuildClientConfig}. */
|
|
@@ -959,6 +1774,14 @@ export declare class RealtimeSessionRuntime {
|
|
|
959
1774
|
private parseSessionConfig;
|
|
960
1775
|
/** Subscribes this service's policy handlers to the realtime client's events. */
|
|
961
1776
|
private wireClientHandlers;
|
|
1777
|
+
/**
|
|
1778
|
+
* The model's video arrived: publish it on {@link AgentVideo$}, and mark each channel that shows it (one that sinks
|
|
1779
|
+
* outbound video) as used, so the host shows its surface. A client the session has already let go of is ignored, so a
|
|
1780
|
+
* late frame cannot bring the video back after the call.
|
|
1781
|
+
*/
|
|
1782
|
+
private onAgentVideo;
|
|
1783
|
+
/** Clears {@link AgentVideo$} when the call's client goes. */
|
|
1784
|
+
private clearAgentVideo;
|
|
962
1785
|
/** Maps a client state event onto the UI connection state. */
|
|
963
1786
|
private onClientStateChange;
|
|
964
1787
|
/**
|
|
@@ -969,6 +1792,11 @@ export declare class RealtimeSessionRuntime {
|
|
|
969
1792
|
private mapClientState;
|
|
970
1793
|
/** True when the live control channel is usable (open and not torn down / failed). */
|
|
971
1794
|
private isSessionLive;
|
|
1795
|
+
/**
|
|
1796
|
+
* Moves {@link ConnectionState$} to `state`, and {@link Resuming$} first: a `'connecting'` while the call is live, or
|
|
1797
|
+
* already resuming, is a resume; any other state ends one. Every state change goes through here.
|
|
1798
|
+
*/
|
|
1799
|
+
private setConnectionState;
|
|
972
1800
|
/**
|
|
973
1801
|
* Applies transcript policy to client transcript events. Interim deltas don't become
|
|
974
1802
|
* captions/turns (the client already drives the speaking state) but DO mark this turn's
|
|
@@ -994,18 +1822,28 @@ export declare class RealtimeSessionRuntime {
|
|
|
994
1822
|
* - NORMAL turns only — NARRATION interims are ephemeral and never persisted, so they must not
|
|
995
1823
|
* claim the next real turn's start slot.
|
|
996
1824
|
*
|
|
997
|
-
*
|
|
998
|
-
*
|
|
999
|
-
*
|
|
1000
|
-
*
|
|
1001
|
-
* assistant answer, which always has interims.
|
|
1825
|
+
* Stamped on a turn's first interim transcript (all drivers for the assistant — the post-tool-gap
|
|
1826
|
+
* assistant answer — and user-interim drivers like Gemini/AssemblyAI). Final-only user turns
|
|
1827
|
+
* (OpenAI-protocol drivers) take their start from {@link markUserSpeechStart}'s own slot
|
|
1828
|
+
* instead; one with no speech-start signal either (ElevenLabs) relays `null` as-is.
|
|
1002
1829
|
*/
|
|
1003
1830
|
private markTurnAudioStart;
|
|
1831
|
+
/**
|
|
1832
|
+
* Stamps {@link userSpeechStartMs} when the client signals that the user began to speak. Keeps
|
|
1833
|
+
* the FIRST onset since the last final user transcript: the VAD fires again after a
|
|
1834
|
+
* mid-sentence pause, and that second onset is not where the utterance began. An onset that an
|
|
1835
|
+
* assistant turn which began after it has since superseded ({@link userSpeechStartSuperseded}) is
|
|
1836
|
+
* replaced instead.
|
|
1837
|
+
*/
|
|
1838
|
+
private markUserSpeechStart;
|
|
1839
|
+
/** Ends the held user onset: a final user transcript (empty or not) closed the utterance it began. */
|
|
1840
|
+
private releaseUserSpeechStart;
|
|
1004
1841
|
/**
|
|
1005
1842
|
* The current per-turn offset in ms — the RECORDER's clock when one runs (an offset into a
|
|
1006
1843
|
* seekable file), else the SESSION clock (#3832: orderable and displayable, not seekable),
|
|
1007
1844
|
* else `null` before any call is live. One function so the two stamp sites cannot disagree
|
|
1008
|
-
* about which clock a session is on.
|
|
1845
|
+
* about which clock a session is on. Always whole milliseconds: the offsets travel as GraphQL
|
|
1846
|
+
* `Int`, which refuses a fraction, and a 48 kHz sample clock rarely lands on a whole millisecond.
|
|
1009
1847
|
*/
|
|
1010
1848
|
private nowTurnOffsetMs;
|
|
1011
1849
|
/**
|
|
@@ -1014,7 +1852,12 @@ export declare class RealtimeSessionRuntime {
|
|
|
1014
1852
|
* client's caption window).
|
|
1015
1853
|
*/
|
|
1016
1854
|
private replaceLastCaption;
|
|
1017
|
-
/**
|
|
1855
|
+
/**
|
|
1856
|
+
* Finalizes the user turn: push a caption + relay the final transcript.
|
|
1857
|
+
*
|
|
1858
|
+
* @param source `'typed'` for a {@link SendText} turn: it has no speech onset, so it neither takes
|
|
1859
|
+
* nor clears the held one (a spoken transcript may still be pending for it).
|
|
1860
|
+
*/
|
|
1018
1861
|
private onUserTranscript;
|
|
1019
1862
|
/**
|
|
1020
1863
|
* Routes a provider tool call: names matching a registered client-tool prefix execute
|
|
@@ -1040,6 +1883,14 @@ export declare class RealtimeSessionRuntime {
|
|
|
1040
1883
|
* for the surface panel's artifact tabs.
|
|
1041
1884
|
*/
|
|
1042
1885
|
private emitDelegationResult;
|
|
1886
|
+
/**
|
|
1887
|
+
* Offers a delegated run's artifacts to the channels that host artifacts (an Interactive Component channel
|
|
1888
|
+
* shows a component artifact, or swaps in a newer version of one it already shows). Each channel is asked
|
|
1889
|
+
* first whether it wants them; an advertised, unopened channel that does is mounted before it is handed
|
|
1890
|
+
* them. One channel failing never stops the others, and nothing here can fail the delegation result,
|
|
1891
|
+
* which has already been emitted.
|
|
1892
|
+
*/
|
|
1893
|
+
private offerDelegationArtifacts;
|
|
1043
1894
|
/**
|
|
1044
1895
|
* Cancels ONE in-flight delegated tool call — the overlay's per-card ✕ affordance.
|
|
1045
1896
|
*
|
|
@@ -1073,7 +1924,12 @@ export declare class RealtimeSessionRuntime {
|
|
|
1073
1924
|
* 0 on a structured failure or a thrown transport error (both logged, never thrown).
|
|
1074
1925
|
*/
|
|
1075
1926
|
private cancelSessionTool;
|
|
1076
|
-
/**
|
|
1927
|
+
/**
|
|
1928
|
+
* Mints a session through the installed {@link Launcher} — by default the stock
|
|
1929
|
+
* `StartRealtimeClientSession` mutation (see {@link DefaultRealtimeSessionLauncher}). `showsAgentVideo` is whether
|
|
1930
|
+
* one of the session's channels could show the agent's video; without one the server asks the model for no avatar.
|
|
1931
|
+
* `conversationBranchId` is the fork the session starts on; `null` is Main.
|
|
1932
|
+
*/
|
|
1077
1933
|
private mintSession;
|
|
1078
1934
|
/** Calls the `ExecuteRealtimeSessionTool` mutation; returns the ResultJson string. */
|
|
1079
1935
|
private executeSessionTool;
|
|
@@ -1095,20 +1951,27 @@ export declare class RealtimeSessionRuntime {
|
|
|
1095
1951
|
* Relays a final transcript turn to MJ via `RelayRealtimeTranscript`.
|
|
1096
1952
|
*
|
|
1097
1953
|
* When the session is being recorded, per-turn timing rides along: `utteranceEndMs` is the
|
|
1098
|
-
* recording-relative offset at finalization, and `utteranceStartMs` is
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1954
|
+
* recording-relative offset at finalization, and `utteranceStartMs` is where THIS turn's audio
|
|
1955
|
+
* actually began — NOT inherited from the previous turn's end. For a user turn whose onset the
|
|
1956
|
+
* client signalled, that is {@link userSpeechStartMs}; otherwise it is the offset captured by
|
|
1957
|
+
* {@link markTurnAudioStart} on the turn's first interim. That distinction is the timing fix:
|
|
1958
|
+
* when a tool-call / silence gap sits between turns, the post-gap turn's audio starts much
|
|
1959
|
+
* later, so inheriting the prior turn's end stamped the cue ~gap-length too early. Both are
|
|
1960
|
+
* omitted (left `null`) when the session isn't being recorded. `utteranceStartMs` is also
|
|
1961
|
+
* `null` when no start was measured for this turn; it is never replaced with a placeholder 0.
|
|
1104
1962
|
*
|
|
1105
1963
|
* A correction (`replacesPrevious`) doesn't open a new turn, so it carries no start and doesn't
|
|
1106
1964
|
* reset the per-turn start guard. After a normal finalization the guard is cleared so the NEXT
|
|
1107
|
-
* turn re-stamps its start from where ITS audio begins
|
|
1965
|
+
* turn re-stamps its start from where ITS audio begins — except for a user turn that used its
|
|
1966
|
+
* own speech-start slot, which leaves the shared slot alone (it may hold the in-flight
|
|
1967
|
+
* assistant turn's start). Every final user transcript, corrections included, clears the
|
|
1968
|
+
* speech-start slot.
|
|
1108
1969
|
*
|
|
1109
1970
|
* @param replacesPrevious CORRECTION semantics: the server updates the session's most
|
|
1110
1971
|
* recent persisted turn of this role IN PLACE instead of appending (e.g. ElevenLabs'
|
|
1111
1972
|
* post-barge-in `agent_response_correction`).
|
|
1973
|
+
* @param source `'typed'` for a user turn sent with {@link SendText}: it does not read or clear
|
|
1974
|
+
* {@link userSpeechStartMs}, which belongs to the user's speech.
|
|
1112
1975
|
*/
|
|
1113
1976
|
private relayTranscript;
|
|
1114
1977
|
/**
|
|
@@ -1121,9 +1984,14 @@ export declare class RealtimeSessionRuntime {
|
|
|
1121
1984
|
/**
|
|
1122
1985
|
* Accumulates one usage DELTA from the realtime client (per-response token counts —
|
|
1123
1986
|
* the `OnUsage` contract shape) and schedules the debounced relay. Negative / non-finite
|
|
1124
|
-
* values are clamped to 0; an all-zero delta is dropped without arming the timer.
|
|
1987
|
+
* values are clamped to 0; an all-zero delta is dropped without arming the timer. The
|
|
1988
|
+
* update's input and output detail blocks add into the pending details by the record's
|
|
1989
|
+
* rules (amounts add up, inbound video running totals keep the larger value), so an update
|
|
1990
|
+
* that carries only avatar video seconds still counts.
|
|
1125
1991
|
*/
|
|
1126
1992
|
private onUsageDelta;
|
|
1993
|
+
/** Schedules the debounced usage relay unless one is already pending. */
|
|
1994
|
+
private armUsageFlush;
|
|
1127
1995
|
/** Clamps a driver-reported token delta: undefined / negative / non-finite become 0. */
|
|
1128
1996
|
private clampUsageDelta;
|
|
1129
1997
|
/**
|
|
@@ -1228,6 +2096,11 @@ export declare class RealtimeSessionRuntime {
|
|
|
1228
2096
|
private teardown;
|
|
1229
2097
|
/** The body of {@link teardown}; never called concurrently with itself. */
|
|
1230
2098
|
private runTeardown;
|
|
2099
|
+
/**
|
|
2100
|
+
* Calls the host's {@link IRealtimeMediaHost.ReleaseMicrophone}. Best-effort by contract: a failure
|
|
2101
|
+
* is logged and never thrown, because it must never block ending a call.
|
|
2102
|
+
*/
|
|
2103
|
+
private releaseMicrophone;
|
|
1231
2104
|
/** Calls the `CloseAgentSession` mutation (provisioned in P4b). */
|
|
1232
2105
|
private closeServerSession;
|
|
1233
2106
|
/** Pushes a caption onto the live list (immutable update for change detection). */
|