@tormentalabs/claude-code-wire-compat 0.1.0-rc.17 → 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 +36 -2
- package/README.md +53 -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/betas.ts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
import { BETA_REGISTRY } from "./beta-registry.js";
|
|
4
|
+
import type {
|
|
5
|
+
ClaudeCodeCapabilities,
|
|
6
|
+
ClaudeCodeProtocolProfile,
|
|
7
|
+
} from "./contracts.js";
|
|
8
|
+
import { ClaudeCodeWireError } from "./contracts.js";
|
|
9
|
+
import {
|
|
10
|
+
supportsMidConversationSystem,
|
|
11
|
+
supportsStructuredOutputs,
|
|
12
|
+
} from "./model-capabilities.js";
|
|
13
|
+
import { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* Provenance. The emitted order is a port of the genuine client's base beta set
|
|
17
|
+
* `$9r` (byte offset 227387921), followed by the gated pushes the request
|
|
18
|
+
* builder performs afterwards (bytes 238153535-238155672). The identifiers
|
|
19
|
+
* themselves come from registry `Udd`, ported verbatim as `BETA_REGISTRY`.
|
|
20
|
+
*
|
|
21
|
+
* The order is EMERGENT, not declared: upstream neither sorts the array nor
|
|
22
|
+
* keeps a canonical list, so the sequence below is load-bearing and must not be
|
|
23
|
+
* reordered for tidiness. `docs/source-trace.md` records the same fact under
|
|
24
|
+
* "Beta registry and push order".
|
|
25
|
+
*/
|
|
26
|
+
export interface ComposeBetasInput {
|
|
27
|
+
readonly rawModel: string;
|
|
28
|
+
readonly normalizedId: string;
|
|
29
|
+
readonly capabilities: ClaudeCodeCapabilities;
|
|
30
|
+
readonly thinkingDisplayActive: boolean;
|
|
31
|
+
readonly cacheTtl?: "5m" | "1h" | null;
|
|
32
|
+
readonly speed?: "standard" | "fast" | null;
|
|
33
|
+
/**
|
|
34
|
+
* Package extension, not observed upstream behaviour. Consumer-supplied beta
|
|
35
|
+
* identifiers appended AFTER the derived canonical set. See
|
|
36
|
+
* `docs/source-trace.md`, governance ledger L10.
|
|
37
|
+
*/
|
|
38
|
+
readonly additionalBetas?: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* Package extension, not observed upstream behaviour. Beta identifiers
|
|
41
|
+
* removed from the emitted set AFTER composition and AFTER the
|
|
42
|
+
* `additionalBetas` merge, so suppression beats addition. An identifier that
|
|
43
|
+
* is not in the composed set is a silent no-op. See `docs/source-trace.md`,
|
|
44
|
+
* governance ledger L14.
|
|
45
|
+
*/
|
|
46
|
+
readonly suppressBetas?: readonly string[];
|
|
47
|
+
/**
|
|
48
|
+
* Package extension, not observed upstream behaviour. Forces (`true`) or
|
|
49
|
+
* suppresses (`false`) the 1M-context beta for this request, overriding the
|
|
50
|
+
* `[1m]` model marker. See `docs/source-trace.md`, governance ledger L10.
|
|
51
|
+
*/
|
|
52
|
+
readonly use1MContextOverride?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reports the emitted beta set together with the identifiers `suppressBetas`
|
|
57
|
+
* actually removed. `suppressedBetaNames` is empty when the seam is unused or
|
|
58
|
+
* matched nothing, which is what keeps the evidence key absent for every
|
|
59
|
+
* request built before the seam existed.
|
|
60
|
+
*/
|
|
61
|
+
export interface ComposedBetas {
|
|
62
|
+
readonly betas: readonly string[];
|
|
63
|
+
readonly suppressedBetaNames: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Bounds a caller-supplied beta list (`additionalBetas` and `suppressBetas`
|
|
68
|
+
* share these rules verbatim). The header is a comma-joined single
|
|
69
|
+
* field, so a comma, control character, or whitespace in an entry would let a
|
|
70
|
+
* caller synthesize extra beta values (or, with CR/LF, an entirely separate
|
|
71
|
+
* header). The allowlist below is deliberately narrower than the observed
|
|
72
|
+
* upstream identifiers require, because every genuine beta name in
|
|
73
|
+
* `BETA_REGISTRY` matches it.
|
|
74
|
+
*/
|
|
75
|
+
const ADDITIONAL_BETA_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
76
|
+
const MAX_ADDITIONAL_BETA_LENGTH = 128;
|
|
77
|
+
const MAX_ADDITIONAL_BETAS = 32;
|
|
78
|
+
|
|
79
|
+
function validateAdditionalBetas(value: unknown): readonly string[] {
|
|
80
|
+
if (!Array.isArray(value) || value.length > MAX_ADDITIONAL_BETAS) {
|
|
81
|
+
throw new ClaudeCodeWireError("INVALID_INPUT");
|
|
82
|
+
}
|
|
83
|
+
return value.map((entry: unknown): string => {
|
|
84
|
+
if (
|
|
85
|
+
typeof entry !== "string" ||
|
|
86
|
+
entry.length === 0 ||
|
|
87
|
+
entry.length > MAX_ADDITIONAL_BETA_LENGTH ||
|
|
88
|
+
!ADDITIONAL_BETA_PATTERN.test(entry)
|
|
89
|
+
) {
|
|
90
|
+
throw new ClaudeCodeWireError("INVALID_INPUT");
|
|
91
|
+
}
|
|
92
|
+
return entry;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const NO_SUPPRESSED_BETAS: readonly string[] = Object.freeze([]);
|
|
97
|
+
|
|
98
|
+
export function composeBetas(
|
|
99
|
+
input: ComposeBetasInput,
|
|
100
|
+
profile: ClaudeCodeProtocolProfile = CLAUDE_CODE_2_1_195_PROFILE,
|
|
101
|
+
): readonly string[] {
|
|
102
|
+
return composeBetasWithAudit(input, profile).betas;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function composeBetasWithAudit(
|
|
106
|
+
input: ComposeBetasInput,
|
|
107
|
+
profile: ClaudeCodeProtocolProfile = CLAUDE_CODE_2_1_195_PROFILE,
|
|
108
|
+
): ComposedBetas {
|
|
109
|
+
const out: string[] = [];
|
|
110
|
+
const policy = profile.betaPolicy;
|
|
111
|
+
const experimental = policy.experimentalBetasEnabled;
|
|
112
|
+
|
|
113
|
+
if (!input.normalizedId.includes("haiku"))
|
|
114
|
+
out.push(BETA_REGISTRY.CLAUDE_CODE.header);
|
|
115
|
+
if (policy.oauthAuthenticated) out.push(BETA_REGISTRY.OAUTH_AUTH.header);
|
|
116
|
+
// Package extension: `use1MContextOverride` replaces the model-marker gate
|
|
117
|
+
// for this request. The profile gate still applies, so an override cannot
|
|
118
|
+
// enable a beta the pinned profile declares unavailable.
|
|
119
|
+
const oneMillionRequested =
|
|
120
|
+
input.use1MContextOverride ?? /\[1m\]/iu.test(input.rawModel);
|
|
121
|
+
if (policy.oneMillionContextEnabled && oneMillionRequested) {
|
|
122
|
+
out.push(BETA_REGISTRY.LONG_CONTEXT.header);
|
|
123
|
+
}
|
|
124
|
+
if (
|
|
125
|
+
policy.interleavedThinkingEnabled &&
|
|
126
|
+
input.capabilities.interleavedThinking
|
|
127
|
+
) {
|
|
128
|
+
out.push(BETA_REGISTRY.INTERLEAVED_THINKING.header);
|
|
129
|
+
}
|
|
130
|
+
if (
|
|
131
|
+
experimental &&
|
|
132
|
+
input.capabilities.interleavedThinking &&
|
|
133
|
+
policy.interactive &&
|
|
134
|
+
!policy.thinkingSummariesShown &&
|
|
135
|
+
!input.thinkingDisplayActive
|
|
136
|
+
) {
|
|
137
|
+
out.push(BETA_REGISTRY.REDACT_THINKING.header);
|
|
138
|
+
}
|
|
139
|
+
if (
|
|
140
|
+
policy.thinkingTokenCountEnabled &&
|
|
141
|
+
experimental &&
|
|
142
|
+
input.capabilities.interleavedThinking
|
|
143
|
+
) {
|
|
144
|
+
out.push(BETA_REGISTRY.THINKING_TOKEN_COUNT.header);
|
|
145
|
+
}
|
|
146
|
+
if (experimental && policy.narrationSummariesEnabled)
|
|
147
|
+
out.push(BETA_REGISTRY.NARRATION_SUMMARIES.header);
|
|
148
|
+
if (experimental && input.capabilities.contextManagement)
|
|
149
|
+
out.push(BETA_REGISTRY.CONTEXT_MANAGEMENT.header);
|
|
150
|
+
if (
|
|
151
|
+
experimental &&
|
|
152
|
+
supportsStructuredOutputs(input.normalizedId) &&
|
|
153
|
+
policy.structuredOutputsEnabled
|
|
154
|
+
) {
|
|
155
|
+
out.push(BETA_REGISTRY.STRUCTURED_OUTPUTS.header);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// No web-search beta: upstream pushes it only for vertex and foundry.
|
|
159
|
+
if (experimental) out.push(BETA_REGISTRY.PROMPT_CACHING_SCOPE.header);
|
|
160
|
+
if (supportsMidConversationSystem(input.normalizedId))
|
|
161
|
+
out.push(BETA_REGISTRY.MID_CONVERSATION_SYSTEM.header);
|
|
162
|
+
if (input.capabilities.effort) out.push(BETA_REGISTRY.EFFORT.header);
|
|
163
|
+
|
|
164
|
+
if (input.speed === "fast" && !out.includes(BETA_REGISTRY.SPEED.header)) {
|
|
165
|
+
out.push(BETA_REGISTRY.SPEED.header);
|
|
166
|
+
}
|
|
167
|
+
if (policy.afkModeEnabled && !out.includes(BETA_REGISTRY.AFK_MODE.header)) {
|
|
168
|
+
out.push(BETA_REGISTRY.AFK_MODE.header);
|
|
169
|
+
}
|
|
170
|
+
if (
|
|
171
|
+
input.cacheTtl === "1h" &&
|
|
172
|
+
experimental &&
|
|
173
|
+
!out.includes(BETA_REGISTRY.EXTENDED_CACHE_TTL.header)
|
|
174
|
+
) {
|
|
175
|
+
out.push(BETA_REGISTRY.EXTENDED_CACHE_TTL.header);
|
|
176
|
+
}
|
|
177
|
+
if (profile.contextHintEnabled) out.push(BETA_REGISTRY.CONTEXT_HINT.header);
|
|
178
|
+
if (
|
|
179
|
+
policy.cacheDiagnosisEnabled &&
|
|
180
|
+
!out.includes(BETA_REGISTRY.CACHE_DIAGNOSIS.header)
|
|
181
|
+
) {
|
|
182
|
+
out.push(BETA_REGISTRY.CACHE_DIAGNOSIS.header);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// No advisor-tool beta: upstream has no observed unconditional push site.
|
|
186
|
+
|
|
187
|
+
// Package extension. Canonical, upstream-derived identifiers always precede
|
|
188
|
+
// caller-supplied ones, and a caller entry that duplicates an already-emitted
|
|
189
|
+
// identifier is dropped rather than reordering the canonical prefix.
|
|
190
|
+
if (input.additionalBetas !== undefined) {
|
|
191
|
+
for (const beta of validateAdditionalBetas(input.additionalBetas)) {
|
|
192
|
+
if (!out.includes(beta)) out.push(beta);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Package extension. The suppression filter is deliberately LAST: it runs
|
|
197
|
+
// after the canonical composition and after the `additionalBetas` merge, so
|
|
198
|
+
// an identifier named by both seams does not reach the wire. Removal is
|
|
199
|
+
// reported in composed order, never in caller order, and an identifier that
|
|
200
|
+
// was never composed is a silent no-op because a consumer cannot know which
|
|
201
|
+
// betas this package derives for a given model.
|
|
202
|
+
if (input.suppressBetas === undefined) {
|
|
203
|
+
return Object.freeze({
|
|
204
|
+
betas: Object.freeze(out),
|
|
205
|
+
suppressedBetaNames: NO_SUPPRESSED_BETAS,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
const suppressed = new Set(validateAdditionalBetas(input.suppressBetas));
|
|
209
|
+
const kept: string[] = [];
|
|
210
|
+
const removed: string[] = [];
|
|
211
|
+
for (const beta of out) {
|
|
212
|
+
if (suppressed.has(beta)) removed.push(beta);
|
|
213
|
+
else kept.push(beta);
|
|
214
|
+
}
|
|
215
|
+
return Object.freeze({
|
|
216
|
+
betas: Object.freeze(kept),
|
|
217
|
+
suppressedBetaNames: Object.freeze(removed),
|
|
218
|
+
});
|
|
219
|
+
}
|