twilio-agent-connect 1.0.2 → 1.0.3

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
@@ -1174,6 +1174,41 @@ interface InitiateConversationResult {
1174
1174
  interface InitiateVoiceConversationResult {
1175
1175
  callSid: string;
1176
1176
  }
1177
+ /**
1178
+ * Webhook payload structure from Twilio Conversation Orchestrator.
1179
+ * This structure is used by all channels (messaging and voice) for webhook events.
1180
+ */
1181
+ interface ConversationWebhookPayload {
1182
+ eventType: string;
1183
+ timestamp?: string;
1184
+ data?: {
1185
+ id?: string;
1186
+ conversationId?: string;
1187
+ accountId?: string;
1188
+ serviceId?: string;
1189
+ status?: string;
1190
+ participantType?: string;
1191
+ profileId?: string;
1192
+ channelId?: string;
1193
+ author?: {
1194
+ address?: string;
1195
+ channel?: string;
1196
+ participantId?: string;
1197
+ };
1198
+ content?: {
1199
+ type?: string;
1200
+ text?: string;
1201
+ };
1202
+ recipients?: Array<{
1203
+ address?: string;
1204
+ channel?: string;
1205
+ participantId?: string;
1206
+ deliveryStatus?: string;
1207
+ }>;
1208
+ [key: string]: unknown;
1209
+ };
1210
+ [key: string]: unknown;
1211
+ }
1177
1212
 
1178
1213
  /**
1179
1214
  * ConversationRelay API Types
@@ -2340,12 +2375,23 @@ interface BaseChannelOptions {
2340
2375
  * - "always": Memory is automatically retrieved for every inbound message and available in `onMessageReady` callback.
2341
2376
  */
2342
2377
  memoryMode?: MemoryMode;
2378
+ /**
2379
+ * Maximum number of idempotency tokens to track for webhook deduplication.
2380
+ * Default is 10000. Must be a positive integer.
2381
+ */
2382
+ dedupCapacity?: number;
2343
2383
  }
2344
2384
  /**
2345
2385
  * Abstract base class for all channel implementations
2346
2386
  *
2347
- * Provides common functionality for conversation lifecycle management,
2348
- * session tracking, and shared utilities across different channel types.
2387
+ * Provides common functionality for:
2388
+ * - Conversation lifecycle management and session tracking
2389
+ * - Webhook deduplication using Twilio idempotency tokens
2390
+ * - Event filtering to prevent cross-channel fanout
2391
+ * - Memory retrieval integration
2392
+ * - Error handling and logging
2393
+ *
2394
+ * Subclasses must implement channel-specific webhook processing and message sending.
2349
2395
  */
