@tormentalabs/claude-code-wire-compat 0.4.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 +217 -0
- package/README.md +51 -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 +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -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 +33 -2
- package/dist/model-identity.js.map +1 -1
- package/dist/model-queries.d.ts +89 -0
- package/dist/model-queries.d.ts.map +1 -0
- package/dist/model-queries.js +232 -0
- package/dist/model-queries.js.map +1 -0
- 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 -3
- 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 +28 -3
- package/src/model-capabilities.ts +55 -19
- package/src/model-identity.ts +29 -2
- package/src/model-queries.ts +273 -0
- 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
package/src/build-request.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import { resolveModel } from "./models.js";
|
|
31
31
|
import { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
32
32
|
import { CLAUDE_CODE_2_1_233_PROFILE } from "./profiles/claude-code-2.1.233.js";
|
|
33
|
+
import { CLAUDE_CODE_2_1_280_PROFILE } from "./profiles/claude-code-2.1.280.js";
|
|
33
34
|
import type { NormalizedRequestInput } from "./redaction.js";
|
|
34
35
|
import { buildRedactedEvidence, toSafeErrorDetails } from "./redaction.js";
|
|
35
36
|
import {
|
|
@@ -38,7 +39,7 @@ import {
|
|
|
38
39
|
} from "./request-body.js";
|
|
39
40
|
import { sha256Hex } from "./sha256.js";
|
|
40
41
|
import { buildCanonicalSystem, IDENTITY_TEXT } from "./system-prompt.js";
|
|
41
|
-
import { isThinkingDisplayActive } from "./thinking.js";
|
|
42
|
+
import { isThinkingActive, isThinkingDisplayActive } from "./thinking.js";
|
|
42
43
|
import { classifySurrogateAt } from "./unicode.js";
|
|
43
44
|
|
|
44
45
|
const METHOD = "POST";
|
|
@@ -152,6 +153,8 @@ const CAPABILITY_KEYS = [
|
|
|
152
153
|
"contextManagement",
|
|
153
154
|
"temperature",
|
|
154
155
|
"rejectsDisabledThinking",
|
|
156
|
+
"midConvToolChange",
|
|
157
|
+
"perTurnEffort",
|
|
155
158
|
] as const;
|
|
156
159
|
const CAPABILITY_KEY_SET = new Set(CAPABILITY_KEYS);
|
|
157
160
|
/** Adds the optional package-extension override keys carried by evidence. */
|
|
@@ -319,10 +322,14 @@ function containsString(value: unknown, target: string): boolean {
|
|
|
319
322
|
}
|
|
320
323
|
|
|
321
324
|
/**
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
325
|
+
* Every pinned profile singleton, and nothing else. The set is not enumerated
|
|
326
|
+
* in prose here: the literal below IS the enumeration and cannot go stale,
|
|
327
|
+
* whereas a count in a comment goes stale on the next port -- which is exactly
|
|
328
|
+
* what happened to the text this replaced. Which profile a caller gets when it
|
|
329
|
+
* passes none is `DEFAULT_PROFILE` below and is deliberately not restated
|
|
330
|
+
* here, so that a default switch touches one line rather than two. Admitting a
|
|
331
|
+
* profile is exactly this list -- `validateProfile` never changed to accept a
|
|
332
|
+
* new one.
|
|
326
333
|
*
|
|
327
334
|
* Membership is by REFERENCE, deliberately. A structural check would accept a
|
|
328
335
|
* caller-built object that merely looks like a pinned profile, and every wire
|
|
@@ -339,11 +346,16 @@ function containsString(value: unknown, target: string): boolean {
|
|
|
339
346
|
const ACCEPTED_PROFILES: ReadonlySet<ClaudeCodeProtocolProfile> = new Set([
|
|
340
347
|
CLAUDE_CODE_2_1_195_PROFILE,
|
|
341
348
|
CLAUDE_CODE_2_1_233_PROFILE,
|
|
349
|
+
CLAUDE_CODE_2_1_280_PROFILE,
|
|
342
350
|
]);
|
|
343
351
|
|
|
344
352
|
/**
|
|
345
|
-
* The profile
|
|
346
|
-
*
|
|
353
|
+
* The profile the request-building entry points (`buildClaudeCodeRequest`,
|
|
354
|
+
* `buildClaudeCodeCountTokensRequest` and `parseBuiltClaudeCodeRequest`)
|
|
355
|
+
* resolve to when the caller supplies none. It is not a global default: the
|
|
356
|
+
* model-query and anti-verbosity helpers deliberately keep their own,
|
|
357
|
+
* separately declared default and are NOT governed by this constant. Declared
|
|
358
|
+
* once so that the default is a single, greppable seam: a test
|
|
347
359
|
* that means "whatever the default is" reads THIS instead of naming a
|
|
348
360
|
* version, which keeps a default switch to a one-line diff and keeps tests
|
|
349
361
|
* that genuinely mean 2.1.195 honest about saying so.
|
|
@@ -352,7 +364,7 @@ const ACCEPTED_PROFILES: ReadonlySet<ClaudeCodeProtocolProfile> = new Set([
|
|
|
352
364
|
* from `src/index.ts`: the public runtime surface stays closed.
|
|
353
365
|
*/
|
|
354
366
|
export const DEFAULT_PROFILE: ClaudeCodeProtocolProfile =
|
|
355
|
-
|
|
367
|
+
CLAUDE_CODE_2_1_280_PROFILE;
|
|
356
368
|
|
|
357
369
|
function validateProfile(
|
|
358
370
|
profile: ClaudeCodeProtocolProfile,
|
|
@@ -950,6 +962,8 @@ function requestedCapabilities(
|
|
|
950
962
|
temperature: raw?.temperature ?? supported.temperature,
|
|
951
963
|
rejectsDisabledThinking:
|
|
952
964
|
raw?.rejectsDisabledThinking ?? supported.rejectsDisabledThinking,
|
|
965
|
+
midConvToolChange: raw?.midConvToolChange ?? supported.midConvToolChange,
|
|
966
|
+
perTurnEffort: raw?.perTurnEffort ?? supported.perTurnEffort,
|
|
953
967
|
};
|
|
954
968
|
for (const key of CAPABILITY_KEYS) {
|
|
955
969
|
if (typeof result[key] !== "boolean") fail("UNSUPPORTED_CAPABILITY");
|
|
@@ -1038,7 +1052,7 @@ function parseCapabilityDecisions(
|
|
|
1038
1052
|
value: unknown,
|
|
1039
1053
|
): ClaudeCodeCapabilityDecisions {
|
|
1040
1054
|
if (!isRecord(value)) fail();
|
|
1041
|
-
//
|
|
1055
|
+
// Every capability key is mandatory; the package-extension override keys
|
|
1042
1056
|
// are optional and must survive the round-trip untouched, so they are allowed
|
|
1043
1057
|
// here but never synthesized.
|
|
1044
1058
|
assertExactKeys(value, CAPABILITY_DECISION_KEY_SET);
|
|
@@ -1064,6 +1078,8 @@ function parseCapabilityDecisions(
|
|
|
1064
1078
|
contextManagement: readBoolean("contextManagement"),
|
|
1065
1079
|
temperature: readBoolean("temperature"),
|
|
1066
1080
|
rejectsDisabledThinking: readBoolean("rejectsDisabledThinking"),
|
|
1081
|
+
midConvToolChange: readBoolean("midConvToolChange"),
|
|
1082
|
+
perTurnEffort: readBoolean("perTurnEffort"),
|
|
1067
1083
|
};
|
|
1068
1084
|
}
|
|
1069
1085
|
|
|
@@ -1071,7 +1087,7 @@ function parseCapabilityDecisions(
|
|
|
1071
1087
|
* Validates evidence against the profile the request was parsed under, not
|
|
1072
1088
|
* against a hardcoded singleton. `parseBuiltClaudeCodeRequest` already
|
|
1073
1089
|
* validates `url` against `pinnedProfile.endpoint`; the profile id is the one
|
|
1074
|
-
* remaining field where
|
|
1090
|
+
* remaining field where pinned profiles differ, so it has to follow
|
|
1075
1091
|
* the same source or a request built with a non-default profile could never
|
|
1076
1092
|
* be re-parsed. Still fail-closed: the profile reaching here has already
|
|
1077
1093
|
* passed `validateProfile`.
|
|
@@ -1442,6 +1458,10 @@ export async function buildClaudeCodeCountTokensRequest(
|
|
|
1442
1458
|
normalizedId: resolvedModel.id,
|
|
1443
1459
|
capabilities: resolvedModel.capabilities,
|
|
1444
1460
|
thinkingDisplayActive: false,
|
|
1461
|
+
// A constant, not a re-derivation: this path carries no thinking
|
|
1462
|
+
// request, so `isThinkingActive(undefined, capabilities)` is false
|
|
1463
|
+
// by construction.
|
|
1464
|
+
thinkingActive: false,
|
|
1445
1465
|
},
|
|
1446
1466
|
effectiveProfile,
|
|
1447
1467
|
),
|
|
@@ -1516,8 +1536,9 @@ export async function buildClaudeCodeCountTokensRequest(
|
|
|
1516
1536
|
/**
|
|
1517
1537
|
* Builds one canonical request for the pinned Claude Code wire profile.
|
|
1518
1538
|
*
|
|
1519
|
-
* @param profile - The
|
|
1520
|
-
* `
|
|
1539
|
+
* @param profile - The accepted values are the exported pinned profile
|
|
1540
|
+
* singletons held in `ACCEPTED_PROFILES`; omitting the argument resolves to
|
|
1541
|
+
* `DEFAULT_PROFILE`. Any other object, even a
|
|
1521
1542
|
* structurally identical clone, is rejected with `ClaudeCodeWireError` code
|
|
1522
1543
|
* `INVALID_INPUT`. This deliberate fail-closed behaviour prevents callers from
|
|
1523
1544
|
* substituting an unpinned protocol profile.
|
|
@@ -1581,13 +1602,12 @@ export async function buildClaudeCodeRequest(
|
|
|
1581
1602
|
validated.suppressBillingBlock,
|
|
1582
1603
|
validated.suppressIdentityBlock,
|
|
1583
1604
|
);
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
);
|
|
1605
|
+
/*
|
|
1606
|
+
* Composition runs BEFORE body construction so that a beta site can hand a
|
|
1607
|
+
* decision to the body emitter. Both calls are pure and neither feeds the
|
|
1608
|
+
* other's arguments today, so the order itself changes no serialised byte;
|
|
1609
|
+
* the swap lands on its own so the packed-consumer digests prove that.
|
|
1610
|
+
*/
|
|
1591
1611
|
const composedBetas = composeBetasWithAudit(
|
|
1592
1612
|
{
|
|
1593
1613
|
rawModel: validated.source.model,
|
|
@@ -1598,6 +1618,10 @@ export async function buildClaudeCodeRequest(
|
|
|
1598
1618
|
capabilities,
|
|
1599
1619
|
effectiveProfile.betaPolicy,
|
|
1600
1620
|
),
|
|
1621
|
+
thinkingActive: isThinkingActive(
|
|
1622
|
+
validated.source.thinking,
|
|
1623
|
+
capabilities,
|
|
1624
|
+
),
|
|
1601
1625
|
...(validated.source.cacheControl?.ttl === undefined
|
|
1602
1626
|
? {}
|
|
1603
1627
|
: { cacheTtl: validated.source.cacheControl.ttl }),
|
|
@@ -1616,6 +1640,14 @@ export async function buildClaudeCodeRequest(
|
|
|
1616
1640
|
},
|
|
1617
1641
|
effectiveProfile,
|
|
1618
1642
|
);
|
|
1643
|
+
const canonicalBody = buildCanonicalBody(
|
|
1644
|
+
evidenceRequest(validated.source, validated.source.model),
|
|
1645
|
+
effectiveModel,
|
|
1646
|
+
system,
|
|
1647
|
+
metadata,
|
|
1648
|
+
effectiveProfile,
|
|
1649
|
+
composedBetas.thinkingDisplayOverride,
|
|
1650
|
+
);
|
|
1619
1651
|
const betas = composedBetas.betas;
|
|
1620
1652
|
const headerPlan = buildOrderedHeaderPlan({
|
|
1621
1653
|
accessToken: validated.source.accessToken,
|
|
@@ -1706,8 +1738,9 @@ export async function buildClaudeCodeRequest(
|
|
|
1706
1738
|
/**
|
|
1707
1739
|
* Validates and clones a previously built request into a deeply frozen value.
|
|
1708
1740
|
*
|
|
1709
|
-
* @param profile - The
|
|
1710
|
-
* `
|
|
1741
|
+
* @param profile - The accepted values are the exported pinned profile
|
|
1742
|
+
* singletons held in `ACCEPTED_PROFILES`; omitting the argument resolves to
|
|
1743
|
+
* `DEFAULT_PROFILE`. Any other object, even a
|
|
1711
1744
|
* structurally identical clone, is rejected with `ClaudeCodeWireError` code
|
|
1712
1745
|
* `INVALID_INPUT`. This deliberate fail-closed behaviour prevents callers from
|
|
1713
1746
|
* substituting an unpinned protocol profile.
|
package/src/contracts.ts
CHANGED
|
@@ -669,6 +669,10 @@ export interface ClaudeCodeCapabilities {
|
|
|
669
669
|
readonly contextManagement: boolean;
|
|
670
670
|
readonly temperature: boolean;
|
|
671
671
|
readonly rejectsDisabledThinking: boolean;
|
|
672
|
+
/** Catalogue string `mid_conv_tool_change`. */
|
|
673
|
+
readonly midConvToolChange: boolean;
|
|
674
|
+
/** Catalogue string `per_turn_effort`. */
|
|
675
|
+
readonly perTurnEffort: boolean;
|
|
672
676
|
}
|
|
673
677
|
|
|
674
678
|
/** Host-state beta gates pinned for a default first-party environment. */
|
|
@@ -1221,8 +1225,9 @@ export interface RedactedRequestEvidence {
|
|
|
1221
1225
|
}
|
|
1222
1226
|
|
|
1223
1227
|
/**
|
|
1224
|
-
* Records
|
|
1225
|
-
*
|
|
1228
|
+
* Records every model capability decision -- the key set is exactly
|
|
1229
|
+
* `keyof ClaudeCodeCapabilities`, so it widens whenever that interface does --
|
|
1230
|
+
* plus any package-extension beta override the caller supplied.
|
|
1226
1231
|
*
|
|
1227
1232
|
* The override keys are OPTIONAL and are emitted only when the corresponding
|
|
1228
1233
|
* member of `betaOverrides` is present, so evidence for a request that omits
|
package/src/fingerprint.ts
CHANGED
|
@@ -56,6 +56,11 @@ export async function createBillingFingerprint(
|
|
|
56
56
|
crypto?: Pick<Crypto, "subtle">,
|
|
57
57
|
): Promise<string> {
|
|
58
58
|
const cryptoProvider = crypto ?? getDefaultCrypto();
|
|
59
|
+
// The upstream transcription in the analysis doc writes `||` here; `??` is a
|
|
60
|
+
// deliberate, safe difference, not a divergence to "fix" in either
|
|
61
|
+
// direction. A string subscript yields either a one-unit string, which is
|
|
62
|
+
// always truthy, or `undefined`, so no value makes the two operators
|
|
63
|
+
// disagree (and no test can tell them apart).
|
|
59
64
|
const material = `${FINGERPRINT_PREFIX}${firstUserText[4] ?? "0"}${firstUserText[7] ?? "0"}${firstUserText[20] ?? "0"}${cliVersion}`;
|
|
60
65
|
const bytes = new TextEncoder().encode(material);
|
|
61
66
|
|
package/src/headers.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
import { ClaudeCodeWireError } from "./contracts.js";
|
|
9
9
|
import { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
10
10
|
import { CLAUDE_CODE_2_1_233_PROFILE } from "./profiles/claude-code-2.1.233.js";
|
|
11
|
+
import { CLAUDE_CODE_2_1_280_PROFILE } from "./profiles/claude-code-2.1.280.js";
|
|
11
12
|
|
|
12
13
|
const HEADER_NAMES = Object.freeze({
|
|
13
14
|
anthropicBeta: "anthropic-beta",
|
|
@@ -163,13 +164,14 @@ function parseExtraHeaderPolicy(value: unknown): ClaudeCodeExtraHeaderPolicy {
|
|
|
163
164
|
|
|
164
165
|
/**
|
|
165
166
|
* Accepts a pinned profile by REFERENCE, never by shape, and returns the
|
|
166
|
-
* singleton itself so nothing downstream can be handed a look-alike.
|
|
167
|
-
*
|
|
167
|
+
* singleton itself so nothing downstream can be handed a look-alike. Each
|
|
168
|
+
* pinned profile widens the accepted set by exactly one object;
|
|
168
169
|
* anything else, including a structural clone, still fails closed.
|
|
169
170
|
*/
|
|
170
171
|
function parseProfile(value: unknown): ClaudeCodeProtocolProfile {
|
|
171
172
|
if (value === CLAUDE_CODE_2_1_195_PROFILE) return CLAUDE_CODE_2_1_195_PROFILE;
|
|
172
173
|
if (value === CLAUDE_CODE_2_1_233_PROFILE) return CLAUDE_CODE_2_1_233_PROFILE;
|
|
174
|
+
if (value === CLAUDE_CODE_2_1_280_PROFILE) return CLAUDE_CODE_2_1_280_PROFILE;
|
|
173
175
|
throw new ClaudeCodeWireError("INVALID_INPUT");
|
|
174
176
|
}
|
|
175
177
|
|
package/src/index.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Public entry point for the Claude Code wire compatibility package.
|
|
5
5
|
*
|
|
6
|
-
* Only the surfaces listed below are public.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Only the surfaces listed below are public. Internal protocol modules remain
|
|
7
|
+
* private; the documented builder, parser, anti-verbosity helpers, read-only
|
|
8
|
+
* model queries and the transcribed beta registries are exported here, and
|
|
9
|
+
* nothing else.
|
|
9
10
|
*
|
|
10
11
|
* Importing this module has no side effects. It reads no environment, opens
|
|
11
12
|
* no network connection, touches no clock or random source, and holds no
|
|
@@ -53,11 +54,35 @@ export {
|
|
|
53
54
|
selectAntiVerbositySection,
|
|
54
55
|
} from "./anti-verbosity.js";
|
|
55
56
|
|
|
57
|
+
export { BETA_REGISTRY } from "./beta-registry.js";
|
|
58
|
+
|
|
56
59
|
export {
|
|
57
60
|
buildClaudeCodeCountTokensRequest,
|
|
58
61
|
buildClaudeCodeRequest,
|
|
59
62
|
parseBuiltClaudeCodeRequest,
|
|
60
63
|
} from "./build-request.js";
|
|
61
64
|
|
|
65
|
+
export { TOKEN_COUNTING_BETA } from "./count-tokens.js";
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
hasOneMillionContext,
|
|
69
|
+
isAdaptiveThinkingModel,
|
|
70
|
+
isClaude3Model,
|
|
71
|
+
isEligibleFor1MContext,
|
|
72
|
+
isFable5Model,
|
|
73
|
+
isHaikuModel,
|
|
74
|
+
isMythos5Model,
|
|
75
|
+
isOpus46Model,
|
|
76
|
+
isOpus47Model,
|
|
77
|
+
isOpus48Model,
|
|
78
|
+
isSonnet46Model,
|
|
79
|
+
modelCapability,
|
|
80
|
+
supportsStructuredOutputs,
|
|
81
|
+
supportsWebSearch,
|
|
82
|
+
} from "./model-queries.js";
|
|
83
|
+
|
|
84
|
+
export { BETA_REGISTRY_2_1_233 } from "./profiles/beta-registry-2.1.233.js";
|
|
85
|
+
export { BETA_REGISTRY_2_1_280 } from "./profiles/beta-registry-2.1.280.js";
|
|
62
86
|
export { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
63
87
|
export { CLAUDE_CODE_2_1_233_PROFILE } from "./profiles/claude-code-2.1.233.js";
|
|
88
|
+
export { CLAUDE_CODE_2_1_280_PROFILE } from "./profiles/claude-code-2.1.280.js";
|
|
@@ -9,20 +9,20 @@ import { profileBehaviors } from "./profile-behaviors.js";
|
|
|
9
9
|
import { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
10
10
|
|
|
11
11
|
/*
|
|
12
|
-
* Capability derivation, ported from the genuine client's
|
|
13
|
-
* predicates.
|
|
12
|
+
* Capability derivation, ported from the genuine client's capability
|
|
13
|
+
* predicates and, since 2.1.280, from catalogue strings that have no predicate
|
|
14
|
+
* upstream at all.
|
|
14
15
|
*
|
|
15
16
|
* READ THIS FIRST -- there are two derivation paths and they are not
|
|
16
17
|
* interchangeable:
|
|
17
18
|
*
|
|
18
19
|
* 1. Catalogue path (`deriveCapabilitiesFromCatalogue`), taken for every id
|
|
19
|
-
* present in the
|
|
20
|
-
* verbatim upstream string in
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* predicate-derived.
|
|
20
|
+
* present in the catalogue. The fields listed in
|
|
21
|
+
* `CATALOGUE_BACKED_CAPABILITIES` have a verbatim upstream string in
|
|
22
|
+
* `ClaudeCodeCatalogueEntry.capabilities` and are read from there; that
|
|
23
|
+
* map is the enumeration, so this comment does not repeat it. The rest
|
|
24
|
+
* (`thinking`, `interleavedThinking`, `temperature`) have NO catalogue
|
|
25
|
+
* string in any client version and stay predicate-derived.
|
|
26
26
|
* 2. Predicate fallback (`deriveCapabilitiesFromPredicates`), taken for ids
|
|
27
27
|
* with no catalogue entry -- `claude-mythos-5` (absent by product
|
|
28
28
|
* decision D-1), ids from a newer client, and anything that escaped
|
|
@@ -39,7 +39,7 @@ import { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
|
39
39
|
* surprising:
|
|
40
40
|
*
|
|
41
41
|
* On the first-party provider -- the only provider this package targets --
|
|
42
|
-
* every one of these
|
|
42
|
+
* every one of these predicates reduces to a pure function of the
|
|
43
43
|
* normalized model id.
|
|
44
44
|
*
|
|
45
45
|
* Why. Upstream, each predicate has the shape
|
|
@@ -87,7 +87,7 @@ import { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
|
87
87
|
* `claude-mythos-5` has no catalogue entry by product decision D-1. Upstream
|
|
88
88
|
* special-cases it by name in `Kw`, `Hke`, `Yte` and `Uot`; this port subsumes
|
|
89
89
|
* those clauses into the first-party fallback, which yields an identical
|
|
90
|
-
* result. The explicit D-1 test asserting its full
|
|
90
|
+
* result. The explicit D-1 test asserting its full capability row is the
|
|
91
91
|
* guard for that equivalence.
|
|
92
92
|
*
|
|
93
93
|
* Model ids reaching these functions have already been normalized by
|
|
@@ -337,11 +337,19 @@ export function rejectsDisabledThinking(normalizedId: string): boolean {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
/**
|
|
340
|
-
* The
|
|
341
|
-
*
|
|
342
|
-
* `thinking`, `interleavedThinking
|
|
343
|
-
* string in any client version and
|
|
344
|
-
* paths.
|
|
340
|
+
* The `ClaudeCodeCapabilities` fields the catalogue represents, paired with
|
|
341
|
+
* their verbatim upstream capability string. Whatever is not a key here --
|
|
342
|
+
* `thinking`, `interleavedThinking` and `temperature` today -- has no
|
|
343
|
+
* catalogue string in any client version and is derived from its predicate on
|
|
344
|
+
* both paths. The literal below is the enumeration; no count is restated in
|
|
345
|
+
* prose, because a count beside the thing it counts goes stale on the next
|
|
346
|
+
* port.
|
|
347
|
+
*
|
|
348
|
+
* `per_turn_timing` is deliberately NOT mapped. It is a real 2.1.280
|
|
349
|
+
* catalogue string carried by three models, but the beta it would gate,
|
|
350
|
+
* `timing-2026-09-09`, is environment-gated off on the default path, so a
|
|
351
|
+
* field here would be a capability nothing reads. Mapping it would invent a
|
|
352
|
+
* derived value with no emission behind it.
|
|
345
353
|
*/
|
|
346
354
|
const CATALOGUE_BACKED_CAPABILITIES = {
|
|
347
355
|
effort: "effort",
|
|
@@ -350,12 +358,14 @@ const CATALOGUE_BACKED_CAPABILITIES = {
|
|
|
350
358
|
adaptiveThinking: "adaptive_thinking",
|
|
351
359
|
contextManagement: "context_management",
|
|
352
360
|
rejectsDisabledThinking: "rejects_disabled_thinking",
|
|
361
|
+
midConvToolChange: "mid_conv_tool_change",
|
|
362
|
+
perTurnEffort: "per_turn_effort",
|
|
353
363
|
} as const;
|
|
354
364
|
|
|
355
365
|
/**
|
|
356
|
-
* Pure catalogue -> capabilities mapping. Reads nothing but `entry` for
|
|
357
|
-
*
|
|
358
|
-
*
|
|
366
|
+
* Pure catalogue -> capabilities mapping. Reads nothing but `entry` for every
|
|
367
|
+
* catalogue-backed field; `normalizedId` is used only for the fields the
|
|
368
|
+
* catalogue does not represent.
|
|
359
369
|
*
|
|
360
370
|
* This function applies no exceptions and no id special cases. The one cell
|
|
361
371
|
* where the 2.1.195 catalogue disagrees with the wire is corrected by the
|
|
@@ -381,6 +391,8 @@ export function deriveCapabilitiesFromCatalogue(
|
|
|
381
391
|
rejectsDisabledThinking: has(
|
|
382
392
|
CATALOGUE_BACKED_CAPABILITIES.rejectsDisabledThinking,
|
|
383
393
|
),
|
|
394
|
+
midConvToolChange: has(CATALOGUE_BACKED_CAPABILITIES.midConvToolChange),
|
|
395
|
+
perTurnEffort: has(CATALOGUE_BACKED_CAPABILITIES.perTurnEffort),
|
|
384
396
|
});
|
|
385
397
|
}
|
|
386
398
|
|
|
@@ -408,6 +420,30 @@ function deriveCapabilitiesFromPredicates(
|
|
|
408
420
|
contextManagement: supportsContextManagement(normalizedId),
|
|
409
421
|
temperature: supportsTemperature(normalizedId),
|
|
410
422
|
rejectsDisabledThinking: rejectsDisabledThinking(normalizedId),
|
|
423
|
+
/*
|
|
424
|
+
* Both are FALSE here, and this is the one place where the two derivation
|
|
425
|
+
* paths stop agreeing in kind: the six older catalogue-backed fields above
|
|
426
|
+
* resolve maximally permissive for an uncatalogued id, these two resolve
|
|
427
|
+
* restrictive. That asymmetry is deliberate, and the reason differs per
|
|
428
|
+
* field.
|
|
429
|
+
*
|
|
430
|
+
* `perTurnEffort: false` MATCHES upstream. Its lookup consults the static
|
|
431
|
+
* catalogue and, at default settings, the remote client-data path that
|
|
432
|
+
* could override it is empty, so an id with no entry yields false there
|
|
433
|
+
* too.
|
|
434
|
+
*
|
|
435
|
+
* `midConvToolChange: false` DIVERGES from upstream, which still sends
|
|
436
|
+
* `mid-conversation-tool-changes-2026-07-01` for a model it has no
|
|
437
|
+
* catalogue entry for. The analysis document records that as a divergence
|
|
438
|
+
* recorded but not ported, for the same reason the package declines the
|
|
439
|
+
* neighbouring permissive tail: a capability granted to an unknown id is a
|
|
440
|
+
* guess about a model this package knows nothing about.
|
|
441
|
+
*
|
|
442
|
+
* Neither field has an upstream predicate to fall back on -- both exist
|
|
443
|
+
* only as catalogue strings -- so there is no third option here.
|
|
444
|
+
*/
|
|
445
|
+
midConvToolChange: false,
|
|
446
|
+
perTurnEffort: false,
|
|
411
447
|
});
|
|
412
448
|
}
|
|
413
449
|
|
package/src/model-identity.ts
CHANGED
|
@@ -7,17 +7,44 @@ export function stripModelMarkers(model: string): string {
|
|
|
7
7
|
return model.replace(/\[(1|2)m\]/gi, "");
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Rewrites dotted version separators to the hyphenated form upstream uses on
|
|
12
|
+
* the wire (`claude-opus-4.7` -> `claude-opus-4-7`). Only a digit-dot-digit
|
|
13
|
+
* run is rewritten, so ids with no dotted version (`gpt-4o`, `""`) and
|
|
14
|
+
* unrelated dots are left byte-identical.
|
|
15
|
+
*
|
|
16
|
+
* The plugin performs the same rewrite at its wire seam
|
|
17
|
+
* (`lib/mimicry/wire-compat.mjs`); the plugin's own family predicates accept
|
|
18
|
+
* `[._-]` interchangeably, so this keeps package classification in parity.
|
|
19
|
+
*/
|
|
20
|
+
function dottedToDashedVersion(model: string): string {
|
|
21
|
+
return model.replace(/(\d)\.(\d)/g, "$1-$2");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Ports upstream `$_` (binary offset 226639025); in the 2.1.280 bundle the
|
|
26
|
+
* function is `GF` at byte offset 7413751. The two offsets are in different
|
|
27
|
+
* address spaces: the first indexes the 2.1.195 platform binary, the second
|
|
28
|
+
* the carved 2.1.280 JavaScript bundle, so neither can be found in the other.
|
|
29
|
+
*
|
|
30
|
+
* Rung order is load-bearing: each test is a substring test, so a longer id
|
|
31
|
+
* must precede the shorter id it contains or the shorter rung swallows it.
|
|
32
|
+
*/
|
|
11
33
|
export function normalizeModelId(model: string): string {
|
|
12
|
-
model = model.toLowerCase();
|
|
34
|
+
model = dottedToDashedVersion(model.toLowerCase());
|
|
35
|
+
if (model.includes("claude-fable-5-1")) return "claude-fable-5-1";
|
|
13
36
|
if (model.includes("claude-fable-5")) return "claude-fable-5";
|
|
37
|
+
if (model.includes("claude-mythos-5-1")) return "claude-mythos-5-1";
|
|
14
38
|
if (model.includes("claude-mythos-5")) return "claude-mythos-5";
|
|
39
|
+
if (model.includes("claude-opus-5-5")) return "claude-opus-5-5";
|
|
40
|
+
if (model.includes("claude-opus-5")) return "claude-opus-5";
|
|
15
41
|
if (model.includes("claude-opus-4-8")) return "claude-opus-4-8";
|
|
16
42
|
if (model.includes("claude-opus-4-7")) return "claude-opus-4-7";
|
|
17
43
|
if (model.includes("claude-opus-4-6")) return "claude-opus-4-6";
|
|
18
44
|
if (model.includes("claude-opus-4-5")) return "claude-opus-4-5";
|
|
19
45
|
if (model.includes("claude-opus-4-1")) return "claude-opus-4-1";
|
|
20
46
|
if (/claude-opus-4(?!-\d(?!\d))/.test(model)) return "claude-opus-4-0";
|
|
47
|
+
if (model.includes("claude-sonnet-5")) return "claude-sonnet-5";
|
|
21
48
|
if (model.includes("claude-sonnet-4-6")) return "claude-sonnet-4-6";
|
|
22
49
|
if (model.includes("claude-sonnet-4-5")) return "claude-sonnet-4-5";
|
|
23
50
|
if (/claude-sonnet-4(?!-\d(?!\d))/.test(model)) return "claude-sonnet-4-0";
|