项目文件夹

文件
Ali Khokhar aba94d5c3c Disable nonessential Anthropic traffic for FCC Claude sessions (#1083)
## Problem

Claude proxy environment policy was duplicated between `fcc-claude`,
managed messaging, and live smoke drivers. Managed messaging preserved a
legacy endpoint variable, inherited more Anthropic state, and did not
disable nonessential traffic, so FCC-launched Claude sessions could
drift apart.

## Changes

| Before | After |
| --- | --- |
| `fcc-claude` and managed messaging assembled proxy environments
independently. | One shared owner strips inherited Anthropic variables
and configures proxy URL, auth, discovery, compaction, and
nonessential-traffic policy. |
| Managed messaging carried a `/v1` API URL and converted it back to a
proxy root while also setting a legacy endpoint variable. | Managed
messaging receives the loopback-safe proxy root and uses the supported
`ANTHROPIC_BASE_URL` contract directly. |
| Managed and interactive policy could diverge while smoke drivers
duplicated both shapes. | `fcc-claude`, messaging, and Claude smoke
drivers use the same canonical environment builder. |
| Managed execution concerns were mixed with shared proxy policy. |
Messaging adds only noninteractive process settings and keeps `--model
opus` plus stream-JSON flags in command construction. |
| IDE examples left nonessential Anthropic traffic enabled. | VS Code
and JetBrains examples disable nonessential Anthropic traffic. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR centralizes Claude Code proxy environment setup for FCC-launched
sessions. The main changes are:

- Adds one shared builder for Claude proxy environment variables.
- Routes managed messaging sessions through the same proxy policy as
`fcc-claude`.
- Strips inherited `ANTHROPIC_*` state before launching Claude.
- Sets the nonessential-traffic disable flag for managed and interactive
Claude launches.
- Updates smoke tests, docs, and version metadata for the new proxy-root
URL shape.
</details>

<h3>Confidence Score: 5/5</h3>

This looks safe to merge.

No blocking issues were found in the changed code. Managed Claude
launches now use the shared environment builder, and the managed path
now sets the nonessential-traffic disable flag.

No files need attention.

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- The executable harness claude\_env\_policy\_harness.py was generated
to enable direct module-level runtime proof without requiring a real
Claude binary or live Anthropic credentials.
- A focused pytest run was executed, and it completed with 14 tests
passing and exit code 0, validating the harness workflow.

<a
href="https://app.greptile.com/trex/runs/14173386/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<details open><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| src/free_claude_code/cli/claude_env.py | Adds the shared Claude proxy
environment builder and canonical traffic-disable policy. |
| src/free_claude_code/cli/managed/claude.py | Delegates managed Claude
environment construction to the shared proxy builder. |
| src/free_claude_code/runtime/application.py | Passes the loopback-safe
proxy root into the managed Claude session manager. |
| src/free_claude_code/cli/managed/session.py | Renames managed session
URL state to use the proxy-root contract. |
| src/free_claude_code/cli/managed/manager.py | Carries the proxy-root
URL through manager-created managed sessions. |

</details>

<sub>Reviews (2): Last reviewed commit: ["Keep README focused on client
setup"](https://github.com/alishahryar1/free-claude-code/commit/83739a2f8953771ec9e447fc83a2057b08708891)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=43684511)</sub>

<!-- /greptile_comment -->
2026-07-12 15:05:54 -07:00

787 行
23 KiB
Python

"""Claude Code CLI characterization helpers for provider smoke matrices."""
import json
import os
import re
import subprocess
import time
import uuid
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any
from free_claude_code.cli.claude_env import build_claude_proxy_env
from smoke.lib.child_process import run_captured_text
from smoke.lib.config import ProviderModel, SmokeConfig, redacted
from smoke.lib.server import RunningServer
REGRESSION_CLASSIFICATIONS = frozenset({"harness_bug", "product_failure"})
_HTTP_REGRESSION_PATTERNS = (
r'POST /v1/messages[^"\n]* HTTP/1\.1" 4(?!01|03|04|08|09)\d\d',
r'POST /v1/messages[^"\n]* HTTP/1\.1" 5\d\d',
)
_UPSTREAM_UNAVAILABLE_MARKERS = (
"upstream_unavailable",
"readtimeout",
"connecterror",
"connection refused",
"timed out",
"rate limit",
"overloaded",
"capacity",
"upstream provider",
"provider api request failed",
"httpstatuserror",
)
_HTTP_429_PATTERNS = (
r'HTTP/1\.[01]" 429\b',
r"\bHTTP/1\.[01] 429\b",
r"\bstatus_code=429\b",
r"\bstatus[=:]\s*429\b",
r"\b429 Too Many Requests\b",
)
_MISSING_ENV_MARKERS = (
"api key",
"not logged in",
"authentication",
"permission denied",
)
_EMPTY_MCP_CONFIG = '{"mcpServers":{}}'
_SUBAGENT_SYSTEM_PROMPT = (
"You are a deterministic smoke-test coordinator. Use Agent when asked to "
"use a subagent."
)
@dataclass(frozen=True, slots=True)
class ClaudeCliRun:
command: tuple[str, ...]
returncode: int | None
stdout: str
stderr: str
duration_s: float
timed_out: bool = False
@property
def combined_output(self) -> str:
return f"{self.stdout}\n{self.stderr}"
@dataclass(frozen=True, slots=True)
class CliMatrixOutcome:
model: str
full_model: str
source: str
feature: str
outcome: str
classification: str
duration_s: float
cli_returncode: int | None
token_evidence: dict[str, Any]
request_count: int
log_path: str
stdout_excerpt: str
stderr_excerpt: str
log_excerpt: str
def run_claude_cli(
*,
claude_bin: str,
server: RunningServer,
config: SmokeConfig,
cwd: Path,
prompt: str,
tools: str | None,
bare: bool = True,
pre_tool_args: tuple[str, ...] = (),
extra_args: tuple[str, ...] = (),
session_id: str | None = None,
resume_session_id: str | None = None,
no_session_persistence: bool = True,
) -> ClaudeCliRun:
"""Run Claude Code CLI against the local smoke proxy."""
cwd.mkdir(parents=True, exist_ok=True)
cmd = list(
_build_claude_cli_command(
claude_bin=claude_bin,
prompt=prompt,
tools=tools,
bare=bare,
pre_tool_args=pre_tool_args,
extra_args=extra_args,
session_id=session_id,
resume_session_id=resume_session_id,
no_session_persistence=no_session_persistence,
)
)
env = build_claude_proxy_env(
proxy_root_url=server.base_url,
auth_token=config.settings.anthropic_auth_token,
base_env=os.environ,
)
env["TERM"] = "dumb"
env["NO_COLOR"] = "1"
env["PYTHONIOENCODING"] = "utf-8"
started = time.monotonic()
try:
result = run_captured_text(
cmd,
cwd=cwd,
env=env,
timeout=config.timeout_s,
check=False,
)
except subprocess.TimeoutExpired as exc:
return ClaudeCliRun(
command=tuple(cmd),
returncode=None,
stdout=_coerce_timeout_text(exc.stdout),
stderr=_coerce_timeout_text(exc.stderr),
duration_s=time.monotonic() - started,
timed_out=True,
)
return ClaudeCliRun(
command=tuple(cmd),
returncode=result.returncode,
stdout=_coerce_timeout_text(result.stdout),
stderr=_coerce_timeout_text(result.stderr),
duration_s=time.monotonic() - started,
)
def _build_claude_cli_command(
*,
claude_bin: str,
prompt: str,
tools: str | None,
bare: bool = True,
pre_tool_args: tuple[str, ...] = (),
extra_args: tuple[str, ...] = (),
session_id: str | None = None,
resume_session_id: str | None = None,
no_session_persistence: bool = True,
) -> tuple[str, ...]:
cmd: list[str] = [claude_bin]
if bare:
cmd.append("--bare")
if resume_session_id:
cmd.extend(["--resume", resume_session_id])
if session_id:
cmd.extend(["--session-id", session_id])
cmd.extend(
[
"--output-format",
"stream-json",
"--include-partial-messages",
"--verbose",
"--permission-mode",
"bypassPermissions",
"--dangerously-skip-permissions",
"--model",
"sonnet",
]
)
if no_session_persistence:
cmd.append("--no-session-persistence")
cmd.extend(pre_tool_args)
if tools is not None:
cmd.extend(["--tools", tools])
if tools:
cmd.extend(["--allowedTools", tools])
cmd.extend(extra_args)
cmd.extend(["-p", prompt])
return tuple(cmd)
def run_cli_feature_probes(
*,
claude_bin: str,
server: RunningServer,
smoke_config: SmokeConfig,
provider_model: ProviderModel,
model_dir: Path,
marker_prefix: str,
) -> list[CliMatrixOutcome]:
return [
_basic_text(
claude_bin, server, smoke_config, provider_model, model_dir, marker_prefix
),
_thinking(
claude_bin, server, smoke_config, provider_model, model_dir, marker_prefix
),
_tool_use_roundtrip(
claude_bin, server, smoke_config, provider_model, model_dir, marker_prefix
),
_interleaved_thinking_tool(
claude_bin, server, smoke_config, provider_model, model_dir, marker_prefix
),
_subagent_task(
claude_bin, server, smoke_config, provider_model, model_dir, marker_prefix
),
_compact_command(
claude_bin, server, smoke_config, provider_model, model_dir, marker_prefix
),
]
def read_log_offset(log_path: Path) -> int:
"""Return the current text length of a smoke server log."""
if not log_path.is_file():
return 0
return len(log_path.read_text(encoding="utf-8", errors="replace"))
def read_log_delta(log_path: Path, offset: int) -> str:
"""Return smoke server log text written after ``offset``."""
if not log_path.is_file():
return ""
text = log_path.read_text(encoding="utf-8", errors="replace")
return text[offset:]
def token_evidence(
*,
feature: str,
marker: str,
run: ClaudeCliRun,
log_delta: str,
) -> dict[str, Any]:
"""Collect compact evidence for a CLI feature probe."""
combined = f"{run.combined_output}\n{log_delta}"
lower = combined.lower()
return {
"feature": feature,
"marker_present": bool(marker and marker in combined),
"thinking_delta_count": combined.count("thinking_delta"),
"tool_use_count": combined.count('"tool_use"'),
"tool_result_count": combined.count('"tool_result"'),
"agent_catalog_present": _tool_catalog_has(log_delta, "Agent"),
"agent_tool_count": _agent_tool_count(combined),
"agent_result_count": _agent_result_count(combined),
"task_tool_count": combined.count('"name": "Task"')
+ combined.count('"name":"Task"'),
"run_in_background_false": "run_in_background" in combined and "false" in lower,
"compact_boundary": "compact_boundary" in combined,
"compact_metadata": "compact_metadata" in combined,
"http_422": 'HTTP/1.1" 422' in combined,
"http_500": bool(re.search(r'HTTP/1\.1" 5\d\d', combined)),
"timed_out": run.timed_out,
}
def classify_probe(
*,
run: ClaudeCliRun,
log_delta: str,
marker: str,
requires_tool_result: bool = False,
requires_agent: bool = False,
requires_task: bool = False,
requires_compact: bool = False,
) -> tuple[str, str]:
"""Classify a probe without failing compatibility characterization failures."""
combined = f"{run.combined_output}\n{log_delta}"
lower = combined.lower()
if _has_proxy_regression(log_delta):
return "failed", "product_failure"
if run.returncode != 0 and any(
marker_text in lower for marker_text in _MISSING_ENV_MARKERS
):
return "skipped", "missing_env"
if run.timed_out:
return "failed", "probe_timeout"
if requires_agent and not _tool_catalog_has(log_delta, "Agent"):
return "failed", "harness_bug"
marker_ok = not marker or marker in combined
tool_ok = not requires_tool_result or '"tool_result"' in combined
agent_ok = not requires_agent or (
_agent_tool_count(combined) > 0 and _agent_result_count(combined) > 0
)
task_ok = not requires_task or (
('"name": "Task"' in combined or '"name":"Task"' in combined)
and "run_in_background" in combined
and "false" in lower
)
compact_ok = not requires_compact or (
"compact_boundary" in combined
or "compact_metadata" in combined
or "/compact" in combined
or "compact" in lower
)
cli_ok = run.returncode == 0
if cli_ok and marker_ok and tool_ok and agent_ok and task_ok and compact_ok:
return "passed", "passed"
if _has_upstream_unavailable_text(combined):
return "failed", "upstream_unavailable"
if not _has_proxy_request(log_delta):
return "failed", "harness_bug"
return "failed", "model_feature_failure"
def make_outcome(
*,
model: str,
full_model: str,
source: str,
feature: str,
marker: str,
run: ClaudeCliRun,
log_delta: str,
log_path: Path,
requires_tool_result: bool = False,
requires_agent: bool = False,
requires_task: bool = False,
requires_compact: bool = False,
) -> CliMatrixOutcome:
"""Build one report outcome from a CLI run and its server log delta."""
outcome, classification = classify_probe(
run=run,
log_delta=log_delta,
marker=marker,
requires_tool_result=requires_tool_result,
requires_agent=requires_agent,
requires_task=requires_task,
requires_compact=requires_compact,
)
evidence = token_evidence(
feature=feature,
marker=marker,
run=run,
log_delta=log_delta,
)
return CliMatrixOutcome(
model=model,
full_model=full_model,
source=source,
feature=feature,
outcome=outcome,
classification=classification,
duration_s=round(run.duration_s, 3),
cli_returncode=run.returncode,
token_evidence=evidence,
request_count=_request_count(log_delta),
log_path=str(log_path),
stdout_excerpt=_excerpt(run.stdout),
stderr_excerpt=_excerpt(run.stderr),
log_excerpt=_excerpt(log_delta),
)
def write_matrix_report(
config: SmokeConfig,
outcomes: list[CliMatrixOutcome],
*,
target: str,
filename_prefix: str,
) -> Path:
"""Write a Claude CLI compatibility matrix report."""
config.results_dir.mkdir(parents=True, exist_ok=True)
path = (
config.results_dir
/ f"{filename_prefix}-matrix-{config.worker_id}-{int(time.time())}.json"
)
payload = {
"started_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"worker_id": config.worker_id,
"target": target,
"models": sorted({outcome.full_model for outcome in outcomes}),
"outcomes": [asdict(outcome) for outcome in outcomes],
}
path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
return path
def regression_failures(outcomes: list[CliMatrixOutcome]) -> list[str]:
"""Return report lines for classifications that should fail pytest."""
return [
f"{outcome.full_model} {outcome.feature}: {outcome.classification}"
for outcome in outcomes
if outcome.classification in REGRESSION_CLASSIFICATIONS
]
def _basic_text(
claude_bin: str,
server: RunningServer,
smoke_config: SmokeConfig,
provider_model: ProviderModel,
model_dir: Path,
marker_prefix: str,
) -> CliMatrixOutcome:
marker = _marker(marker_prefix, "BASIC")
return _run_probe(
claude_bin=claude_bin,
server=server,
smoke_config=smoke_config,
provider_model=provider_model,
workspace=model_dir / "basic_text",
feature="basic_text",
marker=marker,
prompt=f"Reply with exactly {marker} and no other text.",
tools="",
)
def _thinking(
claude_bin: str,
server: RunningServer,
smoke_config: SmokeConfig,
provider_model: ProviderModel,
model_dir: Path,
marker_prefix: str,
) -> CliMatrixOutcome:
marker = _marker(marker_prefix, "THINK")
return _run_probe(
claude_bin=claude_bin,
server=server,
smoke_config=smoke_config,
provider_model=provider_model,
workspace=model_dir / "thinking",
feature="thinking",
marker=marker,
prompt=(
"Think privately about the request, then reply with exactly "
f"{marker} and no other text."
),
tools="",
extra_args=("--effort", "high"),
)
def _tool_use_roundtrip(
claude_bin: str,
server: RunningServer,
smoke_config: SmokeConfig,
provider_model: ProviderModel,
model_dir: Path,
marker_prefix: str,
) -> CliMatrixOutcome:
marker = _marker(marker_prefix, "TOOL")
workspace = model_dir / "tool_use_roundtrip"
(workspace / "smoke-read.txt").parent.mkdir(parents=True, exist_ok=True)
(workspace / "smoke-read.txt").write_text(marker, encoding="utf-8")
return _run_probe(
claude_bin=claude_bin,
server=server,
smoke_config=smoke_config,
provider_model=provider_model,
workspace=workspace,
feature="tool_use_roundtrip",
marker=marker,
prompt=(
"Use the Read tool to read smoke-read.txt. Reply with exactly the "
"secret token from that file and no other text."
),
tools="Read",
requires_tool_result=True,
)
def _interleaved_thinking_tool(
claude_bin: str,
server: RunningServer,
smoke_config: SmokeConfig,
provider_model: ProviderModel,
model_dir: Path,
marker_prefix: str,
) -> CliMatrixOutcome:
marker = _marker(marker_prefix, "INTERLEAVED")
workspace = model_dir / "interleaved_thinking_tool"
(workspace / "smoke-interleaved.txt").parent.mkdir(parents=True, exist_ok=True)
(workspace / "smoke-interleaved.txt").write_text(marker, encoding="utf-8")
return _run_probe(
claude_bin=claude_bin,
server=server,
smoke_config=smoke_config,
provider_model=provider_model,
workspace=workspace,
feature="interleaved_thinking_tool",
marker=marker,
prompt=(
"Think privately, use Read on smoke-interleaved.txt, then reply with "
"exactly the secret token from that file and no other text."
),
tools="Read",
extra_args=("--effort", "high"),
requires_tool_result=True,
)
def _subagent_task(
claude_bin: str,
server: RunningServer,
smoke_config: SmokeConfig,
provider_model: ProviderModel,
model_dir: Path,
marker_prefix: str,
) -> CliMatrixOutcome:
marker = _marker(marker_prefix, "TASK")
workspace = model_dir / "subagent_task"
(workspace / "smoke-subagent.txt").parent.mkdir(parents=True, exist_ok=True)
(workspace / "smoke-subagent.txt").write_text(marker, encoding="utf-8")
agents = json.dumps(
{
"smoke_reader": {
"description": "Reads one requested file and returns its token.",
"prompt": (
"Read the requested file with Read and return only the token "
"inside it."
),
"tools": ["Read"],
"permissionMode": "bypassPermissions",
"background": False,
}
}
)
bare, tools, pre_tool_args, extra_args = _subagent_probe_options(agents)
return _run_probe(
claude_bin=claude_bin,
server=server,
smoke_config=smoke_config,
provider_model=provider_model,
workspace=workspace,
feature="subagent_task",
marker=marker,
prompt=(
"Use the smoke_reader subagent to read smoke-subagent.txt. After the "
"first agent result, reply with exactly the token and stop. Do not "
"call any other tools."
),
tools=tools,
bare=bare,
pre_tool_args=pre_tool_args,
extra_args=extra_args,
requires_tool_result=True,
requires_agent=True,
)
def _subagent_probe_options(
agents: str,
) -> tuple[bool, str, tuple[str, ...], tuple[str, ...]]:
return (
False,
"Agent,Read",
(
"--setting-sources",
"local",
"--strict-mcp-config",
"--mcp-config",
_EMPTY_MCP_CONFIG,
"--system-prompt",
_SUBAGENT_SYSTEM_PROMPT,
),
("--agents", agents),
)
def _compact_command(
claude_bin: str,
server: RunningServer,
smoke_config: SmokeConfig,
provider_model: ProviderModel,
model_dir: Path,
marker_prefix: str,
) -> CliMatrixOutcome:
marker = _marker(marker_prefix, "COMPACT")
workspace = model_dir / "compact_command"
session_id = str(uuid.uuid4())
offset = read_log_offset(server.log_path)
first = run_claude_cli(
claude_bin=claude_bin,
server=server,
config=smoke_config,
cwd=workspace,
prompt=f"Remember this smoke token: {marker}. Reply with exactly {marker}.",
tools="",
session_id=session_id,
no_session_persistence=False,
)
second = run_claude_cli(
claude_bin=claude_bin,
server=server,
config=smoke_config,
cwd=workspace,
prompt=f"/compact preserve {marker}",
tools="",
resume_session_id=session_id,
no_session_persistence=False,
)
log_delta = read_log_delta(server.log_path, offset)
run = ClaudeCliRun(
command=(*first.command, "&&", *second.command),
returncode=second.returncode if first.returncode == 0 else first.returncode,
stdout=f"{first.stdout}\n{second.stdout}",
stderr=f"{first.stderr}\n{second.stderr}",
duration_s=first.duration_s + second.duration_s,
timed_out=first.timed_out or second.timed_out,
)
return make_outcome(
model=provider_model.model_name,
full_model=provider_model.full_model,
source=provider_model.source,
feature="compact_command",
marker="",
run=run,
log_delta=log_delta,
log_path=server.log_path,
requires_compact=True,
)
def _run_probe(
*,
claude_bin: str,
server: RunningServer,
smoke_config: SmokeConfig,
provider_model: ProviderModel,
workspace: Path,
feature: str,
marker: str,
prompt: str,
tools: str | None,
bare: bool = True,
pre_tool_args: tuple[str, ...] = (),
extra_args: tuple[str, ...] = (),
requires_tool_result: bool = False,
requires_agent: bool = False,
requires_task: bool = False,
) -> CliMatrixOutcome:
offset = read_log_offset(server.log_path)
run = run_claude_cli(
claude_bin=claude_bin,
server=server,
config=smoke_config,
cwd=workspace,
prompt=prompt,
tools=tools,
bare=bare,
pre_tool_args=pre_tool_args,
extra_args=extra_args,
)
log_delta = read_log_delta(server.log_path, offset)
return make_outcome(
model=provider_model.model_name,
full_model=provider_model.full_model,
source=provider_model.source,
feature=feature,
marker=marker,
run=run,
log_delta=log_delta,
log_path=server.log_path,
requires_tool_result=requires_tool_result,
requires_agent=requires_agent,
requires_task=requires_task,
)
def _has_proxy_regression(log_delta: str) -> bool:
if "CREATE_MESSAGE_ERROR" in log_delta:
return True
return any(re.search(pattern, log_delta) for pattern in _HTTP_REGRESSION_PATTERNS)
def _has_proxy_request(log_delta: str) -> bool:
return (
"POST /v1/messages" in log_delta
or "API_REQUEST:" in log_delta
or '"event": "free_claude_code.api.request.received"' in log_delta
or (
'"http_method": "POST"' in log_delta
and '"http_path": "/v1/messages"' in log_delta
)
)
def _tool_catalog_has(log_delta: str, tool_name: str) -> bool:
catalog = _first_tool_catalog(log_delta)
return (
f"'name': '{tool_name}'" in catalog
or f'"name": "{tool_name}"' in catalog
or f'"name":"{tool_name}"' in catalog
)
def _first_tool_catalog(log_delta: str) -> str:
for line in log_delta.splitlines():
if "FULL_PAYLOAD" not in line:
continue
single_index = line.find("'tools': [")
double_index = line.find('"tools": [')
if single_index == -1 and double_index == -1:
continue
start = single_index if single_index != -1 else double_index
end_candidates = [
index
for marker in ("'tool_choice'", '"tool_choice"', "'thinking'", '"thinking"')
if (index := line.find(marker, start)) != -1
]
end = min(end_candidates) if end_candidates else len(line)
return line[start:end]
return ""
def _agent_tool_count(text: str) -> int:
return (
text.count('"name": "Agent"')
+ text.count('"name":"Agent"')
+ len(
re.findall(
r"'type': 'tool_use'[^}\n]+?'name': 'Agent'",
text,
flags=re.DOTALL,
)
)
)
def _agent_result_count(text: str) -> int:
return text.count("agentId:") + text.count('"agentId"') + text.count("'agentId'")
def _has_upstream_unavailable_text(text: str) -> bool:
lower = text.lower()
if any(marker_text in lower for marker_text in _UPSTREAM_UNAVAILABLE_MARKERS):
return True
return any(
re.search(pattern, text, flags=re.IGNORECASE) for pattern in _HTTP_429_PATTERNS
)
def _request_count(log_delta: str) -> int:
access_log_count = log_delta.count("POST /v1/messages")
service_log_count = log_delta.count("API_REQUEST:")
structured_log_count = log_delta.count(
'"event": "free_claude_code.api.request.received"'
)
return max(access_log_count, service_log_count, structured_log_count)
def _marker(scope: str, prefix: str) -> str:
return f"FCC_{scope}_{prefix}_{uuid.uuid4().hex[:8].upper()}"
def _excerpt(value: str | None, *, max_chars: int = 2400) -> str:
if value is None:
value = ""
if len(value) <= max_chars:
return redacted(value)
return redacted(value[-max_chars:])
def _coerce_timeout_text(value: str | bytes | None) -> str:
if value is None:
return ""
if isinstance(value, bytes):
return value.decode("utf-8", errors="replace")
return value