From e63cb590270b463698033eaecc6de010e153bce7 Mon Sep 17 00:00:00 2001 From: agentforce314 Date: Mon, 13 Jul 2026 14:04:45 -0700 Subject: [PATCH] fix(anthropic): stop the OAuth identity rewrite from corrupting .clawcodex paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _prepare_subscription_request's clean() rewrote EVERY case-insensitive "clawcodex" in the system prompt to "Claude Code" — including filesystem paths. On Claude-subscription logins the model was told its memory lives at ~/.Claude Code/projects//memory and its history at ~/.Claude Code/sessions — literal nonexistent locations it then searched verbatim. This is the real source of the "guessed wrong paths" #706/#707 addressed: those fixes emit correct paths, and this rewrite corrupted them after prompt assembly, at the provider layer. Constrain the rewrite to standalone brand mentions: matches inside path segments (~/.clawcodex, /etc/clawcodex), env vars ($CLAWCODEX_CONFIG_DIR), module/file names (clawcodex_dirs), and domains (clawcodex.app) survive verbatim. Prose disguise and the official-prefix block are unchanged. Co-Authored-By: Claude Fable 5 --- src/providers/anthropic_provider.py | 16 +++++++++- tests/test_anthropic_subscription.py | 47 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/providers/anthropic_provider.py b/src/providers/anthropic_provider.py index 69f2b4bb..afe52ed4 100644 --- a/src/providers/anthropic_provider.py +++ b/src/providers/anthropic_provider.py @@ -309,7 +309,21 @@ def _prepare_subscription_request( prefix = "You are Claude Code, Anthropic's official CLI for Claude." def clean(text: str) -> str: - return re.sub(r"claw[ -]?codex", "Claude Code", text, flags=re.IGNORECASE) + # Disguise standalone brand mentions only. A match inside a + # machine-readable token must survive verbatim: path segments + # (~/.clawcodex/sessions, /etc/clawcodex), env vars + # ($CLAWCODEX_CONFIG_DIR), module/file names (clawcodex_dirs), + # and domains (clawcodex.app). Rewriting those made the system + # prompt name literal nonexistent paths — ~/.Claude Code/… — + # which the model then obediently searched (the "guessed" wrong + # paths #706 set out to fix were in fact produced here, after + # prompt assembly). + return re.sub( + r"(? None: assert result.usage["billing_mode"] == "subscription" +def test_identity_rewrite_preserves_paths_env_vars_and_domains(monkeypatch) -> None: + """The OAuth identity disguise must not corrupt machine-readable tokens. + + Regression for the `.Claude Code` misdirection: rewriting `clawcodex` + inside paths turned the #706/#707 prompt lines (and the auto-memory + section) into literal nonexistent locations — `~/.Claude Code/sessions` + — which the model then searched verbatim. + """ + monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False) + with patch.object(auth, "get_valid_credentials", return_value=_credentials()), \ + patch("src.providers.anthropic_provider.anthropic.Anthropic"): + provider = AnthropicProvider(api_key="") + + system_blocks = [ + {"type": "text", "text": ( + "- clawcodex data directory: /Users/u/.clawcodex — sessions live in " + "the sessions/ subdirectory, or under $CLAWCODEX_CONFIG_DIR." + )}, + {"type": "text", "text": ( + "You have a persistent, file-based memory system at " + "`/Users/u/.clawcodex/projects/-Users-u-proj/memory/`.\n" + 'Grep with pattern="x" path="/Users/u/.clawcodex/sessions/" glob="*.json"\n' + "Managed policy lives in /etc/clawcodex; see clawcodex_dirs.py and " + "https://clawcodex.app/install.sh. Run clawcodex to start. clawcodex, " + "the tool, saves hooks in .clawcodex/settings.json." + )}, + ] + _, _, system = provider._prepare_subscription_request([], None, system_blocks) + + assert system[0]["text"].startswith("You are Claude Code") + text = "\n".join(b["text"] for b in system[1:]) + # Machine-readable tokens survive verbatim. + assert "/Users/u/.clawcodex — sessions" in text + assert "/Users/u/.clawcodex/projects/-Users-u-proj/memory/" in text + assert '/Users/u/.clawcodex/sessions/' in text + assert "$CLAWCODEX_CONFIG_DIR" in text + assert "/etc/clawcodex" in text + assert "clawcodex_dirs.py" in text + assert "clawcodex.app" in text + assert ".clawcodex/settings.json" in text + # Nothing is rewritten INTO a bogus path. + assert ".Claude Code" not in text + # Standalone brand mentions are still disguised. + assert "Claude Code data directory:" in text + assert "Run Claude Code to start. Claude Code, the tool," in text + + def test_subscription_usage_is_not_priced_as_api_billing() -> None: from src.cost_tracker import record_api_usage with patch("src.cost_tracker.add_to_total_cost_state"), \