fix(agent): preserve todos as tool state after compression - #58162
fix(agent): preserve todos as tool state after compression#581620xLeathery wants to merge 1 commit into
Conversation
d4751e2 to
5579188
Compare
Competing fix cluster for #26979 (all open, different mechanisms): this PR carries active todos as paired assistant/tool history; #26981 removes the injection entirely; #18883 strips subjects from the injection. #35349 (closed) folded the snapshot back into a user turn, which this PR explicitly avoids. Not a duplicate of any -- flagging the cluster so a maintainer can pick the canonical approach. |
tonydwb
left a comment
There was a problem hiding this comment.
LGTM. The _STALE_TODO_SHOULD_NOT_BE_A_USER_MESSAGE sentinel strings are intentional test fixtures, not stale TODOs — confirmed by context. Core logic (preserving todos as tool state after compression) looks correct. Well-tested fix with dedicated unit tests.
Reviewed by Hermes Agent
teknium1
left a comment
There was a problem hiding this comment.
Thanks for preserving todo state as a paired tool result rather than a synthetic user instruction. The underlying issue remains on current main: agent/conversation_compression.py:810-813 still appends format_for_injection() as role="user".
Problems
- The current insertion order is stale against
main. The PR returns unchanged history when it contains no user turn (agent/conversation_compression.py:126-134in PR head557918825581), but current main then restores a real user turn in_ensure_compressed_has_user_turn()(agent/conversation_compression.py:418-456, added by6e176e4c). In that assistant/tool-only compression case, active todos are not preserved. Current main has an explicit regression for this shape attests/agent/test_compression_concurrent_fork.py:206-241.
Suggested changes
- Preserve the current user-turn guard, run it before inserting the assistant/tool todo pair, and add a regression covering the assistant-only compressor result plus todo hydration.
Automated hermes-sweeper review.
| compressed.append({"role": "user", "content": todo_snapshot}) | ||
| todo_state_messages = _todo_state_messages_for_compression(agent) | ||
| if todo_state_messages: | ||
| compressed = _insert_todo_state_messages(compressed, todo_state_messages) |
There was a problem hiding this comment.
Current main adds _ensure_compressed_has_user_turn() after this hunk. If compression returns assistant/tool-only history, this helper returns unchanged at line 134 and the later guard restores a user turn without any todo pair. Run the user-turn guard before this insertion and cover that current-main compatibility path.
Summary
assistanttool-call +toolresult history instead of appending a syntheticusermessage.Why
Context compression used to append the preserved todo snapshot as a fresh
usermessage. That blurred internal continuity state with user intent, and could make preserved todo text look like the latest user turn after compaction.This keeps the two concepts separate: real user messages stay user messages, and active todo state is carried as paired tool history that the existing todo hydration path can restore.
Unlike #35349, this does not fold todo state back into a user turn; it carries active todos as paired assistant/tool history.
Closes #26979.
Test plan
python3.11manual compression smoke: verified latest real user turn remains the user tail, one paired todo tool result is inserted, only active todos are preserved, and completed todos are omitted.python3 scripts/check-windows-footguns.py agent/conversation_compression.py tools/todo_tool.py tests/agent/test_context_compression_todo_leak.py tests/tools/test_todo_tool.pyscripts/run_tests.sh tests/agent/test_context_compression_todo_leak.py tests/tools/test_todo_tool.py tests/run_agent/test_run_agent.py tests/tools/test_read_loop_detection.py tests/agent/test_context_compressor.py tests/agent/test_context_compressor_summary_continuity.py tests/agent/test_context_compressor_temporal_anchoring.py tests/agent/test_compression_concurrent_fork.py tests/agent/test_compression_rotation_state.py tests/agent/test_compression_progress.py tests/agent/test_compressed_summary_metadata.py— 645 passed.Platforms tested