@memberjunction/ai-bridge-teams 0.0.1 → 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.
@@ -0,0 +1,291 @@
1
+ /**
2
+ * @fileoverview `TeamsNativeMeetingSdk` — a **real, two-way** {@link ITeamsMeetingSdk} binding over a
3
+ * native **Microsoft Teams real-time-media / calling-bot addon** (the application-hosted-media bot build).
4
+ * Unlike a receive-only or stub binding, this adapter gives the agent a **voice**: {@link sendAudioFrame}
5
+ * forwards the agent's synthesized PCM to the native client's outbound audio-socket **send** path, and the
6
+ * host controls (chat / mute) actually act.
7
+ *
8
+ * ## Why a native binding
9
+ * Pushing audio *into* a Teams meeting and observing per-participant inbound audio requires the
10
+ * application-hosted-media bot model (Azure Communication Services calling-bot + Microsoft Graph
11
+ * cloud-communications API). The native addon exposes both the outbound audio **send** path (the bot's
12
+ * voice) and the per-participant inbound raw-audio receive callback, so a single binding carries
13
+ * **bidirectional** audio. This is the binding a two-way "agent talks in the meeting" deployment uses.
14
+ *
15
+ * ## The native module seam (no SDK types leak)
16
+ * The ACS/Graph calling-bot media stack has **no single official Node real-time-media package** that fits
17
+ * this seam; deployments wrap it as a N-API addon or a local sidecar process exposing the small
18
+ * {@link NativeMeetingModule} surface declared below. This adapter depends ONLY on that structural surface
19
+ * — none of the ACS/Graph SDK's real types leak into the package, and the module is loaded **lazily**
20
+ * behind an injectable {@link NativeModuleLoader} so `@memberjunction/ai-bridge-teams` builds and
21
+ * unit-tests with **no addon and no network**. Tests inject a fake module; production points
22
+ * {@link TeamsNativeSdkConfig.NativeModuleSpecifier} at the real addon.
23
+ *
24
+ * ## Entitlements + auth (deployment responsibility)
25
+ * The native bot needs the Graph cloud-communications **application-hosted media** permissions and joins
26
+ * with an OAuth bearer / application token (the bot's identity in the tenant) — resolved **upstream** (MJ
27
+ * credential system / provider `Configuration`) and **never inlined**. See the package README's
28
+ * "Native two-way audio" section.
29
+ *
30
+ * Every spot where the native addon's exact surface is assumed carries a `// VERIFY against the native
31
+ * Teams real-time-media addon` note so a live test can confirm it.
32
+ *
33
+ * @module @memberjunction/ai-bridge-teams
34
+ * @author MemberJunction.com
35
+ */
36
+ import { ITeamsMeetingSdk, TeamsAudioFrame, TeamsJoinArgs, TeamsJoinResult, TeamsParticipant, TeamsParticipantRole } from './teams-sdk.js';
37
+ /**
38
+ * Copies arbitrary PCM input (`Uint8Array` view or standalone `ArrayBuffer`) into a fresh, exactly-sized
39
+ * `ArrayBuffer`. Copying (rather than aliasing) protects the bridge from a native buffer the addon may
40
+ * recycle for the next frame, and respects a `Uint8Array`'s byteOffset/byteLength window.
41
+ */
42
+ export declare function toPcmArrayBuffer(data: Uint8Array | ArrayBuffer): ArrayBuffer;
43
+ /** One raw per-participant audio frame the native addon surfaces (inbound hearing + diarization). */
44
+ export interface NativeAudioFrame {
45
+ /** Raw PCM bytes for this frame (`Uint8Array` view or standalone `ArrayBuffer`). */
46
+ data: Uint8Array | ArrayBuffer;
47
+ /** The Teams participant id that produced the audio (the diarization speaker label). */
48
+ participantId: string | number;
49
+ /** The participant's display name at capture time, when the addon provides it. */
50
+ displayName?: string;
51
+ /** Optional epoch-ms capture timestamp. */
52
+ timestampMs?: number;
53
+ }
54
+ /** One participant as the native addon reports it. Mapped onto {@link TeamsParticipant}. */
55
+ export interface NativeParticipant {
56
+ /** The native participant id. */
57
+ participantId: string | number;
58
+ /** The participant's display name. */
59
+ displayName?: string;
60
+ /** The participant's role as the addon reports it (`organizer` / `presenter` / `attendee`). */
61
+ role?: string;
62
+ /** Whether this participant is the bot itself. */
63
+ isSelf?: boolean;
64
+ }
65
+ /** The arguments the native addon's `join()` accepts. Mirrors {@link TeamsJoinArgs} plus raw-audio opts. */
66
+ export interface NativeJoinArgs {
67
+ /** The Teams meeting join URL (the `https://teams.microsoft.com/l/meetup-join/...` link). */
68
+ joinUrl: string;
69
+ /** The parsed meeting thread id (chat/conversation id behind the join URL), when resolved upstream. */
70
+ threadId?: string;
71
+ /** The bot's display name in the participant list. */
72
+ displayName: string;
73
+ /** The OAuth bearer / application token authorizing the bot's Graph + ACS calls (resolved upstream; never inline). */
74
+ accessToken?: string;
75
+ /** The Azure tenant id the meeting belongs to, when joining cross-tenant. */
76
+ tenantId?: string;
77
+ /** Raw-audio sample rate the bot sends/receives (Hz), when the addon needs it told. */
78
+ sampleRate?: number;
79
+ /** Raw-audio channel count, when the addon needs it told. */
80
+ channels?: number;
81
+ }
82
+ /** What the native addon's `join()` resolves to. */
83
+ export interface NativeJoinResult {
84
+ /** The bot's own participant id in the joined meeting. */
85
+ botParticipantId: string | number;
86
+ /** The Teams call id the bot joined (the durable external connection id). */
87
+ callId: string;
88
+ }
89
+ /**
90
+ * The live native-meeting client the addon hands back. The surface closely mirrors {@link ITeamsMeetingSdk}
91
+ * but in the addon's own (lower-cased) vocabulary; this adapter maps between the two. VERIFY against the
92
+ * native Teams real-time-media addon.
93
+ */
94
+ export interface NativeMeetingClient {
95
+ /** Joins the meeting (async — the native join handshake is network-bound). */
96
+ join(args: NativeJoinArgs): Promise<NativeJoinResult>;
97
+ /** Leaves the meeting and releases native resources. */
98
+ leave(): Promise<void>;
99
+ /** Sends one raw PCM frame as the bot's outbound audio (the outbound audio-socket — the agent's voice). */
100
+ sendAudioFrame(pcm: ArrayBuffer): void;
101
+ /** Registers the inbound per-participant raw-audio callback. "Latest handler wins." */
102
+ onAudioFrame(cb: (frame: NativeAudioFrame) => void): void;
103
+ /** Registers the participant-join callback. */
104
+ onParticipantJoin(cb: (participant: NativeParticipant) => void): void;
105
+ /** Registers the participant-leave callback (id of the participant that left). */
106
+ onParticipantLeave(cb: (participantId: string | number) => void): void;
107
+ /** Registers the native hand-raise/lower callback (⚠️ partial on Teams). */
108
+ onHandRaise(cb: (participantId: string | number, raised: boolean) => void): void;
109
+ /** Returns the current participant roster (including the bot). */
110
+ getParticipants(): Promise<NativeParticipant[]>;
111
+ /** Posts a message to the Teams meeting chat thread (everyone). */
112
+ postChatMessage(text: string): Promise<void>;
113
+ /** Mutes a participant (requires the bot be organizer/presenter with the relevant policy). */
114
+ muteParticipant(participantId: string | number): Promise<void>;
115
+ /** Registers the meeting-ended callback. */
116
+ onMeetingEnded(cb: () => void): void;
117
+ }
118
+ /** The native addon module surface — a factory that constructs a {@link NativeMeetingClient}. */
119
+ export interface NativeMeetingModule {
120
+ /**
121
+ * Constructs a native meeting client from resolved options (credentials already resolved upstream).
122
+ * VERIFY against the native Teams real-time-media addon (`createClient` vs a `Client` constructor).
123
+ */
124
+ createClient(options: NativeClientOptions): NativeMeetingClient;
125
+ }
126
+ /** Options passed to {@link NativeMeetingModule.createClient}. Credentials are resolved upstream. */
127
+ export interface NativeClientOptions {
128
+ /** The bot's Azure AD application (client) id (resolved upstream). */
129
+ AppId?: string;
130
+ /** The Azure tenant id the bot operates in (resolved upstream). */
131
+ TenantId?: string;
132
+ /** A pre-resolved OAuth bearer / application token, when the deployment computes it out-of-band. */
133
+ AccessToken?: string;
134
+ }
135
+ /**
136
+ * Resolved configuration for {@link TeamsNativeMeetingSdk}. Credentials resolve **upstream** (MJ credential
137
+ * system / provider `Configuration`) — this object carries already-resolved values; **never inline
138
+ * secrets** at a call site.
139
+ */
140
+ export interface TeamsNativeSdkConfig {
141
+ /** The bot's Azure AD application (client) id (resolved upstream). */
142
+ AppId?: string;
143
+ /** The Azure tenant id the bot operates in (resolved upstream). */
144
+ TenantId?: string;
145
+ /** A pre-resolved OAuth bearer / application token, when the deployment computes it out-of-band. */
146
+ AccessToken?: string;
147
+ /** The bot's display name (defaults applied by the bridge when absent). */
148
+ BotDisplayName?: string;
149
+ /** Raw-audio sample rate (Hz) the bot sends/receives. */
150
+ SampleRate?: number;
151
+ /** Raw-audio channel count. */
152
+ Channels?: number;
153
+ /**
154
+ * The module specifier of the native Teams real-time-media addon/sidecar to load (e.g. an internal
155
+ * N-API addon package name or an absolute path to a sidecar entry). Required in production; tests
156
+ * inject a loader.
157
+ */
158
+ NativeModuleSpecifier?: string;
159
+ }
160
+ /** Normalizes the addon's free-form role string onto the bridge's {@link TeamsParticipantRole}. */
161
+ export declare function mapNativeRole(role?: string): TeamsParticipantRole;
162
+ /**
163
+ * **Pure mapping** of one native participant onto the bridge's {@link TeamsParticipant}. Isolated from the
164
+ * addon and from I/O so it is unit-tested directly.
165
+ */
166
+ export declare function mapNativeParticipant(p: NativeParticipant): TeamsParticipant;
167
+ /**
168
+ * **Pure mapping** of one native inbound audio frame onto the bridge's diarized {@link TeamsAudioFrame}.
169
+ * Copies the PCM (see {@link toPcmArrayBuffer}) and resolves the speaker label. Isolated for direct testing.
170
+ */
171
+ export declare function mapNativeAudioFrame(frame: NativeAudioFrame): TeamsAudioFrame;
172
+ /**
173
+ * The injectable loader for the native Teams real-time-media addon — overridable so unit tests supply a
174
+ * fake module with no addon and no network. Production leaves it as {@link defaultNativeLoader}, which
175
+ * lazily imports the configured {@link TeamsNativeSdkConfig.NativeModuleSpecifier}.
176
+ */
177
+ export type NativeModuleLoader = (specifier: string) => Promise<NativeMeetingModule>;
178
+ /**
179
+ * Lazily loads the native Teams real-time-media addon at the given specifier (category: runtime plugin
180
+ * discovery from config — the addon path is deployment-supplied and not known at build time). Throws a
181
+ * precise, actionable error when it can't be loaded so a misconfigured deployment fails loudly, not
182
+ * silently.
183
+ *
184
+ * VERIFY against the native Teams real-time-media addon: the module's default/namespace interop + that it
185
+ * exposes `createClient`.
186
+ */
187
+ export declare const defaultNativeLoader: NativeModuleLoader;
188
+ /**
189
+ * A **real, two-way** {@link ITeamsMeetingSdk} over the native Teams real-time-media addon (outbound audio
190
+ * send + per-participant inbound receive).
191
+ *
192
+ * Gives the agent both **hearing** (per-participant inbound audio → diarized {@link TeamsAudioFrame}s) and
193
+ * a **voice** ({@link sendAudioFrame} → the native outbound audio-socket), plus working host controls
194
+ * (chat / mute) and roster/hand-raise events. Construct via {@link BindTeamsNative} (the factory the
195
+ * bridge's `SetSdkFactory` wants), not directly, so config resolution + the lazy loader wire consistently.
196
+ */
197
+ export declare class TeamsNativeMeetingSdk implements ITeamsMeetingSdk {
198
+ /** Resolved config (credentials + raw-audio + the native module specifier). */
199
+ private readonly config;
200
+ /** The native-addon module loader (overridable for tests). */
201
+ private readonly loadModule;
202
+ /** The live native client once {@link join} succeeds. */
203
+ private client;
204
+ /** The inbound per-participant audio handler registered by the bridge. */
205
+ private audioHandler?;
206
+ /** The participant-join handler. */
207
+ private joinHandler?;
208
+ /** The participant-leave handler. */
209
+ private leaveHandler?;
210
+ /** The hand-raise handler. */
211
+ private handRaiseHandler?;
212
+ /** The meeting-ended handler. */
213
+ private endedHandler?;
214
+ /**
215
+ * @param config Resolved credentials + raw-audio opts + the native module specifier.
216
+ * @param loadModule The native-addon loader (defaults to the lazy specifier loader).
217
+ */
218
+ constructor(config: TeamsNativeSdkConfig, loadModule?: NativeModuleLoader);
219
+ /**
220
+ * Loads the native addon, constructs a client, wires its callbacks, and joins the meeting with the
221
+ * resolved bot token. Brings BOTH hearing and the agent's voice online.
222
+ *
223
+ * @param args The bridge's join args (join URL, thread id, bot name, auth).
224
+ * @returns The bot/call identifiers.
225
+ * @throws When the native module specifier is missing or the addon can't be loaded.
226
+ */
227
+ join(args: TeamsJoinArgs): Promise<TeamsJoinResult>;
228
+ /** Leaves the meeting and releases the native client. Tolerant of teardown errors. */
229
+ leave(): Promise<void>;
230
+ /**
231
+ * Sends one raw PCM frame as the bot's outbound audio — **the agent's real voice into the meeting**,
232
+ * via the native outbound audio-socket. No-ops (without throwing) before {@link join} so an early
233
+ * model frame never crashes the session.
234
+ *
235
+ * @param pcm The PCM audio bytes to speak into the meeting.
236
+ */
237
+ sendAudioFrame(pcm: ArrayBuffer): void;
238
+ /** Registers the inbound per-participant audio handler (the diarized hearing path). */
239
+ onAudioFrame(cb: (frame: TeamsAudioFrame) => void): void;
240
+ /** Registers the participant-join handler. */
241
+ onParticipantJoin(cb: (participant: TeamsParticipant) => void): void;
242
+ /** Registers the participant-leave handler. */
243
+ onParticipantLeave(cb: (participantId: string) => void): void;
244
+ /** Registers the native hand-raise/lower handler (⚠️ partial on Teams; tolerant of never firing). */
245
+ onHandRaise(cb: (participantId: string, raised: boolean) => void): void;
246
+ /** Returns the current roster from the native client, mapped to {@link TeamsParticipant}. */
247
+ getParticipants(): Promise<TeamsParticipant[]>;
248
+ /** Registers the meeting-ended handler. */
249
+ onMeetingEnded(cb: () => void): void;
250
+ /**
251
+ * Posts a message to the Teams meeting chat via the native client. No-ops (without throwing) before
252
+ * {@link join}.
253
+ *
254
+ * @param text The chat message text.
255
+ */
256
+ postChatMessage(text: string): Promise<void>;
257
+ /**
258
+ * Mutes a participant via the native host control (requires the bot be organizer/presenter). No-ops
259
+ * (without throwing) before {@link join}.
260
+ *
261
+ * @param participantId The participant to mute.
262
+ */
263
+ muteParticipant(participantId: string): Promise<void>;
264
+ /** Wires the native client's callbacks to this adapter's handlers, mapping native shapes to the seam. */
265
+ private wireClient;
266
+ }
267
+ /**
268
+ * Builds the {@link import('./teams-sdk.js').TeamsMeetingSdkFactory}-shaped factory that constructs a
269
+ * {@link TeamsNativeMeetingSdk} from the bridge's per-session `Configuration`. Pass the result to
270
+ * `TeamsBridge.SetSdkFactory(...)` so a deployment activates **two-way native Teams audio** without code
271
+ * changes to the driver.
272
+ *
273
+ * Reads bot credentials / token + raw-audio opts + the native module specifier out of the config map the
274
+ * engine passes at connect (credentials resolved upstream — **never inline secrets**).
275
+ *
276
+ * @example
277
+ * // Where bridge drivers are configured (creds already resolved from the MJ credential system):
278
+ * bridge.SetSdkFactory(BindTeamsNative());
279
+ *
280
+ * @param loadModule Optional native-addon loader override (tests inject a fake; production omits it).
281
+ * @returns A factory `(config) => TeamsNativeMeetingSdk`.
282
+ */
283
+ export declare function BindTeamsNative(loadModule?: NativeModuleLoader): (config?: Record<string, unknown>) => TeamsNativeMeetingSdk;
284
+ /**
285
+ * Extracts a {@link TeamsNativeSdkConfig} from the engine's loosely-typed `Configuration` map without ever
286
+ * widening to `any`. Each field is read + type-checked individually so a malformed config yields a clean,
287
+ * partially-resolved object (and {@link TeamsNativeMeetingSdk.join} then throws a precise error if the
288
+ * required specifier is absent) rather than a half-typed blob.
289
+ */
290
+ export declare function readNativeConfig(config?: Record<string, unknown>): TeamsNativeSdkConfig;
291
+ //# sourceMappingURL=teams-native-sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams-native-sdk.d.ts","sourceRoot":"","sources":["../src/teams-native-sdk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,320 @@
1
+ /**
2
+ * @fileoverview `TeamsNativeMeetingSdk` — a **real, two-way** {@link ITeamsMeetingSdk} binding over a
3
+ * native **Microsoft Teams real-time-media / calling-bot addon** (the application-hosted-media bot build).
4
+ * Unlike a receive-only or stub binding, this adapter gives the agent a **voice**: {@link sendAudioFrame}
5
+ * forwards the agent's synthesized PCM to the native client's outbound audio-socket **send** path, and the
6
+ * host controls (chat / mute) actually act.
7
+ *
8
+ * ## Why a native binding
9
+ * Pushing audio *into* a Teams meeting and observing per-participant inbound audio requires the
10
+ * application-hosted-media bot model (Azure Communication Services calling-bot + Microsoft Graph
11
+ * cloud-communications API). The native addon exposes both the outbound audio **send** path (the bot's
12
+ * voice) and the per-participant inbound raw-audio receive callback, so a single binding carries
13
+ * **bidirectional** audio. This is the binding a two-way "agent talks in the meeting" deployment uses.
14
+ *
15
+ * ## The native module seam (no SDK types leak)
16
+ * The ACS/Graph calling-bot media stack has **no single official Node real-time-media package** that fits
17
+ * this seam; deployments wrap it as a N-API addon or a local sidecar process exposing the small
18
+ * {@link NativeMeetingModule} surface declared below. This adapter depends ONLY on that structural surface
19
+ * — none of the ACS/Graph SDK's real types leak into the package, and the module is loaded **lazily**
20
+ * behind an injectable {@link NativeModuleLoader} so `@memberjunction/ai-bridge-teams` builds and
21
+ * unit-tests with **no addon and no network**. Tests inject a fake module; production points
22
+ * {@link TeamsNativeSdkConfig.NativeModuleSpecifier} at the real addon.
23
+ *
24
+ * ## Entitlements + auth (deployment responsibility)
25
+ * The native bot needs the Graph cloud-communications **application-hosted media** permissions and joins
26
+ * with an OAuth bearer / application token (the bot's identity in the tenant) — resolved **upstream** (MJ
27
+ * credential system / provider `Configuration`) and **never inlined**. See the package README's
28
+ * "Native two-way audio" section.
29
+ *
30
+ * Every spot where the native addon's exact surface is assumed carries a `// VERIFY against the native
31
+ * Teams real-time-media addon` note so a live test can confirm it.
32
+ *
33
+ * @module @memberjunction/ai-bridge-teams
34
+ * @author MemberJunction.com
35
+ */
36
+ import { LogError } from '@memberjunction/core';
37
+ // ──────────────────────────────────────────────────────────────────────────────
38
+ // Local PCM-coercion helper. The Teams package has no existing `toArrayBuffer`
39
+ // export (unlike Zoom, which reuses its RTMS binding's), so we define a
40
+ // non-colliding local one (`toPcmArrayBuffer`) to avoid clashing under index.ts's
41
+ // `export *`.
42
+ // ──────────────────────────────────────────────────────────────────────────────
43
+ /**
44
+ * Copies arbitrary PCM input (`Uint8Array` view or standalone `ArrayBuffer`) into a fresh, exactly-sized
45
+ * `ArrayBuffer`. Copying (rather than aliasing) protects the bridge from a native buffer the addon may
46
+ * recycle for the next frame, and respects a `Uint8Array`'s byteOffset/byteLength window.
47
+ */
48
+ export function toPcmArrayBuffer(data) {
49
+ if (data instanceof ArrayBuffer) {
50
+ return data.slice(0);
51
+ }
52
+ const out = new ArrayBuffer(data.byteLength);
53
+ new Uint8Array(out).set(data);
54
+ return out;
55
+ }
56
+ /** Normalizes the addon's free-form role string onto the bridge's {@link TeamsParticipantRole}. */
57
+ export function mapNativeRole(role) {
58
+ switch ((role ?? '').trim().toLowerCase()) {
59
+ case 'organizer':
60
+ return 'Organizer';
61
+ case 'presenter':
62
+ case 'co-organizer':
63
+ case 'coorganizer':
64
+ return 'Presenter';
65
+ default:
66
+ return 'Attendee';
67
+ }
68
+ }
69
+ /**
70
+ * **Pure mapping** of one native participant onto the bridge's {@link TeamsParticipant}. Isolated from the
71
+ * addon and from I/O so it is unit-tested directly.
72
+ */
73
+ export function mapNativeParticipant(p) {
74
+ return {
75
+ ParticipantId: String(p.participantId),
76
+ DisplayName: p.displayName,
77
+ Role: mapNativeRole(p.role),
78
+ IsSelf: p.isSelf,
79
+ };
80
+ }
81
+ /**
82
+ * **Pure mapping** of one native inbound audio frame onto the bridge's diarized {@link TeamsAudioFrame}.
83
+ * Copies the PCM (see {@link toPcmArrayBuffer}) and resolves the speaker label. Isolated for direct testing.
84
+ */
85
+ export function mapNativeAudioFrame(frame) {
86
+ return {
87
+ Pcm: toPcmArrayBuffer(frame.data),
88
+ ParticipantId: String(frame.participantId),
89
+ DisplayName: frame.displayName,
90
+ TimestampMs: typeof frame.timestampMs === 'number' ? frame.timestampMs : Date.now(),
91
+ };
92
+ }
93
+ /** Structural guard: a value is a {@link NativeMeetingModule} when it exposes a `createClient` function. */
94
+ function isNativeModule(value) {
95
+ return (value != null &&
96
+ typeof value === 'object' &&
97
+ typeof value.createClient === 'function');
98
+ }
99
+ /** Unwraps a `{ default }` interop wrapper (CJS-with-default), returning the inner value or the input. */
100
+ function unwrapDefault(mod) {
101
+ if (mod && typeof mod === 'object' && 'default' in mod) {
102
+ const inner = mod.default;
103
+ if (isNativeModule(inner)) {
104
+ return inner;
105
+ }
106
+ }
107
+ return mod;
108
+ }
109
+ /**
110
+ * Lazily loads the native Teams real-time-media addon at the given specifier (category: runtime plugin
111
+ * discovery from config — the addon path is deployment-supplied and not known at build time). Throws a
112
+ * precise, actionable error when it can't be loaded so a misconfigured deployment fails loudly, not
113
+ * silently.
114
+ *
115
+ * VERIFY against the native Teams real-time-media addon: the module's default/namespace interop + that it
116
+ * exposes `createClient`.
117
+ */
118
+ export const defaultNativeLoader = async (specifier) => {
119
+ try {
120
+ const mod = await import(/* @vite-ignore */ specifier);
121
+ const resolved = unwrapDefault(mod);
122
+ if (!isNativeModule(resolved)) {
123
+ throw new Error('resolved module has no createClient() factory');
124
+ }
125
+ return resolved;
126
+ }
127
+ catch (err) {
128
+ const message = err instanceof Error ? err.message : String(err);
129
+ throw new Error(`TeamsNativeMeetingSdk could not load the native Teams real-time-media addon at '${specifier}'. ` +
130
+ 'Build/install the native Teams calling-bot addon (the ACS application-hosted-media build, with ' +
131
+ 'Graph cloud-communications permissions) and set TeamsNativeSdkConfig.NativeModuleSpecifier to it. ' +
132
+ `Underlying error: ${message}`);
133
+ }
134
+ };
135
+ /**
136
+ * A **real, two-way** {@link ITeamsMeetingSdk} over the native Teams real-time-media addon (outbound audio
137
+ * send + per-participant inbound receive).
138
+ *
139
+ * Gives the agent both **hearing** (per-participant inbound audio → diarized {@link TeamsAudioFrame}s) and
140
+ * a **voice** ({@link sendAudioFrame} → the native outbound audio-socket), plus working host controls
141
+ * (chat / mute) and roster/hand-raise events. Construct via {@link BindTeamsNative} (the factory the
142
+ * bridge's `SetSdkFactory` wants), not directly, so config resolution + the lazy loader wire consistently.
143
+ */
144
+ export class TeamsNativeMeetingSdk {
145
+ /**
146
+ * @param config Resolved credentials + raw-audio opts + the native module specifier.
147
+ * @param loadModule The native-addon loader (defaults to the lazy specifier loader).
148
+ */
149
+ constructor(config, loadModule = defaultNativeLoader) {
150
+ /** The live native client once {@link join} succeeds. */
151
+ this.client = null;
152
+ this.config = config;
153
+ this.loadModule = loadModule;
154
+ }
155
+ // ── ITeamsMeetingSdk — lifecycle ─────────────────────────────────────────────────
156
+ /**
157
+ * Loads the native addon, constructs a client, wires its callbacks, and joins the meeting with the
158
+ * resolved bot token. Brings BOTH hearing and the agent's voice online.
159
+ *
160
+ * @param args The bridge's join args (join URL, thread id, bot name, auth).
161
+ * @returns The bot/call identifiers.
162
+ * @throws When the native module specifier is missing or the addon can't be loaded.
163
+ */
164
+ async join(args) {
165
+ const specifier = this.config.NativeModuleSpecifier;
166
+ if (!specifier) {
167
+ throw new Error('TeamsNativeMeetingSdk.join: no NativeModuleSpecifier configured. Set it to the native Teams ' +
168
+ 'real-time-media bot addon (ACS application-hosted-media build) in the session Configuration. ' +
169
+ 'See the README.');
170
+ }
171
+ const mod = await this.loadModule(specifier);
172
+ const client = mod.createClient({
173
+ AppId: this.config.AppId,
174
+ TenantId: this.config.TenantId,
175
+ AccessToken: this.config.AccessToken,
176
+ });
177
+ this.wireClient(client);
178
+ const result = await client.join({
179
+ joinUrl: args.JoinUrl,
180
+ threadId: args.ThreadId,
181
+ displayName: args.BotDisplayName || this.config.BotDisplayName || 'AI Agent',
182
+ accessToken: args.AccessToken ?? this.config.AccessToken,
183
+ tenantId: args.TenantId ?? this.config.TenantId,
184
+ sampleRate: this.config.SampleRate,
185
+ channels: this.config.Channels,
186
+ });
187
+ this.client = client;
188
+ return {
189
+ BotParticipantId: String(result.botParticipantId),
190
+ CallId: result.callId || args.ThreadId || args.JoinUrl,
191
+ };
192
+ }
193
+ /** Leaves the meeting and releases the native client. Tolerant of teardown errors. */
194
+ async leave() {
195
+ const client = this.client;
196
+ this.client = null;
197
+ if (client) {
198
+ try {
199
+ await client.leave();
200
+ }
201
+ catch (err) {
202
+ LogError(`[TeamsNativeMeetingSdk] leave() failed: ${err instanceof Error ? err.message : String(err)}`);
203
+ }
204
+ }
205
+ }
206
+ // ── ITeamsMeetingSdk — two-way audio (real) ──────────────────────────────────────
207
+ /**
208
+ * Sends one raw PCM frame as the bot's outbound audio — **the agent's real voice into the meeting**,
209
+ * via the native outbound audio-socket. No-ops (without throwing) before {@link join} so an early
210
+ * model frame never crashes the session.
211
+ *
212
+ * @param pcm The PCM audio bytes to speak into the meeting.
213
+ */
214
+ sendAudioFrame(pcm) {
215
+ this.client?.sendAudioFrame(pcm);
216
+ }
217
+ /** Registers the inbound per-participant audio handler (the diarized hearing path). */
218
+ onAudioFrame(cb) {
219
+ this.audioHandler = cb;
220
+ }
221
+ // ── ITeamsMeetingSdk — roster + signals ──────────────────────────────────────────
222
+ /** Registers the participant-join handler. */
223
+ onParticipantJoin(cb) {
224
+ this.joinHandler = cb;
225
+ }
226
+ /** Registers the participant-leave handler. */
227
+ onParticipantLeave(cb) {
228
+ this.leaveHandler = cb;
229
+ }
230
+ /** Registers the native hand-raise/lower handler (⚠️ partial on Teams; tolerant of never firing). */
231
+ onHandRaise(cb) {
232
+ this.handRaiseHandler = cb;
233
+ }
234
+ /** Returns the current roster from the native client, mapped to {@link TeamsParticipant}. */
235
+ async getParticipants() {
236
+ if (!this.client) {
237
+ return [];
238
+ }
239
+ const natives = await this.client.getParticipants();
240
+ return natives.map(mapNativeParticipant);
241
+ }
242
+ /** Registers the meeting-ended handler. */
243
+ onMeetingEnded(cb) {
244
+ this.endedHandler = cb;
245
+ }
246
+ // ── ITeamsMeetingSdk — host controls (real) ──────────────────────────────────────
247
+ /**
248
+ * Posts a message to the Teams meeting chat via the native client. No-ops (without throwing) before
249
+ * {@link join}.
250
+ *
251
+ * @param text The chat message text.
252
+ */
253
+ async postChatMessage(text) {
254
+ await this.client?.postChatMessage(text);
255
+ }
256
+ /**
257
+ * Mutes a participant via the native host control (requires the bot be organizer/presenter). No-ops
258
+ * (without throwing) before {@link join}.
259
+ *
260
+ * @param participantId The participant to mute.
261
+ */
262
+ async muteParticipant(participantId) {
263
+ await this.client?.muteParticipant(participantId);
264
+ }
265
+ // ── internals ────────────────────────────────────────────────────────────────────
266
+ /** Wires the native client's callbacks to this adapter's handlers, mapping native shapes to the seam. */
267
+ wireClient(client) {
268
+ client.onAudioFrame((frame) => this.audioHandler?.(mapNativeAudioFrame(frame)));
269
+ client.onParticipantJoin((p) => this.joinHandler?.(mapNativeParticipant(p)));
270
+ client.onParticipantLeave((id) => this.leaveHandler?.(String(id)));
271
+ client.onHandRaise((id, raised) => this.handRaiseHandler?.(String(id), raised));
272
+ client.onMeetingEnded(() => this.endedHandler?.());
273
+ }
274
+ }
275
+ /**
276
+ * Builds the {@link import('./teams-sdk.js').TeamsMeetingSdkFactory}-shaped factory that constructs a
277
+ * {@link TeamsNativeMeetingSdk} from the bridge's per-session `Configuration`. Pass the result to
278
+ * `TeamsBridge.SetSdkFactory(...)` so a deployment activates **two-way native Teams audio** without code
279
+ * changes to the driver.
280
+ *
281
+ * Reads bot credentials / token + raw-audio opts + the native module specifier out of the config map the
282
+ * engine passes at connect (credentials resolved upstream — **never inline secrets**).
283
+ *
284
+ * @example
285
+ * // Where bridge drivers are configured (creds already resolved from the MJ credential system):
286
+ * bridge.SetSdkFactory(BindTeamsNative());
287
+ *
288
+ * @param loadModule Optional native-addon loader override (tests inject a fake; production omits it).
289
+ * @returns A factory `(config) => TeamsNativeMeetingSdk`.
290
+ */
291
+ export function BindTeamsNative(loadModule = defaultNativeLoader) {
292
+ return (config) => new TeamsNativeMeetingSdk(readNativeConfig(config), loadModule);
293
+ }
294
+ /**
295
+ * Extracts a {@link TeamsNativeSdkConfig} from the engine's loosely-typed `Configuration` map without ever
296
+ * widening to `any`. Each field is read + type-checked individually so a malformed config yields a clean,
297
+ * partially-resolved object (and {@link TeamsNativeMeetingSdk.join} then throws a precise error if the
298
+ * required specifier is absent) rather than a half-typed blob.
299
+ */
300
+ export function readNativeConfig(config) {
301
+ const cfg = config ?? {};
302
+ return {
303
+ AppId: readString(cfg.AppId),
304
+ TenantId: readString(cfg.TenantId),
305
+ AccessToken: readString(cfg.AccessToken),
306
+ BotDisplayName: readString(cfg.BotDisplayName),
307
+ SampleRate: readNumber(cfg.SampleRate),
308
+ Channels: readNumber(cfg.Channels),
309
+ NativeModuleSpecifier: readString(cfg.NativeModuleSpecifier),
310
+ };
311
+ }
312
+ /** Reads a value as a non-empty string, or `undefined`. */
313
+ function readString(value) {
314
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
315
+ }
316
+ /** Reads a value as a finite number, or `undefined`. */
317
+ function readNumber(value) {
318
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
319
+ }
320
+ //# sourceMappingURL=teams-native-sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams-native-sdk.js","sourceRoot":"","sources":["../src/teams-native-sdk.ts"],"names":[],"mappings":"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"}