Skip to content

Refactor provider cache-control transforms - #1

Draft
wchoi32 wants to merge 3 commits into
mainfrom
pr/05-provider-transforms
Draft

wchoi32 wants to merge 3 commits into
mainfrom
pr/05-provider-transforms

Conversation

@wchoi32

@wchoi32 wchoi32 commented Mar 23, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • extract shared cache-control helper logic for provider transforms
  • apply the shared helper in the Anthropic and OpenRouter transform paths
  • add targeted transform tests

Review Notes

  • local draft on fork for architectural and code review only
  • source review did not surface a concrete regression in this slice
  • targeted tests were not fully validated from this environment

John Choi and others added 3 commits March 22, 2026 18:25
- Change if/if to if/else-if in OpenRouter user message loop to prevent
  redundant cache_control processing when string content is converted to array
- Add direct unit tests for cache-control.ts (empty array, no match,
  last match, immutability)
- Add negative test verifying non-Anthropic models do NOT get cache_control

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…che path

The reference-equality predicate (part) => part === lastTextPart relies
on lastTextPart being either .pop()'d from or freshly pushed into
msg.content. This is safe but non-obvious to future editors.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning message nudging
  the LLM to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses JSON.stringify comparison of tool params, matching
the approach used by OpenCode (PR anomalyco/opencode#3445).

Changes:
- TaskState.ts: add lastToolParams and consecutiveIdenticalToolCount
- ToolExecutor.ts: add detection logic in handleCompleteBlock()
- doom-loop-detection.test.ts: 9 unit tests covering thresholds,
  resets, edge cases

No new files beyond the test. No new settings. No UI changes.
Existing consecutiveMistakeCount and lastToolName tracking unchanged.

Refs: cline#9923, cline#9916, cline#9846, cline#9816, cline#8237, cline#8637

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@wchoi32 wchoi32 mentioned this pull request Mar 23, 2026
13 tasks done
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- loop-detection.test.ts: 17 tests

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- loop-detection.test.ts: 17 tests

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- loop-detection.test.ts: 17 tests

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 18 tests

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 added a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 added a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
wchoi32 pushed a commit that referenced this pull request Mar 23, 2026
Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including cline#9923, cline#9916, cline#9846, cline#9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses recursive canonical serialization (sorted keys at
every nesting level) so semantically identical params with different
key order are correctly matched. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (stableStringify, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 21 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: cline#9923, cline#9916, cline#9846, cline#9816
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant