@modainteract/moda-interact-shared 2.2.0 → 2.4.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.
@@ -0,0 +1,324 @@
1
+ import {
2
+ CommerceEnvironmentSchema
3
+ } from "../../chunk-FTUTJH7W.js";
4
+ import {
5
+ canonicalJson
6
+ } from "../../chunk-AUS3WQIW.js";
7
+
8
+ // src/security/credentials/errors.ts
9
+ var MESSAGES = {
10
+ INVALID_KEYRING: "Invalid credential keyring configuration",
11
+ KEY_UNAVAILABLE: "Credential encryption key is unavailable",
12
+ INVALID_CREDENTIAL: "Invalid encrypted credential",
13
+ INVALID_COMMAND_HMAC_KEY: "Invalid command authentication key"
14
+ };
15
+ var CredentialSecurityError = class extends Error {
16
+ code;
17
+ constructor(code) {
18
+ super(MESSAGES[code]);
19
+ this.name = "CredentialSecurityError";
20
+ this.code = code;
21
+ }
22
+ };
23
+
24
+ // src/security/credentials/aad.ts
25
+ var KEY_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
26
+ var PURPOSES = /* @__PURE__ */ new Set(["MERCHANT_KNOWLEDGE", "REFERENCE_TAXONOMY"]);
27
+ var FORBIDDEN_ID_CHARACTERS = /[\r\n\0]/;
28
+ var INVALID_UNICODE = /\p{Surrogate}/u;
29
+ var RESERVED_KEY_IDS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
30
+ function invalidAad() {
31
+ throw new CredentialSecurityError("INVALID_CREDENTIAL");
32
+ }
33
+ function isBoundedIdentifier(value, limit) {
34
+ return typeof value === "string" && value.length >= 1 && value.length <= limit && value.trim().length > 0 && !FORBIDDEN_ID_CHARACTERS.test(value) && !INVALID_UNICODE.test(value);
35
+ }
36
+ function createCommerceEmbeddingCredentialAad(input) {
37
+ if (input === null || typeof input !== "object" || !CommerceEnvironmentSchema.safeParse(input.environment).success || !PURPOSES.has(input.purpose) || !isBoundedIdentifier(input.provider, 128) || typeof input.keyId !== "string" || !KEY_ID_PATTERN.test(input.keyId) || RESERVED_KEY_IDS.has(input.keyId)) {
38
+ return invalidAad();
39
+ }
40
+ return canonicalJson({
41
+ credentialType: "EMBEDDING",
42
+ environment: input.environment,
43
+ purpose: input.purpose,
44
+ provider: input.provider,
45
+ keyId: input.keyId
46
+ });
47
+ }
48
+ function createWooRestReadGrantAad(input) {
49
+ if (input === null || typeof input !== "object" || !isBoundedIdentifier(input.shopId, 128) || !isBoundedIdentifier(input.installationId, 128) || !isBoundedIdentifier(input.authorizationAttemptId, 128) || !Number.isSafeInteger(input.credentialVersionSnapshot) || input.credentialVersionSnapshot < 1) {
50
+ return invalidAad();
51
+ }
52
+ return JSON.stringify([
53
+ "moda-woo-rest-read-v1",
54
+ input.shopId,
55
+ input.installationId,
56
+ input.authorizationAttemptId,
57
+ input.credentialVersionSnapshot
58
+ ]);
59
+ }
60
+
61
+ // src/security/credentials/keyring.ts
62
+ import { Buffer } from "buffer";
63
+ var KEY_ID_PATTERN2 = /^[A-Za-z0-9_-]{1,64}$/;
64
+ var STANDARD_BASE64_KEY_PATTERN = /^[A-Za-z0-9+/]{43}=$/;
65
+ var RESERVED_KEY_IDS2 = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
66
+ var MAX_CONFIG_BYTES = 4096;
67
+ var MAX_KEYS = 16;
68
+ var KEY_BYTES = 32;
69
+ var privateKeys = /* @__PURE__ */ new WeakMap();
70
+ function invalidKeyring() {
71
+ throw new CredentialSecurityError("INVALID_KEYRING");
72
+ }
73
+ function readJsonString(raw, start) {
74
+ if (raw[start] !== '"') invalidKeyring();
75
+ let cursor = start + 1;
76
+ while (cursor < raw.length) {
77
+ const character = raw[cursor];
78
+ if (character === "\\") {
79
+ cursor += 2;
80
+ continue;
81
+ }
82
+ if (character === '"') {
83
+ const decoded = JSON.parse(raw.slice(start, cursor + 1));
84
+ if (typeof decoded !== "string") invalidKeyring();
85
+ return [decoded, cursor + 1];
86
+ }
87
+ cursor += 1;
88
+ }
89
+ return invalidKeyring();
90
+ }
91
+ function parseUniqueStringObject(raw) {
92
+ let decoded;
93
+ try {
94
+ decoded = JSON.parse(raw);
95
+ } catch {
96
+ return invalidKeyring();
97
+ }
98
+ if (decoded === null || typeof decoded !== "object" || Array.isArray(decoded)) {
99
+ return invalidKeyring();
100
+ }
101
+ let index = 0;
102
+ const skipWhitespace = () => {
103
+ while (index < raw.length && /[ \t\r\n]/.test(raw[index])) index += 1;
104
+ };
105
+ const entries = /* @__PURE__ */ new Map();
106
+ skipWhitespace();
107
+ if (raw[index++] !== "{") return invalidKeyring();
108
+ skipWhitespace();
109
+ if (raw[index] === "}") return invalidKeyring();
110
+ while (index < raw.length) {
111
+ const [key, afterKey] = readJsonString(raw, index);
112
+ if (entries.has(key)) return invalidKeyring();
113
+ if (!KEY_ID_PATTERN2.test(key) || RESERVED_KEY_IDS2.has(key)) return invalidKeyring();
114
+ index = afterKey;
115
+ skipWhitespace();
116
+ if (raw[index++] !== ":") return invalidKeyring();
117
+ skipWhitespace();
118
+ const [value, afterValue] = readJsonString(raw, index);
119
+ entries.set(key, value);
120
+ if (entries.size > MAX_KEYS) return invalidKeyring();
121
+ index = afterValue;
122
+ skipWhitespace();
123
+ const separator = raw[index++];
124
+ if (separator === "}") break;
125
+ if (separator !== ",") return invalidKeyring();
126
+ skipWhitespace();
127
+ }
128
+ skipWhitespace();
129
+ if (index !== raw.length || entries.size === 0) return invalidKeyring();
130
+ return entries;
131
+ }
132
+ function parseCredentialKeyring(raw) {
133
+ if (typeof raw !== "string" || raw.trim().length === 0 || Buffer.byteLength(raw, "utf8") > MAX_CONFIG_BYTES) {
134
+ return invalidKeyring();
135
+ }
136
+ let entries;
137
+ try {
138
+ entries = parseUniqueStringObject(raw);
139
+ } catch {
140
+ return invalidKeyring();
141
+ }
142
+ const keys = /* @__PURE__ */ new Map();
143
+ for (const [keyId, encoded] of entries) {
144
+ if (!STANDARD_BASE64_KEY_PATTERN.test(encoded)) return invalidKeyring();
145
+ const bytes = Buffer.from(encoded, "base64");
146
+ if (bytes.length !== KEY_BYTES || bytes.toString("base64") !== encoded) {
147
+ return invalidKeyring();
148
+ }
149
+ keys.set(keyId, Uint8Array.from(bytes));
150
+ }
151
+ const keyIds = Object.freeze([...keys.keys()].sort());
152
+ const descriptor = Object.freeze({
153
+ keyIds,
154
+ hasKey: (keyId) => typeof keyId === "string" && keys.has(keyId)
155
+ });
156
+ privateKeys.set(descriptor, keys);
157
+ return descriptor;
158
+ }
159
+ function resolveActiveCredentialKey(keyring, activeKeyId) {
160
+ const keys = privateKeys.get(keyring);
161
+ if (!keys || typeof activeKeyId !== "string" || !KEY_ID_PATTERN2.test(activeKeyId) || RESERVED_KEY_IDS2.has(activeKeyId) || !keys.has(activeKeyId)) {
162
+ throw new CredentialSecurityError("KEY_UNAVAILABLE");
163
+ }
164
+ return activeKeyId;
165
+ }
166
+ function getCredentialKeyBytes(keyring, keyId) {
167
+ const bytes = privateKeys.get(keyring)?.get(keyId);
168
+ if (!bytes) throw new CredentialSecurityError("KEY_UNAVAILABLE");
169
+ return Uint8Array.from(bytes);
170
+ }
171
+
172
+ // src/security/credentials/command-hmac-key.ts
173
+ import { Buffer as Buffer2 } from "buffer";
174
+ import { timingSafeEqual } from "crypto";
175
+ var CANONICAL_BASE64_32_BYTE_KEY = /^[A-Za-z0-9+/]{43}=$/;
176
+ function invalidCommandHmacKey() {
177
+ throw new CredentialSecurityError("INVALID_COMMAND_HMAC_KEY");
178
+ }
179
+ function parseCommerceConnectionCommandHmacKey(raw, aesKeyring) {
180
+ if (typeof raw !== "string" || !CANONICAL_BASE64_32_BYTE_KEY.test(raw)) {
181
+ return invalidCommandHmacKey();
182
+ }
183
+ const decoded = Buffer2.from(raw, "base64");
184
+ if (decoded.length !== 32 || decoded.toString("base64") !== raw) {
185
+ return invalidCommandHmacKey();
186
+ }
187
+ let reusesEncryptionKey = false;
188
+ try {
189
+ for (const keyId of aesKeyring.keyIds) {
190
+ const aesBytes = getCredentialKeyBytes(aesKeyring, keyId);
191
+ try {
192
+ reusesEncryptionKey = timingSafeEqual(decoded, aesBytes) || reusesEncryptionKey;
193
+ } finally {
194
+ aesBytes.fill(0);
195
+ }
196
+ }
197
+ if (aesKeyring.keyIds.length === 0) return invalidCommandHmacKey();
198
+ } catch {
199
+ return invalidCommandHmacKey();
200
+ }
201
+ if (reusesEncryptionKey) return invalidCommandHmacKey();
202
+ return Uint8Array.from(decoded);
203
+ }
204
+
205
+ // src/security/credentials/aead.ts
206
+ import { Buffer as Buffer3 } from "buffer";
207
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
208
+ import { TextDecoder } from "util";
209
+ var NONCE_BYTES = 12;
210
+ var AUTH_TAG_BYTES = 16;
211
+ var MAX_CREDENTIAL_BYTES = 8192;
212
+ var MAX_AAD_BYTES = 4096;
213
+ var KEY_ID_PATTERN3 = /^[A-Za-z0-9_-]{1,64}$/;
214
+ var RESERVED_KEY_IDS3 = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
215
+ function invalidCredential() {
216
+ throw new CredentialSecurityError("INVALID_CREDENTIAL");
217
+ }
218
+ function isValidUnicode(value) {
219
+ for (let index = 0; index < value.length; index += 1) {
220
+ const unit = value.charCodeAt(index);
221
+ if (unit >= 55296 && unit <= 56319) {
222
+ if (index + 1 === value.length) return false;
223
+ const next = value.charCodeAt(index + 1);
224
+ if (next < 56320 || next > 57343) return false;
225
+ index += 1;
226
+ } else if (unit >= 56320 && unit <= 57343) {
227
+ return false;
228
+ }
229
+ }
230
+ return true;
231
+ }
232
+ function aadBytes(value) {
233
+ let bytes;
234
+ if (typeof value === "string") {
235
+ if (!isValidUnicode(value)) return invalidCredential();
236
+ bytes = Buffer3.from(value, "utf8");
237
+ } else if (value instanceof Uint8Array) {
238
+ bytes = Buffer3.from(value);
239
+ } else {
240
+ return invalidCredential();
241
+ }
242
+ if (bytes.length < 1 || bytes.length > MAX_AAD_BYTES) return invalidCredential();
243
+ return bytes;
244
+ }
245
+ function validEnvelope(value) {
246
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
247
+ const candidate = value;
248
+ return typeof candidate.keyId === "string" && KEY_ID_PATTERN3.test(candidate.keyId) && !RESERVED_KEY_IDS3.has(candidate.keyId) && candidate.nonce instanceof Uint8Array && candidate.nonce.byteLength === NONCE_BYTES && candidate.ciphertext instanceof Uint8Array && candidate.ciphertext.byteLength >= 1 && candidate.ciphertext.byteLength <= MAX_CREDENTIAL_BYTES && candidate.authTag instanceof Uint8Array && candidate.authTag.byteLength === AUTH_TAG_BYTES;
249
+ }
250
+ function protectCredentialOperation(operation) {
251
+ try {
252
+ return operation();
253
+ } catch (error) {
254
+ if (error instanceof CredentialSecurityError) throw error;
255
+ return invalidCredential();
256
+ }
257
+ }
258
+ function encryptCredential(input) {
259
+ return protectCredentialOperation(() => {
260
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return invalidCredential();
261
+ const { keyring, activeKeyId, plaintext, aad } = input;
262
+ if (typeof plaintext !== "string" || !isValidUnicode(plaintext)) return invalidCredential();
263
+ const plaintextBytes = Buffer3.from(plaintext, "utf8");
264
+ if (plaintextBytes.length < 1 || plaintextBytes.length > MAX_CREDENTIAL_BYTES) {
265
+ plaintextBytes.fill(0);
266
+ return invalidCredential();
267
+ }
268
+ let key;
269
+ try {
270
+ const authenticatedData = aadBytes(aad);
271
+ const keyId = resolveActiveCredentialKey(keyring, activeKeyId);
272
+ key = getCredentialKeyBytes(keyring, keyId);
273
+ const nonce = randomBytes(NONCE_BYTES);
274
+ const cipher = createCipheriv("aes-256-gcm", key, nonce, { authTagLength: AUTH_TAG_BYTES });
275
+ cipher.setAAD(authenticatedData);
276
+ const ciphertext = Buffer3.concat([cipher.update(plaintextBytes), cipher.final()]);
277
+ return Object.freeze({
278
+ keyId,
279
+ nonce: Uint8Array.from(nonce),
280
+ ciphertext: Uint8Array.from(ciphertext),
281
+ authTag: Uint8Array.from(cipher.getAuthTag())
282
+ });
283
+ } finally {
284
+ key?.fill(0);
285
+ plaintextBytes.fill(0);
286
+ }
287
+ });
288
+ }
289
+ function decryptCredential(input) {
290
+ return protectCredentialOperation(() => {
291
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return invalidCredential();
292
+ const { keyring, envelope, aad } = input;
293
+ if (!validEnvelope(envelope)) return invalidCredential();
294
+ const authenticatedData = aadBytes(aad);
295
+ const key = getCredentialKeyBytes(keyring, envelope.keyId);
296
+ let plaintextBytes;
297
+ try {
298
+ const decipher = createDecipheriv("aes-256-gcm", key, Buffer3.from(envelope.nonce), {
299
+ authTagLength: AUTH_TAG_BYTES
300
+ });
301
+ decipher.setAAD(authenticatedData);
302
+ decipher.setAuthTag(Buffer3.from(envelope.authTag));
303
+ plaintextBytes = Buffer3.concat([
304
+ decipher.update(Buffer3.from(envelope.ciphertext)),
305
+ decipher.final()
306
+ ]);
307
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(plaintextBytes);
308
+ } finally {
309
+ plaintextBytes?.fill(0);
310
+ key.fill(0);
311
+ }
312
+ });
313
+ }
314
+ export {
315
+ CredentialSecurityError,
316
+ createCommerceEmbeddingCredentialAad,
317
+ createWooRestReadGrantAad,
318
+ decryptCredential,
319
+ encryptCredential,
320
+ parseCommerceConnectionCommandHmacKey,
321
+ parseCredentialKeyring,
322
+ resolveActiveCredentialKey
323
+ };
324
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/security/credentials/errors.ts","../../../src/security/credentials/aad.ts","../../../src/security/credentials/keyring.ts","../../../src/security/credentials/command-hmac-key.ts","../../../src/security/credentials/aead.ts"],"sourcesContent":["/** A stable, secret-free error vocabulary shared by credential-security operations. */\nexport type CredentialSecurityErrorCode =\n | \"INVALID_KEYRING\"\n | \"KEY_UNAVAILABLE\"\n | \"INVALID_CREDENTIAL\"\n | \"INVALID_COMMAND_HMAC_KEY\";\n\nconst MESSAGES: Readonly<Record<CredentialSecurityErrorCode, string>> = {\n INVALID_KEYRING: \"Invalid credential keyring configuration\",\n KEY_UNAVAILABLE: \"Credential encryption key is unavailable\",\n INVALID_CREDENTIAL: \"Invalid encrypted credential\",\n INVALID_COMMAND_HMAC_KEY: \"Invalid command authentication key\",\n};\n\nexport class CredentialSecurityError extends Error {\n readonly code: CredentialSecurityErrorCode;\n\n constructor(code: CredentialSecurityErrorCode) {\n super(MESSAGES[code]);\n this.name = \"CredentialSecurityError\";\n this.code = code;\n }\n}\n","import { canonicalJson } from \"../../commerce/canonical-json.js\";\nimport {\n CommerceEnvironmentSchema,\n type CommerceEnvironment,\n} from \"../../commerce/model/index.js\";\nimport { CredentialSecurityError } from \"./errors.js\";\n\nconst KEY_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;\nconst PURPOSES = new Set([\"MERCHANT_KNOWLEDGE\", \"REFERENCE_TAXONOMY\"]);\nconst FORBIDDEN_ID_CHARACTERS = /[\\r\\n\\0]/;\nconst INVALID_UNICODE = /\\p{Surrogate}/u;\nconst RESERVED_KEY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\n\ntype EmbeddingPurpose = \"MERCHANT_KNOWLEDGE\" | \"REFERENCE_TAXONOMY\";\n\nexport type CommerceEmbeddingCredentialAadInput = Readonly<{\n environment: CommerceEnvironment;\n purpose: EmbeddingPurpose;\n provider: string;\n keyId: string;\n}>;\n\nexport type WooRestReadGrantAadInput = Readonly<{\n shopId: string;\n installationId: string;\n authorizationAttemptId: string;\n credentialVersionSnapshot: number;\n}>;\n\nfunction invalidAad(): never {\n // The message/code are constant: never interpolate merchant identifiers or secrets.\n throw new CredentialSecurityError(\"INVALID_CREDENTIAL\");\n}\n\nfunction isBoundedIdentifier(value: unknown, limit: number): value is string {\n return typeof value === \"string\" &&\n value.length >= 1 && value.length <= limit &&\n value.trim().length > 0 &&\n !FORBIDDEN_ID_CHARACTERS.test(value) &&\n !INVALID_UNICODE.test(value);\n}\n\n/** Preserve the existing embedding canonical-JSON AAD bytes across writer/readers. */\nexport function createCommerceEmbeddingCredentialAad(\n input: CommerceEmbeddingCredentialAadInput,\n): string {\n if (\n input === null || typeof input !== \"object\" ||\n !CommerceEnvironmentSchema.safeParse(input.environment).success ||\n !PURPOSES.has(input.purpose) ||\n !isBoundedIdentifier(input.provider, 128) ||\n typeof input.keyId !== \"string\" ||\n !KEY_ID_PATTERN.test(input.keyId) ||\n RESERVED_KEY_IDS.has(input.keyId)\n ) {\n return invalidAad();\n }\n\n return canonicalJson({\n credentialType: \"EMBEDDING\",\n environment: input.environment,\n purpose: input.purpose,\n provider: input.provider,\n keyId: input.keyId,\n });\n}\n\n/** Preserve the existing Woo API/Background JSON-array AAD bytes exactly. */\nexport function createWooRestReadGrantAad(input: WooRestReadGrantAadInput): string {\n if (\n input === null || typeof input !== \"object\" ||\n !isBoundedIdentifier(input.shopId, 128) ||\n !isBoundedIdentifier(input.installationId, 128) ||\n !isBoundedIdentifier(input.authorizationAttemptId, 128) ||\n !Number.isSafeInteger(input.credentialVersionSnapshot) ||\n input.credentialVersionSnapshot < 1\n ) {\n return invalidAad();\n }\n\n return JSON.stringify([\n \"moda-woo-rest-read-v1\",\n input.shopId,\n input.installationId,\n input.authorizationAttemptId,\n input.credentialVersionSnapshot,\n ]);\n}\n","import { Buffer } from \"node:buffer\";\nimport { CredentialSecurityError } from \"./errors.js\";\n\nconst KEY_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;\nconst STANDARD_BASE64_KEY_PATTERN = /^[A-Za-z0-9+/]{43}=$/;\nconst RESERVED_KEY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst MAX_CONFIG_BYTES = 4096;\nconst MAX_KEYS = 16;\nconst KEY_BYTES = 32;\n\nexport interface CredentialKeyring {\n readonly keyIds: readonly string[];\n hasKey(keyId: string): boolean;\n}\n\n// The keyring descriptor never contains key material. In particular, consumers cannot\n// obtain a reference to any retained secret through keyIds, hasKey, or object inspection.\nconst privateKeys = new WeakMap<CredentialKeyring, ReadonlyMap<string, Uint8Array>>();\n\nfunction invalidKeyring(): never {\n throw new CredentialSecurityError(\"INVALID_KEYRING\");\n}\n\n/**\n * Read a JSON string token, returning the decoded contents and the next offset.\n * JSON.parse validates escapes (including Unicode escapes) and rejects malformed text.\n * Only decoded property names are compared, preventing escaped duplicate aliases.\n */\nfunction readJsonString(raw: string, start: number): [string, number] {\n if (raw[start] !== '\"') invalidKeyring();\n let cursor = start + 1;\n while (cursor < raw.length) {\n const character = raw[cursor];\n if (character === \"\\\\\") {\n cursor += 2;\n continue;\n }\n if (character === '\"') {\n const decoded: unknown = JSON.parse(raw.slice(start, cursor + 1));\n if (typeof decoded !== \"string\") invalidKeyring();\n return [decoded, cursor + 1];\n }\n cursor += 1;\n }\n return invalidKeyring();\n}\n\n/**\n * A small strict JSON-object scanner: the only supported format is a top-level\n * object mapping JSON string IDs to JSON string key values. Using JSON.parse(raw)\n * alone would silently discard duplicate property names, which we must reject.\n */\nfunction parseUniqueStringObject(raw: string): Map<string, string> {\n // Validate the complete JSON grammar, including escape sequences and trailing data.\n let decoded: unknown;\n try {\n decoded = JSON.parse(raw);\n } catch {\n return invalidKeyring();\n }\n if (decoded === null || typeof decoded !== \"object\" || Array.isArray(decoded)) {\n return invalidKeyring();\n }\n\n let index = 0;\n const skipWhitespace = (): void => {\n while (index < raw.length && /[ \\t\\r\\n]/.test(raw[index]!)) index += 1;\n };\n\n const entries = new Map<string, string>();\n skipWhitespace();\n if (raw[index++] !== \"{\") return invalidKeyring();\n skipWhitespace();\n if (raw[index] === \"}\") return invalidKeyring();\n\n while (index < raw.length) {\n const [key, afterKey] = readJsonString(raw, index);\n if (entries.has(key)) return invalidKeyring();\n if (!KEY_ID_PATTERN.test(key) || RESERVED_KEY_IDS.has(key)) return invalidKeyring();\n index = afterKey;\n skipWhitespace();\n if (raw[index++] !== \":\") return invalidKeyring();\n skipWhitespace();\n const [value, afterValue] = readJsonString(raw, index);\n entries.set(key, value);\n if (entries.size > MAX_KEYS) return invalidKeyring();\n index = afterValue;\n skipWhitespace();\n const separator = raw[index++];\n if (separator === \"}\") break;\n if (separator !== \",\") return invalidKeyring();\n skipWhitespace();\n }\n skipWhitespace();\n if (index !== raw.length || entries.size === 0) return invalidKeyring();\n return entries;\n}\n\n/** Validate and retain canonical 32-byte AES key material without exposing the bytes. */\nexport function parseCredentialKeyring(raw: string | undefined): CredentialKeyring {\n if (typeof raw !== \"string\" || raw.trim().length === 0 || Buffer.byteLength(raw, \"utf8\") > MAX_CONFIG_BYTES) {\n return invalidKeyring();\n }\n\n let entries: Map<string, string>;\n try {\n entries = parseUniqueStringObject(raw);\n } catch {\n // No native JSON parsing error or untrusted data may escape this boundary.\n return invalidKeyring();\n }\n\n const keys = new Map<string, Uint8Array>();\n for (const [keyId, encoded] of entries) {\n if (!STANDARD_BASE64_KEY_PATTERN.test(encoded)) return invalidKeyring();\n const bytes = Buffer.from(encoded, \"base64\");\n if (bytes.length !== KEY_BYTES || bytes.toString(\"base64\") !== encoded) {\n return invalidKeyring();\n }\n keys.set(keyId, Uint8Array.from(bytes));\n }\n\n const keyIds = Object.freeze([...keys.keys()].sort());\n const descriptor: CredentialKeyring = Object.freeze({\n keyIds,\n hasKey: (keyId: string): boolean => typeof keyId === \"string\" && keys.has(keyId),\n });\n privateKeys.set(descriptor, keys);\n return descriptor;\n}\n\n/** An explicit writer selection; never select the first key or trim an input. */\nexport function resolveActiveCredentialKey(\n keyring: CredentialKeyring,\n activeKeyId: string | undefined,\n): string {\n const keys = privateKeys.get(keyring);\n if (\n !keys ||\n typeof activeKeyId !== \"string\" ||\n !KEY_ID_PATTERN.test(activeKeyId) ||\n RESERVED_KEY_IDS.has(activeKeyId) ||\n !keys.has(activeKeyId)\n ) {\n throw new CredentialSecurityError(\"KEY_UNAVAILABLE\");\n }\n return activeKeyId;\n}\n\n/**\n * Package-internal helper for SHARED-002. Never re-export from the public Node\n * entrypoint. Return a defensive copy so an AEAD consumer cannot mutate the\n * retained key material (including after an encryption failure).\n */\nexport function getCredentialKeyBytes(keyring: CredentialKeyring, keyId: string): Uint8Array {\n const bytes = privateKeys.get(keyring)?.get(keyId);\n if (!bytes) throw new CredentialSecurityError(\"KEY_UNAVAILABLE\");\n return Uint8Array.from(bytes);\n}\n","import { Buffer } from \"node:buffer\";\nimport { timingSafeEqual } from \"node:crypto\";\nimport { CredentialSecurityError } from \"./errors.js\";\nimport { getCredentialKeyBytes, type CredentialKeyring } from \"./keyring.js\";\n\nconst CANONICAL_BASE64_32_BYTE_KEY = /^[A-Za-z0-9+/]{43}=$/;\n\nfunction invalidCommandHmacKey(): never {\n throw new CredentialSecurityError(\"INVALID_COMMAND_HMAC_KEY\");\n}\n\n/**\n * Decode the Commerce connection-command HMAC secret, without accessing service\n * configuration or exposing AES key bytes. The caller supplies the configured\n * value and the canonical AES keyring explicitly.\n */\nexport function parseCommerceConnectionCommandHmacKey(\n raw: string | undefined,\n aesKeyring: CredentialKeyring,\n): Uint8Array {\n if (typeof raw !== \"string\" || !CANONICAL_BASE64_32_BYTE_KEY.test(raw)) {\n return invalidCommandHmacKey();\n }\n\n const decoded = Buffer.from(raw, \"base64\");\n if (decoded.length !== 32 || decoded.toString(\"base64\") !== raw) {\n return invalidCommandHmacKey();\n }\n\n // Examine *every* AES key, rather than returning on the first match. A\n // failed or forged keyring must never bypass the separation requirement.\n let reusesEncryptionKey = false;\n try {\n for (const keyId of aesKeyring.keyIds) {\n const aesBytes = getCredentialKeyBytes(aesKeyring, keyId);\n try {\n reusesEncryptionKey = timingSafeEqual(decoded, aesBytes) || reusesEncryptionKey;\n } finally {\n aesBytes.fill(0); // Only wipes the defensive copy, not the keyring.\n }\n }\n // The parser creates a nonempty keyring; reject invalid caller-supplied\n // substitutes rather than treating them as an empty, trusted keyring.\n if (aesKeyring.keyIds.length === 0) return invalidCommandHmacKey();\n } catch {\n return invalidCommandHmacKey();\n }\n\n if (reusesEncryptionKey) return invalidCommandHmacKey();\n return Uint8Array.from(decoded);\n}\n","import { Buffer } from \"node:buffer\";\nimport { createCipheriv, createDecipheriv, randomBytes } from \"node:crypto\";\nimport { TextDecoder } from \"node:util\";\nimport { CredentialSecurityError } from \"./errors.js\";\nimport {\n getCredentialKeyBytes,\n resolveActiveCredentialKey,\n type CredentialKeyring,\n} from \"./keyring.js\";\n\nconst NONCE_BYTES = 12;\nconst AUTH_TAG_BYTES = 16;\nconst MAX_CREDENTIAL_BYTES = 8192;\nconst MAX_AAD_BYTES = 4096;\nconst KEY_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;\nconst RESERVED_KEY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\n\n/** Crypto envelope only; persistence and AAD construction belong to the caller. */\nexport type CredentialEnvelope = Readonly<{\n keyId: string;\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n authTag: Uint8Array;\n}>;\n\nfunction invalidCredential(): never {\n throw new CredentialSecurityError(\"INVALID_CREDENTIAL\");\n}\n\n/** Reject UTF-16 strings that Node would otherwise encode with replacement bytes. */\nfunction isValidUnicode(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const unit = value.charCodeAt(index);\n if (unit >= 0xd800 && unit <= 0xdbff) {\n if (index + 1 === value.length) return false;\n const next = value.charCodeAt(index + 1);\n if (next < 0xdc00 || next > 0xdfff) return false;\n index += 1;\n } else if (unit >= 0xdc00 && unit <= 0xdfff) {\n return false;\n }\n }\n return true;\n}\n\nfunction aadBytes(value: unknown): Buffer {\n let bytes: Buffer;\n if (typeof value === \"string\") {\n if (!isValidUnicode(value)) return invalidCredential();\n bytes = Buffer.from(value, \"utf8\");\n } else if (value instanceof Uint8Array) {\n // Copy the exact view, not its possibly larger underlying ArrayBuffer.\n bytes = Buffer.from(value);\n } else {\n return invalidCredential();\n }\n if (bytes.length < 1 || bytes.length > MAX_AAD_BYTES) return invalidCredential();\n return bytes;\n}\n\nfunction validEnvelope(value: unknown): value is CredentialEnvelope {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const candidate = value as Partial<CredentialEnvelope>;\n return (\n typeof candidate.keyId === \"string\" &&\n KEY_ID_PATTERN.test(candidate.keyId) &&\n !RESERVED_KEY_IDS.has(candidate.keyId) &&\n candidate.nonce instanceof Uint8Array &&\n candidate.nonce.byteLength === NONCE_BYTES &&\n candidate.ciphertext instanceof Uint8Array &&\n candidate.ciphertext.byteLength >= 1 &&\n candidate.ciphertext.byteLength <= MAX_CREDENTIAL_BYTES &&\n candidate.authTag instanceof Uint8Array &&\n candidate.authTag.byteLength === AUTH_TAG_BYTES\n );\n}\n\n/** Keep native crypto and UTF-8 decoding exceptions out of the public error contract. */\nfunction protectCredentialOperation<T>(operation: () => T): T {\n try {\n return operation();\n } catch (error) {\n if (error instanceof CredentialSecurityError) throw error;\n return invalidCredential();\n }\n}\n\n/** Encrypt a UTF-8 credential with caller-provided, mandatory authenticated data. */\nexport function encryptCredential(input: {\n keyring: CredentialKeyring;\n activeKeyId: string;\n plaintext: string;\n aad: string | Uint8Array;\n}): CredentialEnvelope {\n return protectCredentialOperation(() => {\n if (typeof input !== \"object\" || input === null || Array.isArray(input)) return invalidCredential();\n const { keyring, activeKeyId, plaintext, aad } = input;\n if (typeof plaintext !== \"string\" || !isValidUnicode(plaintext)) return invalidCredential();\n const plaintextBytes = Buffer.from(plaintext, \"utf8\");\n if (plaintextBytes.length < 1 || plaintextBytes.length > MAX_CREDENTIAL_BYTES) {\n plaintextBytes.fill(0);\n return invalidCredential();\n }\n let key: Uint8Array | undefined;\n try {\n const authenticatedData = aadBytes(aad);\n const keyId = resolveActiveCredentialKey(keyring, activeKeyId);\n key = getCredentialKeyBytes(keyring, keyId);\n const nonce = randomBytes(NONCE_BYTES);\n const cipher = createCipheriv(\"aes-256-gcm\", key, nonce, { authTagLength: AUTH_TAG_BYTES });\n cipher.setAAD(authenticatedData);\n const ciphertext = Buffer.concat([cipher.update(plaintextBytes), cipher.final()]);\n return Object.freeze({\n keyId,\n nonce: Uint8Array.from(nonce),\n ciphertext: Uint8Array.from(ciphertext),\n authTag: Uint8Array.from(cipher.getAuthTag()),\n });\n } finally {\n key?.fill(0);\n plaintextBytes.fill(0);\n }\n });\n}\n\n/** Decrypt only authenticated UTF-8 text; never return unauthenticated bytes. */\nexport function decryptCredential(input: {\n keyring: CredentialKeyring;\n envelope: CredentialEnvelope;\n aad: string | Uint8Array;\n}): string {\n return protectCredentialOperation(() => {\n if (typeof input !== \"object\" || input === null || Array.isArray(input)) return invalidCredential();\n const { keyring, envelope, aad } = input;\n if (!validEnvelope(envelope)) return invalidCredential();\n const authenticatedData = aadBytes(aad);\n const key = getCredentialKeyBytes(keyring, envelope.keyId);\n let plaintextBytes: Buffer | undefined;\n try {\n const decipher = createDecipheriv(\"aes-256-gcm\", key, Buffer.from(envelope.nonce), {\n authTagLength: AUTH_TAG_BYTES,\n });\n decipher.setAAD(authenticatedData);\n decipher.setAuthTag(Buffer.from(envelope.authTag));\n plaintextBytes = Buffer.concat([\n decipher.update(Buffer.from(envelope.ciphertext)),\n decipher.final(),\n ]);\n // ignoreBOM preserves a leading U+FEFF so decrypt(encrypt(text)) is exact.\n return new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true }).decode(plaintextBytes);\n } finally {\n plaintextBytes?.fill(0);\n key.fill(0);\n }\n });\n}\n"],"mappings":"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","names":["KEY_ID_PATTERN","RESERVED_KEY_IDS","Buffer","Buffer","Buffer","KEY_ID_PATTERN","RESERVED_KEY_IDS","Buffer"]}
@@ -39,10 +39,19 @@ type ModelCall = {
39
39
  arguments: unknown;
40
40
  };
