@adatechnology/meta-whatsapp-module 0.2.0-rc.29 → 0.2.0-rc.30

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.cts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
2
2
  import { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
3
3
  import * as _adatechnology_meta_whatsapp_contracts from '@adatechnology/meta-whatsapp-contracts';
4
- import { TranscriptionMode, SessionState, SessionMode, ConversationSummary, MessageDirection, MessageSender, MessageStatus, CacheInterface, FlowGraphData, FlowGraphSummary, LiveFlowPosition, WhatsAppSettings, RealtimeNotifierInterface, ChannelAdapterInterface, ObjectStorageInterface, FlowActionKind, FlowActionHandler, ConversationSession, MetaWhatsAppHooks, SubjectResolverInterface, CatalogPort } from '@adatechnology/meta-whatsapp-contracts';
4
+ import { TranscriptionMode, SessionState, SessionMode, ConversationSummary, MessageDirection, MessageSender, MessageStatus, CacheInterface, FlowGraphData, FlowGraphSummary, LiveFlowPosition, WhatsAppSettings, RealtimeNotifierInterface, ChannelAdapterInterface, ObjectStorageInterface, FlowActionKind, FlowActionHandler, ConversationSession, WhatsAppMessage, WhatsAppStatus, MetaWhatsAppHooks, SubjectResolverInterface, CatalogPort } from '@adatechnology/meta-whatsapp-contracts';
5
5
  export { PREVIEW_MEDIA_ID_PREFIX, TranscriptionMode, resolvePreviewUploadId, toPreviewMediaId } from '@adatechnology/meta-whatsapp-contracts';
6
6
  import { WhatsAppMessageProvider } from '@adatechnology/meta-whatsapp-provider';
7
- export { WEBHOOK_NONCE_TTL_SECONDS } from '@adatechnology/meta-graph-core';
7
+ export { WEBHOOK_CLAIM_TTL_SECONDS, WEBHOOK_NONCE_TTL_SECONDS } from '@adatechnology/meta-graph-core';
8
8
 
9
9
  type MetaWhatsAppDatabase = PgDatabase<PgQueryResultHKT, any, any>;
10
10
  type DrizzleMigrateFunction = (db: never, config: {
@@ -2316,6 +2316,15 @@ declare class FlowMediaRepository {
2316
2316
 
2317
2317
  interface NonceStoreInterface {
2318
2318
  setIfAbsent(key: string, ttlSeconds: number): Promise<boolean>;
2319
+ /**
2320
+ * Estende a chave já reivindicada para a janela cheia. Um SET simples, sem NX.
2321
+ *
2322
+ * Opcional só por compatibilidade com hosts que ainda não o implementam: sem ele o claim curto
2323
+ * expira, e a Meta pode reentregar uma entrega já processada — trabalho repetido, que a dedupe
2324
+ * por `waMessageId` ainda segura antes de virar efeito visível ao cliente. Implementar é o
2325
+ * caminho correto.
2326
+ */
2327
+ confirm?(key: string, ttlSeconds: number): Promise<void>;
2319
2328
  }
2320
2329
 
2321
2330
  declare function verifyWebhookChallenge(params: {
@@ -2329,11 +2338,101 @@ declare function verifyWebhookSignature(params: {
2329
2338
  signatureHeader: string | null | undefined;
2330
2339
  appSecret: string;
2331
2340
  }): void;
2341
+ /**
2342
+ * Reivindica a entrega por pouco tempo. O par obrigatório é `confirmWebhookDelivery` ao fim do
2343
+ * processamento — ver `WEBHOOK_CLAIM_TTL_SECONDS` para o porquê dos dois tempos.
2344
+ */
2332
2345
  declare function claimWebhookDelivery(params: {
2333
2346
  nonceStore: NonceStoreInterface;
2334
2347
  signatureHeader: string;
2335
2348
  ttlSeconds?: number;
2336
2349
  }): Promise<boolean>;
2350
+ /** Só depois da entrega processada por inteiro: é isto que fecha a janela anti-replay. */
2351
+ declare function confirmWebhookDelivery(params: {
2352
+ nonceStore: NonceStoreInterface;
2353
+ signatureHeader: string;
2354
+ ttlSeconds?: number;
2355
+ }): Promise<void>;
2356
+
2357
+ /**
2358
+ * Copyright (c) 2026 Ada Technology. MIT License.
2359
+ *
2360
+ * Separa a entrega do webhook em duas metades: o que precisa acontecer antes de responder à Meta
2361
+ * (persistir a mensagem, que é escrita local e rápida) e o que pode esperar alguns milissegundos
2362
+ * (os hooks do host, que costumam falar com rede — motor de fluxo, IA, integrações).
2363
+ *
2364
+ * A segunda metade rodava dentro da requisição do webhook. Um deploy no meio de uma conversa
2365
+ * matava a chamada em voo, e o cliente ficava sem resposta sem que nada registrasse a perda. Com
2366
+ * uma fila no host, derrubar o processo vira atraso: o job espera e é retomado.
2367
+ */
2368
+
2369
+ type InboundMediaDescriptor = {
2370
+ sourceMediaId: string;
2371
+ mimeType: string;
2372
+ filename?: string;
2373
+ };
2374
+ type InboundMessageEffectsJob = {
2375
+ kind: 'message';
2376
+ companyId: string;
2377
+ message: WhatsAppMessage;
2378
+ savedMessageId: string;
2379
+ media?: InboundMediaDescriptor;
2380
+ receivedAt: number;
2381
+ };
2382
+ type InboundStatusEffectsJob = {
2383
+ kind: 'status';
2384
+ companyId: string;
2385
+ status: WhatsAppStatus;
2386
+ whatsappNumber: string;
2387
+ receivedAt: number;
2388
+ };
2389
+ type InboundDispatchJob = InboundMessageEffectsJob | InboundStatusEffectsJob;
2390
+ /**
2391
+ * Porta de fila. O módulo não escolhe a tecnologia — BullMQ, SQS, o que o host já tiver — mas
2392
+ * exige dela duas garantias, e sem as duas o padrão não entrega o que promete:
2393
+ *
2394
+ * 1. **Durabilidade**: o job sobrevive à morte do processo que o enfileirou. Uma fila em memória
2395
+ * reintroduz exatamente a perda que este desenho existe para evitar.
2396
+ * 2. **Retentativa com backoff**: o destino dos hooks (n8n, IA, API de terceiro) também cai em
2397
+ * deploy. Sem retry, o job falha uma vez e a conversa morre do mesmo jeito.
2398
+ *
2399
+ * `jobId` é estável e derivado da mensagem: reentrega da Meta e re-enfileiramento produzem o mesmo
2400
+ * id, e a fila descarta o segundo em vez de rodar o efeito duas vezes.
2401
+ */
2402
+ interface InboundDispatchQueueInterface {
2403
+ enqueue(job: InboundDispatchJob, options: {
2404
+ jobId: string;
2405
+ }): Promise<void>;
2406
+ }
2407
+ declare function buildInboundJobId(job: InboundDispatchJob): string;
2408
+ declare function toSessionContract(row: SessionRow): ConversationSession;
2409
+ /**
2410
+ * Os efeitos de host de uma entrega já persistida. Vive fora do `ReceiveWebhookUseCase` porque
2411
+ * roda nos dois lados: inline, quando o host não configurou fila, e dentro do worker, quando
2412
+ * configurou. Um só corpo de regra para os dois caminhos — duas cópias divergiriam.
2413
+ */
2414
+ declare class InboundEffectsDispatcher {
2415
+ private readonly params;
2416
+ constructor(params: {
2417
+ sessionRepository: SessionRepository;
2418
+ hooks?: MetaWhatsAppHooks;
2419
+ realtime?: RealtimeNotifierInterface;
2420
+ });
2421
+ run(job: InboundDispatchJob): Promise<void>;
2422
+ runMessageEffects(job: InboundMessageEffectsJob): Promise<void>;
2423
+ runStatusEffects(job: InboundStatusEffectsJob): Promise<void>;
2424
+ }
2425
+ /**
2426
+ * Ponto de entrada do worker do host: recebe o job que a fila devolveu e roda os efeitos.
2427
+ *
2428
+ * Deixe a exceção propagar. É ela que faz a fila contar a tentativa e reagendar com backoff;
2429
+ * capturar aqui para logar transforma falha recuperável em mensagem perdida em silêncio.
2430
+ */
2431
+ declare class ProcessInboundDispatchUseCase {
2432
+ private readonly dispatcher;
2433
+ constructor(dispatcher: InboundEffectsDispatcher);
2434
+ execute(job: InboundDispatchJob): Promise<void>;
2435
+ }
2337
2436
 
2338
2437
  type ReceiveWebhookParams = {
2339
2438
  companyId: string;
@@ -2358,10 +2457,17 @@ declare class ReceiveWebhookUseCase {
2358
2457
  startState: SessionState;
2359
2458
  hooks?: MetaWhatsAppHooks;
2360
2459
  realtime?: RealtimeNotifierInterface;
2460
+ /**
2461
+ * Sem fila o módulo se comporta como sempre: os hooks rodam dentro da requisição do webhook.
2462
+ * Configurá-la é o que faz a conversa sobreviver a um deploy no meio do atendimento.
2463
+ */
2464
+ inboundQueue?: InboundDispatchQueueInterface;
2361
2465
  });
2466
+ private readonly dispatcher;
2362
2467
  execute(input: ReceiveWebhookParams): Promise<ReceiveWebhookResult>;
2363
2468
  private handleMessage;
2364
2469
  private handleStatus;
2470
+ private dispatch;
2365
2471
  }
2366
2472
 
2367
2473
  /**
@@ -2895,4 +3001,4 @@ type CreateSendProductListActionParams = {
2895
3001
  */
2896
3002
  declare function createSendProductListAction(params: CreateSendProductListActionParams): FlowActionHandler;
2897
3003
 
2898
- export { type AttachFlowMediaParams, type AudioTranscriber, type CompanyDocumentView, type CompanyDocumentsPage, type ConversationDocumentView, type ConversationDocumentsPage, type CreateFlowGraphParams, CreateFlowGraphUseCase, type CreateMetaWhatsAppModuleParams, type CreateSendMediaActionParams, type CreateSendProductListActionParams, DEFAULT_FLOW_GRAPH_CACHE_TTL_SECONDS, type DeleteConversationParams, type DeleteConversationResult, DeleteConversationUseCase, DeleteFlowGraphUseCase, DocumentRepository, type DocumentRow, type DrizzleMigrateFunction, type ExportConversationParams, type ExportConversationResult, ExportConversationUseCase, FlowGraphCache, FlowGraphRepository, type FlowGraphRow, FlowInterpreter, type FlowMediaLocation, FlowMediaRepository, type FlowMediaRow, type FlowMediaTranscriptLogger, type FlowRunResult, type FlowStepInput, type FlowStepResult, GetFlowGraphUseCase, GetLiveFlowPositionsUseCase, type IngestInboundMediaParams, type IngestInboundMediaResult, IngestInboundMediaUseCase, type IngestTranscriptionOptions, type InsertMessageParams, InvalidFlowGraphError, type LinkDocumentParams, type ListCompanyDocumentsParams, ListCompanyDocumentsUseCase, type ListConversationDocumentsParams, ListConversationDocumentsUseCase, type ListConversationsFilters, type ListConversationsParams, ListConversationsUseCase, type ListDocumentsParams, type ListDocumentsResult, ListFlowGraphsUseCase, type ListMessagesParams, ListMessagesUseCase, type LogMessageParams, LogMessageUseCase, META_WHATSAPP_MIGRATIONS_TABLE, type MessageModerator, MessageRepository, type MessageRow, type MetaWhatsAppDatabase, type MetaWhatsAppModule, type MetaWhatsAppModuleConfig, type MetaWhatsAppModuleFeatures, type MetaWhatsAppModuleProviders, type MetaWhatsAppTranscriptionConfig, type NewDocumentRow, type NewFlowGraphRow, type NewFlowMediaRow, type NewMessageRow, type NewSessionRow, type NewSettingsRow, type NonceStoreInterface, OptimisticLockError, PRODUCT_LIST_LIMIT, type PreviewMediaSupport, type PurgeExpiredDocumentsParams, type PurgeExpiredDocumentsResult, PurgeExpiredDocumentsUseCase, type RealtimeRelay, type ReceiveWebhookParams, type ReceiveWebhookResult, ReceiveWebhookUseCase, type ReleaseConversationParams, ReleaseConversationUseCase, type ListMessagesParams$1 as RepositoryListMessagesParams, type ResolveTranscriptionPolicyDependencies, type RunMetaWhatsAppMigrationsParams, type SaveFlowGraphParams, SaveFlowGraphUseCase, type SaveTranscriptionByWaMessageIdParams, type SaveTranscriptionParams, type SendMediaParams, SendMessageUseCase, type SendTemplateParams, type SendTextParams, SessionRepository, type SessionRow, SettingsRepository, type SettingsRow, SseHub, type SseListener, type StorePreviewMediaParams, type StorePreviewMediaResult, StorePreviewMediaUseCase, TRANSCRIPTION_MODE, TRANSCRIPTION_STATUS, type TakeoverConversationParams, TakeoverConversationUseCase, type TicketStoreInterface, type TranscribeAudioDependencies, type TranscribeAudioParams, type TranscribeAudioResult, TranscribeAudioUseCase, type TranscriptionPolicy, type TranscriptionPolicyDefaults, type TranscriptionPolicyResolver, type TranscriptionStatus, type UpdateFlowMediaParams, WhatsAppChannelAdapter, claimWebhookDelivery, createMetaWhatsAppModule, createSendMediaAction, createSendProductListAction, createTranscriptionPolicyResolver, documents, extractMediaDescriptor, flowGraphs, flowMedia, isAudioMimeType, isRetriableTranscriptionError, isUnsupportedTranscriptionError, issueSseTicket, messages, metaWhatsAppMigrationsFolder, metaWhatsAppSchema, redeemSseTicket, resolveFailureStatus, runMetaWhatsAppMigrations, sessions, settings, transcriptionRetryAfterSeconds, verifyWebhookChallenge, verifyWebhookSignature };
3004
+ export { type AttachFlowMediaParams, type AudioTranscriber, type CompanyDocumentView, type CompanyDocumentsPage, type ConversationDocumentView, type ConversationDocumentsPage, type CreateFlowGraphParams, CreateFlowGraphUseCase, type CreateMetaWhatsAppModuleParams, type CreateSendMediaActionParams, type CreateSendProductListActionParams, DEFAULT_FLOW_GRAPH_CACHE_TTL_SECONDS, type DeleteConversationParams, type DeleteConversationResult, DeleteConversationUseCase, DeleteFlowGraphUseCase, DocumentRepository, type DocumentRow, type DrizzleMigrateFunction, type ExportConversationParams, type ExportConversationResult, ExportConversationUseCase, FlowGraphCache, FlowGraphRepository, type FlowGraphRow, FlowInterpreter, type FlowMediaLocation, FlowMediaRepository, type FlowMediaRow, type FlowMediaTranscriptLogger, type FlowRunResult, type FlowStepInput, type FlowStepResult, GetFlowGraphUseCase, GetLiveFlowPositionsUseCase, type InboundDispatchJob, type InboundDispatchQueueInterface, InboundEffectsDispatcher, type InboundMediaDescriptor, type InboundMessageEffectsJob, type InboundStatusEffectsJob, type IngestInboundMediaParams, type IngestInboundMediaResult, IngestInboundMediaUseCase, type IngestTranscriptionOptions, type InsertMessageParams, InvalidFlowGraphError, type LinkDocumentParams, type ListCompanyDocumentsParams, ListCompanyDocumentsUseCase, type ListConversationDocumentsParams, ListConversationDocumentsUseCase, type ListConversationsFilters, type ListConversationsParams, ListConversationsUseCase, type ListDocumentsParams, type ListDocumentsResult, ListFlowGraphsUseCase, type ListMessagesParams, ListMessagesUseCase, type LogMessageParams, LogMessageUseCase, META_WHATSAPP_MIGRATIONS_TABLE, type MessageModerator, MessageRepository, type MessageRow, type MetaWhatsAppDatabase, type MetaWhatsAppModule, type MetaWhatsAppModuleConfig, type MetaWhatsAppModuleFeatures, type MetaWhatsAppModuleProviders, type MetaWhatsAppTranscriptionConfig, type NewDocumentRow, type NewFlowGraphRow, type NewFlowMediaRow, type NewMessageRow, type NewSessionRow, type NewSettingsRow, type NonceStoreInterface, OptimisticLockError, PRODUCT_LIST_LIMIT, type PreviewMediaSupport, ProcessInboundDispatchUseCase, type PurgeExpiredDocumentsParams, type PurgeExpiredDocumentsResult, PurgeExpiredDocumentsUseCase, type RealtimeRelay, type ReceiveWebhookParams, type ReceiveWebhookResult, ReceiveWebhookUseCase, type ReleaseConversationParams, ReleaseConversationUseCase, type ListMessagesParams$1 as RepositoryListMessagesParams, type ResolveTranscriptionPolicyDependencies, type RunMetaWhatsAppMigrationsParams, type SaveFlowGraphParams, SaveFlowGraphUseCase, type SaveTranscriptionByWaMessageIdParams, type SaveTranscriptionParams, type SendMediaParams, SendMessageUseCase, type SendTemplateParams, type SendTextParams, SessionRepository, type SessionRow, SettingsRepository, type SettingsRow, SseHub, type SseListener, type StorePreviewMediaParams, type StorePreviewMediaResult, StorePreviewMediaUseCase, TRANSCRIPTION_MODE, TRANSCRIPTION_STATUS, type TakeoverConversationParams, TakeoverConversationUseCase, type TicketStoreInterface, type TranscribeAudioDependencies, type TranscribeAudioParams, type TranscribeAudioResult, TranscribeAudioUseCase, type TranscriptionPolicy, type TranscriptionPolicyDefaults, type TranscriptionPolicyResolver, type TranscriptionStatus, type UpdateFlowMediaParams, WhatsAppChannelAdapter, buildInboundJobId, claimWebhookDelivery, confirmWebhookDelivery, createMetaWhatsAppModule, createSendMediaAction, createSendProductListAction, createTranscriptionPolicyResolver, documents, extractMediaDescriptor, flowGraphs, flowMedia, isAudioMimeType, isRetriableTranscriptionError, isUnsupportedTranscriptionError, issueSseTicket, messages, metaWhatsAppMigrationsFolder, metaWhatsAppSchema, redeemSseTicket, resolveFailureStatus, runMetaWhatsAppMigrations, sessions, settings, toSessionContract, transcriptionRetryAfterSeconds, verifyWebhookChallenge, verifyWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
2
2
  import { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
3
3
  import * as _adatechnology_meta_whatsapp_contracts from '@adatechnology/meta-whatsapp-contracts';
4
- import { TranscriptionMode, SessionState, SessionMode, ConversationSummary, MessageDirection, MessageSender, MessageStatus, CacheInterface, FlowGraphData, FlowGraphSummary, LiveFlowPosition, WhatsAppSettings, RealtimeNotifierInterface, ChannelAdapterInterface, ObjectStorageInterface, FlowActionKind, FlowActionHandler, ConversationSession, MetaWhatsAppHooks, SubjectResolverInterface, CatalogPort } from '@adatechnology/meta-whatsapp-contracts';
4
+ import { TranscriptionMode, SessionState, SessionMode, ConversationSummary, MessageDirection, MessageSender, MessageStatus, CacheInterface, FlowGraphData, FlowGraphSummary, LiveFlowPosition, WhatsAppSettings, RealtimeNotifierInterface, ChannelAdapterInterface, ObjectStorageInterface, FlowActionKind, FlowActionHandler, ConversationSession, WhatsAppMessage, WhatsAppStatus, MetaWhatsAppHooks, SubjectResolverInterface, CatalogPort } from '@adatechnology/meta-whatsapp-contracts';
5
5
  export { PREVIEW_MEDIA_ID_PREFIX, TranscriptionMode, resolvePreviewUploadId, toPreviewMediaId } from '@adatechnology/meta-whatsapp-contracts';
6
6
  import { WhatsAppMessageProvider } from '@adatechnology/meta-whatsapp-provider';
7
- export { WEBHOOK_NONCE_TTL_SECONDS } from '@adatechnology/meta-graph-core';
7
+ export { WEBHOOK_CLAIM_TTL_SECONDS, WEBHOOK_NONCE_TTL_SECONDS } from '@adatechnology/meta-graph-core';
8
8
 
9
9
  type MetaWhatsAppDatabase = PgDatabase<PgQueryResultHKT, any, any>;
10
10
  type DrizzleMigrateFunction = (db: never, config: {
@@ -2316,6 +2316,15 @@ declare class FlowMediaRepository {
2316
2316
 
2317
2317
  interface NonceStoreInterface {
2318
2318
  setIfAbsent(key: string, ttlSeconds: number): Promise<boolean>;
2319
+ /**
2320
+ * Estende a chave já reivindicada para a janela cheia. Um SET simples, sem NX.
2321
+ *
2322
+ * Opcional só por compatibilidade com hosts que ainda não o implementam: sem ele o claim curto
2323
+ * expira, e a Meta pode reentregar uma entrega já processada — trabalho repetido, que a dedupe
2324
+ * por `waMessageId` ainda segura antes de virar efeito visível ao cliente. Implementar é o
2325
+ * caminho correto.
2326
+ */
2327
+ confirm?(key: string, ttlSeconds: number): Promise<void>;
2319
2328
  }
2320
2329
 
2321
2330
  declare function verifyWebhookChallenge(params: {
@@ -2329,11 +2338,101 @@ declare function verifyWebhookSignature(params: {
2329
2338
  signatureHeader: string | null | undefined;
2330
2339
  appSecret: string;
2331
2340
  }): void;
2341
+ /**
2342
+ * Reivindica a entrega por pouco tempo. O par obrigatório é `confirmWebhookDelivery` ao fim do
2343
+ * processamento — ver `WEBHOOK_CLAIM_TTL_SECONDS` para o porquê dos dois tempos.
2344
+ */
2332
2345
  declare function claimWebhookDelivery(params: {
2333
2346
  nonceStore: NonceStoreInterface;
2334
2347
  signatureHeader: string;
2335
2348
  ttlSeconds?: number;
2336
2349
  }): Promise<boolean>;
2350
+ /** Só depois da entrega processada por inteiro: é isto que fecha a janela anti-replay. */
2351
+ declare function confirmWebhookDelivery(params: {
2352
+ nonceStore: NonceStoreInterface;
2353
+ signatureHeader: string;
2354
+ ttlSeconds?: number;
2355
+ }): Promise<void>;
2356
+
2357
+ /**
2358
+ * Copyright (c) 2026 Ada Technology. MIT License.
2359
+ *
2360
+ * Separa a entrega do webhook em duas metades: o que precisa acontecer antes de responder à Meta
2361
+ * (persistir a mensagem, que é escrita local e rápida) e o que pode esperar alguns milissegundos
2362
+ * (os hooks do host, que costumam falar com rede — motor de fluxo, IA, integrações).
2363
+ *
2364
+ * A segunda metade rodava dentro da requisição do webhook. Um deploy no meio de uma conversa
2365
+ * matava a chamada em voo, e o cliente ficava sem resposta sem que nada registrasse a perda. Com
2366
+ * uma fila no host, derrubar o processo vira atraso: o job espera e é retomado.
2367
+ */
2368
+
2369
+ type InboundMediaDescriptor = {
2370
+ sourceMediaId: string;
2371
+ mimeType: string;
2372
+ filename?: string;
2373
+ };
2374
+ type InboundMessageEffectsJob = {
2375
+ kind: 'message';
2376
+ companyId: string;
2377
+ message: WhatsAppMessage;
2378
+ savedMessageId: string;
2379
+ media?: InboundMediaDescriptor;
2380
+ receivedAt: number;
2381
+ };
2382
+ type InboundStatusEffectsJob = {
2383
+ kind: 'status';
2384
+ companyId: string;
2385
+ status: WhatsAppStatus;
2386
+ whatsappNumber: string;
2387
+ receivedAt: number;
2388
+ };
2389
+ type InboundDispatchJob = InboundMessageEffectsJob | InboundStatusEffectsJob;
2390
+ /**
2391
+ * Porta de fila. O módulo não escolhe a tecnologia — BullMQ, SQS, o que o host já tiver — mas
2392
+ * exige dela duas garantias, e sem as duas o padrão não entrega o que promete:
2393
+ *
2394
+ * 1. **Durabilidade**: o job sobrevive à morte do processo que o enfileirou. Uma fila em memória
2395
+ * reintroduz exatamente a perda que este desenho existe para evitar.
2396
+ * 2. **Retentativa com backoff**: o destino dos hooks (n8n, IA, API de terceiro) também cai em
2397
+ * deploy. Sem retry, o job falha uma vez e a conversa morre do mesmo jeito.
2398
+ *
2399
+ * `jobId` é estável e derivado da mensagem: reentrega da Meta e re-enfileiramento produzem o mesmo
2400
+ * id, e a fila descarta o segundo em vez de rodar o efeito duas vezes.
2401
+ */
2402
+ interface InboundDispatchQueueInterface {
2403
+ enqueue(job: InboundDispatchJob, options: {
2404
+ jobId: string;
2405
+ }): Promise<void>;
2406
+ }
2407
+ declare function buildInboundJobId(job: InboundDispatchJob): string;
2408
+ declare function toSessionContract(row: SessionRow): ConversationSession;
2409
+ /**
2410
+ * Os efeitos de host de uma entrega já persistida. Vive fora do `ReceiveWebhookUseCase` porque
2411
+ * roda nos dois lados: inline, quando o host não configurou fila, e dentro do worker, quando
2412
+ * configurou. Um só corpo de regra para os dois caminhos — duas cópias divergiriam.
2413
+ */
2414
+ declare class InboundEffectsDispatcher {
2415
+ private readonly params;
2416
+ constructor(params: {
2417
+ sessionRepository: SessionRepository;
2418
+ hooks?: MetaWhatsAppHooks;
2419
+ realtime?: RealtimeNotifierInterface;
2420
+ });
2421
+ run(job: InboundDispatchJob): Promise<void>;
2422
+ runMessageEffects(job: InboundMessageEffectsJob): Promise<void>;
2423
+ runStatusEffects(job: InboundStatusEffectsJob): Promise<void>;
2424
+ }
2425
+ /**
2426
+ * Ponto de entrada do worker do host: recebe o job que a fila devolveu e roda os efeitos.
2427
+ *
2428
+ * Deixe a exceção propagar. É ela que faz a fila contar a tentativa e reagendar com backoff;
2429
+ * capturar aqui para logar transforma falha recuperável em mensagem perdida em silêncio.
2430
+ */
2431
+ declare class ProcessInboundDispatchUseCase {
2432
+ private readonly dispatcher;
2433
+ constructor(dispatcher: InboundEffectsDispatcher);
2434
+ execute(job: InboundDispatchJob): Promise<void>;
2435
+ }
2337
2436
 
2338
2437
  type ReceiveWebhookParams = {
2339
2438
  companyId: string;
@@ -2358,10 +2457,17 @@ declare class ReceiveWebhookUseCase {
2358
2457
  startState: SessionState;
2359
2458
  hooks?: MetaWhatsAppHooks;
2360
2459
  realtime?: RealtimeNotifierInterface;
2460
+ /**
2461
+ * Sem fila o módulo se comporta como sempre: os hooks rodam dentro da requisição do webhook.
2462
+ * Configurá-la é o que faz a conversa sobreviver a um deploy no meio do atendimento.
2463
+ */
2464
+ inboundQueue?: InboundDispatchQueueInterface;
2361
2465
  });
2466
+ private readonly dispatcher;
2362
2467
  execute(input: ReceiveWebhookParams): Promise<ReceiveWebhookResult>;
2363
2468
  private handleMessage;
2364
2469
  private handleStatus;
2470
+ private dispatch;
2365
2471
  }
2366
2472
 
2367
2473
  /**
@@ -2895,4 +3001,4 @@ type CreateSendProductListActionParams = {
2895
3001
  */
2896
3002
  declare function createSendProductListAction(params: CreateSendProductListActionParams): FlowActionHandler;
2897
3003
 
2898
- export { type AttachFlowMediaParams, type AudioTranscriber, type CompanyDocumentView, type CompanyDocumentsPage, type ConversationDocumentView, type ConversationDocumentsPage, type CreateFlowGraphParams, CreateFlowGraphUseCase, type CreateMetaWhatsAppModuleParams, type CreateSendMediaActionParams, type CreateSendProductListActionParams, DEFAULT_FLOW_GRAPH_CACHE_TTL_SECONDS, type DeleteConversationParams, type DeleteConversationResult, DeleteConversationUseCase, DeleteFlowGraphUseCase, DocumentRepository, type DocumentRow, type DrizzleMigrateFunction, type ExportConversationParams, type ExportConversationResult, ExportConversationUseCase, FlowGraphCache, FlowGraphRepository, type FlowGraphRow, FlowInterpreter, type FlowMediaLocation, FlowMediaRepository, type FlowMediaRow, type FlowMediaTranscriptLogger, type FlowRunResult, type FlowStepInput, type FlowStepResult, GetFlowGraphUseCase, GetLiveFlowPositionsUseCase, type IngestInboundMediaParams, type IngestInboundMediaResult, IngestInboundMediaUseCase, type IngestTranscriptionOptions, type InsertMessageParams, InvalidFlowGraphError, type LinkDocumentParams, type ListCompanyDocumentsParams, ListCompanyDocumentsUseCase, type ListConversationDocumentsParams, ListConversationDocumentsUseCase, type ListConversationsFilters, type ListConversationsParams, ListConversationsUseCase, type ListDocumentsParams, type ListDocumentsResult, ListFlowGraphsUseCase, type ListMessagesParams, ListMessagesUseCase, type LogMessageParams, LogMessageUseCase, META_WHATSAPP_MIGRATIONS_TABLE, type MessageModerator, MessageRepository, type MessageRow, type MetaWhatsAppDatabase, type MetaWhatsAppModule, type MetaWhatsAppModuleConfig, type MetaWhatsAppModuleFeatures, type MetaWhatsAppModuleProviders, type MetaWhatsAppTranscriptionConfig, type NewDocumentRow, type NewFlowGraphRow, type NewFlowMediaRow, type NewMessageRow, type NewSessionRow, type NewSettingsRow, type NonceStoreInterface, OptimisticLockError, PRODUCT_LIST_LIMIT, type PreviewMediaSupport, type PurgeExpiredDocumentsParams, type PurgeExpiredDocumentsResult, PurgeExpiredDocumentsUseCase, type RealtimeRelay, type ReceiveWebhookParams, type ReceiveWebhookResult, ReceiveWebhookUseCase, type ReleaseConversationParams, ReleaseConversationUseCase, type ListMessagesParams$1 as RepositoryListMessagesParams, type ResolveTranscriptionPolicyDependencies, type RunMetaWhatsAppMigrationsParams, type SaveFlowGraphParams, SaveFlowGraphUseCase, type SaveTranscriptionByWaMessageIdParams, type SaveTranscriptionParams, type SendMediaParams, SendMessageUseCase, type SendTemplateParams, type SendTextParams, SessionRepository, type SessionRow, SettingsRepository, type SettingsRow, SseHub, type SseListener, type StorePreviewMediaParams, type StorePreviewMediaResult, StorePreviewMediaUseCase, TRANSCRIPTION_MODE, TRANSCRIPTION_STATUS, type TakeoverConversationParams, TakeoverConversationUseCase, type TicketStoreInterface, type TranscribeAudioDependencies, type TranscribeAudioParams, type TranscribeAudioResult, TranscribeAudioUseCase, type TranscriptionPolicy, type TranscriptionPolicyDefaults, type TranscriptionPolicyResolver, type TranscriptionStatus, type UpdateFlowMediaParams, WhatsAppChannelAdapter, claimWebhookDelivery, createMetaWhatsAppModule, createSendMediaAction, createSendProductListAction, createTranscriptionPolicyResolver, documents, extractMediaDescriptor, flowGraphs, flowMedia, isAudioMimeType, isRetriableTranscriptionError, isUnsupportedTranscriptionError, issueSseTicket, messages, metaWhatsAppMigrationsFolder, metaWhatsAppSchema, redeemSseTicket, resolveFailureStatus, runMetaWhatsAppMigrations, sessions, settings, transcriptionRetryAfterSeconds, verifyWebhookChallenge, verifyWebhookSignature };
3004
+ export { type AttachFlowMediaParams, type AudioTranscriber, type CompanyDocumentView, type CompanyDocumentsPage, type ConversationDocumentView, type ConversationDocumentsPage, type CreateFlowGraphParams, CreateFlowGraphUseCase, type CreateMetaWhatsAppModuleParams, type CreateSendMediaActionParams, type CreateSendProductListActionParams, DEFAULT_FLOW_GRAPH_CACHE_TTL_SECONDS, type DeleteConversationParams, type DeleteConversationResult, DeleteConversationUseCase, DeleteFlowGraphUseCase, DocumentRepository, type DocumentRow, type DrizzleMigrateFunction, type ExportConversationParams, type ExportConversationResult, ExportConversationUseCase, FlowGraphCache, FlowGraphRepository, type FlowGraphRow, FlowInterpreter, type FlowMediaLocation, FlowMediaRepository, type FlowMediaRow, type FlowMediaTranscriptLogger, type FlowRunResult, type FlowStepInput, type FlowStepResult, GetFlowGraphUseCase, GetLiveFlowPositionsUseCase, type InboundDispatchJob, type InboundDispatchQueueInterface, InboundEffectsDispatcher, type InboundMediaDescriptor, type InboundMessageEffectsJob, type InboundStatusEffectsJob, type IngestInboundMediaParams, type IngestInboundMediaResult, IngestInboundMediaUseCase, type IngestTranscriptionOptions, type InsertMessageParams, InvalidFlowGraphError, type LinkDocumentParams, type ListCompanyDocumentsParams, ListCompanyDocumentsUseCase, type ListConversationDocumentsParams, ListConversationDocumentsUseCase, type ListConversationsFilters, type ListConversationsParams, ListConversationsUseCase, type ListDocumentsParams, type ListDocumentsResult, ListFlowGraphsUseCase, type ListMessagesParams, ListMessagesUseCase, type LogMessageParams, LogMessageUseCase, META_WHATSAPP_MIGRATIONS_TABLE, type MessageModerator, MessageRepository, type MessageRow, type MetaWhatsAppDatabase, type MetaWhatsAppModule, type MetaWhatsAppModuleConfig, type MetaWhatsAppModuleFeatures, type MetaWhatsAppModuleProviders, type MetaWhatsAppTranscriptionConfig, type NewDocumentRow, type NewFlowGraphRow, type NewFlowMediaRow, type NewMessageRow, type NewSessionRow, type NewSettingsRow, type NonceStoreInterface, OptimisticLockError, PRODUCT_LIST_LIMIT, type PreviewMediaSupport, ProcessInboundDispatchUseCase, type PurgeExpiredDocumentsParams, type PurgeExpiredDocumentsResult, PurgeExpiredDocumentsUseCase, type RealtimeRelay, type ReceiveWebhookParams, type ReceiveWebhookResult, ReceiveWebhookUseCase, type ReleaseConversationParams, ReleaseConversationUseCase, type ListMessagesParams$1 as RepositoryListMessagesParams, type ResolveTranscriptionPolicyDependencies, type RunMetaWhatsAppMigrationsParams, type SaveFlowGraphParams, SaveFlowGraphUseCase, type SaveTranscriptionByWaMessageIdParams, type SaveTranscriptionParams, type SendMediaParams, SendMessageUseCase, type SendTemplateParams, type SendTextParams, SessionRepository, type SessionRow, SettingsRepository, type SettingsRow, SseHub, type SseListener, type StorePreviewMediaParams, type StorePreviewMediaResult, StorePreviewMediaUseCase, TRANSCRIPTION_MODE, TRANSCRIPTION_STATUS, type TakeoverConversationParams, TakeoverConversationUseCase, type TicketStoreInterface, type TranscribeAudioDependencies, type TranscribeAudioParams, type TranscribeAudioResult, TranscribeAudioUseCase, type TranscriptionPolicy, type TranscriptionPolicyDefaults, type TranscriptionPolicyResolver, type TranscriptionStatus, type UpdateFlowMediaParams, WhatsAppChannelAdapter, buildInboundJobId, claimWebhookDelivery, confirmWebhookDelivery, createMetaWhatsAppModule, createSendMediaAction, createSendProductListAction, createTranscriptionPolicyResolver, documents, extractMediaDescriptor, flowGraphs, flowMedia, isAudioMimeType, isRetriableTranscriptionError, isUnsupportedTranscriptionError, issueSseTicket, messages, metaWhatsAppMigrationsFolder, metaWhatsAppSchema, redeemSseTicket, resolveFailureStatus, runMetaWhatsAppMigrations, sessions, settings, toSessionContract, transcriptionRetryAfterSeconds, verifyWebhookChallenge, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -2005,7 +2005,7 @@ var WhatsAppChannelAdapter = class {
2005
2005
  import { whatsAppWebhookPayloadSchema } from "@adatechnology/meta-whatsapp-contracts";
2006
2006
 
2007
2007
  // src/channel/webhookSecurity.ts
2008
- import { WEBHOOK_NONCE_TTL_SECONDS, buildWebhookDeliveryKey, isValidWebhookChallenge, isValidWebhookSignature } from "@adatechnology/meta-graph-core";
2008
+ import { WEBHOOK_CLAIM_TTL_SECONDS, WEBHOOK_NONCE_TTL_SECONDS, buildWebhookDeliveryKey, isValidWebhookChallenge, isValidWebhookSignature } from "@adatechnology/meta-graph-core";
2009
2009
  import { InvalidWebhookSignatureError } from "@adatechnology/meta-whatsapp-contracts";
2010
2010
  var WEBHOOK_NONCE_NAMESPACE = "meta-whatsapp";
2011
2011
  function verifyWebhookChallenge(params) {
@@ -2017,14 +2017,21 @@ function verifyWebhookSignature(params) {
2017
2017
  if (!isValidWebhookSignature(params)) throw new InvalidWebhookSignatureError();
2018
2018
  }
2019
2019
  __name(verifyWebhookSignature, "verifyWebhookSignature");
2020
- async function claimWebhookDelivery(params) {
2021
- const key = buildWebhookDeliveryKey({
2020
+ function deliveryKey(signatureHeader) {
2021
+ return buildWebhookDeliveryKey({
2022
2022
  namespace: WEBHOOK_NONCE_NAMESPACE,
2023
- signatureHeader: params.signatureHeader
2023
+ signatureHeader
2024
2024
  });
2025
- return params.nonceStore.setIfAbsent(key, params.ttlSeconds ?? WEBHOOK_NONCE_TTL_SECONDS);
2025
+ }
2026
+ __name(deliveryKey, "deliveryKey");
2027
+ async function claimWebhookDelivery(params) {
2028
+ return params.nonceStore.setIfAbsent(deliveryKey(params.signatureHeader), params.ttlSeconds ?? WEBHOOK_CLAIM_TTL_SECONDS);
2026
2029
  }
2027
2030
  __name(claimWebhookDelivery, "claimWebhookDelivery");
2031
+ async function confirmWebhookDelivery(params) {
2032
+ await params.nonceStore.confirm?.(deliveryKey(params.signatureHeader), params.ttlSeconds ?? WEBHOOK_NONCE_TTL_SECONDS);
2033
+ }
2034
+ __name(confirmWebhookDelivery, "confirmWebhookDelivery");
2028
2035
 
2029
2036
  // src/channel/IngestInboundMedia.use-case.ts
2030
2037
  import { eq as eq7, and as and6 } from "drizzle-orm";
@@ -2345,7 +2352,11 @@ function extractMediaDescriptor(message) {
2345
2352
  }
2346
2353
  __name(extractMediaDescriptor, "extractMediaDescriptor");
2347
2354
 
2348
- // src/channel/ReceiveWebhook.use-case.ts
2355
+ // src/channel/inboundDispatch.ts
2356
+ function buildInboundJobId(job) {
2357
+ return job.kind === "message" ? `wa-inbound-message:${job.message.id}` : `wa-inbound-status:${job.status.id}:${job.status.status}`;
2358
+ }
2359
+ __name(buildInboundJobId, "buildInboundJobId");
2349
2360
  function toSessionContract(row) {
2350
2361
  return {
2351
2362
  id: row.id,
@@ -2366,6 +2377,65 @@ function toSessionContract(row) {
2366
2377
  };
2367
2378
  }
2368
2379
  __name(toSessionContract, "toSessionContract");
2380
+ function extractAnswer(message) {
2381
+ const interactive = message.interactive;
2382
+ if (interactive?.button_reply) return interactive.button_reply.id;
2383
+ if (interactive?.list_reply) return interactive.list_reply.id;
2384
+ return message.text?.body;
2385
+ }
2386
+ __name(extractAnswer, "extractAnswer");
2387
+ var InboundEffectsDispatcher = class {
2388
+ static {
2389
+ __name(this, "InboundEffectsDispatcher");
2390
+ }
2391
+ params;
2392
+ constructor(params) {
2393
+ this.params = params;
2394
+ }
2395
+ async run(job) {
2396
+ if (job.kind === "message") return this.runMessageEffects(job);
2397
+ return this.runStatusEffects(job);
2398
+ }
2399
+ async runMessageEffects(job) {
2400
+ const { companyId, message, savedMessageId, media } = job;
2401
+ if (media) {
2402
+ await this.params.hooks?.onMediaReceived?.({
2403
+ companyId,
2404
+ messageId: savedMessageId,
2405
+ whatsappNumber: message.from,
2406
+ sourceMediaId: media.sourceMediaId,
2407
+ mimeType: media.mimeType,
2408
+ ...media.filename ? {
2409
+ filename: media.filename
2410
+ } : {}
2411
+ });
2412
+ }
2413
+ const sessionRow = await this.params.sessionRepository.getContext(companyId, message.from);
2414
+ if (!sessionRow) return;
2415
+ if (sessionRow.mode === "human") return;
2416
+ const outcome = await this.params.hooks?.onMessageReceived?.(message, toSessionContract(sessionRow));
2417
+ if (outcome?.outcome === "handled") return;
2418
+ void extractAnswer(message);
2419
+ }
2420
+ async runStatusEffects(job) {
2421
+ const sessionRow = await this.params.sessionRepository.getContext(job.companyId, job.whatsappNumber);
2422
+ await this.params.hooks?.onStatusUpdate?.(job.status, sessionRow ? toSessionContract(sessionRow) : null);
2423
+ }
2424
+ };
2425
+ var ProcessInboundDispatchUseCase = class {
2426
+ static {
2427
+ __name(this, "ProcessInboundDispatchUseCase");
2428
+ }
2429
+ dispatcher;
2430
+ constructor(dispatcher) {
2431
+ this.dispatcher = dispatcher;
2432
+ }
2433
+ async execute(job) {
2434
+ await this.dispatcher.run(job);
2435
+ }
2436
+ };
2437
+
2438
+ // src/channel/ReceiveWebhook.use-case.ts
2369
2439
  function extractContent(message) {
2370
2440
  if (message.text?.body) return message.text.body;
2371
2441
  const interactive = message.interactive;
@@ -2378,13 +2448,6 @@ function extractContent(message) {
2378
2448
  return message.image?.caption ?? message.document?.caption ?? null;
2379
2449
  }
2380
2450
  __name(extractContent, "extractContent");
2381
- function extractAnswer(message) {
2382
- const interactive = message.interactive;
2383
- if (interactive?.button_reply) return interactive.button_reply.id;
2384
- if (interactive?.list_reply) return interactive.list_reply.id;
2385
- return message.text?.body;
2386
- }
2387
- __name(extractAnswer, "extractAnswer");
2388
2451
  function extractPayload(message) {
2389
2452
  const payload = {};
2390
2453
  if (message.interactive) payload["interactive"] = message.interactive;
@@ -2405,7 +2468,17 @@ var ReceiveWebhookUseCase = class {
2405
2468
  params;
2406
2469
  constructor(params) {
2407
2470
  this.params = params;
2471
+ this.dispatcher = new InboundEffectsDispatcher({
2472
+ sessionRepository: params.sessionRepository,
2473
+ ...params.hooks ? {
2474
+ hooks: params.hooks
2475
+ } : {},
2476
+ ...params.realtime ? {
2477
+ realtime: params.realtime
2478
+ } : {}
2479
+ });
2408
2480
  }
2481
+ dispatcher;
2409
2482
  async execute(input) {
2410
2483
  verifyWebhookSignature({
2411
2484
  rawBody: input.rawBody,
@@ -2444,6 +2517,10 @@ var ReceiveWebhookUseCase = class {
2444
2517
  }
2445
2518
  }
2446
2519
  }
2520
+ await confirmWebhookDelivery({
2521
+ nonceStore: this.params.nonceStore,
2522
+ signatureHeader: input.signatureHeader
2523
+ });
2447
2524
  return {
2448
2525
  duplicate: false,
2449
2526
  messagesProcessed,
@@ -2466,24 +2543,16 @@ var ReceiveWebhookUseCase = class {
2466
2543
  });
2467
2544
  if (!saved) return;
2468
2545
  const media = extractMediaDescriptor(saved);
2469
- if (media) {
2470
- await this.params.hooks?.onMediaReceived?.({
2471
- companyId,
2472
- messageId: saved.id,
2473
- whatsappNumber: message.from,
2474
- sourceMediaId: media.sourceMediaId,
2475
- mimeType: media.mimeType,
2476
- ...media.filename ? {
2477
- filename: media.filename
2478
- } : {}
2479
- });
2480
- }
2481
- const sessionRow = await this.params.sessionRepository.getContext(companyId, message.from);
2482
- if (!sessionRow) return;
2483
- if (sessionRow.mode === "human") return;
2484
- const outcome = await this.params.hooks?.onMessageReceived?.(message, toSessionContract(sessionRow));
2485
- if (outcome?.outcome === "handled") return;
2486
- void extractAnswer(message);
2546
+ await this.dispatch({
2547
+ kind: "message",
2548
+ companyId,
2549
+ message,
2550
+ savedMessageId: saved.id,
2551
+ ...media ? {
2552
+ media
2553
+ } : {},
2554
+ receivedAt: Date.now()
2555
+ });
2487
2556
  }
2488
2557
  async handleStatus(companyId, status) {
2489
2558
  const updated = await this.params.messageRepository.updateMessageStatus(companyId, status.id, status.status);
@@ -2492,8 +2561,24 @@ var ReceiveWebhookUseCase = class {
2492
2561
  waMessageId: status.id,
2493
2562
  status: status.status
2494
2563
  });
2495
- const sessionRow = await this.params.sessionRepository.getContext(companyId, updated.whatsappNumber);
2496
- await this.params.hooks?.onStatusUpdate?.(status, sessionRow ? toSessionContract(sessionRow) : null);
2564
+ await this.dispatch({
2565
+ kind: "status",
2566
+ companyId,
2567
+ status,
2568
+ whatsappNumber: updated.whatsappNumber,
2569
+ receivedAt: Date.now()
2570
+ });
2571
+ }
2572
+ // Com fila configurada, os efeitos saem da requisição do webhook; sem ela, rodam aqui mesmo e o
2573
+ // comportamento é o de sempre. É o mesmo `InboundEffectsDispatcher` nos dois caminhos.
2574
+ async dispatch(job) {
2575
+ if (this.params.inboundQueue) {
2576
+ await this.params.inboundQueue.enqueue(job, {
2577
+ jobId: buildInboundJobId(job)
2578
+ });
2579
+ return;
2580
+ }
2581
+ await this.dispatcher.run(job);
2497
2582
  }
2498
2583
  };
2499
2584
 
@@ -2806,6 +2891,7 @@ export {
2806
2891
  FlowMediaRepository,
2807
2892
  GetFlowGraphUseCase,
2808
2893
  GetLiveFlowPositionsUseCase,
2894
+ InboundEffectsDispatcher,
2809
2895
  IngestInboundMediaUseCase,
2810
2896
  InvalidFlowGraphError,
2811
2897
  ListCompanyDocumentsUseCase,
@@ -2819,6 +2905,7 @@ export {
2819
2905
  OptimisticLockError,
2820
2906
  PREVIEW_MEDIA_ID_PREFIX,
2821
2907
  PRODUCT_LIST_LIMIT,
2908
+ ProcessInboundDispatchUseCase,
2822
2909
  PurgeExpiredDocumentsUseCase,
2823
2910
  ReceiveWebhookUseCase,
2824
2911
  ReleaseConversationUseCase,
@@ -2832,9 +2919,12 @@ export {
2832
2919
  TRANSCRIPTION_STATUS,
2833
2920
  TakeoverConversationUseCase,
2834
2921
  TranscribeAudioUseCase,
2922
+ WEBHOOK_CLAIM_TTL_SECONDS,
2835
2923
  WEBHOOK_NONCE_TTL_SECONDS,
2836
2924
  WhatsAppChannelAdapter,
2925
+ buildInboundJobId,
2837
2926
  claimWebhookDelivery,
2927
+ confirmWebhookDelivery,
2838
2928
  createMetaWhatsAppModule,
2839
2929
  createSendMediaAction,
2840
2930
  createSendProductListAction,
@@ -2857,6 +2947,7 @@ export {
2857
2947
  sessions,
2858
2948
  settings,
2859
2949
  toPreviewMediaId,
2950
+ toSessionContract,
2860
2951
  transcriptionRetryAfterSeconds,
2861
2952
  verifyWebhookChallenge,
2862
2953
  verifyWebhookSignature