@memberjunction/ng-conversations 6.1.1 → 6.1.3
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/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +36 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +23 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +1 -1
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-activity-rail.component.js +2 -2
- package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +2 -2
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.js +104 -72
- package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.d.ts +11 -4
- package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.js +43 -1
- package/dist/lib/components/realtime/realtime-session-state.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +58 -2
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +154 -4
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +51 -2
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +285 -5
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts +39 -4
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +128 -8
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/package.json +31 -31
|
@@ -4,6 +4,7 @@ import { Metadata } from '@memberjunction/core';
|
|
|
4
4
|
import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
5
5
|
import { AIEngineBase } from '@memberjunction/ai-engine-base';
|
|
6
6
|
import { MJGlobal } from '@memberjunction/global';
|
|
7
|
+
import { DEFAULT_REALTIME_AUDIO_TRACKS } from '@memberjunction/ai';
|
|
7
8
|
import { BaseRealtimeClient, LoadAssemblyAIRealtimeClient, LoadElevenLabsRealtimeClient, LoadGeminiRealtimeClient, LoadHuggingFaceRealtimeClient, LoadOpenAIRealtimeClient, LoadxAIRealtimeClient } from '@memberjunction/ai-realtime-client';
|
|
8
9
|
import { BuildNarrationInstructions } from './narration-template';
|
|
9
10
|
import { ParseDelegationResultJson, FormatToolName } from './delegation-result-parser';
|
|
@@ -30,6 +31,45 @@ LoadElevenLabsRealtimeClient();
|
|
|
30
31
|
LoadAssemblyAIRealtimeClient();
|
|
31
32
|
LoadxAIRealtimeClient();
|
|
32
33
|
LoadHuggingFaceRealtimeClient();
|
|
34
|
+
/**
|
|
35
|
+
* Converts a {@link RealtimeTrackDescriptor} to its JSON form for the session-config bag.
|
|
36
|
+
*
|
|
37
|
+
* Every field's VALUE is already JSON-safe; the interface simply is not assignable to `JSONValue`
|
|
38
|
+
* because it declares no index signature and `UsageBasis` is `readonly`. Written out field by field
|
|
39
|
+
* rather than asserted, so adding a descriptor field is a compile error here instead of a field that
|
|
40
|
+
* silently stops reaching the driver.
|
|
41
|
+
*/
|
|
42
|
+
function trackDescriptorToJSON(track) {
|
|
43
|
+
const json = { Modality: track.Modality, Direction: track.Direction };
|
|
44
|
+
if (track.Encoding !== undefined) {
|
|
45
|
+
json['Encoding'] = track.Encoding;
|
|
46
|
+
}
|
|
47
|
+
if (track.Rate !== undefined) {
|
|
48
|
+
json['Rate'] = track.Rate;
|
|
49
|
+
}
|
|
50
|
+
if (track.UsageBasis !== undefined) {
|
|
51
|
+
json['UsageBasis'] = [...track.UsageBasis];
|
|
52
|
+
}
|
|
53
|
+
if (track.RequiresConsent !== undefined) {
|
|
54
|
+
json['RequiresConsent'] = track.RequiresConsent;
|
|
55
|
+
}
|
|
56
|
+
return json;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Reads the `Direction:Modality` dedupe key off an already-JSON track entry, or `null` when the
|
|
60
|
+
* entry is not a track-shaped object. Used for tracks the mint supplied, which arrive as raw JSON.
|
|
61
|
+
*/
|
|
62
|
+
function trackKeyFromJSON(raw) {
|
|
63
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const direction = raw['Direction'];
|
|
67
|
+
const modality = raw['Modality'];
|
|
68
|
+
if (typeof direction !== 'string' || typeof modality !== 'string') {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return `${direction}:${modality}`;
|
|
72
|
+
}
|
|
33
73
|
/**
|
|
34
74
|
* Drives a **client-direct** real-time voice session: the browser mints an ephemeral
|
|
35
75
|
* token from the MJ server, then connects DIRECTLY to the realtime provider. Audio
|
|
@@ -60,6 +100,7 @@ export class RealtimeSessionService {
|
|
|
60
100
|
_delegationProgress$ = new Subject();
|
|
61
101
|
_delegationResult$ = new Subject();
|
|
62
102
|
_delegationNarration$ = new Subject();
|
|
103
|
+
_thoughtNarration$ = new Subject();
|
|
63
104
|
_agentName$ = new BehaviorSubject('Sage');
|
|
64
105
|
_modelName$ = new BehaviorSubject(null);
|
|
65
106
|
_minimized$ = new BehaviorSubject(false);
|
|
@@ -93,6 +134,11 @@ export class RealtimeSessionService {
|
|
|
93
134
|
* renders them as a transient "live note" near the active working card.
|
|
94
135
|
*/
|
|
95
136
|
DelegationNarration$ = this._delegationNarration$.asObservable();
|
|
137
|
+
/**
|
|
138
|
+
* Model-authored thought / reasoning narrations (see {@link RealtimeThoughtNarration}). These are
|
|
139
|
+
* reasoning summaries author-emitted during extended thinking, separate from spoken progress updates.
|
|
140
|
+
*/
|
|
141
|
+
ThoughtNarration$ = this._thoughtNarration$.asObservable();
|
|
96
142
|
/** Display name of the agent the active session fronts (set at session start). */
|
|
97
143
|
AgentName$ = this._agentName$.asObservable();
|
|
98
144
|
/**
|
|
@@ -538,6 +584,15 @@ export class RealtimeSessionService {
|
|
|
538
584
|
this.wireClientHandlers(client);
|
|
539
585
|
this.localStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
540
586
|
await client.Connect(this.buildClientConfig(session), this.localStream);
|
|
587
|
+
// Notify active channels that the session client is connected and tracks are established
|
|
588
|
+
for (const channel of this._activeChannels$.value) {
|
|
589
|
+
try {
|
|
590
|
+
channel.OnSessionStarted?.();
|
|
591
|
+
}
|
|
592
|
+
catch (err) {
|
|
593
|
+
console.error(`[RealtimeSession] Error in channel '${channel.ChannelName}' OnSessionStarted:`, err);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
541
596
|
// Start browser-side recording (mic + agent mix) when consented. Best-effort: an
|
|
542
597
|
// unsupported browser / missing remote stream degrades gracefully (mic-only or off)
|
|
543
598
|
// and never blocks the call. The remote stream may still be null here (the WebRTC
|
|
@@ -689,6 +744,27 @@ export class RealtimeSessionService {
|
|
|
689
744
|
GetAudioActivity() {
|
|
690
745
|
return this.client?.GetAudioActivity() ?? null;
|
|
691
746
|
}
|
|
747
|
+
/**
|
|
748
|
+
* The active {@link BaseRealtimeClient} driving the media plane, or null when not connected.
|
|
749
|
+
*/
|
|
750
|
+
get Client() {
|
|
751
|
+
return this.client;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Relays a video frame to the underlying realtime client if active.
|
|
755
|
+
*/
|
|
756
|
+
SendVideoFrame(base64Image, mimeType) {
|
|
757
|
+
if (!this.client || !this.isSessionLive()) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
this.client.SendVideoFrame?.(base64Image, mimeType);
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Checks whether a media track is established on the active realtime client.
|
|
764
|
+
*/
|
|
765
|
+
IsTrackEstablished(modality, direction) {
|
|
766
|
+
return this.client?.IsTrackEstablished(modality, direction) ?? false;
|
|
767
|
+
}
|
|
692
768
|
// ── Browser-side call recording ────────────────────────────────────────────
|
|
693
769
|
/**
|
|
694
770
|
* Reads the per-user recording-consent preference from `MJ: User Settings` (via
|
|
@@ -1040,7 +1116,12 @@ export class RealtimeSessionService {
|
|
|
1040
1116
|
// App-context stream + client-tool execution for the headless ClientContextChannel. The host
|
|
1041
1117
|
// (Explorer) feeds both; absent on hosts that supply no app context / register no client tools.
|
|
1042
1118
|
AppContext$: this.AppContext$,
|
|
1043
|
-
ExecuteClientTool: (name, params) => this.executeAppClientTool(name, params)
|
|
1119
|
+
ExecuteClientTool: (name, params) => this.executeAppClientTool(name, params),
|
|
1120
|
+
get Client() {
|
|
1121
|
+
return service.client;
|
|
1122
|
+
},
|
|
1123
|
+
SendVideoFrame: (base64Image, mimeType) => this.SendVideoFrame(base64Image, mimeType),
|
|
1124
|
+
IsTrackEstablished: (modality, direction) => this.IsTrackEstablished(modality, direction),
|
|
1044
1125
|
};
|
|
1045
1126
|
}
|
|
1046
1127
|
/**
|
|
@@ -1266,14 +1347,44 @@ export class RealtimeSessionService {
|
|
|
1266
1347
|
}
|
|
1267
1348
|
return client;
|
|
1268
1349
|
}
|
|
1269
|
-
/**
|
|
1350
|
+
/**
|
|
1351
|
+
* Builds the client-direct session config the realtime client connects with.
|
|
1352
|
+
* Aggregates tracks sourced by active channels into `requestedTracks` so the driver
|
|
1353
|
+
* can negotiate them (e.g., establishing inbound video streaming for Whiteboard / RemoteBrowser).
|
|
1354
|
+
*/
|
|
1270
1355
|
buildClientConfig(session) {
|
|
1356
|
+
const sessionConfig = this.parseSessionConfig(session.SessionConfigJson);
|
|
1357
|
+
const channelTracks = this._activeChannels$.value.flatMap((c) => c.GetSourcedTracks());
|
|
1358
|
+
if (channelTracks.length > 0) {
|
|
1359
|
+
// `requestedTracks` crosses a JSON boundary — the driver reads it back out of the session
|
|
1360
|
+
// config bag (`GeminiRealtimeClient.parseSessionConfig`). A `RealtimeTrackDescriptor` is NOT
|
|
1361
|
+
// structurally a `JSONValue`: it has no index signature and `UsageBasis` is readonly, so the
|
|
1362
|
+
// conversion is written out rather than asserted. Dedupe key and precedence are unchanged —
|
|
1363
|
+
// audio floor first, then anything the mint supplied, then the channels' own tracks.
|
|
1364
|
+
const existing = Array.isArray(sessionConfig['requestedTracks'])
|
|
1365
|
+
? sessionConfig['requestedTracks']
|
|
1366
|
+
: [];
|
|
1367
|
+
const trackMap = new Map();
|
|
1368
|
+
for (const t of DEFAULT_REALTIME_AUDIO_TRACKS) {
|
|
1369
|
+
trackMap.set(`${t.Direction}:${t.Modality}`, trackDescriptorToJSON(t));
|
|
1370
|
+
}
|
|
1371
|
+
for (const raw of existing) {
|
|
1372
|
+
const key = trackKeyFromJSON(raw);
|
|
1373
|
+
if (key) {
|
|
1374
|
+
trackMap.set(key, raw);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
for (const t of channelTracks) {
|
|
1378
|
+
trackMap.set(`${t.Direction}:${t.Modality}`, trackDescriptorToJSON(t));
|
|
1379
|
+
}
|
|
1380
|
+
sessionConfig['requestedTracks'] = Array.from(trackMap.values());
|
|
1381
|
+
}
|
|
1271
1382
|
return {
|
|
1272
1383
|
Provider: session.Provider,
|
|
1273
1384
|
Model: session.Model,
|
|
1274
1385
|
EphemeralToken: session.EphemeralToken,
|
|
1275
1386
|
ExpiresAt: session.ExpiresAt,
|
|
1276
|
-
SessionConfig:
|
|
1387
|
+
SessionConfig: sessionConfig
|
|
1277
1388
|
};
|
|
1278
1389
|
}
|
|
1279
1390
|
/**
|
|
@@ -1391,11 +1502,20 @@ export class RealtimeSessionService {
|
|
|
1391
1502
|
this.hasActiveInterimUserCaption = false;
|
|
1392
1503
|
this.pendingUserCaption = '';
|
|
1393
1504
|
if (transcript.Kind === 'narration') {
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1505
|
+
if (transcript.IsThought) {
|
|
1506
|
+
this._thoughtNarration$.next({
|
|
1507
|
+
CallID: 'thought-session',
|
|
1508
|
+
Text: transcript.Text,
|
|
1509
|
+
IsFinal: transcript.IsFinal ?? true,
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
else {
|
|
1513
|
+
this._delegationNarration$.next({ Text: transcript.Text });
|
|
1514
|
+
// Remember what was actually SAID so later updates build on it instead of repeating.
|
|
1515
|
+
this.spokenNarrations.push(transcript.Text);
|
|
1516
|
+
if (this.spokenNarrations.length > RealtimeSessionService.MaxPriorNarrations) {
|
|
1517
|
+
this.spokenNarrations.shift();
|
|
1518
|
+
}
|
|
1399
1519
|
}
|
|
1400
1520
|
}
|
|
1401
1521
|
else if (transcript.ReplacesPrevious) {
|