@tormentalabs/claude-code-wire-compat 0.6.0 → 0.7.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 +59 -0
- package/dist/build-request.d.ts.map +1 -1
- package/dist/build-request.js +49 -35
- package/dist/build-request.js.map +1 -1
- package/dist/limits.d.ts +3 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +4 -0
- package/dist/limits.js.map +1 -0
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +45 -2
- package/dist/redaction.js.map +1 -1
- package/dist/request-body.d.ts +3 -1
- package/dist/request-body.d.ts.map +1 -1
- package/dist/request-body.js +61 -43
- package/dist/request-body.js.map +1 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +23 -22
- package/dist/system-prompt.js.map +1 -1
- package/dist/unicode.d.ts +41 -0
- package/dist/unicode.d.ts.map +1 -1
- package/dist/unicode.js +37 -0
- package/dist/unicode.js.map +1 -1
- package/dist/violation.d.ts +23 -0
- package/dist/violation.d.ts.map +1 -0
- package/dist/violation.js +210 -0
- package/dist/violation.js.map +1 -0
- package/package.json +1 -1
- package/src/build-request.ts +66 -35
- package/src/limits.ts +4 -0
- package/src/redaction.ts +62 -2
- package/src/request-body.ts +82 -46
- package/src/system-prompt.ts +32 -24
- package/src/unicode.ts +71 -0
- package/src/violation.ts +226 -0
package/src/violation.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
import type { TextViolation } from "./unicode.js";
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Safe validation-failure diagnostics (decision P1.T1, D4).
|
|
7
|
+
*
|
|
8
|
+
* Every value produced here is safe by construction: reasons come from a
|
|
9
|
+
* closed set, path segments from a closed vocabulary of schema-defined keys
|
|
10
|
+
* plus numeric indices and the `*` placeholder for user-controlled keys, and
|
|
11
|
+
* reported code units only ever come from control or surrogate ranges. No
|
|
12
|
+
* caller text, key name or excerpt can leak through these fields.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const VIOLATION_REASONS: ReadonlySet<string> = new Set([
|
|
16
|
+
"lone-surrogate",
|
|
17
|
+
"control-char",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Schema-defined keys allowed verbatim in a violation path. Any other object
|
|
22
|
+
* key (tool input names, `input_schema` property names, arbitrary metadata
|
|
23
|
+
* members) is user-controlled text and is reported as `*`.
|
|
24
|
+
*/
|
|
25
|
+
const PATH_KEY_VOCABULARY: ReadonlySet<string> = new Set([
|
|
26
|
+
"$schema",
|
|
27
|
+
"accept",
|
|
28
|
+
"additionalBetas",
|
|
29
|
+
"additionalProperties",
|
|
30
|
+
"anthropicAdditionalProtection",
|
|
31
|
+
"app",
|
|
32
|
+
"arch",
|
|
33
|
+
"accountUuid",
|
|
34
|
+
"body",
|
|
35
|
+
"cache_control",
|
|
36
|
+
"citations",
|
|
37
|
+
"clientApp",
|
|
38
|
+
"clientRequestId",
|
|
39
|
+
"claudeRemoteContainerId",
|
|
40
|
+
"claudeRemoteSessionId",
|
|
41
|
+
"content",
|
|
42
|
+
"context",
|
|
43
|
+
"crypto",
|
|
44
|
+
"data",
|
|
45
|
+
"default",
|
|
46
|
+
"defer_loading",
|
|
47
|
+
"description",
|
|
48
|
+
"deviceId",
|
|
49
|
+
"document",
|
|
50
|
+
"effort",
|
|
51
|
+
"enum",
|
|
52
|
+
"evidence",
|
|
53
|
+
"examples",
|
|
54
|
+
"extraHeaderPolicy",
|
|
55
|
+
"extraHeaders",
|
|
56
|
+
"headers",
|
|
57
|
+
"id",
|
|
58
|
+
"image",
|
|
59
|
+
"input",
|
|
60
|
+
"input_schema",
|
|
61
|
+
"input_examples",
|
|
62
|
+
"items",
|
|
63
|
+
"max_tokens",
|
|
64
|
+
"maxTokens",
|
|
65
|
+
"media_type",
|
|
66
|
+
"messages",
|
|
67
|
+
"metadata",
|
|
68
|
+
"metadataOverrides",
|
|
69
|
+
"method",
|
|
70
|
+
"model",
|
|
71
|
+
"name",
|
|
72
|
+
"os",
|
|
73
|
+
"output_config",
|
|
74
|
+
"profileOverride",
|
|
75
|
+
"properties",
|
|
76
|
+
"redacted_thinking",
|
|
77
|
+
"required",
|
|
78
|
+
"role",
|
|
79
|
+
"runtime",
|
|
80
|
+
"runtimeVersion",
|
|
81
|
+
"search_results",
|
|
82
|
+
"sessionId",
|
|
83
|
+
"signature",
|
|
84
|
+
"source",
|
|
85
|
+
"stainlessHelper",
|
|
86
|
+
"stainlessRetryCount",
|
|
87
|
+
"stop_sequences",
|
|
88
|
+
"stream",
|
|
89
|
+
"strict",
|
|
90
|
+
"system",
|
|
91
|
+
"text",
|
|
92
|
+
"thinking",
|
|
93
|
+
"timing",
|
|
94
|
+
"title",
|
|
95
|
+
"tool_name",
|
|
96
|
+
"tool_reference",
|
|
97
|
+
"tool_result",
|
|
98
|
+
"tool_use",
|
|
99
|
+
"tool_use_id",
|
|
100
|
+
"tools",
|
|
101
|
+
"type",
|
|
102
|
+
"url",
|
|
103
|
+
"user_id",
|
|
104
|
+
"userId",
|
|
105
|
+
"userIdFields",
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
const MAX_PATH_SEGMENTS = 16;
|
|
109
|
+
const MAX_PATH_LENGTH = 256;
|
|
110
|
+
const TRUNCATION_MARKER = "...";
|
|
111
|
+
const OPAQUE_SUBTREES: ReadonlySet<string> = new Set([
|
|
112
|
+
"headers",
|
|
113
|
+
"input",
|
|
114
|
+
"input_schema",
|
|
115
|
+
"input_examples",
|
|
116
|
+
"metadata",
|
|
117
|
+
"metadataOverrides",
|
|
118
|
+
"userIdFields",
|
|
119
|
+
"extraHeaders",
|
|
120
|
+
"data",
|
|
121
|
+
"default",
|
|
122
|
+
"examples",
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
export type ViolationPathSegment = string | number;
|
|
126
|
+
|
|
127
|
+
function mapSegment(segment: ViolationPathSegment, opaque: boolean): string {
|
|
128
|
+
if (typeof segment === "number") {
|
|
129
|
+
return Number.isSafeInteger(segment) &&
|
|
130
|
+
!Object.is(segment, -0) &&
|
|
131
|
+
segment >= 0 &&
|
|
132
|
+
segment <= 999_999
|
|
133
|
+
? String(segment)
|
|
134
|
+
: "*";
|
|
135
|
+
}
|
|
136
|
+
return !opaque && PATH_KEY_VOCABULARY.has(segment) ? segment : "*";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Renders a pointer-like path (`/messages/0/content`) from walk segments.
|
|
141
|
+
* At most 16 segments, then a trailing `/...`; at most 256 characters.
|
|
142
|
+
*/
|
|
143
|
+
export function formatViolationPath(
|
|
144
|
+
segments: readonly ViolationPathSegment[],
|
|
145
|
+
): string {
|
|
146
|
+
let path = "";
|
|
147
|
+
let opaque = false;
|
|
148
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
149
|
+
const segment = mapSegment(segments[index] ?? "*", opaque);
|
|
150
|
+
const tailLength = index < segments.length - 1 ? 4 : 0;
|
|
151
|
+
if (
|
|
152
|
+
index >= MAX_PATH_SEGMENTS ||
|
|
153
|
+
path.length + 1 + segment.length + tailLength > MAX_PATH_LENGTH
|
|
154
|
+
) {
|
|
155
|
+
return `${path}/${TRUNCATION_MARKER}`;
|
|
156
|
+
}
|
|
157
|
+
path += `/${segment}`;
|
|
158
|
+
opaque ||= segment === "*" || OPAQUE_SUBTREES.has(segment);
|
|
159
|
+
}
|
|
160
|
+
return path || "/";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function isValidViolationReason(value: string): boolean {
|
|
164
|
+
return VIOLATION_REASONS.has(value);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const PATH_INDEX_SEGMENT = /^(?:0|[1-9]\d{0,5})$/u;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Revalidates a formatted violation path for the redaction allowlist. Every
|
|
171
|
+
* segment must be a vocabulary key, a decimal index of at most 6 digits, `*`,
|
|
172
|
+
* or the single trailing truncation marker; the whole string is capped.
|
|
173
|
+
*/
|
|
174
|
+
export function isValidViolationPath(value: string): boolean {
|
|
175
|
+
if (value.length === 0 || value.length > MAX_PATH_LENGTH) return false;
|
|
176
|
+
if (!value.startsWith("/")) return false;
|
|
177
|
+
if (value === "/") return true;
|
|
178
|
+
const segments = value.slice(1).split("/");
|
|
179
|
+
if (segments.length > MAX_PATH_SEGMENTS + 1) return false;
|
|
180
|
+
let opaque = false;
|
|
181
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
182
|
+
const segment = segments[index];
|
|
183
|
+
if (segment === undefined) return false;
|
|
184
|
+
if (segment === TRUNCATION_MARKER) return index === segments.length - 1;
|
|
185
|
+
if (index >= MAX_PATH_SEGMENTS) return false;
|
|
186
|
+
if (segment === "*") {
|
|
187
|
+
opaque = true;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (PATH_INDEX_SEGMENT.test(segment)) continue;
|
|
191
|
+
if (opaque || !PATH_KEY_VOCABULARY.has(segment)) return false;
|
|
192
|
+
opaque ||= OPAQUE_SUBTREES.has(segment);
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Numeric range guard for `violationCodeUnit`: controls or surrogates only. */
|
|
198
|
+
export function isValidViolationCodeUnit(value: number): boolean {
|
|
199
|
+
if (!Number.isInteger(value) || Object.is(value, -0) || value < 0)
|
|
200
|
+
return false;
|
|
201
|
+
return (
|
|
202
|
+
value <= 0x1f ||
|
|
203
|
+
(value >= 0x7f && value <= 0x9f) ||
|
|
204
|
+
(value >= 0xd800 && value <= 0xdfff)
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Builds the safeDetails record for one text violation found at `path`.
|
|
210
|
+
* `inKey` distinguishes an offending object key from an offending value.
|
|
211
|
+
*/
|
|
212
|
+
export function violationDetails(
|
|
213
|
+
violation: TextViolation,
|
|
214
|
+
path: readonly ViolationPathSegment[],
|
|
215
|
+
textLength: number,
|
|
216
|
+
inKey: boolean,
|
|
217
|
+
): Readonly<Record<string, string | number | boolean>> {
|
|
218
|
+
return {
|
|
219
|
+
violationReason: violation.reason,
|
|
220
|
+
violationPath: formatViolationPath(path),
|
|
221
|
+
violationOffset: violation.offset,
|
|
222
|
+
violationCodeUnit: violation.codeUnit,
|
|
223
|
+
violationTextLength: textLength,
|
|
224
|
+
violationInKey: inKey,
|
|
225
|
+
};
|
|
226
|
+
}
|