[BUG] Massive dump of context caching issues, duplication of actions, and bloat

Open 💬 3 comments Opened Jun 1, 2026 by michael-greider

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

I am gonna preface this with the reasoning behind putting this in one big report. It is all correlated to the same major issue we all have been having. It just shows up in different places

1. Background and Motivation

Claude Code is Anthropic's CLI tool for AI-assisted software development. Users on the Max plan ($200/month) and Max20x tier receive a weekly token allowance. Usage is expected to correlate with visible activity — messages sent, tool calls made, responses received.

Starting around late May 2026 (version 2.1.150+), observable usage no longer correlated with visible session activity. Four sessions, each using a single agent with no MCP servers, consumed 85% of a Max20x weekly allowance. The sessions were unremarkable: standard coding tasks, moderate conversation length, no unusual patterns.

I personally respect and understand the A/B testing done in December, gotta know how you will scale the platform. But i think it is evident at this point that there is no interest in bringing "responsible ai" within
Anthropic. There is just an interest in enterprise capital to the extent
of gaslighting users and creating a culture of "systems engineers" that
blame the user before blaming the platform.

This report documents what was found inside the 2.1.158 binary that explains the discrepancy. There is no level of "agentic systems engineering with systems" that will stop the usage problem. It is plain robbery.

---

2. Methodology

2.1 Binary Extraction

The Claude Code binary was obtained from npm and extracted:

npm pack @anthropic-ai/claude-code-linux-x64@2.1.158
tar xzf anthropic-ai-claude-code-linux-x64-2.1.158.tgz
# Extract via bun-unpack tooling into extracted-2.1.158/
# Strip CJS wrapper to get raw application code

Result: cli-stripped.js, 15,514,776 bytes.
SHA256: 7afe182969bc477bb5b4a9b5eeb444a519e79118c897c880211a7f54f27b067f

2.2 Symbol Recovery

The source leak in 2.0.96 provides enough information weighed against the newer isolated ELF binary for easy decomp. Let alone the monumentous security risk, it exposes a sloppy engineering discipline of "just add more".

Since version 2.1.150, Anthropic's build preserves source-level export names inside the binary in the pattern sourceName:()=>minifiedName. This allowed recovery of 4,452 export name mappings without decompilation:

grep -oP '[a-zA-Z_][a-zA-Z0-9_]+:\(\)=>[a-zA-Z0-9_$]+' cli-stripped.js | sort -u > export-map.txt

2.3 Analysis Approach

All findings were obtained through:

  • grep with byte offsets for locating code patterns
  • dd for extracting surrounding context at specific byte positions
  • Pattern matching on known function signatures, string literals, and telemetry event names
  • No decompiler was used. No code was modified. All excerpts are verbatim from the minified binary.

2.4 Verification

Every code excerpt in this report can be independently verified by anyone with access to the same npm package. Byte offsets are provided where relevant. Grep commands are reproducible.

---

3. The Core Architecture: A State Machine With Silent Re-entry

The conversation engine lives in a single async generator function (Al_, byte offset ~10,629,277). Its structure is a while(!0) loop — an infinite loop that continues until an explicit return statement is reached. Within this loop, 6 distinct code paths end with continue, which re-enters the loop from the top.

Each re-entry:

  1. Re-evaluates whether autocompaction is needed (potentially making a separate API call)
  2. Re-assembles the full message array
  3. Makes a fresh API call to the model with the complete conversation history

The state transitions are tracked by a D object that gets reassigned before each continue:

D={
  messages:[...],           // full conversation so far
  toolUseContext:Z,
  compactTracking:Q,
  maxOutputTokensRecoveryCount:E+1,
  hasAttemptedReactiveCompact:S,
  maxOutputTokensOverride:void 0,
  // ...
  transition:{reason:"max_output_tokens_recovery",attempt:E+1}
};
continue  // goes back to while(!0) top

