@burdenoff/website-sdk 2026.922.2 → 2026.922.4

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.
@@ -17,6 +17,18 @@
17
17
  */
18
18
  type ExploreMessageRole = "USER" | "ASSISTANT";
19
19
  type ExploreMessageStatus = "PENDING" | "RUNNING" | "COMPLETED" | "FAILED";
20
+ /** `enum ExploreFeedbackRating` — the visitor's verdict on one answer. */
21
+ type ExploreFeedbackRating = "HELPFUL" | "NOT_HELPFUL";
22
+ /**
23
+ * The reasons offered alongside `NOT_HELPFUL`, in the order the chooser shows them.
24
+ *
25
+ * `reason` is `String` on the wire, not an enum, so this list is the SDK's half of the
26
+ * agreement rather than something the schema enforces — the same treatment
27
+ * {@link ExploreCtaKind} gets. Use the union when you are *producing* a reason; read one
28
+ * back as a plain string.
29
+ */
30
+ declare const EXPLORE_FEEDBACK_REASONS: readonly ["INACCURATE", "INCOMPLETE", "OFF_TOPIC", "HARD_TO_FOLLOW", "OTHER"];
31
+ type ExploreFeedbackReason = (typeof EXPLORE_FEEDBACK_REASONS)[number];
20
32
  /**
21
33
  * The `ExploreCta.kind` values enumerated in CONTRACT.md §1.
22
34
  *
@@ -56,6 +68,21 @@ interface ExploreCtaLink {
56
68
  url: string;
57
69
  product?: string | null;
58
70
  }
71
+ /**
72
+ * `type ExploreFeedback` — what is on record for one answer, as the server holds it.
73
+ *
74
+ * A rating REPLACES the previous one (the server upserts on `messageId`), so this is the
75
+ * visitor's current opinion and never a history. `reason` is null for `HELPFUL` by
76
+ * definition, and typed `string` rather than the union because it is `String` on the wire —
77
+ * a reason added server-side must read back, not break the build of 38 websites.
78
+ */
79
+ interface ExploreFeedback {
80
+ rating: ExploreFeedbackRating;
81
+ /** One of {@link EXPLORE_FEEDBACK_REASONS}. Null for `HELPFUL`. */
82
+ reason?: string | null;
83
+ comment?: string | null;
84
+ createdAt: string;
85
+ }
59
86
  /** `type ExploreMessage`. `content` is `""` while the turn is PENDING. */
