@modainteract/moda-interact-shared 3.1.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -112,8 +112,8 @@ import { runCommerceTurn, type RunnerTool } from
112
112
  | `ToolDescriptorSchema`, `definitionToMcpDescriptor` | Full versioned Tool descriptor versus the MCP-facing name/description/inputSchema projection. The server-only execution definition is not exposed as model instructions. |
113
113
  | `CommerceManifestSchema` | Resolved immutable release contract. Each selected Capability identifies one Feature and one exact Tool descriptor; `featureBehaviours` contains one behaviour prompt for each represented Feature. Commerce provides the manifest; callers validate it. |
114
114
  | `GrantedToolSchema`, `GrantedToolsSchema` | Pinned Tool identity, exact Tool revision, name/version and sorted originating Capability keys selected for a conversation. One Tool reused by several Capabilities is emitted once with all Capability provenance. |
115
- | `CommerceConversationGrantSchema` | Persisted conversation permission snapshot. Background stores the resolved release, selected Capability keys and granted Tools and reuses them on later turns. Empty Capability/Tool selections are valid. |
116
- | `CommerceTurnIdentitySchema`, `CommerceAssertionSchema` | Turn identity and resolve/execute assertion shapes. These do not sign or verify JWT signatures; transport/authentication belongs to the services. |
115
+ | `CommerceConversationGrantSchema` | Persisted conversation permission snapshot. Background owns it and stores the resolved release, selected Capability keys and granted Tools for reuse on later turns. Empty Capability/Tool selections are valid. |
116
+ | `CommerceTurnIdentitySchema`, `CommerceAssertionSchema` | Background-owned turn identity and resolve/execute assertion shapes. These do not sign or verify JWT signatures; transport/authentication belongs to the services. |
117
117
  | `selectCapabilities` | Pure selection over ordinary Feature Capabilities plus caller-supplied Feature eligibility facts. There is no BASE/RECOVERY_POLICY Capability kind or mandatory `conversation_core`. |
118
118
  | `deduplicateTools` | Builds the granted-Tool union from one Tool descriptor per selected Capability while retaining deterministic Capability provenance. |
119
119
  | `manifestMatchesGrant`, `currentlyGrantedTools` | Compare pinned selection and current availability. Do not replace signature, tenant, lease or persistence checks. |
@@ -121,6 +121,11 @@ import { runCommerceTurn, type RunnerTool } from
121
121
  | `CommerceBasketSchema`, `CommerceProductSchema`, `CommerceOfferSchema`, `CommerceEvidenceSchema`, `CommerceAlternativeSchema` | Bounded commerce facts/evidence exchanged between services. Parsing does not prove external facts are true or current. |
122
122
  | `CommerceToolResultSchema`, `commerceToolResultSchema` | Standard result envelope; the factory accepts a specific data schema. |
123
123
 
124
+ Background owns conversation, grant and turn state. MCP receives authenticated
125
+ shop identity and may forward only already accepted opaque adapter context, such
126
+ as `CommerceOutreachToolContextSchema`; it does not interpret that context as
127
+ conversation authority.
128
+
124
129
  The direct release contract is Feature/Capability/Tool based. A selected Capability
125
130
  contains exactly one `featureId` and one exact `toolDescriptor`; Feature behaviour
126
131
  is carried separately and exactly once for each represented Feature. For example:
@@ -2382,3 +2387,85 @@ Type-only exports:
2382
2387
  - `RunnerTool`
2383
2388
 
2384
2389
  </details>
