Excerpts of the Claude Code CLI's OAuth credential handling, recovered from the embedded (minified) JS source in the shipped binaries. Identifiers are minified; readable names are given in comments. Extracted with `strings -n 8 ` and python substring search (see the report's Appendix for the exact commands). Binaries: linux-x64 2.1.234 ~/.local/share/claude/versions/2.1.234 (what bb spawns on this machine) darwin-arm64 2.1.234 and 2.1.233 https://downloads.claude.ai/claude-code-releases//darwin-arm64/claude -------------------------------------------------------------------------------- 1) getClaudeAIOAuthTokens (sync, `ua`) and getClaudeAIOAuthTokensAsync (`s5`/`az`) are MEMOIZED (`iu(...)` = lodash-style memoize with `.cache`; `B0r/FCr(...)` = promise memoize). linux 2.1.234: ua=iu(()=>{ if(pg())return null; // CLAUDE_CODE_SIMPLE / --bare if(V.CLAUDE_CODE_OAUTH_TOKEN)return{accessToken:V.CLAUDE_CODE_OAUTH_TOKEN, ...}; let e=nye(),t=(r)=>({accessToken:r, ...}); // fd token if(e&&(!x6t()||IH()))return t(e); if(IH())return null; try{let o=al().read()?.claudeAiOauth;if(o?.accessToken)return o}catch(r){ke(r)} // <- read from secure storage ONCE, then cached if(e)return t(e); return null}); s5=B0r(async()=>{ ... try{let n=(await al().readAsync())?.claudeAiOauth;if(n?.accessToken)return n}catch(t){ke(t)} ... }); -------------------------------------------------------------------------------- 2) The only pre-request cache invalidation: `A8_` (linux) / `OG_` (darwin 2.1.234) / `bM_` (darwin 2.1.233), called first thing from the "refresh OAuth token if needed" step (`q6s`/`JWs`, wrapped by `Nb`/`Fb`) which the API client runs before creating each request ("[API:request] Creating client ... await Fb(); let m=ua()"). async function A8_(){ try{ let{mtimeMs:e}=await g7t.stat(ERr.join(tee(),".credentials.json")); // /.credentials.json if(e!==Bmd)Bmd=e,OV() // mtime changed -> clear ua/s5 memo caches (OV = s8s + ...) }catch{ // file missing (macOS keychain users): clear memo caches and re-read via the storage backend ua.cache?.clear?.(),s5.cache?.clear?.(); let t=(await s5())?.accessToken??null; if(t!==Fmd)Fmd=t,rye(),QRe() } } function s8s(){ua.cache?.clear?.(),s5.cache?.clear?.(),oye()} function OV(){s8s(),rye(),QRe()} => If /.credentials.json EXISTS and its mtime is unchanged, nothing is invalidated and the process keeps the token it loaded at startup (this is what the preserve-mtime repro exercises on Linux). => Other invalidation points are only: successful token refresh/save, /login inside the same process, 401 recovery ("tengu_oauth_401_recovered_from_disk" / "..._from_keychain"), structured-IO env update. A 429 (usage limit) does NOT invalidate anything. -------------------------------------------------------------------------------- 3) Storage backend `al()`. linux: function al(){if(TJu)return TJu;return q4s} // q4s = {name:"plaintext", read(){ readFileSync(/.credentials.json) } ...} (keychain code compiled out) darwin: function al(){if(MXu)return MXu;return _Xu(G3s,j2o)} // keychain-with-plaintext-fallback darwin keychain backend (2.1.234; identical in 2.1.233 with other minified names) has its OWN 30 s cache and serves STALE data when `security` fails/times out (2 s timeout on re-reads vs 10 s for the startup prefetch): G3s={name:"keychain", read(){let e=AR.cache;if(Date.now()-e.cachedAt<$2o)return e.data; // $2o = 30000 ms let t=AR.lastReadFailure;if(t!==null&&Date.now()-t{ ... if(o===MI){ T("[keychain] readAsync failed; not caching a null"); AR.lastReadFailure=Date.now(); if(e.data!==null)AR.cache={data:e.data,cachedAt:Date.now()}; return e.data } // failure => stale token re-cached for another 30 s ... })}, invalidateCache(){iee()}, ...} function iee(){AR.cache={data:null,cachedAt:0},AR.generation++,AR.readInFlight=null,AR.lastReadFailure=null} var MVe="-credentials",$2o=30000,z3s=1000; qxr=2000 (security timeout for re-reads); SF_=1e4 (startup prefetch timeout) The plaintext fallback file on darwin is only deleted when a keychain write succeeds AND the keychain previously held no entry (`_Xu.update`: `if(i===null)await t.delete(o)`), so a leftover ~/.claude/.credentials.json can coexist with the keychain entry that `claude login` actually updates. -------------------------------------------------------------------------------- 4) Behavioural difference seen in the repro (CORRECTED after verification): the "1 call per 429 turn" behaviour seen in the first draft's default runs belongs to the Agent SDK's own bundled CLI (@anthropic-ai/claude-agent-sdk-linux-x64 0.3.197 = Claude Code 2.1.197), which the SDK spawns when no pathToClaudeCodeExecutable is given. bb never uses that binary: it passes the PATH `claude` (resolveClaudeCodeExecutable). The real 2.1.233 AND 2.1.234 binaries both retry a usage-limit 429 (10 retries with backoff, 11 calls per turn) and only then return `result is_error=true`. None of 2.1.197 / 2.1.233 / 2.1.234 invalidates the token cache on 429 (preserve-mtime run pins token A on all three).