Skip to content

Commit f849c36

Browse files
committed
fix(summon): stop MOIM from telling models to sleep while waiting for tasks
The MOIM hint for running background tasks injected "sleep N to wait" into the conversation, causing some models to call `developer__shell: sleep N` which times out or wastes the entire turn. Replace with guidance to use load() which waits for the task result. Also stop load() from killing async delegate tasks after 300s — return the task to the background and let the model decide whether to wait again or cancel. Change-Id: Idc3e9100d5f487798c10991ec8672dc4b0a75a10 Signed-off-by: rabi <ramishra@redhat.com>
1 parent c324cd3 commit f849c36

1 file changed

Lines changed: 36 additions & 48 deletions

File tree

  • crates/goose/src/agents/platform_extensions

crates/goose/src/agents/platform_extensions/summon.rs

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,12 @@ impl SummonClient {
330330
"Load knowledge into your current context or discover available sources.\n\n\
331331
Call with no arguments to list all available sources (subrecipes, recipes, skills, agents).\n\
332332
Call with a source name to load its content into your context.\n\
333-
For background tasks: load(source: \"task_id\", cancel: true) stops and returns output.\n\n\
333+
For background tasks: load(source: \"task_id\") waits for the task and returns the result.\n\
334+
To cancel a running task: load(source: \"task_id\", cancel: true) stops and returns output.\n\n\
334335
Examples:\n\
335336
- load() → Lists available sources\n\
336-
- load(source: \"rust-patterns\") → Loads the rust-patterns skill"
337+
- load(source: \"rust-patterns\") → Loads the rust-patterns skill\n\
338+
- load(source: \"20260219_1\") → Waits for background task, then returns result"
337339
.to_string(),
338340
schema.as_object().unwrap().clone(),
339341
)
@@ -880,7 +882,7 @@ impl SummonClient {
880882

881883
// Wait for the running task to complete, keeping the tool call
882884
// alive so notifications (subagent tool calls) stream in real time.
883-
let task = running.remove(task_id).unwrap();
885+
let mut task = running.remove(task_id).unwrap();
884886
drop(running);
885887

886888
let buffered = {
@@ -896,45 +898,37 @@ impl SummonClient {
896898
}
897899
}
898900

899-
let description = task.description.clone();
900-
let mut handle = task.handle;
901-
902-
let (output, timed_out) = tokio::select! {
903-
result = &mut handle => {
904-
let s = match result {
901+
tokio::select! {
902+
result = &mut task.handle => {
903+
let output = match result {
905904
Ok(Ok(s)) => s,
906905
Ok(Err(e)) => format!("Error: {}", e),
907906
Err(e) => format!("Task panicked: {}", e),
908907
};
909-
(s, false)
908+
909+
return Ok(vec![Content::text(format!(
910+
"# Background Task Result: {}\n\n\
911+
**Task:** {}\n\
912+
**Status:** ✓ Completed\n\
913+
**Duration:** {} ({} turns)\n\n\
914+
## Output\n\n{}",
915+
task_id,
916+
task.description,
917+
round_duration(task.started_at.elapsed()),
918+
task.turns.load(Ordering::Relaxed),
919+
output
920+
))]);
910921
}
911922
_ = tokio::time::sleep(Duration::from_secs(300)) => {
912-
handle.abort();
913-
("Task timed out waiting for completion (aborted after 5 min)".to_string(), true)
914-
}
915-
};
923+
self.background_tasks.lock().await.insert(task_id.to_string(), task);
916924

917-
let duration = task.started_at.elapsed();
918-
let turns_taken = task.turns.load(Ordering::Relaxed);
919-
let status = if timed_out {
920-
"⏱ Timed out"
921-
} else {
922-
"✓ Completed"
923-
};
924-
925-
return Ok(vec![Content::text(format!(
926-
"# Background Task Result: {}\n\n\
927-
**Task:** {}\n\
928-
**Status:** {}\n\
929-
**Duration:** {} ({} turns)\n\n\
930-
## Output\n\n{}",
931-
task_id,
932-
description,
933-
status,
934-
round_duration(duration),
935-
turns_taken,
936-
output
937-
))]);
925+
return Err(format!(
926+
"Task '{task_id}' is still running after waiting 5 min. \
927+
Use load(source: \"{task_id}\") to wait again, or \
928+
load(source: \"{task_id}\", cancel: true) to stop."
929+
));
930+
}
931+
}
938932
}
939933

940934
Err(format!("Task '{}' not found.", task_id))
@@ -1628,7 +1622,8 @@ impl SummonClient {
16281622
.insert(task_id.clone(), task);
16291623

16301624
Ok(vec![Content::text(format!(
1631-
"Task {} started in background: \"{}\"\nUse load(source: \"{}\") to wait for the result (it will block until complete).",
1625+
"Task {} started in background: \"{}\"\n\
1626+
Continue with other work. When you need the result, use load(source: \"{}\").",
16321627
task_id, description, task_id
16331628
))])
16341629
}
@@ -1714,19 +1709,13 @@ impl McpClientTrait for SummonClient {
17141709
let mut lines = vec!["Background tasks:".to_string()];
17151710
let now = current_epoch_millis();
17161711

1717-
let mut shortest_elapsed_secs: Option<u64> = None;
1718-
17191712
let mut sorted_running: Vec<_> = running.values().collect();
17201713
sorted_running.sort_by_key(|t| &t.id);
17211714

17221715
for task in sorted_running {
17231716
let elapsed = task.started_at.elapsed();
17241717
let idle_ms = now.saturating_sub(task.last_activity.load(Ordering::Relaxed));
17251718

1726-
let elapsed_secs = elapsed.as_secs();
1727-
shortest_elapsed_secs =
1728-
Some(shortest_elapsed_secs.map_or(elapsed_secs, |s| s.min(elapsed_secs)));
1729-
17301719
lines.push(format!(
17311720
"• {}: \"{}\" - running {}, {} turns, idle {}",
17321721
task.id,
@@ -1757,12 +1746,11 @@ impl McpClientTrait for SummonClient {
17571746
));
17581747
}
17591748

1760-
if let Some(shortest) = shortest_elapsed_secs {
1761-
let sleep_secs = 300u64.saturating_sub(shortest).max(10);
1762-
lines.push(format!(
1763-
"\n→ sleep {} to wait, or load(source: \"id\", cancel: true) to stop",
1764-
sleep_secs
1765-
));
1749+
if !running.is_empty() {
1750+
lines.push(
1751+
"\n→ Use load(source: \"<id>\") to wait for a task, or load(source: \"<id>\", cancel: true) to stop it"
1752+
.to_string(),
1753+
);
17661754
}
17671755

17681756
Some(lines.join("\n"))

0 commit comments

Comments
 (0)