@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.
@@ -1,6 +1,8 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-or-later
2
+ import { MAX_INPUT_SIZE } from "./limits.js";
2
3
  import { ClaudeCodeWireError } from "./contracts.js";
3
- import { classifySurrogateAt } from "./unicode.js";
4
+ import { inspectText, TEXT_POLICY_PROSE } from "./unicode.js";
5
+ import { violationDetails } from "./violation.js";
4
6
  /**
5
7
  * The pinned identity text, byte-exact.
6
8
  *
@@ -18,26 +20,15 @@ import { classifySurrogateAt } from "./unicode.js";
18
20
  */
19
21
  export const IDENTITY_TEXT = "You are Claude Code, Anthropic's official CLI for Claude.";
20
22
  const MAX_INPUT_DEPTH = 64;
21
- const MAX_INPUT_SIZE = 1_000_000;
22
- function fail(code) {
23
- throw new ClaudeCodeWireError(code);
23
+ function fail(code, safeDetails = {}) {
24
+ throw new ClaudeCodeWireError(code, safeDetails);
24
25
  }
25
- function validateText(text) {
26
- for (let index = 0; index < text.length; index += 1) {
27
- const codeUnit = text.charCodeAt(index);
28
- if (codeUnit === 0 ||
29
- (codeUnit < 0x20 &&
30
- codeUnit !== 0x09 &&
31
- codeUnit !== 0x0a &&
32
- codeUnit !== 0x0d) ||
33
- (codeUnit >= 0x7f && codeUnit <= 0x9f)) {
34
- fail("INVALID_UNICODE");
35
- }
36
- const classification = classifySurrogateAt(text, index);
37
- if (classification === "loneSurrogate")
38
- fail("INVALID_UNICODE");
39
- if (classification === "surrogatePair")
40
- index += 1;
26
+ function validateText(text, path, inKey) {
27
+ // Body prose policy (P1.T1): the system field follows the same rule as
28
+ // every other body lane — only lone surrogates are rejected.
29
+ const violation = inspectText(text, TEXT_POLICY_PROSE);
30
+ if (violation !== null) {
31
+ fail("INVALID_UNICODE", violationDetails(violation, path, text.length, inKey));
41
32
  }
42
33
  }
43
34
  function isUnknownRecord(value) {
@@ -46,6 +37,7 @@ function isUnknownRecord(value) {
46
37
  function validateStructure(value) {
47
38
  const ancestors = new WeakSet();
48
39
  let size = 0;
40
+ const path = ["system"];
49
41
  function visit(current, depth) {
50
42
  if (depth > MAX_INPUT_DEPTH)
51
43
  fail("INPUT_TOO_DEEP");
@@ -53,7 +45,7 @@ function validateStructure(value) {
53
45
  size += current.length;
54
46
  if (size > MAX_INPUT_SIZE)
55
47
  fail("INPUT_TOO_LARGE");
56
- validateText(current);
48
+ validateText(current, path, false);
57
49
  return;
58
50
  }
59
51
  if (!isUnknownRecord(current))
@@ -62,13 +54,22 @@ function validateStructure(value) {
62
54
  fail("CYCLIC_INPUT");
63
55
  ancestors.add(current);
64
56
  for (const key of Reflect.ownKeys(current)) {
57
+ // Only ARRAY indices become numeric segments (QA F1/F4); object keys
58
+ // stay strings and digits inside them render as `*`.
59
+ const segment = typeof key !== "string"
60
+ ? "*"
61
+ : Array.isArray(current) && /^(?:0|[1-9]\d{0,5})$/u.test(key)
62
+ ? Number(key)
63
+ : key;
64
+ path.push(segment);
65
65
  if (typeof key === "string") {
66
66
  size += key.length;
67
67
  if (size > MAX_INPUT_SIZE)
68
68
  fail("INPUT_TOO_LARGE");
69
- validateText(key);
69
+ validateText(key, path, true);
70
70
  }
71
71
  visit(current[key], depth + 1);
72
+ path.pop();
72
73
  }
73
74
  ancestors.delete(current);
74
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../src/system-prompt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../src/system-prompt.ts"],"names":[],"mappings":"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"}
package/dist/unicode.d.ts CHANGED
@@ -7,4 +7,45 @@ export type SurrogateClassification = "notSurrogate" | "surrogatePair" | "loneSu
7
7
  * a negated in-range test rather than an out-of-range test.
8
8
  */
9
9
  export declare function classifySurrogateAt(value: string, index: number): SurrogateClassification;
10
+ export type TextViolationReason = "control-char" | "lone-surrogate";
11
+ /**
12
+ * One character-level policy violation found while screening a string.
13
+ *
14
+ * `offset` is the UTF-16 code-unit index of the offending unit and `codeUnit`
15
+ * is the unit itself. Both are safe diagnostics: offsets are numbers and the
16
+ * only code units ever reported are control characters or surrogate halves,
17
+ * never prose content.
18
+ */
19
+ export interface TextViolation {
20
+ readonly reason: TextViolationReason;
21
+ readonly offset: number;
22
+ readonly codeUnit: number;
23
+ }
24
+ /**
25
+ * Character-level acceptance policy for one string lane.
26
+ *
27
+ * `rejectControls` rejects every C0 control except TAB (0x09), LF (0x0A) and
28
+ * CR (0x0D), plus DEL (0x7F). Lone surrogates are always rejected, rather than
29
+ * depending on whether a caller serializes or directly encodes the string.
30
+ */
31
+ export interface TextPolicy {
32
+ readonly rejectControls: boolean;
33
+ }
34
+ /** Body prose: every well-formed UTF-16 string is accepted. */
35
+ export declare const TEXT_POLICY_PROSE: TextPolicy;
36
+ /**
37
+ * Identifier graph screening: C0 except TAB/LF/CR, plus DEL, plus lone
38
+ * surrogates. This is exactly the set the graph inspectors rejected before the
39
+ * shared validator existed.
40
+ */
41
+ export declare const TEXT_POLICY_IDENTIFIER: TextPolicy;
42
+ /**
43
+ * Screens one string under `policy`, returning the first violation or null.
44
+ *
45
+ * The walk order matches the historical per-module inspectors: at each index
46
+ * the control check runs before the surrogate check, and a well-formed pair
47
+ * consumes two code units. `classifySurrogateAt` is the single surrogate
48
+ * authority; this function never re-implements it.
49
+ */
50
+ export declare function inspectText(value: string, policy: TextPolicy): TextViolation | null;
10
51
  //# sourceMappingURL=unicode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"unicode.d.ts","sourceRoot":"","sources":["../src/unicode.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,uBAAuB,GACjC,cAAc,GAAG,eAAe,GAAG,eAAe,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,uBAAuB,CAQzB"}
1
+ {"version":3,"file":"unicode.d.ts","sourceRoot":"","sources":["../src/unicode.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,uBAAuB,GACjC,cAAc,GAAG,eAAe,GAAG,eAAe,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,uBAAuB,CAQzB;AAED,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,gBAAgB,CAAC;AAEpE;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,EAAE,UAE9B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,UAEnC,CAAC;AAEH;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,UAAU,GACjB,aAAa,GAAG,IAAI,CAiBtB"}
package/dist/unicode.js CHANGED
@@ -16,4 +16,41 @@ export function classifySurrogateAt(value, index) {
16
16
  return "loneSurrogate";
17
17
  return "notSurrogate";
18
18
  }
19
+ /** Body prose: every well-formed UTF-16 string is accepted. */
20
+ export const TEXT_POLICY_PROSE = Object.freeze({
21
+ rejectControls: false,
22
+ });
23
+ /**
24
+ * Identifier graph screening: C0 except TAB/LF/CR, plus DEL, plus lone
25
+ * surrogates. This is exactly the set the graph inspectors rejected before the
26
+ * shared validator existed.
27
+ */
28
+ export const TEXT_POLICY_IDENTIFIER = Object.freeze({
29
+ rejectControls: true,
30
+ });
31
+ /**
32
+ * Screens one string under `policy`, returning the first violation or null.
33
+ *
34
+ * The walk order matches the historical per-module inspectors: at each index
35
+ * the control check runs before the surrogate check, and a well-formed pair
36
+ * consumes two code units. `classifySurrogateAt` is the single surrogate
37
+ * authority; this function never re-implements it.
38
+ */
39
+ export function inspectText(value, policy) {
40
+ for (let index = 0; index < value.length; index += 1) {
41
+ const unit = value.charCodeAt(index);
42
+ if (policy.rejectControls &&
43
+ ((unit <= 0x1f && unit !== 0x09 && unit !== 0x0a && unit !== 0x0d) ||
44
+ unit === 0x7f)) {
45
+ return { reason: "control-char", offset: index, codeUnit: unit };
46
+ }
47
+ const classification = classifySurrogateAt(value, index);
48
+ if (classification === "loneSurrogate") {
49
+ return { reason: "lone-surrogate", offset: index, codeUnit: unit };
50
+ }
51
+ if (classification === "surrogatePair")
52
+ index += 1;
53
+ }
54
+ return null;
55
+ }
19
56
  //# sourceMappingURL=unicode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"unicode.js","sourceRoot":"","sources":["../src/unicode.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAK5C;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAa,EACb,KAAa;IAEb,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC;IAC9E,CAAC;IACD,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM;QAAE,OAAO,eAAe,CAAC;IAC7D,OAAO,cAAc,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"unicode.js","sourceRoot":"","sources":["../src/unicode.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAK5C;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAa,EACb,KAAa;IAEb,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC;IAC9E,CAAC;IACD,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM;QAAE,OAAO,eAAe,CAAC;IAC7D,OAAO,cAAc,CAAC;AACxB,CAAC;AA6BD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,iBAAiB,GAAe,MAAM,CAAC,MAAM,CAAC;IACzD,cAAc,EAAE,KAAK;CACtB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAe,MAAM,CAAC,MAAM,CAAC;IAC9D,cAAc,EAAE,IAAI;CACrB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,KAAa,EACb,MAAkB;IAElB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACrC,IACE,MAAM,CAAC,cAAc;YACrB,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC;gBAChE,IAAI,KAAK,IAAI,CAAC,EAChB,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QACnE,CAAC;QACD,MAAM,cAAc,GAAG,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACzD,IAAI,cAAc,KAAK,eAAe,EAAE,CAAC;YACvC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QACrE,CAAC;QACD,IAAI,cAAc,KAAK,eAAe;YAAE,KAAK,IAAI,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { TextViolation } from "./unicode.js";
2
+ export declare const VIOLATION_REASONS: ReadonlySet<string>;
3
+ export type ViolationPathSegment = string | number;
4
+ /**
5
+ * Renders a pointer-like path (`/messages/0/content`) from walk segments.
6
+ * At most 16 segments, then a trailing `/...`; at most 256 characters.
7
+ */
8
+ export declare function formatViolationPath(segments: readonly ViolationPathSegment[]): string;
9
+ export declare function isValidViolationReason(value: string): boolean;
10
+ /**
11
+ * Revalidates a formatted violation path for the redaction allowlist. Every
12
+ * segment must be a vocabulary key, a decimal index of at most 6 digits, `*`,
13
+ * or the single trailing truncation marker; the whole string is capped.
14
+ */
15
+ export declare function isValidViolationPath(value: string): boolean;
16
+ /** Numeric range guard for `violationCodeUnit`: controls or surrogates only. */
17
+ export declare function isValidViolationCodeUnit(value: number): boolean;
18
+ /**
19
+ * Builds the safeDetails record for one text violation found at `path`.
20
+ * `inKey` distinguishes an offending object key from an offending value.
21
+ */
22
+ export declare function violationDetails(violation: TextViolation, path: readonly ViolationPathSegment[], textLength: number, inKey: boolean): Readonly<Record<string, string | number | boolean>>;
23
+ //# sourceMappingURL=violation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"violation.d.ts","sourceRoot":"","sources":["../src/violation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAYlD,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAGhD,CAAC;AA2GH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,CAAC;AAcnD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,GACxC,MAAM,CAgBR;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7D;AAID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAqB3D;AAED,gFAAgF;AAChF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQ/D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,oBAAoB,EAAE,EACrC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,GACb,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,CASrD"}
@@ -0,0 +1,210 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ /*
3
+ * Safe validation-failure diagnostics (decision P1.T1, D4).
4
+ *
5
+ * Every value produced here is safe by construction: reasons come from a
6
+ * closed set, path segments from a closed vocabulary of schema-defined keys
7
+ * plus numeric indices and the `*` placeholder for user-controlled keys, and
8
+ * reported code units only ever come from control or surrogate ranges. No
9
+ * caller text, key name or excerpt can leak through these fields.
10
+ */
11
+ export const VIOLATION_REASONS = new Set([
12
+ "lone-surrogate",
13
+ "control-char",
14
+ ]);
15
+ /**
16
+ * Schema-defined keys allowed verbatim in a violation path. Any other object
17
+ * key (tool input names, `input_schema` property names, arbitrary metadata
18
+ * members) is user-controlled text and is reported as `*`.
19
+ */
20
+ const PATH_KEY_VOCABULARY = new Set([
21
+ "$schema",
22
+ "accept",
23
+ "additionalBetas",
24
+ "additionalProperties",
25
+ "anthropicAdditionalProtection",
26
+ "app",
27
+ "arch",
28
+ "accountUuid",
29
+ "body",
30
+ "cache_control",
31
+ "citations",
32
+ "clientApp",
33
+ "clientRequestId",
34
+ "claudeRemoteContainerId",
35
+ "claudeRemoteSessionId",
36
+ "content",
37
+ "context",
38
+ "crypto",
39
+ "data",
40
+ "default",
41
+ "defer_loading",
42
+ "description",
43
+ "deviceId",
44
+ "document",
45
+ "effort",
46
+ "enum",
47
+ "evidence",
48
+ "examples",
49
+ "extraHeaderPolicy",
50
+ "extraHeaders",
51
+ "headers",
52
+ "id",
53
+ "image",
54
+ "input",
55
+ "input_schema",
56
+ "input_examples",
57
+ "items",
58
+ "max_tokens",
59
+ "maxTokens",
60
+ "media_type",
61
+ "messages",
62
+ "metadata",
63
+ "metadataOverrides",
64
+ "method",
65
+ "model",
66
+ "name",
67
+ "os",
68
+ "output_config",
69
+ "profileOverride",
70
+ "properties",
71
+ "redacted_thinking",
72
+ "required",
73
+ "role",
74
+ "runtime",
75
+ "runtimeVersion",
76
+ "search_results",
77
+ "sessionId",
78
+ "signature",
79
+ "source",
80
+ "stainlessHelper",
81
+ "stainlessRetryCount",
82
+ "stop_sequences",
83
+ "stream",
84
+ "strict",
85
+ "system",
86
+ "text",
87
+ "thinking",
88
+ "timing",
89
+ "title",
90
+ "tool_name",
91
+ "tool_reference",
92
+ "tool_result",
93
+ "tool_use",
94
+ "tool_use_id",
95
+ "tools",
96
+ "type",
97
+ "url",
98
+ "user_id",
99
+ "userId",
100
+ "userIdFields",
101
+ ]);
102
+ const MAX_PATH_SEGMENTS = 16;
103
+ const MAX_PATH_LENGTH = 256;
104
+ const TRUNCATION_MARKER = "...";
105
+ const OPAQUE_SUBTREES = new Set([
106
+ "headers",
107
+ "input",
108
+ "input_schema",
109
+ "input_examples",
110
+ "metadata",
111
+ "metadataOverrides",
112
+ "userIdFields",
113
+ "extraHeaders",
114
+ "data",
115
+ "default",
116
+ "examples",
117
+ ]);
118
+ function mapSegment(segment, opaque) {
119
+ if (typeof segment === "number") {
120
+ return Number.isSafeInteger(segment) &&
121
+ !Object.is(segment, -0) &&
122
+ segment >= 0 &&
123
+ segment <= 999_999
124
+ ? String(segment)
125
+ : "*";
126
+ }
127
+ return !opaque && PATH_KEY_VOCABULARY.has(segment) ? segment : "*";
128
+ }
129
+ /**
130
+ * Renders a pointer-like path (`/messages/0/content`) from walk segments.
131
+ * At most 16 segments, then a trailing `/...`; at most 256 characters.
132
+ */
133
+ export function formatViolationPath(segments) {
134
+ let path = "";
135
+ let opaque = false;
136
+ for (let index = 0; index < segments.length; index += 1) {
137
+ const segment = mapSegment(segments[index] ?? "*", opaque);
138
+ const tailLength = index < segments.length - 1 ? 4 : 0;
139
+ if (index >= MAX_PATH_SEGMENTS ||
140
+ path.length + 1 + segment.length + tailLength > MAX_PATH_LENGTH) {
141
+ return `${path}/${TRUNCATION_MARKER}`;
142
+ }
143
+ path += `/${segment}`;
144
+ opaque ||= segment === "*" || OPAQUE_SUBTREES.has(segment);
145
+ }
146
+ return path || "/";
147
+ }
148
+ export function isValidViolationReason(value) {
149
+ return VIOLATION_REASONS.has(value);
150
+ }
151
+ const PATH_INDEX_SEGMENT = /^(?:0|[1-9]\d{0,5})$/u;
152
+ /**
153
+ * Revalidates a formatted violation path for the redaction allowlist. Every
154
+ * segment must be a vocabulary key, a decimal index of at most 6 digits, `*`,
155
+ * or the single trailing truncation marker; the whole string is capped.
156
+ */
157
+ export function isValidViolationPath(value) {
158
+ if (value.length === 0 || value.length > MAX_PATH_LENGTH)
159
+ return false;
160
+ if (!value.startsWith("/"))
161
+ return false;
162
+ if (value === "/")
163
+ return true;
164
+ const segments = value.slice(1).split("/");
165
+ if (segments.length > MAX_PATH_SEGMENTS + 1)
166
+ return false;
167
+ let opaque = false;
168
+ for (let index = 0; index < segments.length; index += 1) {
169
+ const segment = segments[index];
170
+ if (segment === undefined)
171
+ return false;
172
+ if (segment === TRUNCATION_MARKER)
173
+ return index === segments.length - 1;
174
+ if (index >= MAX_PATH_SEGMENTS)
175
+ return false;
176
+ if (segment === "*") {
177
+ opaque = true;
178
+ continue;
179
+ }
180
+ if (PATH_INDEX_SEGMENT.test(segment))
181
+ continue;
182
+ if (opaque || !PATH_KEY_VOCABULARY.has(segment))
183
+ return false;
184
+ opaque ||= OPAQUE_SUBTREES.has(segment);
185
+ }
186
+ return true;
187
+ }
188
+ /** Numeric range guard for `violationCodeUnit`: controls or surrogates only. */
189
+ export function isValidViolationCodeUnit(value) {
190
+ if (!Number.isInteger(value) || Object.is(value, -0) || value < 0)
191
+ return false;
192
+ return (value <= 0x1f ||
193
+ (value >= 0x7f && value <= 0x9f) ||
194
+ (value >= 0xd800 && value <= 0xdfff));
195
+ }
196
+ /**
197
+ * Builds the safeDetails record for one text violation found at `path`.
198
+ * `inKey` distinguishes an offending object key from an offending value.
199
+ */
200
+ export function violationDetails(violation, path, textLength, inKey) {
201
+ return {
202
+ violationReason: violation.reason,
203
+ violationPath: formatViolationPath(path),
204
+ violationOffset: violation.offset,
205
+ violationCodeUnit: violation.codeUnit,
206
+ violationTextLength: textLength,
207
+ violationInKey: inKey,
208
+ };
209
+ }
210
+ //# sourceMappingURL=violation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"violation.js","sourceRoot":"","sources":["../src/violation.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tormentalabs/claude-code-wire-compat",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Runtime-neutral Claude Code Messages wire compatibility primitives",
5
5
  "type": "module",
6
6
  "private": false,
@@ -1,5 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-or-later
2
2
 
3
+ import { MAX_INPUT_SIZE } from "./limits.js";
4
+
3
5
  import { composeBetas, composeBetasWithAudit } from "./betas.js";
4
6
  import type {
5
7
  BuiltClaudeCodeCountTokensRequest,
@@ -40,11 +42,15 @@ import {
40
42
  import { sha256Hex } from "./sha256.js";
41
43
  import { buildCanonicalSystem, IDENTITY_TEXT } from "./system-prompt.js";
42
44
  import { isThinkingActive, isThinkingDisplayActive } from "./thinking.js";
43
- import { classifySurrogateAt } from "./unicode.js";
45
+ import {
46
+ inspectText,
47
+ TEXT_POLICY_IDENTIFIER,
48
+ TEXT_POLICY_PROSE,
49
+ } from "./unicode.js";
50
+ import { violationDetails, type ViolationPathSegment } from "./violation.js";
44
51
 
45
52
  const METHOD = "POST";
46
53
  const MAX_INPUT_DEPTH = 100;
47
- const MAX_INPUT_SIZE = 1_000_000;
48
54
  const FORBIDDEN_KEYS = new Set(["__proto__", "prototype", "constructor"]);
49
55
  const INPUT_KEYS = new Set([
50
56
  "accessToken",
@@ -200,8 +206,9 @@ type UnknownRecord = Readonly<Record<string, unknown>>;
200
206
 
201
207
  function fail(
202
208
  code: ConstructorParameters<typeof ClaudeCodeWireError>[0] = "INVALID_INPUT",
209
+ safeDetails: Readonly<Record<string, string | number | boolean>> = {},
203
210
  ): never {
204
- throw new ClaudeCodeWireError(code);
211
+ throw new ClaudeCodeWireError(code, safeDetails);
205
212
  }
206
213
 
207
214
  function isRecord(value: unknown): value is UnknownRecord {
@@ -231,39 +238,51 @@ function assertExactKeys(
231
238
  /**
232
239
  * Screens one string from the caller's input graph.
233
240
  *
234
- * TAB (0x09), LF (0x0A) and CR (0x0D) are ALLOWED. This function walks the
235
- * whole input graph, which is overwhelmingly BODY content — message text,
236
- * system blocks, tool descriptions — where a line break is ordinary prose that
237
- * `JSON.stringify` escapes on the way out. Rejecting them here made the package
238
- * unusable for real traffic: no genuine prompt is a single line.
239
- *
240
- * The strict rule those three characters used to be caught by is a HEADER rule,
241
- * and it still lives where it belongs and still applies in full:
242
- * `assertHeaderText` in `src/headers.ts` rejects every control character,
243
- * including these three, because a bare LF in a header is request smuggling.
244
- * `src/metadata.ts` is likewise unchanged: `user_id` and metadata keys are
245
- * identifiers that travel as JSON inside a header, not prose.
241
+ * BODY PROSE POLICY (decision P1.T1, 2026-09-25): every well-formed UTF-16
242
+ * string is accepted. Control characters are valid Unicode scalars;
243
+ * `JSON.stringify` escapes the C0 range and emits DEL/C1 raw, and
244
+ * `TextEncoder` encodes every scalar deterministically, so no control
245
+ * character can desync the body from its hash. Real tool output legitimately
246
+ * carries ESC (ANSI colour), NUL, FF and DEL; synthetic probes reproduced
247
+ * rejection before fetch, not the original incident's exact input. The old rule was a
248
+ * library-local defensive heuristic with no upstream provenance; whether the
249
+ * remote API rejects any scalar is a remote concern, surfaced as a remote
250
+ * error, not a local pre-flight abort.
246
251
  *
247
- * Every other C0 control (0x00-0x08, 0x0B, 0x0C, 0x0E-0x1F) and DEL (0x7F)
248
- * stay rejected: they have no meaning in prompt text and are a reliable signal
249
- * of a corrupted or hostile input.
252
+ * What did NOT move:
250
253
  *
251
- * LONE SURROGATES stay rejected in every context, deliberately. `TextEncoder`
252
- * silently replaces them with U+FFFD, so an unpaired surrogate would corrupt
253
- * the body — and the body hash recorded in evidence — with no error anywhere.
254
+ * - HEADERS: `assertHeaderText` in `src/headers.ts` still rejects every
255
+ * control character, TAB/LF/CR included, because a bare LF in a header is
256
+ * request smuggling. `src/metadata.ts` is likewise unchanged: `user_id` and
257
+ * metadata keys are identifiers that travel as JSON inside a header, not
258
+ * prose, and keep their own strict rule.
259
+ * - LONE SURROGATES stay rejected in every context, deliberately.
260
+ * Raw `TextEncoder` replaces them, whereas JSON serialization escapes them.
261
+ * Rejecting them keeps the text contract independent of serialization order.
254
262
  */
255
- function inspectString(value: string): number {
256
- for (let index = 0; index < value.length; index += 1) {
257
- const unit = value.charCodeAt(index);
258
- if (
259
- (unit <= 0x1f && unit !== 0x09 && unit !== 0x0a && unit !== 0x0d) ||
260
- unit === 0x7f
261
- ) {
262
- fail("INVALID_UNICODE");
263
- }
264
- const classification = classifySurrogateAt(value, index);
265
- if (classification === "loneSurrogate") fail("INVALID_UNICODE");
266
- if (classification === "surrogatePair") index += 1;
263
+ function inspectString(
264
+ value: string,
265
+ path: readonly ViolationPathSegment[],
266
+ inKey: boolean,
267
+ ): number {
268
+ const root = path[0];
269
+ const prose =
270
+ root === "messages" ||
271
+ root === "system" ||
272
+ root === "tools" ||
273
+ root === "stopSequences" ||
274
+ root === "stop_sequences" ||
275
+ root === "body";
276
+ const violation = inspectText(
277
+ value,
278
+ prose ? TEXT_POLICY_PROSE : TEXT_POLICY_IDENTIFIER,
279
+ );
280
+ if (violation !== null) {
281
+ if (violation.reason === "control-char") fail("INVALID_UNICODE");
282
+ fail(
283
+ "INVALID_UNICODE",
284
+ violationDetails(violation, path, value.length, inKey),
285
+ );
267
286
  }
268
287
  return new TextEncoder().encode(value).byteLength;
269
288
  }
@@ -271,11 +290,14 @@ function inspectString(value: string): number {
271
290
  function inspectGraph(value: unknown): void {
272
291
  const active = new WeakSet();
273
292
  let size = 0;
293
+ // Mutable walk stack: pushed/popped per node and only READ (synchronously)
294
+ // when a failure renders it, so successful requests pay no per-node copy.
295
+ const path: ViolationPathSegment[] = [];
274
296
 
275
297
  function visit(current: unknown, depth: number): void {
276
298
  if (depth > MAX_INPUT_DEPTH) fail("INPUT_TOO_DEEP");
277
299
  if (typeof current === "string") {
278
- size += inspectString(current);
300
+ size += inspectString(current, path, false);
279
301
  } else if (
280
302
  current === null ||
281
303
  typeof current === "boolean" ||
@@ -300,8 +322,16 @@ function inspectGraph(value: unknown): void {
300
322
  size += keys.length;
301
323
  for (const key of keys) {
302
324
  if (typeof key !== "string" || FORBIDDEN_KEYS.has(key)) fail();
303
- size += inspectString(key);
325
+ // Only ARRAY indices become numeric segments; digits inside an object
326
+ // key are user-controlled text and must stay masked (QA F1).
327
+ const segment: ViolationPathSegment =
328
+ Array.isArray(current) && /^(?:0|[1-9]\d{0,5})$/u.test(key)
329
+ ? Number(key)
330
+ : key;
331
+ path.push(segment);
332
+ size += inspectString(key, path, true);
304
333
  visit(ownValue(current, key), depth + 1);
334
+ path.pop();
305
335
  }
306
336
  active.delete(current);
307
337
  }
@@ -1763,6 +1793,7 @@ export function parseBuiltClaudeCodeRequest(
1763
1793
  const body = ownValue(value, "body");
1764
1794
  if (typeof body !== "string") fail();
1765
1795
  const parsedBody = parseBody(body);
1796
+ inspectGraph(parsedBody);
1766
1797
  const headers = parseHeaders(ownValue(value, "headers"));
1767
1798
  const evidence = parseEvidence(ownValue(value, "evidence"), pinnedProfile);
1768
1799
  // Reading evidence is not trusting evidence. A claim that the seam
package/src/limits.ts ADDED
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+ /** Aggregate graph budget, not a serialized request-size guarantee. */
4
+ export const MAX_INPUT_SIZE = 1_000_000;