@tormentalabs/claude-code-wire-compat 0.1.0-rc.17 → 0.2.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 +101 -2
- package/README.md +67 -2
- package/dist/betas.d.ts +37 -0
- package/dist/betas.d.ts.map +1 -1
- package/dist/betas.js +55 -23
- package/dist/betas.js.map +1 -1
- package/dist/build-request.d.ts +11 -0
- package/dist/build-request.d.ts.map +1 -1
- package/dist/build-request.js +133 -11
- package/dist/build-request.js.map +1 -1
- package/dist/contracts.d.ts +53 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/fingerprint.d.ts +29 -2
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +60 -7
- package/dist/fingerprint.js.map +1 -1
- package/dist/headers.d.ts.map +1 -1
- package/dist/headers.js +12 -4
- package/dist/headers.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/model-capabilities.d.ts +31 -3
- package/dist/model-capabilities.d.ts.map +1 -1
- package/dist/model-capabilities.js +145 -12
- package/dist/model-capabilities.js.map +1 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +8 -4
- package/dist/models.js.map +1 -1
- package/dist/profile-behaviors.d.ts +61 -0
- package/dist/profile-behaviors.d.ts.map +1 -0
- package/dist/profile-behaviors.js +53 -0
- package/dist/profile-behaviors.js.map +1 -0
- package/dist/profiles/beta-registry-2.1.233.d.ts +140 -0
- package/dist/profiles/beta-registry-2.1.233.d.ts.map +1 -0
- package/dist/profiles/beta-registry-2.1.233.js +183 -0
- package/dist/profiles/beta-registry-2.1.233.js.map +1 -0
- package/dist/profiles/claude-code-2.1.195.d.ts.map +1 -1
- package/dist/profiles/claude-code-2.1.195.js +14 -0
- package/dist/profiles/claude-code-2.1.195.js.map +1 -1
- package/dist/profiles/claude-code-2.1.233.d.ts +3 -0
- package/dist/profiles/claude-code-2.1.233.d.ts.map +1 -0
- package/dist/profiles/claude-code-2.1.233.js +235 -0
- package/dist/profiles/claude-code-2.1.233.js.map +1 -0
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +14 -1
- package/dist/redaction.js.map +1 -1
- package/dist/request-body.d.ts.map +1 -1
- package/dist/request-body.js +12 -10
- package/dist/request-body.js.map +1 -1
- package/dist/thinking.d.ts +33 -7
- package/dist/thinking.d.ts.map +1 -1
- package/dist/thinking.js +105 -36
- package/dist/thinking.js.map +1 -1
- package/package.json +10 -2
- package/src/anti-verbosity.ts +219 -0
- package/src/beta-registry.ts +140 -0
- package/src/betas.ts +302 -0
- package/src/build-request.ts +1799 -0
- package/src/contracts.ts +1286 -0
- package/src/count-tokens.ts +84 -0
- package/src/fingerprint.ts +155 -0
- package/src/headers.ts +448 -0
- package/src/index.ts +63 -0
- package/src/metadata.ts +331 -0
- package/src/model-capabilities.ts +453 -0
- package/src/model-identity.ts +45 -0
- package/src/models.ts +50 -0
- package/src/profile-behaviors.ts +114 -0
- package/src/profiles/beta-registry-2.1.233.ts +200 -0
- package/src/profiles/claude-code-2.1.195.ts +168 -0
- package/src/profiles/claude-code-2.1.233.ts +240 -0
- package/src/redaction.ts +536 -0
- package/src/request-body.ts +1931 -0
- package/src/sha256.ts +114 -0
- package/src/system-prompt.ts +222 -0
- package/src/thinking.ts +346 -0
- package/src/unicode.ts +24 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public entry point for the Claude Code wire compatibility package.
|
|
5
|
+
*
|
|
6
|
+
* Only the surfaces listed below are public. The Wave 2 implementation
|
|
7
|
+
* Internal protocol modules remain private; only the documented builder and
|
|
8
|
+
* parser are exported here.
|
|
9
|
+
*
|
|
10
|
+
* Importing this module has no side effects. It reads no environment, opens
|
|
11
|
+
* no network connection, touches no clock or random source, and holds no
|
|
12
|
+
* mutable module-level state.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export type {
|
|
16
|
+
AntiVerbosityPolicy,
|
|
17
|
+
AntiVerbositySection,
|
|
18
|
+
BuiltClaudeCodeCountTokensRequest,
|
|
19
|
+
BuiltClaudeCodeRequest,
|
|
20
|
+
ClaudeCodeBetaOverrides,
|
|
21
|
+
ClaudeCodeBetaPolicy,
|
|
22
|
+
ClaudeCodeCapabilities,
|
|
23
|
+
ClaudeCodeCapabilityDecisions,
|
|
24
|
+
ClaudeCodeCatalogueEntry,
|
|
25
|
+
ClaudeCodeEffort,
|
|
26
|
+
ClaudeCodeExtraHeaderPolicy,
|
|
27
|
+
ClaudeCodeMetadataOverrides,
|
|
28
|
+
ClaudeCodeModelFamily,
|
|
29
|
+
ClaudeCodeProtocolProfile,
|
|
30
|
+
ClaudeCodeCountTokensInput,
|
|
31
|
+
ClaudeCodeRequestInput,
|
|
32
|
+
ClaudeCodeRuntimeIdentity,
|
|
33
|
+
ClaudeCodeWireErrorCode,
|
|
34
|
+
HeaderPair,
|
|
35
|
+
JsonPrimitive,
|
|
36
|
+
JsonValue,
|
|
37
|
+
Message,
|
|
38
|
+
MessageContent,
|
|
39
|
+
RedactedRequestEvidence,
|
|
40
|
+
SystemInput,
|
|
41
|
+
TextBlock,
|
|
42
|
+
ThinkingDisplay,
|
|
43
|
+
ToolDefinition,
|
|
44
|
+
ToolResultBlock,
|
|
45
|
+
ToolUseBlock,
|
|
46
|
+
} from "./contracts.js";
|
|
47
|
+
|
|
48
|
+
export { ClaudeCodeWireError } from "./contracts.js";
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
DEFAULT_ANTI_VERBOSITY_POLICY,
|
|
52
|
+
antiVerbosityText,
|
|
53
|
+
selectAntiVerbositySection,
|
|
54
|
+
} from "./anti-verbosity.js";
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
buildClaudeCodeCountTokensRequest,
|
|
58
|
+
buildClaudeCodeRequest,
|
|
59
|
+
parseBuiltClaudeCodeRequest,
|
|
60
|
+
} from "./build-request.js";
|
|
61
|
+
|
|
62
|
+
export { CLAUDE_CODE_2_1_195_PROFILE } from "./profiles/claude-code-2.1.195.js";
|
|
63
|
+
export { CLAUDE_CODE_2_1_233_PROFILE } from "./profiles/claude-code-2.1.233.js";
|
package/src/metadata.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
import { ClaudeCodeWireError } from "./contracts.js";
|
|
4
|
+
import type {
|
|
5
|
+
ClaudeCodeMetadataOverrides,
|
|
6
|
+
ClaudeCodeRuntimeIdentity,
|
|
7
|
+
JsonValue,
|
|
8
|
+
} from "./contracts.js";
|
|
9
|
+
import { inspectJsonInputs, validatedJson } from "./request-body.js";
|
|
10
|
+
import { classifySurrogateAt } from "./unicode.js";
|
|
11
|
+
|
|
12
|
+
const MAX_TEXT_LENGTH = 8_192;
|
|
13
|
+
const FORBIDDEN_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
14
|
+
const CORRELATION_KEYS = new Set([
|
|
15
|
+
"user_id",
|
|
16
|
+
"device_id",
|
|
17
|
+
"account_uuid",
|
|
18
|
+
"session_id",
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* The members of the derived `user_id` JSON object.
|
|
22
|
+
*
|
|
23
|
+
* `metadataOverrides.userIdFields` may not carry these keys: the package writes
|
|
24
|
+
* them last so correlation always wins, and a silently overwritten caller value
|
|
25
|
+
* would look honoured while being discarded.
|
|
26
|
+
*/
|
|
27
|
+
const IDENTITY_KEYS = new Set(["device_id", "account_uuid", "session_id"]);
|
|
28
|
+
|
|
29
|
+
function hasInvalidUtf16(value: string): boolean {
|
|
30
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
31
|
+
const classification = classifySurrogateAt(value, index);
|
|
32
|
+
if (classification === "loneSurrogate") return true;
|
|
33
|
+
if (classification === "surrogatePair") index += 1;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasControlCharacter(value: string): boolean {
|
|
39
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
40
|
+
const codeUnit = value.charCodeAt(index);
|
|
41
|
+
if (codeUnit <= 0x1f || codeUnit === 0x7f) return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ownDataValue(value: object, key: string): unknown {
|
|
47
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
48
|
+
return descriptor === undefined || !("value" in descriptor)
|
|
49
|
+
? undefined
|
|
50
|
+
: descriptor.value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function validateIdentityText(
|
|
54
|
+
value: unknown,
|
|
55
|
+
field: keyof ClaudeCodeRuntimeIdentity,
|
|
56
|
+
): string {
|
|
57
|
+
if (typeof value !== "string") {
|
|
58
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY", { field });
|
|
59
|
+
}
|
|
60
|
+
if (hasInvalidUtf16(value)) {
|
|
61
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field });
|
|
62
|
+
}
|
|
63
|
+
if (
|
|
64
|
+
value.length === 0 ||
|
|
65
|
+
value.length > MAX_TEXT_LENGTH ||
|
|
66
|
+
value.trim().length === 0 ||
|
|
67
|
+
hasControlCharacter(value)
|
|
68
|
+
) {
|
|
69
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY", { field });
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function validateIdentityObject(identity: unknown): object {
|
|
75
|
+
if (
|
|
76
|
+
typeof identity !== "object" ||
|
|
77
|
+
identity === null ||
|
|
78
|
+
Array.isArray(identity)
|
|
79
|
+
) {
|
|
80
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY");
|
|
81
|
+
}
|
|
82
|
+
for (const key of Reflect.ownKeys(identity)) {
|
|
83
|
+
if (typeof key !== "string" || FORBIDDEN_KEYS.has(key)) {
|
|
84
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return identity;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function validateRuntimeIdentity(
|
|
91
|
+
identity: unknown,
|
|
92
|
+
): ClaudeCodeRuntimeIdentity {
|
|
93
|
+
const source = validateIdentityObject(identity);
|
|
94
|
+
const sessionId = validateIdentityText(
|
|
95
|
+
ownDataValue(source, "sessionId"),
|
|
96
|
+
"sessionId",
|
|
97
|
+
);
|
|
98
|
+
const deviceId = validateIdentityText(
|
|
99
|
+
ownDataValue(source, "deviceId"),
|
|
100
|
+
"deviceId",
|
|
101
|
+
);
|
|
102
|
+
const accountUuid = validateIdentityText(
|
|
103
|
+
ownDataValue(source, "accountUuid"),
|
|
104
|
+
"accountUuid",
|
|
105
|
+
);
|
|
106
|
+
const runtime = validateIdentityText(
|
|
107
|
+
ownDataValue(source, "runtime"),
|
|
108
|
+
"runtime",
|
|
109
|
+
);
|
|
110
|
+
const runtimeVersion = validateIdentityText(
|
|
111
|
+
ownDataValue(source, "runtimeVersion"),
|
|
112
|
+
"runtimeVersion",
|
|
113
|
+
);
|
|
114
|
+
const os = validateIdentityText(ownDataValue(source, "os"), "os");
|
|
115
|
+
const arch = validateIdentityText(ownDataValue(source, "arch"), "arch");
|
|
116
|
+
|
|
117
|
+
if (runtime !== "node" && runtime !== "bun" && runtime !== "workerd") {
|
|
118
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY", { field: "runtime" });
|
|
119
|
+
}
|
|
120
|
+
if (os !== "Windows" && os !== "Linux" && os !== "macOS") {
|
|
121
|
+
throw new ClaudeCodeWireError("INVALID_IDENTITY", { field: "os" });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
sessionId,
|
|
126
|
+
deviceId,
|
|
127
|
+
accountUuid,
|
|
128
|
+
runtime,
|
|
129
|
+
runtimeVersion,
|
|
130
|
+
os,
|
|
131
|
+
arch,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function validateMetadataKey(key: PropertyKey): string {
|
|
136
|
+
if (typeof key !== "string" || FORBIDDEN_KEYS.has(key)) {
|
|
137
|
+
throw new ClaudeCodeWireError("INVALID_INPUT");
|
|
138
|
+
}
|
|
139
|
+
if (hasInvalidUtf16(key)) {
|
|
140
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: "metadataKey" });
|
|
141
|
+
}
|
|
142
|
+
if (key.length > MAX_TEXT_LENGTH) {
|
|
143
|
+
throw new ClaudeCodeWireError("INPUT_TOO_LARGE", {
|
|
144
|
+
field: "metadataKey",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (key.length === 0 || hasControlCharacter(key)) {
|
|
148
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "metadataKey" });
|
|
149
|
+
}
|
|
150
|
+
return key;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function validateMetadataValue(value: unknown, key: string): JsonValue {
|
|
154
|
+
if (typeof value === "string") {
|
|
155
|
+
if (hasInvalidUtf16(value)) {
|
|
156
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: key });
|
|
157
|
+
}
|
|
158
|
+
if (value.length > MAX_TEXT_LENGTH) {
|
|
159
|
+
throw new ClaudeCodeWireError("INPUT_TOO_LARGE", { field: key });
|
|
160
|
+
}
|
|
161
|
+
if (hasControlCharacter(value)) {
|
|
162
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: key });
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
if (typeof value === "number") {
|
|
167
|
+
if (!Number.isFinite(value)) {
|
|
168
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: key });
|
|
169
|
+
}
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
if (typeof value === "boolean" || value === null) return value;
|
|
173
|
+
if (typeof value !== "object") {
|
|
174
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: key });
|
|
175
|
+
}
|
|
176
|
+
inspectJsonInputs([value], (nestedString) => {
|
|
177
|
+
if (hasInvalidUtf16(nestedString)) {
|
|
178
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: key });
|
|
179
|
+
}
|
|
180
|
+
if (nestedString.length > MAX_TEXT_LENGTH) {
|
|
181
|
+
throw new ClaudeCodeWireError("INPUT_TOO_LARGE", { field: key });
|
|
182
|
+
}
|
|
183
|
+
if (hasControlCharacter(nestedString)) {
|
|
184
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: key });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return validatedJson(value);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function expectedCorrelationValue(
|
|
191
|
+
key: string,
|
|
192
|
+
identity: ClaudeCodeRuntimeIdentity,
|
|
193
|
+
userId: string,
|
|
194
|
+
): string | undefined {
|
|
195
|
+
switch (key) {
|
|
196
|
+
case "user_id":
|
|
197
|
+
return userId;
|
|
198
|
+
case "device_id":
|
|
199
|
+
return identity.deviceId;
|
|
200
|
+
case "account_uuid":
|
|
201
|
+
return identity.accountUuid;
|
|
202
|
+
case "session_id":
|
|
203
|
+
return identity.sessionId;
|
|
204
|
+
default:
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function validateMetadataObject(metadata: unknown): object {
|
|
210
|
+
if (
|
|
211
|
+
typeof metadata !== "object" ||
|
|
212
|
+
metadata === null ||
|
|
213
|
+
Array.isArray(metadata)
|
|
214
|
+
) {
|
|
215
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "metadata" });
|
|
216
|
+
}
|
|
217
|
+
return metadata;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Validates a package-extension `user_id` replacement.
|
|
222
|
+
*
|
|
223
|
+
* PACKAGE EXTENSION, not observed Claude Code behaviour. The value is opaque to
|
|
224
|
+
* this package, so it is checked only for wire-safety, never for correlation.
|
|
225
|
+
*/
|
|
226
|
+
function validateOverrideUserId(value: unknown): string {
|
|
227
|
+
if (typeof value !== "string") {
|
|
228
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "userId" });
|
|
229
|
+
}
|
|
230
|
+
if (hasInvalidUtf16(value)) {
|
|
231
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: "userId" });
|
|
232
|
+
}
|
|
233
|
+
if (value.length > MAX_TEXT_LENGTH) {
|
|
234
|
+
throw new ClaudeCodeWireError("INPUT_TOO_LARGE", { field: "userId" });
|
|
235
|
+
}
|
|
236
|
+
if (hasControlCharacter(value)) {
|
|
237
|
+
throw new ClaudeCodeWireError("INVALID_UNICODE", { field: "userId" });
|
|
238
|
+
}
|
|
239
|
+
if (value.length === 0 || value.trim().length === 0) {
|
|
240
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "userId" });
|
|
241
|
+
}
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Validates the package-extension members merged into the `user_id` object. */
|
|
246
|
+
function validateUserIdFields(value: unknown): [string, JsonValue][] {
|
|
247
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
248
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: "userIdFields" });
|
|
249
|
+
}
|
|
250
|
+
const entries: [string, JsonValue][] = [];
|
|
251
|
+
for (const rawKey of Reflect.ownKeys(value)) {
|
|
252
|
+
const key = validateMetadataKey(rawKey);
|
|
253
|
+
if (IDENTITY_KEYS.has(key)) {
|
|
254
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: key });
|
|
255
|
+
}
|
|
256
|
+
entries.push([key, validateMetadataValue(ownDataValue(value, key), key)]);
|
|
257
|
+
}
|
|
258
|
+
return entries;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Resolves the emitted `user_id`, applying the package-extension seam.
|
|
263
|
+
*
|
|
264
|
+
* With no overrides this is the derived correlation triple, byte for byte as
|
|
265
|
+
* before the seam existed. The two override members are mutually exclusive
|
|
266
|
+
* because they express opposite intents: one abandons the derived value, the
|
|
267
|
+
* other extends it.
|
|
268
|
+
*/
|
|
269
|
+
function resolveUserId(
|
|
270
|
+
identity: ClaudeCodeRuntimeIdentity,
|
|
271
|
+
overrides: unknown,
|
|
272
|
+
): string {
|
|
273
|
+
const correlation = {
|
|
274
|
+
device_id: identity.deviceId,
|
|
275
|
+
account_uuid: identity.accountUuid,
|
|
276
|
+
session_id: identity.sessionId,
|
|
277
|
+
};
|
|
278
|
+
if (overrides === undefined) return JSON.stringify(correlation);
|
|
279
|
+
if (
|
|
280
|
+
typeof overrides !== "object" ||
|
|
281
|
+
overrides === null ||
|
|
282
|
+
Array.isArray(overrides)
|
|
283
|
+
) {
|
|
284
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", {
|
|
285
|
+
field: "metadataOverrides",
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
const hasUserId = Object.hasOwn(overrides, "userId");
|
|
289
|
+
const hasUserIdFields = Object.hasOwn(overrides, "userIdFields");
|
|
290
|
+
if (hasUserId && hasUserIdFields) {
|
|
291
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", {
|
|
292
|
+
field: "metadataOverrides",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if (hasUserId) {
|
|
296
|
+
return validateOverrideUserId(ownDataValue(overrides, "userId"));
|
|
297
|
+
}
|
|
298
|
+
if (!hasUserIdFields) return JSON.stringify(correlation);
|
|
299
|
+
const fields = validateUserIdFields(ownDataValue(overrides, "userIdFields"));
|
|
300
|
+
// Caller members first, correlation last: the triple always wins, which
|
|
301
|
+
// mirrors how the consumer composes the same object.
|
|
302
|
+
return JSON.stringify({ ...Object.fromEntries(fields), ...correlation });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function buildCorrelatedMetadata(
|
|
306
|
+
identity: ClaudeCodeRuntimeIdentity,
|
|
307
|
+
suppliedMetadata?: Readonly<Record<string, JsonValue>>,
|
|
308
|
+
overrides?: ClaudeCodeMetadataOverrides,
|
|
309
|
+
): Readonly<Record<string, JsonValue>> {
|
|
310
|
+
const validatedIdentity = validateRuntimeIdentity(identity);
|
|
311
|
+
const userId = resolveUserId(validatedIdentity, overrides);
|
|
312
|
+
const entries: [string, JsonValue][] = [["user_id", userId]];
|
|
313
|
+
|
|
314
|
+
if (suppliedMetadata === undefined) return Object.freeze({ user_id: userId });
|
|
315
|
+
const metadataObject = validateMetadataObject(suppliedMetadata);
|
|
316
|
+
|
|
317
|
+
for (const rawKey of Reflect.ownKeys(metadataObject)) {
|
|
318
|
+
const key = validateMetadataKey(rawKey);
|
|
319
|
+
const value = validateMetadataValue(ownDataValue(metadataObject, key), key);
|
|
320
|
+
if (CORRELATION_KEYS.has(key)) {
|
|
321
|
+
const expected = expectedCorrelationValue(key, validatedIdentity, userId);
|
|
322
|
+
if (value !== expected) {
|
|
323
|
+
throw new ClaudeCodeWireError("INVALID_INPUT", { field: key });
|
|
324
|
+
}
|
|
325
|
+
if (key === "user_id") continue;
|
|
326
|
+
}
|
|
327
|
+
entries.push([key, value]);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return Object.freeze(Object.fromEntries(entries));
|
|
331
|
+
}
|