2390
+
2391
+
2392
+ ## Product discovery and code-owned discount contracts
2393
+
2394
+ The existing `/commerce` entrypoint exports `CommerceCategoryCandidateSchema`,
2395
+ `CommerceCategoryResolutionSchema`, `CommerceOptionalLookupUnavailableSchema`,
2396
+ and `CommerceProductRecommendationSelectionSchema`, with matching inferred types.
2397
+ The product input/output dictionaries include category resolution and flat,
2398
+ category-aware search. Recommendation selection contains identifiers and quantity,
2399
+ not model-authored prices or savings; applicability and cross-field relationships
2400
+ remain service policy.
2401
+
2402
+ `CommerceBuiltInToolDescriptorSchema`, `CommerceBuiltInToolGrantSchema`, and
2403
+ `commerceDiscountBuiltInDescriptors` define the two code-owned discount reads.
2404
+ Their durable identity is the canonical tool name and `definitionVersion`; the
2405
+ strict descriptor and grant schemas reject extra fields. Reading these exports
2406
+ needs no provider/database configuration.
2407
+
2408
+ The corrected built-in identities are exactly
2409
+ `commerce_get_discount_options@2.0.0` and
2410
+ `commerce_get_discount_details@2.0.0`. Their strict descriptor/grant schemas reject
2411
+ legacy `1.0.0`; the coordinated pre-production Database/consumer migration must
2412
+ finish before corrected consumers run. No legacy version union is supported.
2413
+
2414
+ Only discount offer `startsAt`/`endsAt` and discount condition-facts
2415
+ `observedAt`/`unavailableAt` use the internal Shared-owned UTC validator:
2416
+ `YYYY-MM-DDTHH:mm:ssZ` or `YYYY-MM-DDTHH:mm:ss.<digits>Z`, with one or more
2417
+ fractional digits when the fraction is present. Seconds and uppercase `T`/`Z`
2418
+ are required; Gregorian dates and clock ranges are validated, including leap-year
2419
+ rules. Minute-only times, numeric offsets, leap seconds, invalid calendar dates
2420
+ and surrounding whitespace are rejected. Values are validated without coercion
2421
+ or normalization. Existing nullability and offer start-before-end checks remain.
2422
+ The exported general `DateSchema` and all its non-discount uses are unchanged.
2423
+ This correction is independent of Zod's `z.iso.datetime` implementation.
2424
+
2425
+ Grants and runner descriptors now explicitly distinguish published identities
2426
+ from `kind: "BUILT_IN"`. Built-ins have no fabricated tool/revision IDs or
2427
+ capability provenance. Manifest `builtInTools` defaults to an empty array, but
2428
+ retained grants must equal the exact canonical descriptor set as well as the
2429
+ published capability union. `deduplicateTools` accepts the optional built-in
2430
+ descriptor set; `currentlyGrantedTools` requires an explicit currently permitted
2431
+ canonical built-in set and otherwise denies built-ins. Hosts still check current
2432
+ tenant/turn/release/permission before and after asynchronous tool work.
2433
+
2434
+ The runtime runner version is **1.1.0**. Optional lookup unavailability is data
2435
+ inside `status: "OK"`; hard errors, stale turns and required referrals retain
2436
+ their existing semantics. Details permit bounded nullable scalars and depth five;
2437
+ input depth stays three and details payloads stay at 16KiB.
2438
+
2439
+ The timestamp/version correction and prior identity cleanup require a coordinated
2440
+ breaking package publication and consumer migrations in the separate release task
2441
+ (ARCH-058-SHARED-006). This source change does not bump package metadata or publish
2442
+ to a registry.
2443
+
2444
+ Run `npm run test:product-discovery-contracts` for the focused contracts/runner
2445
+ fixtures. To execute the actual Commerce-owned projector/publication integration,
2446
+ set `COMMERCE_CONTRACT_SOURCE_ROOT` to its explicit accepted source checkout.
2447
+ The test passes candidate schemas as function arguments to that existing
2448
+ projector; it neither changes nor links the consumer's installed package.
2449
+ The installed consumer still has the released operation registry: candidate operation
2450
+ metadata is checked against candidate Shared, and the installed publication pipeline
2451
+ is exercised with a registered operation and the actual generated input schema.
2452
+ This does not certify consumer adoption of the new operations before release.
2453
+ Without this test-only source prerequisite, that integration case is explicitly
2454
+ skipped rather than reported as executed.
2455
+
2456
+ The discovery input dictionary also supports explicit `browse` (default `false`)
2457
+ and the published `commerce_get_shipping_information` operation
2458
+ (`shipping.getInformation`, version `1.0.0`). The named
2459
+ `CommerceShippingInformationInputSchema` / `CommerceShippingInformationSchema`
2460
+ and inferred types expose bounded native facts and independent product/policy outcomes.
2461
+ Policy HTML remains untrusted data; adapters enforce ownership, status relationships,
2462
+ verified hosts and the UTF-8 body budget.
2463
+
2464
+ `CommerceResponseDispositionSchema` / `CommerceResponseDisposition` and
2465
+ `CommerceAnswerPresentationSelectionSchema` / `CommerceAnswerPresentationSelection`
2466
+ add optional identifier-only presentation fields. Merge their shape with recommendation
2467
+ and existing release details; Commerce owns explicit published instructions, and Background
2468
+ owns controlled composition. Product-bearing shipping observations latch that host path even
2469
+ without a selection; policy-only observations alone may retain ordinary grounded answers.
2470
+ Expected limitations never clear a terminal tool error. The default empty response contract,
2471
+ hard-referral envelope and two-tool discount built-in allowlist remain unchanged.
@@ -1,6 +1,12 @@
1
1
  import {
2
- CommerceManifestSchema
3
- } from "./chunk-H7AUKACO.js";
2
+ CommerceConversationGrantSchema,
3
+ CommerceManifestSchema,
4
+ GrantedToolsSchema,
5
+ PublishedToolDescriptorSchema,
6
+ isBuiltInTool,
7
+ isTrustedCommerceBuiltInDescriptor,
8
+ isTrustedCommerceBuiltInGrant
9
+ } from "./chunk-NA76IPIW.js";
4
10
  import {
5
11
  IdSchema
6
12
  } from "./chunk-QVPEX7KY.js";
@@ -25,11 +31,11 @@ function selectCapabilities(candidates, facts) {
25
31
  throw new TypeError("Duplicate release membership");
26
32
  return selected.map((c) => c.key);
27
33
  }
28
- function deduplicateTools(capabilities) {
34
+ function deduplicateTools(capabilities, builtInTools = []) {
29
35
  const byId = /* @__PURE__ */ new Map();
30
36
  const names = /* @__PURE__ */ new Map();
31
37
  for (const cap of capabilities) {
32
- const d = cap.toolDescriptor;
38
+ const d = PublishedToolDescriptorSchema.parse(cap.toolDescriptor);
33
39
  const previous = byId.get(d.toolId);
34
40
  const descriptor = canonicalJson(d);
35
41
  if (previous && previous.descriptor !== descriptor || names.has(d.name) && names.get(d.name) !== d.toolId)
@@ -51,23 +57,33 @@ function deduplicateTools(capabilities) {
51
57
  }
52
58
  });
53
59
  }