41
41
  /** `text` is optional so existing structured-tool invokers remain compatible. */
42
+ /** Only bounded provider metadata is carried into the runner. Never include response bodies or reasoning. */
43
+ type ModelResponseSummary = {
44
+ finishReason?: "stop" | "length" | "tool_calls" | "content_filter" | "other";
45
+ reasoningTokens?: number;
46
+ hasVisibleText: boolean;
47
+ hasReasoningBlocks: boolean;
48
+ contentKind: "missing" | "plain_text" | "typed_blocks" | "non_text_blocks";
49
+ };
42
50
  type ModelStep = {
43
51
  calls: ModelCall[];
44
52
  outputTokens: number;
45
53
  text?: string;
54
+ providerSummary?: ModelResponseSummary;
46
55
  };
47
56
  type ModelRequest = {
48
57
  instructions: readonly string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modainteract/moda-interact-shared",
3
- "version": "2.2.0",
3
+ "version": "2.4.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -99,6 +99,10 @@
99
99
  "./incidents": {
100
100
  "types": "./dist/incidents/index.d.ts",
101
101
  "import": "./dist/incidents/index.js"
102
+ },
103
+ "./security/credentials/node": {
104
+ "types": "./dist/security/credentials/node.d.ts",
105
+ "import": "./dist/security/credentials/node.js"
102
106
  }
103
107
  },
