# GA 伪装 Claude Code 全流程 SOP

> SophHub 分享版：仅包含机制、排错经验与通用 9 工具映射口径；已移除本机增强工具映射、真实 token/session/device/account/capture 内容。部署前请按自己的 `assets/tools_schema.json` 和 `ga.py do_*` 重新校验。


来源与验证：本 SOP 基于当前 `claude-max-proxy/proxy.py`、`tool_name_mapping.json`、脱敏后的真 CC/GA capture 样本 以及 L4 会话索引片段整理；写入前已用 `py_compile`、函数级构造测试、最新 capture diff 验证过关键行为。禁止写入易变 session/PID/时间戳/token。

## 目标

让 GenericAgent/GA 的 Anthropic `/v1/messages` 出口尽量贴近 Claude Code/CC，同时不破坏 GA 工具调用：

- GA 入站工具名/参数仍由 GA 正常使用。
- proxy 出站请求伪装为 CC headers/body/system/tools。
- 上游响应中的 CC 工具名需映射回 GA/OC 工具名，避免工具调用链断裂。
- identity/session 贴近 CC 行为，但不硬编码历史会话 ID。

## 当前组件与路径

- 代理目录：`claude-max-proxy/`
- 核心代理：`claude-max-proxy/proxy.py`
- 工具映射：`claude-max-proxy/tool_name_mapping.json`
- 真 CC MITM 样本：脱敏后的真 CC MITM 样本
- GA/proxy 出站样本：脱敏后的 GA/proxy 出站样本
- 默认端口：`PORT=5678`
- 默认 upstream：`https://api.anthropic.com`
- dry-run capture 目录：`CAPTURE_DIR`，默认 `captures`

## 已实现的 proxy 伪装点

### 1. dry-run 默认关闭

`proxy.py` 当前逻辑：

```python
DRY_RUN = os.environ.get("DRY_RUN", "0").lower() in ("1", "true", "yes", "on")
```

含义：

- 默认真实转发 upstream。
- 只有显式 `DRY_RUN=1/true/yes/on` 才短路保存 capture。
- 旧安全默认曾是 dry-run 开启；现在为便于真实联调已关闭。

### 2. CC 版本与 User-Agent 伪装

`detect_cc_version()` 调 `claude --version` 获取主版本；`.cc_build` 可缓存 build 号；`CC_VERSION` 形如 `2.x.x/<build>`。

headers 中伪装：

- `user-agent`: 优先复用真 CC capture 的 `user-agent`，否则 fallback 为 `claude-cli/{CC_VERSION} (...)`
- `x-app`: fallback `cli`
- `anthropic-dangerous-direct-browser-access`: fallback `true`
- `anthropic-version`: fallback `2023-06-01`
- `X-Stainless-Lang`: fallback `js`
- `X-Stainless-OS`: 本机 OS 名
- `X-Stainless-Arch`: 本机架构
- `X-Stainless-Runtime`: fallback `node`
- 可选透传真 CC capture 中的 `X-Stainless-Package-Version`、`X-Stainless-Runtime-Version`、`X-Stainless-Timeout`、`X-Stainless-Retry-Count`

注意：UA suffix 在样本中不是绝对固定；历史分析里 GA 默认 suffix 曾是 `controlled`，可改成更贴近 CC 的值，但不要假定唯一常量。

### 3. 认证与 cch/billing header

当前 proxy 用本机 `~/.claude/.credentials.json` 取 access token，出站：

- 需要同时写入认证相关 header；示例中必须使用占位符，禁止发布真实 token。

`anthropic-beta` fallback：

```text
claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,effort-2025-11-24
```

`x-claude-code-client-sha256` / CCH：

- `CCH_SEED = 0x6E52736AC806831E`
- `inject_system_and_cch()` 会基于最终 body 字节计算 CCH，并写入 headers。
- 历史核对过 CC 源码/gist 与 GA 当前算法；不要随意改算法。
- `billing` 相关 header 在已收集 CC 样本中未出现；用户曾怀疑可能与缓存读取相关，但已有缓存样本不支持该推断。不要凭空补 billing header，需先抓到真 CC 证据。