The transition.reason field values found in the binary are:

  • "max_output_tokens_recovery" — output too long, trying again
  • "malformed_tool_use_retry" — model's tool call was unparseable, retry
  • "stop_hook_blocking" — a hook says "don't stop yet", retry
  • "reactive_compact_retry" — compaction triggered mid-turn, retry after summarizing
  • "precomputed_compact_swap" — pre-computed compaction result applied, retry
  • "next_turn" — normal tool-use continuation (expected behavior)

All except "next_turn" represent silent retries invisible to the user.

---

4. Finding 1: Output Token Recovery Loop

Location

Byte offset ~10,641,000 within Al_

Mechanism

When the model's response exceeds the configured output token limit (stop_reason = max_tokens on the output side), the system injects a hidden instruction and re-queries. This happens up to 3 times.

The cap constant:

_l_=3

The injected message:

V8({
  content:"Output token limit hit. Resume directly — no apology, no recap of what you were doing. "+
    "Pick up mid-thought if that is where the cut happened. Break remaining work into smaller pieces.",
  isMeta:!0
})

The isMeta:!0 flag means this message is hidden from the user's conversation view. They never see it.

Default Limits That Trigger This

Per-model output caps from the binary:

if(K==="claude-opus-4-8")$=64000,q=128000;
if(K==="claude-opus-4-7")$=64000,q=128000;
if(K==="claude-opus-4-6")$=64000,q=128000;
if(K==="claude-sonnet-4-6")$=32000,q=128000;

The default for all Opus models is 64,000 output tokens. The model routinely produces responses approaching or exceeding this during extended code generation, refactoring, or explanation tasks. This isn't an edge case — it's the normal operating condition for complex work on Opus.

Cost

Each retry re-sends the entire conversation to that point, plus all previous assistant messages from the current turn:

messages:[...U,..._H,k$]
// U = all messages up to this turn
// _H = all assistant messages generated this turn (including cut-off ones)
// k$ = the hidden recovery instruction