104
108
  "files": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commerce/runner/diagnostic-messages.ts","../src/commerce/runner/diagnostics.ts","../src/commerce/runner/failure.ts"],"sourcesContent":["import type { RunnerDiagnosticReason, RunnerTransportCode } from \"./diagnostics.js\";\n\ntype Explanation = Readonly<{ message: string; action: string }>;\n\n/** Fixed operator-facing explanations. No provider-supplied or customer-supplied text is interpolated. */\nconst explanations = {\n CANCELLED_BY_CALLER: [\"The calling service cancelled this conversation turn.\", \"Inspect caller cancellation and superseded-message handling.\"],\n TURN_DEADLINE_EXCEEDED: [\"The conversation turn exceeded its overall deadline.\", \"Inspect elapsed time across the model and tool calls.\"],\n OPERATION_TIMEOUT: [\"One model or tool operation exceeded its per-operation time limit.\", \"Inspect the operation stage, provider latency and timeout configuration.\"],\n UPSTREAM_OPERATION_FAILED: [\"A model or tool operation threw an exception before returning a usable result.\", \"Inspect the stage, exceptionName and transportCode; check upstream service health.\"],\n UNEXPECTED_EXCEPTION: [\"The runner encountered an unexpected exception.\", \"Inspect the failing stage and exceptionName; investigate the owning service.\"],\n INPUT_TOO_LARGE: [\"The manifest or conversation grant exceeded the allowed payload size.\", \"Reduce the manifest or grant size and check the producer's limits.\"],\n TURN_INVALID: [\"The supplied conversation-turn identity failed schema validation.\", \"Inspect the reported schema issue paths and the turn producer.\"],\n GRANT_INVALID: [\"The supplied conversation grant failed schema validation.\", \"Inspect the reported schema issue paths and grant issuer.\"],\n MANIFEST_INVALID: [\"The supplied capability manifest failed schema validation.\", \"Verify manifest version and schema fields against the published contract.\"],\n GRANT_MISMATCH: [\"The turn identity, grant and manifest do not belong to the same authorised conversation.\", \"Check shop, conversation, grant and inbound-version correlation; do not bypass authorization.\"],\n RUNNER_VERSION_INCOMPATIBLE: [\"The release manifest requires a different runner version.\", \"Align the deployed Shared runner version with the release's compatibility range.\"],\n RESPONSE_CONTRACT_INVALID: [\"The trusted final-response contract failed integrity or compatibility verification.\", \"Recheck the published release and contract digest.\"],\n LANGUAGE_INVALID: [\"The supplied conversation language or language-source fields are invalid.\", \"Check the host language configuration and supported locale values.\"],\n BUDGET_INVALID: [\"A configured model, remote-call, deadline or token budget is invalid.\", \"Check budget ranges supplied by the caller.\"],\n CONTEXT_INVALID: [\"The trusted instructions, history or context failed size or shape constraints.\", \"Inspect context construction and trim oversized history or instructions.\"],\n TOOL_REGISTRATION_INVALID: [\"The host registered duplicate or otherwise invalid tools.\", \"Check the tool registry supplied to the runner.\"],\n MODEL_STEP_BUDGET_EXHAUSTED: [\"The model used every allowed reasoning step without reaching a valid final answer.\", \"Inspect model routing and tool-call loops; reassess the configured step budget.\"],\n MODEL_INVOCATION_FAILED: [\"The model invocation failed without a provider-specific diagnostic.\", \"Inspect the model host and preserve a typed provider failure at its boundary.\"],\n MODEL_STEP_NOT_OBJECT: [\"The model step was not a non-null object.\", \"Check the model adapter's returned step type.\"],\n MODEL_STEP_CALLS_MISSING: [\"The model step did not contain a calls field.\", \"Ensure the model adapter returns a calls array, even when empty.\"],\n MODEL_STEP_CALLS_NOT_ARRAY: [\"The model step's calls field was not an array.\", \"Ensure the model adapter maps provider tool calls to an array.\"],\n MODEL_OUTPUT_TOKENS_MISSING: [\"The model step did not contain an outputTokens field.\", \"Ensure the adapter includes the provider's output-token usage count.\"],\n MODEL_OUTPUT_TOKENS_NOT_SAFE_INTEGER: [\"The model step's outputTokens was not a safe integer.\", \"Ensure the adapter returns a finite, whole-number output token count.\"],\n MODEL_OUTPUT_TOKENS_NEGATIVE: [\"The model step's outputTokens was negative.\", \"Correct the model adapter's token-usage mapping; token counts cannot be negative.\"],\n MODEL_OUTPUT_TOKENS_OVER_BUDGET: [\"The model step's outputTokens exceeded the configured output-token budget.\", \"Inspect the model's maxOutputTokens enforcement and returned usage count.\"],\n MODEL_TOOL_CALL_LIMIT_EXCEEDED: [\"The model step contained more than 32 tool calls.\", \"Limit provider tool-call output to the runner's maximum of 32 calls.\"],\n MODEL_TOOL_CALL_NULL: [\"The model returned a null tool-call entry.\", \"Check the model adapter's tool-call conversion; each entry must have a name and arguments object.\"],\n MODEL_TOOL_CALL_NOT_OBJECT: [\"The model returned a tool-call entry that was not an object.\", \"Check the model adapter's tool-call conversion; arrays and primitive entries are invalid.\"],\n MODEL_TOOL_CALL_NAME_MISSING: [\"The model returned a tool call without a name field.\", \"Include a non-empty name string in every model tool call.\"],\n MODEL_TOOL_CALL_NAME_NOT_STRING: [\"The model returned a tool call whose name was not a string.\", \"Ensure the tool-call name is a string in the model adapter.\"],\n MODEL_TOOL_CALL_NAME_EMPTY: [\"The model returned a tool call with an empty name.\", \"Check provider tool naming and model adapter normalization.\"],\n MODEL_TOOL_CALL_NAME_TOO_LONG: [\"The model returned a tool-call name longer than 128 characters.\", \"Limit each tool-call name to 128 characters in the model adapter.\"],\n MODEL_TOOL_CALL_ARGUMENTS_MISSING: [\"The model returned a tool call without an arguments field.\", \"Include an arguments object for every model tool call.\"],\n MODEL_TOOL_CALL_ARGUMENTS_NULL: [\"The model returned a tool call with null arguments.\", \"Return an object, including an empty object when appropriate, instead of null arguments.\"],\n MODEL_TOOL_CALL_ARGUMENTS_NOT_OBJECT: [\"The model returned tool-call arguments that were not an object.\", \"Convert provider tool-call arguments to a non-array object in the model adapter.\"],\n MODEL_TEXT_NOT_STRING: [\"The model's candidate reply was not text.\", \"Check the model adapter's conversion of the provider response content.\"],\n MODEL_TEXT_TOO_LONG: [\"The model's candidate reply exceeded 4096 characters.\", \"Limit the natural-language reply to the response contract's maximum length.\"],\n MODEL_STEP_TOO_LARGE: [\"The model step exceeded the 262144-byte serialized payload limit.\", \"Reduce tool-call argument payload size before returning the model step.\"],\n MODEL_STEP_NOT_SERIALIZABLE: [\"The model step could not be encoded as canonical JSON.\", \"Inspect model-step data for cycles, invalid Unicode or unsupported JSON values.\"],\n MODEL_STEP_VALIDATION_FAILED: [\"The runner threw an exception while reading or checking the model step.\", \"Inspect the safe exception metadata and model-step adapter implementation.\"],\n MODEL_CALL_ROUTE_INVALID: [\"The model produced neither a usable reply nor an allowed tool-call route, or mixed finalResponse with other calls.\", \"Inspect provider finish conditions, reply content and tool-call routing.\"],\n MODEL_RESULT_MISSING: [\"The runner graph finished without producing a final response.\", \"Inspect graph termination and the preceding model/tool steps.\"],\n REQUEST_INVALID: [\"The model adapter received an invalid invocation request.\", \"Inspect the model request construction and trusted payload validation.\"],\n MODEL_REQUEST_HISTORY_INVALID: [\"A history entry sent to the model was malformed or exceeded its text limit.\", \"Inspect the history formatter and permitted role/text shape.\"],\n MODEL_REQUEST_TOOL_RESULT_INVALID: [\"A tool-result message sent to the model failed the expected contract.\", \"Inspect the tool-result formatter and CommerceToolResult schema.\"],\n MODEL_CONFIGURATION_INVALID: [\"The configured model provider, model ID, options or schema version is invalid.\", \"Correct model selection and published configuration before invoking the model.\"],\n MODEL_CREDENTIAL_INVALID: [\"The model adapter received an empty or invalid credential value.\", \"Check secret resolution, credential availability and injection into the model host.\"],\n MODEL_ADAPTER_INITIALIZATION_FAILED: [\"The model client could not be constructed from its validated configuration.\", \"Inspect model SDK initialization and dependency compatibility.\"],\n MODEL_REQUEST_CANCELLED: [\"The model request was cancelled before invocation.\", \"Inspect caller cancellation; do not interpret this as provider downtime.\"],\n TOOL_BIND_FAILED: [\"The model client rejected the supplied tool definitions during binding.\", \"Check provider tool-calling support and function schemas.\"],\n PROVIDER_AUTH_FAILED: [\"The model provider rejected authentication or access to the requested resource.\", \"Check the provider credential, permissions and selected model.\"],\n PROVIDER_RATE_LIMITED: [\"The model provider returned HTTP 429: requests are being rate-limited or the quota is exhausted.\", \"Check provider usage, billing/quota and any Retry-After guidance; use bounded backoff.\"],\n PROVIDER_HTTP_ERROR: [\"The model provider returned an unsuccessful HTTP response.\", \"Inspect statusCode and check provider availability, model access and request compatibility.\"],\n PROVIDER_REQUEST_FAILED: [\"The model provider request failed before a usable HTTP response was received.\", \"Inspect transportCode and exceptionName; check DNS, connectivity, TLS and provider availability.\"],\n PROVIDER_RESPONSE_INVALID: [\"The model provider returned a response that the adapter cannot interpret.\", \"Check the provider SDK response shape and model adapter compatibility.\"],\n PROVIDER_RESPONSE_USAGE_INVALID: [\"The model response is missing a valid output-token usage count.\", \"Inspect provider usage_metadata.output_tokens and SDK response mapping.\"],\n PROVIDER_RESPONSE_TOOL_CALL_INVALID: [\"The model response contains a malformed tool call name or arguments object.\", \"Inspect provider tool-call formatting and SDK response mapping.\"],\n TOOL_DESCRIPTOR_MISMATCH: [\"The requested tool does not match an authorised published tool descriptor.\", \"Check published tool revisions and descriptor hashes.\"],\n TOOL_AUTHORIZATION_FAILED: [\"The host failed while checking whether a requested tool remains authorised.\", \"Inspect authorization-service availability and the requested tool.\"],\n TOOL_INPUT_INVALID: [\"The model supplied tool arguments that do not satisfy the tool input schema.\", \"Inspect issuePaths and the model's tool schema; do not invoke the tool.\"],\n TOOL_CALL_BUDGET_EXHAUSTED: [\"The turn exhausted its remote-call allowance before the next tool request.\", \"Inspect repeated tool calls, retry attempts and budget settings.\"],\n TOOL_EXECUTION_FAILED: [\"An authorised tool threw an exception during execution.\", \"Inspect the toolName, attempt, transportCode and owning tool service.\"],\n TOOL_RESULT_TOO_LARGE: [\"A tool returned more data than the runner permits.\", \"Reduce or paginate tool output before returning it to the runner.\"],\n TOOL_RESULT_INVALID: [\"A tool returned a result that failed serialization or schema validation.\", \"Inspect issuePaths and the tool adapter's CommerceToolResult output.\"],\n TOOL_RESULT_MISSING: [\"The tool execution loop ended without receiving a result.\", \"Inspect tool execution lifecycle and retry completion.\"],\n TOOL_STALE_TURN: [\"The tool reported that the conversation turn is no longer current.\", \"Inspect conversation versioning and abandon this obsolete turn.\"],\n EVIDENCE_INVALID: [\"Tool evidence failed its integrity, grant or freshness checks.\", \"Inspect tool evidence generation and turn/grant correlation.\"],\n FINAL_SCHEMA_INVALID: [\"The model's finalResponse failed runtime schema validation.\", \"Inspect issueCodes and issuePaths; correct response formatting or tool instructions.\"],\n FINAL_LANGUAGE_MISMATCH: [\"The final answer included language detection despite an explicit customer language.\", \"Check model instructions governing language metadata.\"],\n FINAL_REFERRAL_REQUIRED: [\"The model did not produce the mandatory store referral after a denied or failed tool.\", \"Check final-response referral rules and the recorded tool outcome.\"],\n FINAL_EVIDENCE_INVALID: [\"The final answer cited absent, unqualified or incorrectly scoped evidence.\", \"Inspect evidence identity, qualification and answer construction.\"],\n FINAL_EVIDENCE_EXPIRED: [\"The final answer cited evidence that expired during the turn.\", \"Refresh evidence and reconsider the turn's remaining deadline.\"],\n EVIDENCE_RESERVATION_EXCEEDED: [\"The final answer cited more evidence than the remaining remote-call reservation allows.\", \"Inspect evidence references and the remote-call budget reservation contract.\"],\n} as const satisfies Record<RunnerDiagnosticReason, readonly [string, string]>;\n\nconst transports = {\n ENOTFOUND: [\"The provider hostname could not be resolved by DNS.\", \"Check the configured hostname and DNS resolver.\"],\n EAI_AGAIN: [\"The provider DNS lookup temporarily failed.\", \"Check DNS availability and retry after a short delay.\"],\n ECONNREFUSED: [\"The provider connection was actively refused.\", \"Check the host, port and service listener.\"],\n ECONNRESET: [\"The provider connection was reset before the request finished.\", \"Check upstream restarts, proxies and connection stability.\"],\n ETIMEDOUT: [\"The network connection to the provider timed out.\", \"Check network reachability and upstream latency.\"],\n ENETUNREACH: [\"The network route to the provider is unreachable.\", \"Check egress routes, firewall and network configuration.\"],\n EHOSTUNREACH: [\"The provider host is unreachable.\", \"Check provider host, routing and firewall rules.\"],\n EPIPE: [\"The network connection closed while data was being written.\", \"Check upstream connection stability and proxy timeouts.\"],\n UND_ERR_CONNECT_TIMEOUT: [\"The HTTP client timed out establishing a provider connection.\", \"Check egress connectivity and provider latency.\"],\n UND_ERR_HEADERS_TIMEOUT: [\"The HTTP client timed out waiting for provider response headers.\", \"Check provider availability and response latency.\"],\n UND_ERR_SOCKET: [\"The HTTP client reported a socket failure.\", \"Check network and upstream connection stability.\"],\n CERT_HAS_EXPIRED: [\"The remote TLS certificate has expired.\", \"Check the certificate chain and provider endpoint.\"],\n UNABLE_TO_VERIFY_LEAF_SIGNATURE: [\"TLS certificate chain verification failed.\", \"Check trusted roots and certificate chain; do not disable TLS validation.\"],\n DEPTH_ZERO_SELF_SIGNED_CERT: [\"TLS rejected an untrusted self-signed certificate.\", \"Check trusted roots and proxy certificates; do not disable TLS validation.\"],\n ERR_TLS_CERT_ALTNAME_INVALID: [\"The provider TLS certificate does not match its hostname.\", \"Check endpoint hostname and certificate SANs.\"],\n} as const satisfies Record<RunnerTransportCode, readonly [string, string]>;\n\n/** HTTP descriptions are based on status alone and make no claims about hidden provider response bodies. */\nconst httpDetails: Readonly<Record<number, Explanation>> = {\n 400: { message: \"The provider rejected the request as invalid (HTTP 400).\", action: \"Check request parameters and provider model capabilities.\" },\n 402: { message: \"The provider returned HTTP 402 (payment or credits required).\", action: \"Check provider billing, available credits and account limits.\" },\n 404: { message: \"The provider returned HTTP 404 (endpoint or model not found).\", action: \"Check provider model ID, permissions and API route.\" },\n 413: { message: \"The provider rejected an oversized request (HTTP 413).\", action: \"Reduce request size and review model context limits.\" },\n 422: { message: \"The provider could not process the request parameters (HTTP 422).\", action: \"Check model input and tool schema compatibility.\" },\n 500: { message: \"The provider returned an internal server error (HTTP 500).\", action: \"Check provider status and use bounded retries.\" },\n 502: { message: \"The provider gateway reported an upstream error (HTTP 502).\", action: \"Check provider status, gateway health and bounded retries.\" },\n 503: { message: \"The provider is temporarily unavailable (HTTP 503).\", action: \"Check provider availability and backoff guidance.\" },\n 504: { message: \"The provider gateway timed out (HTTP 504).\", action: \"Check upstream latency and provider availability.\" },\n};\n\nexport function describeRunnerDiagnostic(input: {\n reasonCode: RunnerDiagnosticReason;\n statusCode?: number;\n transportCode?: RunnerTransportCode;\n callIndex?: number;\n}): Explanation {\n if (\n input.transportCode &&\n [\"PROVIDER_REQUEST_FAILED\", \"UPSTREAM_OPERATION_FAILED\", \"MODEL_INVOCATION_FAILED\", \"TOOL_EXECUTION_FAILED\", \"TOOL_AUTHORIZATION_FAILED\"].includes(input.reasonCode)\n ) {\n const explanation = transports[input.transportCode];\n return { message: `${explanation[0]} (${input.transportCode})`, action: explanation[1] };\n }\n if (input.reasonCode === \"PROVIDER_HTTP_ERROR\" && input.statusCode !== undefined && httpDetails[input.statusCode])\n return httpDetails[input.statusCode];\n const explanation = explanations[input.reasonCode];\n if (input.reasonCode === \"MODEL_TOOL_CALL_NULL\" && input.callIndex !== undefined)\n return {\n message: `The model returned a null tool-call entry at index ${input.callIndex}. Each tool call must contain a non-empty name and an arguments object.`,\n action: explanation[1],\n };\n if (input.reasonCode.startsWith(\"MODEL_TOOL_CALL_\") && input.callIndex !== undefined)\n return { message: `${explanation[0].replace(/\\.$/, \"\")} (index ${input.callIndex}).`, action: explanation[1] };\n return { message: explanation[0], action: explanation[1] };\n}\n","import type { ZodError } from \"zod\";\nimport { describeRunnerDiagnostic } from \"./diagnostic-messages.js\";\n\n/** Finite, non-customer-facing failure taxonomy for Commerce hosts and logs. */\nexport const RUNNER_DIAGNOSTIC_STAGES = [\n \"preflight.validate\", \"preflight.contract\", \"runtime.cancellation\", \"runtime.deadline\",\n \"runtime.operation\", \"tools.resolve\", \"model.invoke\", \"model.validate\", \"model.route\",\n \"tool.authorize\", \"tool.validate\", \"tool.execute\", \"tool.result\", \"evidence.validate\",\n \"final.validate\", \"graph.execute\",\n] as const;\nexport type RunnerDiagnosticStage = typeof RUNNER_DIAGNOSTIC_STAGES[number];\n\nexport const RUNNER_DIAGNOSTIC_REASONS = [\n \"CANCELLED_BY_CALLER\", \"TURN_DEADLINE_EXCEEDED\", \"OPERATION_TIMEOUT\",\n \"UPSTREAM_OPERATION_FAILED\", \"UNEXPECTED_EXCEPTION\",\n \"INPUT_TOO_LARGE\", \"TURN_INVALID\", \"GRANT_INVALID\", \"MANIFEST_INVALID\",\n \"GRANT_MISMATCH\", \"RUNNER_VERSION_INCOMPATIBLE\", \"RESPONSE_CONTRACT_INVALID\",\n \"LANGUAGE_INVALID\", \"BUDGET_INVALID\", \"CONTEXT_INVALID\", \"TOOL_REGISTRATION_INVALID\",\n \"MODEL_STEP_BUDGET_EXHAUSTED\", \"MODEL_INVOCATION_FAILED\",\n \"MODEL_STEP_NOT_OBJECT\", \"MODEL_STEP_CALLS_MISSING\", \"MODEL_STEP_CALLS_NOT_ARRAY\",\n \"MODEL_OUTPUT_TOKENS_MISSING\", \"MODEL_OUTPUT_TOKENS_NOT_SAFE_INTEGER\",\n \"MODEL_OUTPUT_TOKENS_NEGATIVE\", \"MODEL_OUTPUT_TOKENS_OVER_BUDGET\",\n \"MODEL_TOOL_CALL_LIMIT_EXCEEDED\", \"MODEL_TOOL_CALL_NULL\", \"MODEL_TOOL_CALL_NOT_OBJECT\",\n \"MODEL_TOOL_CALL_NAME_MISSING\", \"MODEL_TOOL_CALL_NAME_NOT_STRING\", \"MODEL_TOOL_CALL_NAME_EMPTY\",\n \"MODEL_TOOL_CALL_NAME_TOO_LONG\", \"MODEL_TOOL_CALL_ARGUMENTS_MISSING\",\n \"MODEL_TOOL_CALL_ARGUMENTS_NULL\", \"MODEL_TOOL_CALL_ARGUMENTS_NOT_OBJECT\",\n \"MODEL_TEXT_NOT_STRING\", \"MODEL_TEXT_TOO_LONG\",\n \"MODEL_STEP_TOO_LARGE\", \"MODEL_STEP_NOT_SERIALIZABLE\", \"MODEL_STEP_VALIDATION_FAILED\",\n \"MODEL_CALL_ROUTE_INVALID\", \"MODEL_RESULT_MISSING\",\n \"REQUEST_INVALID\", \"MODEL_REQUEST_HISTORY_INVALID\", \"MODEL_REQUEST_TOOL_RESULT_INVALID\",\n \"MODEL_CONFIGURATION_INVALID\", \"MODEL_CREDENTIAL_INVALID\", \"MODEL_ADAPTER_INITIALIZATION_FAILED\",\n \"MODEL_REQUEST_CANCELLED\", \"TOOL_BIND_FAILED\", \"PROVIDER_AUTH_FAILED\", \"PROVIDER_RATE_LIMITED\",\n \"PROVIDER_HTTP_ERROR\", \"PROVIDER_REQUEST_FAILED\", \"PROVIDER_RESPONSE_INVALID\",\n \"PROVIDER_RESPONSE_USAGE_INVALID\", \"PROVIDER_RESPONSE_TOOL_CALL_INVALID\",\n \"TOOL_DESCRIPTOR_MISMATCH\", \"TOOL_AUTHORIZATION_FAILED\", \"TOOL_INPUT_INVALID\",\n \"TOOL_CALL_BUDGET_EXHAUSTED\", \"TOOL_EXECUTION_FAILED\", \"TOOL_RESULT_TOO_LARGE\",\n \"TOOL_RESULT_INVALID\", \"TOOL_RESULT_MISSING\", \"TOOL_STALE_TURN\",\n \"EVIDENCE_INVALID\", \"FINAL_SCHEMA_INVALID\", \"FINAL_LANGUAGE_MISMATCH\",\n \"FINAL_REFERRAL_REQUIRED\", \"FINAL_EVIDENCE_INVALID\", \"FINAL_EVIDENCE_EXPIRED\",\n \"EVIDENCE_RESERVATION_EXCEEDED\",\n] as const;\nexport type RunnerDiagnosticReason = typeof RUNNER_DIAGNOSTIC_REASONS[number];\n\n/** Recognised transport failures, never arbitrary OS/provider error codes. */\nexport const RUNNER_TRANSPORT_CODES = [\n \"ENOTFOUND\", \"EAI_AGAIN\", \"ECONNREFUSED\", \"ECONNRESET\", \"ETIMEDOUT\",\n \"ENETUNREACH\", \"EHOSTUNREACH\", \"EPIPE\", \"UND_ERR_CONNECT_TIMEOUT\",\n \"UND_ERR_HEADERS_TIMEOUT\", \"UND_ERR_SOCKET\", \"CERT_HAS_EXPIRED\",\n \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\", \"DEPTH_ZERO_SELF_SIGNED_CERT\",\n \"ERR_TLS_CERT_ALTNAME_INVALID\",\n] as const;\nexport type RunnerTransportCode = typeof RUNNER_TRANSPORT_CODES[number];\n\nconst recognizedExceptionNames = new Set([\n \"TypeError\", \"ReferenceError\", \"SyntaxError\", \"RangeError\", \"URIError\",\n \"EvalError\", \"AggregateError\", \"AbortError\", \"TimeoutError\",\n]);\nconst recognizedTransportCodes = new Set<string>(RUNNER_TRANSPORT_CODES);\n\n\n/** All optional fields are allowlisted; never include free-form provider or exception text. */\nexport type RunnerDiagnostic = Readonly<{\n stage: RunnerDiagnosticStage;\n reasonCode: RunnerDiagnosticReason;\n /** Fixed, safe operator explanation; computed from the validated code and detail. */\n reasonMessage?: string;\n /** Fixed, safe next step for the operator; never provider-supplied. */\n operatorAction?: string;\n /** Transport/exception details are from a fixed allowlist, not error.message. */\n transportCode?: RunnerTransportCode;\n exceptionName?: string;\n statusCode?: number;\n providerCode?: number;\n providerStage?: \"request\" | \"binding\" | \"provider\" | \"response\";\n modelStep?: number;\n /** Zero-based index for a rejected tool call. Never includes user content. */\n callIndex?: number;\n remoteCallNumber?: number;\n attempt?: number;\n toolName?: string;\n issueCount?: number;\n issueCodes?: readonly string[];\n issuePaths?: readonly string[];\n}>;\n\nconst stages = new Set<string>(RUNNER_DIAGNOSTIC_STAGES);\nconst reasons = new Set<string>(RUNNER_DIAGNOSTIC_REASONS);\nconst issueCodes = new Set([\n \"invalid_type\", \"too_big\", \"too_small\", \"invalid_format\", \"invalid_value\",\n \"unrecognized_keys\", \"invalid_union\", \"invalid_key\", \"invalid_element\", \"custom\",\n]);\nconst knownPathParts = new Set([\n \"turn\", \"grant\", \"manifest\", \"shopId\", \"conversationId\", \"checkoutRecoveryId\",\n \"inboundVersion\", \"initialInboundVersion\", \"id\", \"releaseId\", \"responseContract\",\n \"responseContractHash\", \"runnerCompatibility\", \"grantedTools\", \"capabilities\",\n \"featureBehaviours\", \"language\", \"tag\", \"source\", \"budget\", \"modelSteps\",\n \"remoteCalls\", \"deadlineMs\", \"outputTokens\", \"history\", \"context\", \"instructions\",\n \"answerKind\", \"referralReason\", \"replyText\", \"detectedLanguageTag\", \"evidenceIds\",\n \"details\", \"contractVersion\", \"status\", \"data\", \"renderedText\", \"calls\",\n \"name\", \"arguments\", \"toolName\", \"toolId\", \"toolRevisionId\", \"definitionVersion\",\n]);\n\nfunction boundedInt(value: unknown, minimum: number, maximum: number): number | undefined {\n return Number.isSafeInteger(value) && (value as number) >= minimum && (value as number) <= maximum\n ? value as number\n : undefined;\n}\n\n/** Also sanitize internally constructed diagnostics before exposing them to hosts or logs. */\nexport function safeRunnerDiagnostic(diagnostic: RunnerDiagnostic): RunnerDiagnostic {\n const fields: RunnerDiagnostic = {\n stage: stages.has(diagnostic.stage) ? diagnostic.stage : \"graph.execute\",\n reasonCode: reasons.has(diagnostic.reasonCode) ? diagnostic.reasonCode : \"UNEXPECTED_EXCEPTION\",\n ...(recognizedTransportCodes.has(diagnostic.transportCode ?? \"\") ? { transportCode: diagnostic.transportCode } : {}),\n ...(recognizedExceptionNames.has(diagnostic.exceptionName ?? \"\") ? { exceptionName: diagnostic.exceptionName } : {}),\n ...(boundedInt(diagnostic.statusCode, 100, 599) === undefined ? {} : { statusCode: diagnostic.statusCode }),\n ...(boundedInt(diagnostic.providerCode, 0, 999999) === undefined ? {} : { providerCode: diagnostic.providerCode }),\n ...([\"request\", \"binding\", \"provider\", \"response\"].includes(diagnostic.providerStage ?? \"\")\n ? { providerStage: diagnostic.providerStage } : {}),\n ...(boundedInt(diagnostic.modelStep, 1, 12) === undefined ? {} : { modelStep: diagnostic.modelStep }),\n ...(boundedInt(diagnostic.callIndex, 0, 31) === undefined ? {} : { callIndex: diagnostic.callIndex }),\n ...(boundedInt(diagnostic.remoteCallNumber, 1, 10) === undefined ? {} : { remoteCallNumber: diagnostic.remoteCallNumber }),\n ...(boundedInt(diagnostic.attempt, 1, 2) === undefined ? {} : { attempt: diagnostic.attempt }),\n ...(typeof diagnostic.toolName === \"string\" && /^[a-z][a-z0-9_]{0,127}$/.test(diagnostic.toolName)\n ? { toolName: diagnostic.toolName } : {}),\n ...(boundedInt(diagnostic.issueCount, 0, 10000) === undefined ? {} : { issueCount: diagnostic.issueCount }),\n ...(Array.isArray(diagnostic.issueCodes) ? {\n issueCodes: [...new Set(diagnostic.issueCodes.filter((code) => issueCodes.has(code)))].slice(0, 5),\n } : {}),\n ...(Array.isArray(diagnostic.issuePaths) ? {\n issuePaths: [...new Set(diagnostic.issuePaths.filter((path) =>\n typeof path === \"string\" && path.length <= 80 && path.split(\".\").every((part) => knownPathParts.has(part)),\n ))].slice(0, 5),\n } : {}),\n };\n const explanation = describeRunnerDiagnostic(fields);\n return Object.freeze({ ...fields, reasonMessage: explanation.message, operatorAction: explanation.action });\n}\n\n/** Extract only known error names and network codes from a bounded cause chain. */\nexport function safeRunnerCauseMetadata(error: unknown): Pick<RunnerDiagnostic, \"transportCode\" | \"exceptionName\"> {\n const visited = new Set<unknown>();\n let current = error;\n let transportCode: RunnerTransportCode | undefined;\n let exceptionName: string | undefined;\n for (let depth = 0; depth < 6 && current instanceof Error && !visited.has(current); depth += 1) {\n visited.add(current);\n try {\n const code = (current as Error & { code?: unknown }).code;\n if (typeof code === \"string\" && recognizedTransportCodes.has(code))\n transportCode = code as RunnerTransportCode;\n if (!exceptionName && recognizedExceptionNames.has(current.name)) exceptionName = current.name;\n current = current.cause;\n } catch {\n break; // A hostile error object/getter must not interrupt the original failure path.\n }\n }\n return {\n ...(transportCode ? { transportCode } : {}),\n ...(exceptionName ? { exceptionName } : {}),\n };\n}\n\n/** Only static schema keys and Zod's finite issue-code vocabulary may leave this boundary. */\nexport function schemaIssueMetadata(error: ZodError): Pick<RunnerDiagnostic, \"issueCount\" | \"issueCodes\" | \"issuePaths\"> {\n const paths = error.issues.map((issue) =>\n issue.path.filter((part): part is string => typeof part === \"string\")\n .filter((part) => knownPathParts.has(part)).slice(0, 3).join(\".\"),\n ).filter(Boolean);\n return {\n issueCount: Math.min(error.issues.length, 10000),\n issueCodes: [...new Set(error.issues.map((issue) => issue.code).filter((code) => issueCodes.has(code)))].slice(0, 5),\n issuePaths: [...new Set(paths)].slice(0, 5),\n };\n}\n","import type { RunCommerceTurnInput, RunnerErrorCode, RunCommerceTurnResult } from \"./types.js\";\nimport { safeRunnerCauseMetadata, safeRunnerDiagnostic, type RunnerDiagnostic, type RunnerDiagnosticStage } from \"./diagnostics.js\";\n\nexport class RunnerFailure extends Error {\n readonly diagnostic: RunnerDiagnostic;\n\n constructor(readonly code: RunnerErrorCode, diagnostic: RunnerDiagnostic, cause?: unknown) {\n super(code, { cause });\n this.name = \"RunnerFailure\";\n this.diagnostic = safeRunnerDiagnostic({ ...safeRunnerCauseMetadata(cause), ...diagnostic });\n }\n}\n\n/** Typed, provider-independent cause that model invokers may throw. */\nexport class CommerceModelInvocationFailure extends Error {\n readonly diagnostic: RunnerDiagnostic;\n\n constructor(diagnostic: RunnerDiagnostic, cause?: unknown) {\n super(\"Commerce model unavailable\", { cause });\n this.name = \"CommerceModelInvocationFailure\";\n this.diagnostic = safeRunnerDiagnostic({ ...safeRunnerCauseMetadata(cause), ...diagnostic });\n }\n}\n\nfunction findRunnerFailure(error: unknown): RunnerFailure | undefined {\n const visited = new Set<unknown>();\n let current = error;\n for (let depth = 0; depth < 6 && current instanceof Error && !visited.has(current); depth += 1) {\n visited.add(current);\n if (current instanceof RunnerFailure) return current;\n current = current.cause;\n }\n return undefined;\n}\n\nexport function mapRunnerFailure(\n error: unknown, signal?: AbortSignal, fallbackStage: RunnerDiagnosticStage = \"graph.execute\",\n): RunCommerceTurnResult {\n const failure = findRunnerFailure(error);\n const code = failure?.code ?? (signal?.aborted ? \"CANCELLED\" : \"INVALID_INPUT\");\n const diagnostic = failure?.diagnostic ?? safeRunnerDiagnostic(signal?.aborted\n ? { stage: \"runtime.cancellation\", reasonCode: \"CANCELLED_BY_CALLER\" }\n : { stage: fallbackStage, reasonCode: \"UNEXPECTED_EXCEPTION\", ...safeRunnerCauseMetadata(error) });\n return {\n ok: false,\n error: { code, retryable: code === \"UNAVAILABLE\" || code === \"DEADLINE\", diagnostic },\n };\n}\n\nexport function failureCode(error: unknown): RunnerErrorCode {\n return findRunnerFailure(error)?.code ?? \"INVALID_INPUT\";\n}\n\nexport function inputSignal(input: RunCommerceTurnInput): AbortSignal {\n return input.signal;\n}\n"],"mappings":"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","names":["explanation"]}