54
- return [...byId.values()].map((v) => v.tool).sort((a, b) => a.toolName.localeCompare(b.toolName, "en"));
60
+ if (builtInTools.some((tool) => !isTrustedCommerceBuiltInDescriptor(tool)))
61
+ throw new TypeError("Untrusted built-in descriptor");
62
+ return GrantedToolsSchema.parse([
63
+ ...[...byId.values()].map((value) => value.tool),
64
+ ...builtInTools.map((tool) => ({
65
+ kind: "BUILT_IN",
66
+ toolName: tool.name,
67
+ definitionVersion: tool.definitionVersion
68
+ }))
69
+ ]).sort((a, b) => a.toolName.localeCompare(b.toolName, "en"));
55
70
  }
56
71
  function manifestMatchesGrant(manifest, grant) {
72
+ if (!CommerceManifestSchema.safeParse(manifest).success || !CommerceConversationGrantSchema.safeParse(grant).success) return false;
57
73
  return manifest.releaseId === grant.releaseId && canonicalJson(manifest.selectedCapabilityKeys) === canonicalJson(grant.selectedCapabilityKeys) && canonicalJson(
58
74
  [...manifest.grantedTools].sort(
59
- (a, b) => a.toolId.localeCompare(b.toolId)
75
+ (a, b) => a.toolName.localeCompare(b.toolName)
60
76
  )
61
77
  ) === canonicalJson(
62
78
  [...grant.grantedTools].sort(
63
- (a, b) => a.toolId.localeCompare(b.toolId)
79
+ (a, b) => a.toolName.localeCompare(b.toolName)
64
80
  )
65
81
  );
66
82
  }
67
83
  var parseCommerceManifest = (raw) => CommerceManifestSchema.parse(raw);
