@modainteract/moda-interact-shared 0.12.2 → 0.13.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.
@@ -0,0 +1,1027 @@
1
+ import {
2
+ canonicaliseLanguageTag,
3
+ normalizeCurrencyCode
4
+ } from "./chunk-B6Y3QQLI.js";
5
+
6
+ // src/commerce/canonical-json.ts
7
+ function canonicalJson(value, order = "codepoint") {
8
+ const seen = /* @__PURE__ */ new Set();
9
+ const compare = (a, b) => {
10
+ const aa = Array.from(a, (c) => c.codePointAt(0));
11
+ const bb = Array.from(b, (c) => c.codePointAt(0));
12
+ for (let i = 0; i < Math.min(aa.length, bb.length); i++)
13
+ if (aa[i] !== bb[i]) return aa[i] - bb[i];
14
+ return aa.length - bb.length;
15
+ };
16
+ function visit(v, depth) {
17
+ if (depth > 32) throw new TypeError("JSON depth exceeded");
18
+ if (v === null) return "null";
19
+ if (typeof v === "string") {
20
+ if (/\p{Surrogate}/u.test(v)) throw new TypeError("Invalid Unicode");
21
+ return JSON.stringify(v);
22
+ }
23
+ if (typeof v === "boolean") return String(v);
24
+ if (typeof v === "number" && Number.isFinite(v)) return JSON.stringify(v);
25
+ if (typeof v !== "object" || !v || seen.has(v))
26
+ throw new TypeError("Invalid JSON");
27
+ if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null)
28
+ throw new TypeError("Invalid JSON object");
29
+ seen.add(v);
30
+ let result;
31
+ if (Array.isArray(v)) {
32
+ if (Object.keys(v).length !== v.length)
33
+ throw new TypeError("Sparse or extended array");
34
+ result = "[" + v.map((item) => visit(item, depth + 1)).join(",") + "]";
35
+ } else {
36
+ const keys = Object.keys(v).sort(
37
+ order === "codepoint" ? compare : void 0
38
+ );
39
+ result = "{" + keys.map(
40
+ (k) => visit(k, depth + 1) + ":" + visit(v[k], depth + 1)
41
+ ).join(",") + "}";
42
+ }
43
+ seen.delete(v);
44
+ return result;
45
+ }
46
+ return visit(value, 0);
47
+ }
48
+ var jsonBytes = (value) => new TextEncoder().encode(canonicalJson(value)).length;
49
+ var responseContractCanonicalJson = (value) => canonicalJson(value, "utf16");
50
+
51
+ // src/commerce/subset.ts
52
+ import { z } from "zod";
53
+ var MONEY_PATTERN = "^[0-9]{1,18}(\\.[0-9]{1,6})?$";
54
+ var safeName = (name) => /^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(name) && !["__proto__", "prototype", "constructor"].includes(name);
55
+ var safePath = (path) => path.length <= 256 && path.split(".").every(safeName);
56
+ var keywords = [
57
+ "type",
58
+ "description",
59
+ "properties",
60
+ "required",
61
+ "additionalProperties",
62
+ "items",
63
+ "enum",
64
+ "minLength",
65
+ "maxLength",
66
+ "minimum",
67
+ "maximum",
68
+ "minItems",
69
+ "maxItems"
70
+ ];
71
+ function validateSubset(value, mode) {
72
+ let propertyCount = 0;
73
+ const maxDepth = mode === "input" ? 3 : 4;
74
+ function walk(raw, depth) {
75
+ if (depth > maxDepth || !raw || typeof raw !== "object" || Array.isArray(raw))
76
+ return false;
77
+ const s = raw;
78
+ if (Object.keys(s).some(
79
+ (k) => !keywords.includes(k) && !(mode === "input" && k === "pattern")
80
+ ))
81
+ return false;
82
+ const nullable = Array.isArray(s.type);
83
+ if (nullable && (mode !== "input" || s.type.length !== 2 || s.type[1] !== "null"))
84
+ return false;
85
+ const type = typeof s.type === "string" ? s.type : s.type[0];
86
+ if (![
87
+ "object",
88
+ "string",
89
+ "integer",
90
+ "boolean",
91
+ "array",
92
+ ...mode === "details" ? ["number"] : []
93
+ ].includes(type))
94
+ return false;
95
+ if (s.description !== void 0 && (typeof s.description !== "string" || s.description.length > 1e3))
96
+ return false;
97
+ const specific = {
98
+ object: ["properties", "required", "additionalProperties"],
99
+ array: ["items", "minItems", "maxItems"],
100
+ string: ["minLength", "maxLength", "pattern"],
101
+ integer: ["minimum", "maximum"],
102
+ number: ["minimum", "maximum"],
103
+ boolean: []
104
+ };
105
+ if (Object.keys(s).some(
106
+ (k) => !["type", "description", "enum"].includes(k) && !specific[type].includes(k)
107
+ ))
108
+ return false;
109
+ if (s.enum !== void 0 && (!Array.isArray(s.enum) || s.enum.length < 1 || s.enum.length > (mode === "input" ? 32 : 20) || new Set(s.enum.map((v) => canonicalJson(v))).size !== s.enum.length || s.enum.some(
110
+ (v) => v === null ? !nullable : typeof v !== (type === "integer" ? "number" : type) || type === "integer" && !Number.isSafeInteger(v) || typeof v === "object"
111
+ )))
112
+ return false;
113
+ for (const [lo, hi] of [
114
+ ["minLength", "maxLength"],
115
+ ["minItems", "maxItems"],
116
+ ["minimum", "maximum"]
117
+ ]) {
118
+ if (s[lo] !== void 0 && (typeof s[lo] !== "number" || !Number.isFinite(s[lo])))
119
+ return false;
120
+ if (s[hi] !== void 0 && (typeof s[hi] !== "number" || !Number.isFinite(s[hi])))
121
+ return false;
122
+ if (lo !== "minimum" && [s[lo], s[hi]].some(
123
+ (v) => v !== void 0 && (!Number.isSafeInteger(v) || v < 0)
124
+ ))
125
+ return false;
126
+ if (s[lo] !== void 0 && s[hi] !== void 0 && s[lo] > s[hi])
127
+ return false;
128
+ }
129
+ if (type === "string")
130
+ return s.maxLength !== void 0 && s.maxLength <= 4096 && (s.pattern === void 0 || s.pattern === MONEY_PATTERN);
131
+ if (type === "array")
132
+ return s.maxItems !== void 0 && s.maxItems <= (mode === "input" ? 100 : 20) && walk(s.items, depth + 1);
133
+ if (type === "object") {
134
+ if (s.additionalProperties !== false || !s.properties || typeof s.properties !== "object" || Array.isArray(s.properties))
135
+ return false;
136
+ const names = Object.keys(s.properties);
137
+ propertyCount += names.length;
138
+ if (propertyCount > 32 || names.some((n) => !safeName(n)) || s.required !== void 0 && (!Array.isArray(s.required) || new Set(s.required).size !== s.required.length || s.required.some((n) => !names.includes(n))))
139
+ return false;
140
+ return Object.values(s.properties).every(
141
+ (child) => walk(child, depth + 1)
142
+ );
143
+ }
144
+ return true;
145
+ }
146
+ try {
147
+ return jsonBytes(value) <= 32768 && walk(value, 1) && value.type === "object";
148
+ } catch {
149
+ return false;
150
+ }
151
+ }
152
+ var InputSchemaSchema = z.custom(
153
+ (v) => validateSubset(v, "input")
154
+ );
155
+ var DetailsSchemaSchema = z.custom(
156
+ (v) => validateSubset(v, "details")
157
+ );
158
+ function matchesSubset(schema, value) {
159
+ const nullable = Array.isArray(schema.type);
160
+ const type = typeof schema.type === "string" ? schema.type : schema.type[0];
161
+ if (value === null)
162
+ return nullable && (!schema.enum || schema.enum.includes(null));
163
+ if (schema.enum && !schema.enum.includes(value)) return false;
164
+ if (type === "string")
165
+ return typeof value === "string" && value.length >= (schema.minLength ?? 0) && value.length <= schema.maxLength && (!schema.pattern || new RegExp(schema.pattern).test(value));
166
+ if (type === "boolean") return typeof value === "boolean";
167
+ if (type === "integer" || type === "number")
168
+ return typeof value === "number" && Number.isFinite(value) && (type !== "integer" || Number.isSafeInteger(value)) && value >= (schema.minimum ?? -Infinity) && value <= (schema.maximum ?? Infinity);
169
+ if (type === "array")
170
+ return Array.isArray(value) && value.length >= (schema.minItems ?? 0) && value.length <= schema.maxItems && value.every((v) => matchesSubset(schema.items, v));
171
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
172
+ const obj = value;
173
+ return Object.keys(obj).every((k) => Object.hasOwn(schema.properties, k)) && (schema.required ?? []).every((k) => Object.hasOwn(obj, k)) && Object.entries(obj).every(
174
+ ([k, v]) => matchesSubset(schema.properties[k], v)
175
+ );
176
+ }
177
+ function compileSubset(schema, mode) {
178
+ if (!validateSubset(schema, mode)) throw new TypeError("Unsupported schema");
179
+ return z.custom((value) => {
180
+ try {
181
+ return jsonBytes(value) <= (mode === "details" ? 16384 : 131072) && matchesSubset(schema, value);
182
+ } catch {
183
+ return false;
184
+ }
185
+ });
186
+ }
187
+
188
+ // src/commerce/primitives.ts
189
+ import { z as z2 } from "zod";
190
+ var ContractVersionSchema = z2.literal("commerce.v1");
191
+ var IdSchema = z2.string().min(1).max(128).refine(
192
+ (v) => v.trim() === v && !["standalone", "product-only", "unknown-shop"].includes(v)
193
+ );
194
+ var SemverSchema = z2.string().max(64).regex(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/);
195
+ var HashSchema = z2.string().regex(/^[a-f0-9]{64}$/);
196
+ var DateSchema = z2.iso.datetime({ offset: false });
197
+ var MoneySchema = z2.string().regex(/^[0-9]{1,18}(\.[0-9]{1,6})?$/);
198
+ var CurrencySchema = z2.string().refine((v) => {
199
+ try {
200
+ return normalizeCurrencyCode(v) === v;
201
+ } catch {
202
+ return false;
203
+ }
204
+ });
205
+ var LanguageSchema = z2.string().max(128).refine((v) => {
206
+ try {
207
+ return canonicaliseLanguageTag(v) === v;
208
+ } catch {
209
+ return false;
210
+ }
211
+ });
212
+ var distinct = (values) => new Set(values).size === values.length;
213
+ var boundedJson = (bytes) => z2.unknown().refine((v) => {
214
+ try {
215
+ return jsonBytes(v) <= bytes;
216
+ } catch {
217
+ return false;
218
+ }
219
+ });
220
+
221
+ // src/commerce/definitions.ts
222
+ import { z as z3 } from "zod";
223
+ import { gt } from "semver";
224
+ var ToolNameSchema = z3.string().regex(/^[a-z][a-z0-9_]{0,127}$/).refine((n) => n !== "finalResponse");
225
+ var ToolBindingSchema = z3.strictObject({
226
+ toolId: IdSchema,
227
+ toolRevisionId: IdSchema
228
+ });
229
+ var ToolBindingsSchema = z3.array(ToolBindingSchema).max(32).refine((v) => distinct(v.map((x) => x.toolId)));
230
+ var GrantedToolSchema = z3.strictObject({
231
+ toolId: IdSchema,
232
+ toolRevisionId: IdSchema,
233
+ toolName: ToolNameSchema,
234
+ definitionVersion: SemverSchema,
235
+ capabilityKeys: z3.array(IdSchema).min(1).max(32).refine((v) => distinct(v) && v.join("\0") === [...v].sort().join("\0"))
236
+ });
237
+ var GrantedToolsSchema = z3.array(GrantedToolSchema).max(32).refine(
238
+ (v) => distinct(v.map((x) => x.toolId)) && distinct(v.map((x) => x.toolName))
239
+ );
240
+ var ToolDescriptorSchema = z3.strictObject({
241
+ toolId: IdSchema,
242
+ toolRevisionId: IdSchema,
243
+ name: ToolNameSchema,
244
+ definitionVersion: SemverSchema,
245
+ description: z3.string().min(1).max(4096),
246
+ inputSchema: InputSchemaSchema
247
+ });
248
+ var MappingSchema = z3.union([
249
+ z3.strictObject({
250
+ input: z3.string().refine(safeName),
251
+ omitIfMissing: z3.literal(true).optional()
252
+ }),
253
+ z3.strictObject({ literal: boundedJson(8192) })
254
+ ]);
255
+ var MappingsSchema = z3.record(z3.string().refine(safeName), MappingSchema).refine((v) => Object.keys(v).length <= 32);
256
+ var POLICY_OPERATIONS = [
257
+ "recovery.getBasket",
258
+ "shopify.searchProducts",
259
+ "discounts.getOptions",
260
+ "discounts.evaluate",
261
+ "products.findQualifying",
262
+ "products.findSimilar"
263
+ ];
264
+ var CommerceExecutionSchema = z3.discriminatedUnion("kind", [
265
+ z3.strictObject({
266
+ kind: z3.literal("SHOPIFY_STOREFRONT_QUERY"),
267
+ executorVersion: z3.literal("1.0.0"),
268
+ apiVersion: z3.literal("2026-07"),
269
+ schemaHash: HashSchema,
270
+ document: z3.string().min(1).refine((v) => new TextEncoder().encode(v).length <= 16384),
271
+ operationName: z3.string().refine(safeName),
272
+ variables: MappingsSchema,
273
+ resultPath: z3.string().refine(safePath)
274
+ }),
275
+ z3.strictObject({
276
+ kind: z3.literal("POLICY_OPERATION"),
277
+ operation: z3.enum(POLICY_OPERATIONS),
278
+ operationVersion: z3.literal("1.0.0"),
279
+ arguments: MappingsSchema
280
+ })
281
+ ]);
282
+ function tokensValid(text, root) {
283
+ if (/[<>]/.test(text)) return false;
284
+ const residue = text.replace(
285
+ /\{\{([^{}]+)\}\}/g,
286
+ (_all, path) => path.startsWith(root + ".") && safePath(path.slice(root.length + 1)) ? "" : "{{invalid}}"
287
+ );
288
+ return !residue.includes("{{") && !residue.includes("}}");
289
+ }
290
+ var fixed = z3.string().max(4096).refine((v) => !/[<>]|\{\{|\}\}/.test(v));
291
+ var ResponseTemplateSchema = z3.discriminatedUnion("kind", [
292
+ z3.strictObject({
293
+ kind: z3.literal("text"),
294
+ text: z3.string().max(4096).refine((v) => tokensValid(v, "result")),
295
+ unavailable: fixed
296
+ }),
297
+ z3.strictObject({
298
+ kind: z3.literal("items"),
299
+ itemsPath: z3.string().refine(safePath),
300
+ item: z3.string().max(4096).refine((v) => tokensValid(v, "item")),
301
+ empty: fixed,
302
+ unavailable: fixed
303
+ })
304
+ ]);
305
+ var identity = {
306
+ name: ToolNameSchema,
307
+ definitionVersion: SemverSchema,
308
+ description: z3.string().min(1).max(4096)
309
+ };
310
+ var forbiddenInput = /^(shopid|customerid|checkoutrecoveryid|conversationid|grantid|releaseid|token|accesstoken|domain|headers|url|graphql|document|authorization)$/i;
311
+ var CommerceToolDefinitionSchema = z3.strictObject({
312
+ ...identity,
313
+ inputSchema: InputSchemaSchema,
314
+ execution: CommerceExecutionSchema,
315
+ responseTemplate: ResponseTemplateSchema
316
+ }).superRefine((d, ctx) => {
317
+ const properties = d.inputSchema.properties;
318
+ const containsAuthority = (schema) => Object.entries(schema.properties ?? {}).some(
319
+ ([name, child]) => forbiddenInput.test(name) || containsAuthority(child)
320
+ ) || !!(schema.items && containsAuthority(schema.items));
321
+ if (containsAuthority(d.inputSchema))
322
+ ctx.addIssue({
323
+ code: "custom",
324
+ message: "Authority/credential inputs are forbidden"
325
+ });
326
+ const mapping = d.execution.kind === "POLICY_OPERATION" ? d.execution.arguments : d.execution.variables;
327
+ for (const [name, m] of Object.entries(mapping))
328
+ if ("input" in m && !Object.hasOwn(properties, m.input))
329
+ ctx.addIssue({
330
+ code: "custom",
331
+ path: ["execution", name],
332
+ message: "Unknown mapped input"
333
+ });
334
+ });
335
+ var draftObject = boundedJson(65536).refine(
336
+ (v) => !!v && typeof v === "object" && !Array.isArray(v)
337
+ );
338
+ var CommerceToolDraftDefinitionSchema = z3.strictObject({
339
+ ...identity,
340
+ inputSchema: draftObject,
341
+ execution: draftObject,
342
+ responseTemplate: draftObject
343
+ }).refine((v) => new TextEncoder().encode(canonicalJson(v)).length <= 131072);
344
+ function at(schema, path) {
345
+ let current = schema;
346
+ for (const part of path.split(".")) current = current?.properties?.[part];
347
+ return current;
348
+ }
349
+ function validateDefinitionForPublication(raw, compiler) {
350
+ const definition = CommerceToolDefinitionSchema.parse(raw);
351
+ const compiled = compiler.compile(
352
+ definition.execution,
353
+ definition.inputSchema
354
+ );
355
+ if (!compiled.validateMappedArguments(definition.execution))
356
+ throw new TypeError("Invalid variable/operation mapping");
357
+ const template = definition.responseTemplate;
358
+ const schema = template.kind === "text" ? compiled.outputSchema : at(compiled.outputSchema, template.itemsPath)?.items;
359
+ if (!schema) throw new TypeError("Unknown template items path");
360
+ const text = template.kind === "text" ? template.text : template.item;
361
+ for (const match of text.matchAll(/\{\{(?:result|item)\.([^{}]+)\}\}/g)) {
362
+ const target = at(schema, match[1]);
363
+ const type = target && (Array.isArray(target.type) ? target.type[0] : target.type);
364
+ if (!type || ["object", "array"].includes(type))
365
+ throw new TypeError("Unknown or nonscalar template path");
366
+ }
367
+ return definition;
368
+ }
369
+ function definitionToMcpDescriptor(definition) {
370
+ const d = CommerceToolDefinitionSchema.parse(definition);
371
+ return {
372
+ name: d.name,
373
+ description: d.description,
374
+ inputSchema: d.inputSchema
375
+ };
376
+ }
377
+ function mapToolArguments(definition, raw) {
378
+ const input = compileSubset(definition.inputSchema, "input").parse(raw);
379
+ const mapping = definition.execution.kind === "POLICY_OPERATION" ? definition.execution.arguments : definition.execution.variables;
380
+ const result = /* @__PURE__ */ Object.create(null);
381
+ for (const [key, m] of Object.entries(mapping)) {
382
+ if ("literal" in m) result[key] = m.literal;
383
+ else if (Object.hasOwn(input, m.input)) result[key] = input[m.input];
384
+ else if (!m.omitIfMissing) throw new TypeError("Missing mapped input");
385
+ }
386
+ return result;
387
+ }
388
+ var toolHashInput = (definition) => ({
389
+ contractVersion: ContractVersionSchema.value,
390
+ definition: CommerceToolDefinitionSchema.parse(definition)
391
+ });
392
+ function capabilityHashInput(promptTemplate, configuration, toolBindings) {
393
+ if (!promptTemplate.trim() || promptTemplate.length > 32e3)
394
+ throw new TypeError("Invalid capability prompt");
395
+ return {
396
+ contractVersion: ContractVersionSchema.value,
397
+ promptTemplate,
398
+ configuration,
399
+ toolBindings: ToolBindingsSchema.parse(toolBindings).sort(
400
+ (a, b) => a.toolId < b.toolId ? -1 : 1
401
+ )
402
+ };
403
+ }
404
+ function validateDefinitionVersion(proposed, highestPublished) {
405
+ SemverSchema.parse(proposed);
406
+ if (highestPublished === null) return proposed === "1.0.0";
407
+ SemverSchema.parse(highestPublished);
408
+ return gt(proposed, highestPublished);
409
+ }
410
+ var CommerceToolIdentitySchema = z3.strictObject({
411
+ id: IdSchema,
412
+ name: ToolNameSchema,
413
+ enabled: z3.boolean()
414
+ });
415
+ var CommerceToolRevisionIdentitySchema = z3.strictObject({
416
+ toolId: IdSchema,
417
+ toolRevisionId: IdSchema,
418
+ definitionVersion: SemverSchema,
419
+ contentHash: HashSchema
420
+ });
421
+ var POLICY_OPERATION_DESCRIPTORS = POLICY_OPERATIONS.map(
422
+ (operation) => ({ operation, operationVersion: "1.0.0" })
423
+ );
424
+
425
+ // src/commerce/response.ts
426
+ import { z as z4 } from "zod";
427
+ var CommerceResponseContractSchema = z4.strictObject({
428
+ version: z4.literal("response.v1"),
429
+ instructions: z4.string().min(1).max(8e3),
430
+ detailsSchema: DetailsSchemaSchema
431
+ }).refine((v) => jsonBytes(v) <= 32768);
432
+ var EMPTY_RESPONSE_CONTRACT = {
433
+ version: "response.v1",
434
+ instructions: "Write a concise, natural WhatsApp reply supported by the available facts. Return an empty details object.",
435
+ detailsSchema: {
436
+ type: "object",
437
+ properties: {},
438
+ required: [],
439
+ additionalProperties: false
440
+ }
441
+ };
442
+ var ReferralReasonSchema = z4.enum([
443
+ "INSUFFICIENT_TOOLS",
444
+ "UNVERIFIABLE_FACTS",
445
+ "TOOL_UNAVAILABLE",
446
+ "TOOL_REVOKED"
447
+ ]);
448
+ var CommerceFinalResponseSchema = z4.strictObject({
449
+ answerKind: z4.enum(["ANSWER", "REFER_TO_STORE"]),
450
+ replyText: z4.string().min(1).max(4096),
451
+ referralReason: ReferralReasonSchema.nullable(),
452
+ detectedLanguageTag: LanguageSchema.nullable(),
453
+ detectedLanguageConfidence: z4.number().min(0).max(1).nullable(),
454
+ evidenceIds: z4.array(IdSchema).max(3).refine((v) => new Set(v).size === v.length),
455
+ details: z4.record(z4.string(), z4.unknown()).refine((v) => {
456
+ try {
457
+ return jsonBytes(v) <= 16384;
458
+ } catch {
459
+ return false;
460
+ }
461
+ })
462
+ }).superRefine((v, ctx) => {
463
+ if (v.detectedLanguageTag === null !== (v.detectedLanguageConfidence === null))
464
+ ctx.addIssue({
465
+ code: "custom",
466
+ message: "Language metadata must be a complete pair"
467
+ });
468
+ if (v.answerKind === "ANSWER" ? v.referralReason !== null : v.referralReason === null || v.evidenceIds.length > 0 || Object.keys(v.details).length > 0)
469
+ ctx.addIssue({
470
+ code: "custom",
471
+ message: "Invalid answer/referral envelope"
472
+ });
473
+ });
474
+ function finalResponseSchema(definition) {
475
+ const contract = CommerceResponseContractSchema.parse(definition);
476
+ const details = compileSubset(contract.detailsSchema, "details");
477
+ return CommerceFinalResponseSchema.refine(
478
+ (v) => v.answerKind === "REFER_TO_STORE" || details.safeParse(v.details).success,
479
+ { message: "Details do not match pinned contract" }
480
+ );
481
+ }
482
+ function verifyResponseContract(definition, hash, digest) {
483
+ const parsed = CommerceResponseContractSchema.parse(definition);
484
+ if (!/^[a-f0-9]{64}$/.test(hash) || digest(responseContractCanonicalJson(parsed)) !== hash)
485
+ throw new TypeError("Incompatible response contract");
486
+ return parsed;
487
+ }
488
+ function finalResponseToolSchema(definition) {
489
+ const contract = CommerceResponseContractSchema.parse(definition);
490
+ const properties = {
491
+ answerKind: { enum: ["ANSWER", "REFER_TO_STORE"] },
492
+ replyText: { type: "string", minLength: 1, maxLength: 4096 },
493
+ referralReason: {
494
+ enum: [
495
+ null,
496
+ "INSUFFICIENT_TOOLS",
497
+ "UNVERIFIABLE_FACTS",
498
+ "TOOL_UNAVAILABLE",
499
+ "TOOL_REVOKED"
500
+ ]
501
+ },
502
+ detectedLanguageTag: { type: ["string", "null"], maxLength: 128 },
503
+ detectedLanguageConfidence: {
504
+ type: ["number", "null"],
505
+ minimum: 0,
506
+ maximum: 1
507
+ },
508
+ evidenceIds: {
509
+ type: "array",
510
+ maxItems: 3,
511
+ uniqueItems: true,
512
+ items: { type: "string", maxLength: 128 }
513
+ },
514
+ details: { type: "object" }
515
+ };
516
+ return {
517
+ type: "object",
518
+ additionalProperties: false,
519
+ properties,
520
+ required: Object.keys(properties),
521
+ allOf: [
522
+ {
523
+ oneOf: [
524
+ {
525
+ properties: {
526
+ answerKind: { const: "ANSWER" },
527
+ referralReason: { type: "null" },
528
+ details: contract.detailsSchema
529
+ }
530
+ },
531
+ {
532
+ properties: {
533
+ answerKind: { const: "REFER_TO_STORE" },
534
+ referralReason: { enum: ReferralReasonSchema.options },
535
+ evidenceIds: { maxItems: 0 },
536
+ details: {
537
+ type: "object",
538
+ properties: {},
539
+ additionalProperties: false
540
+ }
541
+ }
542
+ }
543
+ ]
544
+ },
545
+ {
546
+ oneOf: [
547
+ {
548
+ properties: {
549
+ detectedLanguageTag: { type: "null" },
550
+ detectedLanguageConfidence: { type: "null" }
551
+ }
552
+ },
553
+ {
554
+ properties: {
555
+ detectedLanguageTag: { type: "string" },
556
+ detectedLanguageConfidence: { type: "number" }
557
+ }
558
+ }
559
+ ]
560
+ }
561
+ ]
562
+ };
563
+ }
564
+
565
+ // src/commerce/schemas.ts
566
+ import { z as z5 } from "zod";
567
+ import { validRange } from "semver";
568
+ var CommerceTurnIdentitySchema = z5.strictObject({
569
+ contractVersion: ContractVersionSchema,
570
+ shopId: IdSchema,
571
+ checkoutRecoveryId: IdSchema,
572
+ conversationId: IdSchema,
573
+ inboundVersion: z5.number().int().positive()
574
+ });
575
+ var CommerceConversationGrantSchema = z5.strictObject({
576
+ id: IdSchema,
577
+ shopId: IdSchema,
578
+ conversationId: IdSchema,
579
+ initialInboundVersion: z5.number().int().positive(),
580
+ releaseId: IdSchema,
581
+ selectedCapabilityKeys: z5.array(IdSchema).min(1).max(32).refine((v) => distinct(v) && v.includes("conversation_core")),
582
+ grantedTools: GrantedToolsSchema,
583
+ runnerVersion: SemverSchema,
584
+ createdAt: DateSchema,
585
+ expiresAt: DateSchema.nullable()
586
+ }).superRefine((v, c) => {
587
+ if (v.expiresAt && Date.parse(v.expiresAt) <= Date.parse(v.createdAt))
588
+ c.addIssue({ code: "custom", message: "Invalid grant retention date" });
589
+ if (jsonBytes(v.grantedTools) > 65536 || jsonBytes(v.selectedCapabilityKeys) > 8192 || v.grantedTools.some(
590
+ (t) => t.capabilityKeys.some((k) => !v.selectedCapabilityKeys.includes(k))
591
+ ))
592
+ c.addIssue({ code: "custom", message: "Unselected grant provenance" });
593
+ });
594
+ var CommerceConfigurationSchema = z5.strictObject({
595
+ maxRecommendations: z5.number().int().min(1).max(3).optional(),
596
+ maxSearchResults: z5.number().int().min(1).max(20).optional()
597
+ });
598
+ var CommerceManifestSchema = z5.strictObject({
599
+ contractVersion: ContractVersionSchema,
600
+ releaseId: IdSchema,
601
+ runnerCompatibility: z5.string().min(1).max(128).refine((v) => !!validRange(v)),
602
+ capabilities: z5.array(
603
+ z5.strictObject({
604
+ key: IdSchema,
605
+ revisionId: IdSchema,
606
+ position: z5.number().int().nonnegative(),
607
+ promptName: z5.string().min(1).max(512),
608
+ configuration: CommerceConfigurationSchema,
609
+ toolDescriptors: z5.array(ToolDescriptorSchema).max(32)
610
+ })
611
+ ).min(1).max(32),
612
+ selectedCapabilityKeys: z5.array(IdSchema).min(1).max(32).refine(distinct),
613
+ grantedTools: GrantedToolsSchema,
614
+ responseContract: CommerceResponseContractSchema,
615
+ responseContractHash: HashSchema
616
+ }).superRefine((v, c) => {
617
+ if (jsonBytes(v) > 262144)
618
+ c.addIssue({ code: "custom", message: "Manifest exceeds output limit" });
619
+ const keys = v.capabilities.map((x) => x.key);
620
+ if (!distinct(keys) || !keys.includes("conversation_core") || JSON.stringify(keys) !== JSON.stringify(v.selectedCapabilityKeys) || v.capabilities.some(
621
+ (x, i) => i > 0 && x.position <= v.capabilities[i - 1].position
622
+ ))
623
+ c.addIssue({
624
+ code: "custom",
625
+ message: "Invalid capability ordering/selection"
626
+ });
627
+ const union = /* @__PURE__ */ new Map();
628
+ for (const cap of v.capabilities) {
629
+ if (cap.promptName !== `commerce/${v.releaseId}/${cap.key}/${cap.revisionId}`)
630
+ c.addIssue({ code: "custom", message: "Prompt identity mismatch" });
631
+ for (const d of cap.toolDescriptors) {
632
+ const old = union.get(d.toolId);
633
+ const descriptor = canonicalJson(d);
634
+ if (old) {
635
+ if (old.descriptor !== descriptor)
636
+ c.addIssue({
637
+ code: "custom",
638
+ message: "Conflicting tool revisions/descriptors"
639
+ });
640
+ old.tool.capabilityKeys.push(cap.key);
641
+ } else
642
+ union.set(d.toolId, {
643
+ descriptor,
644
+ tool: {
645
+ toolId: d.toolId,
646
+ toolRevisionId: d.toolRevisionId,
647
+ toolName: d.name,
648
+ definitionVersion: d.definitionVersion,
649
+ capabilityKeys: [cap.key]
650
+ }
651
+ });
652
+ }
653
+ }
654
+ if (union.size !== v.grantedTools.length || v.grantedTools.some((t) => {
655
+ const expected = union.get(t.toolId)?.tool;
656
+ return !expected || expected.toolRevisionId !== t.toolRevisionId || expected.toolName !== t.toolName || expected.definitionVersion !== t.definitionVersion || JSON.stringify([...new Set(expected.capabilityKeys)].sort()) !== JSON.stringify(t.capabilityKeys);
657
+ }))
658
+ c.addIssue({
659
+ code: "custom",
660
+ message: "Grant must equal selected binding union"
661
+ });
662
+ });
663
+ var CommerceReleaseIdentitySchema = z5.strictObject({
664
+ contractVersion: ContractVersionSchema,
665
+ releaseId: IdSchema,
666
+ runnerCompatibility: z5.string().min(1).max(128).refine((v) => !!validRange(v))
667
+ });
668
+ var CommerceResolveAssertionSchema = CommerceTurnIdentitySchema.extend(
669
+ { purpose: z5.literal("resolve") }
670
+ );
671
+ var CommerceExecuteAssertionSchema = CommerceTurnIdentitySchema.extend(
672
+ { purpose: z5.literal("execute"), grantId: IdSchema, releaseId: IdSchema }
673
+ );
674
+ var CommerceAssertionSchema = z5.discriminatedUnion("purpose", [
675
+ CommerceResolveAssertionSchema,
676
+ CommerceExecuteAssertionSchema
677
+ ]);
678
+ var qty = z5.number().int().min(1).max(999);
679
+ var CommerceProposalSchema = z5.strictObject({
680
+ operations: z5.array(
681
+ z5.discriminatedUnion("kind", [
682
+ z5.strictObject({
683
+ kind: z5.literal("ADD"),
684
+ variantId: IdSchema,
685
+ quantity: qty
686
+ }),
687
+ z5.strictObject({
688
+ kind: z5.literal("REPLACE"),
689
+ lineId: IdSchema,
690
+ variantId: IdSchema,
691
+ quantity: qty
692
+ })
693
+ ])
694
+ ).min(1).max(3)
695
+ }).refine(
696
+ (v) => distinct(
697
+ v.operations.flatMap((o) => o.kind === "REPLACE" ? [o.lineId] : [])
698
+ )
699
+ );
700
+ var CommerceBasketSchema = z5.strictObject({
701
+ basketId: IdSchema,
702
+ source: z5.enum(["RECOVERY_SNAPSHOT", "PROPOSED"]),
703
+ observedAt: DateSchema,
704
+ currency: CurrencySchema.nullable(),
705
+ lines: z5.array(
706
+ z5.strictObject({
707
+ lineId: IdSchema,
708
+ productId: IdSchema.nullable(),
709
+ variantId: IdSchema.nullable(),
710
+ quantity: qty.nullable(),
711
+ unitPrice: MoneySchema.nullable()
712
+ })
713
+ ).max(100),
714
+ fingerprint: HashSchema,
715
+ unknownFields: z5.array(z5.string().min(1).max(256)).max(128)
716
+ }).refine((v) => distinct(v.lines.map((line) => line.lineId)));
717
+ var CommerceProductSchema = z5.strictObject({
718
+ productId: IdSchema,
719
+ variantId: IdSchema,
720
+ title: z5.string().min(1).max(512),
721
+ url: z5.url({ protocol: /^https$/ }).refine((v) => {
722
+ const u = new URL(v);
723
+ return !u.username && !u.password && !u.port;
724
+ }).nullable(),
725
+ available: z5.boolean().nullable(),
726
+ unitPrice: MoneySchema.nullable(),
727
+ currency: CurrencySchema.nullable(),
728
+ productType: z5.string().max(512).nullable(),
729
+ vendor: z5.string().max(512).nullable(),
730
+ observedAt: DateSchema
731
+ });
732
+ var CommerceOfferSchema = z5.strictObject({
733
+ offerId: IdSchema,
734
+ title: z5.string().min(1).max(512),
735
+ method: z5.enum(["AUTOMATIC", "CODE"]),
736
+ code: z5.string().min(1).max(128).nullable(),
737
+ startsAt: DateSchema.nullable(),
738
+ endsAt: DateSchema.nullable(),
739
+ support: z5.enum(["SUPPORTED", "UNSUPPORTED", "UNKNOWN"]),
740
+ reasonCodes: z5.array(z5.string().min(1).max(64)).max(16)
741
+ }).refine(
742
+ (v) => (v.startsAt === null || v.endsAt === null || Date.parse(v.startsAt) < Date.parse(v.endsAt)) && (v.method !== "AUTOMATIC" || v.code === null)
743
+ );
744
+ var condition = z5.strictObject({
745
+ code: z5.string().min(1).max(64),
746
+ description: z5.string().max(256)
747
+ });
748
+ var CommerceEvidenceSchema = z5.strictObject({
749
+ evidenceId: HashSchema,
750
+ turn: CommerceTurnIdentitySchema,
751
+ grantId: IdSchema,
752
+ releaseId: IdSchema,
753
+ offerId: IdSchema,
754
+ proposal: CommerceProposalSchema.nullable(),
755
+ basketFingerprint: HashSchema,
756
+ ruleFingerprint: HashSchema,
757
+ evaluatedAt: DateSchema,
758
+ expiresAt: DateSchema,
759
+ outcome: z5.enum([
760
+ "QUALIFIES_FOR_KNOWN_RULES",
761
+ "DOES_NOT_QUALIFY",
762
+ "UNKNOWN",
763
+ "UNSUPPORTED"
764
+ ]),
765
+ currency: CurrencySchema.nullable(),
766
+ savings: MoneySchema.nullable(),
767
+ resultingTotal: MoneySchema.nullable(),
768
+ evaluatedConditions: z5.array(condition).max(32),
769
+ unresolvedConditions: z5.array(condition).max(32)
770
+ }).superRefine((v, c) => {
771
+ if (Date.parse(v.expiresAt) - Date.parse(v.evaluatedAt) > 6e4)
772
+ c.addIssue({
773
+ code: "custom",
774
+ message: "Evidence lifetime exceeds ceiling"
775
+ });
776
+ if (v.outcome === "QUALIFIES_FOR_KNOWN_RULES" && (v.unresolvedConditions.length || Date.parse(v.expiresAt) <= Date.parse(v.evaluatedAt) || v.currency === null || v.savings === null || v.resultingTotal === null))
777
+ c.addIssue({ code: "custom", message: "Unsupported qualifying claim" });
778
+ });
779
+ var CommerceAlternativeSchema = z5.strictObject({
780
+ product: CommerceProductSchema,
781
+ proposal: CommerceProposalSchema,
782
+ extraSpend: MoneySchema.nullable(),
783
+ resultingTotal: MoneySchema.nullable(),
784
+ currency: CurrencySchema.nullable(),
785
+ evidence: CommerceEvidenceSchema.nullable(),
786
+ similarityReasons: z5.array(z5.string().max(256)).max(3)
787
+ }).refine((v) => v.product.available === true);
788
+ var CommerceErrorCodeSchema = z5.enum([
789
+ "INVALID_INPUT",
790
+ "DENIED",
791
+ "STALE_TURN",
792
+ "NOT_FOUND",
793
+ "UNAVAILABLE",
794
+ "THROTTLED",
795
+ "DEADLINE",
796
+ "INCOMPATIBLE_VERSION"
797
+ ]);
798
+ function commerceToolResultSchema(data) {
799
+ return z5.union([
800
+ z5.strictObject({
801
+ contractVersion: ContractVersionSchema,
802
+ status: z5.literal("OK"),
803
+ data,
804
+ renderedText: z5.string().max(4096)
805
+ }),
806
+ z5.strictObject({
807
+ contractVersion: ContractVersionSchema,
808
+ status: z5.literal("ERROR"),
809
+ code: CommerceErrorCodeSchema,
810
+ retryable: z5.boolean(),
811
+ renderedText: z5.string().max(4096).optional()
812
+ })
813
+ ]);
814
+ }
815
+ var CommerceToolResultSchema = commerceToolResultSchema(
816
+ boundedJson(262144)
817
+ );
818
+ var limit = z5.number().int().min(1).max(3).default(3);
819
+ var CommerceToolInputs = {
820
+ commerce_get_basket: z5.strictObject({}),
821
+ commerce_search_products: z5.strictObject({
822
+ query: z5.string().min(1).max(200),
823
+ maximumPrice: MoneySchema.optional(),
824
+ availableOnly: z5.boolean().default(true),
825
+ limit: z5.number().int().min(1).max(20).default(10),
826
+ cursor: z5.string().max(2048).nullable().default(null)
827
+ }),
828
+ commerce_get_discount_options: z5.strictObject({}),
829
+ commerce_evaluate_discount: z5.strictObject({
830
+ offerId: IdSchema,
831
+ proposal: CommerceProposalSchema.nullable()
832
+ }),
833
+ commerce_find_qualifying_products: z5.strictObject({
834
+ offerId: IdSchema,
835
+ query: z5.string().min(1).max(200).nullable(),
836
+ limit
837
+ }),
838
+ commerce_find_similar_products: z5.strictObject({
839
+ variantId: IdSchema,
840
+ offerId: IdSchema.nullable(),
841
+ limit
842
+ })
843
+ };
844
+ var alternatives = z5.strictObject({
845
+ alternatives: z5.array(CommerceAlternativeSchema).max(3),
846
+ truncated: z5.boolean()
847
+ });
848
+ var CommerceToolOutputs = {
849
+ commerce_get_basket: CommerceBasketSchema,
850
+ commerce_search_products: z5.strictObject({
851
+ products: z5.array(CommerceProductSchema).max(20),
852
+ cursor: z5.string().max(2048).nullable(),
853
+ truncated: z5.boolean()
854
+ }),
855
+ commerce_get_discount_options: z5.strictObject({
856
+ mode: z5.enum(["NONE", "FIXED", "AI_BEST_APPLICABLE"]),
857
+ offers: z5.array(CommerceOfferSchema).max(50),
858
+ truncated: z5.boolean()
859
+ }),
860
+ commerce_evaluate_discount: CommerceEvidenceSchema,
861
+ commerce_find_qualifying_products: alternatives,
862
+ commerce_find_similar_products: alternatives
863
+ };
864
+ function productBelongsToDomain(product, verifiedDomain) {
865
+ try {
866
+ return product.url === null || new URL(product.url).hostname === verifiedDomain;
867
+ } catch {
868
+ return false;
869
+ }
870
+ }
871
+
872
+ // src/commerce/selection.ts
873
+ import { z as z6 } from "zod";
874
+ var CommerceCapabilityBindingSchema = z6.discriminatedUnion("kind", [
875
+ z6.strictObject({
876
+ kind: z6.literal("BASE"),
877
+ key: z6.literal("conversation_core")
878
+ }),
879
+ z6.strictObject({
880
+ kind: z6.literal("FEATURE"),
881
+ key: IdSchema,
882
+ featureId: IdSchema
883
+ }),
884
+ z6.strictObject({
885
+ kind: z6.literal("RECOVERY_POLICY"),
886
+ key: z6.literal("discount_assistance")
887
+ })
888
+ ]);
889
+ function selectCapabilities(candidates, facts) {
890
+ const selected = candidates.filter((c) => {
891
+ const b = CommerceCapabilityBindingSchema.parse(c.binding);
892
+ if (!c.enabled) return false;
893
+ if (b.kind === "BASE") return true;
894
+ if (b.kind === "RECOVERY_POLICY")
895
+ return facts.offerMode === "FIXED" || facts.offerMode === "AI_BEST_APPLICABLE";
896
+ const f = facts.features.find((f2) => f2.id === b.featureId);
897
+ return !!f && f.active && f.planMappingEnabled && (f.mode === "ALWAYS_ENABLED" || f.preferenceEnabled === true);
898
+ }).sort((a, b) => a.position - b.position);
899
+ if (!selected.some((c) => c.binding.kind === "BASE"))
900
+ throw new TypeError("Base capability unavailable");
901
+ if (new Set(selected.map((c) => c.binding.key)).size !== selected.length || new Set(selected.map((c) => c.position)).size !== selected.length)
902
+ throw new TypeError("Duplicate release membership");
903
+ return selected.map((c) => c.binding.key);
904
+ }
905
+ function deduplicateTools(capabilities) {
906
+ const byId = /* @__PURE__ */ new Map();
907
+ const names = /* @__PURE__ */ new Map();
908
+ for (const cap of capabilities)
909
+ for (const d of cap.toolDescriptors) {
910
+ const previous = byId.get(d.toolId);
911
+ const descriptor = canonicalJson(d);
912
+ if (previous && previous.descriptor !== descriptor || names.has(d.name) && names.get(d.name) !== d.toolId)
913
+ throw new TypeError("Conflicting tool association");
914
+ names.set(d.name, d.toolId);
915
+ if (previous)
916
+ previous.tool.capabilityKeys = [
917
+ .../* @__PURE__ */ new Set([...previous.tool.capabilityKeys, cap.key])
918
+ ].sort();
919
+ else
920
+ byId.set(d.toolId, {
921
+ descriptor,
922
+ tool: {
923
+ toolId: d.toolId,
924
+ toolRevisionId: d.toolRevisionId,
925
+ toolName: d.name,
926
+ definitionVersion: d.definitionVersion,
927
+ capabilityKeys: [cap.key]
928
+ }
929
+ });
930
+ }
931
+ return [...byId.values()].map((v) => v.tool).sort((a, b) => a.toolName.localeCompare(b.toolName, "en"));
932
+ }
933
+ function manifestMatchesGrant(manifest, grant) {
934
+ return manifest.releaseId === grant.releaseId && canonicalJson(manifest.selectedCapabilityKeys) === canonicalJson(grant.selectedCapabilityKeys) && canonicalJson(
935
+ [...manifest.grantedTools].sort(
936
+ (a, b) => a.toolId.localeCompare(b.toolId)
937
+ )
938
+ ) === canonicalJson(
939
+ [...grant.grantedTools].sort(
940
+ (a, b) => a.toolId.localeCompare(b.toolId)
941
+ )
942
+ );
943
+ }
944
+ var parseCommerceManifest = (raw) => CommerceManifestSchema.parse(raw);
945
+ function currentlyGrantedTools(grant, eligibleCapabilityKeys, enabledToolIds) {
946
+ return grant.grantedTools.filter(
947
+ (t) => enabledToolIds.has(t.toolId) && t.capabilityKeys.some((k) => eligibleCapabilityKeys.has(k))
948
+ );
949
+ }
950
+
951
+ export {
952
+ canonicalJson,
953
+ jsonBytes,
954
+ responseContractCanonicalJson,
955
+ MONEY_PATTERN,
956
+ safeName,
957
+ safePath,
958
+ validateSubset,
959
+ InputSchemaSchema,
960
+ DetailsSchemaSchema,
961
+ matchesSubset,
962
+ compileSubset,
963
+ ContractVersionSchema,
964
+ IdSchema,
965
+ SemverSchema,
966
+ HashSchema,
967
+ DateSchema,
968
+ MoneySchema,
969
+ CurrencySchema,
970
+ LanguageSchema,
971
+ distinct,
972
+ boundedJson,
973
+ ToolNameSchema,
974
+ ToolBindingSchema,
975
+ ToolBindingsSchema,
976
+ GrantedToolSchema,
977
+ GrantedToolsSchema,
978
+ ToolDescriptorSchema,
979
+ POLICY_OPERATIONS,
980
+ CommerceExecutionSchema,
981
+ ResponseTemplateSchema,
982
+ CommerceToolDefinitionSchema,
983
+ CommerceToolDraftDefinitionSchema,
984
+ validateDefinitionForPublication,
985
+ definitionToMcpDescriptor,
986
+ mapToolArguments,
987
+ toolHashInput,
988
+ capabilityHashInput,
989
+ validateDefinitionVersion,
990
+ CommerceToolIdentitySchema,
991
+ CommerceToolRevisionIdentitySchema,
992
+ POLICY_OPERATION_DESCRIPTORS,
993
+ CommerceResponseContractSchema,
994
+ EMPTY_RESPONSE_CONTRACT,
995
+ ReferralReasonSchema,
996
+ CommerceFinalResponseSchema,
997
+ finalResponseSchema,
998
+ verifyResponseContract,
999
+ finalResponseToolSchema,
1000
+ CommerceTurnIdentitySchema,
1001
+ CommerceConversationGrantSchema,
1002
+ CommerceConfigurationSchema,
1003
+ CommerceManifestSchema,
1004
+ CommerceReleaseIdentitySchema,
1005
+ CommerceResolveAssertionSchema,
1006
+ CommerceExecuteAssertionSchema,
1007
+ CommerceAssertionSchema,
1008
+ CommerceProposalSchema,
1009
+ CommerceBasketSchema,
1010
+ CommerceProductSchema,
1011
+ CommerceOfferSchema,
1012
+ CommerceEvidenceSchema,
1013
+ CommerceAlternativeSchema,
1014
+ CommerceErrorCodeSchema,
1015
+ commerceToolResultSchema,
1016
+ CommerceToolResultSchema,
1017
+ CommerceToolInputs,
1018
+ CommerceToolOutputs,
1019
+ productBelongsToDomain,
1020
+ CommerceCapabilityBindingSchema,
1021
+ selectCapabilities,
1022
+ deduplicateTools,
1023
+ manifestMatchesGrant,
1024
+ parseCommerceManifest,
1025
+ currentlyGrantedTools
1026
+ };
1027
+ //# sourceMappingURL=chunk-QNANS4R4.js.map