fix(hub): Claude Code SDK 经 OpenRouter Anthropic Skin,保留 provider-agnostic

我之前判断错了——OpenRouter 有 Anthropic Messages API 兼容端点
(https://openrouter.ai/api),Claude Code SDK 直连不需要代理。
ANTHROPIC_AUTH_TOKEN=OpenRouter key,ANTHROPIC_API_KEY='' 即可。

而且 ANTHROPIC_DEFAULT_SONNET_MODEL 可设 OpenRouter model ID
(z-ai/glm-4.7 等),所以 provider-agnostic 没丢。

ADR-0017 重写:从'Anthropic 专有'改为'Claude Code SDK via OpenRouter,
保留 provider-agnostic'。tsc rc=0。
This commit is contained in:
2026-07-08 01:46:09 +08:00
parent 082562ca08
commit b9f50407be
3 changed files with 55 additions and 40 deletions
@@ -1,8 +1,8 @@
# ADR 0017: Agent Runtime — Claude Code SDK
# ADR 0017: Agent Runtime — Claude Code SDK via OpenRouter
## Status
Accepted — supersedes the original ADR-0017 (provider-agnostic session).
Accepted.
## Context
@@ -16,38 +16,42 @@ partial-message streaming, and a battle-tested multi-turn loop. Building these
ourselves meant re-implementing what the Claude Code SDK already provides.
The Feishu integration also proved costly to hand-roll — card schema quirks,
patch limitations, file support — each API quirk cost a round-trip. The
Claude-to-IM library (2.7K stars) has already solved these, but its
LLMProvider interface expects Claude Code SDK's SSE event format. Using it with
a generic provider would require a conversion layer.
patch limitations, file support — each API quirk cost a round-trip.
## Decision
Adopt `@anthropic-ai/claude-agent-sdk` as the agent runtime. The SDK's `query()`
function owns the agent loop (tool dispatch, compaction, streaming). Built-in
tools (Read, Write, Bash, Glob, Grep) cover the entire tool surface — no custom
tools needed. `cph check` / `cph build` run via Bash.
Adopt `@anthropic-ai/claude-agent-sdk` as the agent runtime, routed through
OpenRouter's **"Anthropic Skin"** (`https://openrouter.ai/api`).
This means:
- The agent layer is **Anthropic-specific**, not provider-agnostic. Claude Code
SDK speaks the Anthropic Messages API and cannot be pointed at OpenRouter
(which is OpenAI-compatible).
- An Anthropic API key (or OAuth via Claude Pro/Max) is required.
- Model selection is within the Anthropic model family (Sonnet, Haiku, Opus),
not across vendors.
OpenRouter exposes an Anthropic Messages API-compatible endpoint. Claude Code
SDK speaks its native protocol directly to OpenRouter — no proxy, no format
conversion. The SDK's `query()` owns the agent loop (tool dispatch, compaction,
streaming). Built-in tools (Read, Write, Bash, Glob, Grep) cover the entire
tool surface — no custom tools needed. `cph check` / `cph build` run via Bash.
Environment variables:
```
ANTHROPIC_BASE_URL=https://openrouter.ai/api
ANTHROPIC_AUTH_TOKEN=<OpenRouter API key>
ANTHROPIC_API_KEY="" # must be explicitly empty
```
Model routing: `ANTHROPIC_DEFAULT_SONNET_MODEL` etc. accept OpenRouter model
IDs (e.g. `z-ai/glm-4.7`, `anthropic/claude-sonnet-4-20250514`). This means
**provider-agnosticism is preserved** — any OpenRouter model that supports tool
use can be used, not just Anthropic models.
## Consequences
- We lose the ability to route to GLM or other non-Anthropic models via
OpenRouter. This is the accepted cost of getting a mature agent loop.
- The Vercel AI SDK dependency is dropped.
- Provider-agnosticism preserved via OpenRouter — GLM, Claude, GPT, etc. all
work as long as the model supports tool use.
- The agent loop is Claude Code SDK's (compaction, tool approval, partial
message streaming) — not hand-rolled.
- Custom tools (read_file, write_file, cph_check, cph_build) are replaced by
the SDK's built-in Read/Write/Bash/Glob/Grep.
- The SSE event format matches Claude-to-IM's expected protocol natively
adopting Claude-to-IM for Feishu integration is a direct fit, no conversion.
- Per-run model selection still works (role→model mapping), but models are
Anthropic aliases, not OpenRouter routing IDs.
- If provider-agnosticism becomes a hard requirement again, the migration path
is: replace `query()` with a Vercel AI SDK `streamText` call + an SSE format
converter (~100 lines). The rest of the architecture (trigger, lock,
permission, Feishu client) is provider-agnostic already.
- The SSE event format matches Claude-to-IM's expected protocol natively.
- Per-run model selection works via role→model mapping; models are OpenRouter
IDs, not Anthropic-only aliases.
- OpenRouter recommends setting Anthropic as the top-priority provider for
Claude Code; non-Anthropic models may have reduced compatibility with some
SDK features (e.g. thinking blocks).