### 4. session 行为：进程/会话级随机，header 与 metadata 同步

真 CC 多样本显示：同一会话内 `X-Claude-Code-Session-Id` 与 `metadata.user_id.session_id` 固定且相等；不同会话随机变化。当前 proxy 应模拟为“进程启动生成一次 UUID，会话内复用”。

当前规则：

- proxy 启动时生成一次 `SESSION_ID = str(uuid.uuid4())`。
- `build_headers()` 中 `X-Claude-Code-Session-Id = SESSION_ID`。
- `sync_metadata_session(body, session_id)` 中 `metadata.user_id.session_id = header session_id`。
- 不要硬编码历史 CC 的 session_id。
- 验证时看两点：
  - 同一 proxy 进程多次 `build_headers()` session 一致。
  - capture 中 header session 与 metadata session 相等。

### 5. metadata.user_id identity

当前最终规则：

- `device_id`：复用最新真 CC capture 的 `metadata.user_id.device_id`。
- `account_uuid`：保留 GA/proxy 入站 body 原值，不再覆盖成真 CC capture 的 account_uuid。
- `session_id`：同步为当前 proxy 会话 session。

这个点经历过修正：曾短暂把 `account_uuid` 也覆盖为真 CC 值，但用户要求“改回原版 proxy 逻辑”，所以现在只覆盖 `device_id`。

### 6. system blocks 伪装

`load_latest_cc_system_blocks()` 从真 CC capture 中读取 `system[0:3]`。

`inject_system_and_cch(body)` 当前行为：

- 若存在真 CC 三段 system：
  - `system[0] = 真 CC system[0]`
  - `system[1] = 真 CC system[1]`
  - `system[2] = 真 CC system[2] + GA 英文 system prompt`
- 保留/继承对应 `cache_control`。
- 保证 `body["system"]` 最终为三段结构。
- 最新 diff 验证过：
  - `system[0]` 完全等于原版 CC。
  - `system[1]` 完全等于原版 CC。
  - `system[2]` 以原版 CC `system[2]` 开头，后面追加 GA 英文 SP。
- 历史讨论结论：不要把 GA 长 system prompt 塞到 user message；应走 system 三段形态，否则与 CC 请求形态差异大。

### 7. 顶层 body 参数归一化

`inject_system_and_cch()` 中把 body 归一到当前 CC-like 目标：

- `model = "claude-opus-4-6"`
- `max_tokens = 64000`
- `thinking = {"type": "adaptive"}`
- `context_management = {"edits": [{"type": "clear_thinking_20251015", "keep": "all"}]}`
- `output_config.effort = "max"`
- `stream` 保留入站/配置值；历史讨论认为 CC 也可 false，因此 stream 差异本身不一定异常。

历史分析：

- 真 CC 在不同参数/模型下可能没有 `thinking` 字段；GA 的 controlled/thinking/context 由 GA Native 配置与 beta 能力控制。
- 不要只为了贴近某个样本盲删 `thinking/context_management/output_config`，需确认不会破坏当前模型能力。

### 8. tool name 双向映射与参数适配

`tool_name_mapping.json` 必须使用**当前 GA 实际工具名**作为本地侧 key；每个 value 必须唯一；不要再使用旧 OC 名如 `exec/read/edit/write/sessions_*`，否则反向映射会把模型 tool_use 还原到 `ga.py` 不存在的 `do_*`，报未知工具。

通用/原版 GA 9 个实际工具 direct 映射建议：

- `code_run -> Bash`
- `file_read -> Read`
- `file_patch -> Edit`
- `file_write -> Write`
- `web_scan -> WebFetch`
- `web_execute_js -> WebSearch`
- `update_working_checkpoint -> TodoWrite`
- `ask_user -> AskUserQuestion`
- `start_long_term_update -> TaskUpdate`

