diff --git a/config.default.yaml b/config.default.yaml index c255c3b..d15f43f 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -21,7 +21,8 @@ copilotBaseUrl: https://api.githubcopilot.com # When true, start updates ~/.claude/settings.json with ANTHROPIC_BASE_URL # and a dummy ANTHROPIC_AUTH_TOKEN so Claude Code can use this proxy. -# Also seeds context/output budgets from model discovery when not already set. +# Also seeds context/output budgets from model discovery, and +# CLAUDE_CODE_AUTO_MODE_SERVER=0, when not already set. claudeSetup: true # Log verbosity: diff --git a/src/lib/claude-settings.ts b/src/lib/claude-settings.ts index 65ba6eb..f5dbeea 100644 --- a/src/lib/claude-settings.ts +++ b/src/lib/claude-settings.ts @@ -134,6 +134,14 @@ export async function applyClaudeConfig( env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = String(input.maxOutputTokens) } + // In auto mode Claude Code asks the server to check actions. In #162 those checks did not reach + // it through the relay, so it made its own classifier requests and printed a notice asking the + // gateway to support them. "0" skips the server check and the notice; the classifier requests + // stay as they were. Seeded only when absent, so a value the user set is kept. + if (env.CLAUDE_CODE_AUTO_MODE_SERVER === undefined) { + env.CLAUDE_CODE_AUTO_MODE_SERVER = "0" + } + const next: Record = { ...parsed, ...(nextModel !== undefined && { model: nextModel }), diff --git a/tests/unit/claude-settings.test.ts b/tests/unit/claude-settings.test.ts index d7a2371..6ceec30 100644 --- a/tests/unit/claude-settings.test.ts +++ b/tests/unit/claude-settings.test.ts @@ -83,6 +83,38 @@ test("preserves explicit client context and output overrides", async () => { }) }) +// #162: through the relay, auto mode's server checks did not reach Claude Code, and it showed a +// notice. The writer seeds CLAUDE_CODE_AUTO_MODE_SERVER=0, but only when the key is absent. +test("seeds CLAUDE_CODE_AUTO_MODE_SERVER=0 only when it is absent", async () => { + const input = (configPath: string) => ({ + baseUrl: "http://127.0.0.1:4142", + configPath, + gptModel: "gpt-6-astra", + gptLimits: astraLimits, + maxOutputTokens: 128_000, + }) + + await withTemporarySettings(async (configPath) => { + await applyClaudeConfig(input(configPath)) + const env = (await readSettings(configPath)).env as Record + + assert.equal(env.CLAUDE_CODE_AUTO_MODE_SERVER, "0") + assert.equal((await applyClaudeConfig(input(configPath))).changed, false) + }) + + for (const value of ["0", "1"]) { + await withTemporarySettings(async (configPath) => { + await fs.mkdir(path.dirname(configPath), { recursive: true }) + await fs.writeFile(configPath, JSON.stringify({ env: { CLAUDE_CODE_AUTO_MODE_SERVER: value } })) + + await applyClaudeConfig(input(configPath)) + const env = (await readSettings(configPath)).env as Record + + assert.equal(env.CLAUDE_CODE_AUTO_MODE_SERVER, value) + }) + } +}) + // Why: a fresh managed Claude Code setup must actually select the configured GPT // default and expose its 1M client-side context identity without changing the // canonical model that Copilot receives. diff --git a/wiki/EN-Configuration.md b/wiki/EN-Configuration.md index dd5154c..6f3e8ed 100644 --- a/wiki/EN-Configuration.md +++ b/wiki/EN-Configuration.md @@ -665,6 +665,7 @@ ANTHROPIC_BASE_URL=http://127.0.0.1:4142 ANTHROPIC_AUTH_TOKEN= CLAUDE_CODE_MAX_CONTEXT_TOKENS= CLAUDE_CODE_MAX_OUTPUT_TOKENS= +CLAUDE_CODE_AUTO_MODE_SERVER=0 ``` into: @@ -684,6 +685,17 @@ With `claudeSetup: false`, configure these client variables yourself. The settings writer uses the same snapshot/atomic replacement boundary; malformed or existing empty settings files are not overwritten, and unrelated values remain. +`CLAUDE_CODE_AUTO_MODE_SERVER=0` is also written only when the key is absent. In +auto mode, Claude Code asks the server to check actions such as shell commands. +Through the relay those checks did not reach Claude Code on the `chat-completions` +or `messages` route, so it made its own classifier requests and printed a notice +that the session isn't eligible for auto mode's no-charge classifier requests. +With `0`, Claude Code skips the server check and the notice; the classifier +requests stay as they were. The value stays in `~/.claude/settings.json`: remove +or change it when Claude Code connects without the relay, or through a gateway +that supports the server checks. Claude Code documents the variable as temporary; +see [auto mode classifier billing](https://code.claude.com/docs/en/auto-mode-classifier-billing). + The local dummy token is **not network authentication**. Host/Origin checks and JSON content-type validation reduce browser-origin misuse, not access by an arbitrary network client. Keep the listener on loopback; see diff --git a/wiki/EN-Logging-Troubleshooting.md b/wiki/EN-Logging-Troubleshooting.md index 9a88639..918e339 100644 --- a/wiki/EN-Logging-Troubleshooting.md +++ b/wiki/EN-Logging-Troubleshooting.md @@ -924,10 +924,26 @@ Expected values: - `ANTHROPIC_BASE_URL` points at `http://127.0.0.1:4142` - `ANTHROPIC_AUTH_TOKEN` exists; it is a dummy value for local relay use +- `CLAUDE_CODE_AUTO_MODE_SERVER` is `0`, unless you set another value Changing `host` or `port` requires restarting the relay, because the listening socket cannot move during hot reload. +## Claude Code says the session isn't eligible for auto mode + +Claude Code can print a notice like this, in the terminal or, with `claude -p`, +on stderr: + +```text +We're changing auto mode to no longer charge for classifier requests in Claude Code. However, this session isn't eligible because your requests go through 127.0.0.1:4142, which isn't compatible with this update. Nothing breaks: auto mode keeps working, and its classifier requests are billed as before. +``` + +It is expected through the relay: auto mode's server checks do not reach Claude +Code, so it makes its own classifier requests. `CLAUDE_CODE_AUTO_MODE_SERVER=0` in +Claude Code's environment stops the server check and the notice. With +`claudeSetup: true`, `copilot-relay start` writes it when it is absent; see +[Configuration](EN-Configuration.md). + ## Safe log sharing **Never share raw captures wholesale.** Prompts, tool arguments/results, signed diff --git a/wiki/ZH-Configuration.md b/wiki/ZH-Configuration.md index 66212fe..ade9e3f 100644 --- a/wiki/ZH-Configuration.md +++ b/wiki/ZH-Configuration.md @@ -572,6 +572,7 @@ ANTHROPIC_BASE_URL=http://127.0.0.1:4142 ANTHROPIC_AUTH_TOKEN= CLAUDE_CODE_MAX_CONTEXT_TOKENS=<发现的 GPT context 窗口> CLAUDE_CODE_MAX_OUTPUT_TOKENS=<两个配置模型中最大的已公布输出预算> +CLAUDE_CODE_AUTO_MODE_SERVER=0 ``` 这里的 token 是本地 relay 占位值。真正访问 GitHub Copilot 用的是 @@ -584,6 +585,15 @@ CLAUDE_CODE_MAX_OUTPUT_TOKENS=<两个配置模型中最大的已公布输出预 设置写入同样使用快照及原子替换;不会覆盖格式错误或已经存在的空设置文件,其他无关 值也会保留。 +`CLAUDE_CODE_AUTO_MODE_SERVER=0` 同样只在缺少该键时写入。auto mode 下,Claude Code +会请服务器检查 shell 命令等操作。经过 relay 时,这些检查在 `chat-completions` 和 +`messages` 路由上都没有到达 Claude Code,于是它自己发起 classifier 请求,并提示该 +会话不能享受 auto mode 不再对 classifier 请求收费的变更。设为 `0` 后,Claude Code +不再请求服务器检查,也不再显示该提示;classifier 请求与原来相同。这个值会留在 +`~/.claude/settings.json` 里:Claude Code 不经过 relay 连接,或改用支持服务器检查的 +网关时,请删除或修改它。Claude Code 文档说明该变量是临时的,见 +[auto mode classifier billing](https://code.claude.com/docs/en/auto-mode-classifier-billing)。 + 本地占位 token **不是网络认证**。Host/Origin 与 JSON content-type 检查减少的是浏览器 来源滥用,不能阻止任意网络客户端。请保持 loopback 监听,见[架构](ZH-Architecture.md)。 diff --git a/wiki/ZH-Logging-Troubleshooting.md b/wiki/ZH-Logging-Troubleshooting.md index 465d66c..1d3b382 100644 --- a/wiki/ZH-Logging-Troubleshooting.md +++ b/wiki/ZH-Logging-Troubleshooting.md @@ -816,9 +816,23 @@ cat ~/.claude/settings.json - `ANTHROPIC_BASE_URL` 指向 `http://127.0.0.1:4142` - `ANTHROPIC_AUTH_TOKEN` 存在;它是给本地中继用的占位值 +- `CLAUDE_CODE_AUTO_MODE_SERVER` 为 `0`,除非你设置了其他值 改 `host` 或 `port` 需要重启中继,因为监听 socket 无法在热重载期间迁移。 +## Claude Code 提示会话不符合 auto mode 的条件 + +Claude Code 可能在终端中,或用 `claude -p` 时在 stderr 上,打印类似这样的提示: + +```text +We're changing auto mode to no longer charge for classifier requests in Claude Code. However, this session isn't eligible because your requests go through 127.0.0.1:4142, which isn't compatible with this update. Nothing breaks: auto mode keeps working, and its classifier requests are billed as before. +``` + +经过中继时这是预期行为:auto mode 的服务器检查到达不了 Claude Code,所以它自己发起 +classifier 请求。在 Claude Code 的环境中设置 `CLAUDE_CODE_AUTO_MODE_SERVER=0`,可停止 +服务器检查和这条提示。`claudeSetup: true` 时,`copilot-relay start` 会在缺少该值时 +写入它;见[配置](ZH-Configuration.md)。 + ## 安全分享日志 **绝不要整份分享原始捕获。** 即使排除了认证 header,提示词、工具参数/结果、签名