@sendbird/ai-agent-messenger-react 1.33.0 → 1.34.1

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
@@ -396,6 +396,12 @@ declare interface AIAgentClientHandlers {
396
396
  * `Linking.openURL` on React Native.
397
397
  */
398
398
  onClickLink?: (params: { url: string }) => void;
399
+ /**
400
+ * Called whenever the SDK emits a live metric — connection, conversation lifecycle,
401
+ * handoff, and message events. Use for real-time analytics. Implementations should be
402
+ * efficient and non-blocking, as this may be called frequently.
403
+ */
404
+ onLiveMetric?: (metric: LiveMetric) => void;
399
405
  }
400
406
 
401
407
  declare type AIAgentCommonIconName =
@@ -755,6 +761,12 @@ declare interface AIAgentInterface {
755
761
  /** @internal Initializes a conversation on the channel if needed (AI Agent channel without conversation). */
756
762
  initConversationIfNeeded: (channel: ChatSDKGroupChannel) => Promise<void>;
757
763
 
764
+ /** @internal Live metric handler; bridged from {@link AIAgentClientHandlers.onLiveMetric}. */
765
+ onLiveMetric?: LiveMetricDispatch;
766
+
767
+ /** @internal Live metric manager owned by this SDK instance. */
768
+ readonly liveMetricManager: LiveMetricManager;
769
+
758
770
  /** Disposes all resources held by this instance (WebSocket, desk auth, event handlers). */
759
771
  dispose: () => void;
760
772
 
@@ -1565,6 +1577,13 @@ export declare const ConnectionDelayLayout: {
1565
1577
  }) => null;
1566
1578
  };
1567
1579
 
