fix(copilot): use messages_dict in fallback context compaction (#11922)

## Summary

Fixes a bug where the fallback path in context compaction passes
`recent_messages` (already sliced) instead of `messages_dict` (full
conversation) to `_ensure_tool_pairs_intact`.

This caused the function to fail to find assistant messages that exist
in the original conversation but were outside the sliced window,
resulting in orphan tool_results being sent to Anthropic and rejected
with:

```
messages.66.content.0: unexpected tool_use_id found in tool_result blocks: toolu_vrtx_019bi1PDvEn7o5ByAxcS3VdA
```

## Changes

- Pass `messages_dict` and `slice_start` (relative to full conversation)
instead of `recent_messages` and `reduced_slice_start` (relative to
already-sliced list)

## Testing

This is a targeted fix for the fallback path. The bug only manifests
when:
1. Token count > 120k (triggers compaction)
2. Initial compaction + summary still exceeds limit (triggers fallback)
3. A tool_result's corresponding assistant is in `messages_dict` but not
in `recent_messages`

## Related

- Fixes SECRT-1861
- Related: SECRT-1839 (original fix that missed this code path)
This commit is contained in:
Otto
2026-02-02 13:01:05 +00:00
committed by GitHub
parent 2abbb7fbc8
commit f7350c797a

View File

@@ -1184,11 +1184,14 @@ async def _stream_chat_chunks(
else recent_messages
)
# Ensure tool pairs stay intact in the reduced slice
reduced_slice_start = max(
# Note: Search in messages_dict (full conversation) not recent_messages
# (already sliced), so we can find assistants outside the current slice.
# Calculate where reduced_recent starts in messages_dict
reduced_start_in_dict = slice_start + max(
0, len(recent_messages) - keep_count
)
reduced_recent = _ensure_tool_pairs_intact(
reduced_recent, recent_messages, reduced_slice_start
reduced_recent, messages_dict, reduced_start_in_dict
)
if has_system_prompt:
messages = [