clawmoney 0.14.2 → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -53,8 +53,21 @@ const DEFAULT_USER_AGENT = `claude-cli/${DEFAULT_CLI_VERSION} (external, ${DEFAU
|
|
|
53
53
|
// same file — both projects have the identical string. This value is part
|
|
54
54
|
// of Anthropic's server-side check that the request came from a real CLI.
|
|
55
55
|
const CLAUDE_FINGERPRINT_SALT = "59cf53e54c78";
|
|
56
|
+
// Headers that real Claude Code emits on every /v1/messages call. The
|
|
57
|
+
// Anthropic SDK would inject these automatically; since we bypass the SDK
|
|
58
|
+
// and hand-roll the fetch call we have to include them verbatim.
|
|
59
|
+
//
|
|
60
|
+
// Note the deliberate omissions:
|
|
61
|
+
// - `anthropic-beta` is NOT static — it is per-request and derived from
|
|
62
|
+
// the model via pickClaudeBetasForModel(). Real Claude Code passes
|
|
63
|
+
// the list via the SDK's `betas: [...]` body param and the SDK then
|
|
64
|
+
// emits it as a comma-joined `anthropic-beta` header. We do the same
|
|
65
|
+
// thing by building the header inline in doCallClaudeApi so Haiku
|
|
66
|
+
// requests drop `claude-code-20250219` like the real CLI.
|
|
67
|
+
// - `accept` is overridden per-request to `text/event-stream` when we
|
|
68
|
+
// set stream:true (see doCallClaudeApi). Leaving it out of the static
|
|
69
|
+
// set so we can pick the right value at call time.
|
|
56
70
|
const STATIC_CLAUDE_CODE_HEADERS = {
|
|
57
|
-
"accept": "application/json",
|
|
58
71
|
"x-stainless-retry-count": "0",
|
|
59
72
|
"x-stainless-timeout": "600",
|
|
60
73
|
"x-stainless-lang": "js",
|
|
@@ -67,10 +80,6 @@ const STATIC_CLAUDE_CODE_HEADERS = {
|
|
|
67
80
|
"anthropic-version": "2023-06-01",
|
|
68
81
|
"x-app": "cli",
|
|
69
82
|
"content-type": "application/json",
|
|
70
|
-
// Minimal beta set that Max-tier subscriptions always accept. Adding
|
|
71
|
-
// context-1m or context-management here will get rejected as "long
|
|
72
|
-
// context beta not available for this subscription" on non-Enterprise tiers.
|
|
73
|
-
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14",
|
|
74
83
|
};
|
|
75
84
|
// System prompt captured from real Claude Code ≥ 2.1.x. The first marker line
|
|
76
85
|
// matches claudeCodeSystemPrompts template #2 in sub2api's validator
|
|
@@ -88,6 +97,85 @@ const MODEL_ID_OVERRIDES = {
|
|
|
88
97
|
function normalizeModel(model) {
|
|
89
98
|
return MODEL_ID_OVERRIDES[model] ?? model;
|
|
90
99
|
}
|
|
100
|
+
// ── Per-model thinking + betas selection (mirrors real Claude Code) ──
|
|
101
|
+
//
|
|
102
|
+
// Real Claude Code ALWAYS sends a `thinking` body field for Claude 4+
|
|
103
|
+
// models, and the shape depends on whether the model supports adaptive
|
|
104
|
+
// thinking. Source: claude-code-best/src/utils/thinking.ts:
|
|
105
|
+
// - modelSupportsThinking() → any canonical name NOT matching "claude-3-"
|
|
106
|
+
// - modelSupportsAdaptiveThinking() → only canonical names containing
|
|
107
|
+
// "opus-4-6" or "sonnet-4-6"
|
|
108
|
+
//
|
|
109
|
+
// If we send requests to Anthropic without this field but with Claude 4+
|
|
110
|
+
// models, the per-account traffic pattern is "zero thinking on every
|
|
111
|
+
// message" which is a clear relay-farm fingerprint (real users on these
|
|
112
|
+
// tiers get adaptive thinking automatically and have no way to turn it
|
|
113
|
+
// off short of setting alwaysThinkingEnabled=false).
|
|
114
|
+
function modelSupportsThinking(model) {
|
|
115
|
+
return !normalizeModel(model).includes("claude-3-");
|
|
116
|
+
}
|
|
117
|
+
function modelSupportsAdaptiveThinking(model) {
|
|
118
|
+
const m = normalizeModel(model);
|
|
119
|
+
return m.includes("opus-4-6") || m.includes("sonnet-4-6");
|
|
120
|
+
}
|
|
121
|
+
// Anthropic's /v1/messages rejects thinking.enabled.budget_tokens < 1024.
|
|
122
|
+
const CLAUDE_MIN_THINKING_BUDGET = 1024;
|
|
123
|
+
function pickClaudeThinkingConfig(model, maxTokens) {
|
|
124
|
+
if (!modelSupportsThinking(model)) {
|
|
125
|
+
return { config: undefined, adjustedMaxTokens: maxTokens };
|
|
126
|
+
}
|
|
127
|
+
if (modelSupportsAdaptiveThinking(model)) {
|
|
128
|
+
// Adaptive has no fixed budget — the API internally picks. Don't
|
|
129
|
+
// inflate max_tokens; keep caller's cap.
|
|
130
|
+
return { config: { type: "adaptive" }, adjustedMaxTokens: maxTokens };
|
|
131
|
+
}
|
|
132
|
+
// Budget thinking (4-5 / haiku-4-5): budget_tokens must be >= 1024 AND
|
|
133
|
+
// strictly less than max_tokens. If caller's max_tokens is too low to
|
|
134
|
+
// fit the 1024 floor + 1, bump max_tokens so we can send a valid
|
|
135
|
+
// thinking block. Real Claude Code uses `getMaxThinkingTokensForModel
|
|
136
|
+
// = getModelMaxOutputTokens(model).upperLimit - 1` which is usually
|
|
137
|
+
// many thousands, but for a relay we want to respect the caller's cap
|
|
138
|
+
// unless it would force an invalid request.
|
|
139
|
+
const requiredMax = CLAUDE_MIN_THINKING_BUDGET + 1;
|
|
140
|
+
const adjustedMaxTokens = Math.max(maxTokens, requiredMax);
|
|
141
|
+
const budget = Math.max(CLAUDE_MIN_THINKING_BUDGET, adjustedMaxTokens - 1);
|
|
142
|
+
return {
|
|
143
|
+
config: { type: "enabled", budget_tokens: budget },
|
|
144
|
+
adjustedMaxTokens,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Assemble the `betas` array that goes into the /v1/messages body. Real
|
|
149
|
+
* Claude Code constructs this dynamically per-request from
|
|
150
|
+
* getAllModelBetas() — the key branches are:
|
|
151
|
+
* 1. non-haiku → push `claude-code-20250219`
|
|
152
|
+
* 2. OAuth subscriber → push `oauth-2025-04-20`
|
|
153
|
+
* 3. model supports interleaved-source-processing (ISP, i.e. any 4+) →
|
|
154
|
+
* push `interleaved-thinking-2025-05-14`
|
|
155
|
+
* Source: claude-code-best/src/utils/betas.ts:233-261 (getAllModelBetas).
|
|
156
|
+
*
|
|
157
|
+
* The Anthropic SDK later materializes this array into the
|
|
158
|
+
* `anthropic-beta` HTTP header as a comma-separated list. Sending it via
|
|
159
|
+
* the body instead of a static header is indistinguishable from the SDK
|
|
160
|
+
* wire format (we are literally doing the same thing the SDK does), but
|
|
161
|
+
* making it dynamic per-model avoids the Haiku mismatch where real CLI
|
|
162
|
+
* drops `claude-code-20250219` but our old static header always sent it.
|
|
163
|
+
*/
|
|
164
|
+
function pickClaudeBetasForModel(model) {
|
|
165
|
+
const m = normalizeModel(model);
|
|
166
|
+
const isHaiku = m.includes("haiku");
|
|
167
|
+
const betas = [];
|
|
168
|
+
if (!isHaiku)
|
|
169
|
+
betas.push("claude-code-20250219");
|
|
170
|
+
// OAuth subscriber — always true for us since we only serve relay from
|
|
171
|
+
// Max-tier OAuth tokens.
|
|
172
|
+
betas.push("oauth-2025-04-20");
|
|
173
|
+
// Interleaved thinking — all Claude 4+ models support it.
|
|
174
|
+
if (modelSupportsThinking(model)) {
|
|
175
|
+
betas.push("interleaved-thinking-2025-05-14");
|
|
176
|
+
}
|
|
177
|
+
return betas;
|
|
178
|
+
}
|
|
91
179
|
// ── Proxy (honor HTTPS_PROXY / http_proxy env vars) ──
|
|
92
180
|
//
|
|
93
181
|
// Node's native fetch does NOT read these env vars automatically, so if the
|
|
@@ -640,9 +728,15 @@ async function doCallClaudeApi(opts) {
|
|
|
640
728
|
// message text so the cc_version.<FP3> suffix varies request-by-request,
|
|
641
729
|
// matching what real Claude Code sends. See computeClaudeFingerprint().
|
|
642
730
|
const attributionHeader = buildClaudeAttributionHeader(sanitizedPrompt, fingerprint.cc_version, fingerprint.cc_entrypoint);
|
|
731
|
+
// Per-request betas + thinking config, picked from the real CLI's
|
|
732
|
+
// per-model logic (see pickClaudeBetasForModel / pickClaudeThinkingConfig).
|
|
733
|
+
// These are two of the strongest fingerprint signals Anthropic could use
|
|
734
|
+
// to distinguish relay traffic from genuine CLI traffic.
|
|
735
|
+
const betasForRequest = pickClaudeBetasForModel(opts.model);
|
|
736
|
+
const { config: thinkingConfig, adjustedMaxTokens } = pickClaudeThinkingConfig(opts.model, maxTokens);
|
|
643
737
|
const body = {
|
|
644
738
|
model: normalizeModel(opts.model),
|
|
645
|
-
max_tokens:
|
|
739
|
+
max_tokens: adjustedMaxTokens,
|
|
646
740
|
system: [
|
|
647
741
|
{
|
|
648
742
|
type: "text",
|
|
@@ -681,7 +775,18 @@ async function doCallClaudeApi(opts) {
|
|
|
681
775
|
// entire account's traffic would be the opposite polarity of what the
|
|
682
776
|
// CLI ever emits. Switch to streaming to match.
|
|
683
777
|
stream: true,
|
|
778
|
+
// NOTE: `betas` is a client-side SDK-only param — the Anthropic SDK
|
|
779
|
+
// strips it out of the body and emits it as the `anthropic-beta`
|
|
780
|
+
// HTTP header. Anthropic's API rejects requests that carry `betas`
|
|
781
|
+
// in the wire body with `betas: Extra inputs are not permitted`.
|
|
782
|
+
// The header is set on the fetch call below, so don't put it here.
|
|
684
783
|
};
|
|
784
|
+
// `thinking` is always set on Claude 4+ models by real CLI. Omitting it
|
|
785
|
+
// would be an account-wide zero-thinking anomaly. Adaptive for 4-6
|
|
786
|
+
// models, enabled+budget for 4-5 / haiku.
|
|
787
|
+
if (thinkingConfig) {
|
|
788
|
+
body.thinking = thinkingConfig;
|
|
789
|
+
}
|
|
685
790
|
const bodyJson = JSON.stringify(body);
|
|
686
791
|
let transientAttempt = 0;
|
|
687
792
|
let hasRefreshed = false;
|
|
@@ -691,6 +796,15 @@ async function doCallClaudeApi(opts) {
|
|
|
691
796
|
method: "POST",
|
|
692
797
|
headers: {
|
|
693
798
|
...STATIC_CLAUDE_CODE_HEADERS,
|
|
799
|
+
// SSE streaming — Anthropic returns event-stream body when
|
|
800
|
+
// stream:true is set in the body. The SDK default sets an accept
|
|
801
|
+
// that includes text/event-stream; we match that exactly.
|
|
802
|
+
"accept": "application/json, text/event-stream",
|
|
803
|
+
// `anthropic-beta` is what the Anthropic SDK generates from the
|
|
804
|
+
// body's `betas` array. We could leave body.betas and drop this
|
|
805
|
+
// header, but some Anthropic deploys check header presence too,
|
|
806
|
+
// so we send both for safety. The values must match.
|
|
807
|
+
"anthropic-beta": betasForRequest.join(","),
|
|
694
808
|
"user-agent": fingerprint.user_agent,
|
|
695
809
|
"authorization": `Bearer ${creds.accessToken}`,
|
|
696
810
|
"x-claude-code-session-id": sessionId,
|