1580
+ declare type ConnectionEventDetail =
1581
+ | 'connected'
1582
+ | 'reconnectStarted'
1583
+ | 'reconnectSucceeded'
1584
+ | 'reconnectFailed'
1585
+ | 'disconnected';
1586
+
1568
1587
  declare interface ContextObject {
1569
1588
  language?: string;
1570
1589
  country?: string;
@@ -2635,6 +2654,10 @@ declare type ErrorLike = StatsPayloadError;
2635
2654
  */
2636
2655
  export declare interface ExtendedMessagePayload {
2637
2656
  is_thinking: boolean;
2657
+ // Whether this message is the last bubble in a multi-bubble turn.
2658
+ // Sent as `true` only on the last bubble; omitted (undefined) on non-last bubbles.
2659
+ // Only reliable once the server stream has completed (data.stream = false).
2660
+ is_last_in_turn?: boolean;
2638
2661
  cta_button: {
2639
2662
  label: string;
2640
2663
  url: string;
@@ -2909,7 +2932,7 @@ export declare const IncomingMessageLayout: {
2909
2932
  };
2910
2933
  }) => ReactNode;
2911
2934
  SuggestedReplies: ({ extendedMessagePayload, onClickSuggestedReply, suggestedRepliesDirection, }: IncomingMessageProps) => ReactNode;
2912
- MessageTemplate: (props: Pick<IncomingMessageProps, "onGetCachedMessageTemplate" | "onRequestMessageTemplate" | "onHandleTemplateInternalAction" | "messageTemplateErrorFallback" | "messageTemplateLoadingFallback" | "extendedMessagePayload"> & {
2935
+ MessageTemplate: (props: Pick<IncomingMessageProps, "onClickLink" | "onGetCachedMessageTemplate" | "onRequestMessageTemplate" | "onHandleTemplateInternalAction" | "messageTemplateErrorFallback" | "messageTemplateLoadingFallback" | "extendedMessagePayload"> & {
2913
2936
  statsCallbacks?: Pick<IncomingMessageStatsCallbacks, "onMessageTemplateError">;
2914
2937
  }) => ReactNode;
2915
2938
  CustomMessageTemplate: (_: ({
@@ -3401,7 +3424,7 @@ export declare const IncomingMessageLayout: {
3401
3424
  };
3402
3425
  }) => ReactNode;
3403
3426
  SuggestedReplies: ({ extendedMessagePayload, onClickSuggestedReply, suggestedRepliesDirection, }: IncomingMessageProps) => ReactNode;
3404
- MessageTemplate: (props: Pick<IncomingMessageProps, "onGetCachedMessageTemplate" | "onRequestMessageTemplate" | "onHandleTemplateInternalAction" | "messageTemplateErrorFallback" | "messageTemplateLoadingFallback" | "extendedMessagePayload"> & {
3427
+ MessageTemplate: (props: Pick<IncomingMessageProps, "onClickLink" | "onGetCachedMessageTemplate" | "onRequestMessageTemplate" | "onHandleTemplateInternalAction" | "messageTemplateErrorFallback" | "messageTemplateLoadingFallback" | "extendedMessagePayload"> & {
3405
3428
  statsCallbacks?: Pick<IncomingMessageStatsCallbacks, "onMessageTemplateError">;
3406
3429
  }) => ReactNode;
3407
3430
  CustomMessageTemplate: (_: ({
@@ -3888,7 +3911,7 @@ export declare const IncomingMessageLayout: {
3888
3911
  };
3889
3912
  }) => ReactNode;
3890
3913
  SuggestedReplies: ({ extendedMessagePayload, onClickSuggestedReply, suggestedRepliesDirection, }: IncomingMessageProps) => ReactNode;
3891
- MessageTemplate: (props: Pick<IncomingMessageProps, "onGetCachedMessageTemplate" | "onRequestMessageTemplate" | "onHandleTemplateInternalAction" | "messageTemplateErrorFallback" | "messageTemplateLoadingFallback" | "extendedMessagePayload"> & {
3914
+ MessageTemplate: (props: Pick<IncomingMessageProps, "onClickLink" | "onGetCachedMessageTemplate" | "onRequestMessageTemplate" | "onHandleTemplateInternalAction" | "messageTemplateErrorFallback" | "messageTemplateLoadingFallback" | "extendedMessagePayload"> & {
3892
3915
  statsCallbacks?: Pick<IncomingMessageStatsCallbacks, "onMessageTemplateError">;
3893
3916
  }) => ReactNode;
3894
3917
  CustomMessageTemplate: (_: ({
@@ -4361,7 +4384,7 @@ export declare const IncomingMessageLayout: {
4361
4384
  }>;
4362
4385
  } & {
4363
4386
  MessageTemplate: (props: {
4364
- component: (props: Pick<IncomingMessageProps, "onGetCachedMessageTemplate" | "onRequestMessageTemplate" | "onHandleTemplateInternalAction" | "messageTemplateErrorFallback" | "messageTemplateLoadingFallback" | "extendedMessagePayload"> & {
4387
+ component: (props: Pick<IncomingMessageProps, "onClickLink" | "onGetCachedMessageTemplate" | "onRequestMessageTemplate" | "onHandleTemplateInternalAction" | "messageTemplateErrorFallback" | "messageTemplateLoadingFallback" | "extendedMessagePayload"> & {
4365
4388
  statsCallbacks?: Pick<IncomingMessageStatsCallbacks, "onMessageTemplateError">;
4366
4389
  }) => ReactNode;
4367
4390
  }) => null;
@@ -4986,6 +5009,110 @@ declare interface LayoutContextValue<Props, Components extends Record<string, Co
4986
5009
  updateComponent: <K extends keyof Components>(key: K, component: Components[K]) => void;
4987
5010
  }
4988
5011
 
5012
+ /**
5013
+ * Base contract for every metric emitted by the Live Metric module.
5014
+ * The injected `dispatch` boundary receives values of this type.
5015
+ */
5016
+ export declare interface LiveMetric {
5017
+ readonly category: MetricCategory;
5018
+ readonly metricKey: string;
5019
+ readonly data: Record<string, string>;
5020
+ }
5021
+
5022
+ /** Outward boundary: emitters dispatch built metrics through this callback. */
5023
+ declare type LiveMetricDispatch = (metric: LiveMetric) => void;
5024
+
5025
+ /**
5026
+ * Category emitter contract. Each emitter accepts its own source variant,
5027
+ * decides whether to emit based on its state cache, and dispatches metrics.
5028
+ */
5029
+ declare interface LiveMetricEmitter<S extends MetricSource = MetricSource> {
5030
+ handleEvent(source: S): void;
5031
+ }
5032
+
5033
+ /**
5034
+ * Central manager for the Live Metric module.
5035
+ *
5036
+ * Registers Sendbird SDK handlers, wraps raw callbacks into {@link MetricSource}s,
5037
+ * routes each source to its category emitter, and lets emitters dispatch public
5038
+ * metrics through the settable {@link LiveMetricManager.dispatch} sink. Outgoing message
5039
+ * and explicit conversation-initialized sources are injected via {@link LiveMetricManager.track}.
5040
+ */
5041
+ declare class LiveMetricManager {
5042
+ private readonly id = 'aiagent_livemetric';
5043
+
5044
+ /** Sink for emitted metrics. Set by the owner; metrics are dropped while unset. */
5045
+ dispatch?: LiveMetricDispatch;
5046
+
5047
+ // Project to the plain public shape: drops internal fields and resolves the
5048
+ // non-enumerable `data` getter so the payload survives JSON.stringify/spread.
5049
+ private readonly emit: LiveMetricDispatch = ({ category, metricKey, data }) => {
5050
+ this.dispatch?.({ category, metricKey, data });
5051
+ };
5052
+ private readonly emitters: Record<MetricSource['type'], LiveMetricEmitter>;
5053
+
5054
+ constructor(private readonly chatSDK: ChatSDKType) {
5055
+ const getCurrentUserId = () => this.chatSDK.currentUser?.userId;
5056
+ this.emitters = {
5057
+ connection: new ConnectionMetricEmitter(this.emit),
5058
+ conversation: new ConversationMetricEmitter(this.emit),
5059
+ handoff: new HandoffMetricEmitter(this.emit),
5060
+ incomingMessage: new IncomingMessageMetricEmitter(this.emit, getCurrentUserId),
5061
+ outgoingMessage: new OutgoingMessageMetricEmitter(this.emit, getCurrentUserId),
5062
+ };
5063
+ }
5064
+
5065
+ start(): void {
5066
+ const connectionHandler = new ConnectionHandler({
5067
+ onConnected: (userId) => this.trackConnection('connected', userId),
5068
+ onDisconnected: (userId) => this.trackConnection('disconnected', userId),
5069
+ onReconnectStarted: () => this.trackConnection('reconnectStarted'),
5070
+ onReconnectSucceeded: () => this.trackConnection('reconnectSucceeded'),
5071
+ onReconnectFailed: () => this.trackConnection('reconnectFailed'),
5072
+ });
5073
+
5074
+ const channelHandler = new GroupChannelHandler({
5075
+ onMessageReceived: (channel, message) => {
5076
+ if (!channel.isGroupChannel()) return;
5077
+ this.track({ type: 'incomingMessage', channel, message });
5078
+ },
5079
+ onChannelChanged: (channel) => {
5080
+ if (!channel.isGroupChannel()) return;
5081
+
5082
+ // Note: onChannelChanged can be triggered by various events (message received, user joined/left, etc.)
5083
+ this.track({ type: 'handoff', channel });
5084
+ this.track({ type: 'conversation', channel });
5085
+ },
5086
+ onUserJoined: (channel) => this.track({ type: 'handoff', channel }),
5087
+ onUserLeft: (channel) => this.track({ type: 'handoff', channel }),
5088
+ });
5089
+
5090
+ this.chatSDK.addConnectionHandler(this.id, connectionHandler);
5091
+ this.chatSDK.groupChannel.addGroupChannelHandler(this.id, channelHandler);
5092
+ }
5093
+
5094
+ /**
5095
+ * Routes a source to its emitter. Also the entry point for externally injected
5096
+ * sources (outgoing message status, explicit conversation-initialized).
5097
+ */
5098
+ track(source: MetricSource): void {
5099
+ try {
5100
+ this.emitters[source.type].handleEvent(source);
5101
+ } catch {
5102
+ // no-op
5103
+ }
5104
+ }
5105
+
5106
+ dispose(): void {
5107
+ this.chatSDK.removeConnectionHandler(this.id);
5108
+ this.chatSDK.groupChannel.removeGroupChannelHandler(this.id);
5109
+ }
5110
+
5111
+ private trackConnection(detail: ConnectionEventDetail, userId = this.chatSDK.currentUser?.userId): void {
5112
+ if (userId) this.track({ type: 'connection', detail, userId });
5113
+ }
5114
+ }
5115
+
4989
5116
  declare interface LocalizationContextValue extends LanguageSet {
4990
5117
  format: typeof format;
4991
5118
  defaultStringSet: StringSet;
@@ -5353,6 +5480,18 @@ declare type MessengerSettingsUpdatedParams =
5353
5480
  | { settings: MessengerSettingsResponse; source: 'server' }
5354
5481
  | { settings: CachedMessengerSettings; source: 'cache' };
5355
5482
 
5483
+ declare type MetricCategory = 'handoff' | 'conversation' | 'message' | 'connection';
5484
+
5485
+ /**
5486
+ * An event + action represented as a single value. Input to the emitters.
5487
+ */
5488
+ declare type MetricSource =
5489
+ | { type: 'connection'; detail: ConnectionEventDetail; userId: string }
5490
+ | { type: 'handoff'; channel: ChatSDKGroupChannel }
5491
+ | { type: 'conversation'; channel: ChatSDKGroupChannel }
5492
+ | { type: 'incomingMessage'; channel: ChatSDKGroupChannel; message: ChatSDKBaseMessage }
5493
+ | { type: 'outgoingMessage'; channel: ChatSDKGroupChannel; message: ChatSDKBaseMessage };
5494
+
5356
5495
  declare type MultiSelectField = {
5357
5496
  key: string;
5358
5497
  type: 'multi-select';