g | x | w | all
Bytes Lang Time Link
408Python3230720T180945ZAjax1234
174JavaScript ES6190426T150238ZArnauld

Python3, 408 bytes:

E=enumerate;P=print
def f(m):
 q,d=[(x,y)for x,r in E(m)for y,s in E(r)if'S'==s],{}
 while q:
  x,y=q.pop(0)
  for M,X,Y in[('up',-1,0),('down',1,0),('left',0,-1),('right',0,1)]:
   if 0<=(j:=x+X)<len(m)and 0<=(k:=y+Y)<len(m[0])and(j,k)not in(V:=d.get((x,y),[])):
    C=m[j][k]
    if'X'==C:P(M+' solved');return
    elif'#'==C:P(M+' wall')
    else:print(M+' ok');q+=[(j,k)];d[(x,y)]=V+[(j,k)]
 P('no exit')

Try it online!

JavaScript (ES6),  180  174 bytes

Uses prompt() to output the direction and retrieve the result.

_=>(g=p=>[...'012301234'].some((d,i)=>g[p]>>d&1|i<4&g[P=[p[0]+(d-2)%2,p[1]+~-d%2]]>0?0:(r=prompt('up/left/down/right/no exit'.split`/`[g[p]|=1<<d,d]))<'s'?g(P):r<'t'))([0,0])

Try it online! (with automated I/O)

Interactive snippet

WARNING: this code will display a prompt() dialog until 'solved' is entered or the function figures out that there's no exit at all.

(
_=>(g=p=>[...'012301234'].some((d,i)=>g[p]>>d&1|i<4&g[P=[p[0]+(d-2)%2,p[1]+~-d%2]]>0?0:(r=prompt('up/left/down/right/no exit'.split`/`[g[p]|=1<<d,d]))<'s'?g(P):r<'t'))([0,0])
)()

Commented

_ => (                      // anonymous function taking no argument
  g = p =>                  // g = recursive function taking the current position p = [x, y]
    [ ...'0123',            // i<4  : try to move on squares that haven't been visited yet
      ...'0123',            // 3<i<8: try to go back to where we initially came from
      ...'4'                // i=8  : if everything failed, there must be no exit
    ].some((d, i) =>        // for each direction d at index i:
      g[p] >> d & 1         //   if this direction was already tried at this position
      | i < 4 &             //   or i is less than 4 and
      g[P = [               //   the square at the new position P = [X, Y] with:
        p[0] + (d - 2) % 2, //     X = x + dx[d]
        p[1] + ~-d % 2      //     Y = y + dy[d]
      ]] > 0 ?              //   was already visited:
        0                   //     abort
      : (                   //   else:
        r = prompt(         //     output the direction:
          [ 'up',           //       0 = up
            'left',         //       1 = left
            'down',         //       2 = down
            'right',        //       3 = right
            'no exit'       //       4 = no exit
          ][                //
            g[p] |= 1 << d, //       mark this direction as used
            d               //       d = actual index of the string to output
          ]                 //     r = result of prompt()
        )                   //
      ) < 's' ?             //     if r = 'ok':
        g(P)                //       do a recursive call at the new position
      :                     //     else:
        r < 't'             //       yield true if r = 'solved' or false if r = 'wall'
    )                       // end of some()
)([0, 0])                   // initial call to g at (0, 0)