@tormentalabs/claude-code-wire-compat 0.5.0 → 0.7.0-rc.1
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 +226 -0
- package/README.md +19 -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 +93 -50
- 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 +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.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/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 +19 -1
- package/dist/model-identity.js.map +1 -1
- package/dist/model-queries.d.ts +17 -5
- package/dist/model-queries.d.ts.map +1 -1
- package/dist/model-queries.js +33 -6
- package/dist/model-queries.js.map +1 -1
- 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 +48 -2
- package/dist/redaction.js.map +1 -1
- package/dist/request-body.d.ts +4 -2
- package/dist/request-body.d.ts.map +1 -1
- package/dist/request-body.js +77 -48
- 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/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/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 +6 -2
- package/src/betas.ts +208 -9
- package/src/build-request.ts +120 -56
- package/src/contracts.ts +7 -2
- package/src/fingerprint.ts +5 -0
- package/src/headers.ts +4 -2
- package/src/index.ts +2 -0
- package/src/limits.ts +4 -0
- package/src/model-capabilities.ts +55 -19
- package/src/model-identity.ts +14 -1
- package/src/model-queries.ts +38 -6
- 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 +65 -2
- package/src/request-body.ts +107 -49
- package/src/system-prompt.ts +32 -24
- package/src/thinking.ts +148 -26
- package/src/unicode.ts +71 -0
- package/src/violation.ts +226 -0
package/src/request-body.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
2
|
|
|
3
|
+
import { MAX_INPUT_SIZE as MAX_SIZE } from "./limits.js";
|
|
4
|
+
|
|
3
5
|
import { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
4
6
|
import { clampMaxTokens, resolveThinking } from "./thinking.js";
|
|
5
7
|
import type { ThinkingDisplay, ThinkingRequest } from "./thinking.js";
|
|
@@ -29,11 +31,15 @@ import type {
|
|
|
29
31
|
import { deriveCapabilities } from "./model-capabilities.js";
|
|
30
32
|
import { stripModelMarkers } from "./model-identity.js";
|
|
31
33
|
import { IDENTITY_TEXT } from "./system-prompt.js";
|
|
32
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
inspectText,
|
|
36
|
+
TEXT_POLICY_IDENTIFIER,
|
|
37
|
+
TEXT_POLICY_PROSE,
|
|
38
|
+
} from "./unicode.js";
|
|
39
|
+
import { violationDetails, type ViolationPathSegment } from "./violation.js";
|
|
33
40
|
|
|
34
41
|
const MAX_DEPTH = 100;
|
|
35
42
|
const MAX_ITEMS = 100_000;
|
|
36
|
-
const MAX_SIZE = 1_000_000;
|
|
37
43
|
const FORBIDDEN_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
38
44
|
const MESSAGE_KEYS = new Set(["role", "content"]);
|
|
39
45
|
const CACHE_CONTROL_KEYS = new Set(["type", "ttl"]);
|
|
@@ -357,13 +363,16 @@ type ModelResolution = Readonly<{
|
|
|
357
363
|
contextManagement: boolean;
|
|
358
364
|
temperature: boolean;
|
|
359
365
|
rejectsDisabledThinking: boolean;
|
|
366
|
+
midConvToolChange: boolean;
|
|
367
|
+
perTurnEffort: boolean;
|
|
360
368
|
}>;
|
|
361
369
|
}>;
|
|
362
370
|
|
|
363
371
|
function fail(
|
|
364
372
|
code: ConstructorParameters<typeof ClaudeCodeWireError>[0],
|
|
373
|
+
safeDetails: Readonly<Record<string, string | number | boolean>> = {},
|
|
365
374
|
): never {
|
|
366
|
-
throw new ClaudeCodeWireError(code);
|
|
375
|
+
throw new ClaudeCodeWireError(code, safeDetails);
|
|
367
376
|
}
|
|
368
377
|
|
|
369
378
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -378,25 +387,21 @@ function inspectString(
|
|
|
378
387
|
value: string,
|
|
379
388
|
state: InspectionState,
|
|
380
389
|
validateString?: (value: string) => void,
|
|
390
|
+
path: readonly ViolationPathSegment[] = [],
|
|
391
|
+
inKey = false,
|
|
381
392
|
): void {
|
|
382
393
|
state.size += value.length;
|
|
383
394
|
if (state.size > MAX_SIZE) fail("INPUT_TOO_LARGE");
|
|
384
395
|
validateString?.(value);
|
|
385
396
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
(
|
|
393
|
-
|
|
394
|
-
) {
|
|
395
|
-
fail("INVALID_INPUT");
|
|
396
|
-
}
|
|
397
|
-
const classification = classifySurrogateAt(value, index);
|
|
398
|
-
if (classification === "loneSurrogate") fail("INVALID_UNICODE");
|
|
399
|
-
if (classification === "surrogatePair") index += 1;
|
|
397
|
+
// Body prose policy (P1.T1): only a lone surrogate can fail here now, so
|
|
398
|
+
// the historical INVALID_INPUT-for-controls branch is gone by construction.
|
|
399
|
+
const violation = inspectText(value, TEXT_POLICY_PROSE);
|
|
400
|
+
if (violation !== null) {
|
|
401
|
+
fail(
|
|
402
|
+
"INVALID_UNICODE",
|
|
403
|
+
violationDetails(violation, path, value.length, inKey),
|
|
404
|
+
);
|
|
400
405
|
}
|
|
401
406
|
}
|
|
402
407
|
|
|
@@ -405,11 +410,12 @@ function inspect(
|
|
|
405
410
|
depth: number,
|
|
406
411
|
state: InspectionState,
|
|
407
412
|
validateString?: (value: string) => void,
|
|
413
|
+
path: ViolationPathSegment[] = [],
|
|
408
414
|
): void {
|
|
409
415
|
if (depth > MAX_DEPTH) fail("INPUT_TOO_DEEP");
|
|
410
416
|
if (value === null || typeof value === "boolean") return;
|
|
411
417
|
if (typeof value === "string") {
|
|
412
|
-
inspectString(value, state, validateString);
|
|
418
|
+
inspectString(value, state, validateString, path, false);
|
|
413
419
|
return;
|
|
414
420
|
}
|
|
415
421
|
if (typeof value === "number") {
|
|
@@ -428,7 +434,9 @@ function inspect(
|
|
|
428
434
|
if (state.size > MAX_SIZE) fail("INPUT_TOO_LARGE");
|
|
429
435
|
for (let index = 0; index < value.length; index += 1) {
|
|
430
436
|
if (!hasOwn(value, String(index))) fail("INVALID_INPUT");
|
|
431
|
-
|
|
437
|
+
path.push(index);
|
|
438
|
+
inspect(value[index], depth + 1, state, validateString, path);
|
|
439
|
+
path.pop();
|
|
432
440
|
}
|
|
433
441
|
} else {
|
|
434
442
|
const prototype = Reflect.getPrototypeOf(value);
|
|
@@ -439,27 +447,37 @@ function inspect(
|
|
|
439
447
|
if (typeof key !== "string" || FORBIDDEN_KEYS.has(key)) {
|
|
440
448
|
fail("INVALID_INPUT");
|
|
441
449
|
}
|
|
442
|
-
|
|
450
|
+
// Object keys stay strings: digits inside user keys must not become
|
|
451
|
+
// readable numeric path segments (QA F1).
|
|
452
|
+
path.push(key);
|
|
453
|
+
inspectString(key, state, validateString, path, true);
|
|
443
454
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
444
455
|
if (descriptor === undefined || !("value" in descriptor)) {
|
|
445
456
|
fail("INVALID_INPUT");
|
|
446
457
|
}
|
|
447
|
-
inspect(descriptor.value, depth + 1, state, validateString);
|
|
458
|
+
inspect(descriptor.value, depth + 1, state, validateString, path);
|
|
459
|
+
path.pop();
|
|
448
460
|
}
|
|
449
461
|
}
|
|
450
462
|
state.active.delete(value);
|
|
451
463
|
}
|
|
452
464
|
|
|
465
|
+
/** Paths without a caller hint are relative to the inspected value's root. */
|
|
453
466
|
export function inspectJsonInputs(
|
|
454
467
|
values: readonly unknown[],
|
|
455
468
|
validateString?: (value: string) => void,
|
|
469
|
+
paths?: readonly (readonly ViolationPathSegment[])[],
|
|
456
470
|
): void {
|
|
457
471
|
const state: InspectionState = {
|
|
458
472
|
active: new WeakSet(),
|
|
459
473
|
items: 0,
|
|
460
474
|
size: 0,
|
|
461
475
|
};
|
|
462
|
-
for (
|
|
476
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
477
|
+
inspect(values[index], 0, state, validateString, [
|
|
478
|
+
...(paths?.[index] ?? []),
|
|
479
|
+
]);
|
|
480
|
+
}
|
|
463
481
|
}
|
|
464
482
|
|
|
465
483
|
function requireRecord(value: unknown): Record<string, unknown> {
|
|
@@ -481,6 +499,13 @@ function requireString(value: unknown): string {
|
|
|
481
499
|
return value;
|
|
482
500
|
}
|
|
483
501
|
|
|
502
|
+
function requireIdentifier(value: unknown): string {
|
|
503
|
+
const text = requireString(value);
|
|
504
|
+
if (inspectText(text, TEXT_POLICY_IDENTIFIER) !== null)
|
|
505
|
+
fail("INVALID_UNICODE");
|
|
506
|
+
return text;
|
|
507
|
+
}
|
|
508
|
+
|
|
484
509
|
function requirePositiveInteger(value: unknown): number {
|
|
485
510
|
if (!Number.isSafeInteger(value) || typeof value !== "number" || value <= 0) {
|
|
486
511
|
fail("INVALID_INPUT");
|
|
@@ -827,7 +852,7 @@ function imageSource(value: unknown): ImageBlock["source"] {
|
|
|
827
852
|
)
|
|
828
853
|
fail("INVALID_INPUT");
|
|
829
854
|
entries.push([key, item]);
|
|
830
|
-
} else entries.push([key,
|
|
855
|
+
} else entries.push([key, requireIdentifier(item)]);
|
|
831
856
|
}
|
|
832
857
|
return Object.fromEntries(entries) as unknown as ImageBlock["source"];
|
|
833
858
|
}
|
|
@@ -888,7 +913,11 @@ function documentSource(value: unknown): DocumentBlock["source"] {
|
|
|
888
913
|
}),
|
|
889
914
|
]);
|
|
890
915
|
}
|
|
891
|
-
} else
|
|
916
|
+
} else
|
|
917
|
+
entries.push([
|
|
918
|
+
key,
|
|
919
|
+
type === "text" ? requireString(item) : requireIdentifier(item),
|
|
920
|
+
]);
|
|
892
921
|
}
|
|
893
922
|
return Object.fromEntries(entries) as unknown as DocumentBlock["source"];
|
|
894
923
|
}
|
|
@@ -923,7 +952,11 @@ function thinkingBlock(
|
|
|
923
952
|
if (key === "type") entries.push([key, "thinking"]);
|
|
924
953
|
else if (key === "cache_control")
|
|
925
954
|
entries.push([key, nullable(item, cacheControl)]);
|
|
926
|
-
else
|
|
955
|
+
else
|
|
956
|
+
entries.push([
|
|
957
|
+
key,
|
|
958
|
+
key === "signature" ? requireIdentifier(item) : requireString(item),
|
|
959
|
+
]);
|
|
927
960
|
}
|
|
928
961
|
return Object.fromEntries(entries) as unknown as ThinkingBlock;
|
|
929
962
|
}
|
|
@@ -948,7 +981,7 @@ function redactedThinkingBlock(
|
|
|
948
981
|
if (key === "type") entries.push([key, "redacted_thinking"]);
|
|
949
982
|
else if (key === "cache_control")
|
|
950
983
|
entries.push([key, nullable(item, cacheControl)]);
|
|
951
|
-
else entries.push([key,
|
|
984
|
+
else entries.push([key, requireIdentifier(item)]);
|
|
952
985
|
}
|
|
953
986
|
return Object.fromEntries(entries) as unknown as RedactedThinkingBlock;
|
|
954
987
|
}
|
|
@@ -964,8 +997,8 @@ function searchResultBlock(value: unknown): SearchResultBlock {
|
|
|
964
997
|
if (key === "content") {
|
|
965
998
|
if (!Array.isArray(item)) fail("INVALID_INPUT");
|
|
966
999
|
entries.push([key, item.map((block) => textBlock(block))]);
|
|
967
|
-
} else if (key === "source"
|
|
968
|
-
|
|
1000
|
+
} else if (key === "source") entries.push([key, requireIdentifier(item)]);
|
|
1001
|
+
else if (key === "title") entries.push([key, requireString(item)]);
|
|
969
1002
|
else if (key === "type") entries.push([key, "search_result"]);
|
|
970
1003
|
else if (key === "cache_control")
|
|
971
1004
|
entries.push([key, nullable(item, cacheControl)]);
|
|
@@ -982,7 +1015,7 @@ function toolReferenceBlock(value: unknown): ToolReferenceBlock {
|
|
|
982
1015
|
const entries: [string, unknown][] = [];
|
|
983
1016
|
for (const key of Object.keys(record)) {
|
|
984
1017
|
const item = record[key];
|
|
985
|
-
if (key === "tool_name") entries.push([key,
|
|
1018
|
+
if (key === "tool_name") entries.push([key, requireIdentifier(item)]);
|
|
986
1019
|
else if (key === "type") entries.push([key, "tool_reference"]);
|
|
987
1020
|
else entries.push([key, nullable(item, cacheControl)]);
|
|
988
1021
|
}
|
|
@@ -998,7 +1031,7 @@ function toolUseBlock(value: unknown): ToolUseBlock {
|
|
|
998
1031
|
for (const key of Object.keys(record)) {
|
|
999
1032
|
const item = record[key];
|
|
1000
1033
|
if (key === "id" || key === "name")
|
|
1001
|
-
entries.push([key,
|
|
1034
|
+
entries.push([key, requireIdentifier(item)]);
|
|
1002
1035
|
else if (key === "input") entries.push([key, validatedJson(item)]);
|
|
1003
1036
|
else if (key === "type") entries.push([key, "tool_use"]);
|
|
1004
1037
|
else if (key === "cache_control")
|
|
@@ -1023,7 +1056,7 @@ function toolCaller(value: unknown): ToolUseBlock["caller"] {
|
|
|
1023
1056
|
return Object.fromEntries(
|
|
1024
1057
|
Object.keys(record).map((key) => [
|
|
1025
1058
|
key,
|
|
1026
|
-
key === "type" ? type :
|
|
1059
|
+
key === "type" ? type : requireIdentifier(record[key]),
|
|
1027
1060
|
]),
|
|
1028
1061
|
) as unknown as NonNullable<ToolUseBlock["caller"]>;
|
|
1029
1062
|
}
|
|
@@ -1046,7 +1079,7 @@ function toolResultBlock(value: unknown): ToolResultBlock {
|
|
|
1046
1079
|
const entries: [string, unknown][] = [];
|
|
1047
1080
|
for (const key of Object.keys(record)) {
|
|
1048
1081
|
const item = record[key];
|
|
1049
|
-
if (key === "tool_use_id") entries.push([key,
|
|
1082
|
+
if (key === "tool_use_id") entries.push([key, requireIdentifier(item)]);
|
|
1050
1083
|
else if (key === "type") entries.push([key, "tool_result"]);
|
|
1051
1084
|
else if (key === "cache_control")
|
|
1052
1085
|
entries.push([key, nullable(item, cacheControl)]);
|
|
@@ -1325,8 +1358,8 @@ function customToolDefinition(record: Record<string, unknown>): ToolDefinition {
|
|
|
1325
1358
|
for (const key of Object.keys(record)) {
|
|
1326
1359
|
const item = record[key];
|
|
1327
1360
|
if (key === "input_schema") entries.push([key, toolInputSchema(item)]);
|
|
1328
|
-
else if (key === "name"
|
|
1329
|
-
|
|
1361
|
+
else if (key === "name") entries.push([key, requireIdentifier(item)]);
|
|
1362
|
+
else if (key === "description") entries.push([key, requireString(item)]);
|
|
1330
1363
|
else if (key === "allowed_callers")
|
|
1331
1364
|
entries.push([key, allowedCallers(item)]);
|
|
1332
1365
|
else if (key === "cache_control")
|
|
@@ -1355,7 +1388,7 @@ function builtInToolDefinition(
|
|
|
1355
1388
|
if (item !== spec.name) fail("INVALID_INPUT");
|
|
1356
1389
|
entries.push([key, item]);
|
|
1357
1390
|
} else if (key === "mcp_server_name" || key === "model")
|
|
1358
|
-
entries.push([key,
|
|
1391
|
+
entries.push([key, requireIdentifier(item)]);
|
|
1359
1392
|
else if (key === "allowed_callers")
|
|
1360
1393
|
entries.push([key, allowedCallers(item)]);
|
|
1361
1394
|
else if (key === "cache_control" || key === "caching")
|
|
@@ -1399,7 +1432,7 @@ function tools(value: unknown): readonly ToolDefinition[] {
|
|
|
1399
1432
|
? builtInToolDefinition(record)
|
|
1400
1433
|
: customToolDefinition(record);
|
|
1401
1434
|
if (hasOwn(record, "name")) {
|
|
1402
|
-
const name =
|
|
1435
|
+
const name = requireIdentifier(record["name"]);
|
|
1403
1436
|
if (names.has(name)) fail("INVALID_INPUT");
|
|
1404
1437
|
names.add(name);
|
|
1405
1438
|
}
|
|
@@ -1451,13 +1484,17 @@ function modelResolution(
|
|
|
1451
1484
|
(capabilities.temperature !== undefined &&
|
|
1452
1485
|
typeof capabilities.temperature !== "boolean") ||
|
|
1453
1486
|
(capabilities.rejectsDisabledThinking !== undefined &&
|
|
1454
|
-
typeof capabilities.rejectsDisabledThinking !== "boolean")
|
|
1487
|
+
typeof capabilities.rejectsDisabledThinking !== "boolean") ||
|
|
1488
|
+
(capabilities.midConvToolChange !== undefined &&
|
|
1489
|
+
typeof capabilities.midConvToolChange !== "boolean") ||
|
|
1490
|
+
(capabilities.perTurnEffort !== undefined &&
|
|
1491
|
+
typeof capabilities.perTurnEffort !== "boolean")
|
|
1455
1492
|
) {
|
|
1456
1493
|
fail("INVALID_INPUT");
|
|
1457
1494
|
}
|
|
1458
1495
|
return {
|
|
1459
|
-
id:
|
|
1460
|
-
wireId:
|
|
1496
|
+
id: requireIdentifier(record["id"]),
|
|
1497
|
+
wireId: requireIdentifier(record["wireId"]),
|
|
1461
1498
|
capabilities: {
|
|
1462
1499
|
thinking: capabilityBoolean(capabilities.thinking, derived.thinking),
|
|
1463
1500
|
adaptiveThinking: capabilityBoolean(
|
|
@@ -1486,6 +1523,14 @@ function modelResolution(
|
|
|
1486
1523
|
capabilities.rejectsDisabledThinking,
|
|
1487
1524
|
derived.rejectsDisabledThinking,
|
|
1488
1525
|
),
|
|
1526
|
+
midConvToolChange: capabilityBoolean(
|
|
1527
|
+
capabilities.midConvToolChange,
|
|
1528
|
+
derived.midConvToolChange,
|
|
1529
|
+
),
|
|
1530
|
+
perTurnEffort: capabilityBoolean(
|
|
1531
|
+
capabilities.perTurnEffort,
|
|
1532
|
+
derived.perTurnEffort,
|
|
1533
|
+
),
|
|
1489
1534
|
},
|
|
1490
1535
|
};
|
|
1491
1536
|
}
|
|
@@ -1633,7 +1678,8 @@ function toolChoice(value: unknown): Readonly<Record<string, unknown>> {
|
|
|
1633
1678
|
const entries: [string, unknown][] = [];
|
|
1634
1679
|
for (const key of Object.keys(record)) {
|
|
1635
1680
|
if (key === "type") entries.push([key, type]);
|
|
1636
|
-
else if (key === "name")
|
|
1681
|
+
else if (key === "name")
|
|
1682
|
+
entries.push([key, requireIdentifier(record[key])]);
|
|
1637
1683
|
else entries.push([key, requireBoolean(record[key])]);
|
|
1638
1684
|
}
|
|
1639
1685
|
return Object.fromEntries(entries);
|
|
@@ -1731,14 +1777,19 @@ export function buildCanonicalBody(
|
|
|
1731
1777
|
rawSystemBlocks: unknown,
|
|
1732
1778
|
rawMetadata: unknown,
|
|
1733
1779
|
profile?: ClaudeCodeProtocolProfile,
|
|
1780
|
+
thinkingDisplayOverride?: "updates",
|
|
1734
1781
|
): Readonly<Record<string, unknown>> {
|
|
1735
|
-
inspectJsonInputs(
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1782
|
+
inspectJsonInputs(
|
|
1783
|
+
[
|
|
1784
|
+
rawInput,
|
|
1785
|
+
rawResolvedModel,
|
|
1786
|
+
rawSystemBlocks,
|
|
1787
|
+
rawMetadata,
|
|
1788
|
+
...(profile === undefined ? [] : [profile]),
|
|
1789
|
+
],
|
|
1790
|
+
undefined,
|
|
1791
|
+
[[], ["model"], ["system"], ["metadata"], ["profileOverride"]],
|
|
1792
|
+
);
|
|
1742
1793
|
|
|
1743
1794
|
const input = requireRecord(rawInput);
|
|
1744
1795
|
assertExactKeys(input, INPUT_KEY_SET);
|
|
@@ -1837,6 +1888,7 @@ export function buildCanonicalBody(
|
|
|
1837
1888
|
effectiveProfile.betaPolicy,
|
|
1838
1889
|
maxTokens,
|
|
1839
1890
|
effectiveProfile,
|
|
1891
|
+
thinkingDisplayOverride,
|
|
1840
1892
|
);
|
|
1841
1893
|
if (resolved.emitted !== undefined) result["thinking"] = resolved.emitted;
|
|
1842
1894
|
|
|
@@ -1902,9 +1954,15 @@ export function buildCanonicalBody(
|
|
|
1902
1954
|
result["output_format"] = nullable(item, outputFormat);
|
|
1903
1955
|
else if (key === "toolChoice") {
|
|
1904
1956
|
const validatedToolChoice = toolChoice(item);
|
|
1957
|
+
// Upstream demotes only `tool` here. This package deliberately also
|
|
1958
|
+
// demotes `any`: the Messages API rejects every forced tool choice while
|
|
1959
|
+
// extended thinking is on, so passing `any` through could only produce
|
|
1960
|
+
// an HTTP 400. Shared by every profile; see `MEMORY.md`, 2026-09-24.
|
|
1961
|
+
const forcedToolChoice =
|
|
1962
|
+
validatedToolChoice["type"] === "tool" ||
|
|
1963
|
+
validatedToolChoice["type"] === "any";
|
|
1905
1964
|
result["tool_choice"] =
|
|
1906
|
-
|
|
1907
|
-
resolved.extendedThinkingActive
|
|
1965
|
+
forcedToolChoice && resolved.extendedThinkingActive
|
|
1908
1966
|
? { type: "auto" }
|
|
1909
1967
|
: validatedToolChoice;
|
|
1910
1968
|
} else if (key === "topP") result["top_p"] = requireNumber(item);
|
package/src/system-prompt.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
2
|
|
|
3
|
+
import { MAX_INPUT_SIZE } from "./limits.js";
|
|
4
|
+
|
|
3
5
|
import type {
|
|
4
6
|
ClaudeCodeRuntimeIdentity,
|
|
5
7
|
SystemInput,
|
|
6
8
|
TextBlock,
|
|
7
9
|
} from "./contracts.js";
|
|
8
10
|
import { ClaudeCodeWireError } from "./contracts.js";
|
|
9
|
-
import {
|
|
11
|
+
import { inspectText, TEXT_POLICY_PROSE } from "./unicode.js";
|
|
12
|
+
import { violationDetails, type ViolationPathSegment } from "./violation.js";
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* The pinned identity text, byte-exact.
|
|
@@ -26,7 +29,6 @@ import { classifySurrogateAt } from "./unicode.js";
|
|
|
26
29
|
export const IDENTITY_TEXT =
|
|
27
30
|
"You are Claude Code, Anthropic's official CLI for Claude.";
|
|
28
31
|
const MAX_INPUT_DEPTH = 64;
|
|
29
|
-
const MAX_INPUT_SIZE = 1_000_000;
|
|
30
32
|
type UnknownRecord = Readonly<Record<PropertyKey, unknown>>;
|
|
31
33
|
|
|
32
34
|
function fail(
|
|
@@ -36,38 +38,34 @@ function fail(
|
|
|
36
38
|
| "INPUT_TOO_DEEP"
|
|
37
39
|
| "INPUT_TOO_LARGE"
|
|
38
40
|
| "CYCLIC_INPUT",
|
|
41
|
+
safeDetails: Readonly<Record<string, string | number | boolean>> = {},
|
|
39
42
|
): never {
|
|
40
|
-
throw new ClaudeCodeWireError(code);
|
|
43
|
+
throw new ClaudeCodeWireError(code, safeDetails);
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
function validateText(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const classification = classifySurrogateAt(text, index);
|
|
59
|
-
if (classification === "loneSurrogate") fail("INVALID_UNICODE");
|
|
60
|
-
if (classification === "surrogatePair") index += 1;
|
|
46
|
+
function validateText(
|
|
47
|
+
text: string,
|
|
48
|
+
path: readonly ViolationPathSegment[],
|
|
49
|
+
inKey: boolean,
|
|
50
|
+
): void {
|
|
51
|
+
// Body prose policy (P1.T1): the system field follows the same rule as
|
|
52
|
+
// every other body lane — only lone surrogates are rejected.
|
|
53
|
+
const violation = inspectText(text, TEXT_POLICY_PROSE);
|
|
54
|
+
if (violation !== null) {
|
|
55
|
+
fail(
|
|
56
|
+
"INVALID_UNICODE",
|
|
57
|
+
violationDetails(violation, path, text.length, inKey),
|
|
58
|
+
);
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
function isUnknownRecord(value: unknown): value is UnknownRecord {
|
|
65
63
|
return value !== null && typeof value === "object";
|
|
66
64
|
}
|
|
67
|
-
|
|
68
65
|
function validateStructure(value: unknown): void {
|
|
69
66
|
const ancestors = new WeakSet();
|
|
70
67
|
let size = 0;
|
|
68
|
+
const path: ViolationPathSegment[] = ["system"];
|
|
71
69
|
|
|
72
70
|
function visit(current: unknown, depth: number): void {
|
|
73
71
|
if (depth > MAX_INPUT_DEPTH) fail("INPUT_TOO_DEEP");
|
|
@@ -75,7 +73,7 @@ function validateStructure(value: unknown): void {
|
|
|
75
73
|
if (typeof current === "string") {
|
|
76
74
|
size += current.length;
|
|
77
75
|
if (size > MAX_INPUT_SIZE) fail("INPUT_TOO_LARGE");
|
|
78
|
-
validateText(current);
|
|
76
|
+
validateText(current, path, false);
|
|
79
77
|
return;
|
|
80
78
|
}
|
|
81
79
|
|
|
@@ -84,12 +82,22 @@ function validateStructure(value: unknown): void {
|
|
|
84
82
|
|
|
85
83
|
ancestors.add(current);
|
|
86
84
|
for (const key of Reflect.ownKeys(current)) {
|
|
85
|
+
// Only ARRAY indices become numeric segments (QA F1/F4); object keys
|
|
86
|
+
// stay strings and digits inside them render as `*`.
|
|
87
|
+
const segment: ViolationPathSegment =
|
|
88
|
+
typeof key !== "string"
|
|
89
|
+
? "*"
|
|
90
|
+
: Array.isArray(current) && /^(?:0|[1-9]\d{0,5})$/u.test(key)
|
|
91
|
+
? Number(key)
|
|
92
|
+
: key;
|
|
93
|
+
path.push(segment);
|
|
87
94
|
if (typeof key === "string") {
|
|
88
95
|
size += key.length;
|
|
89
96
|
if (size > MAX_INPUT_SIZE) fail("INPUT_TOO_LARGE");
|
|
90
|
-
validateText(key);
|
|
97
|
+
validateText(key, path, true);
|
|
91
98
|
}
|
|
92
99
|
visit(current[key], depth + 1);
|
|
100
|
+
path.pop();
|
|
93
101
|
}
|
|
94
102
|
ancestors.delete(current);
|
|
95
103
|
}
|