@@ -3,6 +3,7 @@ |
| 3 | from __future__ import annotations | 3 | from __future__ import annotations |
| 4 | | 4 | |
| 5 | from ..llm.base import Message, Role | 5 | from ..llm.base import Message, Role |
| | 6 | +from ..utils.todos import active_todo_store_path, clear_active_todos |
| 6 | from .bootstrap import ( | 7 | from .bootstrap import ( |
| 7 | RuntimeBootstrapSource, | 8 | RuntimeBootstrapSource, |
| 8 | RuntimeBootstrapView, | 9 | RuntimeBootstrapView, |
@@ -12,7 +13,7 @@ from .chat_lane import ConversationalTurnRunner |
| 12 | from .conversation import ConfirmationHandler, ConversationRuntime, EventSink, UserQuestionHandler | 13 | from .conversation import ConfirmationHandler, ConversationRuntime, EventSink, UserQuestionHandler |
| 13 | from .decomposition_lane import DecompositionTurnRunner | 14 | from .decomposition_lane import DecompositionTurnRunner |
| 14 | from .deliberation import should_decompose | 15 | from .deliberation import should_decompose |
| 15 | -from .events import TurnSummary | 16 | +from .events import AgentEvent, TurnSummary |
| 16 | from .explore import ExploreRuntime | 17 | from .explore import ExploreRuntime |
| 17 | from .task_classification import is_conversational | 18 | from .task_classification import is_conversational |
| 18 | from .workflow import WorkflowMode | 19 | from .workflow import WorkflowMode |
@@ -48,8 +49,7 @@ class RuntimeLauncher: |
| 48 | if is_conversational(user_message): | 49 | if is_conversational(user_message): |
| 49 | return await self.run_conversational(user_message, emit) | 50 | return await self.run_conversational(user_message, emit) |
| 50 | | 51 | |
| 51 | - if self.source.current_task is None: | 52 | + await self._begin_top_level_task(user_message, emit) |
| 52 | - self.source.current_task = user_message | | |
| 53 | | 53 | |
| 54 | requested_mode = self._requested_workflow_mode(use_plan) | 54 | requested_mode = self._requested_workflow_mode(use_plan) |
| 55 | | 55 | |
@@ -167,6 +167,20 @@ class RuntimeLauncher: |
| 167 | return WorkflowMode.PLAN.value | 167 | return WorkflowMode.PLAN.value |
| 168 | return None | 168 | return None |
| 169 | | 169 | |
| | 170 | + async def _begin_top_level_task(self, user_message: str, emit: EventSink) -> None: |
| | 171 | + """Reset task-scoped state before a new top-level task starts.""" |
| | 172 | + |
| | 173 | + previous_task = self.source.current_task |
| | 174 | + had_active_todos = active_todo_store_path(self.source.project_root).exists() |
| | 175 | + self.source.current_task = user_message |
| | 176 | + |
| | 177 | + if previous_task == user_message: |
| | 178 | + return |
| | 179 | + |
| | 180 | + clear_active_todos(self.source.project_root) |
| | 181 | + if previous_task is not None or had_active_todos: |
| | 182 | + await emit(AgentEvent(type="todo_update", todo_items=[])) |
| | 183 | + |
| 170 | | 184 | |
| 171 | def build_runtime_launcher(source: RuntimeBootstrapSource) -> RuntimeLauncher: | 185 | def build_runtime_launcher(source: RuntimeBootstrapSource) -> RuntimeLauncher: |
| 172 | """Build a public runtime launcher from the shared bootstrap source.""" | 186 | """Build a public runtime launcher from the shared bootstrap source.""" |