@@ -381,6 +381,24 @@ class ToolBatchRunner: |
| 381 | 381 | tool_call, |
| 382 | 382 | dod=dod, |
| 383 | 383 | ) |
| 384 | + completed_todowrite_response = ( |
| 385 | + self._completed_todowrite_verification_response( |
| 386 | + tool_call=executed_tool_call, |
| 387 | + dod=dod, |
| 388 | + single_tool_batch=len(tool_calls) == 1, |
| 389 | + ) |
| 390 | + ) |
| 391 | + if completed_todowrite_response is not None: |
| 392 | + summary.final_response = completed_todowrite_response |
| 393 | + await emit( |
| 394 | + AgentEvent( |
| 395 | + type="response", |
| 396 | + content=completed_todowrite_response, |
| 397 | + ) |
| 398 | + ) |
| 399 | + result.halted = True |
| 400 | + result.final_response = completed_todowrite_response |
| 401 | + return result |
| 384 | 402 | if self._should_preempt_for_verification_handoff( |
| 385 | 403 | tool_call=executed_tool_call, |
| 386 | 404 | dod=dod, |
@@ -503,6 +521,47 @@ class ToolBatchRunner: |
| 503 | 521 | return False |
| 504 | 522 | return tool_call.name in ({"TodoWrite"} | _BOOKKEEPING_NOTE_TOOL_NAMES) |
| 505 | 523 | |
| 524 | + def _completed_todowrite_verification_response( |
| 525 | + self, |
| 526 | + *, |
| 527 | + tool_call: ToolCall, |
| 528 | + dod: DefinitionOfDone, |
| 529 | + single_tool_batch: bool, |
| 530 | + ) -> str | None: |
| 531 | + if tool_call.name != "TodoWrite" or not single_tool_batch: |
| 532 | + return None |
| 533 | + if not all_planned_artifact_outputs_exist( |
| 534 | + dod, |
| 535 | + project_root=self.context.project_root, |
| 536 | + ): |
| 537 | + return None |
| 538 | + |
| 539 | + pending_items = [ |
| 540 | + item |
| 541 | + for item in effective_pending_todo_items( |
| 542 | + dod, |
| 543 | + project_root=self.context.project_root, |
| 544 | + ) |
| 545 | + if item not in _TODO_NUDGE_EXCLUDED_ITEMS |
| 546 | + ] |
| 547 | + if pending_items: |
| 548 | + return None |
| 549 | + |
| 550 | + verification_commands = dod.verification_commands or derive_verification_commands( |
| 551 | + dod, |
| 552 | + project_root=self.context.project_root, |
| 553 | + task_statement=getattr(self.context.session, "current_task", "") or "", |
| 554 | + supplement_existing=True, |
| 555 | + ) |
| 556 | + if not verification_commands: |
| 557 | + return None |
| 558 | + |
| 559 | + self.context.set_workflow_mode("verify") |
| 560 | + return ( |
| 561 | + "Todo tracking is complete; running Loader verification on the generated " |
| 562 | + "files now." |
| 563 | + ) |
| 564 | + |
| 506 | 565 | def _queue_duplicate_observation_nudge( |
| 507 | 566 | self, |
| 508 | 567 | tool_call: ToolCall, |