Repository navigation
[Bug]: Claude Messages to Astra cache plateau/reset with growing history; Codex CLI comparison #3765
Description
Client or integration
Claude Desktop 3P and ocx claude, compared with Codex CLI.
Area
Proxy and routing: Anthropic Messages to native OpenAI Responses.
Summary
With gpt-6-astra, a Claude Desktop main-agent conversation reused essentially the same 27.6k-token prefix across several continuations, despite growing input. Separate synthetic ocx claude sequences show that this route can grow cache reads, but one also went warm → cold within seconds. A Codex CLI control grew its cached prefix on every continuation.
This reports measured symptoms, not a proven OCX root cause or a claim that every continuation must hit cache. Please investigate prefix preservation and effective session/cache identity at the native forward boundary. Existing logs do not distinguish client prefix changes, OCX translation, and upstream cache placement/expiry.
Version
OpenCodex CLI and running proxy: 2.43.0, no version skew at the time of measurement.
Operating system
macOS 26.5.2.
Provider and model
OpenAI / gpt-6-astra, native OpenAI Responses adapter through a local OCX proxy.
Additional environment details
- Desktop embedded Claude Code: 2.1.260, transcript entrypoint
claude-desktop-3p. - Standalone Claude Code: 2.1.258.
- Codex CLI: 0.153.2.
- Local loopback proxy; native OpenAI account-pool route,
openai-responsesadapter. - All successful rows below: resolved
gpt-6-astra, requested efforthigh, HTTP 200, one attempt, the same anonymized account label A. This does not prove a common upstream cache machine/cohort. - Measurements: 2026-09-06, 10:18–10:34 UTC.
Desktop main-agent evidence
OCX's conversation group also contained a child Astra agent and Luna calls. I matched the local Desktop transcript to OCX usage by input/cache counts and timestamps, selected isSidechain=false, and deduplicated assistant content blocks sharing one message ID. Only the six main-agent requests are below:
| Request start UTC | Total input tokens | Cache-read tokens | Read share |
|---|---|---|---|
| 10:18:45 | 43,084 | 0 | 0.0% |
| 10:20:13 | 43,201 | 27,648 | 64.0% |
| 10:21:49 | 43,653 | 27,648 | 63.3% |
| 10:21:54 | 43,816 | 27,648 | 63.1% |
| 10:22:08 | 45,592 | 27,648 | 60.6% |
| 10:24:06 | 47,427 | 28,928 | 61.0% |
Total: 139,520 / 266,773 = 52.3% cache reads; excluding the initial cold request, 62.4%. Requests 2–5 remain at exactly 27,648 cached tokens. The first two responses are text; request 3 returns a Skill call; thinking blocks first appear in response 4. Thus dropping prior thinking blocks cannot explain the earliest plateau by itself. No compaction event appears in this main transcript.
The five child-Astra rows and four Luna rows are excluded from this table. Treating the whole log conversation group as the main agent would give a misleading result.
Synthetic CLI comparisons
Each sequence used a fresh session, the same generated user-text growth pattern, four sequential turns with --resume/exec resume, high effort, and the response OK. No tools were called. Earlier user records were retained; each turn appended 120 new records after an initial 400. Each session's OCX conversation ID remained nonempty and equal across its successful requests.
| Sequence | Total input tokens by turn | Cache-read tokens by turn |
|---|---|---|
| Codex CLI | 29,150 → 31,361 → 33,572 → 35,783 | 0 → 28,928 → 31,232 → 33,408 |
ocx claude --bare, explicit fixed system prompt |
7,388 → 9,599 → 11,810 → 14,021 | 0 → 0 → 9,472 → 11,648 |
ocx claude --bare, no custom system prompt |
7,412 → 9,623 → 11,834 → 14,045 | 0 → 7,296 → 9,472 → 0 |
The last sequence's request starts were 10:33:46, 10:33:49, 10:33:54, and 10:33:58 UTC. Its final successful turn lost all reported cache reads about five seconds after a warm request.
These are minimal controls, not identical wire payloads or a client performance ranking: the clients have different system/tool envelopes, Desktop used real tools, and the standalone/embedded Claude versions differ. Two additional startup rows, one per Claude sequence, returned 499 without reported usage; they are excluded rather than counted as zero-cache successes.
Reproduction
The intermittent miss is not established as deterministic. To repeat the minimal comparison:
-
Use an already running local OCX proxy with the same Astra route. Use a disposable working directory and isolated
CLAUDE_CONFIG_DIRfor Claude. -
Generate records using:
records = '\n'.join( f'Record {i:04d}: amber cedar river stone meadow lantern willow garden copper island violet orchard.' for i in range(start, end) )
Ranges:
[0,400),[400,520),[520,640),[640,760). Each turn asks to retain the reference records, reply onlyOK, and take no external action. -
Claude invocation:
ocx claude --bare -p --output-format json --model gpt-6-astra --effort high --tools '' --strict-mcp-config --mcp-config '{"mcpServers":{}}' --setting-sources '' --max-turns 1, then--resume <returned-session-id>for later turns. Supply authentication appropriate to the local proxy. For this loopback-only sample, an invocation-scopedapiKeyHelperreturned OCX's documentedopencodex-proxymarker; no upstream credential or global auth setting was changed. The first Claude sequence additionally supplied a fixed--system-promptasking only forOKand no tools. The second omitted that override. -
Codex invocation:
codex exec --ignore-user-config --skip-git-repo-check --json -m gpt-6-astra -c 'openai_base_url="http://127.0.0.1:10100/v1"' -c 'model_reasoning_effort="high"' -c 'project_doc_max_bytes=0' -c 'features.shell_tool=false' -c 'features.apply_patch_freeform=false' -c 'sandbox_mode="read-only"' -c 'approval_policy="never"' -. Continue the returned ID withcodex exec resume, retaining the same overrides. Prompts go through stdin. -
Correlate successful requests in
usage.jsonl. OCX/OpenAIinputTokensincludes cached input; Claude's clientinput_tokensexcludes it. Add Claude input + cache-read + cache-creation before comparing. Client totals and OCX totals agreed in these samples.
Source checks and remaining evidence gap
The following installed files were byte-identical to main at commit 06ec553630fa2ee51a96b5cbf694089021249194:
inbound.ts: metadata-backed cache key generation; also contains the assistant thinking-drop policy.claude-messages.ts: native metadata-backed requests synthesize an underscoresession_idheader from that key.outbound.ts: cache counters are mapped into Anthropic usage.src/adapters/openai-responses.ts: includessession_idin forward headers and reads upstream cached-token usage.
The populated conversation groups are consistent with metadata-backed identity, but there was no inbound/outbound wire capture. Stable logged identity/account labels do not establish byte-identical prefixes or the effective forwarded headers. Claude inbound debug was disabled. The next useful capture would record only field presence/equality and prefix-change locations across a warm/cold pair, including tools/instructions/history, cache key, underscore/hyphenated session identifiers, and selected account. No private request content is needed publicly.
Related: #3433 tracks Hermes Chat Completions cache misses with missing identity fields; this report adds the Claude Messages path with populated conversation correlation and a Desktop main-agent plateau. #3719 explicitly concerns intentional Anthropic upstream thinking replay; this report's upstream is OpenAI Astra. No duplicate claiming this exact comparison was found.
Checks
- Searched existing issues and current source.
- Removed personal paths, original prompts, session IDs, account identifiers, credentials, and private tool output.
- No proxy/provider configuration change, restart, or local source patch was made for this investigation.
Activity
github-actions commentedon Sep 6, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
lidge-jun commentedon Sep 6, 2026
리뷰 · 우선순위 56 / 80
이 이슈는 Claude Messages → 네이티브 OpenAI Responses(
gpt-6-astra) 경로에서, 대화가 이어져도 캐시 읽기 토큰이 한 구간에 고정되거나(Desktop 메인 에이전트 27,648 정체), 짧게 따뜻했다가 다시 0으로 떨어지는(합성ocx claude한 시퀀스) 증상을 측정한 보고서입니다. Codex CLI 대조군은 이어질수록 cached prefix가 커졌습니다. 리포터도 말했듯 OCX 루트 원인 확정이 아니라, 클라이언트 prefix 변화 / OCX 번역 / 업스트림 캐시 배치·만료를 로그만으로는 구분할 수 없다는 증거 공백이 핵심입니다.지금
devHEADa349b521b(2.44.0,#3766이후)의 Claude→Responses 캐시 신원은 리포트가 가리킨 파일과 맞습니다.src/claude/inbound.ts는metadata.user_id가 있으면 sha256으로prompt_cache_key를 만들고(cacheKeySource: "metadata"), 없으면 system+tools 코호트 해시("system")로 폴백합니다. assistantthinking/redacted_thinking은 리플레이 시 드롭하는 v1 정책입니다(대략 L237–L238).src/server/claude-messages.ts네이티브 라우트는cacheKeySource === "metadata"일 때만session_id헤더를 캐시 키에서 uuid 형태로 합성하고, system 폴백에는 헤더를 안 붙입니다(대략 L827–L835, audit 133 R2#3).src/claude/outbound.ts는 업스트림 cached 토큰을 Anthropiccache_read_input_tokens등으로 옮기고,src/adapters/openai-responses.ts는session_id를 포워드 헤더에 두고 cached usage를 읽습니다.Desktop 표는 요청 2–5가 입력은 늘어도 cache-read가 정확히 27,648에 머뭅니다. 스킬 호출·thinking 등장 전에 이미 정체라, thinking-drop만으로 초기 평탄을 설명하기 어렵다는 리포터 관찰과 맞습니다. 합성 CLI는 같은 성장 패턴인데 Codex는 캐시가 자라고, Claude bare는 늦게 따뜻해지거나 수 초 만에 식습니다. 와이어 캡처가 없어 “OCX가 prefix를 깨뜨렸다”와 “업스트림이 같은 키/계정에서도 배치를 바꿨다”를 갈라낼 수 없습니다. 관련
#3433(Hermes Chat Completions, 신원 필드 공백)과 겹치는 주제지만 여기는 Claude Messages + 채워진 conversation 상관입니다.#3719는 Anthropic 업스트림 thinking 리플레이 설계라 업스트림이 Astra인 이 이슈와는 다릅니다.release-244는 대시보드·문서 마감 직후이고, 이 이슈는 즉시 머지할 패치보다 관측 설계가 먼저입니다. types/config 분할에 무효화될 PR도 아닙니다.
src/claude/inbound.ts- metadata/systemprompt_cache_key분기와 thinking-drop 정책이 HEAD에 그대로 있음. 평탄 원인을 여기만으로 단정할 수 없음src/server/claude-messages.ts- metadata일 때만session_id합성. system 코호트는 body key만. Desktop이 어느 source인지 이슈만으로는 확정 안 됨src/claude/outbound.ts/src/adapters/openai-responses.ts- 캐시 카운터·헤더 포워딩은 존재. “왜 읽기가 안 늘었는지”를 설명하는 진단 필드는 부족증거 공백 - 따뜻한/차가운 한 쌍에 대해 tools/instructions/history prefix 변화 위치, cache key, underscore/hyphen session id, 선택 계정만 빨간 캡처가 없음(리포터도 다음 단계로 제안)
#3433/#3719- 인접하지만 경로·업스트림이 다름. 한 PR로 뭉개면 안 됨메인테이너의 판단이 필요한 지점
needs-repro/needs-design관측 이슈로 둘지cacheKeySource가 metadata인지 system인지 먼저 확인할지#3433과 공동 “cache identity” 스펙으로 묶을지, Claude Messages만 따로 둘지너의 추천
열린 채로 두되 구현 PR은 아직 받지 마세요. 다음 한 장은 (1) Desktop/합성 한 쌍의 빨간 캡처 체크리스트(캐시 키 source,
session_id유무·형태, tools/system/history prefix 동등 여부, 계정 라벨), (2)usage.jsonl에 cacheKeySource·session_id 존재 여부 같은 비공개 메타를 남길지 여부입니다. thinking-drop을 바로 끄거나 session_id를 system 키에도 붙이는 PR은 audit 이유를 깨기 쉬우니 관측 전에는 거절하세요. release-244 기차와는 독립입니다.이 댓글은 grok-bot이 작성했습니다
devin-ai-integration commentedon Sep 24, 2026
Triage:
priority: P2— Claude Messages cache plateau/reset with growing history.Criteria (P2): Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue, perf, or CI reliability.
Matched pull requests:
priority: P3]lidge-jun commentedon Sep 27, 2026
Release-train 4 disposition: keeping this open, with no proxy change in this release. The Claude Messages → Responses path derives
prompt_cache_keyfrommetadata.user_idwhen present and otherwise from a system+tools hash (src/claude/inbound.ts:465-502), so the two symptoms here (a 27,648-token plateau, and a short warm run that drops back to 0) can come from different places: the client changing an early part of the prefix, our translation changing it, or upstream cache placement. Cache counters alone can't separate those.Current
devhas a diagnostic for exactly this: run withOPENCODEX_CACHE_DEBUG=1and it records, per request, process-keyed tags for the inbound and outbound prefix sections alongside the cache counters (src/usage/cache-diagnostic.ts). The useful data is a comparison of two consecutive turns from a plateaued session: which prefix sections kept the same tag and which changed, theprompt_cache_keysource (metadataorsystem), andcached_tokensfor each turn. Please describe which sections matched or changed rather than pasting the tags, request IDs or any prompt text. If an early section changes between turns, the inbound and outbound tags show whether the client or our translation changed it, which narrows where to look. If everything matches andcached_tokensstill drops, that points away from our translation, though it doesn't prove where the cache miss happened.lidge-jun commentedon Oct 7, 2026
Thank you for the detailed cache measurements. Cache counts alone do not establish that OpenCodex changed the prefix or cache identity, and the requested diagnostic comparison remains missing. Please reopen with current versions, sanitized
ocx doctoroutput, and a minimal warm/cold pair describing inbound versus outbound prefix equality and cache-key source without sharing prompts or identifiers.Closing as not reproducible with the current evidence; this is not a fix claim.