2350
2396
  declare abstract class BaseChannel {
2351
2397
  protected readonly tac: TAC;
@@ -2355,6 +2401,8 @@ declare abstract class BaseChannel {
2355
2401
  protected readonly activeConversations: Map<ConversationId, ConversationSession>;
2356
2402
  protected readonly callbacks: BaseChannelEvents;
2357
2403
  protected readonly memoryMode: MemoryMode;
2404
+ private readonly processedWebhookTokens;
2405
+ private readonly maxTrackedTokens;
2358
2406
  constructor(tac: TAC, options?: BaseChannelOptions);
2359
2407
  /**
2360
2408
  * Get the channel type (implemented by subclasses)
@@ -2396,6 +2444,27 @@ declare abstract class BaseChannel {
2396
2444
  * Handle errors with proper context
2397
2445
  */
2398
2446
  protected handleError(error: Error, context?: Record<string, unknown>): void;
2447
+ /**
2448
+ * Check if a webhook has already been processed using Twilio's idempotency token.
2449
+ * Uses a sliding window with fixed capacity to track tokens (FIFO eviction).
2450
+ *
2451
+ * This is intentionally a single synchronous check-and-record to prevent race conditions
2452
+ * where a duplicate arrives while the first request is still awaiting async work.
2453
+ */
2454
+ protected isDuplicateWebhook(idempotencyToken: string): boolean;
2455
+ /**
2456
+ * Remove an idempotency token from the deduplication cache.
2457
+ * Used when webhook processing fails and retries should not be blocked.
2458
+ */
2459
+ protected removeWebhookToken(idempotencyToken: string): void;
2460
+ /**
2461
+ * Self-filtering: check if webhook event belongs to this channel.
2462
+ *
2463
+ * - COMMUNICATION_CREATED: require author.channel matches this channel type
2464
+ * - CONVERSATION_UPDATED: only process if conversation is tracked locally
2465
+ * - Other events: pass through
2466
+ */
2467
+ protected isEventForThisChannel(webhookData: ConversationWebhookPayload): boolean;
2399
2468
  /**
2400
2469
  * Validate webhook payload (override in subclasses for specific validation)
2401
2470
  */
@@ -2576,49 +2645,10 @@ declare class TAC {
2576
2645
  }
2577
2646
 
2578
2647
  /**
2579
- * Messaging webhook event types from Twilio Conversations Service
2580
- * Supports the v2 format for SMS and Chat channels
2581
- */
2582
- interface MessagingWebhookPayload {
2583
- eventType: string;
2584
- timestamp?: string;
2585
- data?: {
2586
- id?: string;
2587
- conversationId?: string;
2588
- accountId?: string;
2589
- serviceId?: string;
2590
- status?: string;
2591
- participantType?: string;
2592
- profileId?: string;
2593
- channelId?: string;
2594
- author?: {
2595
- address?: string;
2596
- channel?: string;
2597
- participantId?: string;
2598
- };
2599
- content?: {
2600
- type?: string;
2601
- text?: string;
2602
- };
2603
- recipients?: Array<{
2604
- address?: string;
2605
- channel?: string;
2606
- participantId?: string;
2607
- deliveryStatus?: string;
2608
- }>;
2609
- [key: string]: unknown;
2610
- };
2611
- [key: string]: unknown;
2612
- }
2613
- /**
2614
- * Messaging channel configuration options
2648
+ * Messaging channel configuration options.
2649
+ * Alias for BaseChannelOptions that can be extended by specific channel implementations.
2615
2650
  */
2616
- interface MessagingChannelConfig {
2617
- /** Maximum number of idempotency tokens to track for deduplication (default: 10,000) */
2618
- dedupCapacity?: number;
2619
- /** Memory retrieval mode. Default is "never". Set to "always" to retrieve memory for every message. */
2620
- memoryMode?: MemoryMode;
2621
- }
2651
+ type MessagingChannelConfig = BaseChannelOptions;
2622
2652
  /**
2623
2653
  * Messaging channel event callbacks extending base callbacks
2624
2654
  */
@@ -2651,21 +2681,12 @@ interface MessagingChannelEvents extends BaseChannelEvents {
2651
2681
  declare abstract class MessagingChannel extends BaseChannel {
2652
2682
  protected readonly conversationClient: ConversationClient;
2653
2683
  protected readonly messagingCallbacks: MessagingChannelEvents;
2654
- private readonly processedTokens;
2655
- private readonly maxTrackedTokens;
2656
2684
  /**
2657
2685
  * Controls whether reconciliation promotes an UNKNOWN customer-side
2658
2686
  * participant to CUSTOMER. Subclasses override to opt out (e.g. chat).
2659
2687
  */
2660
2688
  protected reconcileCustomerType: boolean;
2661
2689
  constructor(tac: TAC, config?: MessagingChannelConfig);
2662
- /**
2663
- * Check if a webhook has already been processed, and if not, record the token immediately.
2664
- * This is intentionally a single synchronous check-and-record to prevent race conditions
2665
- * where a duplicate arrives while the first request is still awaiting async work.
2666
- * Uses a sliding window with FIFO eviction at capacity.
2667
- */
2668
- private isDuplicateWebhook;
2669
2690
  /**
2670
2691
  * Fast-path check: is the author address this channel's default agent address?
2671
2692
  * (e.g., config.phoneNumber for SMS, agentAddress for Chat)
@@ -2691,12 +2712,7 @@ declare abstract class MessagingChannel extends BaseChannel {
2691
2712
  */
2692
2713
  on(event: string, callback: (...args: any[]) => void): void;
2693
2714
  /**
2694
- * Check if this webhook event belongs to this channel.
2695
- * Returns false if the event is clearly for a different channel type.
2696
- */
2697
- private isEventForThisChannel;
2698
- /**
2699
- * Process messaging channel webhook from Twilio Conversations Service
2715
+ * Process messaging channel webhook from Conversation Orchestrator
2700
2716
  */
2701
2717
  processWebhook(payload: unknown, idempotencyToken?: string): Promise<void>;
2702
2718
  /**
@@ -3682,4 +3698,4 @@ declare class TACServer {
3682
3698
  stop(): Promise<void>;
3683
3699
  }
3684
3700
 
3685
- export { type ActionChannelSettings, ActionChannelSettingsSchema, type ActionParticipantRef, ActionParticipantRefSchema, type ActionResponse, ActionResponseSchema, type ActionTextContent, ActionTextContentSchema, type AdapterOptions, type AuthorInfo, AuthorInfoSchema, BaseChannel, type BaseChannelEvents, BaseClient, type BuiltInToolName, BuiltInTools, type CaptureRule, CaptureRuleSchema, type ChannelSettings, ChannelSettingsSchema, type ChannelType, ChannelTypeSchema, ChatChannel, type ChatChannelConfig, type CintelParticipant, CintelParticipantSchema, type Communication, type CommunicationContent, CommunicationContentSchema, type CommunicationParticipant, CommunicationParticipantSchema, CommunicationSchema, type ConversationAddress, ConversationAddressSchema, ConversationClient, type ConversationConfiguration, ConversationConfigurationSchema, type ConversationEndedCallback, type ConversationGroupingType, ConversationGroupingTypeSchema, type ConversationId, type ConversationIntelligenceConfig, ConversationIntelligenceConfigSchema, type ConversationParticipant, ConversationParticipantSchema, type ConversationRelayAttributes, ConversationRelayAttributesSchema, type ConversationRelayCallbackPayload, ConversationRelayCallbackPayloadSchema, type ConversationRelayConfig, ConversationRelayConfigSchema, type ConversationResponse, ConversationResponseSchema, type ConversationSession, ConversationSessionSchema, type ConversationSummaryItem, ConversationSummaryItemSchema, type CreateConversationSummariesResponse, CreateConversationSummariesResponseSchema, type CreateObservationResponse, CreateObservationResponseSchema, type CustomParameters, CustomParametersSchema, EMPTY_MEMORY_RESPONSE, EnvironmentVariables, type ExecutionDetails, ExecutionDetailsSchema, type HandoffPayload, HandoffPayloadSchema, type HandoffResult, type InitiateChatConversationOptions, type InitiateConversationResult, type InitiateMessagingConversationOptions, InitiateMessagingConversationOptionsSchema, type InitiateVoiceConversationOptions, InitiateVoiceConversationOptionsSchema, type InitiateVoiceConversationResult, type IntelligenceConfiguration, IntelligenceConfigurationSchema, type InterruptCallback, type InterruptMessage, InterruptMessageSchema, type JSONSchema, JSONSchemaSchema, type KnowledgeBase, KnowledgeBaseSchema, type KnowledgeBaseStatus, KnowledgeBaseStatusSchema, type KnowledgeChunkResult, KnowledgeChunkResultSchema, KnowledgeClient, type KnowledgeSearchResponse, KnowledgeSearchResponseSchema, type LanguageAttributes, LanguageAttributesSchema, type ListCommunicationsResponse, ListCommunicationsResponseSchema, type ListConversationsResponse, ListConversationsResponseSchema, type ListParticipantsResponse, ListParticipantsResponseSchema, type Logger, type MemoryChannelType, MemoryChannelTypeSchema, MemoryClient, type MemoryCommunication, type MemoryCommunicationContent, MemoryCommunicationContentSchema, MemoryCommunicationSchema, type MemoryDeliveryStatus, MemoryDeliveryStatusSchema, type MemoryMode, MemoryModeSchema, type MemoryParticipant, MemoryParticipantSchema, type MemoryParticipantType, MemoryParticipantTypeSchema, MemoryPromptBuilder, type MemoryRetrievalRequest, MemoryRetrievalRequestSchema, type MemoryRetrievalResponse, MemoryRetrievalResponseSchema, type MessageDirection, MessageDirectionSchema, type MessageReadyCallback, MessagingChannel, type MessagingChannelConfig, type MessagingChannelEvents, type MessagingWebhookPayload, type ObservationInfo, ObservationInfoSchema, type OpenAITool, OpenAIToolSchema, type Operator, type OperatorProcessingResult, OperatorProcessingResultSchema, type OperatorResult, type OperatorResultEvent, OperatorResultEventSchema, OperatorResultProcessor, OperatorResultSchema, OperatorSchema, type ParticipantAddress, ParticipantAddressSchema, type ParticipantAddressType, ParticipantAddressTypeSchema, type ParticipantId, type PendingHandoffData, PendingHandoffDataSchema, type Profile, type ProfileId, type ProfileLookupResponse, ProfileLookupResponseSchema, type ProfileResponse, ProfileResponseSchema, type PromptMessage, PromptMessageSchema, RCSChannel, SMSChannel, type SendMessageActionPayload, SendMessageActionPayloadSchema, type SendMessageActionRequest, SendMessageActionRequestSchema, type SessionInfo, SessionInfoSchema, type SessionMessage, SessionMessageSchema, type SetupMessage, SetupMessageSchema, type StatusCallback, StatusCallbackSchema, type StatusTimeouts, StatusTimeoutsSchema, type StreamTask, type SummaryInfo, SummaryInfoSchema, TAC, type TACChannelType, TACChannelTypeSchema, type TACCommunication, type TACCommunicationAuthor, TACCommunicationAuthorSchema, type TACCommunicationContent, TACCommunicationContentSchema, TACCommunicationSchema, TACConfig, type TACConfigData, TACConfigSchema, type TACDeliveryStatus, TACDeliveryStatusSchema, TACMemoryResponse, type TACOptions, type TACParticipantType, TACParticipantTypeSchema, TACServer, type TACServerConfig, TACTool, type TextTokenMessage, TextTokenMessageSchema, type ToolContext, type ToolExecutionResult, ToolExecutionResultSchema, type ToolFunction, type Transcription, TranscriptionSchema, type TranscriptionWord, TranscriptionWordSchema, type TwilioMemoryConfig, TwilioMemoryConfigSchema, VoiceChannel, type VoiceChannelEvents, type WebSocketMessage, WebSocketMessageSchema, WhatsAppChannel, type _SDKDriftGuards, buildHandoffPayload, createKnowledgeSearchTool, createKnowledgeSearchToolAsync, createKnowledgeTools, createLogger, createMemoryRetrievalTool, createMemoryTools, createMessagingTools, createSendMessageTool, createStudioHandoffTool, defineTool, isConversationId, isParticipantId, isProfileId, maskAddress, maskEmail, maskPhone, postStudioHandoff, scrubObject, scrubPii, studioExecutionsUrl, studioVoiceHandoffUrl };
3701
+ export { type ActionChannelSettings, ActionChannelSettingsSchema, type ActionParticipantRef, ActionParticipantRefSchema, type ActionResponse, ActionResponseSchema, type ActionTextContent, ActionTextContentSchema, type AdapterOptions, type AuthorInfo, AuthorInfoSchema, BaseChannel, type BaseChannelEvents, type BaseChannelOptions, BaseClient, type BuiltInToolName, BuiltInTools, type CaptureRule, CaptureRuleSchema, type ChannelSettings, ChannelSettingsSchema, type ChannelType, ChannelTypeSchema, ChatChannel, type ChatChannelConfig, type CintelParticipant, CintelParticipantSchema, type Communication, type CommunicationContent, CommunicationContentSchema, type CommunicationParticipant, CommunicationParticipantSchema, CommunicationSchema, type ConversationAddress, ConversationAddressSchema, ConversationClient, type ConversationConfiguration, ConversationConfigurationSchema, type ConversationEndedCallback, type ConversationGroupingType, ConversationGroupingTypeSchema, type ConversationId, type ConversationIntelligenceConfig, ConversationIntelligenceConfigSchema, type ConversationParticipant, ConversationParticipantSchema, type ConversationRelayAttributes, ConversationRelayAttributesSchema, type ConversationRelayCallbackPayload, ConversationRelayCallbackPayloadSchema, type ConversationRelayConfig, ConversationRelayConfigSchema, type ConversationResponse, ConversationResponseSchema, type ConversationSession, ConversationSessionSchema, type ConversationSummaryItem, ConversationSummaryItemSchema, type ConversationWebhookPayload, type CreateConversationSummariesResponse, CreateConversationSummariesResponseSchema, type CreateObservationResponse, CreateObservationResponseSchema, type CustomParameters, CustomParametersSchema, EMPTY_MEMORY_RESPONSE, EnvironmentVariables, type ExecutionDetails, ExecutionDetailsSchema, type HandoffPayload, HandoffPayloadSchema, type HandoffResult, type InitiateChatConversationOptions, type InitiateConversationResult, type InitiateMessagingConversationOptions, InitiateMessagingConversationOptionsSchema, type InitiateVoiceConversationOptions, InitiateVoiceConversationOptionsSchema, type InitiateVoiceConversationResult, type IntelligenceConfiguration, IntelligenceConfigurationSchema, type InterruptCallback, type InterruptMessage, InterruptMessageSchema, type JSONSchema, JSONSchemaSchema, type KnowledgeBase, KnowledgeBaseSchema, type KnowledgeBaseStatus, KnowledgeBaseStatusSchema, type KnowledgeChunkResult, KnowledgeChunkResultSchema, KnowledgeClient, type KnowledgeSearchResponse, KnowledgeSearchResponseSchema, type LanguageAttributes, LanguageAttributesSchema, type ListCommunicationsResponse, ListCommunicationsResponseSchema, type ListConversationsResponse, ListConversationsResponseSchema, type ListParticipantsResponse, ListParticipantsResponseSchema, type Logger, type MemoryChannelType, MemoryChannelTypeSchema, MemoryClient, type MemoryCommunication, type MemoryCommunicationContent, MemoryCommunicationContentSchema, MemoryCommunicationSchema, type MemoryDeliveryStatus, MemoryDeliveryStatusSchema, type MemoryMode, MemoryModeSchema, type MemoryParticipant, MemoryParticipantSchema, type MemoryParticipantType, MemoryParticipantTypeSchema, MemoryPromptBuilder, type MemoryRetrievalRequest, MemoryRetrievalRequestSchema, type MemoryRetrievalResponse, MemoryRetrievalResponseSchema, type MessageDirection, MessageDirectionSchema, type MessageReadyCallback, MessagingChannel, type MessagingChannelConfig, type MessagingChannelEvents, type ObservationInfo, ObservationInfoSchema, type OpenAITool, OpenAIToolSchema, type Operator, type OperatorProcessingResult, OperatorProcessingResultSchema, type OperatorResult, type OperatorResultEvent, OperatorResultEventSchema, OperatorResultProcessor, OperatorResultSchema, OperatorSchema, type ParticipantAddress, ParticipantAddressSchema, type ParticipantAddressType, ParticipantAddressTypeSchema, type ParticipantId, type PendingHandoffData, PendingHandoffDataSchema, type Profile, type ProfileId, type ProfileLookupResponse, ProfileLookupResponseSchema, type ProfileResponse, ProfileResponseSchema, type PromptMessage, PromptMessageSchema, RCSChannel, SMSChannel, type SendMessageActionPayload, SendMessageActionPayloadSchema, type SendMessageActionRequest, SendMessageActionRequestSchema, type SessionInfo, SessionInfoSchema, type SessionMessage, SessionMessageSchema, type SetupMessage, SetupMessageSchema, type StatusCallback, StatusCallbackSchema, type StatusTimeouts, StatusTimeoutsSchema, type StreamTask, type SummaryInfo, SummaryInfoSchema, TAC, type TACChannelType, TACChannelTypeSchema, type TACCommunication, type TACCommunicationAuthor, TACCommunicationAuthorSchema, type TACCommunicationContent, TACCommunicationContentSchema, TACCommunicationSchema, TACConfig, type TACConfigData, TACConfigSchema, type TACDeliveryStatus, TACDeliveryStatusSchema, TACMemoryResponse, type TACOptions, type TACParticipantType, TACParticipantTypeSchema, TACServer, type TACServerConfig, TACTool, type TextTokenMessage, TextTokenMessageSchema, type ToolContext, type ToolExecutionResult, ToolExecutionResultSchema, type ToolFunction, type Transcription, TranscriptionSchema, type TranscriptionWord, TranscriptionWordSchema, type TwilioMemoryConfig, TwilioMemoryConfigSchema, VoiceChannel, type VoiceChannelEvents, type WebSocketMessage, WebSocketMessageSchema, WhatsAppChannel, type _SDKDriftGuards, buildHandoffPayload, createKnowledgeSearchTool, createKnowledgeSearchToolAsync, createKnowledgeTools, createLogger, createMemoryRetrievalTool, createMemoryTools, createMessagingTools, createSendMessageTool, createStudioHandoffTool, defineTool, isConversationId, isParticipantId, isProfileId, maskAddress, maskEmail, maskPhone, postStudioHandoff, scrubObject, scrubPii, studioExecutionsUrl, studioVoiceHandoffUrl };
package/dist/index.js CHANGED
@@ -1150,7 +1150,7 @@ function createLogger(options) {
1150
1150
 
1151
1151
  // package.json
1152
1152
  var package_default = {
1153
- version: "1.0.2"};
1153
+ version: "1.0.3"};
1154
1154
  function buildUserAgent() {
1155
1155
  return `twilio-agent-connect-typescript/${package_default.version}`;
1156
1156
  }
@@ -2643,6 +2643,8 @@ var BaseChannel = class {
2643
2643
  activeConversations;
2644
2644
  callbacks;
2645
2645
  memoryMode;
2646
+ processedWebhookTokens;
2647
+ maxTrackedTokens;
2646
2648
  constructor(tac, options) {
2647
2649
  this.tac = tac;
2648
2650
  this.config = tac.getConfig();
@@ -2657,6 +2659,12 @@ var BaseChannel = class {
2657
2659
  throw new Error(`Invalid memoryMode: "${modeToValidate}". Must be "always" or "never".`);
2658
2660
  }
2659
2661
  this.memoryMode = parseResult.data;
2662
+ const capacity = options?.dedupCapacity ?? 1e4;
2663
+ if (capacity < 1 || !Number.isInteger(capacity)) {
2664
+ throw new Error("dedupCapacity must be a positive integer");
2665
+ }
2666
+ this.maxTrackedTokens = capacity;
2667
+ this.processedWebhookTokens = /* @__PURE__ */ new Set();
2660
2668
  }
2661
2669
  /**
2662
2670
  * Register event callbacks
@@ -2774,6 +2782,55 @@ var BaseChannel = class {
2774
2782
  }
2775
2783
  }
2776
2784
  }
2785
+ /**
2786
+ * Check if a webhook has already been processed using Twilio's idempotency token.
2787
+ * Uses a sliding window with fixed capacity to track tokens (FIFO eviction).
2788
+ *
2789
+ * This is intentionally a single synchronous check-and-record to prevent race conditions
2790
+ * where a duplicate arrives while the first request is still awaiting async work.
2791
+ */
2792
+ isDuplicateWebhook(idempotencyToken) {
2793
+ if (this.processedWebhookTokens.has(idempotencyToken)) {
2794
+ return true;
2795
+ }
2796
+ if (this.processedWebhookTokens.size >= this.maxTrackedTokens) {
2797
+ const oldest = this.processedWebhookTokens.values().next().value;
2798
+ this.processedWebhookTokens.delete(oldest);
2799
+ }
2800
+ this.processedWebhookTokens.add(idempotencyToken);
2801
+ return false;
2802
+ }
2803
+ /**
2804
+ * Remove an idempotency token from the deduplication cache.
2805
+ * Used when webhook processing fails and retries should not be blocked.
2806
+ */
2807
+ removeWebhookToken(idempotencyToken) {
2808
+ this.processedWebhookTokens.delete(idempotencyToken);
2809
+ }
2810
+ /**
2811
+ * Self-filtering: check if webhook event belongs to this channel.
2812
+ *
2813
+ * - COMMUNICATION_CREATED: require author.channel matches this channel type
2814
+ * - CONVERSATION_UPDATED: only process if conversation is tracked locally
2815
+ * - Other events: pass through
2816
+ */
2817
+ isEventForThisChannel(webhookData) {
2818
+ const eventType = webhookData.eventType;
2819
+ const authorChannel = webhookData.data?.author?.channel;
2820
+ if (eventType === "COMMUNICATION_CREATED") {
2821
+ if (!authorChannel) {
2822
+ return false;
2823
+ }
2824
+ return authorChannel === this.channelType.toUpperCase();
2825
+ }
2826
+ if (eventType === "CONVERSATION_UPDATED") {
2827
+ const conversationId = this.extractConversationId(webhookData);
2828
+ if (conversationId && !this.activeConversations.has(conversationId)) {
2829
+ return false;
2830
+ }
2831
+ }
2832
+ return true;
2833
+ }
2777
2834
  /**
2778
2835
  * Validate webhook payload (override in subclasses for specific validation)
2779
2836
  */
@@ -2823,12 +2880,9 @@ var CHANNEL_IDENTITY_TYPES = {
2823
2880
  RCS: "rcs",
2824
2881
  WHATSAPP: "whatsapp"
2825
2882
  };
2826
- var DEFAULT_DEDUP_CAPACITY = 1e4;
2827
2883
  var MessagingChannel = class extends BaseChannel {
2828
2884
  conversationClient;
2829
2885
  messagingCallbacks;
2830
- processedTokens = /* @__PURE__ */ new Set();
2831
- maxTrackedTokens;
2832
2886
  /**
2833
2887
  * Controls whether reconciliation promotes an UNKNOWN customer-side
2834
2888
  * participant to CUSTOMER. Subclasses override to opt out (e.g. chat).
@@ -2843,28 +2897,6 @@ var MessagingChannel = class extends BaseChannel {
2843
2897
  }
2844
2898
  this.conversationClient = tac.getConversationClient();
2845
2899
  this.messagingCallbacks = {};
2846
- const capacity = config?.dedupCapacity ?? DEFAULT_DEDUP_CAPACITY;
2847
- if (capacity < 1 || !Number.isInteger(capacity)) {
2848
- throw new Error("dedupCapacity must be a positive integer");
2849
- }
2850
- this.maxTrackedTokens = capacity;
2851
- }
2852
- /**
2853
- * Check if a webhook has already been processed, and if not, record the token immediately.
2854
- * This is intentionally a single synchronous check-and-record to prevent race conditions
2855
- * where a duplicate arrives while the first request is still awaiting async work.
2856
- * Uses a sliding window with FIFO eviction at capacity.
2857
- */
2858
- isDuplicateWebhook(idempotencyToken) {
2859
- if (this.processedTokens.has(idempotencyToken)) {
2860
- return true;
2861
- }
2862
- if (this.processedTokens.size >= this.maxTrackedTokens) {
2863
- const oldest = this.processedTokens.values().next().value;
2864
- this.processedTokens.delete(oldest);
2865
- }
2866
- this.processedTokens.add(idempotencyToken);
2867
- return false;
2868
2900
  }
2869
2901
  /**
2870
2902
  * Check if a message is from the bot itself (2-tier).
@@ -2910,28 +2942,7 @@ var MessagingChannel = class extends BaseChannel {
2910
2942
  }
2911
2943
  }
2912
2944
  /**
2913
- * Check if this webhook event belongs to this channel.
2914
- * Returns false if the event is clearly for a different channel type.
2915
- */
2916
- isEventForThisChannel(webhookData) {
2917
- const eventType = webhookData.eventType;
2918
- const authorChannel = webhookData.data?.author?.channel;
2919
- if (eventType === "COMMUNICATION_CREATED") {
2920
- if (!authorChannel) {
2921
- return false;
2922
- }
2923
- return authorChannel === this.channelType.toUpperCase();
2924
- }
2925
- if (eventType === "CONVERSATION_UPDATED") {
2926
- const conversationId = this.extractConversationId(webhookData);
2927
- if (conversationId && !this.isConversationActive(conversationId)) {
2928
- return false;
2929
- }
2930
- }
2931
- return true;
2932
- }
2933
- /**
2934
- * Process messaging channel webhook from Twilio Conversations Service
2945
+ * Process messaging channel webhook from Conversation Orchestrator
2935
2946
  */
2936
2947
  async processWebhook(payload, idempotencyToken) {
2937
2948
  this.logger.debug({ operation: "webhook_processing" }, "Processing webhook");
@@ -2993,7 +3004,7 @@ var MessagingChannel = class extends BaseChannel {
2993
3004
  this.logger.debug({ event_type: eventType }, "Webhook processing completed");
2994
3005
  } catch (error) {
2995
3006
  if (idempotencyToken) {
2996
- this.processedTokens.delete(idempotencyToken);
3007
+ this.removeWebhookToken(idempotencyToken);
2997
3008
  }
2998
3009
  this.logger.error(
2999
3010
  { err: error, operation: "webhook_processing" },