feat: support auto continuation in subagents and prevent them from calling todo__pause

This commit is contained in:
Dark-Alex-17 committed 2026-09-03 15:50:00 -06:00
1 parent 101b706be5
commit cb5b46800f
8 files changed
+305 -3

No files matched your search

+5
View File
@@ -648,6 +648,11 @@ impl Agent {
enabled_tools: None,
}
}
#[cfg(test)]
pub fn functions_mut(&mut self) -> &mut Functions {
&mut self.functions
}
}
impl RoleLike for Agent {
+1
View File
@@ -74,6 +74,7 @@ pub async fn macro_execute(
macro_ctx.current_depth = ctx.current_depth;
macro_ctx.auto_continue_count = ctx.auto_continue_count;
macro_ctx.todo_list = ctx.todo_list.clone();
macro_ctx.auto_continue_paused = ctx.auto_continue_paused.clone();
macro_ctx.tool_scope.tool_tracker = ctx.tool_scope.tool_tracker.clone();
macro_ctx.discontinuous_last_message();
+3
View File
@@ -63,11 +63,14 @@ pub(in crate::config) const DEFAULT_TODO_INSTRUCTIONS: &str = indoc! {"
- `todo__done`: Mark a task done by id. Call this immediately after completing each step.
- `todo__list`: Show the current todo list.
- `todo__clear`: Clear the entire todo list and reset the goal. Use when the user cancels or changes direction.
- `todo__pause`: Pause auto-continue and yield the turn to the user, keeping incomplete todos intact. Use when you are blocked on user input or a user-side action. Auto-continue resumes on the user's next message.
RULES:
- Always create a todo list before starting work.
- Mark each task done as soon as you finish it; do not batch.
- If the user cancels the current task or changes direction, call `todo__clear` immediately.
- If you need user input (or the user must do something on their end) before you can continue, call `todo__pause` with the reason, ask the user, and end your turn. NEVER mark unfinished todos done just to stop the auto-continue prompts.
- `todo__pause` is for top-level sessions only. Subagents are driven to completion autonomously and cannot pause. If you are a subagent and need input, use a `user__*` tool (it escalates to the root agent mid-turn and blocks until answered, so you keep working without ending your turn).
- If you stop with incomplete tasks, the system will automatically prompt you to continue."
};
+17
View File
@@ -336,6 +336,7 @@ pub struct RequestContext {
pub todo_list: TodoList,
pub skill_registry: SkillRegistry,
pub last_continuation_response: Option<String>,
pub auto_continue_paused: Option<String>,
pub pending_prefill: Option<String>,
pub session_abort: Option<AbortSignal>,
@@ -376,6 +377,7 @@ impl RequestContext {
todo_list: TodoList::default(),
skill_registry: SkillRegistry::default(),
last_continuation_response: None,
auto_continue_paused: None,
pending_prefill: None,
session_abort: None,
render_mode: RenderMode::default(),
@@ -441,6 +443,7 @@ impl RequestContext {
todo_list: TodoList::default(),
skill_registry: SkillRegistry::default(),
last_continuation_response: None,
auto_continue_paused: None,
pending_prefill: None,
session_abort: None,
render_mode: RenderMode::default(),
@@ -495,6 +498,7 @@ impl RequestContext {
todo_list: self.todo_list.clone(),
skill_registry: self.skill_registry.clone(),
last_continuation_response: None,
auto_continue_paused: self.auto_continue_paused.clone(),
pending_prefill: None,
session_abort: self.session_abort.clone(),
render_mode: self.render_mode,
@@ -545,6 +549,7 @@ impl RequestContext {
todo_list: TodoList::default(),
skill_registry: SkillRegistry::default(),
last_continuation_response: None,
auto_continue_paused: None,
pending_prefill: None,
session_abort: None,
render_mode: parent.render_mode,
@@ -582,6 +587,7 @@ impl RequestContext {
pub fn init_todo_list(&mut self, goal: &str) {
self.todo_list = TodoList::new(goal);
self.auto_continue_paused = None;
}
pub fn add_todo(&mut self, task: &str) -> usize {
@@ -595,12 +601,21 @@ impl RequestContext {
pub fn clear_todo_list(&mut self) {
self.todo_list.clear();
self.auto_continue_count = 0;
self.auto_continue_paused = None;
}
pub fn increment_auto_continue_count(&mut self) {
self.auto_continue_count += 1;
}
pub fn pause_auto_continue(&mut self, reason: &str) {
self.auto_continue_paused = Some(reason.to_string());
}
pub fn resume_auto_continue(&mut self) {
self.auto_continue_paused = None;
}
pub fn reset_continuation_count(&mut self) {
self.auto_continue_count = 0;
self.last_continuation_response = None;
@@ -4676,6 +4691,7 @@ impl RequestContext {
self.current_depth = 0;
self.auto_continue_count = 0;
self.todo_list = TodoList::default();
self.auto_continue_paused = None;
if let Some(session_name) = session_name.as_deref() {
self.use_session(app, Some(session_name), abort_signal)
@@ -4719,6 +4735,7 @@ impl RequestContext {
self.auto_continue_count = 0;
self.pending_tasks_guardrail_count = 0;
self.todo_list = TodoList::default();
self.auto_continue_paused = None;
self.rag.take();
// Cleared alongside `rag` so the pair never disagrees: an agent RAG is
// cached under `RagKey::Agent(<agent name>)`, and leaving that key behind