@@ -1046,6 +1046,79 @@ Gather user input for platform and projectName.
10461046 expect ( threadId ) . toBe ( existingThreadId ) ;
10471047 } ) ;
10481048
1049+ it ( 'should generate a new thread when extractWorkflowStateData returns undefined' , async ( ) => {
1050+ // Define a schema where sessionState is truly optional (no .default())
1051+ const OPTIONAL_STATE_SCHEMA = z . object ( {
1052+ payload : z . unknown ( ) . optional ( ) ,
1053+ sessionState : z
1054+ . object ( {
1055+ thread_id : z . string ( ) ,
1056+ } )
1057+ . optional ( ) ,
1058+ } ) ;
1059+
1060+ type OptionalStateInput = z . infer < typeof OPTIONAL_STATE_SCHEMA > ;
1061+
1062+ class OptionalStateOrchestrator extends OrchestratorTool < typeof OPTIONAL_STATE_SCHEMA > {
1063+ constructor (
1064+ mcpServer : McpServer ,
1065+ config : OrchestratorConfig < typeof OPTIONAL_STATE_SCHEMA >
1066+ ) {
1067+ super ( mcpServer , config ) ;
1068+ }
1069+
1070+ protected extractUserInput ( input : OptionalStateInput ) : unknown | undefined {
1071+ return input . payload ;
1072+ }
1073+
1074+ protected extractWorkflowStateData (
1075+ input : OptionalStateInput
1076+ ) : { thread_id : string } | undefined {
1077+ return input . sessionState ;
1078+ }
1079+ }
1080+
1081+ const workflow = new StateGraph ( TestState )
1082+ . addNode ( 'start' , ( _state : State ) => ( {
1083+ messages : [ 'Started workflow' ] ,
1084+ } ) )
1085+ . addEdge ( START , 'start' )
1086+ . addEdge ( 'start' , END ) ;
1087+
1088+ const config : OrchestratorConfig < typeof OPTIONAL_STATE_SCHEMA > = {
1089+ toolId : 'optional-state-orchestrator' ,
1090+ title : 'Optional State Orchestrator' ,
1091+ description : 'Tests undefined extractWorkflowStateData' ,
1092+ workflow,
1093+ inputSchema : OPTIONAL_STATE_SCHEMA ,
1094+ stateManager : new WorkflowStateManager ( { environment : 'test' } ) ,
1095+ logger : mockLogger ,
1096+ } ;
1097+
1098+ const orchestrator = new OptionalStateOrchestrator ( server , config ) ;
1099+
1100+ // Omit sessionState entirely — extractWorkflowStateData should return undefined
1101+ const result = await orchestrator . handleRequest (
1102+ { payload : { test : 'data' } } ,
1103+ createMockExtra ( )
1104+ ) ;
1105+
1106+ expect ( result ) . toBeDefined ( ) ;
1107+ expect ( result . content ) . toBeDefined ( ) ;
1108+
1109+ // Should have started a new workflow (not a resumption)
1110+ expect ( mockLogger . hasLoggedMessage ( 'Starting new workflow execution' , 'info' ) ) . toBe ( true ) ;
1111+
1112+ // The generated thread ID should follow the mmw- prefix convention
1113+ const processingLog = mockLogger . logs . find ( log =>
1114+ log . message . includes ( 'Processing orchestrator request' )
1115+ ) ;
1116+ expect ( processingLog ) . toBeDefined ( ) ;
1117+ const threadId = ( processingLog ?. data as { threadId ?: string } ) ?. threadId ;
1118+ expect ( threadId ) . toBeDefined ( ) ;
1119+ expect ( threadId ) . toMatch ( / ^ m m w - / ) ;
1120+ } ) ;
1121+
10491122 it ( 'should handle interrupt/resume cycle with custom schema' , async ( ) => {
10501123 const mockFs = new MockFileSystem ( ) ;
10511124 const testProjectPath = '/test/project' ;
0 commit comments