@memberjunction/livekit-room-server 6.2.0-edge.2 → 6.2.0-edge.4
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 +85 -0
- package/dist/index.d.ts +18 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/livekit-agent-room-coordinator.d.ts +220 -13
- package/dist/livekit-agent-room-coordinator.d.ts.map +1 -1
- package/dist/livekit-agent-room-coordinator.js +409 -65
- package/dist/livekit-agent-room-coordinator.js.map +1 -1
- package/dist/livekit-egress-service.d.ts +7 -0
- package/dist/livekit-egress-service.d.ts.map +1 -1
- package/dist/livekit-egress-service.js +11 -0
- package/dist/livekit-egress-service.js.map +1 -1
- package/dist/livekit-native-module.d.ts +18 -0
- package/dist/livekit-native-module.d.ts.map +1 -0
- package/dist/livekit-native-module.js +27 -0
- package/dist/livekit-native-module.js.map +1 -0
- package/dist/livekit-participant-service.d.ts +47 -0
- package/dist/livekit-participant-service.d.ts.map +1 -0
- package/dist/livekit-participant-service.js +64 -0
- package/dist/livekit-participant-service.js.map +1 -0
- package/dist/livekit-sip-service.d.ts +141 -0
- package/dist/livekit-sip-service.d.ts.map +1 -0
- package/dist/livekit-sip-service.js +169 -0
- package/dist/livekit-sip-service.js.map +1 -0
- package/dist/livekit-token-service.d.ts +26 -5
- package/dist/livekit-token-service.d.ts.map +1 -1
- package/dist/livekit-token-service.js +22 -7
- package/dist/livekit-token-service.js.map +1 -1
- package/dist/livekit-webhook.d.ts +65 -0
- package/dist/livekit-webhook.d.ts.map +1 -0
- package/dist/livekit-webhook.js +65 -0
- package/dist/livekit-webhook.js.map +1 -0
- package/dist/meeting-dial-in-service.d.ts +83 -0
- package/dist/meeting-dial-in-service.d.ts.map +1 -0
- package/dist/meeting-dial-in-service.js +202 -0
- package/dist/meeting-dial-in-service.js.map +1 -0
- package/dist/room-audio/audio-decoder.d.ts +40 -0
- package/dist/room-audio/audio-decoder.d.ts.map +1 -0
- package/dist/room-audio/audio-decoder.js +205 -0
- package/dist/room-audio/audio-decoder.js.map +1 -0
- package/dist/room-audio/comfort-tone.d.ts +23 -0
- package/dist/room-audio/comfort-tone.d.ts.map +1 -0
- package/dist/room-audio/comfort-tone.js +70 -0
- package/dist/room-audio/comfort-tone.js.map +1 -0
- package/dist/room-audio/pcm-frame-pump.d.ts +134 -0
- package/dist/room-audio/pcm-frame-pump.d.ts.map +1 -0
- package/dist/room-audio/pcm-frame-pump.js +253 -0
- package/dist/room-audio/pcm-frame-pump.js.map +1 -0
- package/dist/room-audio/room-audio-decode-cache.d.ts +37 -0
- package/dist/room-audio/room-audio-decode-cache.d.ts.map +1 -0
- package/dist/room-audio/room-audio-decode-cache.js +75 -0
- package/dist/room-audio/room-audio-decode-cache.js.map +1 -0
- package/dist/room-audio/room-audio-player.d.ts +178 -0
- package/dist/room-audio/room-audio-player.d.ts.map +1 -0
- package/dist/room-audio/room-audio-player.js +404 -0
- package/dist/room-audio/room-audio-player.js.map +1 -0
- package/dist/room-audio/room-audio-sources.d.ts +47 -0
- package/dist/room-audio/room-audio-sources.d.ts.map +1 -0
- package/dist/room-audio/room-audio-sources.js +116 -0
- package/dist/room-audio/room-audio-sources.js.map +1 -0
- package/dist/room-audio/room-speech-synthesizer.d.ts +55 -0
- package/dist/room-audio/room-speech-synthesizer.d.ts.map +1 -0
- package/dist/room-audio/room-speech-synthesizer.js +56 -0
- package/dist/room-audio/room-speech-synthesizer.js.map +1 -0
- package/dist/room-authorization.d.ts +58 -0
- package/dist/room-authorization.d.ts.map +1 -0
- package/dist/room-authorization.js +212 -0
- package/dist/room-authorization.js.map +1 -0
- package/dist/room-handoff/handoff-offer-registry.d.ts +147 -0
- package/dist/room-handoff/handoff-offer-registry.d.ts.map +1 -0
- package/dist/room-handoff/handoff-offer-registry.js +548 -0
- package/dist/room-handoff/handoff-offer-registry.js.map +1 -0
- package/dist/room-handoff/handoff-types.d.ts +172 -0
- package/dist/room-handoff/handoff-types.d.ts.map +1 -0
- package/dist/room-handoff/handoff-types.js +16 -0
- package/dist/room-handoff/handoff-types.js.map +1 -0
- package/dist/room-handoff/room-handoff-engine.d.ts +149 -0
- package/dist/room-handoff/room-handoff-engine.d.ts.map +1 -0
- package/dist/room-handoff/room-handoff-engine.js +563 -0
- package/dist/room-handoff/room-handoff-engine.js.map +1 -0
- package/package.json +15 -9
package/README.md
CHANGED
|
@@ -114,6 +114,91 @@ and logs — the stop-recording mutation still succeeds. See
|
|
|
114
114
|
[REALTIME_SESSION_CAPTURE_GUIDE.md](../../guides/REALTIME_SESSION_CAPTURE_GUIDE.md#meeting-room-recording-livekit-egress--mjstorage)
|
|
115
115
|
for the full loop incl. Meet-app playback.
|
|
116
116
|
|
|
117
|
+
## Room audio player (hold music)
|
|
118
|
+
|
|
119
|
+
`RoomAudioPlayer` joins a room as a small **publish-only** bot and plays audio into it: contact-center hold
|
|
120
|
+
music, with spoken announcements ducked over the top. It reuses the agent bridge's plumbing: bot tokens from
|
|
121
|
+
`LiveKitTokenService` (the `LIVEKIT_*` env vars), the native room client at the same specifier the agent
|
|
122
|
+
coordinator uses (`LIVEKIT_NATIVE_MODULE`, default `@memberjunction/ai-bridge-livekit-native`), and the PCM16
|
|
123
|
+
resampler from `@memberjunction/ai-bridge-base`.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { RoomAudioPlayer } from '@memberjunction/livekit-room-server';
|
|
127
|
+
|
|
128
|
+
const hold = await RoomAudioPlayer.Instance.Start({
|
|
129
|
+
RoomName: callRoom,
|
|
130
|
+
Source: { Kind: 'File', FileID: queue.HoldMusicFileID }, // or { Kind: 'Url', Url }, { Kind: 'Pcm', Pcm, SampleRate }, { Kind: 'ComfortTone' }
|
|
131
|
+
ContextUser: contextUser,
|
|
132
|
+
// Loop: true (default), DisplayName: 'On hold' (default), Identity: 'hold-<playback id>' (default)
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
await hold.Announce({ Text: 'You are caller number three.' }); // music ducks, clip plays, music returns
|
|
136
|
+
hold.Pause(); hold.Resume();
|
|
137
|
+
await hold.Stop(); // idempotent; always disconnects the bot
|
|
138
|
+
|
|
139
|
+
RoomAudioPlayer.Instance.GetActive(callRoom); // live playbacks in a room
|
|
140
|
+
await RoomAudioPlayer.Instance.StopAllInRoom(callRoom); // e.g. when an agent picks up
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Sources.** An `MJ: Files` row (loaded as `ContextUser`, bytes read through `@memberjunction/storage`), an
|
|
144
|
+
**https** URL (plain http is refused; fetched with `SafeFetch`, so every redirect hop is checked against
|
|
145
|
+
private/reserved addresses; 20 s timeout; 25 MB cap), caller-supplied mono PCM16, or the built-in
|
|
146
|
+
**comfort tone** — a procedurally generated 10 s loop of soft chord pads (no asset, nothing to license).
|
|
147
|
+
|
|
148
|
+
**Formats.** WAV (integer PCM 8/16/24/32-bit, IEEE float 32/64-bit, `WAVE_FORMAT_EXTENSIBLE`, any channel count —
|
|
149
|
+
downmixed to mono) and MP3 (decoded by `mpg123-decoder`, a WebAssembly build of libmpg123: no native build step).
|
|
150
|
+
Anything else (Ogg, FLAC, AAC/M4A, …) is refused with an error naming the format. Audio is limited to
|
|
151
|
+
**15 minutes**; hold music loops, so a few minutes is plenty.
|
|
152
|
+
|
|
153
|
+
**Playback.** Audio is resampled to 48 kHz and paced into the room in 20 ms frames against a monotonic clock,
|
|
154
|
+
about 150 ms ahead of real time — the native client's own outbound queue never holds more than that. A blocked
|
|
155
|
+
event loop drops the missed audio instead of bursting it. Decodes are cached by source (8 entries, 256 MB of PCM),
|
|
156
|
+
and concurrent starts of the same file share one download, so a busy queue reads its hold file once. The cache is
|
|
157
|
+
keyed by source, not by user: only the first load checks the reading user's access to the file.
|
|
158
|
+
|
|
159
|
+
**Lifecycle.** A playback whose bot is disconnected by the server stops and deregisters itself, and a verified
|
|
160
|
+
`room_finished` webhook (`LiveKitWebhookParser.Parse`) stops every playback in that room. A non-looping playback
|
|
161
|
+
leaves the room when its audio ends — after the audio already sent ahead of real time (about 150 ms) has played
|
|
162
|
+
out, so the tail is not cut off.
|
|
163
|
+
|
|
164
|
+
**Knowing it finished.** Every handle has `Ended: Promise<RoomAudioEndReason>`, which resolves once the bot has
|
|
165
|
+
left the room, and `EndReason` (`null` while live). Only `'Completed'` means the whole clip reached the room;
|
|
166
|
+
`'Stopped'` (Stop / StopAllInRoom), `'Failed'` (the outbound sink broke) and `'Disconnected'` (the server dropped
|
|
167
|
+
the bot) all mean it was cut short. The first reason wins. Use it to gate anything that must follow a full play:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const disclosure = await RoomAudioPlayer.Instance.Start({
|
|
171
|
+
RoomName: callRoom,
|
|
172
|
+
Source: { Kind: 'File', FileID: disclosureFileID },
|
|
173
|
+
Loop: false,
|
|
174
|
+
DisplayName: 'Recording notice',
|
|
175
|
+
ContextUser: contextUser,
|
|
176
|
+
});
|
|
177
|
+
if ((await disclosure.Ended) === 'Completed') {
|
|
178
|
+
await startRecording(); // the caller has heard the whole notice
|
|
179
|
+
} else {
|
|
180
|
+
// fail closed: do not record
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
A looping playback never completes on its own; its `Ended` resolves `'Stopped'` when it is stopped.
|
|
185
|
+
|
|
186
|
+
**Announcements and text-to-speech.** `Announce({ Pcm, SampleRate })` plays ready-made audio. `Announce({ Text })`
|
|
187
|
+
goes through the `IRoomSpeechSynthesizer` port, and **none is installed by default** — a voice is vendor-specific,
|
|
188
|
+
so the host chooses it. Without one, a text announcement returns `false` and the player logs the omission once.
|
|
189
|
+
To enable it, install the MJ-backed synthesizer (`AITextToSpeechRunner`: model selection, credentials, failover,
|
|
190
|
+
an `MJ: AI Prompt Runs` row) once at startup:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { MJRoomSpeechSynthesizer, RoomAudioPlayer } from '@memberjunction/livekit-room-server';
|
|
194
|
+
|
|
195
|
+
RoomAudioPlayer.Instance.SetSpeechSynthesizer(new MJRoomSpeechSynthesizer({ Voice: 'alloy', ModelID: ttsModelID }));
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The host needs an active TTS model whose vendor offers that voice (pin `ModelID` — voices belong to a vendor)
|
|
199
|
+
and the carrier prompt `Default Text To Speech` (or pass `PromptID`). Any other implementation of
|
|
200
|
+
`IRoomSpeechSynthesizer` works too.
|
|
201
|
+
|
|
117
202
|
## GraphQL surface
|
|
118
203
|
|
|
119
204
|
These are exposed to the browser via MJServer's `RealtimeBridgeResolver`:
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,23 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @memberjunction/livekit-room-server
|
|
8
8
|
*/
|
|
9
|
-
export { LiveKitTokenService, type LiveKitServerConfig, type LiveKitTokenRole, type MintTokenParams, type MintedToken } from './livekit-token-service.js';
|
|
10
|
-
export { LiveKitAgentRoomCoordinator, LIVEKIT_BRIDGE_DRIVER_CLASS, type BridgeOps, type RealtimeSessionFactory, type RealtimeSessionStartContext, type StartAgentRoomSessionParams, type AgentRoomSession, } from './livekit-agent-room-coordinator.js';
|
|
9
|
+
export { LiveKitTokenService, LiveKitUserIdentity, type LiveKitServerConfig, type LiveKitTokenRole, type MintTokenParams, type MintedToken } from './livekit-token-service.js';
|
|
10
|
+
export { LiveKitAgentRoomCoordinator, LIVEKIT_BRIDGE_DRIVER_CLASS, type BridgeOps, type RealtimeSessionFactory, type RealtimeSessionStartContext, type AgentRoomHostOptions, type StartAgentRoomSessionParams, type AgentRoomSession, } from './livekit-agent-room-coordinator.js';
|
|
11
11
|
export { LiveKitEgressService, WsToHttpUrl, wsToHttpUrl, type EgressClientLike, type StartRecordingParams, type RecordingInfo } from './livekit-egress-service.js';
|
|
12
|
+
export { LiveKitParticipantService, type ParticipantUpdateClientLike, type ParticipantUpdateResult } from './livekit-participant-service.js';
|
|
13
|
+
export { LiveKitSipService, LIVEKIT_PARTICIPANT_KIND_SIP, LIVEKIT_SIP_ATTRIBUTES, SIP_DIAL_REQUEST_TIMEOUT_SECONDS, SIP_DIAL_MAX_DURATION_SECONDS, type SipClientLike, type RoomServiceClientLike, type RoomParticipantSummary, type SipTrunkSummary, type DialSipParticipantParams, type EnsureInboundRoutingParams, type InboundRoutingResult, } from './livekit-sip-service.js';
|
|
14
|
+
export { LiveKitWebhookParser, type LiveKitRoomWebhookEvent, type WebhookReceiverLike } from './livekit-webhook.js';
|
|
15
|
+
export * from './room-handoff/handoff-types.js';
|
|
16
|
+
export * from './room-handoff/handoff-offer-registry.js';
|
|
17
|
+
export * from './room-handoff/room-handoff-engine.js';
|
|
18
|
+
export * from './room-authorization.js';
|
|
19
|
+
export * from './meeting-dial-in-service.js';
|
|
20
|
+
export { DEFAULT_LIVEKIT_NATIVE_MODULE, ResolveLiveKitNativeModuleSpecifier } from './livekit-native-module.js';
|
|
21
|
+
export * from './room-audio/audio-decoder.js';
|
|
22
|
+
export * from './room-audio/comfort-tone.js';
|
|
23
|
+
export * from './room-audio/pcm-frame-pump.js';
|
|
24
|
+
export * from './room-audio/room-audio-decode-cache.js';
|
|
25
|
+
export * from './room-audio/room-audio-sources.js';
|
|
26
|
+
export * from './room-audio/room-speech-synthesizer.js';
|
|
27
|
+
export * from './room-audio/room-audio-player.js';
|
|
12
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE5K,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC3B,KAAK,SAAS,EACd,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,gBAAgB,GACtB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEhK,OAAO,EAAE,yBAAyB,EAAE,KAAK,2BAA2B,EAAE,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAE1I,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,sBAAsB,EACtB,gCAAgC,EAChC,6BAA6B,EAC7B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,GAC1B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,oBAAoB,EAAE,KAAK,uBAAuB,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEjH,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAE1C,OAAO,EAAE,6BAA6B,EAAE,mCAAmC,EAAE,MAAM,yBAAyB,CAAC;AAG7G,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sCAAsC,CAAC;AACrD,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AACrD,cAAc,gCAAgC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,24 @@ import { LoadLiveKitBridge } from '@memberjunction/ai-bridge-livekit';
|
|
|
13
13
|
// MJServer does) registers the bridge. The no-op call is a tree-shake guard. Without this the room start
|
|
14
14
|
// fails with "No bridge driver registered for DriverClass 'LiveKitBridge'".
|
|
15
15
|
LoadLiveKitBridge();
|
|
16
|
-
export { LiveKitTokenService } from './livekit-token-service.js';
|
|
16
|
+
export { LiveKitTokenService, LiveKitUserIdentity } from './livekit-token-service.js';
|
|
17
17
|
export { LiveKitAgentRoomCoordinator, LIVEKIT_BRIDGE_DRIVER_CLASS, } from './livekit-agent-room-coordinator.js';
|
|
18
18
|
export { LiveKitEgressService, WsToHttpUrl, wsToHttpUrl } from './livekit-egress-service.js';
|
|
19
|
+
export { LiveKitParticipantService } from './livekit-participant-service.js';
|
|
20
|
+
export { LiveKitSipService, LIVEKIT_PARTICIPANT_KIND_SIP, LIVEKIT_SIP_ATTRIBUTES, SIP_DIAL_REQUEST_TIMEOUT_SECONDS, SIP_DIAL_MAX_DURATION_SECONDS, } from './livekit-sip-service.js';
|
|
21
|
+
export { LiveKitWebhookParser } from './livekit-webhook.js';
|
|
22
|
+
export * from './room-handoff/handoff-types.js';
|
|
23
|
+
export * from './room-handoff/handoff-offer-registry.js';
|
|
24
|
+
export * from './room-handoff/room-handoff-engine.js';
|
|
25
|
+
export * from './room-authorization.js';
|
|
26
|
+
export * from './meeting-dial-in-service.js';
|
|
27
|
+
export { DEFAULT_LIVEKIT_NATIVE_MODULE, ResolveLiveKitNativeModuleSpecifier } from './livekit-native-module.js';
|
|
28
|
+
// Room audio player (contact-center hold music + ducked announcements).
|
|
29
|
+
export * from './room-audio/audio-decoder.js';
|
|
30
|
+
export * from './room-audio/comfort-tone.js';
|
|
31
|
+
export * from './room-audio/pcm-frame-pump.js';
|
|
32
|
+
export * from './room-audio/room-audio-decode-cache.js';
|
|
33
|
+
export * from './room-audio/room-audio-sources.js';
|
|
34
|
+
export * from './room-audio/room-speech-synthesizer.js';
|
|
35
|
+
export * from './room-audio/room-audio-player.js';
|
|
19
36
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,+FAA+F;AAC/F,sGAAsG;AACtG,wGAAwG;AACxG,yGAAyG;AACzG,4EAA4E;AAC5E,iBAAiB,EAAE,CAAC;AAEpB,OAAO,EAAE,mBAAmB,EAA2F,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,+FAA+F;AAC/F,sGAAsG;AACtG,wGAAwG;AACxG,yGAAyG;AACzG,4EAA4E;AAC5E,iBAAiB,EAAE,CAAC;AAEpB,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAA2F,MAAM,yBAAyB,CAAC;AAE5K,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,GAO5B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,WAAW,EAAwE,MAAM,0BAA0B,CAAC;AAEhK,OAAO,EAAE,yBAAyB,EAAkE,MAAM,+BAA+B,CAAC;AAE1I,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,sBAAsB,EACtB,gCAAgC,EAChC,6BAA6B,GAQ9B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,oBAAoB,EAA0D,MAAM,mBAAmB,CAAC;AAEjH,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAE1C,OAAO,EAAE,6BAA6B,EAAE,mCAAmC,EAAE,MAAM,yBAAyB,CAAC;AAE7G,wEAAwE;AACxE,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sCAAsC,CAAC;AACrD,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AACrD,cAAc,gCAAgC,CAAC"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview {@link LiveKitAgentRoomCoordinator} — the server-side **session-start harness** for the
|
|
3
3
|
* MJ-native LiveKit room (§1 of the realtime-bridge buildout plan). Given an agent + a room, it:
|
|
4
|
-
* 1.
|
|
5
|
-
* 2.
|
|
4
|
+
* 1. opens the realtime model session via an injectable {@link RealtimeSessionFactory} seam,
|
|
5
|
+
* 2. mints the agent bot's scoped LiveKit token ({@link LiveKitTokenService}), whose attributes depend on the session, and
|
|
6
6
|
* 3. bridges that session into the room through {@link AIBridgeEngine.StartBridgeSession} (which wires
|
|
7
7
|
* the transport seam + turn-taking automatically).
|
|
8
|
+
* A start that fails after step 1 closes the model session before the error propagates, recording the failure on the
|
|
9
|
+
* agent's co-agent run first ({@link FailedStartRecorder}).
|
|
8
10
|
*
|
|
9
11
|
* The realtime-session factory is a seam (mirroring `LiveKitBridge.SetSdkFactory`): production binds it to
|
|
10
12
|
* the real model-resolution path (`@memberjunction/ai-agents`), and tests/de-risk inject a stub session.
|
|
@@ -14,9 +16,10 @@
|
|
|
14
16
|
*/
|
|
15
17
|
import { BaseSingleton } from '@memberjunction/global';
|
|
16
18
|
import { type IMetadataProvider, type UserInfo } from '@memberjunction/core';
|
|
17
|
-
import type
|
|
18
|
-
import { type
|
|
19
|
-
import {
|
|
19
|
+
import { type IRealtimeSession, type RealtimeToolDefinition } from '@memberjunction/ai';
|
|
20
|
+
import { type NativeAvatarVideoSupport } from '@memberjunction/ai-bridge-livekit';
|
|
21
|
+
import { type BridgeDisconnectReason, type BridgeTurnMode, type TurnAddressingMode } from '@memberjunction/ai-bridge-base';
|
|
22
|
+
import { AIBridgeEngine, type BridgeTranscriptSink, type BridgeTurnTakingToolHandler, type RoomTurnSnapshot } from '@memberjunction/ai-bridge-server';
|
|
20
23
|
import { LiveKitTokenService } from './livekit-token-service.js';
|
|
21
24
|
/** The subset of {@link AIBridgeEngine} the coordinator drives — an injectable seam for unit testing. */
|
|
22
25
|
export type BridgeOps = Pick<AIBridgeEngine, 'Config' | 'ProviderByDriverClass' | 'StartBridgeSession' | 'StopBridgeSession' | 'ReconfigureSessionToMeeting'>;
|
|
@@ -49,7 +52,95 @@ export interface RealtimeSessionStartContext {
|
|
|
49
52
|
MeetingMode?: boolean;
|
|
50
53
|
/** The names the agent answers to (display name + aliases) — phrasing for the meeting prompt. */
|
|
51
54
|
SelfNames?: string[];
|
|
55
|
+
/** Tools the host declares and executes itself (call control, handoff). Added to the model's tool set. */
|
|
56
|
+
HostTools?: RealtimeToolDefinition[];
|
|
57
|
+
/**
|
|
58
|
+
* Optional callback allowing the coordinator to resolve host tools dynamically based on
|
|
59
|
+
* the model and driver class context actually resolved for the session, before session opening.
|
|
60
|
+
*/
|
|
61
|
+
ResolveHostTools?: (resolved: {
|
|
62
|
+
ModelID?: string;
|
|
63
|
+
ModelVendorID?: string;
|
|
64
|
+
DriverClass?: string;
|
|
65
|
+
}) => RealtimeToolDefinition[] | undefined;
|
|
66
|
+
/** Host-authored instructions appended to the system prompt (for example the phone framing). */
|
|
67
|
+
HostFraming?: string;
|
|
68
|
+
/** Role-tagged transcript so far, framed into the prompt when a lost model session is re-opened mid-call. */
|
|
69
|
+
PriorTranscript?: string;
|
|
70
|
+
/** The `MJ: Conversations` row the session writes to — stamped on the co-agent observability run. */
|
|
71
|
+
ConversationID?: string;
|
|
72
|
+
/**
|
|
73
|
+
* `'room'` when this host's bot can publish the agent's avatar into the room (the native module answered
|
|
74
|
+
* `describeAvatarVideo` with support): the session may then ask the model to render it. Absent: audio only, as before.
|
|
75
|
+
*/
|
|
76
|
+
AvatarDelivery?: 'room';
|
|
77
|
+
/**
|
|
78
|
+
* `true` when the agent is answering a phone call in the room ({@link AgentRoomHostOptions.Channel} `'phone'`): the
|
|
79
|
+
* caller sees no video, so the session asks the model for no avatar and reports `phone`. Never set with
|
|
80
|
+
* {@link AvatarDelivery}.
|
|
81
|
+
*/
|
|
82
|
+
PhoneCall?: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What a HOST that owns the call (a phone call arriving in a room, a web room with handoff tools) adds to an agent's
|
|
86
|
+
* room session beyond the plain "join the room" the coordinator does for the Meet UI. All optional: a session started
|
|
87
|
+
* without it behaves exactly as before. A start that created the agent session for this agent alone (a Meet room, a
|
|
88
|
+
* scheduled meeting) passes only {@link AgentRoomHostOptions.OnSessionEnded}, to close that session when the agent leaves.
|
|
89
|
+
*/
|
|
90
|
+
export interface AgentRoomHostOptions {
|
|
91
|
+
/** Tools the host executes itself. */
|
|
92
|
+
HostTools?: RealtimeToolDefinition[];
|
|
93
|
+
/** Instructions appended to the agent's system prompt. */
|
|
94
|
+
HostFraming?: string;
|
|
95
|
+
/** The conversation the session's transcript belongs to. */
|
|
96
|
+
ConversationID?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Called with every model session opened for this agent (the first one, and any re-opened after a drop) so the host
|
|
99
|
+
* can attach its tool handler to each.
|
|
100
|
+
*/
|
|
101
|
+
OnModelSession?: (session: IRealtimeSession) => void;
|
|
102
|
+
/** Where this session's final transcript lines go (instead of the shared room transcript). */
|
|
103
|
+
TranscriptSink?: BridgeTranscriptSink;
|
|
104
|
+
/** The participant talked over the agent (a true barge-in). */
|
|
105
|
+
OnBargeIn?: () => void;
|
|
106
|
+
/** Re-open the model session once, with the conversation so far, if it drops mid-call. */
|
|
107
|
+
RecoverModelSession?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Called once when the session has fully ended, for whatever reason (a stop, an emptied room, a lost model session),
|
|
110
|
+
* for the host's own bookkeeping: closing the agent session it created, freeing a capacity slot.
|
|
111
|
+
*/
|
|
112
|
+
OnSessionEnded?: (reason: BridgeDisconnectReason) => void | Promise<void>;
|
|
113
|
+
/** How the agent got into the room. Default `'OnDemand'`. */
|
|
114
|
+
JoinMethod?: 'InboundRoute' | 'OnDemand' | 'Invite';
|
|
115
|
+
/** Whether the agent was called into the room or placed the call. Default: not stated. */
|
|
116
|
+
Direction?: 'Inbound' | 'Outbound';
|
|
117
|
+
/**
|
|
118
|
+
* How the person the agent serves reached the room: `'phone'`, a phone call through SIP (they hear the agent and see
|
|
119
|
+
* nothing), or `'web'`, a browser. On a phone call the coordinator never asks for the avatar: no room delivery, and the
|
|
120
|
+
* session asks the model for none (`phone`), even when people in the room could see it. Default: not stated, which
|
|
121
|
+
* is treated like `'web'`.
|
|
122
|
+
*/
|
|
123
|
+
Channel?: 'phone' | 'web';
|
|
52
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Installs the engine's turn-taking tool handler on a model session — the seam that lets this package stay free
|
|
127
|
+
* of the agent runtime. Production binds it to `GetBridgeRealtimeRuntime(session)?.SetLocalToolHandler(handler)`
|
|
128
|
+
* (`@memberjunction/ai-agents`); a test binds a spy.
|
|
129
|
+
*/
|
|
130
|
+
export type TurnToolBinder = (session: IRealtimeSession, handler: BridgeTurnTakingToolHandler) => void;
|
|
131
|
+
/**
|
|
132
|
+
* Says whether the agent behind a model session watches meetings (its `realtime.video.watchMeetings` setting) — the
|
|
133
|
+
* seam that keeps this package free of the agent runtime, like {@link TurnToolBinder}. Production binds it to the
|
|
134
|
+
* session's bridged runtime (`@memberjunction/ai-agents`); unbound, no agent watches.
|
|
135
|
+
*/
|
|
136
|
+
export type AgentVisionResolver = (session: IRealtimeSession) => boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Records a failed start on the co-agent run of the model session the start opened, with the start's error — the seam
|
|
139
|
+
* that keeps this package free of the agent runtime, like {@link TurnToolBinder}. Production binds it to the session's
|
|
140
|
+
* bridged runtime (`Finalize(false, error)`, `@memberjunction/ai-agents`). The coordinator calls it before it closes that
|
|
141
|
+
* session, because the close alone finalizes the run as completed, which is what happens when nothing is bound.
|
|
142
|
+
*/
|
|
143
|
+
export type FailedStartRecorder = (session: IRealtimeSession, error: string) => Promise<void>;
|
|
53
144
|
/**
|
|
54
145
|
* Opens a realtime model session for the agent. Production binds this to the real model-resolution path;
|
|
55
146
|
* tests inject a stub. Defaults to a clear "not bound" error so misconfiguration fails loudly.
|
|
@@ -74,14 +165,36 @@ export interface StartAgentRoomSessionParams {
|
|
|
74
165
|
RealtimeModelID?: string;
|
|
75
166
|
/** Optional per-session VOICE override (provider-native voice id) — gives this agent a distinct voice. */
|
|
76
167
|
RealtimeVoice?: string;
|
|
168
|
+
/** Explicit override for whether the model is full-duplex. When omitted, resolved from model metadata or driver capabilities. */
|
|
169
|
+
FullDuplex?: boolean;
|
|
77
170
|
/** Extra aliases the agent answers to (for Passive turn-taking). */
|
|
78
171
|
AgentAliases?: string[];
|
|
79
172
|
/** Turn-taking mode. Default: `'Passive'` (speak only when addressed). */
|
|
80
173
|
TurnMode?: BridgeTurnMode;
|
|
174
|
+
/**
|
|
175
|
+
* How the agent decides it was addressed. `'Auto'` (the default) uses the MODEL's own judgement when its model is
|
|
176
|
+
* full-duplex and falls back to name matching otherwise; `'ModelSide'` / `'Regex'` force one. A room in moderator
|
|
177
|
+
* mode (gated meeting) always uses `'Regex'`.
|
|
178
|
+
*/
|
|
179
|
+
TurnAddressing?: TurnAddressingMode;
|
|
81
180
|
/** The user the session runs as. */
|
|
82
181
|
ContextUser?: UserInfo;
|
|
83
182
|
/** The metadata provider for the session. */
|
|
84
183
|
MetadataProvider?: IMetadataProvider;
|
|
184
|
+
/**
|
|
185
|
+
* What a host that owns the call adds (tools, framing, transcript, recovery). A plain Meet room passes at most its
|
|
186
|
+
* end-of-session hook, to close the agent session it created.
|
|
187
|
+
*/
|
|
188
|
+
Host?: AgentRoomHostOptions;
|
|
189
|
+
}
|
|
190
|
+
/** One agent's membership in a room's roster (for multi-agent meeting detection). */
|
|
191
|
+
export interface RoomAgentEntry {
|
|
192
|
+
/** The MJ agent-session id of this agent in the room. */
|
|
193
|
+
AgentSessionID: string;
|
|
194
|
+
/** The durable bridge row id — the key {@link LiveKitAgentRoomCoordinator.StopAgentRoomSession} removes by. */
|
|
195
|
+
SessionBridgeID: string;
|
|
196
|
+
/** The names this agent answers to (display name + aliases) — used to build its addressing matcher. */
|
|
197
|
+
Names: string[];
|
|
85
198
|
}
|
|
86
199
|
/** The result of starting an agent room session. */
|
|
87
200
|
export interface AgentRoomSession {
|
|
@@ -109,6 +222,10 @@ export declare class LiveKitAgentRoomCoordinator extends BaseSingleton<LiveKitAg
|
|
|
109
222
|
*/
|
|
110
223
|
private roomRosters;
|
|
111
224
|
private bridgeOps;
|
|
225
|
+
private turnToolBinder?;
|
|
226
|
+
private agentVisionResolver?;
|
|
227
|
+
private failedStartRecorder?;
|
|
228
|
+
private turnStateSource;
|
|
112
229
|
private sessionFactory;
|
|
113
230
|
/** BaseSingleton requires a protected constructor. */
|
|
114
231
|
protected constructor();
|
|
@@ -120,6 +237,43 @@ export declare class LiveKitAgentRoomCoordinator extends BaseSingleton<LiveKitAg
|
|
|
120
237
|
* @param factory The factory that opens an {@link IRealtimeSession} for an agent.
|
|
121
238
|
*/
|
|
122
239
|
SetSessionFactory(factory: RealtimeSessionFactory): void;
|
|
240
|
+
/**
|
|
241
|
+
* Binds the seam that installs the engine's turn-taking tool handler on a model session. Without it a
|
|
242
|
+
* full-duplex model's `i_am_addressed` / `yield_turn` calls cannot be executed (the room's floor gate still
|
|
243
|
+
* protects against overlap; the model just cannot reserve the floor or hand it to a named agent).
|
|
244
|
+
*
|
|
245
|
+
* @param binder The binder, or `undefined` to clear it.
|
|
246
|
+
*/
|
|
247
|
+
SetTurnToolBinder(binder: TurnToolBinder | undefined): void;
|
|
248
|
+
/**
|
|
249
|
+
* Binds the seam that says whether the agent behind a model session watches meetings. Without it no agent watches:
|
|
250
|
+
* the bot reads nobody's camera or screen and the room never offers people the choice.
|
|
251
|
+
*
|
|
252
|
+
* @param resolver The resolver, or `undefined` to clear it.
|
|
253
|
+
*/
|
|
254
|
+
SetAgentVisionResolver(resolver: AgentVisionResolver | undefined): void;
|
|
255
|
+
/**
|
|
256
|
+
* Binds the seam that records a failed start on the agent's co-agent run. Without it, the run of an agent whose start
|
|
257
|
+
* failed reads as completed: closing its model session finalizes the run that way.
|
|
258
|
+
*
|
|
259
|
+
* @param recorder The recorder, or `undefined` to clear it.
|
|
260
|
+
*/
|
|
261
|
+
SetFailedStartRecorder(recorder: FailedStartRecorder | undefined): void;
|
|
262
|
+
/**
|
|
263
|
+
* Overrides where a room's turn-taking snapshot comes from (an injectable seam for unit testing; production
|
|
264
|
+
* reads the process-wide {@link AIBridgeEngine}).
|
|
265
|
+
*
|
|
266
|
+
* @param source Returns the snapshot for a room key, or `null` when the room holds no agents.
|
|
267
|
+
*/
|
|
268
|
+
SetTurnStateSource(source: (roomKey: string) => RoomTurnSnapshot | null): void;
|
|
269
|
+
/**
|
|
270
|
+
* A room's live turn-taking state — floor holder, whether a person is speaking, any pending hand-off, the
|
|
271
|
+
* agent-to-agent loop-cap progress, the most recent floor events, and who is seated. Read-only.
|
|
272
|
+
*
|
|
273
|
+
* @param roomName The LiveKit room name (the bridge's room key).
|
|
274
|
+
* @returns The snapshot, or `null` when the room holds no agents.
|
|
275
|
+
*/
|
|
276
|
+
GetRoomTurnState(roomName: string): RoomTurnSnapshot | null;
|
|
123
277
|
/**
|
|
124
278
|
* Overrides the token service used to mint the bot token (e.g. to inject explicit credentials).
|
|
125
279
|
*
|
|
@@ -134,24 +288,66 @@ export declare class LiveKitAgentRoomCoordinator extends BaseSingleton<LiveKitAg
|
|
|
134
288
|
*/
|
|
135
289
|
SetBridgeOps(ops: BridgeOps): void;
|
|
136
290
|
/**
|
|
137
|
-
* Starts the agent's presence in a LiveKit room:
|
|
138
|
-
* bridges
|
|
291
|
+
* Starts the agent's presence in a LiveKit room: opens the realtime session, mints the bot token, and
|
|
292
|
+
* bridges the session into the room.
|
|
293
|
+
*
|
|
294
|
+
* A start that fails once the model session is open (the bot token can't be minted, the bridge doesn't start) closes
|
|
295
|
+
* that session before the error propagates, after recording the failure, with the start's error, on the session's
|
|
296
|
+
* co-agent run ({@link SetFailedStartRecorder}), which would otherwise read as completed. The agent session it runs under
|
|
297
|
+
* (`AgentSessionID`) is the caller's: a caller that created one for this start closes it.
|
|
139
298
|
*
|
|
140
299
|
* @param params The session parameters.
|
|
141
300
|
* @returns The active session handles.
|
|
142
|
-
* @throws {Error} when the LiveKit provider is not configured/registered
|
|
301
|
+
* @throws {Error} when the LiveKit provider is not configured/registered, the session factory is unbound or fails,
|
|
302
|
+
* the bot token can't be minted, or the bridge doesn't start.
|
|
143
303
|
*/
|
|
144
304
|
StartAgentRoomSession(params: StartAgentRoomSessionParams): Promise<AgentRoomSession>;
|
|
305
|
+
/**
|
|
306
|
+
* Whether the agent behind this model session watches the meeting: its setting is on (through the bound
|
|
307
|
+
* {@link AgentVisionResolver}) AND its session takes inbound video. Watching on an audio-only model would offer people
|
|
308
|
+
* a choice no frame could honour, so it is logged and treated as off.
|
|
309
|
+
*/
|
|
310
|
+
private agentWatches;
|
|
311
|
+
/**
|
|
312
|
+
* Whether this host's bots can publish an agent's avatar, from the native room module the bots join with (`ffmpeg`
|
|
313
|
+
* with the H.264 and AAC decoders, and a room SDK that publishes video). Asked before every agent's model session
|
|
314
|
+
* opens; the module probes once and remembers. A module that cannot be loaded, or that predates avatars, means no.
|
|
315
|
+
*/
|
|
316
|
+
DescribeAvatarVideo(): Promise<NativeAvatarVideoSupport>;
|
|
317
|
+
/**
|
|
318
|
+
* What became of the agent's avatar request: the session's own status, except that a session which asked for an
|
|
319
|
+
* avatar and stayed audio only because this host could not publish it carries the host's reason (`decoder-missing`
|
|
320
|
+
* when there is no usable ffmpeg, `phone` on a phone call whose session factory still asked). Logged once per join when
|
|
321
|
+
* an avatar was asked for.
|
|
322
|
+
*/
|
|
323
|
+
private avatarStatusFor;
|
|
324
|
+
/**
|
|
325
|
+
* Mints the bot's join token with its attributes: `mj.agentWatches` when the agent watches, so the room offers people
|
|
326
|
+
* the choice from the moment the bot joins, and `mj.agentAvatar` when the agent asked for an avatar. Minted once the
|
|
327
|
+
* model session is open; when it fails, the start closes that session (see {@link StartAgentRoomSession}).
|
|
328
|
+
*/
|
|
329
|
+
private mintBotToken;
|
|
330
|
+
/**
|
|
331
|
+
* Closes the model session of a start that failed before the bridge engine took it: the bot's token could not be minted,
|
|
332
|
+
* or the bridge did not start (its row could not be saved, the driver could not connect). The failure is recorded on the
|
|
333
|
+
* session's co-agent run first ({@link FailedStartRecorder}), since the close alone would finalize that run as completed.
|
|
334
|
+
* Neither step throws: a failure is logged, so the caller sees why the start failed and the session is still closed.
|
|
335
|
+
*/
|
|
336
|
+
private closeModelSessionOfFailedStart;
|
|
337
|
+
/** Installs the engine's turn-taking tool handler on the model session, when the session has one (a turn-based model in a room). */
|
|
338
|
+
private bindTurnTools;
|
|
339
|
+
/**
|
|
340
|
+
* Resolves whether the model with the given ID is full duplex from model metadata and static driver capabilities.
|
|
341
|
+
*/
|
|
342
|
+
private resolveIsModelFullDuplexFromId;
|
|
145
343
|
/** Appends an agent to a room's roster (creating the room's list on first join). */
|
|
146
344
|
private addToRoster;
|
|
147
345
|
/** Removes an agent from whatever room roster holds its bridge id; prunes the room when it empties. */
|
|
148
346
|
private removeFromRoster;
|
|
149
|
-
/** The default native room-client wrapper specifier — the @livekit/rtc-node package this repo ships. */
|
|
150
|
-
private static readonly DEFAULT_NATIVE_MODULE;
|
|
151
347
|
/**
|
|
152
|
-
* Resolves the native LiveKit room-client module specifier for the bridge session
|
|
153
|
-
*
|
|
154
|
-
*
|
|
348
|
+
* Resolves the native LiveKit room-client module specifier for the bridge session (see
|
|
349
|
+
* {@link ResolveLiveKitNativeModuleSpecifier}: the test override, else `LIVEKIT_NATIVE_MODULE`, else the default
|
|
350
|
+
* `@memberjunction/ai-bridge-livekit-native`). Overridable in tests via {@link SetNativeModuleSpecifier}.
|
|
155
351
|
*/
|
|
156
352
|
private resolveNativeModuleSpecifier;
|
|
157
353
|
/**
|
|
@@ -185,5 +381,16 @@ export declare class LiveKitAgentRoomCoordinator extends BaseSingleton<LiveKitAg
|
|
|
185
381
|
* @returns The number of agent sessions asked to stop (0 when the room held no agents).
|
|
186
382
|
*/
|
|
187
383
|
StopAllAgentsInRoom(roomName: string, reason?: BridgeDisconnectReason, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<number>;
|
|
384
|
+
/**
|
|
385
|
+
* Looks up the room name associated with a session bridge ID if present in the coordinator's active rosters.
|
|
386
|
+
*
|
|
387
|
+
* @param sessionBridgeID The `MJ: AI Agent Session Bridges` row id.
|
|
388
|
+
* @returns The room name, or `undefined` if not tracked in the active rosters.
|
|
389
|
+
*/
|
|
390
|
+
GetRoomForBridge(sessionBridgeID: string): string | undefined;
|
|
391
|
+
/**
|
|
392
|
+
* Returns the agent sessions currently active in the given room.
|
|
393
|
+
*/
|
|
394
|
+
GetAgentsInRoom(roomName: string): ReadonlyArray<RoomAgentEntry>;
|
|
188
395
|
}
|
|
189
396
|
//# sourceMappingURL=livekit-agent-room-coordinator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"livekit-agent-room-coordinator.d.ts","sourceRoot":"","sources":["../src/livekit-agent-room-coordinator.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"livekit-agent-room-coordinator.d.ts","sourceRoot":"","sources":["../src/livekit-agent-room-coordinator.ts"],"names":[],"mappings":"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"}
|