@memberjunction/ai-bridge-webex 5.41.0 → 5.42.0

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/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from './webex-sdk.js';
2
2
  export * from './webex-meeting-controls.js';
3
3
  export * from './webex-bridge.js';
4
+ export * from './webex-native-sdk.js';
5
+ export * from './register-native.js';
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,13 @@
1
1
  export * from './webex-sdk.js';
2
2
  export * from './webex-meeting-controls.js';
3
3
  export * from './webex-bridge.js';
4
+ export * from './webex-native-sdk.js';
5
+ export * from './register-native.js';
4
6
  import { LoadWebexBridge } from './webex-bridge.js';
7
+ import { RegisterWebexNativeSdk } from './register-native.js';
5
8
  // Static reference so bundlers cannot tree-shake the @RegisterClass(BaseRealtimeBridge, 'WebexBridge')
6
9
  // registration. Calling the no-op here keeps the driver resolvable by the engine's ClassFactory.
7
10
  LoadWebexBridge();
11
+ // Register Webex's native two-way SDK binding so the engine auto-binds it at StartBridgeSession.
12
+ RegisterWebexNativeSdk();
8
13
  //# 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,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAE/B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,uGAAuG;AACvG,iGAAiG;AACjG,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,uGAAuG;AACvG,iGAAiG;AACjG,eAAe,EAAE,CAAC;AAClB,iGAAiG;AACjG,sBAAsB,EAAE,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @fileoverview Registers the Webex **native two-way** SDK binding into the {@link BridgeNativeSdkRegistry}