On a 200K-token conversation, with 64K of output already generated:

  • Retry 1 input: ~264K tokens
  • Retry 2 input: ~328K tokens (now includes retry 1's response)
  • Retry 3 input: ~392K tokens

Total additional input cost for 3 retries: approximately 984,000 tokens. None visible to the user.

---

5. Finding 2: Context Overflow Retry

Location

Byte offset ~10,513,505

Mechanism

When the API returns HTTP 400 with message "input length and max_tokens exceed context limit", the system parses the error, computes a reduced output allocation, and retries:

let{inputTokens:h,contextLimit:R}=S,
    C=1000,
    b=Math.max(0,R-h-1000);
if(b<qr6)throw ...;  // qr6=3000, the absolute floor
let B=(_.thinkingConfig.type==="enabled"?_.thinkingConfig.budgetTokens:0)+1,
    I=Math.max(qr6,b,B);
_.maxTokensOverride=I,
d("tengu_max_tokens_context_overflow_adjustment",{inputTokens:h,contextLimit:R,adjustedMaxTokens:I,attempt:J});
continue

The telemetry event name tengu_max_tokens_context_overflow_adjustment confirms this path is instrumented for production monitoring.

How It Fires

This typically fires when:

  • The output recovery loop (#4) has added several large assistant responses to the context
  • Compaction hasn't triggered yet (threshold not reached)
  • The accumulated context + default max_tokens (64K) > model's context limit

The continue statement has no explicit cap beyond the outer retry limit. If context is very close to the limit, this can fire repeatedly until b < qr6 (available space < 3000 tokens), at which point it throws.

---

6. Finding 3: Malformed Tool Use Retry

Location

Same function, after response processing

Mechanism

When the model signals tool_use intent but no parseable tool call exists in the response:

if((sH?.message.stop_reason??YH)==="tool_use"&&qH.length===0&&!sH?.isApiErrorMessage){
  let k$=D.transition?.reason!=="malformed_tool_use_retry";
  if(k$){
    let U$=V8({content:"Your tool call was malformed and could not be parsed. Please retry.",isMeta:!0});
    D={messages:[...U,..._H,U$],...,transition:{reason:"malformed_tool_use_retry"}};
    continue
  }
}

The guard transition?.reason!=="malformed_tool_use_retry" prevents infinite recursion but allows exactly one retry. The message is marked isMeta:!0 — invisible to the user.

Interaction With Other Loops

The retry resets maxOutputTokensRecoveryCount:0 and hasAttemptedReactiveCompact:!1. This means after the malformed retry, ALL other retry paths are re-enabled. If the model again produces a long response on retry, the output recovery loop can fire its full 3 retries.

---

7. Finding 4: Refusal Fallback With Model Switch

Location

Inner streaming loop within Al_

Mechanism

Controlled by server-side gate tengu_loggia_carousel:

function t0H(){return Z$("tengu_loggia_carousel",!1)}

When enabled and the model refuses a request, all assistant messages from the current turn are tombstoned (discarded), the model is switched, and the entire conversation is replayed:

for(let b$ of _H)yield{type:"tombstone",message:b$};
_H.length=0,KH.length=0,qH.length=0

The fallback model is determined by:

function dG(){
  if(process.env.ANTHROPIC_DEFAULT_OPUS_MODEL)return process.env.ANTHROPIC_DEFAULT_OPUS_MODEL;
  if(!nY())return Dz()[oe];
  if(Wq()!=="firstParty")return Dz().opus47;
  return Dz().opus48
}

Cache Implications

Switching models invalidates the prompt cache entirely. The Anthropic API caches prompts per-model — a conversation cached under one model name has zero cache hits when replayed under a different model. The full input must be re-processed at full price.

---

8. Finding 5: Two-Stage Safety Classifier

Location

Auto-mode classifier subsystem (separate from main query loop)

Mechanism

When auto-mode is active, a separate API call runs before each tool-using turn to classify whether the tool use should be allowed:

function eh7(){return Z$("tengu_auto_mode_config",{})?.twoStageClassifier??!0}
// Default: true (enabled)

The classifier has its own retry budget:

var yh7=30000,Gy6=120000,hh7=60000,d38=2
// d38=2: up to 2 retries per stage

It builds its own prompt from the conversation transcript and tool definitions, and makes a separate API call with its own model:

function Bh7(){
  let H=X7();
  if(A7(H)==="claude-opus-4-8"){
    let q=Z$("tengu_cedar_hollow_7m",{});
    if(q?.model)return q.model   // server can override classifier model
  }
  let $=Z$("tengu_auto_mode_config",{});
  if($?.model)return $.model;
  return H
}

The gate tengu_cedar_hollow_7m allows the server to push a different model for the classifier without user knowledge. The "7m" in the name suggests this may be related to a 7-million-token context variant.

How Users Get Into Auto-Mode

A nudge dialog actively pushes users to switch:

A=function(L){
  if(L==="accept")p6("userSettings",{permissions:{defaultMode:"auto"}});
  d("tengu_auto_default_nudge_resolved",{choice:L,current_mode:q})
}

Once accepted, defaultMode:"auto" is written to persistent user settings. The classifier then runs on every tool-using turn for all future sessions.

Cost

The classifier prompt includes the conversation transcript, tool definitions, and permission rules. Estimated 30-80K input tokens per classification call. With 2 retries possible per stage and 2 stages, a single classification decision can make up to 5 API calls. On a session with 50 tool-using turns, the classifier alone could consume 1.5-4M input tokens.

---

9. Finding 6: Stop Hook Blocking Loop

Location

Post-response section of Al_

Mechanism

When a StopHook returns a blocking error, the system appends the error to the conversation and re-queries. The default cap is 8 iterations:

let U$=parseInt(process.env.CLAUDE_CODE_STOP_HOOK_BLOCK_CAP??"",10),
    Q$=Number.isNaN(U$)?8:U$;

Each iteration:

D={
  messages:[...U,..._H,...C$.blockingErrors],
  maxOutputTokensRecoveryCount:0,   // RESETS the output recovery counter
  hasAttemptedReactiveCompact:S,
  stopHookBlockingCount:tH,
  transition:{reason:"stop_hook_blocking"}
};
continue

Counter Reset Issue

The critical detail: maxOutputTokensRecoveryCount:0. Each stop hook iteration resets the output token recovery counter. This means within EACH of the 8 stop hook iterations, the model can also trigger 3 output recovery retries.

Theoretical maximum from a single stop hook conflict: 8 × (1 main call + 3 recovery retries) = 32 API calls, each sending the full (and growing) context.

---

10. Finding 7: Reactive Compaction

Location

Top of the while(!0) loop, before the main API call

Mechanism

When context token count exceeds a threshold, a separate summarization API call runs before the main query:

Threshold calculation:

function pv$(H,$){
  let q=H-13000;  // 13K below context window
  return q
}
// For 200K window: threshold = 187,000 tokens

The rapid-refill breaker:

dc6=3,nc6=3,t08=3
// nc6: if context refills within 3 turns of last compact, count it
// t08: after 3 consecutive rapid refills, trip breaker and error

function ic6(H){
  return H?.compacted===!0&&H.turnCounter<nc6?(H?.consecutiveRapidRefills??0)+1:0
}

Why This Matters

Compaction is a separate API call that processes the full message history. Each compaction attempt consumes ~187K input tokens (the context that needs summarizing) plus output tokens for the summary.

In a scenario where tool outputs are large (reading files, running commands), context can refill to the threshold within 1-2 turns after compaction. The system will compact again, consuming another ~187K. This can happen 3 times before the breaker trips.

After the breaker trips, the user sees:

Autocompact is thrashing: the context refilled to the limit within 3 turns of the previous compact, 3 times in a row.

But by that point, 3 × ~187K = ~561K input tokens have been consumed just for summarization attempts.

---

11. Finding 8: Server-Pushed Configuration Changes

Location

Function ZWH (UI state handler for apply_flag_settings events)

Mechanism

The server can push configuration changes to the running client at any time:

function ZWH(H,$){
  $((q)=>{
    let K=q;
    if("cacheBreakerPhrase"in H){
      let _=H.cacheBreakerPhrase,z=_==null?void 0:String(_);
      if(K.cacheBreakerPhrase!==z)K={...K,cacheBreakerPhrase:z}
    }
    if("autoCompactWindow"in H){
      let _=H.autoCompactWindow,z=_==null?void 0:Number(_);
      if(K.autoCompactWindow!==z)K={...K,autoCompactWindow:z}
    }
    if("model"in H){
      let _=H.model,z=_==null?null:String(_);
      if(K.mainLoopModelForSession!==z)K={...K,mainLoopModelForSession:z};
      if(K.mainLoopModel!==z)K={...K,mainLoopModel:z}
    }
    return K
  })
}

What This Means

Without any user action, the server can:

  1. Change cacheBreakerPhrase — This is part of the system prompt (SX is memoized on this value). Changing it invalidates the memo cache AND busts the server-side prompt cache. Every subsequent API call in that session pays full input token cost with zero cache benefit.
  2. Change autoCompactWindow — Lowering this value makes compaction fire more frequently, consuming more tokens for summarization.
  3. Change model — Switches the user's active model mid-session. If switching to a model with different context handling, this can cascade into overflow retries.

There is no user notification when these changes are pushed. The schema definition in the binary describes apply_flag_settings as:

"Merges the provided settings into the flag settings layer, updating the active configuration."

---

12. Finding 9: Background Daemon

Location

Daemon management subsystem

Evidence

The binary contains a full daemon lifecycle manager with telemetry for:

tengu_bg_daemon_cold_start_ask          — prompting user to install daemon
tengu_bg_daemon_cold_start_ask_answer   — tracking their response
tengu_bg_daemon_install                  — installing as a service
tengu_bg_daemon_spawn_failed            — spawn failures
tengu_bg_daemon_wmi_fallback            — WMI spawn fallback
tengu_bg_daemon_zombie_false_positive   — zombie detection false alarms
tengu_bg_daemon_zombie_restart          — restarting zombie processes
tengu_bg_daemon_binary_takeover         — new binary replacing old daemon
tengu_bg_daemon_service_stale_exec      — stale executable detection
tengu_bg_daemon_service_poll_fallthrough — service not responding
tengu_bg_dispatch_low_mem               — low memory during dispatch
tengu_bg_dispatch_rescued               — rescued dispatch
tengu_bg_dispatch_sigkill_escalate      — SIGKILL escalation
tengu_bg_dispatch_stale_drop            — dropping stale jobs

The daemon explicitly survives SSH disconnection:

N(`daemon: WMI spawn failed (${O.reason}); falling back to direct spawn — daemon will not survive SSH/terminal close`)

This implies the normal path (WMI spawn) DOES survive terminal close.

Risk

If daemon-managed sessions encounter any of the retry loops documented above, they continue consuming tokens after the user has disconnected. The user has no way to observe this happening in real time.

---

13. Finding 10: Streaming Fallback

Location

Inner API call handler within the query loop

Mechanism

When streaming fails (404 on stream creation, mid-stream errors), the system falls back to non-streaming mode. All partial assistant messages from the failed stream are tombstoned:

d("tengu_streaming_fallback_to_non_streaming",{
  model:z.model,
  error:M6 instanceof Error?M6.name:Ju(String(M6)),
  attemptNumber:YH,
  maxOutputTokens:j$,
  thinkingType:q.type,
  fallback_cause:"..."
})

// Tombstone partial messages:
for(let Q$ of _H)yield{type:"tombstone",message:Q$};
d("tengu_orphaned_messages_tombstoned",{orphanedMessageCount:_H.length})

The user has already been billed for the partial streaming response (input tokens + partial output tokens). The non-streaming retry re-sends the full context again.

---

14. Compound Interaction Analysis

These mechanisms do not operate in isolation. The state machine's structure allows cascading:

Path 1: Output Recovery → Overflow → Compaction

  1. Model produces 64K output, hits cap
  2. Output recovery fires (retry 1) — context grows by 64K
  3. Recovery retry's response ALSO hits 64K cap
  4. Output recovery fires again (retry 2) — context grows another 64K
  5. Now input_tokens + max_tokens > context_limit
  6. Context overflow retry fires — adjusts max_tokens, re-sends everything
  7. If context is now above compact threshold, reactive compaction fires
  8. Compaction API call (another ~200K input tokens for summarization)
  9. After compaction, main loop continues with reduced context

Cost of this cascade: approximately 1.5-2M input tokens from what the user experiences as one response.

Path 2: Stop Hook × Output Recovery (Multiplicative)

The stop hook loop resets maxOutputTokensRecoveryCount:0 on each iteration:

D={
  messages:[...U,..._H,...C$.blockingErrors],
  maxOutputTokensRecoveryCount:0,   // <-- RESET
  ...
  transition:{reason:"stop_hook_blocking"}
};

This means each of the 8 possible stop hook iterations can independently trigger 3 output recovery retries. If the model consistently produces long output on each retry:

  • Stop hook iteration 1: 1 call + 3 recoveries = 4 API calls
  • Stop hook iteration 2: 1 call + 3 recoveries = 4 API calls
  • ... (up to 8)
  • Total: up to 32 API calls from one user message

At ~200K+ context per call, that's 6.4M+ input tokens.

Path 3: Classifier + Recovery + Cache Bust (Typical Auto-Mode Session)

Per turn with auto-mode:

  1. Classifier call: ~50K input (+ up to 2 retries = ~150K worst case)
  2. Main response: ~200K input + 64K output
  3. Output hits cap: recovery × 3 adds ~984K input
  4. Server pushes cacheBreakerPhrase change: cache invalidated
  5. Next turn: 0% cache hit, full ~200K input at uncached rate

Over a 20-turn session, this pattern can easily consume 10-15M input tokens — far exceeding what a user would expect from "20 messages."

---

15. What the User Sees vs. What Actually Happens

| User's Perspective | Reality |
|---|---|
| "I sent one message and got one response" | 1-4 API calls (output recovery) |
| "My response seemed slow" | Streaming fallback retried the entire request |
| "I'm in auto-mode, tools just work" | Extra classifier call on every tool turn |
| "Context was compacted" | A separate API call consumed ~200K tokens for summarization |
| "I closed my terminal" | Daemon may still be running, consuming tokens |
| "Nothing changed in my settings" | Server pushed cacheBreakerPhrase, busting cache |
| "I accepted the permission dialog" | Auto-mode enabled permanently, classifier runs forever |

There is no user-facing breakdown of how many retries occurred. The token counter in the UI (if displayed) shows cumulative usage but not per-turn breakdown. The ExtraUsageDialog component only triggers when the plan is exhausted — it doesn't explain why.

---

16. Server-Side Gates Identified

All Z$("tengu_*") calls retrieve server-pushed configuration. These gates control behavior without user opt-in:

| Gate | Controls |
|---|---|
| tengu_loggia_carousel | Refusal fallback (model switch + replay) |
| tengu_cedar_hollow_7m | Classifier model override for Opus 4.8 users |
| tengu_auto_mode_config | Classifier behavior, model, two-stage enable |
| tengu_amber_rokovoko | Precompute buffer fraction (compaction timing) |
| tengu_kairos_loop_persistent | Loop preamble injection |
| tengu_kairos_loop_keepalive | Session keepalive behavior |
| tengu_kairos_loop_dynamic | Dynamic loop pacing |
| tengu_prompt_cache_1h_config | Cache TTL allowlist |
| tengu_slim_subagent_claudemd | Subagent CLAUDE.md inclusion |
| tengu_auto_default_nudge_* | Nudge dialog display/tracking |

The Z$ function retrieves values from a server-pushed flag settings layer. These values can change at any time during a session via apply_flag_settings.

---

17. Constants and Thresholds

Values extracted from the binary that govern retry behavior:

| Constant | Value | Purpose |
|---|---|---|
| _l_ | 3 | Max output token recovery retries per turn |
| qr6 | 3000 | Floor output tokens (minimum allocation) |
| d38 | 2 | Classifier max retries per stage |
| nc6 | 3 | Turns-since-compact for "rapid refill" |
| t08 | 3 | Consecutive rapid refills before breaker |
| Qc6 | 0.2 | Precompute buffer fraction (20%) |
| r36 | 200000 | Default context window (non-1M models) |
| yL4 | 20000 | Context window reserved for system operations |
| cc6 | 100000 | Compact window floor (minimum 100K) |
| hL4 | 1000000 | Compact window ceiling (max 1M) |
| Default output (Opus) | 64000 | Output token cap before recovery fires |
| Stop hook cap | 8 | Max stop hook blocking iterations |

---

18. Conclusions

The Claude Code 2.1.158 binary contains multiple automatic retry mechanisms that silently multiply token consumption without user awareness:

  1. These are not bugs in the traditional sense — they are intentional code paths with telemetry instrumentation, indicating Anthropic tracks their occurrence.
  1. The retry mechanisms compound multiplicatively. A single user turn can generate 4-32 API calls depending on which loops activate.
  1. Server-side gates allow Anthropic to modify retry behavior, cache strategy, and model selection for individual users without their knowledge or consent.
  1. The default output token cap for Opus models (64K) is set below the model's typical output length for complex coding tasks, making the output recovery loop fire frequently under normal use.
  1. The auto-mode nudge dialog creates a permanent cost multiplier by enabling the two-stage classifier on all future sessions.
  1. There is no user-facing mechanism to observe how many hidden retries occurred on any given turn, making it impossible for users to audit their own usage.
  1. Background daemon infrastructure can maintain active sessions after terminal disconnect, with retry loops continuing to consume tokens without observation.

These findings explain how 4 normal coding sessions could consume 85% of a Max20x weekly allowance. The visible session activity (messages, tool calls, responses) represents a fraction of actual API calls being made.

---

Appendix A: Reproduction Steps

To verify any finding in this report:

# 1. Obtain the binary
npm pack @anthropic-ai/claude-code-linux-x64@2.1.158
tar xzf anthropic-ai-claude-code-linux-x64-2.1.158.tgz

# 2. Extract (using bun-unpack or similar)
# Result: cli-stripped.js

# 3. Verify SHA256
sha256sum cli-stripped.js
# Expected: 7afe182969bc477bb5b4a9b5eeb444a519e79118c897c880211a7f54f27b067f

# 4. Search for any finding
grep -oP 'tengu_max_tokens_context_overflow_adjustment' cli-stripped.js
grep -oP '_l_=3' cli-stripped.js
grep -oP 'Output token limit hit' cli-stripped.js
grep -oP 'tengu_loggia_carousel' cli-stripped.js
grep -oP 'twoStageClassifier' cli-stripped.js
grep -oP 'STOP_HOOK_BLOCK_CAP' cli-stripped.js
grep -oP 'apply_flag_settings' cli-stripped.js
grep -oP 'tengu_bg_daemon' cli-stripped.js

Appendix B: Telemetry Events Confirming Production Use

The presence of structured telemetry events with detailed fields (model names, token counts, durations, attempt numbers) demonstrates these are actively monitored production code paths, not dead code:

tengu_max_tokens_context_overflow_adjustment  {inputTokens, contextLimit, adjustedMaxTokens, attempt}
tengu_refusal_fallback_triggered              {original_model, fallback_model, trigger, request_id}
tengu_malformed_tool_use_response             {will_retry, model}
tengu_reactive_compact_triggered              {effort_level, querySource, precomputed, thresholdSource}
tengu_reactive_compact_succeeded              {preCompactTokenCount, postCompactTokenCount, compactionInputTokens, compactionOutputTokens}
tengu_reactive_compact_failed                 {reason, detail, attempts, totalGroups, durationMs}
tengu_auto_compact_rapid_refill_breaker       {consecutiveRapidRefills, turnsSincePreviousCompact}
tengu_streaming_fallback_to_non_streaming     {model, error, attemptNumber, maxOutputTokens, fallback_cause}
tengu_orphaned_messages_tombstoned            {orphanedMessageCount}
tengu_stop_hook_block_count                   {count, is_subagent, hit_max_turns, hit_cap}
tengu_auto_default_nudge_shown                {current_mode}
tengu_auto_default_nudge_resolved             {choice, current_mode}
tengu_bg_daemon_binary_takeover               {daemon_age_ms}
tengu_bg_daemon_zombie_restart                {pid, sock_exists}
tengu_model_fallback_triggered                {original_model, fallback_model, reason}
tengu_query_error                             {assistantMessages, toolUses}

What Should Happen?

There should be a much deeper look-over of the cache mechanisms in the code base. I would also implore whomever it concerns to dm me— this report excludes security vulnerabilities (because duh)

Error Messages/Logs

Steps to Reproduce

To verify any finding in this report:

# 1. Obtain the binary
npm pack @anthropic-ai/claude-code-linux-x64@2.1.158
tar xzf anthropic-ai-claude-code-linux-x64-2.1.158.tgz

# 2. Extract (using bun-unpack or similar)
# Result: cli-stripped.js

# 3. Verify SHA256
sha256sum cli-stripped.js
# Expected: 7afe182969bc477bb5b4a9b5eeb444a519e79118c897c880211a7f54f27b067f

# 4. Search for any finding
grep -oP 'tengu_max_tokens_context_overflow_adjustment' cli-stripped.js
grep -oP '_l_=3' cli-stripped.js
grep -oP 'Output token limit hit' cli-stripped.js
grep -oP 'tengu_loggia_carousel' cli-stripped.js
grep -oP 'twoStageClassifier' cli-stripped.js
grep -oP 'STOP_HOOK_BLOCK_CAP' cli-stripped.js
grep -oP 'apply_flag_settings' cli-stripped.js
grep -oP 'tengu_bg_daemon' cli-stripped.js

Claude Model

Not sure / Multiple models

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.0.88 it was much less bad

Claude Code Version

2.1.158 is the report, but 2.1.159 did not address anything related and is looking the same as I unpack it

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

Other

Additional Information

This is meant to be a safe disclosure, dm me plz Anthropic I just want the damn harness to work good 😂😂

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