@@ -464,6 +464,74 @@ describe("gateway server chat", () => {
464464 } ) ;
465465 } ) ;
466466
467+ test ( "chat.startup does not wait for slow optional model catalog metadata" , async ( ) => {
468+ const sessionDir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , "openclaw-gw-" ) ) ;
469+ try {
470+ testState . sessionStorePath = path . join ( sessionDir , "sessions.json" ) ;
471+ await writeSessionStore ( {
472+ entries : {
473+ main : {
474+ sessionId : "sess-main" ,
475+ modelProvider : "test-provider" ,
476+ model : "slow-catalog-model" ,
477+ updatedAt : Date . now ( ) ,
478+ } ,
479+ } ,
480+ } ) ;
481+ const catalog =
482+ createDeferred < Awaited < ReturnType < GatewayRequestContext [ "loadGatewayModelCatalog" ] > > > ( ) ;
483+ const responses : Array < { ok : boolean ; payload ?: unknown ; error ?: unknown } > = [ ] ;
484+ const context = {
485+ loadGatewayModelCatalog : vi
486+ . fn < GatewayRequestContext [ "loadGatewayModelCatalog" ] > ( )
487+ . mockReturnValue ( catalog . promise ) ,
488+ logGateway : {
489+ info : vi . fn ( ) ,
490+ warn : vi . fn ( ) ,
491+ error : vi . fn ( ) ,
492+ debug : vi . fn ( ) ,
493+ } ,
494+ chatAbortControllers : new Map ( ) ,
495+ chatRunBuffers : new Map ( ) ,
496+ getRuntimeConfig : ( ) => ( { } ) ,
497+ } as unknown as GatewayRequestContext ;
498+ const { chatHandlers } = await import ( "./server-methods/chat.js" ) ;
499+
500+ await chatHandlers [ "chat.startup" ] ( {
501+ req : {
502+ type : "req" ,
503+ id : "startup-slow-catalog" ,
504+ method : "chat.startup" ,
505+ params : { sessionKey : "main" } ,
506+ } ,
507+ params : { sessionKey : "main" } ,
508+ client : null ,
509+ isWebchatConnect : ( ) => false ,
510+ respond : ( ( ok , payload , error ) => {
511+ responses . push ( { ok, payload, error } ) ;
512+ } ) as RespondFn ,
513+ context,
514+ } ) ;
515+
516+ expect ( context . loadGatewayModelCatalog ) . toHaveBeenCalledTimes ( 1 ) ;
517+ expect ( responses ) . toHaveLength ( 1 ) ;
518+ expect ( responses [ 0 ] ?. ok ) . toBe ( true ) ;
519+ const payload = responses [ 0 ] ?. payload as
520+ | {
521+ agentsList ?: { agents ?: Array < { id ?: string } > } ;
522+ metadata ?: unknown ;
523+ sessionInfo ?: { sessionId ?: string } ;
524+ }
525+ | undefined ;
526+ expect ( payload ?. sessionInfo ?. sessionId ) . toBe ( "sess-main" ) ;
527+ expect ( payload ?. agentsList ?. agents ?. map ( ( agent ) => agent . id ) ) . toContain ( "main" ) ;
528+ expect ( payload ?. metadata ) . toBeUndefined ( ) ;
529+ } finally {
530+ testState . sessionStorePath = undefined ;
531+ await fs . rm ( sessionDir , { recursive : true , force : true , maxRetries : 5 , retryDelay : 50 } ) ;
532+ }
533+ } ) ;
534+
467535 test ( "chat.startup omits metadata when configured model visibility needs full discovery" , async ( ) => {
468536 await withGatewayChatHarness ( async ( { ws } ) => {
469537 await writeGatewayConfig ( {
@@ -497,8 +565,18 @@ describe("gateway server chat", () => {
497565 } ) ;
498566
499567 test ( "chat.startup scopes metadata to agent session keys without explicit agentId" , async ( ) => {
500- await withGatewayChatHarness ( async ( { ws } ) => {
501- await writeGatewayConfig ( {
568+ const sessionDir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , "openclaw-gw-" ) ) ;
569+ try {
570+ testState . sessionStorePath = path . join ( sessionDir , "sessions.json" ) ;
571+ await writeSessionStore ( {
572+ entries : {
573+ "agent:work:main" : {
574+ sessionId : "sess-work" ,
575+ updatedAt : Date . now ( ) ,
576+ } ,
577+ } ,
578+ } ) ;
579+ const config = {
502580 agents : {
503581 defaults : {
504582 model : {
@@ -515,6 +593,9 @@ describe("gateway server chat", () => {
515593 model : {
516594 primary : "minimax/MiniMax-M2.7-highspeed" ,
517595 } ,
596+ models : {
597+ "minimax/MiniMax-M2.7-highspeed" : { } ,
598+ } ,
518599 } ,
519600 ] ,
520601 } ,
@@ -530,25 +611,85 @@ describe("gateway server chat", () => {
530611 } ,
531612 } ,
532613 } ,
533- } ) ;
534- await connectOk ( ws ) ;
614+ } ;
615+ await writeGatewayConfig ( config ) ;
616+ const responses : Array < { ok : boolean ; payload ?: unknown ; error ?: unknown } > = [ ] ;
617+ const context = {
618+ loadGatewayModelCatalog : vi
619+ . fn < GatewayRequestContext [ "loadGatewayModelCatalog" ] > ( )
620+ . mockImplementation ( async ( ) => {
621+ await Promise . resolve ( ) ;
622+ await Promise . resolve ( ) ;
623+ return [
624+ {
625+ id : "gpt-main" ,
626+ name : "GPT Main" ,
627+ provider : "openai" ,
628+ input : [ "text" ] ,
629+ } ,
630+ {
631+ id : "MiniMax-M2.7-highspeed" ,
632+ name : "MiniMax M2.7 Highspeed" ,
633+ provider : "minimax" ,
634+ input : [ "text" ] ,
635+ } ,
636+ ] ;
637+ } ) ,
638+ logGateway : {
639+ info : vi . fn ( ) ,
640+ warn : vi . fn ( ) ,
641+ error : vi . fn ( ) ,
642+ debug : vi . fn ( ) ,
643+ } ,
644+ chatAbortControllers : new Map ( ) ,
645+ chatRunBuffers : new Map ( ) ,
646+ getRuntimeConfig : ( ) => config ,
647+ } as unknown as GatewayRequestContext ;
648+ const { chatHandlers } = await import ( "./server-methods/chat.js" ) ;
535649
536- const startup = await rpcReq < {
537- metadata ?: {
538- models ?: Array < { id ?: string ; provider ?: string } > ;
539- } ;
540- } > ( ws , "chat.startup" , { sessionKey : "agent:work:main" } ) ;
650+ await chatHandlers [ "chat.startup" ] ( {
651+ req : {
652+ type : "req" ,
653+ id : "startup-agent-scoped-metadata" ,
654+ method : "chat.startup" ,
655+ params : { sessionKey : "agent:work:main" } ,
656+ } ,
657+ params : { sessionKey : "agent:work:main" } ,
658+ client : null ,
659+ isWebchatConnect : ( ) => false ,
660+ respond : ( ( ok , payload , error ) => {
661+ responses . push ( { ok, payload, error } ) ;
662+ } ) as RespondFn ,
663+ context,
664+ } ) ;
541665
542- expect ( startup . ok ) . toBe ( true ) ;
543- expect ( startup . payload ?. metadata ?. models ) . toEqual (
666+ expect ( context . loadGatewayModelCatalog ) . toHaveBeenCalledTimes ( 1 ) ;
667+ expect ( responses ) . toHaveLength ( 1 ) ;
668+ expect ( responses [ 0 ] ?. ok ) . toBe ( true ) ;
669+ const payload = responses [ 0 ] ?. payload as
670+ | {
671+ metadata ?: {
672+ models ?: Array < { id ?: string ; provider ?: string } > ;
673+ } ;
674+ sessionInfo ?: { key ?: string ; sessionId ?: string } ;
675+ }
676+ | undefined ;
677+ expect ( payload ?. sessionInfo ) . toMatchObject ( {
678+ key : "agent:work:main" ,
679+ sessionId : "sess-work" ,
680+ } ) ;
681+ expect ( payload ?. metadata ?. models ) . toEqual (
544682 expect . arrayContaining ( [
545683 expect . objectContaining ( {
546684 id : "MiniMax-M2.7-highspeed" ,
547685 provider : "minimax" ,
548686 } ) ,
549687 ] ) ,
550688 ) ;
551- } ) ;
689+ } finally {
690+ testState . sessionStorePath = undefined ;
691+ await fs . rm ( sessionDir , { recursive : true , force : true , maxRetries : 5 , retryDelay : 50 } ) ;
692+ }
552693 } ) ;
553694
554695 test ( "chat.metadata coalesces configured models and text commands" , async ( ) => {
0 commit comments