c26357ad6a
Follow-up to the #90972 salvage: - strip loop: copy.deepcopy(msg) -> dict(msg). strip_anthropic_cache_control is copy-on-write on content parts by contract (pops the top-level key, rebuilds content lists/part dicts fresh), so a shallow top-level copy preserves the caller-non-mutation guarantee — verified for all four marker shapes — and removes the redundant second deepcopy the re-mark path paid on already-decorated input. Docstring updated to match. - tests: moved the surviving idempotency tests into tests/agent/test_prompt_caching.py (where this module's tests live) as TestApplyIdempotency; dropped the three tests that duplicated existing coverage (dynamic_tool_accounting ~= TestPromptCachePlan:: test_copies_sections_and_keeps_canonical_tools_plain which already asserts == 4; can_carry_marker_envelope_vs_native ~= TestCanCarryMarker; never_exceeds_four_markers subsumed by the idempotency test). - exact-count assertions per review: idempotency fixture pins == 4, no-tools fallback pins == 3 (marker loss can no longer masquerade as safety); added the one new _can_carry_marker assertion (native=True empty assistant) to TestCanCarryMarker. - new part-level stale-marker mutation guard (the other detection branch, where part-dict aliasing is the risk); fails on pre-fix base with marker accumulation (9 > 4), passes with the fix.