fix(summon): stop MOIM from telling models to sleep while waiting for tasks (#7377)

Signed-off-by: rabi <ramishra@redhat.com>
This commit is contained in:
Rabi Mishra
2026-02-20 18:34:09 +05:30
committed by GitHub
parent 93f123280e
commit c8c8a1237c
@@ -330,10 +330,12 @@ impl SummonClient {
"Load knowledge into your current context or discover available sources.\n\n\ "Load knowledge into your current context or discover available sources.\n\n\
Call with no arguments to list all available sources (subrecipes, recipes, skills, agents).\n\ Call with no arguments to list all available sources (subrecipes, recipes, skills, agents).\n\
Call with a source name to load its content into your context.\n\ Call with a source name to load its content into your context.\n\
For background tasks: load(source: \"task_id\", cancel: true) stops and returns output.\n\n\ For background tasks: load(source: \"task_id\") waits for the task and returns the result.\n\
To cancel a running task: load(source: \"task_id\", cancel: true) stops and returns output.\n\n\
Examples:\n\ Examples:\n\
- load() → Lists available sources\n\ - load() → Lists available sources\n\
- load(source: \"rust-patterns\") → Loads the rust-patterns skill" - load(source: \"rust-patterns\") → Loads the rust-patterns skill\n\
- load(source: \"20260219_1\") → Waits for background task, then returns result"
.to_string(), .to_string(),
schema.as_object().unwrap().clone(), schema.as_object().unwrap().clone(),
) )
@@ -880,7 +882,7 @@ impl SummonClient {
// Wait for the running task to complete, keeping the tool call // Wait for the running task to complete, keeping the tool call
// alive so notifications (subagent tool calls) stream in real time. // alive so notifications (subagent tool calls) stream in real time.
let task = running.remove(task_id).unwrap(); let mut task = running.remove(task_id).unwrap();
drop(running); drop(running);
let buffered = { let buffered = {
@@ -896,45 +898,37 @@ impl SummonClient {
} }
} }
let description = task.description.clone(); tokio::select! {
let mut handle = task.handle; result = &mut task.handle => {
let output = match result {
let (output, timed_out) = tokio::select! {
result = &mut handle => {
let s = match result {
Ok(Ok(s)) => s, Ok(Ok(s)) => s,
Ok(Err(e)) => format!("Error: {}", e), Ok(Err(e)) => format!("Error: {}", e),
Err(e) => format!("Task panicked: {}", e), Err(e) => format!("Task panicked: {}", e),
}; };
(s, false)
return Ok(vec![Content::text(format!(
"# Background Task Result: {}\n\n\
**Task:** {}\n\
**Status:** ✓ Completed\n\
**Duration:** {} ({} turns)\n\n\
## Output\n\n{}",
task_id,
task.description,
round_duration(task.started_at.elapsed()),
task.turns.load(Ordering::Relaxed),
output
))]);
} }
_ = tokio::time::sleep(Duration::from_secs(300)) => { _ = tokio::time::sleep(Duration::from_secs(300)) => {
handle.abort(); self.background_tasks.lock().await.insert(task_id.to_string(), task);
("Task timed out waiting for completion (aborted after 5 min)".to_string(), true)
return Err(format!(
"Task '{task_id}' is still running after waiting 5 min. \
Use load(source: \"{task_id}\") to wait again, or \
load(source: \"{task_id}\", cancel: true) to stop."
));
} }
}; }
let duration = task.started_at.elapsed();
let turns_taken = task.turns.load(Ordering::Relaxed);
let status = if timed_out {
"⏱ Timed out"
} else {
"✓ Completed"
};
return Ok(vec![Content::text(format!(
"# Background Task Result: {}\n\n\
**Task:** {}\n\
**Status:** {}\n\
**Duration:** {} ({} turns)\n\n\
## Output\n\n{}",
task_id,
description,
status,
round_duration(duration),
turns_taken,
output
))]);
} }
Err(format!("Task '{}' not found.", task_id)) Err(format!("Task '{}' not found.", task_id))
@@ -1628,7 +1622,8 @@ impl SummonClient {
.insert(task_id.clone(), task); .insert(task_id.clone(), task);
Ok(vec![Content::text(format!( Ok(vec![Content::text(format!(
"Task {} started in background: \"{}\"\nUse load(source: \"{}\") to wait for the result (it will block until complete).", "Task {} started in background: \"{}\"\n\
Continue with other work. When you need the result, use load(source: \"{}\").",
task_id, description, task_id task_id, description, task_id
))]) ))])
} }
@@ -1714,8 +1709,6 @@ impl McpClientTrait for SummonClient {
let mut lines = vec!["Background tasks:".to_string()]; let mut lines = vec!["Background tasks:".to_string()];
let now = current_epoch_millis(); let now = current_epoch_millis();
let mut shortest_elapsed_secs: Option<u64> = None;
let mut sorted_running: Vec<_> = running.values().collect(); let mut sorted_running: Vec<_> = running.values().collect();
sorted_running.sort_by_key(|t| &t.id); sorted_running.sort_by_key(|t| &t.id);
@@ -1723,10 +1716,6 @@ impl McpClientTrait for SummonClient {
let elapsed = task.started_at.elapsed(); let elapsed = task.started_at.elapsed();
let idle_ms = now.saturating_sub(task.last_activity.load(Ordering::Relaxed)); let idle_ms = now.saturating_sub(task.last_activity.load(Ordering::Relaxed));
let elapsed_secs = elapsed.as_secs();
shortest_elapsed_secs =
Some(shortest_elapsed_secs.map_or(elapsed_secs, |s| s.min(elapsed_secs)));
lines.push(format!( lines.push(format!(
"{}: \"{}\" - running {}, {} turns, idle {}", "{}: \"{}\" - running {}, {} turns, idle {}",
task.id, task.id,
@@ -1757,12 +1746,11 @@ impl McpClientTrait for SummonClient {
)); ));
} }
if let Some(shortest) = shortest_elapsed_secs { if !running.is_empty() {
let sleep_secs = 300u64.saturating_sub(shortest).max(10); lines.push(
lines.push(format!( "\n→ Use load(source: \"<id>\") to wait for a task, or load(source: \"<id>\", cancel: true) to stop it"
"\n→ sleep {} to wait, or load(source: \"id\", cancel: true) to stop", .to_string(),
sleep_secs );
));
} }
Some(lines.join("\n")) Some(lines.join("\n"))