@@ -29,10 +29,12 @@ type WhatsAppInboundActivationAccess = Pick<
2929 "ran" | "allowed" | "shouldSkip" | "reasonCode"
3030> ;
3131
32- type WhatsAppInboundAdmissionAccess = Pick <
33- ResolvedChannelMessageIngress ,
34- "ingress" | "senderAccess" | "commandAccess" | "activationAccess"
35- > ;
32+ type WhatsAppInboundAdmissionAccess = {
33+ ingress : WhatsAppInboundIngressDecision ;
34+ senderAccess : WhatsAppInboundSenderAccess ;
35+ commandAccess : WhatsAppInboundCommandAccess ;
36+ activationAccess : WhatsAppInboundActivationAccess ;
37+ } ;
3638
3739type WhatsAppInboundAdmissionPolicy = {
3840 account : {
@@ -105,6 +107,24 @@ export type WhatsAppInboundAdmission = {
105107 turnAdmission : WhatsAppInboundTurnAdmission ;
106108} ;
107109
110+ export type WhatsAppInboundAdmissionInput =
111+ | WhatsAppInboundAdmission
112+ | Omit < WhatsAppInboundAdmission , "turnAdmission" > ;
113+
114+ export function normalizeWhatsAppInboundAdmission (
115+ admission : WhatsAppInboundAdmissionInput ,
116+ ) : WhatsAppInboundAdmission {
117+ if ( "turnAdmission" in admission ) {
118+ return admission ;
119+ }
120+ // Shipped nested callbacks predate turnAdmission. Normalize once at the
121+ // callback boundary so all runtime consumers receive the canonical shape.
122+ return {
123+ ...admission ,
124+ turnAdmission : mapChannelIngressDecisionToTurnAdmission ( admission . ingress ) ,
125+ } ;
126+ }
127+
108128function copyAccount (
109129 account : WhatsAppInboundAdmissionPolicy [ "account" ] ,
110130) : WhatsAppInboundAdmission [ "account" ] {
@@ -197,53 +217,49 @@ export function buildDeprecatedFlatWhatsAppInboundAdmission(
197217 ? "group_policy_allowed"
198218 : "dm_policy_allowlisted"
199219 : "no_policy_match" ;
200- const ingress : WhatsAppInboundIngressDecision = {
201- admission : admitted ? "dispatch" : "drop" ,
202- decision : admitted ? "allow" : "block" ,
203- decisiveGateId : "legacy-flat-compat" ,
204- reasonCode,
205- } ;
206-
207- // Compatibility only: flat listenerFactory inputs predate SDK ingress, so
208- // construct their legacy admission directly instead of inventing an access graph.
209- return {
210- accountId,
211- isSelfChat : false ,
212- account : {
213- accountId,
214- enabled : true ,
215- sendReadReceipts : true ,
220+ // Compatibility only: deprecated listenerFactory flat inputs predate the
221+ // admission envelope, so convert them through the canonical admission builder.
222+ // Canonical nested inputs without admission remain malformed for runtime use.
223+ return buildWhatsAppInboundAdmission ( {
224+ policy : {
225+ account : {
226+ accountId,
227+ enabled : true ,
228+ sendReadReceipts : true ,
229+ } ,
230+ isSelfChat : false ,
231+ isSamePhone : ( ) => false ,
216232 } ,
217- conversation : {
218- kind : msg . chatType === "group" ? "group" : "direct" ,
219- id : conversationId ,
220- groupSessionId : resolveWhatsAppGroupConversationId ( conversationId ) ,
233+ access : {
234+ ingress : {
235+ admission : admitted ? "dispatch" : "drop" ,
236+ decision : admitted ? "allow" : "block" ,
237+ decisiveGateId : "legacy-flat-compat" ,
238+ reasonCode,
239+ } ,
240+ senderAccess : {
241+ allowed : admitted ,
242+ decision : admitted ? "allow" : "block" ,
243+ reasonCode,
244+ providerMissingFallbackApplied : false ,
245+ } ,
246+ commandAccess : {
247+ requested : false ,
248+ authorized : false ,
249+ shouldBlockControlCommand : false ,
250+ reasonCode : "command_authorized" ,
251+ } ,
252+ activationAccess : {
253+ ran : false ,
254+ allowed : admitted ,
255+ shouldSkip : ! admitted ,
256+ reasonCode : admitted ? "activation_allowed" : "activation_skipped" ,
257+ } ,
221258 } ,
222- sender : {
223- id : senderId ,
224- isSamePhone : false ,
225- } ,
226- ingress,
227- senderAccess : {
228- allowed : admitted ,
229- decision : admitted ? "allow" : "block" ,
230- reasonCode,
231- providerMissingFallbackApplied : false ,
232- } ,
233- commandAccess : {
234- requested : false ,
235- authorized : false ,
236- shouldBlockControlCommand : false ,
237- reasonCode : "command_authorized" ,
238- } ,
239- activationAccess : {
240- ran : false ,
241- allowed : admitted ,
242- shouldSkip : ! admitted ,
243- reasonCode : admitted ? "activation_allowed" : "activation_skipped" ,
244- } ,
245- turnAdmission : mapChannelIngressDecisionToTurnAdmission ( ingress ) ,
246- } ;
259+ isGroup : msg . chatType === "group" ,
260+ conversationId,
261+ senderId,
262+ } ) ;
247263}
248264
249265export function requireWhatsAppInboundAdmission (
0 commit comments