@tormentalabs/claude-code-wire-compat 0.5.0 → 0.6.0
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.
- package/CHANGELOG.md +167 -0
- package/README.md +19 -7
- package/dist/betas.d.ts +65 -7
- package/dist/betas.d.ts.map +1 -1
- package/dist/betas.js +143 -2
- package/dist/betas.js.map +1 -1
- package/dist/build-request.d.ts +12 -6
- package/dist/build-request.d.ts.map +1 -1
- package/dist/build-request.js +44 -15
- package/dist/build-request.js.map +1 -1
- package/dist/contracts.d.ts +7 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +5 -0
- package/dist/fingerprint.js.map +1 -1
- package/dist/headers.d.ts.map +1 -1
- package/dist/headers.js +5 -2
- package/dist/headers.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model-capabilities.d.ts +3 -3
- package/dist/model-capabilities.d.ts.map +1 -1
- package/dist/model-capabilities.js +55 -19
- package/dist/model-capabilities.js.map +1 -1
- package/dist/model-identity.d.ts +9 -1
- package/dist/model-identity.d.ts.map +1 -1
- package/dist/model-identity.js +19 -1
- package/dist/model-identity.js.map +1 -1
- package/dist/model-queries.d.ts +17 -5
- package/dist/model-queries.d.ts.map +1 -1
- package/dist/model-queries.js +33 -6
- package/dist/model-queries.js.map +1 -1
- package/dist/models.js +1 -1
- package/dist/models.js.map +1 -1
- package/dist/profiles/beta-registry-2.1.280.d.ts +211 -0
- package/dist/profiles/beta-registry-2.1.280.d.ts.map +1 -0
- package/dist/profiles/beta-registry-2.1.280.js +292 -0
- package/dist/profiles/beta-registry-2.1.280.js.map +1 -0
- package/dist/profiles/claude-code-2.1.280.d.ts +3 -0
- package/dist/profiles/claude-code-2.1.280.d.ts.map +1 -0
- package/dist/profiles/claude-code-2.1.280.js +359 -0
- package/dist/profiles/claude-code-2.1.280.js.map +1 -0
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +3 -0
- package/dist/redaction.js.map +1 -1
- package/dist/request-body.d.ts +1 -1
- package/dist/request-body.d.ts.map +1 -1
- package/dist/request-body.js +16 -5
- package/dist/request-body.js.map +1 -1
- package/dist/thinking.d.ts +53 -2
- package/dist/thinking.d.ts.map +1 -1
- package/dist/thinking.js +124 -26
- package/dist/thinking.js.map +1 -1
- package/package.json +6 -2
- package/src/betas.ts +208 -9
- package/src/build-request.ts +54 -21
- package/src/contracts.ts +7 -2
- package/src/fingerprint.ts +5 -0
- package/src/headers.ts +4 -2
- package/src/index.ts +2 -0
- package/src/model-capabilities.ts +55 -19
- package/src/model-identity.ts +14 -1
- package/src/model-queries.ts +38 -6
- package/src/models.ts +1 -1
- package/src/profiles/beta-registry-2.1.280.ts +309 -0
- package/src/profiles/claude-code-2.1.280.ts +364 -0
- package/src/redaction.ts +3 -0
- package/src/request-body.ts +25 -3
- package/src/thinking.ts +148 -26
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
import type { ClaudeCodeProtocolProfile } from "../contracts.js";
|
|
4
|
+
import { COUNT_TOKENS_ENDPOINT } from "../count-tokens.js";
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* Protocol profile for genuine client 2.1.280.
|
|
8
|
+
*
|
|
9
|
+
* Provenance: extracted from the official 2.1.280 win32-x64 binary. The
|
|
10
|
+
* catalogue and every scalar below are transcribed from
|
|
11
|
+
* `docs/protocol/versions/claude-code-2.1.280-analysis.md`, which records the
|
|
12
|
+
* byte offsets they came from. The catalogue was additionally re-extracted
|
|
13
|
+
* from the carved bundle independently of that document (cluster bytes
|
|
14
|
+
* 5941320-5955961) and agreed on every field of all twenty entries; that
|
|
15
|
+
* re-extraction is not a claim you have to take on trust, because the method
|
|
16
|
+
* it used is written down in section 5.2.1 of the analysis document and can be
|
|
17
|
+
* re-run against the same dump.
|
|
18
|
+
*
|
|
19
|
+
* `effort_cost_index` is deliberately omitted, as in the 2.1.233 profile, and
|
|
20
|
+
* so are the other nine static-catalogue fields the package does not model
|
|
21
|
+
* (analysis document section 5.1).
|
|
22
|
+
*
|
|
23
|
+
* Context modelling follows 2.1.233 exactly. `supports_1m_suffix` is not
|
|
24
|
+
* modelled, so an entry declaring a 200000 window with nothing but that flag
|
|
25
|
+
* carries no `context` object here. `native_1m_3p` on `claude-sonnet-5` names
|
|
26
|
+
* bedrock, vertex and foundry; this package is anthropic-only, so that flag is
|
|
27
|
+
* not modelled either.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
function deepFreeze<T>(value: T): T {
|
|
31
|
+
if (value !== null && typeof value === "object") {
|
|
32
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
33
|
+
deepFreeze(Reflect.get(value, key));
|
|
34
|
+
}
|
|
35
|
+
Object.freeze(value);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const CLAUDE_CODE_2_1_280_PROFILE: ClaudeCodeProtocolProfile =
|
|
41
|
+
deepFreeze({
|
|
42
|
+
id: "claude-code-2.1.280-sdk-0.112.1",
|
|
43
|
+
cliVersion: "2.1.280",
|
|
44
|
+
sdkVersion: "0.112.1",
|
|
45
|
+
endpoint: "https://api.anthropic.com/v1/messages?beta=true",
|
|
46
|
+
countTokensEndpoint: COUNT_TOKENS_ENDPOINT,
|
|
47
|
+
entrypoint: "cli",
|
|
48
|
+
userAgent: "claude-cli/2.1.280 (external, cli)",
|
|
49
|
+
buildTime: "2026-09-21T20:40:17Z",
|
|
50
|
+
gitSha: "80abbfe7d7232280011ff01a21ae3338f4c6e372",
|
|
51
|
+
// RETAINED from 2.1.233, not asserted. The 2.1.280 analysis document does
|
|
52
|
+
// not examine the billing block at all -- it contains no occurrence of
|
|
53
|
+
// "attribution" -- so this is silence, which is not the same as evidence of
|
|
54
|
+
// no change. The only indirect support is that the salt behind the
|
|
55
|
+
// fingerprint is unchanged (section 13); the fingerprint it produces, not
|
|
56
|
+
// the salt, is what appears on the billing line. Section 13.2 draws this
|
|
57
|
+
// exact distinction for the beta policy and it applies here too: where the
|
|
58
|
+
// evidence is absent the previous release's value is retained rather than
|
|
59
|
+
// re-derived, and a later release that captures live traffic should settle
|
|
60
|
+
// it.
|
|
61
|
+
attributionHeaderEnabled: true,
|
|
62
|
+
provider: "anthropic",
|
|
63
|
+
anthropicVersion: "2023-06-01",
|
|
64
|
+
// Section 9.2: `context_hint` remains off for 2.1.280. Section 7.6 agrees
|
|
65
|
+
// -- its fourteen-identifier default-path `anthropic-beta` literal contains
|
|
66
|
+
// no `context-hint` identifier -- but that is the same gate restated, not a
|
|
67
|
+
// second line of evidence, because the 7.6 row cites the gate 9.2 resolves.
|
|
68
|
+
// Only two things put that identifier into an emitted list: this flag,
|
|
69
|
+
// which also emits a `context_hint` body field, and a caller passing the
|
|
70
|
+
// identifier explicitly through the `additionalBetas` request seam.
|
|
71
|
+
contextHintEnabled: false,
|
|
72
|
+
/*
|
|
73
|
+
* The eleven flags of analysis document section 13.2, which grades each
|
|
74
|
+
* one. That grading is carried here per flag, because two of the eleven are
|
|
75
|
+
* *retained* from 2.1.233 rather than resolved from this build -- their
|
|
76
|
+
* upstream gates gave no answer -- and nothing in the value itself
|
|
77
|
+
* distinguishes a retained flag from a derived one. A later release that
|
|
78
|
+
* resolves a retained gate changes that flag on evidence; a later release
|
|
79
|
+
* that merely repeats it has learned nothing.
|
|
80
|
+
*/
|
|
81
|
+
betaPolicy: {
|
|
82
|
+
// RETAINED. The gate reduces to a predicate whose own gate is
|
|
83
|
+
// unresolved. What it decides is position rather than presence: the SDK
|
|
84
|
+
// appends the OAuth beta unconditionally on a token-cache session
|
|
85
|
+
// (section 8.3), so the choice is slot 2 versus appended last, and this
|
|
86
|
+
// profile pins slot 2 as 2.1.195 and 2.1.233 do.
|
|
87
|
+
oauthAuthenticated: true,
|
|
88
|
+
// Derived.
|
|
89
|
+
experimentalBetasEnabled: true,
|
|
90
|
+
// From the bundle: the gate reads an opt-out environment variable that is
|
|
91
|
+
// unset by default.
|
|
92
|
+
oneMillionContextEnabled: true,
|
|
93
|
+
// Derived.
|
|
94
|
+
interleavedThinkingEnabled: true,
|
|
95
|
+
// Derived.
|
|
96
|
+
interactive: true,
|
|
97
|
+
// Derived.
|
|
98
|
+
thinkingSummariesShown: false,
|
|
99
|
+
// Derived.
|
|
100
|
+
thinkingTokenCountEnabled: true,
|
|
101
|
+
// From the bundle, and inert for this profile either way: the 2.1.280
|
|
102
|
+
// registry slot that would carry `narration_summaries` is null in this
|
|
103
|
+
// build, so there is no entry for this flag to gate.
|
|
104
|
+
narrationSummariesEnabled: false,
|
|
105
|
+
// Derived.
|
|
106
|
+
structuredOutputsEnabled: false,
|
|
107
|
+
// RETAINED. Its gates are unresolved in this build.
|
|
108
|
+
afkModeEnabled: false,
|
|
109
|
+
// Changed from 2.1.233, where the 2.1.233 profile pins `false`. Section
|
|
110
|
+
// 7.6.2 resolves all three legs of the 2.1.280 gate to true. Whether the
|
|
111
|
+
// 2.1.233 value was always wrong cannot be settled without the 2.1.233
|
|
112
|
+
// binary, and inferring one release's value from another's is exactly
|
|
113
|
+
// what the tracking runbook forbids, so that profile is deliberately left
|
|
114
|
+
// alone.
|
|
115
|
+
cacheDiagnosisEnabled: true,
|
|
116
|
+
},
|
|
117
|
+
/**
|
|
118
|
+
* Verbatim genuine-client catalogue, 20 entries in the catalogue's own
|
|
119
|
+
* order. `defaultEffort` is policy exposed as catalogue data; this package
|
|
120
|
+
* must never apply it to a request.
|
|
121
|
+
*/
|
|
122
|
+
supportedModels: {
|
|
123
|
+
"claude-3-5-haiku": {
|
|
124
|
+
family: "haiku",
|
|
125
|
+
capabilities: [],
|
|
126
|
+
maxOutputTokens: { default: 8192, upper: 8192 },
|
|
127
|
+
},
|
|
128
|
+
"claude-haiku-4-5": {
|
|
129
|
+
family: "haiku",
|
|
130
|
+
capabilities: ["context_management"],
|
|
131
|
+
maxOutputTokens: { default: 32000, upper: 64000 },
|
|
132
|
+
},
|
|
133
|
+
"claude-3-5-sonnet": {
|
|
134
|
+
family: "sonnet",
|
|
135
|
+
capabilities: [],
|
|
136
|
+
maxOutputTokens: { default: 8192, upper: 8192 },
|
|
137
|
+
},
|
|
138
|
+
"claude-3-7-sonnet": {
|
|
139
|
+
family: "sonnet",
|
|
140
|
+
capabilities: [],
|
|
141
|
+
maxOutputTokens: { default: 32000, upper: 64000 },
|
|
142
|
+
},
|
|
143
|
+
"claude-sonnet-4-0": {
|
|
144
|
+
family: "sonnet",
|
|
145
|
+
context: { window: 200000, supports1mBeta: true },
|
|
146
|
+
capabilities: ["context_management"],
|
|
147
|
+
maxOutputTokens: { default: 32000, upper: 64000 },
|
|
148
|
+
},
|
|
149
|
+
"claude-sonnet-4-5": {
|
|
150
|
+
family: "sonnet",
|
|
151
|
+
context: { window: 200000, supports1mBeta: true },
|
|
152
|
+
capabilities: ["context_management"],
|
|
153
|
+
maxOutputTokens: { default: 32000, upper: 64000 },
|
|
154
|
+
},
|
|
155
|
+
"claude-sonnet-4-6": {
|
|
156
|
+
family: "sonnet",
|
|
157
|
+
context: { window: 200000, supports1mBeta: true },
|
|
158
|
+
maxOutputTokens: { default: 32000, upper: 128000 },
|
|
159
|
+
capabilities: [
|
|
160
|
+
"effort",
|
|
161
|
+
"max_effort",
|
|
162
|
+
"adaptive_thinking",
|
|
163
|
+
"context_management",
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
// The entry also declares `native_1m_3p` for bedrock/vertex/foundry;
|
|
167
|
+
// this package is anthropic-only, so that flag is not modelled.
|
|
168
|
+
"claude-sonnet-5": {
|
|
169
|
+
family: "sonnet",
|
|
170
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
171
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
172
|
+
capabilities: [
|
|
173
|
+
"effort",
|
|
174
|
+
"max_effort",
|
|
175
|
+
"xhigh_effort",
|
|
176
|
+
"adaptive_thinking",
|
|
177
|
+
"mid_conv_system",
|
|
178
|
+
"context_management",
|
|
179
|
+
],
|
|
180
|
+
defaultEffort: "high",
|
|
181
|
+
},
|
|
182
|
+
"claude-opus-4-0": {
|
|
183
|
+
family: "opus",
|
|
184
|
+
capabilities: ["context_management"],
|
|
185
|
+
maxOutputTokens: { default: 32000, upper: 32000 },
|
|
186
|
+
},
|
|
187
|
+
"claude-opus-4-1": {
|
|
188
|
+
family: "opus",
|
|
189
|
+
capabilities: ["context_management"],
|
|
190
|
+
maxOutputTokens: { default: 32000, upper: 32000 },
|
|
191
|
+
},
|
|
192
|
+
"claude-opus-4-5": {
|
|
193
|
+
family: "opus",
|
|
194
|
+
capabilities: ["context_management"],
|
|
195
|
+
maxOutputTokens: { default: 32000, upper: 64000 },
|
|
196
|
+
},
|
|
197
|
+
"claude-opus-4-6": {
|
|
198
|
+
family: "opus",
|
|
199
|
+
context: { window: 200000, supports1mBeta: true },
|
|
200
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
201
|
+
capabilities: [
|
|
202
|
+
"effort",
|
|
203
|
+
"max_effort",
|
|
204
|
+
"adaptive_thinking",
|
|
205
|
+
"context_management",
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
"claude-opus-4-7": {
|
|
209
|
+
family: "opus",
|
|
210
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
211
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
212
|
+
capabilities: [
|
|
213
|
+
"effort",
|
|
214
|
+
"max_effort",
|
|
215
|
+
"xhigh_effort",
|
|
216
|
+
"adaptive_thinking",
|
|
217
|
+
"context_management",
|
|
218
|
+
],
|
|
219
|
+
defaultEffort: "xhigh",
|
|
220
|
+
},
|
|
221
|
+
// Gains `mid_conv_tool_change` since 2.1.233. That key is mapped by
|
|
222
|
+
// `deriveCapabilitiesFromCatalogue` to `midConvToolChange`, and it
|
|
223
|
+
// drives the `mid-conversation-tool-changes-2026-07-01` beta header
|
|
224
|
+
// (section 7.6) through the guard transcribed in section 13.3.
|
|
225
|
+
"claude-opus-4-8": {
|
|
226
|
+
family: "opus",
|
|
227
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
228
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
229
|
+
capabilities: [
|
|
230
|
+
"effort",
|
|
231
|
+
"max_effort",
|
|
232
|
+
"xhigh_effort",
|
|
233
|
+
"adaptive_thinking",
|
|
234
|
+
"mid_conv_system",
|
|
235
|
+
"mid_conv_tool_change",
|
|
236
|
+
"context_management",
|
|
237
|
+
"fast_mode",
|
|
238
|
+
"lean_prompt",
|
|
239
|
+
],
|
|
240
|
+
defaultEffort: "high",
|
|
241
|
+
},
|
|
242
|
+
// Gains `mid_conv_tool_change` and `thinking_disabled_effort_cap` since
|
|
243
|
+
// 2.1.233.
|
|
244
|
+
"claude-opus-5": {
|
|
245
|
+
family: "opus",
|
|
246
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
247
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
248
|
+
capabilities: [
|
|
249
|
+
"effort",
|
|
250
|
+
"max_effort",
|
|
251
|
+
"xhigh_effort",
|
|
252
|
+
"adaptive_thinking",
|
|
253
|
+
"mid_conv_system",
|
|
254
|
+
"mid_conv_tool_change",
|
|
255
|
+
"context_management",
|
|
256
|
+
"thinking_disabled_effort_cap",
|
|
257
|
+
"fast_mode",
|
|
258
|
+
"lean_prompt",
|
|
259
|
+
"refusal_fallback",
|
|
260
|
+
"opus_5_prompt_bundle",
|
|
261
|
+
],
|
|
262
|
+
defaultEffort: "high",
|
|
263
|
+
},
|
|
264
|
+
// New at 2.1.280, and the only catalogue entry in any ported profile
|
|
265
|
+
// whose `default_effort` is `medium`. It is also the only entry whose
|
|
266
|
+
// default and upper output-token limits are both 128000.
|
|
267
|
+
"claude-opus-5-5": {
|
|
268
|
+
family: "opus",
|
|
269
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
270
|
+
maxOutputTokens: { default: 128000, upper: 128000 },
|
|
271
|
+
capabilities: [
|
|
272
|
+
"effort",
|
|
273
|
+
"max_effort",
|
|
274
|
+
"xhigh_effort",
|
|
275
|
+
"adaptive_thinking",
|
|
276
|
+
"rejects_disabled_thinking",
|
|
277
|
+
"mid_conv_system",
|
|
278
|
+
"mid_conv_tool_change",
|
|
279
|
+
"per_turn_effort",
|
|
280
|
+
"per_turn_timing",
|
|
281
|
+
"context_management",
|
|
282
|
+
"fast_mode",
|
|
283
|
+
"lean_prompt",
|
|
284
|
+
"refusal_fallback",
|
|
285
|
+
"opus_5_5_prompt_bundle",
|
|
286
|
+
],
|
|
287
|
+
defaultEffort: "medium",
|
|
288
|
+
},
|
|
289
|
+
// Gains `mid_conv_tool_change` since 2.1.233. Carries no `fast_mode`,
|
|
290
|
+
// as in 2.1.233.
|
|
291
|
+
"claude-fable-5": {
|
|
292
|
+
family: "fable",
|
|
293
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
294
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
295
|
+
capabilities: [
|
|
296
|
+
"effort",
|
|
297
|
+
"max_effort",
|
|
298
|
+
"xhigh_effort",
|
|
299
|
+
"adaptive_thinking",
|
|
300
|
+
"rejects_disabled_thinking",
|
|
301
|
+
"mid_conv_system",
|
|
302
|
+
"mid_conv_tool_change",
|
|
303
|
+
"context_management",
|
|
304
|
+
"lean_prompt",
|
|
305
|
+
"fable_5_mitigations",
|
|
306
|
+
"refusal_fallback",
|
|
307
|
+
],
|
|
308
|
+
defaultEffort: "high",
|
|
309
|
+
},
|
|
310
|
+
// New at 2.1.280.
|
|
311
|
+
"claude-fable-5-1": {
|
|
312
|
+
family: "fable",
|
|
313
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
314
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
315
|
+
capabilities: [
|
|
316
|
+
"effort",
|
|
317
|
+
"max_effort",
|
|
318
|
+
"xhigh_effort",
|
|
319
|
+
"adaptive_thinking",
|
|
320
|
+
"rejects_disabled_thinking",
|
|
321
|
+
"mid_conv_system",
|
|
322
|
+
"mid_conv_tool_change",
|
|
323
|
+
"per_turn_effort",
|
|
324
|
+
"per_turn_timing",
|
|
325
|
+
"context_management",
|
|
326
|
+
"lean_prompt",
|
|
327
|
+
"fable_5_mitigations",
|
|
328
|
+
"refusal_fallback",
|
|
329
|
+
"fable_5_1_prompt_bundle",
|
|
330
|
+
],
|
|
331
|
+
defaultEffort: "high",
|
|
332
|
+
},
|
|
333
|
+
// Keeps the empty capability array it was catalogued with at 2.1.233.
|
|
334
|
+
// That is a denial, not a gap.
|
|
335
|
+
"claude-mythos-5": {
|
|
336
|
+
family: "mythos",
|
|
337
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
338
|
+
capabilities: [],
|
|
339
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
340
|
+
},
|
|
341
|
+
// New at 2.1.280. Unlike `claude-fable-5-1` it carries no
|
|
342
|
+
// `per_turn_effort` and no `refusal_fallback`.
|
|
343
|
+
"claude-mythos-5-1": {
|
|
344
|
+
family: "mythos",
|
|
345
|
+
context: { window: 1e6, native1m: true, supports1mBeta: true },
|
|
346
|
+
maxOutputTokens: { default: 64000, upper: 128000 },
|
|
347
|
+
capabilities: [
|
|
348
|
+
"effort",
|
|
349
|
+
"max_effort",
|
|
350
|
+
"xhigh_effort",
|
|
351
|
+
"adaptive_thinking",
|
|
352
|
+
"rejects_disabled_thinking",
|
|
353
|
+
"mid_conv_system",
|
|
354
|
+
"mid_conv_tool_change",
|
|
355
|
+
"per_turn_timing",
|
|
356
|
+
"context_management",
|
|
357
|
+
"lean_prompt",
|
|
358
|
+
"fable_5_mitigations",
|
|
359
|
+
"fable_5_1_prompt_bundle",
|
|
360
|
+
],
|
|
361
|
+
defaultEffort: "high",
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
});
|
package/src/redaction.ts
CHANGED
|
@@ -75,6 +75,7 @@ const ENDPOINT = "https://api.anthropic.com/v1/messages?beta=true";
|
|
|
75
75
|
const PINNED_PROFILE_IDS: ReadonlySet<string> = new Set([
|
|
76
76
|
"claude-code-2.1.195-sdk-0.94.0",
|
|
77
77
|
"claude-code-2.1.233-sdk-0.112.1",
|
|
78
|
+
"claude-code-2.1.280-sdk-0.112.1",
|
|
78
79
|
]);
|
|
79
80
|
const FORBIDDEN_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
80
81
|
const SAFE_ERROR_CODES = new Set([
|
|
@@ -267,6 +268,8 @@ function capabilityDecisions(
|
|
|
267
268
|
contextManagement: requested?.contextManagement ?? false,
|
|
268
269
|
temperature: requested?.temperature ?? false,
|
|
269
270
|
rejectsDisabledThinking: requested?.rejectsDisabledThinking ?? false,
|
|
271
|
+
midConvToolChange: requested?.midConvToolChange ?? false,
|
|
272
|
+
perTurnEffort: requested?.perTurnEffort ?? false,
|
|
270
273
|
});
|
|
271
274
|
}
|
|
272
275
|
|
package/src/request-body.ts
CHANGED
|
@@ -357,6 +357,8 @@ type ModelResolution = Readonly<{
|
|
|
357
357
|
contextManagement: boolean;
|
|
358
358
|
temperature: boolean;
|
|
359
359
|
rejectsDisabledThinking: boolean;
|
|
360
|
+
midConvToolChange: boolean;
|
|
361
|
+
perTurnEffort: boolean;
|
|
360
362
|
}>;
|
|
361
363
|
}>;
|
|
362
364
|
|
|
@@ -1451,7 +1453,11 @@ function modelResolution(
|
|
|
1451
1453
|
(capabilities.temperature !== undefined &&
|
|
1452
1454
|
typeof capabilities.temperature !== "boolean") ||
|
|
1453
1455
|
(capabilities.rejectsDisabledThinking !== undefined &&
|
|
1454
|
-
typeof capabilities.rejectsDisabledThinking !== "boolean")
|
|
1456
|
+
typeof capabilities.rejectsDisabledThinking !== "boolean") ||
|
|
1457
|
+
(capabilities.midConvToolChange !== undefined &&
|
|
1458
|
+
typeof capabilities.midConvToolChange !== "boolean") ||
|
|
1459
|
+
(capabilities.perTurnEffort !== undefined &&
|
|
1460
|
+
typeof capabilities.perTurnEffort !== "boolean")
|
|
1455
1461
|
) {
|
|
1456
1462
|
fail("INVALID_INPUT");
|
|
1457
1463
|
}
|
|
@@ -1486,6 +1492,14 @@ function modelResolution(
|
|
|
1486
1492
|
capabilities.rejectsDisabledThinking,
|
|
1487
1493
|
derived.rejectsDisabledThinking,
|
|
1488
1494
|
),
|
|
1495
|
+
midConvToolChange: capabilityBoolean(
|
|
1496
|
+
capabilities.midConvToolChange,
|
|
1497
|
+
derived.midConvToolChange,
|
|
1498
|
+
),
|
|
1499
|
+
perTurnEffort: capabilityBoolean(
|
|
1500
|
+
capabilities.perTurnEffort,
|
|
1501
|
+
derived.perTurnEffort,
|
|
1502
|
+
),
|
|
1489
1503
|
},
|
|
1490
1504
|
};
|
|
1491
1505
|
}
|
|
@@ -1731,6 +1745,7 @@ export function buildCanonicalBody(
|
|
|
1731
1745
|
rawSystemBlocks: unknown,
|
|
1732
1746
|
rawMetadata: unknown,
|
|
1733
1747
|
profile?: ClaudeCodeProtocolProfile,
|
|
1748
|
+
thinkingDisplayOverride?: "updates",
|
|
1734
1749
|
): Readonly<Record<string, unknown>> {
|
|
1735
1750
|
inspectJsonInputs([
|
|
1736
1751
|
rawInput,
|
|
@@ -1837,6 +1852,7 @@ export function buildCanonicalBody(
|
|
|
1837
1852
|
effectiveProfile.betaPolicy,
|
|
1838
1853
|
maxTokens,
|
|
1839
1854
|
effectiveProfile,
|
|
1855
|
+
thinkingDisplayOverride,
|
|
1840
1856
|
);
|
|
1841
1857
|
if (resolved.emitted !== undefined) result["thinking"] = resolved.emitted;
|
|
1842
1858
|
|
|
@@ -1902,9 +1918,15 @@ export function buildCanonicalBody(
|
|
|
1902
1918
|
result["output_format"] = nullable(item, outputFormat);
|
|
1903
1919
|
else if (key === "toolChoice") {
|
|
1904
1920
|
const validatedToolChoice = toolChoice(item);
|
|
1921
|
+
// Upstream demotes only `tool` here. This package deliberately also
|
|
1922
|
+
// demotes `any`: the Messages API rejects every forced tool choice while
|
|
1923
|
+
// extended thinking is on, so passing `any` through could only produce
|
|
1924
|
+
// an HTTP 400. Shared by every profile; see `MEMORY.md`, 2026-09-24.
|
|
1925
|
+
const forcedToolChoice =
|
|
1926
|
+
validatedToolChoice["type"] === "tool" ||
|
|
1927
|
+
validatedToolChoice["type"] === "any";
|
|
1905
1928
|
result["tool_choice"] =
|
|
1906
|
-
|
|
1907
|
-
resolved.extendedThinkingActive
|
|
1929
|
+
forcedToolChoice && resolved.extendedThinkingActive
|
|
1908
1930
|
? { type: "auto" }
|
|
1909
1931
|
: validatedToolChoice;
|
|
1910
1932
|
} else if (key === "topP") result["top_p"] = requireNumber(item);
|
package/src/thinking.ts
CHANGED
|
@@ -70,7 +70,12 @@ export interface ResolvedThinking {
|
|
|
70
70
|
* suppresses `temperature`, matching upstream `nr`.
|
|
71
71
|
*/
|
|
72
72
|
readonly requestActive: boolean;
|
|
73
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Whether a forced `tool_choice` (type `tool` or `any`) must be demoted to
|
|
75
|
+
* `auto`. Upstream demotes only `tool`; demoting `any` as well is a
|
|
76
|
+
* deliberate divergence, because the API rejects any forced tool choice
|
|
77
|
+
* while extended thinking is on (see `MEMORY.md`, 2026-09-24).
|
|
78
|
+
*/
|
|
74
79
|
readonly extendedThinkingActive: boolean;
|
|
75
80
|
}
|
|
76
81
|
|
|
@@ -275,6 +280,92 @@ export function isThinkingDisplayActive(
|
|
|
275
280
|
);
|
|
276
281
|
}
|
|
277
282
|
|
|
283
|
+
/**
|
|
284
|
+
* The three thinking types the resolver can emit, or `undefined` when no
|
|
285
|
+
* `thinking` object reaches the wire at all.
|
|
286
|
+
*/
|
|
287
|
+
export type ResolvedThinkingType = "adaptive" | "enabled" | "disabled";
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Narrows an unvalidated caller `thinking` value to its `type` literal.
|
|
291
|
+
*
|
|
292
|
+
* Deliberately tolerant of unvalidated input for the same reason
|
|
293
|
+
* `isThinkingDisplayActive` is: beta composition asks this question before
|
|
294
|
+
* `buildCanonicalBody` has validated the shape. Anything malformed answers
|
|
295
|
+
* `undefined` here and is rejected later by the body validator.
|
|
296
|
+
*/
|
|
297
|
+
function thinkingRequestType(
|
|
298
|
+
request: unknown,
|
|
299
|
+
): ThinkingRequest["type"] | undefined {
|
|
300
|
+
if (request === null || typeof request !== "object") return undefined;
|
|
301
|
+
const type = (request as Record<string, unknown>)["type"];
|
|
302
|
+
if (type === "enabled" || type === "adaptive" || type === "disabled") {
|
|
303
|
+
return type;
|
|
304
|
+
}
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Decides WHICH thinking object `resolveThinking` will emit, without building
|
|
310
|
+
* it.
|
|
311
|
+
*
|
|
312
|
+
* Split out of `resolveThinking` so that beta composition and body emission
|
|
313
|
+
* answer the same question from one place. A second, independently written
|
|
314
|
+
* copy of this predicate is exactly how a beta header and the body field it
|
|
315
|
+
* is coupled to drift apart.
|
|
316
|
+
*/
|
|
317
|
+
export function resolveThinkingType(
|
|
318
|
+
requestType: ThinkingRequest["type"] | undefined,
|
|
319
|
+
capabilities: ClaudeCodeCapabilities,
|
|
320
|
+
): ResolvedThinkingType | undefined {
|
|
321
|
+
const requestActive = requestType !== undefined && requestType !== "disabled";
|
|
322
|
+
if (requestActive && capabilities.thinking) {
|
|
323
|
+
return capabilities.adaptiveThinking ? "adaptive" : "enabled";
|
|
324
|
+
}
|
|
325
|
+
if (
|
|
326
|
+
requestType === "disabled" &&
|
|
327
|
+
capabilities.thinking &&
|
|
328
|
+
!capabilities.rejectsDisabledThinking
|
|
329
|
+
) {
|
|
330
|
+
return "disabled";
|
|
331
|
+
}
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Upstream `ac = Kg && Fg() && iQt(model)`, the predicate the 2.1.280 thinking
|
|
337
|
+
* push sites gate on, with `Fg()` (`experimentalBetasEnabled`) factored OUT.
|
|
338
|
+
*
|
|
339
|
+
* Every push site conjoins the experimental gate itself, so folding it in here
|
|
340
|
+
* would count it twice and make the one site that legitimately does not want it
|
|
341
|
+
* impossible to express. `Kg`'s environment-disable term is not modelled: this
|
|
342
|
+
* package reads no environment.
|
|
343
|
+
*
|
|
344
|
+
* This is deliberately NOT `ResolvedThinking.requestActive`. That one is true
|
|
345
|
+
* whenever the caller asked for thinking at all, including for a model whose
|
|
346
|
+
* capabilities emit no thinking object — which would ship a thinking beta
|
|
347
|
+
* header for a body that carries no thinking block.
|
|
348
|
+
*
|
|
349
|
+
* Upstream `ac` conjoins only the interleaved predicate `iQt`; requiring a
|
|
350
|
+
* resolved `"adaptive"`/`"enabled"` type additionally requires
|
|
351
|
+
* `capabilities.thinking`, one conjunct more than upstream. That extra term is
|
|
352
|
+
* unobservable in practice: `supportsThinking` and `supportsInterleavedThinking`
|
|
353
|
+
* in `model-capabilities.ts` are the same expression, so no derived capability
|
|
354
|
+
* set separates them. Only an explicit caller capability override can, and then
|
|
355
|
+
* the package declines to announce a thinking beta for a request whose body
|
|
356
|
+
* will carry no thinking object.
|
|
357
|
+
*/
|
|
358
|
+
export function isThinkingActive(
|
|
359
|
+
request: unknown,
|
|
360
|
+
capabilities: ClaudeCodeCapabilities,
|
|
361
|
+
): boolean {
|
|
362
|
+
const type = resolveThinkingType(thinkingRequestType(request), capabilities);
|
|
363
|
+
return (
|
|
364
|
+
(type === "adaptive" || type === "enabled") &&
|
|
365
|
+
capabilities.interleavedThinking
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
278
369
|
/**
|
|
279
370
|
* Resolves the caller's thinking request into the object the genuine client
|
|
280
371
|
* would put on the wire.
|
|
@@ -283,6 +374,13 @@ export function isThinkingDisplayActive(
|
|
|
283
374
|
* for the enabled branch — `budget_tokens` FIRST — and `{type, display}` for
|
|
284
375
|
* adaptive. Serialised bodies are compared byte for byte, so the insertion
|
|
285
376
|
* order below must not be rearranged.
|
|
377
|
+
*
|
|
378
|
+
* `displayOverride` is the body-side half of beta push site 12b
|
|
379
|
+
* (`thinkingDisplayOverride` from `composeBetasWithAudit`); it is not
|
|
380
|
+
* caller-facing, which is why `ThinkingDisplay` stays unwidened. When both it
|
|
381
|
+
* and a caller display are present the override wins -- a combination the
|
|
382
|
+
* composition guard makes unreachable, since site 12b requires that the caller
|
|
383
|
+
* supplied no display.
|
|
286
384
|
*/
|
|
287
385
|
export function resolveThinking(
|
|
288
386
|
request: ThinkingRequest | undefined,
|
|
@@ -291,6 +389,7 @@ export function resolveThinking(
|
|
|
291
389
|
betaPolicy: ClaudeCodeBetaPolicy,
|
|
292
390
|
maxTokens: number,
|
|
293
391
|
profile: ClaudeCodeProtocolProfile = CLAUDE_CODE_2_1_195_PROFILE,
|
|
392
|
+
displayOverride?: "updates",
|
|
294
393
|
): ResolvedThinking {
|
|
295
394
|
// Upstream `nr = n.type !== "disabled" && !CLAUDE_CODE_DISABLE_THINKING`.
|
|
296
395
|
const requestActive = request !== undefined && request.type !== "disabled";
|
|
@@ -301,38 +400,61 @@ export function resolveThinking(
|
|
|
301
400
|
);
|
|
302
401
|
const display = displayActive ? request?.display : undefined;
|
|
303
402
|
|
|
403
|
+
const resolvedType = resolveThinkingType(request?.type, capabilities);
|
|
404
|
+
|
|
304
405
|
let emitted: Record<string, unknown> | undefined;
|
|
305
406
|
|
|
306
|
-
if (
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
407
|
+
if (resolvedType === "adaptive") {
|
|
408
|
+
emitted = { type: "adaptive" };
|
|
409
|
+
if (display !== undefined) emitted["display"] = display;
|
|
410
|
+
// Assignment, not reconstruction: an existing key keeps its position and a
|
|
411
|
+
// new one appends last, matching upstream `{...yc, display: "updates"}`.
|
|
412
|
+
if (displayOverride !== undefined) emitted["display"] = displayOverride;
|
|
413
|
+
} else if (resolvedType === "enabled") {
|
|
414
|
+
// Transcribed from the 2.1.280 analysis document:
|
|
415
|
+
// let hf = mlo(_e);
|
|
416
|
+
// if (r.type === "enabled" && r.budgetTokens !== void 0) hf = r.budgetTokens;
|
|
417
|
+
// hf = Math.max(1024, Math.min(Rv - 1, hf));
|
|
418
|
+
// The default budget is the model's upper limit minus one. The caller's
|
|
419
|
+
// `budgetTokens` replaces it only when the caller itself declared an
|
|
420
|
+
// `enabled` request -- the guard reads the caller's raw `type`, not the
|
|
421
|
+
// resolved one, so an `adaptive` request downgraded to `enabled` on a
|
|
422
|
+
// model without adaptive thinking ignores its budget. The result is
|
|
423
|
+
// clamped to the emitted `max_tokens` minus one, and the floor of 1024 is
|
|
424
|
+
// applied after that clamp, so the floor wins when the two conflict.
|
|
425
|
+
//
|
|
426
|
+
// No older analysis document in this repository transcribes this
|
|
427
|
+
// computation at all, so the floor and the type guard are evidenced for
|
|
428
|
+
// 2.1.280 only. They are applied to every profile as one shared
|
|
429
|
+
// behaviour -- the same deliberate choice already made for the model-id
|
|
430
|
+
// normalizer ladder -- and nothing here asserts whether older clients
|
|
431
|
+
// had them.
|
|
432
|
+
//
|
|
433
|
+
// This is the one wire-visible consumer of the request-derived bound: on
|
|
434
|
+
// a 2.1.222+ profile a caller asking for a `max_tokens` above the
|
|
435
|
+
// catalogue's upper limit seeds the default budget from THEIR number
|
|
436
|
+
// minus one, not from the catalogue's.
|
|
437
|
+
const callerBudget =
|
|
438
|
+
request?.type === "enabled" ? request.budgetTokens : undefined;
|
|
439
|
+
const requested =
|
|
440
|
+
callerBudget ??
|
|
441
|
+
modelOutputTokenLimits(normalizedId, profile, maxTokens).upperLimit - 1;
|
|
442
|
+
emitted = {
|
|
443
|
+
budget_tokens: Math.max(1024, Math.min(maxTokens - 1, requested)),
|
|
444
|
+
};
|
|
445
|
+
emitted["type"] = "enabled";
|
|
446
|
+
if (display !== undefined) emitted["display"] = display;
|
|
447
|
+
// Same in-place assignment as the adaptive arm: order stays
|
|
448
|
+
// `budget_tokens, type, display`.
|
|
449
|
+
if (displayOverride !== undefined) emitted["display"] = displayOverride;
|
|
450
|
+
} else if (resolvedType === "disabled") {
|
|
331
451
|
emitted = { type: "disabled" };
|
|
332
452
|
}
|
|
333
453
|
|
|
334
454
|
// Upstream `Jr = Xn?.type === "enabled" || Xn?.type === "adaptive"
|
|
335
455
|
// || Xn === void 0 && U4e(u)`.
|
|
456
|
+
// Upstream uses this to demote `tool_choice` of type `tool`; the
|
|
457
|
+
// request body also demotes type `any` on it, a documented divergence.
|
|
336
458
|
const extendedThinkingActive =
|
|
337
459
|
emitted?.["type"] === "enabled" ||
|
|
338
460
|
emitted?.["type"] === "adaptive" ||
|