22import { afterEach , beforeEach , describe , expect , it } from "vitest" ;
33import {
44 VoiceCallConfigSchema ,
5+ resolveVoiceCallAgentSessionKey ,
56 resolveTwilioAuthToken ,
67 resolveVoiceCallEffectiveConfig ,
78 resolveVoiceCallNumberRouteKey ,
9+ resolveVoiceCallNumberRouteKeyForCall ,
810 resolveVoiceCallSessionKey ,
911 validateProviderConfig ,
1012 normalizeVoiceCallConfig ,
@@ -296,7 +298,23 @@ describe("resolveVoiceCallConfig session routing", () => {
296298 callId : "call-123" ,
297299 phone : "+1 (555) 000-1111" ,
298300 } ) ,
299- ) . toBe ( "voice:15550001111" ) ;
301+ ) . toBe ( "agent:main:voice:15550001111" ) ;
302+ } ) ;
303+
304+ it ( "scopes generated voice session keys by configured agent" , ( ) => {
305+ const config = resolveVoiceCallConfig ( {
306+ enabled : true ,
307+ provider : "mock" ,
308+ agentId : "Voice" ,
309+ } ) ;
310+
311+ expect (
312+ resolveVoiceCallSessionKey ( {
313+ config,
314+ callId : "CALL-123" ,
315+ phone : "+1 (555) 000-1111" ,
316+ } ) ,
317+ ) . toBe ( "agent:voice:voice:15550001111" ) ;
300318 } ) ;
301319
302320 it ( "can scope voice sessions to each call" , ( ) => {
@@ -313,10 +331,10 @@ describe("resolveVoiceCallConfig session routing", () => {
313331 callId : "call-123" ,
314332 phone : "+1 (555) 000-1111" ,
315333 } ) ,
316- ) . toBe ( "voice:call:call-123" ) ;
334+ ) . toBe ( "agent:main: voice:call:call-123" ) ;
317335 } ) ;
318336
319- it ( "preserves explicit voice session keys" , ( ) => {
337+ it ( "scopes explicit voice session keys by configured agent " , ( ) => {
320338 const config = resolveVoiceCallConfig ( {
321339 enabled : true ,
322340 provider : "mock" ,
@@ -328,9 +346,135 @@ describe("resolveVoiceCallConfig session routing", () => {
328346 config,
329347 callId : "call-123" ,
330348 phone : "+1 (555) 000-1111" ,
331- explicitSessionKey : "meet-room-1" ,
349+ explicitSessionKey : "Meet-Room-1" ,
350+ } ) ,
351+ ) . toBe ( "agent:main:meet-room-1" ) ;
352+ } ) ;
353+
354+ it ( "scopes persisted and explicit keys at the agent session boundary" , ( ) => {
355+ const config = resolveVoiceCallConfig ( {
356+ enabled : true ,
357+ provider : "mock" ,
358+ agentId : "Voice" ,
359+ } ) ;
360+
361+ expect (
362+ resolveVoiceCallAgentSessionKey ( {
363+ config,
364+ sessionKey : "voice:call:legacy-call" ,
365+ } ) ,
366+ ) . toBe ( "agent:voice:voice:call:legacy-call" ) ;
367+ expect (
368+ resolveVoiceCallAgentSessionKey ( {
369+ config,
370+ sessionKey : "meet-room-1" ,
371+ } ) ,
372+ ) . toBe ( "agent:voice:meet-room-1" ) ;
373+ expect (
374+ resolveVoiceCallAgentSessionKey ( {
375+ config,
376+ sessionKey : "agent:main:shared-room" ,
377+ } ) ,
378+ ) . toBe ( "agent:voice:agent:main:shared-room" ) ;
379+ expect (
380+ resolveVoiceCallAgentSessionKey ( {
381+ config,
382+ sessionKey : "agent:other:Matrix:Channel:!RoomAbC:example.org" ,
383+ } ) ,
384+ ) . toBe ( "agent:voice:agent:other:matrix:channel:!RoomAbC:example.org" ) ;
385+ expect (
386+ resolveVoiceCallAgentSessionKey ( {
387+ config,
388+ sessionKey : "agent:voice:agent:other:matrix:channel:!RoomAbC:example.org" ,
389+ } ) ,
390+ ) . toBe ( "agent:voice:agent:other:matrix:channel:!RoomAbC:example.org" ) ;
391+ expect (
392+ resolveVoiceCallAgentSessionKey ( {
393+ config,
394+ sessionKey : "Signal:Group:AbC123=" ,
395+ } ) ,
396+ ) . toBe ( "agent:voice:signal:group:AbC123=" ) ;
397+ expect (
398+ resolveVoiceCallAgentSessionKey ( {
399+ config,
400+ sessionKey : "agent:broken" ,
401+ } ) ,
402+ ) . toBe ( "agent:voice:agent:broken" ) ;
403+ expect (
404+ resolveVoiceCallAgentSessionKey ( {
405+ config,
406+ sessionKey : "agent::broken" ,
407+ } ) ,
408+ ) . toBe ( "agent:voice:agent::broken" ) ;
409+ expect (
410+ resolveVoiceCallAgentSessionKey ( {
411+ config,
412+ sessionKey : "agent::Matrix:Channel:!RoomAbC:example.org" ,
413+ } ) ,
414+ ) . toBe ( "agent:voice:agent::matrix:channel:!RoomAbC:example.org" ) ;
415+ expect (
416+ resolveVoiceCallAgentSessionKey ( {
417+ config,
418+ sessionKey : "agent:other:room::part" ,
419+ } ) ,
420+ ) . toBe ( "agent:voice:agent:other:room::part" ) ;
421+ expect (
422+ resolveVoiceCallAgentSessionKey ( {
423+ config,
424+ sessionKey : "agent:voice:room::part" ,
425+ } ) ,
426+ ) . toBe ( "agent:voice:room::part" ) ;
427+ expect (
428+ resolveVoiceCallAgentSessionKey ( {
429+ config,
430+ sessionKey : "agent:voice::Matrix:Channel:!RoomAbC:example.org" ,
431+ } ) ,
432+ ) . toBe ( "agent:voice:agent:voice::matrix:channel:!RoomAbC:example.org" ) ;
433+ expect (
434+ resolveVoiceCallAgentSessionKey ( {
435+ config,
436+ sessionKey : "agent:bad/id:room" ,
437+ } ) ,
438+ ) . toBe ( "agent:voice:agent:bad/id:room" ) ;
439+ } ) ;
440+
441+ it ( "canonicalizes raw and scoped main aliases with the core session config" , ( ) => {
442+ const config = resolveVoiceCallConfig ( {
443+ enabled : true ,
444+ provider : "mock" ,
445+ agentId : "Voice" ,
446+ } ) ;
447+
448+ for ( const sessionKey of [ "main" , "agent:voice:main" ] ) {
449+ expect (
450+ resolveVoiceCallAgentSessionKey ( {
451+ config,
452+ sessionKey,
453+ coreSession : { mainKey : "work" } ,
454+ } ) ,
455+ ) . toBe ( "agent:voice:work" ) ;
456+ }
457+ expect (
458+ resolveVoiceCallAgentSessionKey ( {
459+ config,
460+ sessionKey : "main" ,
461+ coreSession : { scope : "global" } ,
462+ } ) ,
463+ ) . toBe ( "global" ) ;
464+ expect (
465+ resolveVoiceCallAgentSessionKey ( {
466+ config,
467+ sessionKey : "agent:main:main" ,
468+ coreSession : { mainKey : "work" } ,
469+ } ) ,
470+ ) . toBe ( "agent:voice:agent:main:main" ) ;
471+ expect (
472+ resolveVoiceCallAgentSessionKey ( {
473+ config,
474+ sessionKey : "agent:main:main" ,
475+ coreSession : { scope : "global" } ,
332476 } ) ,
333- ) . toBe ( "meet-room-1 " ) ;
477+ ) . toBe ( "agent:voice:agent:main:main " ) ;
334478 } ) ;
335479
336480 it ( "resolves per-number inbound route overrides over global voice settings" , ( ) => {
@@ -395,6 +539,35 @@ describe("resolveVoiceCallConfig session routing", () => {
395539 expect ( effective . config ) . toBe ( config ) ;
396540 expect ( effective . config . inboundGreeting ) . toBe ( "Hello from global." ) ;
397541 } ) ;
542+
543+ it ( "uses dialed-number fallback only for inbound calls" , ( ) => {
544+ expect (
545+ resolveVoiceCallNumberRouteKeyForCall ( {
546+ direction : "inbound" ,
547+ to : "+15550001111" ,
548+ } ) ,
549+ ) . toBe ( "+15550001111" ) ;
550+ expect (
551+ resolveVoiceCallNumberRouteKeyForCall ( {
552+ direction : "outbound" ,
553+ to : "+15550001111" ,
554+ } ) ,
555+ ) . toBeUndefined ( ) ;
556+ expect (
557+ resolveVoiceCallNumberRouteKeyForCall ( {
558+ direction : "inbound" ,
559+ to : "+15550001111" ,
560+ metadata : { numberRouteKey : "+15550002222" } ,
561+ } ) ,
562+ ) . toBe ( "+15550002222" ) ;
563+ expect (
564+ resolveVoiceCallNumberRouteKeyForCall ( {
565+ direction : "outbound" ,
566+ to : "+15550001111" ,
567+ metadata : { numberRouteKey : "+15550002222" } ,
568+ } ) ,
569+ ) . toBeUndefined ( ) ;
570+ } ) ;
398571} ) ;
399572
400573describe ( "normalizeVoiceCallConfig" , ( ) => {
0 commit comments