Problem (one or two sentences)
Prompt caching for OpenRouter can stop producing cache hits during multi-turn Zoo Code tasks. Zoo emits explicit cache breakpoints for older Claude models, but it does not send OpenRouter a stable session identifier and it skips cache breakpoints entirely for newer cache-capable Claude models that are absent from a static allowlist.
Context (who is affected and when)
Users running agentic, multi-turn tasks through OpenRouter are affected, especially with Anthropic Claude models and large repeated system prompts/tool schemas. A user reported that caching stopped approximately 10–15 days before August 18, 2026; investigation found no corresponding Zoo request-construction regression in that window, but OpenRouter's current sticky-routing behavior exposes existing gaps in Zoo's integration.
Reproduction steps
- Install Zoo Code v3.78.0 and configure the OpenRouter provider.
- Select a cache-capable Anthropic model such as
anthropic/claude-sonnet-4.5, or a newer model not present in Zoo's static cache allowlist such as anthropic/claude-opus-4.8.
- Start a task whose reusable prompt prefix exceeds the model's minimum cacheable token count.
- Run several consecutive agent turns within the cache TTL.
- Inspect each generation in the OpenRouter Activity page or
/api/v1/generation response.
- Observe that
usage.prompt_tokens_details.cached_tokens can remain zero when turns route to different upstream endpoints. For a newer unlisted Claude model, inspect the outgoing request and observe that Zoo adds no explicit block-level cache_control markers.
- On a cache-write turn, observe that OpenRouter may return
usage.prompt_tokens_details.cache_write_tokens, but Zoo does not expose that value in its usage chunk.
Expected result
Zoo should send a stable OpenRouter session_id for every turn in the same task, add explicit cache breakpoints to every Anthropic model whose live metadata reports prompt-cache support, and report both cache-read and cache-write tokens.
Actual result
Zoo sends no session_id, so OpenRouter may route consecutive turns to different endpoints before a hit establishes affinity; newer cache-capable Claude models can receive no cache markers due to a stale static allowlist; and cache-write tokens are omitted from Zoo usage reporting.
Variations tried (optional)
- Confirmed existing allowlisted Claude models still receive block-level
cache_control markers.
- Confirmed OpenRouter's current model API advertises
input_cache_read pricing for newer Claude models absent from Zoo's allowlist.
- Confirmed no relevant Zoo OpenRouter caching code changed during the reported 10–15 day window.
- OpenRouter's current guidance recommends a stable top-level
session_id for agent workflows so sticky routing starts after the first successful request instead of only after a detected cache hit.
- Selecting a specific OpenRouter provider endpoint can reduce endpoint drift but does not fix the stale model allowlist or missing write accounting.
App Version
v3.78.0
API Provider (optional)
OpenRouter
Model Used (optional)
anthropic/claude-sonnet-4.5; also reproducible by request inspection with newer cache-capable Claude models such as anthropic/claude-opus-4.8
Zoo Code Task Links (optional)
N/A
Relevant logs or errors (optional)
OpenRouter usage on affected turns:
usage.prompt_tokens_details.cached_tokens = 0
OpenRouter currently also reports cache creation as:
usage.prompt_tokens_details.cache_write_tokens
Relevant implementation gaps:
- src/api/providers/openrouter.ts gates cache markers with OPEN_ROUTER_PROMPT_CACHING_MODELS
- src/api/providers/openrouter.ts does not send metadata.taskId as session_id
- src/api/providers/openrouter.ts reads cached_tokens but not cache_write_tokens
Investigation notes
OpenRouter prompt caching has not been removed. Current OpenRouter documentation states that a warm cache can only be reused on the provider endpoint where it was written. Without session_id, sticky routing is derived from opening messages and only activates after a cache hit is detected; with session_id, it activates after the first successful request. Zoo already has a stable per-task identifier available as metadata.taskId, making it suitable for OpenRouter session affinity.
Proposed fix:
- Include
metadata.taskId as top-level session_id in OpenRouter chat-completion requests.
- For
anthropic/*, use live model.info.supportsPromptCache to enable explicit breakpoints, retaining the existing allowlist only as a stale-metadata fallback.
- Preserve the existing explicit Gemini behavior rather than adding breakpoints to every implicitly cached Gemini model.
- Map
prompt_tokens_details.cache_write_tokens to Zoo's cacheWriteTokens usage field.
- Add focused tests for session affinity, newly released Claude models, and cache read/write accounting.
Problem (one or two sentences)
Prompt caching for OpenRouter can stop producing cache hits during multi-turn Zoo Code tasks. Zoo emits explicit cache breakpoints for older Claude models, but it does not send OpenRouter a stable session identifier and it skips cache breakpoints entirely for newer cache-capable Claude models that are absent from a static allowlist.
Context (who is affected and when)
Users running agentic, multi-turn tasks through OpenRouter are affected, especially with Anthropic Claude models and large repeated system prompts/tool schemas. A user reported that caching stopped approximately 10–15 days before August 18, 2026; investigation found no corresponding Zoo request-construction regression in that window, but OpenRouter's current sticky-routing behavior exposes existing gaps in Zoo's integration.
Reproduction steps
anthropic/claude-sonnet-4.5, or a newer model not present in Zoo's static cache allowlist such asanthropic/claude-opus-4.8./api/v1/generationresponse.usage.prompt_tokens_details.cached_tokenscan remain zero when turns route to different upstream endpoints. For a newer unlisted Claude model, inspect the outgoing request and observe that Zoo adds no explicit block-levelcache_controlmarkers.usage.prompt_tokens_details.cache_write_tokens, but Zoo does not expose that value in its usage chunk.Expected result
Zoo should send a stable OpenRouter
session_idfor every turn in the same task, add explicit cache breakpoints to every Anthropic model whose live metadata reports prompt-cache support, and report both cache-read and cache-write tokens.Actual result
Zoo sends no
session_id, so OpenRouter may route consecutive turns to different endpoints before a hit establishes affinity; newer cache-capable Claude models can receive no cache markers due to a stale static allowlist; and cache-write tokens are omitted from Zoo usage reporting.Variations tried (optional)
cache_controlmarkers.input_cache_readpricing for newer Claude models absent from Zoo's allowlist.session_idfor agent workflows so sticky routing starts after the first successful request instead of only after a detected cache hit.App Version
v3.78.0
API Provider (optional)
OpenRouter
Model Used (optional)
anthropic/claude-sonnet-4.5; also reproducible by request inspection with newer cache-capable Claude models such asanthropic/claude-opus-4.8Zoo Code Task Links (optional)
N/A
Relevant logs or errors (optional)
Investigation notes
OpenRouter prompt caching has not been removed. Current OpenRouter documentation states that a warm cache can only be reused on the provider endpoint where it was written. Without
session_id, sticky routing is derived from opening messages and only activates after a cache hit is detected; withsession_id, it activates after the first successful request. Zoo already has a stable per-task identifier available asmetadata.taskId, making it suitable for OpenRouter session affinity.Proposed fix:
metadata.taskIdas top-levelsession_idin OpenRouter chat-completion requests.anthropic/*, use livemodel.info.supportsPromptCacheto enable explicit breakpoints, retaining the existing allowlist only as a stale-metadata fallback.prompt_tokens_details.cache_write_tokensto Zoo'scacheWriteTokensusage field.