60
87
  interface ExploreMessage {
61
88
  id: string;
@@ -79,6 +106,16 @@ interface ExploreMessage {
79
106
  answered?: boolean | null;
80
107
  /** Files the visitor attached to this turn. Present on USER messages only. */
81
108
  attachments?: ExploreAttachment[] | null;
109
+ /**
110
+ * The rating this visitor already gave this answer, or null for an unrated one.
111
+ *
112
+ * Selected only by the reads scoped to a `conversationToken`, so a polled or restored
113
+ * transcript shows what was already rated instead of offering the row again. It is always
114
+ * null on a SHARED transcript — that read is performed by someone else, and the rater's
115
+ * verdict and free-text note are not theirs to receive. See
116
+ * `EXPLORE_OWN_MESSAGE_FIELDS`.
117
+ */
118
+ feedback?: ExploreFeedback | null;
82
119
  errorCode?: string | null;
83
120
  createdAt: string;
84
121
  completedAt?: string | null;
@@ -105,6 +142,27 @@ interface ExploreLimits {
105
142
  messagesPerDay: number;
106
143
  historyTurns: number;
107
144
  }
145
+ /**
146
+ * `type ExploreConceptHighlight` — one published concept illustration, offered
147
+ * as a way into the conversation before the visitor has typed anything.
148
+ *
149
+ * Every field is non-null on the wire: a highlight with no picture or no
150
+ * destination is not a highlight, so the server drops it rather than sending a
151
+ * half-built one. `product` is the slug, `productName` the display casing the
152
+ * product's own pages use.
153
+ *
154
+ * The pictures are the published concept mockups — illustrations of an idea,
155
+ * not screenshots of shipping software — which is why every surface that shows
156
+ * one also labels it (`ExploreStrings.conceptIllustration`).
157
+ */
158
+ interface ExploreConceptHighlight {
159
+ product: string;
160
+ productName: string;
161
+ title: string;
162
+ url: string;
163
+ imageUrl: string;
164
+ alt: string;
165
+ }
108
166
  /**
109
167
  * `type ExploreCatalog` — everything the page needs before the first message.
110
168
  *
@@ -120,6 +178,18 @@ interface ExploreCatalog {
120
178
  limits: ExploreLimits;
121
179
  captchaRequired: boolean;
122
180
  recaptchaSiteKey?: string | null;
181
+ /**
182
+ * At most 8, the site's own product first. Non-null and non-nullable per the
183
+ * contract, so a gateway that knows the field always sends an array.
184
+ *
185
+ * A gateway that does NOT know it yet does not answer without it — it refuses
186
+ * the whole document (`GRAPHQL_VALIDATION_FAILED`), which is why the hook
187
+ * retries with `EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS` instead of falling
188
+ * back to the built-in catalog. That retry is the one response that reaches
189
+ * `normalizeCatalog` with this field absent, and the hardening there is what
190
+ * keeps `catalog.conceptHighlights.filter(...)` from throwing in render.
191
+ */
192
+ conceptHighlights: ExploreConceptHighlight[];
123
193
  }
124
194
  /** `input SendExploreMessageInput`. */
125
195
  interface SendExploreMessageInput {
@@ -165,15 +235,38 @@ interface SendExploreMessageResult {
165
235
  remainingToday?: number | null;
166
236
  }
167
237
  /** Loaded once per page. Safe to serve from the SDK's 5-minute query cache. */
168
- declare const EXPLORE_CATALOG_QUERY = "\n query ExploreCatalog($productSlug: String) {\n exploreCatalog(productSlug: $productSlug) {\n enabled\n products {\n slug\n name\n tagline\n website\n }\n examplePrompts\n welcomeTitle\n welcomeBody\n limits {\n maxMessageLength\n maxTurnsPerConversation\n messagesPerDay\n historyTurns\n }\n captchaRequired\n recaptchaSiteKey\n }\n }\n";
238
+ declare const EXPLORE_CATALOG_QUERY: string;
239
+ /**
240
+ * The same catalog WITHOUT `conceptHighlights`, for a gateway whose supergraph
241
+ * predates the field.
242
+ *
243
+ * This is not belt-and-braces. GraphQL validation is all-or-nothing: a gateway
244
+ * that does not know the field rejects the ENTIRE document and returns no
245
+ * `data` at all, so selecting `conceptHighlights` against an older supergraph
246
+ * costs the whole catalog — `enabled`, `captchaRequired`, `recaptchaSiteKey`,
247
+ * the welcome copy and `limits` included — and not merely the strip. Measured
248
+ * against production:
249
+ *
250
+ * {"errors":[{"message":"Cannot query field \\"conceptHighlights\\" on type
251
+ * \\"ExploreCatalog\\".","extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}]}
252
+ *
253
+ * That window is guaranteed even with perfect deploy ordering, because the prod
254
+ * gateway's CDN edge serves a stale supergraph for ~30-40 minutes after the
255
+ * subgraph publishes. `useExploreChat` therefore retries with this document
256
+ * (see `catalogRejectedTheHighlightField`) rather than dropping to the built-in
257
+ * fallback, whose hardcoded `enabled: true` would ignore the server-side kill
258
+ * switch and whose `captchaRequired: false` would let every send be rejected
259
+ * server-side while the composer looked fine.
260
+ */
261
+ declare const EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS: string;
169
262
  /**
170
263
  * The poll. MUST be issued with `client.query(..., { cache: false })` — the
171
264
  * default 5-minute cache would return the first PENDING snapshot forever and
172
265
  * the answer would never appear.
173
266
  */
174
- declare const EXPLORE_MESSAGE_QUERY = "\n query ExploreMessage($conversationToken: String!, $id: ID!) {\n exploreMessage(conversationToken: $conversationToken, id: $id) {\n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n }\n }\n";
267
+ declare const EXPLORE_MESSAGE_QUERY = "\n query ExploreMessage($conversationToken: String!, $id: ID!) {\n exploreMessage(conversationToken: $conversationToken, id: $id) {\n \n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n feedback {\n rating\n reason\n comment\n createdAt\n }\n\n }\n }\n";
175
268
  /** Submit half of the submit + poll pair. */
176
- declare const SEND_EXPLORE_MESSAGE_MUTATION = "\n mutation SendExploreMessage($input: SendExploreMessageInput!) {\n sendExploreMessage(input: $input) {\n conversation {\n id\n token\n productSlug\n turnCount\n createdAt\n }\n userMessage {\n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n }\n assistantMessage {\n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n }\n remainingToday\n }\n }\n";
269
+ declare const SEND_EXPLORE_MESSAGE_MUTATION = "\n mutation SendExploreMessage($input: SendExploreMessageInput!) {\n sendExploreMessage(input: $input) {\n conversation {\n id\n token\n productSlug\n turnCount\n createdAt\n }\n userMessage {\n \n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n feedback {\n rating\n reason\n comment\n createdAt\n }\n\n }\n assistantMessage {\n \n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n feedback {\n rating\n reason\n comment\n createdAt\n }\n\n }\n remainingToday\n }\n }\n";
177
270
  interface ExploreCatalogQueryData {
178
271
  exploreCatalog: ExploreCatalog;
179
272
  }
@@ -184,4 +277,4 @@ interface SendExploreMessageMutationData {
184
277
  sendExploreMessage: SendExploreMessageResult;
185
278
  }
186
279
 
187
- export { type ExploreProduct as E, SEND_EXPLORE_MESSAGE_MUTATION as S, type ExploreLimits as a, type ExploreCatalog as b, type ExploreMessage as c, EXPLORE_CATALOG_QUERY as d, EXPLORE_MESSAGE_QUERY as e, type ExploreCatalogQueryData as f, type ExploreConversation as g, type ExploreCtaKind as h, type ExploreCtaLink as i, type ExploreErrorCode as j, type ExploreMessageQueryData as k, type ExploreMessageRole as l, type ExploreMessageStatus as m, type ExploreReference as n, type SendExploreMessageInput as o, type SendExploreMessageMutationData as p, type SendExploreMessageResult as q };
280
+ export { type ExploreProduct as E, SEND_EXPLORE_MESSAGE_MUTATION as S, type ExploreReference as a, type ExploreLimits as b, type ExploreCatalog as c, type ExploreMessage as d, type ExploreFeedbackRating as e, type ExploreFeedbackReason as f, EXPLORE_CATALOG_QUERY as g, EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS as h, EXPLORE_MESSAGE_QUERY as i, type ExploreCatalogQueryData as j, type ExploreConceptHighlight as k, type ExploreConversation as l, type ExploreCtaKind as m, type ExploreCtaLink as n, type ExploreErrorCode as o, type ExploreMessageQueryData as p, type ExploreMessageRole as q, type ExploreMessageStatus as r, type SendExploreMessageInput as s, type SendExploreMessageMutationData as t, type SendExploreMessageResult as u };
@@ -17,6 +17,18 @@
17
17
  */
18
18
  type ExploreMessageRole = "USER" | "ASSISTANT";
19
19
  type ExploreMessageStatus = "PENDING" | "RUNNING" | "COMPLETED" | "FAILED";
20
+ /** `enum ExploreFeedbackRating` — the visitor's verdict on one answer. */
21
+ type ExploreFeedbackRating = "HELPFUL" | "NOT_HELPFUL";
22
+ /**
23
+ * The reasons offered alongside `NOT_HELPFUL`, in the order the chooser shows them.
24
+ *
25
+ * `reason` is `String` on the wire, not an enum, so this list is the SDK's half of the
26
+ * agreement rather than something the schema enforces — the same treatment
27
+ * {@link ExploreCtaKind} gets. Use the union when you are *producing* a reason; read one
28
+ * back as a plain string.
29
+ */
30
+ declare const EXPLORE_FEEDBACK_REASONS: readonly ["INACCURATE", "INCOMPLETE", "OFF_TOPIC", "HARD_TO_FOLLOW", "OTHER"];
31
+ type ExploreFeedbackReason = (typeof EXPLORE_FEEDBACK_REASONS)[number];
20
32
  /**
21
33
  * The `ExploreCta.kind` values enumerated in CONTRACT.md §1.
22
34
  *
@@ -56,6 +68,21 @@ interface ExploreCtaLink {
56
68
  url: string;
57
69
  product?: string | null;
58
70
  }
71
+ /**
72
+ * `type ExploreFeedback` — what is on record for one answer, as the server holds it.
73
+ *
74
+ * A rating REPLACES the previous one (the server upserts on `messageId`), so this is the
75
+ * visitor's current opinion and never a history. `reason` is null for `HELPFUL` by
76
+ * definition, and typed `string` rather than the union because it is `String` on the wire —
77
+ * a reason added server-side must read back, not break the build of 38 websites.
78
+ */
79
+ interface ExploreFeedback {
80
+ rating: ExploreFeedbackRating;
81
+ /** One of {@link EXPLORE_FEEDBACK_REASONS}. Null for `HELPFUL`. */
82
+ reason?: string | null;
83
+ comment?: string | null;
84
+ createdAt: string;
85
+ }
59
86
  /** `type ExploreMessage`. `content` is `""` while the turn is PENDING. */
60
87
  interface ExploreMessage {
61
88
  id: string;
@@ -79,6 +106,16 @@ interface ExploreMessage {
79
106
  answered?: boolean | null;
80
107
  /** Files the visitor attached to this turn. Present on USER messages only. */
81
108
  attachments?: ExploreAttachment[] | null;
109
+ /**
110
+ * The rating this visitor already gave this answer, or null for an unrated one.
111
+ *
112
+ * Selected only by the reads scoped to a `conversationToken`, so a polled or restored
113
+ * transcript shows what was already rated instead of offering the row again. It is always
114
+ * null on a SHARED transcript — that read is performed by someone else, and the rater's
115
+ * verdict and free-text note are not theirs to receive. See
116
+ * `EXPLORE_OWN_MESSAGE_FIELDS`.
117
+ */
118
+ feedback?: ExploreFeedback | null;
82
119
  errorCode?: string | null;
83
120
  createdAt: string;
84
121
  completedAt?: string | null;
@@ -105,6 +142,27 @@ interface ExploreLimits {
105
142
  messagesPerDay: number;
106
143
  historyTurns: number;
107
144
  }
145
+ /**
146
+ * `type ExploreConceptHighlight` — one published concept illustration, offered
147
+ * as a way into the conversation before the visitor has typed anything.
148
+ *
149
+ * Every field is non-null on the wire: a highlight with no picture or no
150
+ * destination is not a highlight, so the server drops it rather than sending a
151
+ * half-built one. `product` is the slug, `productName` the display casing the
152
+ * product's own pages use.
153
+ *
154
+ * The pictures are the published concept mockups — illustrations of an idea,
155
+ * not screenshots of shipping software — which is why every surface that shows
156
+ * one also labels it (`ExploreStrings.conceptIllustration`).
157
+ */
158
+ interface ExploreConceptHighlight {
159
+ product: string;
160
+ productName: string;
161
+ title: string;
162
+ url: string;
163
+ imageUrl: string;
164
+ alt: string;
165
+ }
108
166
  /**
109
167
  * `type ExploreCatalog` — everything the page needs before the first message.
110
168
  *
@@ -120,6 +178,18 @@ interface ExploreCatalog {
120
178
  limits: ExploreLimits;
121
179
  captchaRequired: boolean;
122
180
  recaptchaSiteKey?: string | null;
181
+ /**
182
+ * At most 8, the site's own product first. Non-null and non-nullable per the
183
+ * contract, so a gateway that knows the field always sends an array.
184
+ *
185
+ * A gateway that does NOT know it yet does not answer without it — it refuses
186
+ * the whole document (`GRAPHQL_VALIDATION_FAILED`), which is why the hook
187
+ * retries with `EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS` instead of falling
188
+ * back to the built-in catalog. That retry is the one response that reaches
189
+ * `normalizeCatalog` with this field absent, and the hardening there is what
190
+ * keeps `catalog.conceptHighlights.filter(...)` from throwing in render.
191
+ */
192
+ conceptHighlights: ExploreConceptHighlight[];
123
193
  }
124
194
  /** `input SendExploreMessageInput`. */
125
195
  interface SendExploreMessageInput {
@@ -165,15 +235,38 @@ interface SendExploreMessageResult {
165
235
  remainingToday?: number | null;
166
236
  }
167
237
  /** Loaded once per page. Safe to serve from the SDK's 5-minute query cache. */
168
- declare const EXPLORE_CATALOG_QUERY = "\n query ExploreCatalog($productSlug: String) {\n exploreCatalog(productSlug: $productSlug) {\n enabled\n products {\n slug\n name\n tagline\n website\n }\n examplePrompts\n welcomeTitle\n welcomeBody\n limits {\n maxMessageLength\n maxTurnsPerConversation\n messagesPerDay\n historyTurns\n }\n captchaRequired\n recaptchaSiteKey\n }\n }\n";
238
+ declare const EXPLORE_CATALOG_QUERY: string;
239
+ /**
240
+ * The same catalog WITHOUT `conceptHighlights`, for a gateway whose supergraph
241
+ * predates the field.
242
+ *
243
+ * This is not belt-and-braces. GraphQL validation is all-or-nothing: a gateway
244
+ * that does not know the field rejects the ENTIRE document and returns no
245
+ * `data` at all, so selecting `conceptHighlights` against an older supergraph
246
+ * costs the whole catalog — `enabled`, `captchaRequired`, `recaptchaSiteKey`,
247
+ * the welcome copy and `limits` included — and not merely the strip. Measured
248
+ * against production:
249
+ *
250
+ * {"errors":[{"message":"Cannot query field \\"conceptHighlights\\" on type
251
+ * \\"ExploreCatalog\\".","extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}]}
252
+ *
253
+ * That window is guaranteed even with perfect deploy ordering, because the prod
254
+ * gateway's CDN edge serves a stale supergraph for ~30-40 minutes after the
255
+ * subgraph publishes. `useExploreChat` therefore retries with this document
256
+ * (see `catalogRejectedTheHighlightField`) rather than dropping to the built-in
257
+ * fallback, whose hardcoded `enabled: true` would ignore the server-side kill
258
+ * switch and whose `captchaRequired: false` would let every send be rejected
259
+ * server-side while the composer looked fine.
260
+ */
261
+ declare const EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS: string;
169
262
  /**
170
263
  * The poll. MUST be issued with `client.query(..., { cache: false })` — the
171
264
  * default 5-minute cache would return the first PENDING snapshot forever and
172
265
  * the answer would never appear.
173
266
  */
174
- declare const EXPLORE_MESSAGE_QUERY = "\n query ExploreMessage($conversationToken: String!, $id: ID!) {\n exploreMessage(conversationToken: $conversationToken, id: $id) {\n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n }\n }\n";
267
+ declare const EXPLORE_MESSAGE_QUERY = "\n query ExploreMessage($conversationToken: String!, $id: ID!) {\n exploreMessage(conversationToken: $conversationToken, id: $id) {\n \n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n feedback {\n rating\n reason\n comment\n createdAt\n }\n\n }\n }\n";
175
268
  /** Submit half of the submit + poll pair. */
176
- declare const SEND_EXPLORE_MESSAGE_MUTATION = "\n mutation SendExploreMessage($input: SendExploreMessageInput!) {\n sendExploreMessage(input: $input) {\n conversation {\n id\n token\n productSlug\n turnCount\n createdAt\n }\n userMessage {\n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n }\n assistantMessage {\n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n }\n remainingToday\n }\n }\n";
269
+ declare const SEND_EXPLORE_MESSAGE_MUTATION = "\n mutation SendExploreMessage($input: SendExploreMessageInput!) {\n sendExploreMessage(input: $input) {\n conversation {\n id\n token\n productSlug\n turnCount\n createdAt\n }\n userMessage {\n \n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n feedback {\n rating\n reason\n comment\n createdAt\n }\n\n }\n assistantMessage {\n \n \n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n\n feedback {\n rating\n reason\n comment\n createdAt\n }\n\n }\n remainingToday\n }\n }\n";
177
270
  interface ExploreCatalogQueryData {
178
271
  exploreCatalog: ExploreCatalog;
179
272
  }
@@ -184,4 +277,4 @@ interface SendExploreMessageMutationData {
184
277
  sendExploreMessage: SendExploreMessageResult;
185
278
  }
186
279
 
187
- export { type ExploreProduct as E, SEND_EXPLORE_MESSAGE_MUTATION as S, type ExploreLimits as a, type ExploreCatalog as b, type ExploreMessage as c, EXPLORE_CATALOG_QUERY as d, EXPLORE_MESSAGE_QUERY as e, type ExploreCatalogQueryData as f, type ExploreConversation as g, type ExploreCtaKind as h, type ExploreCtaLink as i, type ExploreErrorCode as j, type ExploreMessageQueryData as k, type ExploreMessageRole as l, type ExploreMessageStatus as m, type ExploreReference as n, type SendExploreMessageInput as o, type SendExploreMessageMutationData as p, type SendExploreMessageResult as q };
280
+ export { type ExploreProduct as E, SEND_EXPLORE_MESSAGE_MUTATION as S, type ExploreReference as a, type ExploreLimits as b, type ExploreCatalog as c, type ExploreMessage as d, type ExploreFeedbackRating as e, type ExploreFeedbackReason as f, EXPLORE_CATALOG_QUERY as g, EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS as h, EXPLORE_MESSAGE_QUERY as i, type ExploreCatalogQueryData as j, type ExploreConceptHighlight as k, type ExploreConversation as l, type ExploreCtaKind as m, type ExploreCtaLink as n, type ExploreErrorCode as o, type ExploreMessageQueryData as p, type ExploreMessageRole as q, type ExploreMessageStatus as r, type SendExploreMessageInput as s, type SendExploreMessageMutationData as t, type SendExploreMessageResult as u };
@@ -1,5 +1,5 @@
1
- export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, F as FALLBACK_EXPLORE_LIMITS, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from '../use-explore-chat-DC47G-C3.mjs';
2
- import '../explore-types-CLw19R76.mjs';
1
+ export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, F as FALLBACK_EXPLORE_LIMITS, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from '../use-explore-chat-Dgm-jf96.mjs';
2
+ import '../explore-types-DvQPiL1t.mjs';
3
3
 
4
4
  interface UseSpeechInputOptions {
5
5
  /** Receives settled text as it is recognised. Append it to the draft. */
@@ -1,5 +1,5 @@
1
- export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, F as FALLBACK_EXPLORE_LIMITS, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from '../use-explore-chat-DyPMkzXA.js';
2
- import '../explore-types-CLw19R76.js';
1
+ export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, F as FALLBACK_EXPLORE_LIMITS, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from '../use-explore-chat-CNEdKBc1.js';
2
+ import '../explore-types-DvQPiL1t.js';
3
3
 
4
4
  interface UseSpeechInputOptions {
5
5
  /** Receives settled text as it is recognised. Append it to the draft. */
@@ -265,11 +265,21 @@ var EXPLORE_MESSAGE_FIELDS = (
265
265
  completedAt
266
266
  `
267
267
  );
268
- var EXPLORE_CATALOG_QUERY = (
268
+ var EXPLORE_OWN_MESSAGE_FIELDS = (
269
+ /* GraphQL */
270
+ `
271
+ ${EXPLORE_MESSAGE_FIELDS}
272
+ feedback {
273
+ rating
274
+ reason
275
+ comment
276
+ createdAt
277
+ }
278
+ `
279
+ );
280
+ var EXPLORE_CATALOG_ESTABLISHED_FIELDS = (
269
281
  /* GraphQL */
270
282
  `
271
- query ExploreCatalog($productSlug: String) {
272
- exploreCatalog(productSlug: $productSlug) {
273
283
  enabled
274
284
  products {
275
285
  slug
@@ -287,17 +297,41 @@ var EXPLORE_CATALOG_QUERY = (
287
297
  historyTurns
288
298
  }
289
299
  captchaRequired
290
- recaptchaSiteKey
300
+ recaptchaSiteKey`
301
+ );
302
+ var EXPLORE_CATALOG_HIGHLIGHT_FIELD = (
303
+ /* GraphQL */
304
+ `
305
+ conceptHighlights {
306
+ product
307
+ productName
308
+ title
309
+ url
310
+ imageUrl
311
+ alt
312
+ }`
313
+ );
314
+ function exploreCatalogDocument(extraFields) {
315
+ return (
316
+ /* GraphQL */
317
+ `
318
+ query ExploreCatalog($productSlug: String) {
319
+ exploreCatalog(productSlug: $productSlug) {${EXPLORE_CATALOG_ESTABLISHED_FIELDS}${extraFields}
291
320
  }
292
321
  }
293
322
  `
323
+ );
324
+ }
325
+ var EXPLORE_CATALOG_QUERY = exploreCatalogDocument(
326
+ EXPLORE_CATALOG_HIGHLIGHT_FIELD
294
327
  );
328
+ var EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS = exploreCatalogDocument("");
295
329
  var EXPLORE_MESSAGE_QUERY = (
296
330
  /* GraphQL */
297
331
  `
298
332
  query ExploreMessage($conversationToken: String!, $id: ID!) {
299
333
  exploreMessage(conversationToken: $conversationToken, id: $id) {
300
- ${EXPLORE_MESSAGE_FIELDS}
334
+ ${EXPLORE_OWN_MESSAGE_FIELDS}
301
335
  }
302
336
  }
303
337
  `
@@ -315,10 +349,10 @@ var SEND_EXPLORE_MESSAGE_MUTATION = (
315
349
  createdAt
316
350
  }
317
351
  userMessage {
318
- ${EXPLORE_MESSAGE_FIELDS}
352
+ ${EXPLORE_OWN_MESSAGE_FIELDS}
319
353
  }
320
354
  assistantMessage {
321
- ${EXPLORE_MESSAGE_FIELDS}
355
+ ${EXPLORE_OWN_MESSAGE_FIELDS}
322
356
  }
323
357
  remainingToday
324
358
  }
@@ -335,6 +369,22 @@ var RECORD_EXPLORE_CLICK_MUTATION = (
335
369
  }
336
370
  `
337
371
  );
372
+ var SUBMIT_EXPLORE_FEEDBACK_MUTATION = (
373
+ /* GraphQL */
374
+ `
375
+ mutation SubmitExploreFeedback($input: SubmitExploreFeedbackInput!) {
376
+ submitExploreFeedback(input: $input) {
377
+ recorded
378
+ feedback {
379
+ rating
380
+ reason
381
+ comment
382
+ createdAt
383
+ }
384
+ }
385
+ }
386
+ `
387
+ );
338
388
  var CREATE_EXPLORE_SHARE_LINK_MUTATION = (
339
389
  /* GraphQL */
340
390
  `
@@ -639,7 +689,12 @@ function buildFallbackCatalog(examplePrompts, welcome) {
639
689
  welcomeBody: welcome?.body ?? FALLBACK_WELCOME_BODY,
640
690
  limits: FALLBACK_EXPLORE_LIMITS,
641
691
  captchaRequired: false,
642
- recaptchaSiteKey: null
692
+ recaptchaSiteKey: null,
693
+ // Empty, and deliberately not synthesised from `ECOSYSTEM_PRODUCTS`: a concept highlight
694
+ // needs a real published illustration URL, and this SDK has no registry of them. An
695
+ // invented path would render 38 broken frames on the one screen every prerender captures.
696
+ // The strip renders nothing at all when the list is empty, which is the honest fallback.
697
+ conceptHighlights: []
643
698
  };
644
699
  }
645
700
  function normalizeCatalog(raw, fallback) {
@@ -656,9 +711,27 @@ function normalizeCatalog(raw, fallback) {
656
711
  historyTurns: raw.limits?.historyTurns || fallback.limits.historyTurns
657
712
  },
658
713
  captchaRequired: raw.captchaRequired === true,
659
- recaptchaSiteKey: raw.recaptchaSiteKey ?? null
714
+ recaptchaSiteKey: raw.recaptchaSiteKey ?? null,
715
+ // `[ExploreConceptHighlight!]!` on the wire, so a gateway that KNOWS the field always
716
+ // sends an array. The one response that arrives without it is the retry against an older
717
+ // supergraph (`EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS`) — that gateway does not answer
718
+ // the full document at all, it refuses it. Either way
719
+ // `catalog.conceptHighlights.filter(...)` on `undefined` would throw inside render,
720
+ // blanking the welcome screen over a decorative strip.
721
+ conceptHighlights: raw.conceptHighlights?.length ? raw.conceptHighlights : fallback.conceptHighlights
660
722
  };
661
723
  }
724
+ var CATALOG_DOCUMENT_REJECTED_CODES = /* @__PURE__ */ new Set([
725
+ "GRAPHQL_VALIDATION_FAILED",
726
+ "HTTP_400"
727
+ ]);
728
+ function catalogRejectedTheHighlightField(res) {
729
+ if (res.data?.exploreCatalog) return false;
730
+ return (res.errors ?? []).some((error) => {
731
+ const code = error.extensions?.code;
732
+ return typeof code === "string" && CATALOG_DOCUMENT_REJECTED_CODES.has(code) || error.message.includes("conceptHighlights");
733
+ });
734
+ }
662
735
  var RATE_LIMIT_CODES = /* @__PURE__ */ new Set([
663
736
  "RATE_LIMITED",
664
737
  "HTTP_429",
@@ -998,11 +1071,19 @@ function useExploreChat(options = {}) {
998
1071
  react.useEffect(() => {
999
1072
  let cancelled = false;
1000
1073
  const load = async () => {
1001
- const res = await client.query(
1074
+ const variables = { productSlug: productSlug ?? null };
1075
+ let res = await client.query(
1002
1076
  EXPLORE_CATALOG_QUERY,
1003
- { productSlug: productSlug ?? null }
1077
+ variables
1004
1078
  );
1005
1079
  if (cancelled || !mountedRef.current) return;
1080
+ if (catalogRejectedTheHighlightField(res)) {
1081
+ res = await client.query(
1082
+ EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,
1083
+ variables
1084
+ );
1085
+ if (cancelled || !mountedRef.current) return;
1086
+ }
1006
1087
  const raw = res.data?.exploreCatalog;
1007
1088
  if (res.errors?.length || !raw) {
1008
1089
  console.warn(
@@ -1383,6 +1464,42 @@ function useExploreChat(options = {}) {
1383
1464
  },
1384
1465
  [client]
1385
1466
  );
1467
+ const submitFeedback = react.useCallback(
1468
+ async (messageId, rating, detail) => {
1469
+ const token = conversationTokenRef.current;
1470
+ if (!token) return false;
1471
+ const comment = detail?.comment?.trim();
1472
+ const input = {
1473
+ conversationToken: token,
1474
+ messageId,
1475
+ rating,
1476
+ // Only meaningful with NOT_HELPFUL. The server stores null for HELPFUL either
1477
+ // way; sending it would just be noise the backend has to defend against.
1478
+ reason: rating === "NOT_HELPFUL" ? detail?.reason ?? null : null,
1479
+ comment: comment ? comment : null
1480
+ };
1481
+ try {
1482
+ const res = await client.mutate(
1483
+ SUBMIT_EXPLORE_FEEDBACK_MUTATION,
1484
+ { input }
1485
+ );
1486
+ const result = res?.data?.submitExploreFeedback;
1487
+ if (!result?.recorded) return false;
1488
+ const recorded = result.feedback;
1489
+ if (recorded && mountedRef.current) {
1490
+ setMessages(
1491
+ (current) => current.map(
1492
+ (message) => message.id === messageId ? { ...message, feedback: recorded } : message
1493
+ )
1494
+ );
1495
+ }
1496
+ return true;
1497
+ } catch {
1498
+ return false;
1499
+ }
1500
+ },
1501
+ [client]
1502
+ );
1386
1503
  return {
1387
1504
  phase,
1388
1505
  catalog,
@@ -1402,6 +1519,7 @@ function useExploreChat(options = {}) {
1402
1519
  deleteConversation,
1403
1520
  clearHistory,
1404
1521
  recordClick,
1522
+ submitFeedback,
1405
1523
  createShareLink,
1406
1524
  isSharedView
1407
1525
  };