@@ -26,7 +26,9 @@ import 'package:vide_cli/theme/theme.dart';
2626import 'package:vide_cli/components/vide_scaffold.dart' ;
2727
2828class NetworkExecutionPage extends StatefulComponent {
29- const NetworkExecutionPage ({super .key});
29+ final VideSession session;
30+
31+ const NetworkExecutionPage ({required this .session, super .key});
3032
3133 static Future <void > push (BuildContext context, {required VideSession session}) async {
3234 context.read (sessionSelectionProvider.notifier).selectSession (session);
@@ -39,7 +41,7 @@ class NetworkExecutionPage extends StatefulComponent {
3941
4042 return Navigator .of (
4143 context,
42- ).push <void >(PageRoute (builder: (context) => const NetworkExecutionPage (), settings: RouteSettings ()));
44+ ).push <void >(PageRoute (builder: (context) => NetworkExecutionPage (session : session ), settings: RouteSettings ()));
4345 }
4446
4547 @override
@@ -104,8 +106,9 @@ class _NetworkExecutionPageState extends State<NetworkExecutionPage> {
104106 required VoidCallback focusRightSidebar,
105107 }) {
106108 // Get selected agent ID from provider, or use the first agent
107- final selectedAgentIdNotifier = context.read (selectedAgentIdProvider.notifier);
108- final selectedAgentId = context.watch (selectedAgentIdProvider);
109+ final sessionId = component.session.id;
110+ final selectedAgentIdNotifier = context.read (selectedAgentIdProvider (sessionId).notifier);
111+ final selectedAgentId = context.watch (selectedAgentIdProvider (sessionId));
109112
110113 // Find the selected agent, or default to the first agent
111114 String agentId = selectedAgentId ?? (agentIds.isNotEmpty ? agentIds[0 ] : '' );
@@ -116,10 +119,11 @@ class _NetworkExecutionPageState extends State<NetworkExecutionPage> {
116119 selectedAgentIdNotifier.state = agentId;
117120 }
118121
119- final session = context. read (currentVideSessionProvider) ! ;
122+ final session = component.session ;
120123 return Expanded (
121124 child: _AgentChat (
122125 key: ValueKey (agentId),
126+ session: session,
123127 agentId: agentId,
124128 networkId: session.id,
125129 showQuitWarning: _showQuitWarning,
@@ -132,19 +136,16 @@ class _NetworkExecutionPageState extends State<NetworkExecutionPage> {
132136 }
133137
134138 Future <void > _exitWithDaemonCleanup () async {
135- final session = context.read (currentVideSessionProvider);
136- final sessionId = session? .id;
137- if (sessionId != null ) {
138- final daemonState = context.read (daemonConnectionProvider);
139- if (daemonState.isConnected) {
140- try {
141- await context
142- .read (daemonConnectionProvider.notifier)
143- .stopSession (sessionId)
144- .timeout (const Duration (seconds: 3 ));
145- } catch (_) {
146- // Best-effort — don't block exit if stop fails or times out.
147- }
139+ final sessionId = component.session.id;
140+ final daemonState = context.read (daemonConnectionProvider);
141+ if (daemonState.isConnected) {
142+ try {
143+ await context
144+ .read (daemonConnectionProvider.notifier)
145+ .stopSession (sessionId)
146+ .timeout (const Duration (seconds: 3 ));
147+ } catch (_) {
148+ // Best-effort — don't block exit if stop fails or times out.
148149 }
149150 }
150151 shutdownApp ();
@@ -181,9 +182,9 @@ class _NetworkExecutionPageState extends State<NetworkExecutionPage> {
181182 // Remote sessions start disconnected while the WebSocket connects.
182183 final connectionAsync = context.watch (sessionConnectionProvider);
183184 final isConnected = connectionAsync.valueOrNull ?? true ;
184- final session = context. watch (currentVideSessionProvider) ;
185+ final session = component.session ;
185186
186- if (session == null || ! isConnected) {
187+ if (! isConnected) {
187188 return _buildConnectingScreen (context, label: 'Connecting to session...' );
188189 }
189190
@@ -260,6 +261,7 @@ class _NetworkExecutionPageState extends State<NetworkExecutionPage> {
260261}
261262
262263class _AgentChat extends StatefulComponent {
264+ final VideSession session;
263265 final String agentId;
264266 final String networkId;
265267 final bool showQuitWarning;
@@ -269,6 +271,7 @@ class _AgentChat extends StatefulComponent {
269271 final VoidCallback focusRightSidebar;
270272
271273 const _AgentChat ({
274+ required this .session,
272275 required this .agentId,
273276 required this .networkId,
274277 required this .onExit,
@@ -309,15 +312,14 @@ class _AgentChatState extends State<_AgentChat> {
309312 _queuedMessage = queuedMessage;
310313 _model = model;
311314 });
312- context.read (currentModelProvider.notifier).state = model;
315+ context.read (currentModelProvider (component.session.id) .notifier).state = model;
313316 }
314317
315318 @override
316319 void initState () {
317320 super .initState ();
318321
319- final session = context.read (currentVideSessionProvider);
320- if (session == null ) return ;
322+ final session = component.session;
321323
322324 // Listen to conversation updates
323325 _conversationSubscription = session.conversationStream (component.agentId).listen ((conversation) {
@@ -338,7 +340,7 @@ class _AgentChatState extends State<_AgentChat> {
338340 // Listen to model updates
339341 _modelSubscription = session.modelStream (component.agentId).listen ((model) {
340342 setState (() => _model = model);
341- context.read (currentModelProvider.notifier).state = model;
343+ context.read (currentModelProvider (component.session.id) .notifier).state = model;
342344 });
343345
344346 unawaited (_loadInitialAgentRuntimeMetadata (session));
@@ -367,40 +369,37 @@ class _AgentChatState extends State<_AgentChat> {
367369 if (message.attachments != null && message.attachments! .isNotEmpty) {
368370 _sentAttachments[message.text] = message.attachments! ;
369371 }
370- final session = context.read (currentVideSessionProvider);
371- session? .sendMessage (message, agentId: component.agentId);
372+ component.session.sendMessage (message, agentId: component.agentId);
372373 }
373374
374375 bool _isLastAgent () {
375- final session = context.read (currentVideSessionProvider);
376- if (session == null ) return true ; // If no session, treat as last agent (safe default)
377- return session.state.agents.length <= 1 ;
376+ return component.session.state.agents.length <= 1 ;
378377 }
379378
380379 Future <void > _handleCommand (String commandInput) async {
381- final session = context. read (currentVideSessionProvider) ;
380+ final session = component.session ;
382381 final dispatcher = context.read (commandDispatcherProvider);
383382 final commandContext = CommandContext (
384383 agentId: component.agentId,
385- workingDirectory: session? .state.workingDirectory ?? '' ,
384+ workingDirectory: session.state.workingDirectory,
386385 isLastAgent: _isLastAgent (),
387386 sendMessage: (message) {
388- session? .sendMessage (AgentMessage (text: message), agentId: component.agentId);
387+ session.sendMessage (AgentMessage (text: message), agentId: component.agentId);
389388 },
390389 clearConversation: () async {
391- await session? .clearConversation (agentId: component.agentId);
390+ await session.clearConversation (agentId: component.agentId);
392391 setState (() {
393392 _conversation = null ;
394393 });
395394 },
396395 exitApp: component.onExit,
397396 detachApp: shutdownApp,
398397 forkAgent: (name) async {
399- final newAgentId = await session? .forkAgent (component.agentId, name: name);
400- return newAgentId ?? '' ;
398+ final newAgentId = await session.forkAgent (component.agentId, name: name);
399+ return newAgentId;
401400 },
402401 killAgent: () async {
403- await session? .terminateAgent (
402+ await session.terminateAgent (
404403 component.agentId,
405404 terminatedBy: component.agentId, // Self-termination
406405 reason: 'User invoked /kill command' ,
@@ -413,22 +412,21 @@ class _AgentChatState extends State<_AgentChat> {
413412 context,
414413 repoPath: repoPath,
415414 onSendMessage: (message) {
416- session? .sendMessage (AgentMessage (text: message), agentId: component.agentId);
415+ session.sendMessage (AgentMessage (text: message), agentId: component.agentId);
417416 },
418417 onSwitchWorktree: (path) async {
419418 final container = ProviderScope .containerOf (context);
420419 container.read (repoPathOverrideProvider.notifier).state = path;
421420 // Use VideSession.setWorktreePath() instead of direct provider access
422- await session? .setWorktreePath (path);
421+ await session.setWorktreePath (path);
423422 },
424423 );
425424 },
426425 showSettingsDialog: () async {
427426 await SettingsPopup .show (context);
428427 },
429428 showSessionLogs: () {
430- final sessionId = session? .id;
431- if (sessionId == null ) return ;
429+ final sessionId = session.id;
432430 final logPath = VideLogger .instance.sessionLogPath (sessionId);
433431 context.read (filePreviewPathProvider.notifier).state = logPath;
434432 },
@@ -467,9 +465,8 @@ class _AgentChatState extends State<_AgentChat> {
467465 }
468466
469467 Future <List <CommandSuggestion >> _getFileSuggestions (String query) async {
470- final session = context.read (currentVideSessionProvider);
471- final workingDir = session? .state.workingDirectory;
472- if (workingDir == null ) return [];
468+ final workingDir = component.session.state.workingDirectory;
469+ if (workingDir.isEmpty) return [];
473470
474471 final now = DateTime .now ();
475472 if (_cachedFileList == null ||
@@ -517,8 +514,6 @@ class _AgentChatState extends State<_AgentChat> {
517514 String ? patternOverride,
518515 String ? denyReason,
519516 }) {
520- final session = context.read (currentVideSessionProvider);
521-
522517 String reason;
523518 if (granted) {
524519 reason = 'User approved' ;
@@ -528,7 +523,7 @@ class _AgentChatState extends State<_AgentChat> {
528523 reason = 'User denied' ;
529524 }
530525
531- session? .respondToPermission (
526+ component. session.respondToPermission (
532527 request.requestId,
533528 allow: granted,
534529 message: reason,
@@ -541,34 +536,27 @@ class _AgentChatState extends State<_AgentChat> {
541536 }
542537
543538 void _handleAskUserQuestionResponse (AskUserQuestionUIRequest request, Map <String , String > answers) {
544- final session = context.read (currentVideSessionProvider);
545-
546539 // Send the response through the session (unified path for local and remote)
547- session? .respondToAskUserQuestion (request.requestId, answers: answers);
540+ component. session.respondToAskUserQuestion (request.requestId, answers: answers);
548541
549542 // Dequeue the current request to show the next one
550543 context.read (askUserQuestionStateProvider.notifier).dequeueRequest ();
551544 }
552545
553546 void _handlePlanApprovalResponse (PlanApprovalUIRequest request, String action, String ? feedback) {
554- final session = context.read (currentVideSessionProvider);
555-
556- session? .respondToPlanApproval (request.requestId, action: action, feedback: feedback);
547+ component.session.respondToPlanApproval (request.requestId, action: action, feedback: feedback);
557548
558549 // Dequeue the current request to show the next one
559550 context.read (planApprovalStateProvider.notifier).dequeueRequest ();
560551 }
561552
562553 void _handleEscape () {
563- final session = context.read (currentVideSessionProvider);
564- if (session == null ) return ;
565-
566554 // If there's a queued message, clear it first
567555 if (_queuedMessage != null ) {
568- unawaited (session.clearQueuedMessage (component.agentId));
556+ unawaited (component. session.clearQueuedMessage (component.agentId));
569557 } else {
570558 // Otherwise abort the current processing
571- session.abortAgent (component.agentId);
559+ component. session.abortAgent (component.agentId);
572560 }
573561 }
574562
@@ -624,8 +612,7 @@ class _AgentChatState extends State<_AgentChat> {
624612 itemBuilder: (context, index) {
625613 final messageIndex = index;
626614 final message = filteredMessages[messageIndex];
627- final session = context.read (currentVideSessionProvider);
628- final workingDir = session? .state.workingDirectory ?? '' ;
615+ final workingDir = component.session.state.workingDirectory;
629616
630617 // Render system-like user messages (e.g. "[Request interrupted by user]")
631618 // as dimmed inline text instead of a full user message bubble.
@@ -727,10 +714,7 @@ class _AgentChatState extends State<_AgentChat> {
727714 model: _model,
728715 maxDialogHeight: maxDialogHeight,
729716 onClearQueue: () {
730- final session = context.read (currentVideSessionProvider);
731- if (session != null ) {
732- unawaited (session.clearQueuedMessage (component.agentId));
733- }
717+ unawaited (component.session.clearQueuedMessage (component.agentId));
734718 },
735719 onSendMessage: _sendMessage,
736720 onCommand: _handleCommand,
0 commit comments