anthropics / anthropics/claude-code

Workflow agent() model silently replaced by CLAUDE_CODE_SUBAGENT_MODEL when the id is not in the gateway-discovered model list

未关闭
#89,972 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
area:agents area:providers bug platform:macos
主要语言
Python
星标
145k
派生
23.1k
PR 合并指标
PR 指标待抓取

描述

# Workflow agent() model silently replaced by CLAUDE_CODE_SUBAGENT_MODEL when the id is not in the gateway-discovered model list

## Environment

- Claude Code 2.1.245 (also observed on 2.1.226), macOS (darwin arm64)
- Custom gateway: `ANTHROPIC_BASE_URL` pointed at a self-hosted Anthropic-wire proxy, `ANTHROPIC_AUTH_TOKEN` set, `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1`
- `CLAUDE_CODE_SUBAGENT_MODEL` set to a gateway-advertised id (`claude-delegate-capable`)

## What happens

With gateway model discovery enabled, Claude Code fetches `GET /v1/models` from the gateway and treats the returned list as the exhaustive vocabulary for **subagent** model resolution (Workflow `agent()` `model:` opts). Any requested model id absent from that list is **silently replaced with `CLAUDE_CODE_SUBAGENT_MODEL`** before the request leaves the machine:

- No error, no warning, no UI indication at call time.
- The subagent's `agent-*.meta.json` records only the **requested** id (`requestedModel`), which actively misleads post-hoc inspection — the transcript's served model is the only local trace, and nothing correlates the two.
- The substituted request reaches the wire as the fallback model. Verified at the gateway with a live SSE tap of per-request events: agents whose `agent()` opts requested `claude-fable-5`, `claude-fable-5[1m]`, and (in a later repro) a class id added to the gateway after session start all arrived as `model_requested=claude-delegate-capable`.

The main-loop model is unaffected (passes through verbatim); only subagent resolution is gated this way.

## Reproduction

1. Point Claude Code at any Anthropic-compatible gateway whose `/v1/models` returns a *partial* model list (e.g. it omits `claude-opus-5` while happily routing it on `/v1/messages`). Set `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1` and a `CLAUDE_CODE_SUBAGENT_MODEL` that *is* advertised.
2. Run a Workflow with `agent('say hi', {model: 'claude-opus-5'})` (any valid id not in the discovery list).
3. Observe on the gateway that the request arrives with the `CLAUDE_CODE_SUBAGENT_MODEL` value instead; observe locally that no error or warning was emitted and the agent meta records the requested id.

A second wrinkle makes this sharper: the discovery list appears to be fetched **once per session**. A model added to the gateway's `/v1/models` after session start keeps being silently substituted for the rest of that session, so even a "fixed" gateway doesn't stop the behavior for running sessions.

## Impact

A multi-day silent failure in our case: an adversarial-review stage explicitly specified as "strongest model" ran on a different (cheaper) model class for three days across three sessions. Nothing surfaced it; diagnosis required tapping the gateway's event stream and hand-correlating subagent transcripts. The failure mode is the worst kind for model-quality work — outputs remain plausible, so nothing looks wrong.

## Expected behavior

Gating subagent models on the discovered list is a defensible safety choice (an unknown id might 404). The defect is the **silence**. Any of these would be fine:

1. **Refuse**: fail the `agent()` call with a clear error naming the unadvertised id, or
2. **Honor**: pass the id through and let the gateway decide (it may route ids it doesn't advertise), or
3. **Substitute loudly**: keep the fallback but emit a visible warning and record both requested *and* resolved ids in the subagent metadata.

Silent substitution converts a configuration gap into invisible model downgrades.

贡献指南

这个仓库没有索引到贡献指南

调研方向

Start at the gateway model-discovery flow and Workflow agent() subagent model-resolution entry points, then reproduce with a partial GET /v1/models response and an unlisted model id. Trace where the requested id becomes the wire model and how agent-*.meta.json is written. Done means the chosen handling is visible and the requested and resolved models are no longer misleadingly represented.

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
ai, api
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
活跃
描述清晰度
基本清楚
新手友好度
45/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。