@tormentalabs/claude-code-wire-compat 0.1.0-rc.16 → 0.1.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 +132 -1
- package/README.md +53 -1
- package/dist/build-request.d.ts.map +1 -1
- package/dist/build-request.js +164 -26
- package/dist/build-request.js.map +1 -1
- package/dist/contracts.d.ts +103 -1
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/redaction.d.ts +11 -0
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +11 -0
- package/dist/redaction.js.map +1 -1
- package/dist/request-body.d.ts.map +1 -1
- package/dist/request-body.js +74 -19
- package/dist/request-body.js.map +1 -1
- package/dist/system-prompt.d.ts +12 -5
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +29 -13
- package/dist/system-prompt.js.map +1 -1
- package/package.json +2 -1
- package/src/anti-verbosity.ts +219 -0
- package/src/beta-registry.ts +140 -0
- package/src/betas.ts +219 -0
- package/src/build-request.ts +1655 -0
- package/src/contracts.ts +1233 -0
- package/src/count-tokens.ts +84 -0
- package/src/fingerprint.ts +85 -0
- package/src/headers.ts +442 -0
- package/src/index.ts +62 -0
- package/src/metadata.ts +331 -0
- package/src/model-capabilities.ts +295 -0
- package/src/model-identity.ts +45 -0
- package/src/models.ts +46 -0
- package/src/profiles/claude-code-2.1.195.ts +154 -0
- package/src/redaction.ts +521 -0
- package/src/request-body.ts +1924 -0
- package/src/sha256.ts +114 -0
- package/src/system-prompt.ts +222 -0
- package/src/thinking.ts +266 -0
- package/src/unicode.ts +24 -0
package/src/metadata.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
import { ClaudeCodeWireError } from "./contracts.js";
|
|
4
|
+
import type {
|
|
5
|
+
ClaudeCodeMetadataOverrides,
|
|
6
|
+
ClaudeCodeRuntimeIdentity,
|
|
7
|
+
JsonValue,
|
|
8
|
+
} from "./contracts.js";
|
|
9
|
+
import { inspectJsonInputs, validatedJson } from "./request-body.js";
|
|
10
|
+
import { classifySurrogateAt } from "./unicode.js";
|
|
11
|
+
|
|
12
|
+
const MAX_TEXT_LENGTH = 8_192;
|
|
13
|
+
const FORBIDDEN_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
14
|
+
const CORRELATION_KEYS = new Set([
|
|
15
|
+
"user_id",
|
|
16
|
+
"device_id",
|
|
17
|
+
"account_uuid",
|
|
18
|
+
"session_id",
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* The members of the derived `user_id` JSON object.
|
|
22
|
+
*
|
|
23
|
+
* `metadataOverrides.userIdFields` may not carry these keys: the package writes
|
|
24
|
+
* them last so correlation always wins, and a silently overwritten caller value
|
|
25
|
+
* would look honoured while being discarded.
|
|
26
|
+
*/
|
|
27
|
+
const IDENTITY_KEYS = new Set(["device_id", "account_uuid", "session_id"]);
|
|
28
|
+
|
|
29
|
+
function hasInvalidUtf16(value: string): boolean {
|
|
30
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
31
|
+
const classification = classifySurrogateAt(value, index);
|
|
32
|
+
if (classification === "loneSurrogate") return true;
|
|
33
|
+
if (classification === "surrogatePair") index += 1;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasControlCharacter(value: string): boolean {
|
|
39
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
40
|
+
const codeUnit = value.charCodeAt(index);
|
|
41
|
+
if (codeUnit <= 0x1f || codeUnit === 0x7f) return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ownDataValue(value: object, key: string): unknown {
|
|
47
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
48
|
+
return descriptor === undefined || !("value" in descriptor)
|
|
49
|
+
? undefined
|
|
50
|
+
: descriptor.value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function validateIdentityText(
|
|
54
|
+
value: unknown,
|
|
55
|
+
field: keyof ClaudeCodeRuntimeIdentity,
|
|
56
|
+
): string {
|
|
57
|
+
if (typeof value !== "string") {
|
|
58
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY", { field });
|
|
59
|
+
}
|
|
60
|
+
if (hasInvalidUtf16(value)) {
|
|
61
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field });
|
|
62
|
+
}
|
|
63
|
+
if (
|
|
64
|
+
value.length === 0 ||
|
|
65
|
+
value.length > MAX_TEXT_LENGTH ||
|
|
66
|
+
value.trim().length === 0 ||
|
|
67
|
+
hasControlCharacter(value)
|
|
68
|
+
) {
|
|
69
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY", { field });
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function validateIdentityObject(identity: unknown): object {
|
|
75
|
+
if (
|
|
76
|
+
typeof identity !== "object" ||
|
|
77
|
+
identity === null ||
|
|
78
|
+
Array.isArray(identity)
|
|
79
|
+
) {
|
|
80
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY");
|
|
81
|
+
}
|
|
82
|
+
for (const key of Reflect.ownKeys(identity)) {
|
|
83
|
+
if (typeof key !== "string" || FORBIDDEN_KEYS.has(key)) {
|
|
84
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return identity;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function validateRuntimeIdentity(
|
|
91
|
+
identity: unknown,
|
|
92
|
+
): ClaudeCodeRuntimeIdentity {
|
|
93
|
+
const source = validateIdentityObject(identity);
|
|
94
|
+
const sessionId = validateIdentityText(
|
|
95
|
+
ownDataValue(source, "sessionId"),
|
|
96
|
+
"sessionId",
|
|
97
|
+
);
|
|
98
|
+
const deviceId = validateIdentityText(
|
|
99
|
+
ownDataValue(source, "deviceId"),
|
|
100
|
+
"deviceId",
|
|
101
|
+
);
|
|
102
|
+
const accountUuid = validateIdentityText(
|
|
103
|
+
ownDataValue(source, "accountUuid"),
|
|
104
|
+
"accountUuid",
|
|
105
|
+
);
|
|
106
|
+
const runtime = validateIdentityText(
|
|
107
|
+
ownDataValue(source, "runtime"),
|
|
108
|
+
"runtime",
|
|
109
|
+
);
|
|
110
|
+
const runtimeVersion = validateIdentityText(
|
|
111
|
+
ownDataValue(source, "runtimeVersion"),
|
|
112
|
+
"runtimeVersion",
|
|
113
|
+
);
|
|
114
|
+
const os = validateIdentityText(ownDataValue(source, "os"), "os");
|
|
115
|
+
const arch = validateIdentityText(ownDataValue(source, "arch"), "arch");
|
|
116
|
+
|
|
117
|
+
if (runtime !== "node" && runtime !== "bun" && runtime !== "workerd") {
|
|
118
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY", { field: "runtime" });
|
|
119
|
+
}
|
|
120
|
+
if (os !== "Windows" && os !== "Linux" && os !== "macOS") {
|
|
121
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY", { field: "os" });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
sessionId,
|
|
126
|
+
deviceId,
|
|
127
|
+
accountUuid,
|
|
128
|
+
runtime,
|
|
129
|
+
runtimeVersion,
|
|
130
|
+
os,
|
|
131
|
+
arch,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function validateMetadataKey(key: PropertyKey): string {
|
|
136
|
+
if (typeof key !== "string" || FORBIDDEN_KEYS.has(key)) {
|
|
137
|
+
throw new ClaudeCodeWireError("INVALID_INPUT");
|
|
138
|
+
}
|
|
139
|
+
if (hasInvalidUtf16(key)) {
|
|
140
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: "metadataKey" });
|
|
141
|
+
}
|
|
142
|
+
if (key.length > MAX_TEXT_LENGTH) {
|
|
143
|
+
throw new ClaudeCodeWireError("INPUT_TOO_LARGE", {
|
|
144
|
+
field: "metadataKey",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (key.length === 0 || hasControlCharacter(key)) {
|
|
148
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "metadataKey" });
|
|
149
|
+
}
|
|
150
|
+
return key;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function validateMetadataValue(value: unknown, key: string): JsonValue {
|
|
154
|
+
if (typeof value === "string") {
|
|
155
|
+
if (hasInvalidUtf16(value)) {
|
|
156
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: key });
|
|
157
|
+
}
|
|
158
|
+
if (value.length > MAX_TEXT_LENGTH) {
|
|
159
|
+
throw new ClaudeCodeWireError("INPUT_TOO_LARGE", { field: key });
|
|
160
|
+
}
|
|
161
|
+
if (hasControlCharacter(value)) {
|
|
162
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: key });
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
if (typeof value === "number") {
|
|
167
|
+
if (!Number.isFinite(value)) {
|
|
168
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: key });
|
|
169
|
+
}
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
if (typeof value === "boolean" || value === null) return value;
|
|
173
|
+
if (typeof value !== "object") {
|
|
174
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: key });
|
|
175
|
+
}
|
|
176
|
+
inspectJsonInputs([value], (nestedString) => {
|
|
177
|
+
if (hasInvalidUtf16(nestedString)) {
|
|
178
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: key });
|
|
179
|
+
}
|
|
180
|
+
if (nestedString.length > MAX_TEXT_LENGTH) {
|
|
181
|
+
throw new ClaudeCodeWireError("INPUT_TOO_LARGE", { field: key });
|
|
182
|
+
}
|
|
183
|
+
if (hasControlCharacter(nestedString)) {
|
|
184
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: key });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return validatedJson(value);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function expectedCorrelationValue(
|
|
191
|
+
key: string,
|
|
192
|
+
identity: ClaudeCodeRuntimeIdentity,
|
|
193
|
+
userId: string,
|
|
194
|
+
): string | undefined {
|
|
195
|
+
switch (key) {
|
|
196
|
+
case "user_id":
|
|
197
|
+
return userId;
|
|
198
|
+
case "device_id":
|
|
199
|
+
return identity.deviceId;
|
|
200
|
+
case "account_uuid":
|
|
201
|
+
return identity.accountUuid;
|
|
202
|
+
case "session_id":
|
|
203
|
+
return identity.sessionId;
|
|
204
|
+
default:
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function validateMetadataObject(metadata: unknown): object {
|
|
210
|
+
if (
|
|
211
|
+
typeof metadata !== "object" ||
|
|
212
|
+
metadata === null ||
|
|
213
|
+
Array.isArray(metadata)
|
|
214
|
+
) {
|
|
215
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "metadata" });
|
|
216
|
+
}
|
|
217
|
+
return metadata;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Validates a package-extension `user_id` replacement.
|
|
222
|
+
*
|
|
223
|
+
* PACKAGE EXTENSION, not observed Claude Code behaviour. The value is opaque to
|
|
224
|
+
* this package, so it is checked only for wire-safety, never for correlation.
|
|
225
|
+
*/
|
|
226
|
+
function validateOverrideUserId(value: unknown): string {
|
|
227
|
+
if (typeof value !== "string") {
|
|
228
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "userId" });
|
|
229
|
+
}
|
|
230
|
+
if (hasInvalidUtf16(value)) {
|
|
231
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: "userId" });
|
|
232
|
+
}
|
|
233
|
+
if (value.length > MAX_TEXT_LENGTH) {
|
|
234
|
+
throw new ClaudeCodeWireError("INPUT_TOO_LARGE", { field: "userId" });
|
|
235
|
+
}
|
|
236
|
+
if (hasControlCharacter(value)) {
|
|
237
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: "userId" });
|
|
238
|
+
}
|
|
239
|
+
if (value.length === 0 || value.trim().length === 0) {
|
|
240
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "userId" });
|
|
241
|
+
}
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Validates the package-extension members merged into the `user_id` object. */
|
|
246
|
+
function validateUserIdFields(value: unknown): [string, JsonValue][] {
|
|
247
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
248
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "userIdFields" });
|
|
249
|
+
}
|
|
250
|
+
const entries: [string, JsonValue][] = [];
|
|
251
|
+
for (const rawKey of Reflect.ownKeys(value)) {
|
|
252
|
+
const key = validateMetadataKey(rawKey);
|
|
253
|
+
if (IDENTITY_KEYS.has(key)) {
|
|
254
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: key });
|
|
255
|
+
}
|
|
256
|
+
entries.push([key, validateMetadataValue(ownDataValue(value, key), key)]);
|
|
257
|
+
}
|
|
258
|
+
return entries;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Resolves the emitted `user_id`, applying the package-extension seam.
|
|
263
|
+
*
|
|
264
|
+
* With no overrides this is the derived correlation triple, byte for byte as
|
|
265
|
+
* before the seam existed. The two override members are mutually exclusive
|
|
266
|
+
* because they express opposite intents: one abandons the derived value, the
|
|
267
|
+
* other extends it.
|
|
268
|
+
*/
|
|
269
|
+
function resolveUserId(
|
|
270
|
+
identity: ClaudeCodeRuntimeIdentity,
|
|
271
|
+
overrides: unknown,
|
|
272
|
+
): string {
|
|
273
|
+
const correlation = {
|
|
274
|
+
device_id: identity.deviceId,
|
|
275
|
+
account_uuid: identity.accountUuid,
|
|
276
|
+
session_id: identity.sessionId,
|
|
277
|
+
};
|
|
278
|
+
if (overrides === undefined) return JSON.stringify(correlation);
|
|
279
|
+
if (
|
|
280
|
+
typeof overrides !== "object" ||
|
|
281
|
+
overrides === null ||
|
|
282
|
+
Array.isArray(overrides)
|
|
283
|
+
) {
|
|
284
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", {
|
|
285
|
+
field: "metadataOverrides",
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
const hasUserId = Object.hasOwn(overrides, "userId");
|
|
289
|
+
const hasUserIdFields = Object.hasOwn(overrides, "userIdFields");
|
|
290
|
+
if (hasUserId && hasUserIdFields) {
|
|
291
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", {
|
|
292
|
+
field: "metadataOverrides",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if (hasUserId) {
|
|
296
|
+
return validateOverrideUserId(ownDataValue(overrides, "userId"));
|
|
297
|
+
}
|
|
298
|
+
if (!hasUserIdFields) return JSON.stringify(correlation);
|
|
299
|
+
const fields = validateUserIdFields(ownDataValue(overrides, "userIdFields"));
|
|
300
|
+
// Caller members first, correlation last: the triple always wins, which
|
|
301
|
+
// mirrors how the consumer composes the same object.
|
|
302
|
+
return JSON.stringify({ ...Object.fromEntries(fields), ...correlation });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function buildCorrelatedMetadata(
|
|
306
|
+
identity: ClaudeCodeRuntimeIdentity,
|
|
307
|
+
suppliedMetadata?: Readonly<Record<string, JsonValue>>,
|
|
308
|
+
overrides?: ClaudeCodeMetadataOverrides,
|
|
309
|
+
): Readonly<Record<string, JsonValue>> {
|
|
310
|
+
const validatedIdentity = validateRuntimeIdentity(identity);
|
|
311
|
+
const userId = resolveUserId(validatedIdentity, overrides);
|
|
312
|
+
const entries: [string, JsonValue][] = [["user_id", userId]];
|
|
313
|
+
|
|
314
|
+
if (suppliedMetadata === undefined) return Object.freeze({ user_id: userId });
|
|
315
|
+
const metadataObject = validateMetadataObject(suppliedMetadata);
|
|
316
|
+
|
|
317
|
+
for (const rawKey of Reflect.ownKeys(metadataObject)) {
|
|
318
|
+
const key = validateMetadataKey(rawKey);
|
|
319
|
+
const value = validateMetadataValue(ownDataValue(metadataObject, key), key);
|
|
320
|
+
if (CORRELATION_KEYS.has(key)) {
|
|
321
|
+
const expected = expectedCorrelationValue(key, validatedIdentity, userId);
|
|
322
|
+
if (value !== expected) {
|
|
323
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: key });
|
|
324
|
+
}
|
|
325
|
+
if (key === "user_id") continue;
|
|
326
|
+
}
|
|
327
|
+
entries.push([key, value]);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return Object.freeze(Object.fromEntries(entries));
|
|
331
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
import type { ClaudeCodeCapabilities } from "./contracts.js";
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Capability derivation, ported from the genuine client's nine capability
|
|
7
|
+
* predicates.
|
|
8
|
+
*
|
|
9
|
+
* THE LOAD-BEARING FACT, stated up front because it is surprising:
|
|
10
|
+
*
|
|
11
|
+
* On the first-party provider -- the only provider this package targets --
|
|
12
|
+
* every one of these nine predicates reduces to a pure function of the
|
|
13
|
+
* normalized model id. The catalogue's `capabilities` string array does not
|
|
14
|
+
* participate in capability derivation at all.
|
|
15
|
+
*
|
|
16
|
+
* Why. Upstream, each predicate has the shape
|
|
17
|
+
*
|
|
18
|
+
* let override = W9(model, cap); // env-var capability override
|
|
19
|
+
* if (override !== undefined) return override;
|
|
20
|
+
* let id = mo(model); // normalize
|
|
21
|
+
* if (<exclusion list>) return false;
|
|
22
|
+
* if (JB(id, cap) || id === "claude-mythos-5") return true;
|
|
23
|
+
* return ZO(l_(model)); // provider fallback
|
|
24
|
+
*
|
|
25
|
+
* and in this package:
|
|
26
|
+
*
|
|
27
|
+
* - `W9` opens with `if (td()) return;` and `td()` is true for first party,
|
|
28
|
+
* so the override always yields `undefined`. It also reads environment
|
|
29
|
+
* variables, which this package is designed never to do. Guard dropped.
|
|
30
|
+
* - `ZO(...)` is `provider is firstParty|anthropicAws|foundry|mantle`. The
|
|
31
|
+
* profile pins `provider: "anthropic"`, so `ZO(...)` is unconditionally
|
|
32
|
+
* true and `return ZO(l_(e))` becomes `return true`.
|
|
33
|
+
* - Therefore the `JB(id, cap)` catalogue-membership test and the
|
|
34
|
+
* `claude-mythos-5` special case can only return `true` from a position
|
|
35
|
+
* where the fallback already returns `true`. They are unobservable.
|
|
36
|
+
* - `l_(e) === "foundry"` branches are unreachable for the same reason.
|
|
37
|
+
* - `ut(CLAUDE_CODE_ALWAYS_ENABLE_EFFORT)` reads an environment variable;
|
|
38
|
+
* dropped by the same design rule as `W9`.
|
|
39
|
+
*
|
|
40
|
+
* Only the leading exclusion list is observable, so that is all these
|
|
41
|
+
* functions contain.
|
|
42
|
+
*
|
|
43
|
+
* WARNING TO FUTURE READERS. Two things follow that look like bugs and are not:
|
|
44
|
+
*
|
|
45
|
+
* 1. No membership test was lost. Do NOT "restore" a `JB`-equivalent check
|
|
46
|
+
* here. Adding one back cannot change any result, but it reintroduces
|
|
47
|
+
* dead branches that cannot be covered or mutation-killed.
|
|
48
|
+
* 2. The catalogue `capabilities` arrays are retained in the profile as
|
|
49
|
+
* faithful transcribed evidence AND are consumed elsewhere --
|
|
50
|
+
* `mid_conv_system`, `lean_prompt` and `fast_mode` are read by later
|
|
51
|
+
* work packages. Do NOT delete them because this module ignores them.
|
|
52
|
+
*
|
|
53
|
+
* `claude-mythos-5` has no catalogue entry by product decision D-1. Upstream
|
|
54
|
+
* special-cases it by name in `Kw`, `Hke`, `Yte` and `Uot`; this port subsumes
|
|
55
|
+
* those clauses into the first-party fallback, which yields an identical
|
|
56
|
+
* result. The explicit D-1 test asserting its full nine-boolean row is the
|
|
57
|
+
* guard for that equivalence.
|
|
58
|
+
*
|
|
59
|
+
* Model ids reaching these functions have already been normalized by
|
|
60
|
+
* `resolveModel` via `normalizeModelId`.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Upstream `Kw` at byte offset 227719902.
|
|
65
|
+
*
|
|
66
|
+
* Elided: the `W9` override, `CLAUDE_CODE_ALWAYS_ENABLE_EFFORT`, the
|
|
67
|
+
* `JB(id, "effort") || id === "claude-mythos-5"` test, and the `ZO` fallback.
|
|
68
|
+
*/
|
|
69
|
+
export function supportsEffort(normalizedId: string): boolean {
|
|
70
|
+
if (
|
|
71
|
+
normalizedId.includes("claude-3-") ||
|
|
72
|
+
normalizedId === "claude-opus-4-0" ||
|
|
73
|
+
normalizedId === "claude-opus-4-1" ||
|
|
74
|
+
normalizedId === "claude-sonnet-4-0" ||
|
|
75
|
+
normalizedId === "claude-sonnet-4-5" ||
|
|
76
|
+
normalizedId === "claude-haiku-4-5"
|
|
77
|
+
) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Upstream `Hke` at byte offset 227720257.
|
|
85
|
+
*
|
|
86
|
+
* Its exclusion list is `Kw`'s plus `claude-opus-4-5`. Elided: the `W9`
|
|
87
|
+
* override, the `JB(id, "max_effort") || id === "claude-mythos-5"` test, and
|
|
88
|
+
* the `ZO` fallback.
|
|
89
|
+
*/
|
|
90
|
+
export function supportsMaxEffort(normalizedId: string): boolean {
|
|
91
|
+
if (
|
|
92
|
+
normalizedId.includes("claude-3-") ||
|
|
93
|
+
normalizedId === "claude-opus-4-0" ||
|
|
94
|
+
normalizedId === "claude-opus-4-1" ||
|
|
95
|
+
normalizedId === "claude-opus-4-5" ||
|
|
96
|
+
normalizedId === "claude-sonnet-4-0" ||
|
|
97
|
+
normalizedId === "claude-sonnet-4-5" ||
|
|
98
|
+
normalizedId === "claude-haiku-4-5"
|
|
99
|
+
) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Upstream `Yte` at byte offset 227720583.
|
|
107
|
+
*
|
|
108
|
+
* Its exclusion list is `Hke`'s plus `claude-opus-4-6` and
|
|
109
|
+
* `claude-sonnet-4-6`. Elided: the `W9` override, the
|
|
110
|
+
* `JB(id, "xhigh_effort") || id === "claude-mythos-5"` test, and the `ZO`
|
|
111
|
+
* fallback.
|
|
112
|
+
*/
|
|
113
|
+
export function supportsXhighEffort(normalizedId: string): boolean {
|
|
114
|
+
if (
|
|
115
|
+
normalizedId.includes("claude-3-") ||
|
|
116
|
+
normalizedId === "claude-opus-4-0" ||
|
|
117
|
+
normalizedId === "claude-opus-4-1" ||
|
|
118
|
+
normalizedId === "claude-opus-4-5" ||
|
|
119
|
+
normalizedId === "claude-opus-4-6" ||
|
|
120
|
+
normalizedId === "claude-sonnet-4-0" ||
|
|
121
|
+
normalizedId === "claude-sonnet-4-5" ||
|
|
122
|
+
normalizedId === "claude-sonnet-4-6" ||
|
|
123
|
+
normalizedId === "claude-haiku-4-5"
|
|
124
|
+
) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Upstream `Uot` at byte offset 227383245.
|
|
132
|
+
*
|
|
133
|
+
* Its exclusion list is `Kw`'s plus `claude-opus-4-5`, matching `Hke`'s today.
|
|
134
|
+
* The two are transcribed separately on purpose: they are independent upstream
|
|
135
|
+
* predicates that happen to agree at this client version. Elided: the `W9`
|
|
136
|
+
* override, the `JB(id, "adaptive_thinking") || id === "claude-mythos-5"`
|
|
137
|
+
* test, and the `ZO` fallback.
|
|
138
|
+
*/
|
|
139
|
+
export function supportsAdaptiveThinking(normalizedId: string): boolean {
|
|
140
|
+
if (
|
|
141
|
+
normalizedId.includes("claude-3-") ||
|
|
142
|
+
normalizedId === "claude-opus-4-0" ||
|
|
143
|
+
normalizedId === "claude-opus-4-1" ||
|
|
144
|
+
normalizedId === "claude-opus-4-5" ||
|
|
145
|
+
normalizedId === "claude-sonnet-4-0" ||
|
|
146
|
+
normalizedId === "claude-sonnet-4-5" ||
|
|
147
|
+
normalizedId === "claude-haiku-4-5"
|
|
148
|
+
) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Upstream `D9r` at byte offset 227382784.
|
|
156
|
+
*
|
|
157
|
+
* Reduces to the same body as `supportsInterleavedThinking` and
|
|
158
|
+
* `supportsContextManagement` at this client version. Kept separate on
|
|
159
|
+
* purpose: three independent upstream predicates with different provenance
|
|
160
|
+
* that are versioned independently upstream. Do not merge them.
|
|
161
|
+
*
|
|
162
|
+
* Elided: the `W9` override.
|
|
163
|
+
*/
|
|
164
|
+
export function supportsThinking(normalizedId: string): boolean {
|
|
165
|
+
return !normalizedId.includes("claude-3-");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Upstream `QOt` at byte offset 227384610.
|
|
170
|
+
*
|
|
171
|
+
* See the note on `supportsThinking` about the three identical bodies.
|
|
172
|
+
* Elided: the `W9` override, the unreachable `foundry` branch, and the
|
|
173
|
+
* non-`ZO` provider tail (`claude-haiku-4-5` is excluded only for providers
|
|
174
|
+
* outside `ZO`, which cannot occur here -- note this is why haiku 4.5 has
|
|
175
|
+
* interleaved thinking on first party but not on, say, vertex).
|
|
176
|
+
*/
|
|
177
|
+
export function supportsInterleavedThinking(normalizedId: string): boolean {
|
|
178
|
+
return !normalizedId.includes("claude-3-");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Upstream `n0d` at byte offset 227385143.
|
|
183
|
+
*
|
|
184
|
+
* See the note on `supportsThinking` about the three identical bodies.
|
|
185
|
+
* Elided: the unreachable `foundry` branch and the non-`ZO` tail
|
|
186
|
+
* `JB(id, "context_management") || id === "claude-mythos-5"`. `n0d` has no
|
|
187
|
+
* `W9` override upstream.
|
|
188
|
+
*/
|
|
189
|
+
export function supportsContextManagement(normalizedId: string): boolean {
|
|
190
|
+
return !normalizedId.includes("claude-3-");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Upstream `j4e` at byte offset 227385302.
|
|
195
|
+
*
|
|
196
|
+
* Elided: the unconditionally true `ZO` provider gate.
|
|
197
|
+
* This beta-only gate is intentionally absent from `ClaudeCodeCapabilities`.
|
|
198
|
+
*/
|
|
199
|
+
export function supportsStructuredOutputs(normalizedId: string): boolean {
|
|
200
|
+
return !(
|
|
201
|
+
normalizedId.includes("claude-3-") ||
|
|
202
|
+
normalizedId === "claude-opus-4-0" ||
|
|
203
|
+
normalizedId === "claude-sonnet-4-0"
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Upstream `RCn` at byte offset 227387413.
|
|
209
|
+
*
|
|
210
|
+
* Elided: host-state gates, the `W9` override, the unobservable `JB`/mythos
|
|
211
|
+
* clause, and the unconditionally true `ZO` fallback. This exclusion list
|
|
212
|
+
* differs from `rejectsDisabledThinking` by one member: that predicate also
|
|
213
|
+
* excludes `claude-opus-4-8`. Do not merge them.
|
|
214
|
+
* This beta-only gate is intentionally absent from `ClaudeCodeCapabilities`.
|
|
215
|
+
*/
|
|
216
|
+
export function supportsMidConversationSystem(normalizedId: string): boolean {
|
|
217
|
+
return !(
|
|
218
|
+
normalizedId.includes("claude-3-") ||
|
|
219
|
+
normalizedId === "claude-opus-4-0" ||
|
|
220
|
+
normalizedId === "claude-opus-4-1" ||
|
|
221
|
+
normalizedId === "claude-opus-4-5" ||
|
|
222
|
+
normalizedId === "claude-opus-4-6" ||
|
|
223
|
+
normalizedId === "claude-opus-4-7" ||
|
|
224
|
+
normalizedId === "claude-sonnet-4-0" ||
|
|
225
|
+
normalizedId === "claude-sonnet-4-5" ||
|
|
226
|
+
normalizedId === "claude-sonnet-4-6" ||
|
|
227
|
+
normalizedId === "claude-haiku-4-5"
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Upstream `LCn` at byte offset 227385451.
|
|
233
|
+
*
|
|
234
|
+
* INVERTED POLARITY relative to every other predicate in this module: this
|
|
235
|
+
* list is an ALLOWLIST. Membership means temperature IS supported. Do not
|
|
236
|
+
* refactor it into the shared exclusion-list shape.
|
|
237
|
+
*
|
|
238
|
+
* Elided: the `W9` override.
|
|
239
|
+
*/
|
|
240
|
+
export function supportsTemperature(normalizedId: string): boolean {
|
|
241
|
+
return (
|
|
242
|
+
normalizedId.includes("claude-3-") ||
|
|
243
|
+
normalizedId === "claude-opus-4-0" ||
|
|
244
|
+
normalizedId === "claude-opus-4-1" ||
|
|
245
|
+
normalizedId === "claude-opus-4-5" ||
|
|
246
|
+
normalizedId === "claude-opus-4-6" ||
|
|
247
|
+
normalizedId === "claude-sonnet-4-0" ||
|
|
248
|
+
normalizedId === "claude-sonnet-4-5" ||
|
|
249
|
+
normalizedId === "claude-sonnet-4-6" ||
|
|
250
|
+
normalizedId === "claude-haiku-4-5"
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Upstream `U4e` at byte offset 227382881.
|
|
256
|
+
*
|
|
257
|
+
* Its exclusion list is the widest of the five: every catalogue model except
|
|
258
|
+
* `claude-fable-5`. It has no `W9` override and no `claude-mythos-5` clause
|
|
259
|
+
* upstream. Elided: the `JB(id, "rejects_disabled_thinking")` test and the
|
|
260
|
+
* `ZO` fallback.
|
|
261
|
+
*/
|
|
262
|
+
export function rejectsDisabledThinking(normalizedId: string): boolean {
|
|
263
|
+
if (
|
|
264
|
+
normalizedId.includes("claude-3-") ||
|
|
265
|
+
normalizedId === "claude-opus-4-0" ||
|
|
266
|
+
normalizedId === "claude-opus-4-1" ||
|
|
267
|
+
normalizedId === "claude-opus-4-5" ||
|
|
268
|
+
normalizedId === "claude-opus-4-6" ||
|
|
269
|
+
normalizedId === "claude-opus-4-7" ||
|
|
270
|
+
normalizedId === "claude-opus-4-8" ||
|
|
271
|
+
normalizedId === "claude-sonnet-4-0" ||
|
|
272
|
+
normalizedId === "claude-sonnet-4-5" ||
|
|
273
|
+
normalizedId === "claude-sonnet-4-6" ||
|
|
274
|
+
normalizedId === "claude-haiku-4-5"
|
|
275
|
+
) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function deriveCapabilities(
|
|
282
|
+
normalizedId: string,
|
|
283
|
+
): ClaudeCodeCapabilities {
|
|
284
|
+
return Object.freeze({
|
|
285
|
+
thinking: supportsThinking(normalizedId),
|
|
286
|
+
adaptiveThinking: supportsAdaptiveThinking(normalizedId),
|
|
287
|
+
interleavedThinking: supportsInterleavedThinking(normalizedId),
|
|
288
|
+
effort: supportsEffort(normalizedId),
|
|
289
|
+
maxEffort: supportsMaxEffort(normalizedId),
|
|
290
|
+
xhighEffort: supportsXhighEffort(normalizedId),
|
|
291
|
+
contextManagement: supportsContextManagement(normalizedId),
|
|
292
|
+
temperature: supportsTemperature(normalizedId),
|
|
293
|
+
rejectsDisabledThinking: rejectsDisabledThinking(normalizedId),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
import type { ClaudeCodeModelFamily } from "./contracts.js";
|
|
4
|
+
|
|
5
|
+
/** Ports upstream `dp` (binary offset 226644497). */
|
|
6
|
+
export function stripModelMarkers(model: string): string {
|
|
7
|
+
return model.replace(/\[(1|2)m\]/gi, "");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Ports upstream `$_` (binary offset 226639025). */
|
|
11
|
+
export function normalizeModelId(model: string): string {
|
|
12
|
+
model = model.toLowerCase();
|
|
13
|
+
if (model.includes("claude-fable-5")) return "claude-fable-5";
|
|
14
|
+
if (model.includes("claude-mythos-5")) return "claude-mythos-5";
|
|
15
|
+
if (model.includes("claude-opus-4-8")) return "claude-opus-4-8";
|
|
16
|
+
if (model.includes("claude-opus-4-7")) return "claude-opus-4-7";
|
|
17
|
+
if (model.includes("claude-opus-4-6")) return "claude-opus-4-6";
|
|
18
|
+
if (model.includes("claude-opus-4-5")) return "claude-opus-4-5";
|
|
19
|
+
if (model.includes("claude-opus-4-1")) return "claude-opus-4-1";
|
|
20
|
+
if (/claude-opus-4(?!-\d(?!\d))/.test(model)) return "claude-opus-4-0";
|
|
21
|
+
if (model.includes("claude-sonnet-4-6")) return "claude-sonnet-4-6";
|
|
22
|
+
if (model.includes("claude-sonnet-4-5")) return "claude-sonnet-4-5";
|
|
23
|
+
if (/claude-sonnet-4(?!-\d(?!\d))/.test(model)) return "claude-sonnet-4-0";
|
|
24
|
+
if (model.includes("claude-haiku-4-5")) return "claude-haiku-4-5";
|
|
25
|
+
if (model.includes("claude-3-7-sonnet")) return "claude-3-7-sonnet";
|
|
26
|
+
if (model.includes("claude-3-5-sonnet")) return "claude-3-5-sonnet";
|
|
27
|
+
if (model.includes("claude-3-5-haiku")) return "claude-3-5-haiku";
|
|
28
|
+
if (model.includes("claude-3-opus")) return "claude-3-opus";
|
|
29
|
+
if (model.includes("claude-3-sonnet")) return "claude-3-sonnet";
|
|
30
|
+
if (model.includes("claude-3-haiku")) return "claude-3-haiku";
|
|
31
|
+
return model.replace(/-\d{8}$/, "");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Derives the package-local family classification used only by
|
|
36
|
+
* `RedactedRequestEvidence`; upstream has no corresponding family concept.
|
|
37
|
+
*/
|
|
38
|
+
export function modelFamilyOf(normalizedId: string): ClaudeCodeModelFamily {
|
|
39
|
+
if (normalizedId.includes("fable")) return "fable";
|
|
40
|
+
if (normalizedId.includes("mythos")) return "mythos";
|
|
41
|
+
if (normalizedId.includes("haiku")) return "haiku";
|
|
42
|
+
if (normalizedId.includes("sonnet")) return "sonnet";
|
|
43
|
+
if (normalizedId.includes("opus")) return "opus";
|
|
44
|
+
return "unknown";
|
|
45
|
+
}
|