3
+ * so `AIBridgeEngine.StartBridgeSession` auto-binds it onto a resolved `WebexBridge` before connect — no
4
+ * per-deployment wiring needed. The typed downcast + `SetSdkFactory` stay confined here (where the concrete
5
+ * `WebexBridge` type is known), keeping the engine seam free of provider types.
6
+ *
7
+ * A host can still override per-session via `StartBridgeSessionParams.BindSdk`.
8
+ *
9
+ * @module @memberjunction/ai-bridge-webex
10
+ * @author MemberJunction.com
11
+ */
12
+ /**
13
+ * Registers Webex's native two-way binding. Idempotent (latest-wins in the registry), and invoked from the
14
+ * package `index.ts` so merely importing `@memberjunction/ai-bridge-webex` wires the default binding. Also
15
+ * exported as a tree-shake-safe no-op-returning function callers can reference to force-retain the module.
16
+ */
17
+ export declare function RegisterWebexNativeSdk(): void;
18
+ //# sourceMappingURL=register-native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-native.d.ts","sourceRoot":"","sources":["../src/register-native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAO7C"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @fileoverview Registers the Webex **native two-way** SDK binding into the {@link BridgeNativeSdkRegistry}
3
+ * so `AIBridgeEngine.StartBridgeSession` auto-binds it onto a resolved `WebexBridge` before connect — no
4
+ * per-deployment wiring needed. The typed downcast + `SetSdkFactory` stay confined here (where the concrete
5
+ * `WebexBridge` type is known), keeping the engine seam free of provider types.
6
+ *
7
+ * A host can still override per-session via `StartBridgeSessionParams.BindSdk`.
8
+ *
9
+ * @module @memberjunction/ai-bridge-webex
10
+ * @author MemberJunction.com
11
+ */
12
+ import { BridgeNativeSdkRegistry } from '@memberjunction/ai-bridge-base';
13
+ import { BindWebexNative } from './webex-native-sdk.js';
14
+ /** The `MJ: AI Bridge Providers.DriverClass` value the Webex provider row carries. */
15
+ const WEBEX_DRIVER_CLASS = 'WebexBridge';
16
+ /**
17
+ * Registers Webex's native two-way binding. Idempotent (latest-wins in the registry), and invoked from the
18
+ * package `index.ts` so merely importing `@memberjunction/ai-bridge-webex` wires the default binding. Also
19
+ * exported as a tree-shake-safe no-op-returning function callers can reference to force-retain the module.
20
+ */
21
+ export function RegisterWebexNativeSdk() {
22
+ BridgeNativeSdkRegistry.Instance.Register(WEBEX_DRIVER_CLASS, (driver) => {
23
+ // Safe downcast: the registry is keyed by DriverClass, so the engine only ever applies this to the
24
+ // WebexBridge it resolved for 'WebexBridge'. BindWebexNative() defaults to the lazy native loader,
25
+ // which reads NativeModuleSpecifier from the per-session Configuration.
26
+ driver.SetSdkFactory(BindWebexNative());
27
+ });
28
+ }
29
+ // Register on import (mirrors the LoadWebexBridge() tree-shake pattern in index.ts).
30
+ RegisterWebexNativeSdk();
31
+ //# sourceMappingURL=register-native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-native.js","sourceRoot":"","sources":["../src/register-native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,uBAAuB,EAAsB,MAAM,gCAAgC,CAAC;AAE7F,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,sFAAsF;AACtF,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IAClC,uBAAuB,CAAC,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,MAA0B,EAAE,EAAE;QACzF,mGAAmG;QACnG,mGAAmG;QACnG,wEAAwE;QACvE,MAAsB,CAAC,aAAa,CAAC,eAAe,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACP,CAAC;AAED,qFAAqF;AACrF,sBAAsB,EAAE,CAAC"}
@@ -0,0 +1,283 @@
1
+ /**
2
+ * @fileoverview `WebexNativeMeetingSdk` — a **real, two-way** {@link IWebexMeetingSdk} binding over a
3
+ * **native Cisco Webex media bot addon** (a headless meeting-bot build with the raw-media send + receive
4
+ * entitlement). Unlike a UI/embedded-app binding, this adapter gives the agent a **voice**:
5
+ * {@link sendAudioFrame} forwards the agent's synthesized PCM to the addon's outbound audio **send** path,
6
+ * and the host controls (chat / mute) actually act on the live meeting.
7
+ *
8
+ * ## Why a native binding
9
+ * Pushing audio *into* a Webex meeting as a server-side bot requires a media-capable bot runtime — the
10
+ * Webex Meetings SDK's raw audio media tracks (or an equivalent media-bot addon) expose an outbound audio
11
+ * **send** API alongside the per-member inbound raw-audio callback, so a single binding carries
12
+ * **bidirectional** audio. This is the binding a two-way "agent talks in the meeting" deployment uses.
13
+ *
14
+ * ## The native module seam (no SDK types leak)
15
+ * The native Webex media bot is delivered as an N-API addon or a local sidecar process exposing the small
16
+ * {@link NativeMeetingModule} surface declared below. This adapter depends ONLY on that structural surface
17
+ * — none of the Webex SDK's real types leak into the package, and the module is loaded **lazily** behind an
18
+ * injectable {@link NativeModuleLoader} so `@memberjunction/ai-bridge-webex` builds and unit-tests with
19
+ * **no addon and no network**. Tests inject a fake module; production points
20
+ * {@link WebexNativeSdkConfig.NativeModuleSpecifier} at the real addon.
21
+ *
22
+ * ## Entitlements + auth (deployment responsibility)
23
+ * The native bot joins with a Webex **bot/OAuth access token** (NOT inline) and needs the media-bot
24
+ * (raw audio send + receive) entitlement on the Webex org. Credentials resolve **upstream** (MJ credential
25
+ * system / provider `Configuration`) and are **never inlined**.
26
+ *
27
+ * Every spot where the native addon's exact surface is assumed carries a `// VERIFY against the native
28
+ * Webex media bot addon` note so a live test can confirm it.
29
+ *
30
+ * @module @memberjunction/ai-bridge-webex
31
+ * @author MemberJunction.com
32
+ */
33
+ import { IWebexMeetingSdk, WebexAudioFrame, WebexJoinArgs, WebexJoinResult, WebexParticipant, WebexParticipantRole } from './webex-sdk.js';
34
+ /** One raw per-member audio frame the native addon surfaces (inbound hearing + diarization). */
35
+ export interface NativeAudioFrame {
36
+ /** Raw PCM bytes for this frame (`Uint8Array` view or standalone `ArrayBuffer`). */
37
+ data: Uint8Array | ArrayBuffer;
38
+ /** The Webex member id that produced the audio (the diarization speaker label). */
39
+ participantId: string | number;
40
+ /** The member's display name at capture time, when the addon provides it. */
41
+ displayName?: string;
42
+ /** Optional epoch-ms capture timestamp. */
43
+ timestampMs?: number;
44
+ }
45
+ /** One member as the native addon reports it. Mapped onto {@link WebexParticipant}. */
46
+ export interface NativeParticipant {
47
+ /** The native member id. */
48
+ participantId: string | number;
49
+ /** The member's display name. */
50
+ displayName?: string;
51
+ /** The member's role as the addon reports it (`host` / `cohost` / `attendee`). */
52
+ role?: string;
53
+ /** Whether this member is the bot itself. */
54
+ isSelf?: boolean;
55
+ }
56
+ /** The arguments the native addon's `join()` accepts. Mirrors {@link WebexJoinArgs} plus raw-audio opts. */
57
+ export interface NativeJoinArgs {
58
+ /** The Webex meeting link the bot joins (the durable join coordinate). */
59
+ meetingLink: string;
60
+ /** The parsed meeting number / SIP address, when resolved upstream. */
61
+ meetingNumber?: string;
62
+ /** The bot's display name in the participant list. */
63
+ displayName: string;
64
+ /** The Webex bot/OAuth access token authorizing the join (resolved upstream; never inline). */
65
+ accessToken?: string;
66
+ /** The Webex site (org) the meeting belongs to, when joining cross-site. */
67
+ siteUrl?: string;
68
+ /** Raw-audio sample rate the bot sends/receives (Hz), when the addon needs it told. */
69
+ sampleRate?: number;
70
+ /** Raw-audio channel count, when the addon needs it told. */
71
+ channels?: number;
72
+ }
73
+ /** What the native addon's `join()` resolves to. */
74
+ export interface NativeJoinResult {
75
+ /** The bot's own member id in the joined meeting. */
76
+ botParticipantId: string | number;
77
+ /** The Webex meeting id the bot joined. */
78
+ meetingId: string;
79
+ }
80
+ /**
81
+ * The live native-meeting client the addon hands back. The surface closely mirrors {@link IWebexMeetingSdk}
82
+ * but in the addon's own (lower-cased) vocabulary; this adapter maps between the two. VERIFY against the
83
+ * native Webex media bot addon.
84
+ */
85
+ export interface NativeMeetingClient {
86
+ /** Joins the meeting (async — the native join handshake is network-bound). */
87
+ join(args: NativeJoinArgs): Promise<NativeJoinResult>;
88
+ /** Leaves the meeting and releases native resources. */
89
+ leave(): Promise<void>;
90
+ /** Sends one raw PCM frame as the bot's outbound audio (the outbound media track — the agent's voice). */
91
+ sendAudioFrame(pcm: ArrayBuffer): void;
92
+ /** Registers the inbound per-member raw-audio callback. "Latest handler wins." */
93
+ onAudioFrame(cb: (frame: NativeAudioFrame) => void): void;
94
+ /** Registers the member-join callback. */
95
+ onParticipantJoin(cb: (participant: NativeParticipant) => void): void;
96
+ /** Registers the member-leave callback (id of the member that left). */
97
+ onParticipantLeave(cb: (participantId: string | number) => void): void;
98
+ /** Registers the native hand-raise/lower callback. */
99
+ onHandRaise(cb: (participantId: string | number, raised: boolean) => void): void;
100
+ /** Returns the current member roster (including the bot). */
101
+ getParticipants(): Promise<NativeParticipant[]>;
102
+ /** Posts a message to the meeting space chat (everyone). */
103
+ postChatMessage(text: string): Promise<void>;
104
+ /** Mutes a member (requires the bot be host/cohost). */
105
+ muteParticipant(participantId: string | number): Promise<void>;
106
+ /** Registers the meeting-ended callback. */
107
+ onMeetingEnded(cb: () => void): void;
108
+ }
109
+ /** The native addon module surface — a factory that constructs a {@link NativeMeetingClient}. */
110
+ export interface NativeMeetingModule {
111
+ /**
112
+ * Constructs a native meeting client from resolved options (Webex credentials already resolved upstream).
113
+ * VERIFY against the native Webex media bot addon (`createClient` vs a `Client` constructor).
114
+ */
115
+ createClient(options: NativeClientOptions): NativeMeetingClient;
116
+ }
117
+ /** Options passed to {@link NativeMeetingModule.createClient}. Credentials are resolved upstream. */
118
+ export interface NativeClientOptions {
119
+ /** The Webex bot/OAuth access token (resolved upstream). */
120
+ AccessToken?: string;
121
+ /** The Webex site (org) url, when joining cross-site (resolved upstream). */
122
+ SiteUrl?: string;
123
+ }
124
+ /**
125
+ * Resolved configuration for {@link WebexNativeMeetingSdk}. Credentials resolve **upstream** (MJ credential
126
+ * system / provider `Configuration`) — this object carries already-resolved values; **never inline
127
+ * secrets** at a call site.
128
+ */
129
+ export interface WebexNativeSdkConfig {
130
+ /** The Webex bot/OAuth access token authorizing the bot's Meetings + Messaging calls (resolved upstream). */
131
+ AccessToken?: string;
132
+ /** The Webex site (org) the meeting belongs to, when joining cross-site (resolved upstream). */
133
+ SiteUrl?: string;
134
+ /** The bot's display name (defaults applied by the bridge when absent). */
135
+ BotDisplayName?: string;
136
+ /** Raw-audio sample rate (Hz) the bot sends/receives. */
137
+ SampleRate?: number;
138
+ /** Raw-audio channel count. */
139
+ Channels?: number;
140
+ /**
141
+ * The module specifier of the native Webex media bot addon/sidecar to load (e.g. an internal N-API addon
142
+ * package name or an absolute path to a sidecar entry). Required in production; tests inject a loader.
143
+ */
144
+ NativeModuleSpecifier?: string;
145
+ }
146
+ /**
147
+ * Coerces a `Uint8Array | ArrayBuffer` to a standalone `ArrayBuffer`, **copying** the exact byte window so
148
+ * the result never aliases a larger backing buffer (a view over a pooled buffer would otherwise expose
149
+ * neighboring bytes). Defined locally in this binding (the Webex seam carries raw `ArrayBuffer`s
150
+ * elsewhere), so it does not collide with any other export under `index.ts`'s `export *`.
151
+ */
152
+ export declare function toArrayBuffer(data: Uint8Array | ArrayBuffer): ArrayBuffer;
153
+ /** Normalizes the addon's free-form role string onto the bridge's {@link WebexParticipantRole}. */
154
+ export declare function mapNativeRole(role?: string): WebexParticipantRole;
155
+ /**
156
+ * **Pure mapping** of one native member onto the bridge's {@link WebexParticipant}. Isolated from the
157
+ * addon and from I/O so it is unit-tested directly.
158
+ */
159
+ export declare function mapNativeParticipant(p: NativeParticipant): WebexParticipant;
160
+ /**
161
+ * **Pure mapping** of one native inbound audio frame onto the bridge's diarized {@link WebexAudioFrame}.
162
+ * Copies the PCM (see {@link toArrayBuffer}) and resolves the speaker label. Isolated for direct testing.
163
+ */
164
+ export declare function mapNativeAudioFrame(frame: NativeAudioFrame): WebexAudioFrame;
165
+ /**
166
+ * The injectable loader for the native Webex media bot addon — overridable so unit tests supply a fake
167
+ * module with no addon and no network. Production leaves it as {@link defaultNativeLoader}, which lazily
168
+ * imports the configured {@link WebexNativeSdkConfig.NativeModuleSpecifier}.
169
+ */
170
+ export type NativeModuleLoader = (specifier: string) => Promise<NativeMeetingModule>;
171
+ /**
172
+ * Lazily loads the native Webex media bot addon at the given specifier (category: runtime plugin discovery
173
+ * from config — the addon path is deployment-supplied and not known at build time, so a static import is
174
+ * impossible). Throws a precise, actionable error when it can't be loaded so a misconfigured deployment
175
+ * fails loudly, not silently.
176
+ *
177
+ * VERIFY against the native Webex media bot addon: the module's default/namespace interop + that it
178
+ * exposes `createClient`.
179
+ */
180
+ export declare const defaultNativeLoader: NativeModuleLoader;
181
+ /**
182
+ * A **real, two-way** {@link IWebexMeetingSdk} over the native Webex media bot addon (raw-audio send + receive).
183
+ *
184
+ * Gives the agent both **hearing** (per-member inbound audio → diarized {@link WebexAudioFrame}s) and a
185
+ * **voice** ({@link sendAudioFrame} → the native outbound media track), plus working host controls
186
+ * (chat / mute) and roster/hand-raise events. Construct via {@link BindWebexNative} (the factory the
187
+ * bridge's `SetSdkFactory` wants), not directly, so config resolution + the lazy loader wire consistently.
188
+ */
189
+ export declare class WebexNativeMeetingSdk implements IWebexMeetingSdk {
190
+ /** Resolved config (credentials + raw-audio + the native module specifier). */
191
+ private readonly config;
192
+ /** The native-addon module loader (overridable for tests). */
193
+ private readonly loadModule;
194
+ /** The live native client once {@link join} succeeds. */
195
+ private client;
196
+ /** The inbound per-member audio handler registered by the bridge. */
197
+ private audioHandler?;
198
+ /** The member-join handler. */
199
+ private joinHandler?;
200
+ /** The member-leave handler. */
201
+ private leaveHandler?;
202
+ /** The hand-raise handler. */
203
+ private handRaiseHandler?;
204
+ /** The meeting-ended handler. */
205
+ private endedHandler?;
206
+ /**
207
+ * @param config Resolved credentials + raw-audio opts + the native module specifier.
208
+ * @param loadModule The native-addon loader (defaults to the lazy specifier loader).
209
+ */
210
+ constructor(config: WebexNativeSdkConfig, loadModule?: NativeModuleLoader);
211
+ /**
212
+ * Loads the native addon, constructs a client, wires its callbacks, and joins the meeting with the
213
+ * resolved Webex access token. Brings BOTH hearing and the agent's voice online.
214
+ *
215
+ * @param args The bridge's join args (meeting link, meeting number, bot name, auth).
216
+ * @returns The bot/meeting identifiers.
217
+ * @throws When the native module specifier is missing or the addon can't be loaded.
218
+ */
219
+ join(args: WebexJoinArgs): Promise<WebexJoinResult>;
220
+ /** Leaves the meeting and releases the native client. Tolerant of teardown errors. */
221
+ leave(): Promise<void>;
222
+ /**
223
+ * Sends one raw PCM frame as the bot's outbound audio — **the agent's real voice into the meeting**,
224
+ * via the native outbound media track. No-ops (without throwing) before {@link join} so an early model
225
+ * frame never crashes the session.
226
+ *
227
+ * @param pcm The PCM audio bytes to speak into the meeting.
228
+ */
229
+ sendAudioFrame(pcm: ArrayBuffer): void;
230
+ /** Registers the inbound per-member audio handler (the diarized hearing path). */
231
+ onAudioFrame(cb: (frame: WebexAudioFrame) => void): void;
232
+ /** Registers the member-join handler. */
233
+ onParticipantJoin(cb: (participant: WebexParticipant) => void): void;
234
+ /** Registers the member-leave handler. */
235
+ onParticipantLeave(cb: (participantId: string) => void): void;
236
+ /** Registers the native hand-raise/lower handler. */
237
+ onHandRaise(cb: (participantId: string, raised: boolean) => void): void;
238
+ /** Returns the current roster from the native client, mapped to {@link WebexParticipant}. */
239
+ getParticipants(): Promise<WebexParticipant[]>;
240
+ /** Registers the meeting-ended handler. */
241
+ onMeetingEnded(cb: () => void): void;
242
+ /**
243
+ * Posts a message to the meeting space chat via the native client. No-ops (without throwing) before
244
+ * {@link join}.
245
+ *
246
+ * @param text The chat message text.
247
+ */
248
+ postChatMessage(text: string): Promise<void>;
249
+ /**
250
+ * Mutes a member via the native host control (requires the bot be host/cohost). No-ops (without
251
+ * throwing) before {@link join}.
252
+ *
253
+ * @param participantId The member to mute.
254
+ */
255
+ muteParticipant(participantId: string): Promise<void>;
256
+ /** Wires the native client's callbacks to this adapter's handlers, mapping native shapes to the seam. */
257
+ private wireClient;
258
+ }
259
+ /**
260
+ * Builds the {@link import('./webex-sdk.js').WebexMeetingSdkFactory}-shaped factory that constructs a
261
+ * {@link WebexNativeMeetingSdk} from the bridge's per-session `Configuration`. Pass the result to
262
+ * `WebexBridge.SetSdkFactory(...)` so a deployment activates **two-way native Webex audio** without code
263
+ * changes to the driver.
264
+ *
265
+ * Reads the access token / site + raw-audio opts + the native module specifier out of the config map the
266
+ * engine passes at connect (credentials resolved upstream — **never inline secrets**).
267
+ *
268
+ * @example
269
+ * // Where bridge drivers are configured (creds already resolved from the MJ credential system):
270
+ * bridge.SetSdkFactory(BindWebexNative());
271
+ *
272
+ * @param loadModule Optional native-addon loader override (tests inject a fake; production omits it).
273
+ * @returns A factory `(config) => WebexNativeMeetingSdk`.
274
+ */
275
+ export declare function BindWebexNative(loadModule?: NativeModuleLoader): (config?: Record<string, unknown>) => WebexNativeMeetingSdk;
276
+ /**
277
+ * Extracts a {@link WebexNativeSdkConfig} from the engine's loosely-typed `Configuration` map without ever
278
+ * widening to `any`. Each field is read + type-checked individually so a malformed config yields a clean,
279
+ * partially-resolved object (and {@link WebexNativeMeetingSdk.join} then throws a precise error if the
280
+ * required specifier is absent) rather than a half-typed blob.
281
+ */
282
+ export declare function readNativeConfig(config?: Record<string, unknown>): WebexNativeSdkConfig;
283
+ //# sourceMappingURL=webex-native-sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webex-native-sdk.d.ts","sourceRoot":"","sources":["../src/webex-native-sdk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,305 @@
1
+ /**
2
+ * @fileoverview `WebexNativeMeetingSdk` — a **real, two-way** {@link IWebexMeetingSdk} binding over a
3
+ * **native Cisco Webex media bot addon** (a headless meeting-bot build with the raw-media send + receive
4
+ * entitlement). Unlike a UI/embedded-app binding, this adapter gives the agent a **voice**:
5
+ * {@link sendAudioFrame} forwards the agent's synthesized PCM to the addon's outbound audio **send** path,
6
+ * and the host controls (chat / mute) actually act on the live meeting.
7
+ *
8
+ * ## Why a native binding
9
+ * Pushing audio *into* a Webex meeting as a server-side bot requires a media-capable bot runtime — the
10
+ * Webex Meetings SDK's raw audio media tracks (or an equivalent media-bot addon) expose an outbound audio
11
+ * **send** API alongside the per-member inbound raw-audio callback, so a single binding carries
12
+ * **bidirectional** audio. This is the binding a two-way "agent talks in the meeting" deployment uses.
13
+ *
14
+ * ## The native module seam (no SDK types leak)
15
+ * The native Webex media bot is delivered as an N-API addon or a local sidecar process exposing the small
16
+ * {@link NativeMeetingModule} surface declared below. This adapter depends ONLY on that structural surface
17
+ * — none of the Webex SDK's real types leak into the package, and the module is loaded **lazily** behind an
18
+ * injectable {@link NativeModuleLoader} so `@memberjunction/ai-bridge-webex` builds and unit-tests with
19
+ * **no addon and no network**. Tests inject a fake module; production points
20
+ * {@link WebexNativeSdkConfig.NativeModuleSpecifier} at the real addon.
21
+ *
22
+ * ## Entitlements + auth (deployment responsibility)
23
+ * The native bot joins with a Webex **bot/OAuth access token** (NOT inline) and needs the media-bot
24
+ * (raw audio send + receive) entitlement on the Webex org. Credentials resolve **upstream** (MJ credential
25
+ * system / provider `Configuration`) and are **never inlined**.
26
+ *
27
+ * Every spot where the native addon's exact surface is assumed carries a `// VERIFY against the native
28
+ * Webex media bot addon` note so a live test can confirm it.
29
+ *
30
+ * @module @memberjunction/ai-bridge-webex
31
+ * @author MemberJunction.com
32
+ */
33
+ import { LogError } from '@memberjunction/core';
34
+ /**
35
+ * Coerces a `Uint8Array | ArrayBuffer` to a standalone `ArrayBuffer`, **copying** the exact byte window so
36
+ * the result never aliases a larger backing buffer (a view over a pooled buffer would otherwise expose
37
+ * neighboring bytes). Defined locally in this binding (the Webex seam carries raw `ArrayBuffer`s
38
+ * elsewhere), so it does not collide with any other export under `index.ts`'s `export *`.
39
+ */
40
+ export function toArrayBuffer(data) {
41
+ if (data instanceof ArrayBuffer) {
42
+ return data;
43
+ }
44
+ const copy = new Uint8Array(data.byteLength);
45
+ copy.set(data);
46
+ return copy.buffer;
47
+ }
48
+ /** Normalizes the addon's free-form role string onto the bridge's {@link WebexParticipantRole}. */
49
+ export function mapNativeRole(role) {
50
+ switch ((role ?? '').trim().toLowerCase()) {
51
+ case 'host':
52
+ return 'Host';
53
+ case 'cohost':
54
+ case 'co-host':
55
+ return 'Cohost';
56
+ default:
57
+ return 'Attendee';
58
+ }
59
+ }
60
+ /**
61
+ * **Pure mapping** of one native member onto the bridge's {@link WebexParticipant}. Isolated from the
62
+ * addon and from I/O so it is unit-tested directly.
63
+ */
64
+ export function mapNativeParticipant(p) {
65
+ return {
66
+ ParticipantId: String(p.participantId),
67
+ DisplayName: p.displayName,
68
+ Role: mapNativeRole(p.role),
69
+ IsSelf: p.isSelf,
70
+ };
71
+ }
72
+ /**
73
+ * **Pure mapping** of one native inbound audio frame onto the bridge's diarized {@link WebexAudioFrame}.
74
+ * Copies the PCM (see {@link toArrayBuffer}) and resolves the speaker label. Isolated for direct testing.
75
+ */
76
+ export function mapNativeAudioFrame(frame) {
77
+ return {
78
+ Pcm: toArrayBuffer(frame.data),
79
+ ParticipantId: String(frame.participantId),
80
+ DisplayName: frame.displayName,
81
+ TimestampMs: typeof frame.timestampMs === 'number' ? frame.timestampMs : Date.now(),
82
+ };
83
+ }
84
+ /** Structural guard: a value is a {@link NativeMeetingModule} when it exposes a `createClient` function. */
85
+ function isNativeModule(value) {
86
+ return (value != null &&
87
+ typeof value === 'object' &&
88
+ typeof value.createClient === 'function');
89
+ }
90
+ /** Unwraps a `{ default }` interop wrapper (CJS-with-default), returning the inner value or the input. */
91
+ function unwrapDefault(mod) {
92
+ if (mod && typeof mod === 'object' && 'default' in mod) {
93
+ const inner = mod.default;
94
+ if (isNativeModule(inner)) {
95
+ return inner;
96
+ }
97
+ }
98
+ return mod;
99
+ }
100
+ /**
101
+ * Lazily loads the native Webex media bot addon at the given specifier (category: runtime plugin discovery
102
+ * from config — the addon path is deployment-supplied and not known at build time, so a static import is
103
+ * impossible). Throws a precise, actionable error when it can't be loaded so a misconfigured deployment
104
+ * fails loudly, not silently.
105
+ *
106
+ * VERIFY against the native Webex media bot addon: the module's default/namespace interop + that it
107
+ * exposes `createClient`.
108
+ */
109
+ export const defaultNativeLoader = async (specifier) => {
110
+ try {
111
+ // `/* @vite-ignore */`: the specifier is a deployment-supplied, runtime-resolved plugin path (not a
112
+ // build-time constant), so Vite/ESBuild must not try to analyze or pre-bundle it.
113
+ const mod = await import(/* @vite-ignore */ specifier);
114
+ const resolved = unwrapDefault(mod);
115
+ if (!isNativeModule(resolved)) {
116
+ throw new Error('resolved module has no createClient() factory');
117
+ }
118
+ return resolved;
119
+ }
120
+ catch (err) {
121
+ const message = err instanceof Error ? err.message : String(err);
122
+ throw new Error(`WebexNativeMeetingSdk could not load the native Webex media bot addon at '${specifier}'. Build/install ` +
123
+ 'the native Webex media bot addon (the raw-audio send+receive build, media-bot entitled) and set ' +
124
+ `WebexNativeSdkConfig.NativeModuleSpecifier to it. Underlying error: ${message}`);
125
+ }
126
+ };
127
+ /**
128
+ * A **real, two-way** {@link IWebexMeetingSdk} over the native Webex media bot addon (raw-audio send + receive).
129
+ *
130
+ * Gives the agent both **hearing** (per-member inbound audio → diarized {@link WebexAudioFrame}s) and a
131
+ * **voice** ({@link sendAudioFrame} → the native outbound media track), plus working host controls
132
+ * (chat / mute) and roster/hand-raise events. Construct via {@link BindWebexNative} (the factory the
133
+ * bridge's `SetSdkFactory` wants), not directly, so config resolution + the lazy loader wire consistently.
134
+ */
135
+ export class WebexNativeMeetingSdk {
136
+ /**
137
+ * @param config Resolved credentials + raw-audio opts + the native module specifier.
138
+ * @param loadModule The native-addon loader (defaults to the lazy specifier loader).
139
+ */
140
+ constructor(config, loadModule = defaultNativeLoader) {
141
+ /** The live native client once {@link join} succeeds. */
142
+ this.client = null;
143
+ this.config = config;
144
+ this.loadModule = loadModule;
145
+ }
146
+ // ── IWebexMeetingSdk — lifecycle ─────────────────────────────────────────────────
147
+ /**
148
+ * Loads the native addon, constructs a client, wires its callbacks, and joins the meeting with the
149
+ * resolved Webex access token. Brings BOTH hearing and the agent's voice online.
150
+ *
151
+ * @param args The bridge's join args (meeting link, meeting number, bot name, auth).
152
+ * @returns The bot/meeting identifiers.
153
+ * @throws When the native module specifier is missing or the addon can't be loaded.
154
+ */
155
+ async join(args) {
156
+ const specifier = this.config.NativeModuleSpecifier;
157
+ if (!specifier) {
158
+ throw new Error('WebexNativeMeetingSdk.join: no NativeModuleSpecifier configured. Set it to the native Webex ' +
159
+ 'media bot addon (raw-audio send+receive build) in the session Configuration. See the README.');
160
+ }
161
+ const mod = await this.loadModule(specifier);
162
+ const client = mod.createClient({ AccessToken: this.config.AccessToken, SiteUrl: this.config.SiteUrl });
163
+ this.wireClient(client);
164
+ const result = await client.join({
165
+ meetingLink: args.MeetingLink,
166
+ meetingNumber: args.MeetingNumber,
167
+ displayName: args.BotDisplayName || this.config.BotDisplayName || 'AI Agent',
168
+ accessToken: args.AccessToken ?? this.config.AccessToken,
169
+ siteUrl: args.SiteUrl ?? this.config.SiteUrl,
170
+ sampleRate: this.config.SampleRate,
171
+ channels: this.config.Channels,
172
+ });
173
+ this.client = client;
174
+ return {
175
+ BotParticipantId: String(result.botParticipantId),
176
+ MeetingId: result.meetingId || args.MeetingNumber || args.MeetingLink,
177
+ };
178
+ }
179
+ /** Leaves the meeting and releases the native client. Tolerant of teardown errors. */
180
+ async leave() {
181
+ const client = this.client;
182
+ this.client = null;
183
+ if (client) {
184
+ try {
185
+ await client.leave();
186
+ }
187
+ catch (err) {
188
+ LogError(`[WebexNativeMeetingSdk] leave() failed: ${err instanceof Error ? err.message : String(err)}`);
189
+ }
190
+ }
191
+ }
192
+ // ── IWebexMeetingSdk — two-way audio (real) ──────────────────────────────────────
193
+ /**
194
+ * Sends one raw PCM frame as the bot's outbound audio — **the agent's real voice into the meeting**,
195
+ * via the native outbound media track. No-ops (without throwing) before {@link join} so an early model
196
+ * frame never crashes the session.
197
+ *
198
+ * @param pcm The PCM audio bytes to speak into the meeting.
199
+ */
200
+ sendAudioFrame(pcm) {
201
+ this.client?.sendAudioFrame(pcm);
202
+ }
203
+ /** Registers the inbound per-member audio handler (the diarized hearing path). */
204
+ onAudioFrame(cb) {
205
+ this.audioHandler = cb;
206
+ }
207
+ // ── IWebexMeetingSdk — roster + signals ──────────────────────────────────────────
208
+ /** Registers the member-join handler. */
209
+ onParticipantJoin(cb) {
210
+ this.joinHandler = cb;
211
+ }
212
+ /** Registers the member-leave handler. */
213
+ onParticipantLeave(cb) {
214
+ this.leaveHandler = cb;
215
+ }
216
+ /** Registers the native hand-raise/lower handler. */
217
+ onHandRaise(cb) {
218
+ this.handRaiseHandler = cb;
219
+ }
220
+ /** Returns the current roster from the native client, mapped to {@link WebexParticipant}. */
221
+ async getParticipants() {
222
+ if (!this.client) {
223
+ return [];
224
+ }
225
+ const natives = await this.client.getParticipants();
226
+ return natives.map(mapNativeParticipant);
227
+ }
228
+ /** Registers the meeting-ended handler. */
229
+ onMeetingEnded(cb) {
230
+ this.endedHandler = cb;
231
+ }
232
+ // ── IWebexMeetingSdk — host controls (real) ──────────────────────────────────────
233
+ /**
234
+ * Posts a message to the meeting space chat via the native client. No-ops (without throwing) before
235
+ * {@link join}.
236
+ *
237
+ * @param text The chat message text.
238
+ */
239
+ async postChatMessage(text) {
240
+ await this.client?.postChatMessage(text);
241
+ }
242
+ /**
243
+ * Mutes a member via the native host control (requires the bot be host/cohost). No-ops (without
244
+ * throwing) before {@link join}.
245
+ *
246
+ * @param participantId The member to mute.
247
+ */
248
+ async muteParticipant(participantId) {
249
+ await this.client?.muteParticipant(participantId);
250
+ }
251
+ // ── internals ────────────────────────────────────────────────────────────────────
252
+ /** Wires the native client's callbacks to this adapter's handlers, mapping native shapes to the seam. */
253
+ wireClient(client) {
254
+ client.onAudioFrame((frame) => this.audioHandler?.(mapNativeAudioFrame(frame)));
255
+ client.onParticipantJoin((p) => this.joinHandler?.(mapNativeParticipant(p)));
256
+ client.onParticipantLeave((id) => this.leaveHandler?.(String(id)));
257
+ client.onHandRaise((id, raised) => this.handRaiseHandler?.(String(id), raised));
258
+ client.onMeetingEnded(() => this.endedHandler?.());
259
+ }
260
+ }
261
+ /**
262
+ * Builds the {@link import('./webex-sdk.js').WebexMeetingSdkFactory}-shaped factory that constructs a
263
+ * {@link WebexNativeMeetingSdk} from the bridge's per-session `Configuration`. Pass the result to
264
+ * `WebexBridge.SetSdkFactory(...)` so a deployment activates **two-way native Webex audio** without code
265
+ * changes to the driver.
266
+ *
267
+ * Reads the access token / site + raw-audio opts + the native module specifier out of the config map the
268
+ * engine passes at connect (credentials resolved upstream — **never inline secrets**).
269
+ *
270
+ * @example
271
+ * // Where bridge drivers are configured (creds already resolved from the MJ credential system):
272
+ * bridge.SetSdkFactory(BindWebexNative());
273
+ *
274
+ * @param loadModule Optional native-addon loader override (tests inject a fake; production omits it).
275
+ * @returns A factory `(config) => WebexNativeMeetingSdk`.
276
+ */
277
+ export function BindWebexNative(loadModule = defaultNativeLoader) {
278
+ return (config) => new WebexNativeMeetingSdk(readNativeConfig(config), loadModule);
279
+ }
280
+ /**
281
+ * Extracts a {@link WebexNativeSdkConfig} from the engine's loosely-typed `Configuration` map without ever
282
+ * widening to `any`. Each field is read + type-checked individually so a malformed config yields a clean,
283
+ * partially-resolved object (and {@link WebexNativeMeetingSdk.join} then throws a precise error if the
284
+ * required specifier is absent) rather than a half-typed blob.
285
+ */
286
+ export function readNativeConfig(config) {
287
+ const cfg = config ?? {};
288
+ return {
289
+ AccessToken: readString(cfg.AccessToken),
290
+ SiteUrl: readString(cfg.SiteUrl),
291
+ BotDisplayName: readString(cfg.BotDisplayName),
292
+ SampleRate: readNumber(cfg.SampleRate),
293
+ Channels: readNumber(cfg.Channels),
294
+ NativeModuleSpecifier: readString(cfg.NativeModuleSpecifier),
295
+ };
296
+ }
297
+ /** Reads a value as a non-empty string, or `undefined`. */
298
+ function readString(value) {
299
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
300
+ }
301
+ /** Reads a value as a finite number, or `undefined`. */
302
+ function readNumber(value) {
303
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
304
+ }
305
+ //# sourceMappingURL=webex-native-sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webex-native-sdk.js","sourceRoot":"","sources":["../src/webex-native-sdk.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/ai-bridge-webex",
3
3
  "type": "module",
4
- "version": "5.41.0",
4
+ "version": "5.42.0",
5
5
  "description": "MemberJunction: Cisco Webex Realtime Bridge driver. Connects the realtime agent engine to a Webex meeting (audio in/out, diarized roster, participant mute, Webex meeting chat) via an injectable Cisco Webex Meetings SDK seam (Webex Meetings SDK / xAPI / Webex bot framework), and contributes a Meeting Controls facilitator channel.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -17,10 +17,10 @@
17
17
  "author": "MemberJunction.com",
18
18
  "license": "ISC",
19
19
  "dependencies": {
20
- "@memberjunction/core": "5.41.0",
21
- "@memberjunction/global": "5.41.0",
22
- "@memberjunction/core-entities": "5.41.0",
23
- "@memberjunction/ai-bridge-base": "5.41.0"
20
+ "@memberjunction/core": "5.42.0",
21
+ "@memberjunction/global": "5.42.0",
22
+ "@memberjunction/core-entities": "5.42.0",
23
+ "@memberjunction/ai-bridge-base": "5.42.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "24.10.11",