SophHub/通用分享版只保留原版 GA 9 工具映射；本机增强工具（如后台 Agent/Skill 类工具）不纳入通用表，除非对方环境明确实现了对应 `do_*`。


关键边界：

- `sessions_send/sessions_run/sessions_list/sessions_history` 是历史旧名，只能在回程兜底中回收，禁止再暴露到当前工具 schema。
- `AskUserQuestion` 只能回收为 `ask_user`；不要让旧 `sessions_send -> AskUserQuestion` 与 `ask_user -> AskUserQuestion` 同时参与普通反向 dict 生成，否则后者/前者覆盖会导致未知工具。
- proxy 路径：`CC_TO_OC = {v:k ...}` 后必须 `update(LEGACY_TO_OC)`，让旧/借用名只在模型输出回程时归一化。
- llmcore 内置伪装路径也要同步 `_CC_SEMANTIC_ALIASES` 与 `_CC_LEGACY_TO_GA`，否则绕过 proxy 或本地路径仍会复发。
- llmcore 工具 schema 需兼容两种形态取名/设名：顶层 `{"name": ...}` 与 OpenAI 风格 `{"function": {"name": ...}}`。

proxy/llmcore 回程不能只做字符串替换；必须结构化处理普通 JSON 与 SSE 流式 chunk 中的 `tool_use`。

已验证的参数适配：

- `Bash` → `code_run`，`command` → `script`，默认补 `type="powershell"`、`cwd="./"`。
- `Read` → `file_read`，`file_path` → `path`，`offset` → `start`，`limit` → `count`。
- `Edit` → `file_patch`，`file_path` → `path`，`old_string` → `old_content`，`new_string` → `new_content`。
- `Write` → `file_write`，`file_path` → `path`，`content` → `new_content`。
- `ga.py` 的 `file_write` 已兼容 `args.content` / `args.file_text`；否则 Claude Code 风格 `Write(content=...)` 回来但没有 `<file_content>` 标签时会失败。

排错信号：若伪装 GA 日志出现 `Tool: exec` / `未知工具: exec` / `未知工具: sessions_send`，优先检查：映射表是否仍有旧 OC/sessions key；alias value 是否重复；响应回程是否结构化处理 SSE JSON；`AskUserQuestion/sessions_send` 是否都被归一化成 `ask_user`；`Bash(command=...)` 是否被适配为 `code_run(script=...)`。

### 9. capture 与 diff 验证

每次改伪装后至少做以下验证：

1. `py_compile.compile("claude-max-proxy/proxy.py", doraise=True)`；若改了 GA 工具兼容，也编译 `../ga.py`；若改了内置伪装路径，也编译 `../llmcore.py`。
2. 工具集合验证：从 `assets/tools_schema.json` 读取当前 GA 实际工具名，确认 `tool_name_mapping.json` 的 direct key 与 schema 完全一致、value 全唯一、无 `sessions_*` 暴露名。
3. 函数级验证：
   - `build_headers()` 连续调用 session 一致。
   - `sync_metadata_session()` 后 device/account/session 符合规则。
   - `inject_system_and_cch()` 后 system 三段、顶层参数、CCH 写入。
   - 工具回程映射：`AskUserQuestion` 与旧 `sessions_send` 都应变为 `ask_user`；`Bash(command=...)` 最终应变为 `code_run(script=..., type="powershell", cwd="./")`；`Read/Edit/Write(file_path=...)` 最终应变为 `file_read/file_patch/file_write(path=...)`。
   - llmcore 内置伪装：`_cc_disguise()` 输出工具名应为当前唯一 alias 集合；顶层 `name` schema 与 `function.name` schema 都能改名。
