Compact TodoWrite observations
- SHA
746eb3219e90c66e28b67e1e972098fc86e747fc- Parents
-
a608f28 - Tree
0d12fdb
746eb32
746eb3219e90c66e28b67e1e972098fc86e747fca608f28
0d12fdb| Status | File | + | - |
|---|---|---|---|
| M |
src/loader/runtime/parsing.py
|
39 | 0 |
| M |
tests/test_parsing.py
|
36 | 0 |
src/loader/runtime/parsing.pymodified@@ -407,5 +407,44 @@ def parse_tool_calls( | ||
| 407 | 407 | def format_tool_result(tool_name: str, result: str, is_error: bool = False) -> str: |
| 408 | 408 | """Format a tool result for inclusion in conversation.""" |
| 409 | 409 | |
| 410 | + if tool_name == "TodoWrite" and not is_error: | |
| 411 | + try: | |
| 412 | + payload = json.loads(result) | |
| 413 | + except json.JSONDecodeError: | |
| 414 | + payload = None | |
| 415 | + if isinstance(payload, dict): | |
| 416 | + todos = payload.get("new_todos", []) | |
| 417 | + if isinstance(todos, list): | |
| 418 | + completed = 0 | |
| 419 | + in_progress = 0 | |
| 420 | + pending = 0 | |
| 421 | + next_pending: str | None = None | |
| 422 | + for item in todos: | |
| 423 | + if not isinstance(item, dict): | |
| 424 | + continue | |
| 425 | + status = str(item.get("status", "")).strip().lower() | |
| 426 | + content = str(item.get("content", "")).strip() | |
| 427 | + if status == "completed": | |
| 428 | + completed += 1 | |
| 429 | + elif status == "in_progress": | |
| 430 | + in_progress += 1 | |
| 431 | + if next_pending is None and content: | |
| 432 | + next_pending = content | |
| 433 | + else: | |
| 434 | + pending += 1 | |
| 435 | + if next_pending is None and content: | |
| 436 | + next_pending = content | |
| 437 | + summary_parts = [ | |
| 438 | + "updated todo list", | |
| 439 | + f"{completed} completed", | |
| 440 | + f"{in_progress} in progress", | |
| 441 | + f"{pending} pending", | |
| 442 | + ] | |
| 443 | + if next_pending: | |
| 444 | + summary_parts.append(f"next pending: {next_pending}") | |
| 445 | + if payload.get("verification_nudge_needed") is True: | |
| 446 | + summary_parts.append("verification should be reviewed next") | |
| 447 | + result = "; ".join(summary_parts) | |
| 448 | + | |
| 410 | 449 | prefix = "Error" if is_error else "Result" |
| 411 | 450 | return f"Observation [{tool_name}]: {prefix}: {result}" |
tests/test_parsing.pymodified@@ -274,3 +274,39 @@ class TestFormatToolResult: | ||
| 274 | 274 | assert "write" in result |
| 275 | 275 | assert "Error" in result |
| 276 | 276 | assert "Permission denied" in result |
| 277 | + | |
| 278 | + def test_format_todowrite_compacts_payload(self): | |
| 279 | + result = format_tool_result( | |
| 280 | + "TodoWrite", | |
| 281 | + json.dumps( | |
| 282 | + { | |
| 283 | + "old_todos": [ | |
| 284 | + { | |
| 285 | + "content": "Create index.html", | |
| 286 | + "active_form": "Creating index.html", | |
| 287 | + "status": "completed", | |
| 288 | + } | |
| 289 | + ], | |
| 290 | + "new_todos": [ | |
| 291 | + { | |
| 292 | + "content": "Create index.html", | |
| 293 | + "active_form": "Creating index.html", | |
| 294 | + "status": "completed", | |
| 295 | + }, | |
| 296 | + { | |
| 297 | + "content": "Create installation chapter (02-installation.html)", | |
| 298 | + "active_form": "Creating installation chapter", | |
| 299 | + "status": "pending", | |
| 300 | + }, | |
| 301 | + ], | |
| 302 | + "verification_nudge_needed": False, | |
| 303 | + "store_path": "/tmp/.loader/todos/active.json", | |
| 304 | + } | |
| 305 | + ), | |
| 306 | + ) | |
| 307 | + assert "Observation [TodoWrite]: Result: updated todo list" in result | |
| 308 | + assert "1 completed" in result | |
| 309 | + assert "1 pending" in result | |
| 310 | + assert "next pending: Create installation chapter (02-installation.html)" in result | |
| 311 | + assert "old_todos" not in result | |
| 312 | + assert "new_todos" not in result | |