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