4. 如用户要求“不要外网测试”，只能做本地验证：精确重启 proxy 后确认 `PORT=5678` 监听；可访问本地 `/` 看 Flask 存活，但不要请求会转发 upstream 的 `/v1/messages`。
5. 需要真实联调时再发真实 GA 请求或 dry-run 请求。
6. 对比：
   - `mitm_cc_captures/latest.json`
   - 脱敏后的 GA/proxy 出站样本

建议 diff 关注字段：

- headers:
  - `authorization`/`x-api-key` 只确认存在和脱敏形态，禁止记 token。
  - `user-agent`
  - `anthropic-beta`
  - `anthropic-version`
  - `X-Claude-Code-Session-Id`
  - `x-claude-code-client-sha256`
  - stainless headers
- body:
  - `model`
  - `max_tokens`
  - `stream`
  - `thinking`
  - `context_management`
  - `output_config`
  - `metadata.user_id.device_id/account_uuid/session_id`
  - `system[0:3]`
  - `tools[].name`
  - `messages` 数量与内容：真 CC 长会话 vs GA 测试请求不同属预期差异。

### 10. 进程操作注意

重启 proxy 时禁止无条件杀全部 python；只杀 cmdline/cwd 命中 `claude-max-proxy/proxy.py` 的精确进程。

示例思路：

- 枚举 `psutil.process_iter(['pid','cmdline','cwd'])`
- 匹配 `proxy.py` 且 cwd 为 `.../claude-max-proxy`
- terminate 等待；必要时再 kill 精确 PID。
- 用当前 Python/venv 启动 `pythonw.exe proxy.py` 或等价命令。
- 重启后确认 `proxy.py` 进程存在。

## 当前最新 diff 结论模板

符合预期时应看到：

```text
device_id_equal_orig: True
account_uuid_equal_orig: False  # 因保留 GA/proxy 入站 account_uuid
header_meta_session_equal: True
session_equal_orig: False       # 因当前会话随机，不复用历史 CC session
system[0] equal true
system[1] equal true
system[2] startswith orig true + append GA English SP
top-level model/max_tokens/thinking/context_management/output_config match target
```

预期差异：

- `messages_count` 常不同：真 CC capture 可能是长会话，GA 测试可能只有 1 条。
- `tools` 集合可能不同：GA 当前工具集与真 CC clean 9 工具不同；只要映射与回程能闭环，不必强行删到完全相同，除非用户明确要求且验证工具不坏。

## SophHub 分享前脱敏清单

发布到 SophHub 前只分享机制与最小补丁思路，禁止包含本机隐私和可复用凭据：

- 删除/替换本机绝对路径、用户名、PID、端口占用日志、真实 capture 文件名中的会话标识。
- 禁止发布 `authorization`、`x-api-key`、cookie、token、真实 `account_uuid`、真实 `device_id`、真实 `session_id`。
- capture/diff 只保留字段名、是否相等、脱敏后的形态；不要贴完整请求体，尤其不要贴用户消息、项目源码、长 system prompt 全文。
- 可以公开的核心经验：工具 alias 必须一对一唯一；历史旧名只做回程归一化不暴露；普通 JSON 与 SSE 都要结构化 remap；proxy 与 llmcore 两条路径要同时维护；改后必须编译、离线映射测试、精准重启并验证端口。
- 若仓库里包含 `mitm_cc_captures/`、`captures/`、日志、`.env`、临时任务输出，发布前用脚本扫描关键词：`authorization|x-api-key|cookie|session|account_uuid|device_id|Bearer|sk-`。

## 已知 gap / 不要误判

- prompt 完全改成 CC 可能影响 GA 能力；当前策略是 CC system[0:2] + CC system[2] 前缀 + GA 英文 SP 追加。
- tools 数量未必等于 clean CC 的 9 个；历史中 9/11/48 来源不同，需按实际 capture 与当前工具注册判断。
- stream 可 true/false，单独不是强异常。
- UA suffix 不固定，不要把某个 suffix 当唯一真值。
- billing header 未抓到真 CC 证据前不要补。
- session 不是硬编码；是每个 proxy 会话随机一次、会话内固定。