68
- function currentlyGrantedTools(grant, eligibleCapabilityKeys, enabledToolIds) {
84
+ function currentlyGrantedTools(grant, eligibleCapabilityKeys, enabledToolIds, allowedBuiltInTools = []) {
69
85
  return grant.grantedTools.filter(
70
- (t) => enabledToolIds.has(t.toolId) && t.capabilityKeys.some((k) => eligibleCapabilityKeys.has(k))
86
+ (t) => isBuiltInTool(t) ? isTrustedCommerceBuiltInGrant(t) && allowedBuiltInTools.some((descriptor) => isTrustedCommerceBuiltInDescriptor(descriptor) && descriptor.name === t.toolName && descriptor.definitionVersion === t.definitionVersion) : enabledToolIds.has(t.toolId) && t.capabilityKeys.some((k) => eligibleCapabilityKeys.has(k))
71
87
  );
72
88
  }
73
89
 
@@ -78,4 +94,4 @@ export {
78
94
  parseCommerceManifest,
79
95
  currentlyGrantedTools
80
96
  };
81
- //# sourceMappingURL=chunk-PE2JHOJX.js.map
97
+ //# sourceMappingURL=chunk-H7NE3H5R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commerce/selection.ts"],"sourcesContent":["import { IdSchema } from \"./primitives\";\nimport {\n type CommerceManifest,\n CommerceManifestSchema,\n type CommerceConversationGrant,\n CommerceConversationGrantSchema,\n} from \"./schemas\";\nimport { GrantedToolsSchema, PublishedToolDescriptorSchema, type GrantedTool, type PublishedGrantedTool, type PublishedToolDescriptor, type CommerceBuiltInToolDescriptor, isBuiltInTool } from \"./definitions\";\nimport { isTrustedCommerceBuiltInDescriptor, isTrustedCommerceBuiltInGrant } from \"./built-in-tools\";\nimport { canonicalJson } from \"./canonical-json\";\nexport type FeatureFacts = {\n id: string;\n active: boolean;\n planMappingEnabled: boolean;\n mode: \"ALWAYS_ENABLED\" | \"MERCHANT_OPT_IN\";\n preferenceEnabled: boolean | null;\n};\nexport function selectCapabilities(\n candidates: Array<{\n key: string;\n featureId: string;\n enabled: boolean;\n position: number;\n }>,\n facts: {\n features: FeatureFacts[];\n },\n) {\n for (const candidate of candidates) {\n IdSchema.parse(candidate.key);\n IdSchema.parse(candidate.featureId);\n if (!Number.isSafeInteger(candidate.position) || candidate.position < 0)\n throw new TypeError(\"Invalid capability position\");\n }\n const selected = candidates\n .filter((c) => {\n if (!c.enabled) return false;\n const f = facts.features.find((f) => f.id === c.featureId);\n return (\n !!f &&\n f.active &&\n f.planMappingEnabled &&\n (f.mode === \"ALWAYS_ENABLED\" || f.preferenceEnabled === true)\n );\n })\n .sort((a, b) => a.position - b.position);\n if (\n new Set(selected.map((c) => c.key)).size !== selected.length ||\n new Set(selected.map((c) => c.position)).size !== selected.length\n )\n throw new TypeError(\"Duplicate release membership\");\n return selected.map((c) => c.key);\n}\nexport function deduplicateTools(\n capabilities: Array<{ key: string; toolDescriptor: PublishedToolDescriptor }>,\n builtInTools: readonly CommerceBuiltInToolDescriptor[] = [],\n): GrantedTool[] {\n const byId = new Map<string, { tool: PublishedGrantedTool; descriptor: string }>();\n const names = new Map<string, string>();\n for (const cap of capabilities) {\n const d = PublishedToolDescriptorSchema.parse(cap.toolDescriptor);\n const previous = byId.get(d.toolId);\n const descriptor = canonicalJson(d);\n if (\n (previous && previous.descriptor !== descriptor) ||\n (names.has(d.name) && names.get(d.name) !== d.toolId)\n )\n throw new TypeError(\"Conflicting tool association\");\n names.set(d.name, d.toolId);\n if (previous)\n previous.tool.capabilityKeys = [\n ...new Set([...previous.tool.capabilityKeys, cap.key]),\n ].sort();\n else\n byId.set(d.toolId, {\n descriptor,\n tool: {\n toolId: d.toolId,\n toolRevisionId: d.toolRevisionId,\n toolName: d.name,\n definitionVersion: d.definitionVersion,\n capabilityKeys: [cap.key],\n },\n });\n }\n if (builtInTools.some(tool => !isTrustedCommerceBuiltInDescriptor(tool)))\n throw new TypeError(\"Untrusted built-in descriptor\");\n return GrantedToolsSchema.parse([\n ...[...byId.values()].map(value => value.tool),\n ...builtInTools.map(tool => ({\n kind: \"BUILT_IN\" as const, toolName: tool.name,\n definitionVersion: tool.definitionVersion,\n })),\n ]).sort((a, b) => a.toolName.localeCompare(b.toolName, \"en\"));\n}\nexport function manifestMatchesGrant(\n manifest: CommerceManifest,\n grant: CommerceConversationGrant,\n) {\n if (!CommerceManifestSchema.safeParse(manifest).success || !CommerceConversationGrantSchema.safeParse(grant).success) return false;\n return (\n manifest.releaseId === grant.releaseId &&\n canonicalJson(manifest.selectedCapabilityKeys) ===\n canonicalJson(grant.selectedCapabilityKeys) &&\n canonicalJson(\n [...manifest.grantedTools].sort((a, b) =>\n a.toolName.localeCompare(b.toolName),\n ),\n ) ===\n canonicalJson(\n [...grant.grantedTools].sort((a, b) =>\n a.toolName.localeCompare(b.toolName),\n ),\n )\n );\n}\nexport const parseCommerceManifest = (raw: unknown) =>\n CommerceManifestSchema.parse(raw);\nexport function currentlyGrantedTools(\n grant: CommerceConversationGrant,\n eligibleCapabilityKeys: ReadonlySet<string>,\n enabledToolIds: ReadonlySet<string>,\n allowedBuiltInTools: readonly CommerceBuiltInToolDescriptor[] = [],\n) {\n return grant.grantedTools.filter(\n (t) =>\n isBuiltInTool(t)\n ? isTrustedCommerceBuiltInGrant(t) && allowedBuiltInTools.some(descriptor =>\n isTrustedCommerceBuiltInDescriptor(descriptor) && descriptor.name === t.toolName &&\n descriptor.definitionVersion === t.definitionVersion)\n : enabledToolIds.has(t.toolId) && t.capabilityKeys.some((k) => eligibleCapabilityKeys.has(k)),\n );\n}\n"],"mappings":"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","names":["f"]}
@@ -40,7 +40,7 @@ var keywords = [
40
40
  ];
41
41
  function validateSubset(value, mode) {
42
42
  let propertyCount = 0;
43
- const maxDepth = mode === "input" ? 3 : 4;
43
+ const maxDepth = mode === "input" ? 3 : 5;
44
44
  function walk(raw, depth) {
45
45
  if (depth > maxDepth || !raw || typeof raw !== "object" || Array.isArray(raw))
46
46
  return false;
@@ -50,7 +50,7 @@ function validateSubset(value, mode) {
50
50
  ))
51
51
  return false;
52
52
  const nullable = Array.isArray(s.type);
53
- if (nullable && (mode !== "input" || s.type.length !== 2 || s.type[1] !== "null"))
53
+ if (nullable && (s.type.length !== 2 || s.type[1] !== "null" || mode === "details" && ["object", "array"].includes(s.type[0])))
54
54
  return false;
55
55
  const type = typeof s.type === "string" ? s.type : s.type[0];
56
56
  if (![
@@ -255,17 +255,14 @@ var DEFINITION_SIZE_MESSAGE = "Definition exceeds the 65536-byte jsonb text limi
255
255
 
256
256
  // src/commerce/definitions.ts
257
257
  var ToolNameSchema = z3.string().regex(/^[a-z][a-z0-9_]{0,127}$/).refine((n) => n !== "finalResponse");
258
- var GrantedToolSchema = z3.strictObject({
258
+ var PublishedGrantedToolSchema = z3.strictObject({
259
259
  toolId: IdSchema,
260
260
  toolRevisionId: IdSchema,
261
261
  toolName: ToolNameSchema,
262
262
  definitionVersion: SemverSchema,
263
263
  capabilityKeys: z3.array(IdSchema).min(1).max(32).refine((v) => distinct(v) && v.join("\0") === [...v].sort().join("\0"))
264
264
  });
265
- var GrantedToolsSchema = z3.array(GrantedToolSchema).max(32).refine(
266
- (v) => distinct(v.map((x) => x.toolId)) && distinct(v.map((x) => x.toolName))
267
- );
268
- var ToolDescriptorSchema = z3.strictObject({
265
+ var PublishedToolDescriptorSchema = z3.strictObject({
269
266
  toolId: IdSchema,
270
267
  toolRevisionId: IdSchema,
271
268
  name: ToolNameSchema,
@@ -273,6 +270,29 @@ var ToolDescriptorSchema = z3.strictObject({
273
270
  description: z3.string().min(1).max(4096),
274
271
  inputSchema: InputSchemaSchema
275
272
  });
273
+ var CommerceBuiltInToolNameSchema = z3.enum([
274
+ "commerce_get_discount_options",
275
+ "commerce_get_discount_details"
276
+ ]).meta({ maxLength: 29 });
277
+ var CommerceBuiltInToolGrantSchema = z3.strictObject({
278
+ kind: z3.enum(["BUILT_IN"]).meta({ maxLength: 8 }),
279
+ toolName: CommerceBuiltInToolNameSchema,
280
+ definitionVersion: z3.enum(["2.0.0"]).meta({ maxLength: 5 })
281
+ });
282
+ var CommerceBuiltInToolDescriptorSchema = z3.strictObject({
283
+ kind: z3.enum(["BUILT_IN"]).meta({ maxLength: 8 }),
284
+ name: CommerceBuiltInToolNameSchema,
285
+ definitionVersion: z3.enum(["2.0.0"]).meta({ maxLength: 5 }),
286
+ description: z3.string().min(1).max(4096),
287
+ inputSchema: InputSchemaSchema
288
+ });
289
+ var GrantedToolSchema = z3.union([PublishedGrantedToolSchema, CommerceBuiltInToolGrantSchema]);
290
+ var ToolDescriptorSchema = z3.union([PublishedToolDescriptorSchema, CommerceBuiltInToolDescriptorSchema]);
291
+ var GrantedToolsSchema = z3.array(GrantedToolSchema).max(32).refine((values) => {
292
+ const published = values.filter((value) => !("kind" in value));
293
+ return distinct(values.map((value) => value.toolName)) && distinct(published.map((value) => value.toolId));
294
+ });
295
+ var isBuiltInTool = (value) => "kind" in value && value.kind === "BUILT_IN";
276
296
  var MappingSchema = z3.union([
277
297
  z3.strictObject({
278
298
  input: z3.string().refine(safeName),
@@ -282,6 +302,10 @@ var MappingSchema = z3.union([
282
302
  ]);
283
303
  var MappingsSchema = z3.record(z3.string().refine(safeName), MappingSchema).refine((v) => Object.keys(v).length <= 32);
284
304
  var POLICY_OPERATIONS = [
305
+ "taxonomy.resolveCategories",
306
+ "products.search",
307
+ "products.get",
308
+ "shipping.getInformation",
285
309
  "recovery.getBasket",
286
310
  "shopify.searchProducts",
287
311
  "discounts.getOptions",
@@ -499,6 +523,47 @@ var POLICY_OPERATION_DESCRIPTORS = POLICY_OPERATIONS.map(
499
523
  (operation) => ({ operation, operationVersion: "1.0.0" })
500
524
  );
501
525
 
526
+ // src/commerce/built-in-tools.ts
527
+ function immutable(value) {
528
+ for (const child of Object.values(value))
529
+ if (child && typeof child === "object") immutable(child);
530
+ return Object.freeze(value);
531
+ }
532
+ var commerceDiscountBuiltInDescriptors = immutable([
533
+ {
534
+ kind: "BUILT_IN",
535
+ name: "commerce_get_discount_options",
536
+ definitionVersion: "2.0.0",
537
+ description: "Read the current shop's permitted discount options.",
538
+ inputSchema: {
539
+ type: "object",
540
+ properties: {
541
+ limit: { type: "integer", minimum: 1, maximum: 50 },
542
+ cursor: { type: ["string", "null"], maxLength: 2048 }
543
+ },
544
+ additionalProperties: false
545
+ }
546
+ },
547
+ {
548
+ kind: "BUILT_IN",
549
+ name: "commerce_get_discount_details",
550
+ definitionVersion: "2.0.0",
551
+ description: "Read details for a permitted discount in the current shop.",
552
+ inputSchema: {
553
+ type: "object",
554
+ properties: { offerId: { type: "string", minLength: 1, maxLength: 128 } },
555
+ required: ["offerId"],
556
+ additionalProperties: false
557
+ }
558
+ }
559
+ ]);
560
+ function isTrustedCommerceBuiltInDescriptor(value) {
561
+ return commerceDiscountBuiltInDescriptors.some((expected) => canonicalJson(expected) === canonicalJson(value));
562
+ }
563
+ function isTrustedCommerceBuiltInGrant(value) {
564
+ return commerceDiscountBuiltInDescriptors.some((expected) => value.toolName === expected.name && value.definitionVersion === expected.definitionVersion);
565
+ }
566
+
502
567
  // src/commerce/response.ts
503
568
  import { z as z4 } from "zod";
504
569
  var CommerceResponseContractSchema = z4.strictObject({
@@ -700,7 +765,7 @@ var CommerceConversationGrantSchema = z5.strictObject({
700
765
  if (v.expiresAt && Date.parse(v.expiresAt) <= Date.parse(v.createdAt))
701
766
  c.addIssue({ code: "custom", message: "Invalid grant retention date" });
702
767
  if (jsonBytes(v.grantedTools) > 65536 || jsonBytes(v.selectedCapabilityKeys) > 8192 || v.grantedTools.some(
703
- (t) => t.capabilityKeys.some((k) => !v.selectedCapabilityKeys.includes(k))
768
+ (t) => isBuiltInTool(t) ? !isTrustedCommerceBuiltInGrant(t) : t.capabilityKeys.some((k) => !v.selectedCapabilityKeys.includes(k))
704
769
  ))
705
770
  c.addIssue({ code: "custom", message: "Unselected grant provenance" });
706
771
  });
@@ -714,7 +779,7 @@ var CommerceManifestSchema = z5.strictObject({
714
779
  key: IdSchema,
715
780
  featureId: IdSchema,
716
781
  position: z5.number().int().nonnegative(),
717
- toolDescriptor: ToolDescriptorSchema
782
+ toolDescriptor: PublishedToolDescriptorSchema
718
783
  })
719
784
  ).max(32),
720
785
  featureBehaviours: z5.array(
@@ -724,6 +789,7 @@ var CommerceManifestSchema = z5.strictObject({
724
789
  })
725
790
  ).max(32),
726
791
  selectedCapabilityKeys: z5.array(IdSchema).max(32).refine(distinct),
792
+ builtInTools: z5.array(CommerceBuiltInToolDescriptorSchema).max(2).default([]),
727
793
  grantedTools: GrantedToolsSchema,
728
794
  responseContract: CommerceResponseContractSchema,
729
795
  responseContractHash: HashSchema
@@ -765,7 +831,8 @@ var CommerceManifestSchema = z5.strictObject({
765
831
  }
766
832
  });
767
833
  }
768
- if (union.size !== v.grantedTools.length || v.grantedTools.some((t) => {
834
+ if (union.size + v.builtInTools.length !== v.grantedTools.length || !distinct(v.builtInTools.map((tool) => tool.name)) || v.builtInTools.some((tool) => !isTrustedCommerceBuiltInDescriptor(tool)) || v.grantedTools.some((t) => {
835
+ if (isBuiltInTool(t)) return !v.builtInTools.some((descriptor) => descriptor.name === t.toolName && descriptor.definitionVersion === t.definitionVersion && isTrustedCommerceBuiltInGrant(t));
769
836
  const expected = union.get(t.toolId)?.tool;
770
837
  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);
771
838
  }))
@@ -853,13 +920,23 @@ var CommerceProductSchema = z5.strictObject({
853
920
  saleEvidence: CommerceProductSaleEvidenceSchema.nullable().optional(),
854
921
  observedAt: DateSchema
855
922
  });
923
+ var discountUtcPattern = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.[0-9]+)?Z(?![\s\S])/;
924
+ var CommerceDiscountDateTimeSchema = z5.string().regex(discountUtcPattern).refine((value) => {
925
+ const match = discountUtcPattern.exec(value);
926
+ if (!match) return false;
927
+ const [year, month, day, hour, minute, second] = match.slice(1).map(Number);
928
+ if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59) return false;
929
+ const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
930
+ const daysInMonth = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
931
+ return day >= 1 && day <= daysInMonth[month - 1];
932
+ }, "Expected a valid UTC discount timestamp with seconds");
856
933
  var CommerceOfferSchema = z5.strictObject({
857
934
  offerId: IdSchema,
858
935
  title: z5.string().min(1).max(512),
859
936
  method: z5.enum(["AUTOMATIC", "CODE"]),
860
937
  code: z5.string().min(1).max(128).nullable(),
861
- startsAt: DateSchema.nullable(),
862
- endsAt: DateSchema.nullable(),
938
+ startsAt: CommerceDiscountDateTimeSchema.nullable(),
939
+ endsAt: CommerceDiscountDateTimeSchema.nullable(),
863
940
  support: z5.enum(["SUPPORTED", "UNSUPPORTED", "UNKNOWN"]),
864
941
  reasonCodes: z5.array(z5.string().min(1).max(64)).max(16)
865
942
  }).refine(
@@ -879,9 +956,9 @@ var CommerceDiscountCombinationFactsSchema = z5.strictObject({
879
956
  var CommerceDiscountConditionFactsSchema = z5.strictObject({
880
957
  provider: z5.enum(["SHOPIFY", "WOOCOMMERCE"]),
881
958
  completeness: z5.enum(["COMPLETE", "PARTIAL", "UNKNOWN"]),
882
- observedAt: DateSchema,
959
+ observedAt: CommerceDiscountDateTimeSchema,
883
960
  providerStatus: z5.string().max(64),
884
- unavailableAt: DateSchema.nullable(),
961
+ unavailableAt: CommerceDiscountDateTimeSchema.nullable(),
885
962
  discountKind: z5.enum([
886
963
  "PERCENTAGE",
887
964
  "FIXED_AMOUNT",
@@ -1003,11 +1080,111 @@ function commerceToolResultSchema(data) {
1003
1080
  var CommerceToolResultSchema = commerceToolResultSchema(
1004
1081
  boundedJson(262144)
1005
1082
  );
1083
+ var wireId = z5.string().min(1).max(128);
1084
+ var taxonomySource = z5.enum(["SHOPIFY_STANDARD_PRODUCT_TAXONOMY"]).meta({ maxLength: 33 });
1085
+ var CommerceCategoryCandidateSchema = z5.strictObject({
1086
+ categoryId: wireId,
1087
+ name: z5.string().min(1).max(512),
1088
+ fullName: z5.string().min(1).max(4096),
1089
+ parentId: wireId.nullable(),
1090
+ level: z5.number().int().min(0).max(32),
1091
+ ancestorIds: z5.array(wireId).max(32),
1092
+ distance: z5.number().min(0).max(2)
1093
+ });
1094
+ var CommerceCategoryResolutionSchema = z5.union([
1095
+ z5.strictObject({
1096
+ outcome: z5.enum(["CANDIDATES", "NO_CANDIDATES"]).meta({ maxLength: 13 }),
1097
+ generationId: wireId,
1098
+ source: taxonomySource,
1099
+ taxonomyVersion: z5.string().min(1).max(64),
1100
+ languageTag: z5.string().min(1).max(35),
1101
+ candidates: z5.array(CommerceCategoryCandidateSchema).max(10)
1102
+ }),
1103
+ z5.strictObject({
1104
+ outcome: z5.enum(["UNAVAILABLE"]).meta({ maxLength: 11 }),
1105
+ reasonCode: z5.enum(["NOT_READY", "TEMPORARY_UNAVAILABLE"]).meta({ maxLength: 21 })
1106
+ })
1107
+ ]);
1108
+ var CommerceOptionalLookupUnavailableSchema = z5.strictObject({
1109
+ outcome: z5.enum(["UNAVAILABLE"]).meta({ maxLength: 11 }),
1110
+ reasonCode: z5.enum(["TEMPORARY_UNAVAILABLE"]).meta({ maxLength: 21 })
1111
+ });
1112
+ var CommerceProductRecommendationSelectionSchema = z5.strictObject({
1113
+ productRecommendationVersion: z5.enum(["1"]).meta({ maxLength: 1 }).optional(),
1114
+ productRecommendations: z5.array(z5.strictObject({
1115
+ scope: z5.enum(["PRODUCT_ONLY", "ADD_TO_BASKET", "REPLACE_BASKET_LINE"]).meta({ maxLength: 19 }),
1116
+ basketLineId: wireId.nullable(),
1117
+ productId: wireId,
1118
+ variantId: wireId.nullable(),
1119
+ quantity: z5.number().int().min(1).max(99),
1120
+ promotionKind: z5.enum(["NONE", "NATIVE_SALE", "DISCOUNT"]).meta({ maxLength: 11 }),
1121
+ offerIds: z5.array(wireId).max(3)
1122
+ })).min(1).max(3).optional()
1123
+ });
1124
+ var CommerceResponseDispositionSchema = z5.enum([
1125
+ "STANDARD",
1126
+ "OUT_OF_SCOPE_REDIRECT",
1127
+ "SHIPPING_INFORMATION_UNVERIFIED"
1128
+ ]).meta({ maxLength: 31 });
1129
+ var CommerceAnswerPresentationSelectionSchema = z5.strictObject({
1130
+ responseDisposition: CommerceResponseDispositionSchema.optional(),
1131
+ shippingInformationSelection: z5.strictObject({
1132
+ productId: wireId.nullable(),
1133
+ variantId: wireId.nullable()
1134
+ }).optional()
1135
+ });
1136
+ var CommerceShippingInformationInputSchema = z5.strictObject({
1137
+ productId: wireId.nullable().default(null),
1138
+ variantId: wireId.nullable().default(null)
1139
+ });
1140
+ var CommerceShippingInformationSchema = z5.strictObject({
1141
+ provider: z5.enum(["SHOPIFY", "WOOCOMMERCE"]).meta({ maxLength: 11 }),
1142
+ observedAt: DateSchema,
1143
+ product: z5.strictObject({
1144
+ status: z5.enum(["NOT_REQUESTED", "VARIANT_REQUIRED", "AVAILABLE"]).meta({ maxLength: 16 }),
1145
+ productId: wireId.nullable(),
1146
+ variantId: wireId.nullable(),
1147
+ requiresShipping: z5.boolean().nullable(),
1148
+ virtual: z5.boolean().nullable(),
1149
+ weight: z5.strictObject({
1150
+ value: z5.number().min(0),
1151
+ unit: z5.enum(["GRAMS", "KILOGRAMS", "OUNCES", "POUNDS"]).meta({ maxLength: 9 }).nullable()
1152
+ }).nullable(),
1153
+ dimensions: z5.strictObject({
1154
+ length: z5.number().min(0).nullable(),
1155
+ width: z5.number().min(0).nullable(),
1156
+ height: z5.number().min(0).nullable(),
1157
+ unit: z5.string().min(1).max(16).nullable()
1158
+ }).nullable(),
1159
+ shippingClassId: wireId.nullable(),
1160
+ shippingClassSlug: z5.string().min(1).max(512).nullable()
1161
+ }),
1162
+ policy: z5.strictObject({
1163
+ status: z5.enum(["AVAILABLE", "NOT_CONFIGURED", "SCOPE_NOT_GRANTED", "UNSUPPORTED"]).meta({ maxLength: 17 }),
1164
+ trust: z5.enum(["UNTRUSTED_REFERENCE"]).meta({ maxLength: 19 }),
1165
+ title: z5.string().min(1).max(512).nullable(),
1166
+ bodyStatus: z5.enum(["AVAILABLE", "EMPTY", "TOO_LARGE", "NOT_AVAILABLE"]).meta({ maxLength: 13 }),
1167
+ bodyHtml: z5.string().min(1).max(8e3).nullable(),
1168
+ url: z5.url({ protocol: /^https$/ }).min(1).max(2048).nullable(),
1169
+ updatedAt: DateSchema.nullable()
1170
+ })
1171
+ });
1006
1172
  var limit = z5.number().int().min(1).max(3).default(3);
1007
1173
  var CommerceToolInputs = {
1174
+ commerce_resolve_product_categories: z5.strictObject({
1175
+ query: z5.string().min(1).max(200),
1176
+ limit: z5.number().int().min(1).max(10).default(5)
1177
+ }),
1178
+ commerce_get_shipping_information: CommerceShippingInformationInputSchema,
1008
1179
  commerce_get_basket: z5.strictObject({}),
1009
1180
  commerce_search_products: z5.strictObject({
1010
- query: z5.string().min(1).max(200),
1181
+ query: z5.string().min(1).max(200).nullable().default(null),
1182
+ category: z5.strictObject({
1183
+ system: z5.enum(["SHOPIFY_STANDARD_PRODUCT_TAXONOMY", "WOOCOMMERCE_PRODUCT_CATEGORY"]).meta({ maxLength: 33 }),
1184
+ id: wireId,
1185
+ taxonomyVersion: z5.string().min(1).max(64).nullable()
1186
+ }).optional(),
1187
+ browse: z5.boolean().default(false),
1011
1188
  maximumPrice: MoneySchema.optional(),
1012
1189
  availableOnly: z5.boolean().default(true),
1013
1190
  limit: z5.number().int().min(1).max(20).default(10),
@@ -1042,6 +1219,8 @@ var alternatives = z5.strictObject({
1042
1219
  truncated: z5.boolean()
1043
1220
  });
1044
1221
  var CommerceToolOutputs = {
1222
+ commerce_resolve_product_categories: CommerceCategoryResolutionSchema,
1223
+ commerce_get_shipping_information: CommerceShippingInformationSchema,
1045
1224
  commerce_get_basket: CommerceBasketSchema,
1046
1225
  commerce_search_products: z5.strictObject({
1047
1226
  products: z5.array(CommerceProductSchema).max(20),
@@ -1051,15 +1230,15 @@ var CommerceToolOutputs = {
1051
1230
  commerce_get_product: z5.strictObject({
1052
1231
  product: CommerceProductSchema.nullable()
1053
1232
  }),
1054
- commerce_get_discount_options: z5.strictObject({
1233
+ commerce_get_discount_options: z5.union([CommerceOptionalLookupUnavailableSchema, z5.strictObject({
1055
1234
  mode: z5.enum(["NONE", "FIXED", "AI_BEST_APPLICABLE"]),
1056
1235
  offers: z5.array(CommerceOfferSchema).max(50),
1057
1236
  truncated: z5.boolean(),
1058
1237
  cursor: z5.string().max(2048).nullable().optional()
1059
- }),
1060
- commerce_get_discount_details: z5.strictObject({
1238
+ })]),
1239
+ commerce_get_discount_details: z5.union([CommerceOptionalLookupUnavailableSchema, z5.strictObject({
1061
1240
  details: CommerceDiscountDetailsSchema.nullable()
1062
- }),
1241
+ })]),
1063
1242
  commerce_evaluate_discount: CommerceEvidenceSchema,
1064
1243
  commerce_find_qualifying_products: alternatives,
1065
1244
  commerce_find_similar_products: alternatives
@@ -1101,9 +1280,15 @@ export {
1101
1280
  ConnectionIssueSchema,
1102
1281
  ConnectionResultSchema,
1103
1282
  ToolNameSchema,
1283
+ PublishedGrantedToolSchema,
1284
+ PublishedToolDescriptorSchema,
1285
+ CommerceBuiltInToolNameSchema,
1286
+ CommerceBuiltInToolGrantSchema,
1287
+ CommerceBuiltInToolDescriptorSchema,
1104
1288
  GrantedToolSchema,
1105
- GrantedToolsSchema,
1106
1289
  ToolDescriptorSchema,
1290
+ GrantedToolsSchema,
1291
+ isBuiltInTool,
1107
1292
  POLICY_OPERATIONS,
1108
1293
  ExternalHttpExecutionSchema,
1109
1294
  CommerceExecutionSchema,
@@ -1118,6 +1303,9 @@ export {
1118
1303
  CommerceToolIdentitySchema,
1119
1304
  CommerceToolRevisionIdentitySchema,
1120
1305
  POLICY_OPERATION_DESCRIPTORS,
1306
+ commerceDiscountBuiltInDescriptors,
1307
+ isTrustedCommerceBuiltInDescriptor,
1308
+ isTrustedCommerceBuiltInGrant,
1121
1309
  CommerceResponseContractSchema,
1122
1310
  EMPTY_RESPONSE_CONTRACT,
1123
1311
  ReferralReasonSchema,
@@ -1145,8 +1333,16 @@ export {
1145
1333
  CommerceErrorCodeSchema,
1146
1334
  commerceToolResultSchema,
1147
1335
  CommerceToolResultSchema,
1336
+ CommerceCategoryCandidateSchema,
1337
+ CommerceCategoryResolutionSchema,
1338
+ CommerceOptionalLookupUnavailableSchema,
1339
+ CommerceProductRecommendationSelectionSchema,
1340
+ CommerceResponseDispositionSchema,
1341
+ CommerceAnswerPresentationSelectionSchema,
1342
+ CommerceShippingInformationInputSchema,
1343
+ CommerceShippingInformationSchema,
1148
1344
  CommerceToolInputs,
1149
1345
  CommerceToolOutputs,
1150
1346
  productBelongsToDomain
1151
1347
  };
1152
- //# sourceMappingURL=chunk-H7AUKACO.js.map
1348
+ //# sourceMappingURL=chunk-NA76IPIW.js.map