@@ -26,6 +26,9 @@ class VideSdkState extends ChangeNotifier {
2626 VideSdkConnectionState _connectionState = VideSdkConnectionState .disconnected;
2727 String ? _errorMessage;
2828 StreamSubscription <VideEvent >? _eventSubscription;
29+ PlanApprovalRequestEvent ? _pendingPlanApproval;
30+ AskUserQuestionEvent ? _pendingAskUserQuestion;
31+ final List <PermissionRequestEvent > _pendingPermissions = [];
2932
3033 VideSdkState ({String ? host, int ? port, String ? workingDirectory})
3134 : _host = host,
@@ -44,18 +47,96 @@ class VideSdkState extends ChangeNotifier {
4447
4548 VideSdkConnectionState get connectionState => _connectionState;
4649 String ? get errorMessage => _errorMessage;
50+
51+ /// A [VideClient] for API calls (filesystem, git, etc.).
52+ ///
53+ /// Available whenever host and port are configured, regardless of session
54+ /// state.
55+ VideClient ? get client =>
56+ _host != null && _port != null ? VideClient (host: _host! , port: _port! ) : null ;
4757 RemoteVideSession ? get session => _session;
4858 VideState ? get videState => _session? .state;
4959 bool get hasActiveSession =>
5060 _session != null && _connectionState == VideSdkConnectionState .connected;
5161
62+ PlanApprovalRequestEvent ? get pendingPlanApproval => _pendingPlanApproval;
63+ AskUserQuestionEvent ? get pendingAskUserQuestion => _pendingAskUserQuestion;
64+ PermissionRequestEvent ? get currentPermission => _pendingPermissions.firstOrNull;
65+
66+ void dequeuePermission () {
67+ if (_pendingPermissions.isNotEmpty) {
68+ _pendingPermissions.removeAt (0 );
69+ notifyListeners ();
70+ }
71+ }
72+
73+ void _removePermissionByRequestId (String requestId) {
74+ _pendingPermissions.removeWhere ((r) => r.requestId == requestId);
75+ notifyListeners ();
76+ }
77+
78+ /// Whether the server health check has passed (server is reachable).
79+ /// null = not checked yet, true = reachable, false = unreachable.
80+ bool ? _serverReachable;
81+ bool ? get serverReachable => _serverReachable;
82+
83+ /// Sessions from the server filtered to the configured working directory.
84+ List <SessionSummary > _sessions = [];
85+ List <SessionSummary > get sessions => _sessions;
86+ bool _sessionsFetched = false ;
87+ bool get sessionsFetched => _sessionsFetched;
88+
5289 /// Load persisted configuration from shared preferences.
90+ ///
91+ /// Also kicks off a background health check if configured.
5392 Future <void > loadConfig () async {
5493 final prefs = await SharedPreferences .getInstance ();
5594 _host ?? = prefs.getString (_kHostKey);
5695 _port ?? = prefs.getInt (_kPortKey);
5796 _workingDirectory ?? = prefs.getString (_kWorkingDirKey);
5897 notifyListeners ();
98+
99+ if (isConfigured) {
100+ unawaited (checkServerHealth ());
101+ }
102+ }
103+
104+ /// Check if the configured server is reachable.
105+ ///
106+ /// If reachable, also fetches the session list for the working directory.
107+ Future <void > checkServerHealth () async {
108+ if (_host == null || _port == null ) return ;
109+
110+ _serverReachable = null ;
111+ notifyListeners ();
112+
113+ final reachable = await testConnection (host: _host! , port: _port! );
114+ _serverReachable = reachable;
115+ notifyListeners ();
116+
117+ if (reachable) {
118+ unawaited (fetchSessions ());
119+ }
120+ }
121+
122+ /// Fetch sessions from the server filtered to the configured working directory.
123+ Future <void > fetchSessions () async {
124+ if (_host == null || _port == null || _workingDirectory == null ) return ;
125+
126+ final client = VideClient (host: _host! , port: _port! );
127+ try {
128+ final all = await client.listSessions ();
129+ _sessions = all
130+ .where ((s) =>
131+ s.workingDirectory == _workingDirectory &&
132+ s.state == 'ready' )
133+ .toList ()
134+ ..sort ((a, b) => b.createdAt.compareTo (a.createdAt));
135+ _sessionsFetched = true ;
136+ notifyListeners ();
137+ } catch (_) {
138+ // Non-critical — leave list empty
139+ }
59140 }
60141
61142 /// Update and persist configuration.
@@ -75,7 +156,10 @@ class VideSdkState extends ChangeNotifier {
75156
76157 // Disconnect any existing session since config changed
77158 await disconnect ();
159+ _serverReachable = null ;
78160 notifyListeners ();
161+
162+ unawaited (checkServerHealth ());
79163 }
80164
81165 /// Test connection to a server by checking its health endpoint.
@@ -147,7 +231,28 @@ class VideSdkState extends ChangeNotifier {
147231
148232 void _setupEventListening () {
149233 _eventSubscription? .cancel ();
150- _eventSubscription = _session? .events.listen ((_) {
234+ _eventSubscription = _session? .events.listen ((event) {
235+ switch (event) {
236+ case PermissionRequestEvent ():
237+ _pendingPermissions.add (event);
238+ case PermissionResolvedEvent (: final requestId):
239+ _removePermissionByRequestId (requestId);
240+ return ; // already notified in helper
241+ case PlanApprovalRequestEvent ():
242+ _pendingPlanApproval = event;
243+ case PlanApprovalResolvedEvent (: final requestId):
244+ if (_pendingPlanApproval? .requestId == requestId) {
245+ _pendingPlanApproval = null ;
246+ }
247+ case AskUserQuestionEvent ():
248+ _pendingAskUserQuestion = event;
249+ case AskUserQuestionResolvedEvent (: final requestId):
250+ if (_pendingAskUserQuestion? .requestId == requestId) {
251+ _pendingAskUserQuestion = null ;
252+ }
253+ default :
254+ break ;
255+ }
151256 notifyListeners ();
152257 });
153258
@@ -163,8 +268,12 @@ class VideSdkState extends ChangeNotifier {
163268 }
164269
165270 /// Respond to a permission request.
166- void respondToPermission (String requestId, {required bool allow}) {
167- _session? .respondToPermission (requestId, allow: allow);
271+ void respondToPermission (
272+ String requestId, {
273+ required bool allow,
274+ bool remember = false ,
275+ }) {
276+ _session? .respondToPermission (requestId, allow: allow, remember: remember);
168277 }
169278
170279 /// Respond to a plan approval request.
@@ -180,6 +289,14 @@ class VideSdkState extends ChangeNotifier {
180289 );
181290 }
182291
292+ /// Respond to an AskUserQuestion request.
293+ void respondToAskUserQuestion (
294+ String requestId, {
295+ required Map <String , String > answers,
296+ }) {
297+ _session? .respondToAskUserQuestion (requestId, answers: answers);
298+ }
299+
183300 /// Abort the current session.
184301 Future <void > abort () async {
185302 await _session? .abort ();
@@ -194,7 +311,13 @@ class VideSdkState extends ChangeNotifier {
194311 _client = null ;
195312 _connectionState = VideSdkConnectionState .disconnected;
196313 _errorMessage = null ;
314+ _pendingPlanApproval = null ;
315+ _pendingAskUserQuestion = null ;
316+ _pendingPermissions.clear ();
197317 notifyListeners ();
318+
319+ // Refresh session list so the disconnected session appears under "Recent".
320+ unawaited (fetchSessions ());
198321 }
199322
200323 @override
0 commit comments