Compare commits

...

3 Commits

Author SHA1 Message Date
jif-oai
4fedb8a88b feat: drop unified exec end event after the end of the turn 2025-12-15 11:20:31 +01:00
jif-oai
2cd7a5efed Merge remote-tracking branch 'origin/main' into jif/ue-cleaning 2025-12-15 09:14:22 +01:00
jif-oai
7d6bc575af feat: close unified_exec at end of turn 2025-12-13 12:42:45 +01:00
4 changed files with 35 additions and 2 deletions

View File

@@ -159,6 +159,7 @@ impl Session {
for task in self.take_all_running_tasks().await {
self.handle_task_abort(task, reason.clone()).await;
}
self.close_unified_exec_sessions().await;
}
pub async fn on_task_finished(
@@ -167,12 +168,18 @@ impl Session {
last_agent_message: Option<String>,
) {
let mut active = self.active_turn.lock().await;
if let Some(at) = active.as_mut()
let should_close_sessions = if let Some(at) = active.as_mut()
&& at.remove_task(&turn_context.sub_id)
{
*active = None;
}
true
} else {
false
};
drop(active);
if should_close_sessions {
self.close_unified_exec_sessions().await;
}
let event = EventMsg::TaskComplete(TaskCompleteEvent { last_agent_message });
self.send_event(turn_context.as_ref(), event).await;
}
@@ -196,6 +203,13 @@ impl Session {
}
}
async fn close_unified_exec_sessions(&self) {
self.services
.unified_exec_manager
.close_open_sessions()
.await;
}
async fn handle_task_abort(self: &Arc<Self>, task: RunningTask, reason: TurnAbortReason) {
let sub_id = task.turn_context.sub_id.clone();
if task.cancellation_token.is_cancelled() {

View File

@@ -643,6 +643,11 @@ impl UnifiedExecSessionManager {
entry.session.terminate();
}
}
pub(crate) async fn close_open_sessions(&self) {
// Terminating sessions ensures the exit watcher emits ExecCommandEnd events.
self.terminate_all_sessions().await;
}
}
enum SessionStatus {

View File

@@ -300,6 +300,7 @@ pub(crate) struct ChatWidget {
suppressed_exec_calls: HashSet<String>,
last_unified_wait: Option<UnifiedExecWaitState>,
task_complete_pending: bool,
suppress_late_exec_ends: bool,
mcp_startup_status: Option<HashMap<String, McpStartupStatus>>,
// Queue of interruptive UI events deferred during an active write cycle
interrupts: InterruptManager,
@@ -520,6 +521,7 @@ impl ChatWidget {
self.bottom_pane.clear_ctrl_c_quit_hint();
self.bottom_pane.set_task_running(true);
self.retry_status_header = None;
self.suppress_late_exec_ends = false;
self.bottom_pane.set_interrupt_hint_visible(true);
self.set_status_header(String::from("Working"));
self.full_reasoning_buffer.clear();
@@ -535,6 +537,7 @@ impl ChatWidget {
self.running_commands.clear();
self.suppressed_exec_calls.clear();
self.last_unified_wait = None;
self.suppress_late_exec_ends = true;
self.request_redraw();
// If there is a queued user message, send exactly one now to begin the next turn.
@@ -1038,6 +1041,14 @@ impl ChatWidget {
if self.suppressed_exec_calls.remove(&ev.call_id) {
return;
}
if self.suppress_late_exec_ends
&& matches!(
ev.source,
ExecCommandSource::UnifiedExecStartup | ExecCommandSource::UnifiedExecInteraction
)
{
return;
}
let (command, parsed, source) = match running {
Some(rc) => (rc.command, rc.parsed_cmd, rc.source),
None => (ev.command.clone(), ev.parsed_cmd.clone(), ev.source),
@@ -1319,6 +1330,7 @@ impl ChatWidget {
suppressed_exec_calls: HashSet::new(),
last_unified_wait: None,
task_complete_pending: false,
suppress_late_exec_ends: false,
mcp_startup_status: None,
interrupts: InterruptManager::new(),
reasoning_buffer: String::new(),
@@ -1404,6 +1416,7 @@ impl ChatWidget {
suppressed_exec_calls: HashSet::new(),
last_unified_wait: None,
task_complete_pending: false,
suppress_late_exec_ends: false,
mcp_startup_status: None,
interrupts: InterruptManager::new(),
reasoning_buffer: String::new(),

View File

@@ -426,6 +426,7 @@ fn make_chatwidget_manual(
suppressed_exec_calls: HashSet::new(),
last_unified_wait: None,
task_complete_pending: false,
suppress_late_exec_ends: false,
mcp_startup_status: None,
interrupts: InterruptManager::new(),
reasoning_buffer: String::new(),