@sendbird/ai-agent-messenger-react-native 1.6.0 → 1.7.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
@@ -51,16 +51,15 @@ declare interface ActionbookInfo {
51
51
  */
52
52
  declare interface ActiveChannel {
53
53
  url: string;
54
+ /**
55
+ * @deprecated Use `conversationStatus` instead. This field always returns 'open' or 'closed' for backward compatibility.
56
+ * */
57
+ status: 'open' | 'closed';
54
58
  /**
55
59
  * @description Conversation status of the active channel.
56
60
  * If the conversation is not started, this will be undefined.
57
61
  * */
58
62
  conversationStatus?: ConversationStatus;
59
- /**
60
- * @description Status of the active channel. This will be 'closed' if the conversation is closed. otherwise, 'open'.
61
- * @deprecated Please use `conversationStatus` field instead.
62
- * */
63
- status: 'open' | 'closed';
64
63
  }
65
64
 
66
65
  declare interface ActiveChannelUpdatedParams {
@@ -98,6 +97,94 @@ declare interface AgentMessageTemplateInfo {
98
97
  };
99
98
  }
100
99
 
100
+ declare abstract class AIAgentBaseStats {
101
+ protected timers: Map<string, TimerData> = new Map();
102
+ protected committed: boolean = false;
103
+ protected commitCallback: StatsAppendCallback | null = null;
104
+ protected conversationId: number | null = null;
105
+ protected channelUrl: string | null = null;
106
+ protected errorCode: number | null = null;
107
+ protected errorDescription: string | null = null;
108
+ protected extraData: Record<string, unknown> = {};
109
+
110
+ setCommitCallback(callback: StatsAppendCallback): this {
111
+ this.commitCallback = callback;
112
+ return this;
113
+ }
114
+
115
+ setConversationId(id: number): this {
116
+ this.conversationId = id;
117
+ return this;
118
+ }
119
+
120
+ setChannelUrl(url: string): this {
121
+ this.setExtra('channel_url', url);
122
+ return this;
123
+ }
124
+
125
+ setError(code: number | undefined, description: string): this {
126
+ this.errorCode = code ?? DEFAULT_ERROR_CODE;
127
+ this.errorDescription = description;
128
+ return this;
129
+ }
130
+
131
+ setExtra(key: string, value: unknown): this {
132
+ if (value !== null && value !== undefined) {
133
+ this.extraData[key] = value;
134
+ }
135
+ return this;
136
+ }
137
+
138
+ startTimer(key: string): this {
139
+ if (this.committed) return this;
140
+
141
+ const existing = this.timers.get(key);
142
+ if (existing?.startTime !== null && existing?.startTime !== undefined) return this;
143
+
144
+ this.timers.set(key, { startTime: Date.now(), endTime: null });
145
+ return this;
146
+ }
147
+
148
+ stopTimer(key: string): this {
149
+ if (this.committed) return this;
150
+
151
+ const existing = this.timers.get(key);
152
+ if (!existing || existing.startTime === null || existing.endTime !== null) return this;
153
+
154
+ this.timers.set(key, { ...existing, endTime: Date.now() });
155
+ return this;
156
+ }
157
+
158
+ protected getDuration(key: string): number | null {
159
+ const timer = this.timers.get(key);
160
+ if (!timer || timer.startTime === null || timer.endTime === null) return null;
161
+ return timer.endTime - timer.startTime;
162
+ }
163
+
164
+ isCommitted(): boolean {
165
+ return this.committed;
166
+ }
167
+
168
+ protected abstract buildPayload(): AIAgentStatPayload | null;
169
+ protected abstract getMetricKey(): string;
170
+
171
+ commit(): boolean {
172
+ if (this.committed || !this.commitCallback) {
173
+ return false;
174
+ }
175
+
176
+ const payload = this.buildPayload();
177
+
178
+ if (!payload) {
179
+ return false;
180
+ }
181
+
182
+ this.committed = true;
183
+ const result = this.commitCallback(AI_AGENT_STAT_TYPE, payload);
184
+ return result;
185
+ }
186
+ }
187
+
101
188
  declare interface AIAgentCache {
102
189
  template: MessageTemplateCache;
103
190
  messenger: MessengerSessionCache;
@@ -152,6 +239,11 @@ declare interface AIAgentConfig {
152
239
  * */
153
240
  fileEnabled?: boolean;
154
241
  };
242
+ /**
243
+ * (React only) Whether to play an alert sound when a message is received from the AI agent while the browser is not focused.
244
+ * @default false
245
+ * */
246
+ messageAlertSoundEnabled?: boolean;
155
247
  /**
156
248
  * (React only) File viewer configuration.
157
249
  * */
@@ -284,6 +376,7 @@ declare interface AIAgentMessengerSessionContextValue {
284
376
  userSession: null | UserSession;
285
377
  aiAgentInfo: null | AIAgentInfo;
286
378
  launcherInfo: null | LauncherInfo;
379
+ presentMethod: PresentMethod;
287
380
 
288
381
  connectionError?: Error;
289
382
 
@@ -298,6 +391,12 @@ declare interface AIAgentMessengerSessionContextValue {
298
391
  deauthenticate: () => Promise<void>;
299
392
 
300
393
  createAttachmentRules: (params: { channel?: GroupChannel; uploadSizeLimit?: number }) => AttachmentRules;
394
+ /**
395
+ * @internal
396
+ */
397
+ statsTrackers: {
398
+ initialRender: ConversationInitialRenderStatsTracker;
399
+ };
301
400
  }
302
401
 
303
402
  declare interface AIAgentMessengerSessionRef {
@@ -389,6 +488,10 @@ queryParams?: AIAgentQueryParams;
389
488
  * @description AIAgent global default config.
390
489
  * */
391
490
  config?: AIAgentConfig;
491
+ /**
492
+ * @internal Used for conversation initial render stat tracking.
493
+ */
494
+ presentMethod?: PresentMethod;
392
495
  } & {
393
496
  children?: ReactNode | undefined;
394
497
  } & RefAttributes<AIAgentMessengerSessionRef>>;
@@ -404,6 +507,15 @@ declare interface AIAgentSessionHandler extends SessionHandler {
404
507
  onExternalAuthTokenExpired?: (data: ExternalAuthTokenExpiredData) => void;
405
508
  }
406
509
 
510
+ declare interface AIAgentStatPayload {
511
+ key: string;
512
+ value?: string;
513
+ conversation_id?: number;
514
+ error_code?: number;
515
+ error_description?: string;
516
+ extra?: Record<string, unknown>;
517
+ }
518
+
407
519
  /**
408
520
  * Common string set interface shared between react and react-native packages
409
521
  * These are the base strings that both packages use
@@ -710,6 +822,146 @@ declare interface ConversationHeaderTemplateProps {
710
822
  titleAlign?: 'start' | 'center' | 'end';
711
823
  }
712
824
 
825
+ declare class ConversationInitialRenderStats extends AIAgentBaseStats {
826
+ private presentMethod: PresentMethod;
827
+
828
+ constructor(presentMethod: PresentMethod) {
829
+ super();
830
+ this.presentMethod = presentMethod;
831
+ }
832
+
833
+ protected getMetricKey(): string {
834
+ return METRIC_KEY_CONVERSATION_INITIAL_RENDER;
835
+ }
836
+
837
+ protected buildPayload(): AIAgentStatPayload | null {
838
+ const authDuration = this.getDuration(DurationKey.AUTH);
839
+ const getChannelDuration = this.getDuration(DurationKey.GET_CHANNEL);
840
+ const getMessagesDuration = this.getDuration(DurationKey.GET_MESSAGES);
841
+ const totalDurationFromCache = this.getDuration(DurationKey.TOTAL_DURATION_FROM_CACHE);
842
+ const totalDuration = this.getDuration(DurationKey.TOTAL_DURATION);
843
+
844
+ const extra: Record<string, unknown> = {
845
+ ...this.extraData,
846
+ present_method: this.presentMethod,
847
+ ...(authDuration !== null && { auth_duration: authDuration }),
848
+ ...(getChannelDuration !== null && { get_channel_duration: getChannelDuration }),
849
+ ...(getMessagesDuration !== null && { get_messages_duration: getMessagesDuration }),
850
+ ...(totalDurationFromCache !== null && { total_duration_from_cache: totalDurationFromCache }),
851
+ };
852
+
853
+ const payload: AIAgentStatPayload = {
854
+ key: this.getMetricKey(),
855
+ // Use "0" when totalDuration is null (error case)
856
+ value: totalDuration !== null ? String(totalDuration) : '0',
857
+ };
858
+
859
+ if (this.conversationId !== null) payload.conversation_id = this.conversationId;
860
+ if (this.errorCode !== null) payload.error_code = this.errorCode;
861
+ if (this.errorDescription !== null) payload.error_description = this.errorDescription;
862
+ if (Object.keys(extra).length > 0) payload.extra = extra;
863
+
864
+ return payload;
865
+ }
866
+ }
867
+
868
+ /**
869
+ * Manages ConversationInitialRenderStats lifecycle and encapsulates tracking logic.
870
+ */
871
+ declare class ConversationInitialRenderStatsTracker {
872
+ private readonly commitCallback: StatsAppendCallback;
873
+ private readonly presentMethod: PresentMethod;
874
+ private stats: ConversationInitialRenderStats | null = null;
875
+
876
+ constructor(commitCallback: StatsAppendCallback, presentMethod: PresentMethod) {
877
+ this.commitCallback = commitCallback;
878
+ this.presentMethod = presentMethod;
879
+ }
880
+
881
+ private getOrCreateStats(): ConversationInitialRenderStats {
882
+ if (!this.stats || this.stats.isCommitted()) {
883
+ this.stats = new ConversationInitialRenderStats(this.presentMethod);
884
+ this.stats.setCommitCallback(this.commitCallback);
885
+ }
886
+ return this.stats;
887
+ }
888
+
889
+ onAuthStart(): void {
890
+ this.getOrCreateStats()
891
+ .startTimer(DurationKey.TOTAL_DURATION)
892
+ .startTimer(DurationKey.TOTAL_DURATION_FROM_CACHE)
893
+ .startTimer(DurationKey.AUTH);
894
+ }
895
+
896
+ onAuthComplete(): void {
897
+ this.stats?.stopTimer(DurationKey.AUTH);
898
+ }
899
+
900
+ onAuthError(error: Error): void {
901
+ if (this.stats && !this.stats.isCommitted()) {
902
+ this.stats
903
+ .stopTimer(DurationKey.AUTH)
904
+ .setError(error instanceof SendbirdError ? error.code : undefined, error.message)
905
+ .stopTimer(DurationKey.TOTAL_DURATION)
906
+ .commit();
907
+ }
908
+ }
909
+
910
+ onGetChannelStart(): void {
911
+ this.stats?.startTimer(DurationKey.GET_CHANNEL);
912
+ }
913
+
914
+ onGetChannelComplete(conversationId?: number): void {
915
+ if (this.stats) {
916
+ this.stats.stopTimer(DurationKey.GET_CHANNEL);
917
+ if (conversationId !== undefined) {
918
+ this.stats.setConversationId(conversationId);
919
+ }
920
+ this.stats.startTimer(DurationKey.GET_MESSAGES);
921
+ }
922
+ }
923
+
924
+ onGetChannelError(error: Error): void {
925
+ if (this.stats && !this.stats.isCommitted()) {
926
+ this.stats
927
+ .stopTimer(DurationKey.GET_CHANNEL)
928
+ .setError(error instanceof SendbirdError ? error.code : undefined, error.message)
929
+ .stopTimer(DurationKey.TOTAL_DURATION)
930
+ .commit();
931
+ }
932
+ }
933
+
934
+ onCacheResult(error: Error | null): void {
935
+ if (this.stats && !this.stats.isCommitted()) {
936
+ if (error) {
937
+ this.stats.setError(error instanceof SendbirdError ? error.code : undefined, error.message);
938
+ }
939
+ this.stats.stopTimer(DurationKey.TOTAL_DURATION_FROM_CACHE);
940
+ }
941
+ }
942
+
943
+ onApiResult(error: Error | null): void {
944
+ if (this.stats && !this.stats.isCommitted()) {
945
+ if (error) {
946
+ this.stats.setError(error instanceof SendbirdError ? error.code : undefined, error.message);
947
+ }
948
+ this.stats.stopTimer(DurationKey.TOTAL_DURATION).stopTimer(DurationKey.GET_MESSAGES).commit();
949
+ }
950
+ }
951
+
952
+ setChannelUrl(url: string): void {
953
+ this.stats?.setChannelUrl(url);
954
+ }
955
+
956
+ cleanup(): void {
957
+ this.stats?.commit();
958
+ }
959
+
960
+ clear(): void {
961
+ this.stats = null;
962
+ }
963
+ }
964
+
713
965
  export declare const ConversationLayout: {
714
966
  (props: PropsWithChildren): ReactNode;
715
967
  defaults: {
@@ -2464,6 +2716,8 @@ declare type PositionHorizontal = 'start' | 'end';
2464
2716
 
2465
2717
  declare type PositionVertical = 'top' | 'bottom';
2466
2718
 
2719
+ declare type PresentMethod = 'launcher_toggle' | 'direct_present';
2720
+
2467
2721
  declare type RNPermissionsModule = typeof RNPermissions;
2468
2722
 
2469
2723
  export declare type ScrollToBottomButtonProps = {
@@ -2487,6 +2741,8 @@ declare type SingleSelectField = {
2487
2741
  layout: 'default';
2488
2742
  };
2489
2743
 
2744
+ declare type StatsAppendCallback = (type: string, data: AIAgentStatPayload) => boolean;
2745
+
2490
2746
  /**
2491
2747
  * Strings type for React Native components
2492
2748
  * Uses nested structure with lowercase keys
@@ -2575,6 +2831,11 @@ declare type TextField = {
2575
2831
  };
2576
2832
  };
2577
2833
 
2834
+ declare interface TimerData {
2835
+ startTime: number | null;
2836
+ endTime: number | null;
2837
+ }
2838
+
2578
2839
  declare interface TypographyShape {
2579
2840
  h1: TypographyVariant;
2580
2841
  h2: TypographyVariant;