react-instantsearch-core 7.50.0 → 7.51.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.
@@ -1,4 +1,4 @@
1
- /*! React InstantSearch Core 7.50.0 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
1
+ /*! React InstantSearch Core 7.51.0 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
4
4
  typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
@@ -24,7 +24,7 @@
24
24
 
25
25
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
26
26
 
27
- var version$2 = '7.50.0';
27
+ var version$2 = '7.51.0';
28
28
 
29
29
  function _define_property(obj, key, value) {
30
30
  if (key in obj) {
@@ -3919,7 +3919,7 @@
3919
3919
  function requireVersion() {
3920
3920
  if (hasRequiredVersion) return version$1;
3921
3921
  hasRequiredVersion = 1;
3922
- version$1 = '3.29.3';
3922
+ version$1 = '3.30.0';
3923
3923
  return version$1;
3924
3924
  }
3925
3925
 
@@ -5259,15 +5259,45 @@
5259
5259
  return derivedStateQueries;
5260
5260
  });
5261
5261
  var queries = Array.prototype.concat.apply(mainQueries, derivedQueries);
5262
+ var uniqueQueries = [];
5263
+ var deduplicationMap = [];
5264
+ var deduplicated = false;
5265
+ var queryKeys = {};
5266
+ queries.forEach(function(query) {
5267
+ var key = JSON.stringify({
5268
+ indexName: query.indexName,
5269
+ params: query.params
5270
+ });
5271
+ if (Object.prototype.hasOwnProperty.call(queryKeys, key)) {
5272
+ var index = queryKeys[key];
5273
+ deduplicationMap.push(index);
5274
+ deduplicated = true;
5275
+ } else {
5276
+ var newIndex = uniqueQueries.length;
5277
+ uniqueQueries.push(query);
5278
+ queryKeys[key] = newIndex;
5279
+ deduplicationMap.push(newIndex);
5280
+ }
5281
+ });
5262
5282
  var queryId = this._queryId++;
5263
5283
  this._currentNbQueries++;
5264
- if (!queries.length) {
5284
+ if (!uniqueQueries.length) {
5265
5285
  return Promise.resolve({
5266
5286
  results: []
5267
5287
  }).then(this._dispatchAlgoliaResponse.bind(this, states, queryId));
5268
5288
  }
5269
5289
  try {
5270
- this.client.search(queries).then(this._dispatchAlgoliaResponse.bind(this, states, queryId)).catch(this._dispatchAlgoliaError.bind(this, queryId));
5290
+ var searchPromise = this.client.search(uniqueQueries);
5291
+ if (deduplicated) {
5292
+ searchPromise = searchPromise.then(function(res) {
5293
+ return {
5294
+ results: deduplicationMap.map(function(index) {
5295
+ return res.results[index];
5296
+ })
5297
+ };
5298
+ });
5299
+ }
5300
+ searchPromise.then(this._dispatchAlgoliaResponse.bind(this, states, queryId)).catch(this._dispatchAlgoliaError.bind(this, queryId));
5271
5301
  } catch (error) {
5272
5302
  // If we reach this part, we're in an internal error state
5273
5303
  this.emit('error', {
@@ -5980,7 +6010,7 @@
5980
6010
  }
5981
6011
  }
5982
6012
 
5983
- var withUsage$y = createDocumentationMessageGenerator({
6013
+ var withUsage$z = createDocumentationMessageGenerator({
5984
6014
  name: 'configure',
5985
6015
  connector: true
5986
6016
  });
@@ -5996,7 +6026,7 @@
5996
6026
  var renderFn = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : noop, unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
5997
6027
  return function(widgetParams) {
5998
6028
  if (!widgetParams || !isPlainObject(widgetParams.searchParameters)) {
5999
- throw new Error(withUsage$y('The `searchParameters` option expects an object.'));
6029
+ throw new Error(withUsage$z('The `searchParameters` option expects an object.'));
6000
6030
  }
6001
6031
  var connectorState = {};
6002
6032
  function refine(helper) {
@@ -7343,7 +7373,11 @@
7343
7373
  parent: parent,
7344
7374
  instantSearchInstance: instantSearchInstance,
7345
7375
  state: helper.state,
7346
- renderState: instantSearchInstance.renderState,
7376
+ // Keep renderState live so render options retained by connector closures
7377
+ // don't retain previous renderState snapshots. See #6669.
7378
+ get renderState () {
7379
+ return instantSearchInstance.renderState;
7380
+ },
7347
7381
  templatesConfig: instantSearchInstance.templatesConfig,
7348
7382
  createURL: parent.createURL,
7349
7383
  scopedResults: [],
@@ -7364,7 +7398,10 @@
7364
7398
  results: results,
7365
7399
  scopedResults: parent.getScopedResults(),
7366
7400
  state: results && '_state' in results ? results._state : helper.state,
7367
- renderState: instantSearchInstance.renderState,
7401
+ // Keep renderState live to avoid retaining previous renderState snapshots.
7402
+ get renderState () {
7403
+ return instantSearchInstance.renderState;
7404
+ },
7368
7405
  templatesConfig: instantSearchInstance.templatesConfig,
7369
7406
  createURL: parent.createURL,
7370
7407
  searchMetadata: {
@@ -7833,13 +7870,13 @@
7833
7870
  return null;
7834
7871
  }
7835
7872
 
7836
- var withUsage$x = createDocumentationMessageGenerator({
7873
+ var withUsage$y = createDocumentationMessageGenerator({
7837
7874
  name: 'dynamic-widgets',
7838
7875
  connector: true
7839
7876
  });
7840
7877
  var connectDynamicWidgets = function connectDynamicWidgets(renderFn) {
7841
7878
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
7842
- checkRendering(renderFn, withUsage$x());
7879
+ checkRendering(renderFn, withUsage$y());
7843
7880
  return function(widgetParams) {
7844
7881
  var widgets = widgetParams.widgets, _widgetParams_maxValuesPerFacet = widgetParams.maxValuesPerFacet, maxValuesPerFacet = _widgetParams_maxValuesPerFacet === void 0 ? 20 : _widgetParams_maxValuesPerFacet, _widgetParams_facets = widgetParams.facets, facets = _widgetParams_facets === void 0 ? [
7845
7882
  '*'
@@ -7849,10 +7886,10 @@
7849
7886
  if (!(widgets && Array.isArray(widgets) && widgets.every(function(widget) {
7850
7887
  return (typeof widget === "undefined" ? "undefined" : _type_of(widget)) === 'object';
7851
7888
  }))) {
7852
- throw new Error(withUsage$x('The `widgets` option expects an array of widgets.'));
7889
+ throw new Error(withUsage$y('The `widgets` option expects an array of widgets.'));
7853
7890
  }
7854
7891
  if (!Array.isArray(facets)) {
7855
- throw new Error(withUsage$x("The `facets` option only accepts an array of facets, you passed ".concat(JSON.stringify(facets))));
7892
+ throw new Error(withUsage$y("The `facets` option only accepts an array of facets, you passed ".concat(JSON.stringify(facets))));
7856
7893
  }
7857
7894
  var localWidgets = new Map();
7858
7895
  return {
@@ -7954,7 +7991,7 @@
7954
7991
  results: results
7955
7992
  });
7956
7993
  if (!Array.isArray(attributesToRender)) {
7957
- throw new Error(withUsage$x('The `transformItems` option expects a function that returns an Array.'));
7994
+ throw new Error(withUsage$y('The `transformItems` option expects a function that returns an Array.'));
7958
7995
  }
7959
7996
  return {
7960
7997
  attributesToRender: attributesToRender,
@@ -8345,19 +8382,19 @@
8345
8382
  return toFeedSearchResults(lastResults._state, raw);
8346
8383
  });
8347
8384
  }
8348
- var withUsage$w = createDocumentationMessageGenerator({
8385
+ var withUsage$x = createDocumentationMessageGenerator({
8349
8386
  name: 'feeds',
8350
8387
  connector: true
8351
8388
  });
8352
8389
  var connectFeeds = function connectFeeds(renderFn) {
8353
8390
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
8354
- checkRendering(renderFn, withUsage$w());
8391
+ checkRendering(renderFn, withUsage$x());
8355
8392
  return function(widgetParams) {
8356
8393
  var isolated = widgetParams.isolated, _widgetParams_transformFeeds = widgetParams.transformFeeds, transformFeeds = _widgetParams_transformFeeds === void 0 ? function(feeds) {
8357
8394
  return feeds;
8358
8395
  } : _widgetParams_transformFeeds;
8359
8396
  if (isolated !== false) {
8360
- throw new Error(withUsage$w('The `isolated` option currently only supports `false`.'));
8397
+ throw new Error(withUsage$x('The `isolated` option currently only supports `false`.'));
8361
8398
  }
8362
8399
  return {
8363
8400
  $$type: 'ais.feeds',
@@ -8365,7 +8402,7 @@
8365
8402
  init: function init(initOptions) {
8366
8403
  var instantSearchInstance = initOptions.instantSearchInstance;
8367
8404
  if (!instantSearchInstance.compositionID) {
8368
- throw new Error(withUsage$w('The `feeds` widget requires a composition-based InstantSearch instance (compositionID must be set).'));
8405
+ throw new Error(withUsage$x('The `feeds` widget requires a composition-based InstantSearch instance (compositionID must be set).'));
8369
8406
  }
8370
8407
  hydrateFeedsFromInitialResultsIfNeeded(instantSearchInstance, initOptions.parent);
8371
8408
  renderFn(_object_spread_props(_object_spread({}, this.getWidgetRenderState(initOptions)), {
@@ -8411,12 +8448,12 @@
8411
8448
  ];
8412
8449
  feedIDs = transformFeeds(feedIDs);
8413
8450
  if (!Array.isArray(feedIDs)) {
8414
- throw new Error(withUsage$w('The `transformFeeds` option expects a function that returns an Array.'));
8451
+ throw new Error(withUsage$x('The `transformFeeds` option expects a function that returns an Array.'));
8415
8452
  }
8416
8453
  if (!feedIDs.every(function(feedID) {
8417
8454
  return typeof feedID === 'string';
8418
8455
  })) {
8419
- throw new Error(withUsage$w('The `transformFeeds` option expects a function that returns an array of feed IDs (strings).'));
8456
+ throw new Error(withUsage$x('The `transformFeeds` option expects a function that returns an array of feed IDs (strings).'));
8420
8457
  }
8421
8458
  return {
8422
8459
  feedIDs: feedIDs,
@@ -8527,7 +8564,7 @@
8527
8564
  }));
8528
8565
  }
8529
8566
 
8530
- var withUsage$v = createDocumentationMessageGenerator({
8567
+ var withUsage$w = createDocumentationMessageGenerator({
8531
8568
  name: 'index-widget'
8532
8569
  });
8533
8570
  /**
@@ -8655,7 +8692,7 @@
8655
8692
  var index = function index(widgetParams) {
8656
8693
  var _widgetParams_EXPERIMENTAL_isolated;
8657
8694
  if (widgetParams === undefined || widgetParams.indexName === undefined && !widgetParams.isolated && !widgetParams.EXPERIMENTAL_isolated) {
8658
- throw new Error(withUsage$v('The `indexName` option is required.'));
8695
+ throw new Error(withUsage$w('The `indexName` option is required.'));
8659
8696
  }
8660
8697
  // When isolated=true, we use an empty string as the default indexName.
8661
8698
  // This is intentional: isolated indices do not require a real index name.
@@ -8753,7 +8790,7 @@
8753
8790
  addWidgets: function addWidgets(widgets) {
8754
8791
  var _this = this;
8755
8792
  if (!Array.isArray(widgets)) {
8756
- throw new Error(withUsage$v('The `addWidgets` method expects an array of widgets.'));
8793
+ throw new Error(withUsage$w('The `addWidgets` method expects an array of widgets.'));
8757
8794
  }
8758
8795
  var flatWidgets = widgets.reduce(function(acc, w) {
8759
8796
  return acc.concat(Array.isArray(w) ? w : [
@@ -8763,7 +8800,7 @@
8763
8800
  if (flatWidgets.some(function(widget) {
8764
8801
  return typeof widget.init !== 'function' && typeof widget.render !== 'function';
8765
8802
  })) {
8766
- throw new Error(withUsage$v('The widget definition expects a `render` and/or an `init` method.'));
8803
+ throw new Error(withUsage$w('The widget definition expects a `render` and/or an `init` method.'));
8767
8804
  }
8768
8805
  flatWidgets.forEach(function(widget) {
8769
8806
  widget.parent = _this;
@@ -8813,10 +8850,10 @@
8813
8850
  },
8814
8851
  updateWidget: function updateWidget(previousWidget, nextWidget) {
8815
8852
  if (typeof previousWidget.dispose !== 'function') {
8816
- throw new Error(withUsage$v('The widget definition expects a `dispose` method.'));
8853
+ throw new Error(withUsage$w('The widget definition expects a `dispose` method.'));
8817
8854
  }
8818
8855
  if (typeof nextWidget.init !== 'function' && typeof nextWidget.render !== 'function') {
8819
- throw new Error(withUsage$v('The widget definition expects a `render` and/or an `init` method.'));
8856
+ throw new Error(withUsage$w('The widget definition expects a `render` and/or an `init` method.'));
8820
8857
  }
8821
8858
  // The `uiState` as it is before the previous widget is detached, so that
8822
8859
  // the state it owns can be handed over to the next widget.
@@ -8900,7 +8937,7 @@
8900
8937
  removeWidgets: function removeWidgets(widgets) {
8901
8938
  var _this = this;
8902
8939
  if (!Array.isArray(widgets)) {
8903
- throw new Error(withUsage$v('The `removeWidgets` method expects an array of widgets.'));
8940
+ throw new Error(withUsage$w('The `removeWidgets` method expects an array of widgets.'));
8904
8941
  }
8905
8942
  var flatWidgets = widgets.reduce(function(acc, w) {
8906
8943
  return acc.concat(Array.isArray(w) ? w : [
@@ -8910,7 +8947,7 @@
8910
8947
  if (flatWidgets.some(function(widget) {
8911
8948
  return typeof widget.dispose !== 'function';
8912
8949
  })) {
8913
- throw new Error(withUsage$v('The widget definition expects a `dispose` method.'));
8950
+ throw new Error(withUsage$w('The widget definition expects a `dispose` method.'));
8914
8951
  }
8915
8952
  localWidgets = localWidgets.filter(function(widget) {
8916
8953
  return flatWidgets.indexOf(widget) === -1;
@@ -9418,7 +9455,7 @@
9418
9455
  });
9419
9456
  }
9420
9457
 
9421
- var version = '4.118.0';
9458
+ var version = '4.119.0';
9422
9459
 
9423
9460
  var ANONYMOUS_TOKEN_COOKIE_KEY = '_ALGOLIA';
9424
9461
  function getCookie(name) {
@@ -11280,7 +11317,7 @@
11280
11317
  };
11281
11318
  }
11282
11319
 
11283
- var withUsage$u = createDocumentationMessageGenerator({
11320
+ var withUsage$v = createDocumentationMessageGenerator({
11284
11321
  name: 'instantsearch'
11285
11322
  });
11286
11323
  function defaultCreateURL() {
@@ -11351,7 +11388,7 @@
11351
11388
  _this.setMaxListeners(100);
11352
11389
  var _options_indexName = options.indexName, indexName = _options_indexName === void 0 ? '' : _options_indexName, compositionID = options.compositionID, numberLocale = options.numberLocale, _options_initialUiState = options.initialUiState, initialUiState = _options_initialUiState === void 0 ? {} : _options_initialUiState, _options_routing = options.routing, routing = _options_routing === void 0 ? null : _options_routing, _options_insights = options.insights, insights = _options_insights === void 0 ? undefined : _options_insights, searchFunction = options.searchFunction, _options_stalledSearchDelay = options.stalledSearchDelay, stalledSearchDelay = _options_stalledSearchDelay === void 0 ? 200 : _options_stalledSearchDelay, _options_searchClient = options.searchClient, searchClient = _options_searchClient === void 0 ? null : _options_searchClient, _options_insightsClient = options.insightsClient, insightsClient = _options_insightsClient === void 0 ? null : _options_insightsClient, _options_onStateChange = options.onStateChange, onStateChange = _options_onStateChange === void 0 ? null : _options_onStateChange, _options_future1 = options.future, future = _options_future1 === void 0 ? _object_spread({}, INSTANTSEARCH_FUTURE_DEFAULTS, options.future || {}) : _options_future1;
11353
11390
  if (searchClient === null) {
11354
- throw new Error(withUsage$u('The `searchClient` option is required.'));
11391
+ throw new Error(withUsage$v('The `searchClient` option is required.'));
11355
11392
  }
11356
11393
  if (typeof searchClient.search !== 'function') {
11357
11394
  throw new Error("The `searchClient` must implement a `search` method.\n\nSee: https://www.algolia.com/doc/guides/building-search-ui/going-further/backend-search/in-depth/backend-instantsearch/js/");
@@ -11360,7 +11397,7 @@
11360
11397
  searchClient.addAlgoliaAgent("instantsearch.js (".concat(version, ")"));
11361
11398
  }
11362
11399
  if (insightsClient && typeof insightsClient !== 'function') {
11363
- throw new Error(withUsage$u('The `insightsClient` option should be a function.'));
11400
+ throw new Error(withUsage$v('The `insightsClient` option should be a function.'));
11364
11401
  }
11365
11402
  _this._initialOptions = options;
11366
11403
  _this.client = searchClient;
@@ -11509,12 +11546,12 @@
11509
11546
  * @param widgets The array of widgets to add to InstantSearch.
11510
11547
  */ function addWidgets(widgets) {
11511
11548
  if (!Array.isArray(widgets)) {
11512
- throw new Error(withUsage$u('The `addWidgets` method expects an array of widgets. Please use `addWidget`.'));
11549
+ throw new Error(withUsage$v('The `addWidgets` method expects an array of widgets. Please use `addWidget`.'));
11513
11550
  }
11514
11551
  if (this.compositionID && widgets.some(function(w) {
11515
11552
  return !Array.isArray(w) && isIndexWidget(w) && !w._isolated;
11516
11553
  })) {
11517
- throw new Error(withUsage$u('The `index` widget cannot be used with a composition-based InstantSearch implementation.'));
11554
+ throw new Error(withUsage$v('The `index` widget cannot be used with a composition-based InstantSearch implementation.'));
11518
11555
  }
11519
11556
  this.mainIndex.addWidgets(widgets);
11520
11557
  return this;
@@ -11543,7 +11580,7 @@
11543
11580
  * The widgets must implement a `dispose()` method to clear their states.
11544
11581
  */ function removeWidgets(widgets) {
11545
11582
  if (!Array.isArray(widgets)) {
11546
- throw new Error(withUsage$u('The `removeWidgets` method expects an array of widgets. Please use `removeWidget`.'));
11583
+ throw new Error(withUsage$v('The `removeWidgets` method expects an array of widgets. Please use `removeWidget`.'));
11547
11584
  }
11548
11585
  this.mainIndex.removeWidgets(widgets);
11549
11586
  return this;
@@ -11557,7 +11594,7 @@
11557
11594
  */ function start() {
11558
11595
  var _this = this;
11559
11596
  if (this.started) {
11560
- throw new Error(withUsage$u('The `start` method has already been called once.'));
11597
+ throw new Error(withUsage$v('The `start` method has already been called once.'));
11561
11598
  }
11562
11599
  // This Helper is used for the queries, we don't care about its state. The
11563
11600
  // states are managed at the `index` level. We use this Helper to create
@@ -11765,7 +11802,7 @@
11765
11802
  var _this = this;
11766
11803
  var callOnStateChange = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true;
11767
11804
  if (!this.mainHelper) {
11768
- throw new Error(withUsage$u('The `start` method needs to be called before `setUiState`.'));
11805
+ throw new Error(withUsage$v('The `start` method needs to be called before `setUiState`.'));
11769
11806
  }
11770
11807
  // We refresh the index UI state to update the local UI state that the
11771
11808
  // main index passes to the function form of `setUiState`.
@@ -11802,7 +11839,7 @@
11802
11839
  value: function createURL() {
11803
11840
  var nextState = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
11804
11841
  if (!this.started) {
11805
- throw new Error(withUsage$u('The `start` method needs to be called before `createURL`.'));
11842
+ throw new Error(withUsage$v('The `start` method needs to be called before `createURL`.'));
11806
11843
  }
11807
11844
  return this._createURL(nextState);
11808
11845
  }
@@ -11811,7 +11848,7 @@
11811
11848
  key: "refresh",
11812
11849
  value: function refresh() {
11813
11850
  if (!this.mainHelper) {
11814
- throw new Error(withUsage$u('The `start` method needs to be called before `refresh`.'));
11851
+ throw new Error(withUsage$v('The `start` method needs to be called before `refresh`.'));
11815
11852
  }
11816
11853
  this.mainHelper.clearCache().search();
11817
11854
  }
@@ -12343,13 +12380,13 @@
12343
12380
  }, children);
12344
12381
  }
12345
12382
 
12346
- var withUsage$t = createDocumentationMessageGenerator({
12383
+ var withUsage$u = createDocumentationMessageGenerator({
12347
12384
  name: 'autocomplete',
12348
12385
  connector: true
12349
12386
  });
12350
12387
  var connectAutocomplete = function connectAutocomplete(renderFn) {
12351
12388
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
12352
- checkRendering(renderFn, withUsage$t());
12389
+ checkRendering(renderFn, withUsage$u());
12353
12390
  return function(widgetParams) {
12354
12391
  var _ref = widgetParams || {}, _ref_escapeHTML = _ref.escapeHTML, escapeHTML = _ref_escapeHTML === void 0 ? true : _ref_escapeHTML, _ref_transformItems = _ref.transformItems, transformItems = _ref_transformItems === void 0 ? function(indices) {
12355
12392
  return indices;
@@ -12462,20 +12499,20 @@
12462
12499
  return useConnector(connectAutocomplete, props, additionalWidgetProperties);
12463
12500
  }
12464
12501
 
12465
- var withUsage$s = createDocumentationMessageGenerator({
12502
+ var withUsage$t = createDocumentationMessageGenerator({
12466
12503
  name: 'breadcrumb',
12467
12504
  connector: true
12468
12505
  });
12469
12506
  var connectBreadcrumb = function connectBreadcrumb(renderFn) {
12470
12507
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
12471
- checkRendering(renderFn, withUsage$s());
12508
+ checkRendering(renderFn, withUsage$t());
12472
12509
  var connectorState = {};
12473
12510
  return function(widgetParams) {
12474
12511
  var _ref = widgetParams || {}, attributes = _ref.attributes, _ref_separator = _ref.separator, separator = _ref_separator === void 0 ? ' > ' : _ref_separator, _ref_rootPath = _ref.rootPath, rootPath = _ref_rootPath === void 0 ? null : _ref_rootPath, _ref_transformItems = _ref.transformItems, transformItems = _ref_transformItems === void 0 ? function(items) {
12475
12512
  return items;
12476
12513
  } : _ref_transformItems;
12477
12514
  if (!attributes || !Array.isArray(attributes) || attributes.length === 0) {
12478
- throw new Error(withUsage$s('The `attributes` option expects an array of strings.'));
12515
+ throw new Error(withUsage$t('The `attributes` option expects an array of strings.'));
12479
12516
  }
12480
12517
  var _attributes = _sliced_to_array(attributes, 1), hierarchicalFacetName = _attributes[0];
12481
12518
  function getRefinedState(state, facetValue) {
@@ -12690,16 +12727,49 @@
12690
12727
  }
12691
12728
  };
12692
12729
  }
12730
+ /**
12731
+ * Turn-context key under which the compare connector attaches the shopper's
12732
+ * selection to its user message (`metadata.turnContext.selected_products`), as
12733
+ * a JSON-encoded array of records — the Agent Studio comparison contract.
12734
+ */ var SELECTED_PRODUCTS_KEY = 'selected_products';
12735
+ /**
12736
+ * The records a user message carries as a comparison selection, or `[]` when
12737
+ * it carries none. The payload is client-authored, so anything unexpected
12738
+ * (not JSON, not an array) is treated as "no records" rather than thrown.
12739
+ */ function selectedRecords(message) {
12740
+ var _message_metadata;
12741
+ if (message.role !== 'user') {
12742
+ return [];
12743
+ }
12744
+ var turnContext = (_message_metadata = message.metadata) === null || _message_metadata === void 0 ? void 0 : _message_metadata.turnContext;
12745
+ var raw = turnContext === null || turnContext === void 0 ? void 0 : turnContext[SELECTED_PRODUCTS_KEY];
12746
+ if (typeof raw !== 'string') {
12747
+ return [];
12748
+ }
12749
+ try {
12750
+ var parsed = JSON.parse(raw);
12751
+ return Array.isArray(parsed) ? parsed : [];
12752
+ } catch (unused) {
12753
+ return [];
12754
+ }
12755
+ }
12693
12756
  /**
12694
12757
  * Collects into `store` the records of every completed tool call in `messages`
12695
- * whose output holds `hits`. Matching on the output rather than on a tool name
12696
- * means any tool that fetches records contributes them by returning them.
12758
+ * whose output holds `hits`, plus the records a user message carries as a
12759
+ * comparison selection. Matching on the output rather than on a tool name
12760
+ * means any tool that fetches records contributes them by returning them; the
12761
+ * selection counts because the compare flow already hands the agent the very
12762
+ * records it is asked to compare, so the table can be built from them without
12763
+ * a search round-trip.
12697
12764
  *
12698
12765
  * Idempotent, so it can re-run over the whole conversation on every update:
12699
12766
  * streaming deltas and a conversation restored from storage are one code path.
12767
+ * Later messages win per `objectID`, so a record the agent re-fetched after the
12768
+ * selection replaces the selected copy.
12700
12769
  */ function collectChatRecords(messages) {
12701
12770
  var store = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : createChatRecordsStore();
12702
12771
  messages === null || messages === void 0 ? void 0 : messages.forEach(function(message) {
12772
+ store.merge(selectedRecords(message));
12703
12773
  message.parts.forEach(function(part) {
12704
12774
  var _part_output;
12705
12775
  if (!isPartTool(part) || part.state !== 'output-available') {
@@ -13426,6 +13496,15 @@
13426
13496
  this.conversationId = this.generateId();
13427
13497
  }
13428
13498
  },
13499
+ {
13500
+ /**
13501
+ * Continues a conversation started elsewhere (e.g. by a Result Card) under
13502
+ * its server-side id, so the next request carries that thread's context.
13503
+ */ key: "setConversationId",
13504
+ value: function setConversationId(id) {
13505
+ this.conversationId = id;
13506
+ }
13507
+ },
13429
13508
  {
13430
13509
  key: "messages",
13431
13510
  get: function get() {
@@ -14712,7 +14791,154 @@
14712
14791
  return (chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.status) === 'submitted' || (chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.status) === 'streaming';
14713
14792
  }
14714
14793
 
14794
+ function flat(arr) {
14795
+ return arr.reduce(function(acc, array) {
14796
+ return acc.concat(array);
14797
+ }, []);
14798
+ }
14799
+
14800
+ /*
14801
+ * The two directions are not inverses: `getAgentFilters` folds numeric
14802
+ * refinements into the facet groups and writes excludes as `attr:-value`,
14803
+ * whereas `applyAgentFilters` reads numeric refinements from `numericFilters`
14804
+ * only and refines on `-value` literally.
14805
+ */ /**
14806
+ * The active refinements as `facetFilters`-style groups: one group per
14807
+ * conjunctive/numeric refinement, disjunctive values of the same attribute
14808
+ * grouped together.
14809
+ */ function getAgentFilters(results) {
14810
+ var state = results._state;
14811
+ if (!state) {
14812
+ return undefined;
14813
+ }
14814
+ var groups = [];
14815
+ var disjunctiveGroups = {};
14816
+ getRefinements(results, state).forEach(function(refinement) {
14817
+ if (refinement.type === 'numeric') {
14818
+ groups.push([
14819
+ "".concat(refinement.attribute).concat(refinement.operator).concat(refinement.numericValue)
14820
+ ]);
14821
+ return;
14822
+ }
14823
+ var value = refinement.type === 'exclude' ? "".concat(refinement.attribute, ":-").concat(refinement.name) : "".concat(refinement.attribute, ":").concat(refinement.name);
14824
+ if (refinement.type === 'disjunctive') {
14825
+ var group = disjunctiveGroups[refinement.attribute];
14826
+ if (group) {
14827
+ group.push(value);
14828
+ } else {
14829
+ var newGroup = [
14830
+ value
14831
+ ];
14832
+ disjunctiveGroups[refinement.attribute] = newGroup;
14833
+ groups.push(newGroup);
14834
+ }
14835
+ return;
14836
+ }
14837
+ groups.push([
14838
+ value
14839
+ ]);
14840
+ });
14841
+ return groups.length > 0 ? groups : undefined;
14842
+ }
14843
+ function getAttributesToClear$1(param) {
14844
+ var results = param.results, helper = param.helper;
14845
+ return uniq(getRefinements(results, helper.state, true).map(function(refinement) {
14846
+ return refinement.attribute;
14847
+ }));
14848
+ }
14849
+ /**
14850
+ * One Algolia `numericFilters` entry: `'price <= 1500'`. The operators are
14851
+ * exactly the set `helper.addNumericRefinement` accepts, and exactly the set
14852
+ * the Algolia MCP Server emits.
14853
+ */ var NUMERIC_FILTER = /^(.+?)\s*(<=|>=|!=|=|<|>)\s*(-?\d+(?:\.\d+)?)$/;
14854
+ /**
14855
+ * Replaces the helper's refinements and query with the ones a search tool call
14856
+ * searched with, then searches.
14857
+ */ function applyAgentFilters(params, helper) {
14858
+ // clear all filters first
14859
+ var attributesToClear = getAttributesToClear$1({
14860
+ results: helper.lastResults,
14861
+ helper: helper
14862
+ });
14863
+ helper.setState(clearRefinements({
14864
+ helper: helper,
14865
+ attributesToClear: attributesToClear
14866
+ }));
14867
+ if (params.facetFilters) {
14868
+ var refinements = flat(params.facetFilters).reduce(function(acc, filter) {
14869
+ var separatorIndex = filter.indexOf(':');
14870
+ if (separatorIndex > 0) {
14871
+ acc.push({
14872
+ attribute: filter.slice(0, separatorIndex),
14873
+ value: filter.slice(separatorIndex + 1)
14874
+ });
14875
+ }
14876
+ return acc;
14877
+ }, []);
14878
+ var hierarchicalRefinements = new Map();
14879
+ refinements.forEach(function(param) {
14880
+ var attribute = param.attribute, value = param.value;
14881
+ var hierarchicalFacet = helper.state.hierarchicalFacets.find(function(facet) {
14882
+ return facet.name === attribute || facet.attributes.includes(attribute);
14883
+ });
14884
+ if (hierarchicalFacet) {
14885
+ var currentValue = hierarchicalRefinements.get(hierarchicalFacet.name);
14886
+ if (currentValue === undefined || value.length > currentValue.length) {
14887
+ hierarchicalRefinements.set(hierarchicalFacet.name, value);
14888
+ }
14889
+ return;
14890
+ }
14891
+ if (!helper.state.isConjunctiveFacet(attribute) && !helper.state.isDisjunctiveFacet(attribute)) {
14892
+ helper.setState(helper.state.addDisjunctiveFacet(attribute));
14893
+ }
14894
+ helper.toggleFacetRefinement(attribute, value);
14895
+ });
14896
+ hierarchicalRefinements.forEach(function(value, name) {
14897
+ helper.toggleFacetRefinement(name, value);
14898
+ });
14899
+ }
14900
+ if (params.numericFilters) {
14901
+ params.numericFilters.forEach(function(filter) {
14902
+ var match = filter.match(NUMERIC_FILTER);
14903
+ if (!match) {
14904
+ return;
14905
+ }
14906
+ var _match = _sliced_to_array(match, 4), attribute = _match[1], operator = _match[2], value = _match[3];
14907
+ helper.addNumericRefinement(attribute, operator, Number(value));
14908
+ });
14909
+ }
14910
+ if (params.query) {
14911
+ helper.setQuery(params.query);
14912
+ }
14913
+ helper.search();
14914
+ return helper.state;
14915
+ }
14916
+
14917
+ /**
14918
+ * Strips InstantSearch metadata, which is `_`-prefixed (`_highlightResult`,
14919
+ * `_rankingInfo`, `__position`, …), so only record attributes reach the agent.
14920
+ */ function stripInternalHitMetadata(hit) {
14921
+ var clean = {};
14922
+ Object.keys(hit).forEach(function(key) {
14923
+ if (!key.startsWith('_')) {
14924
+ clean[key] = hit[key];
14925
+ }
14926
+ });
14927
+ return clean;
14928
+ }
14929
+
14715
14930
  var SearchIndexToolType = 'algolia_search_index';
14931
+ var RecommendToolType = 'algolia_recommend';
14932
+ var MemorizeToolType = 'algolia_memorize';
14933
+ var MemorySearchToolType = 'algolia_memory_search';
14934
+ var PonderToolType = 'algolia_ponder';
14935
+ var CompareProductsToolType = 'algolia_compare_products';
14936
+ var GroupedResultsToolType = 'algolia_grouped_results';
14937
+ /**
14938
+ * @deprecated The tool is now `algolia_grouped_results`
14939
+ * ({@link GroupedResultsToolType}). This name is still emitted by agents
14940
+ * configured before the rename, and renders the same UI.
14941
+ */ var DisplayResultsToolType = 'algolia_display_results';
14716
14942
  /**
14717
14943
  * Whether `toolName` is the search tool as the Algolia MCP Server exposes it:
14718
14944
  * one tool per index, named after the index it searches
@@ -14721,12 +14947,6 @@
14721
14947
  return toolName.startsWith("".concat(SearchIndexToolType, "_"));
14722
14948
  };
14723
14949
 
14724
- function flat(arr) {
14725
- return arr.reduce(function(acc, array) {
14726
- return acc.concat(array);
14727
- }, []);
14728
- }
14729
-
14730
14950
  /**
14731
14951
  * Reads a human-readable message from a failed HTTP response body when the
14732
14952
  * server returns JSON such as `{ "message": "..." }` (the shared
@@ -14917,7 +15137,7 @@
14917
15137
  return DefaultChatTransport;
14918
15138
  }(HttpChatTransport);
14919
15139
 
14920
- var withUsage$r = createDocumentationMessageGenerator({
15140
+ var withUsage$s = createDocumentationMessageGenerator({
14921
15141
  name: 'chat',
14922
15142
  connector: true
14923
15143
  });
@@ -14976,79 +15196,9 @@
14976
15196
  // Storage availability must not block the visible state change.
14977
15197
  }
14978
15198
  }
14979
- function getAttributesToClear$1(param) {
14980
- var results = param.results, helper = param.helper;
14981
- return uniq(getRefinements(results, helper.state, true).map(function(refinement) {
14982
- return refinement.attribute;
14983
- }));
14984
- }
14985
- /**
14986
- * One Algolia `numericFilters` entry: `'price <= 1500'`. The operators are
14987
- * exactly the set `helper.addNumericRefinement` accepts, and exactly the set
14988
- * the Algolia MCP Server emits.
14989
- */ var NUMERIC_FILTER = /^(.+?)\s*(<=|>=|!=|=|<|>)\s*(-?\d+(?:\.\d+)?)$/;
14990
- function updateStateFromSearchToolInput(params, helper) {
14991
- // clear all filters first
14992
- var attributesToClear = getAttributesToClear$1({
14993
- results: helper.lastResults,
14994
- helper: helper
14995
- });
14996
- helper.setState(clearRefinements({
14997
- helper: helper,
14998
- attributesToClear: attributesToClear
14999
- }));
15000
- if (params.facetFilters) {
15001
- var refinements = flat(params.facetFilters).reduce(function(acc, filter) {
15002
- var separatorIndex = filter.indexOf(':');
15003
- if (separatorIndex > 0) {
15004
- acc.push({
15005
- attribute: filter.slice(0, separatorIndex),
15006
- value: filter.slice(separatorIndex + 1)
15007
- });
15008
- }
15009
- return acc;
15010
- }, []);
15011
- var hierarchicalRefinements = new Map();
15012
- refinements.forEach(function(param) {
15013
- var attribute = param.attribute, value = param.value;
15014
- var hierarchicalFacet = helper.state.hierarchicalFacets.find(function(facet) {
15015
- return facet.name === attribute || facet.attributes.includes(attribute);
15016
- });
15017
- if (hierarchicalFacet) {
15018
- var currentValue = hierarchicalRefinements.get(hierarchicalFacet.name);
15019
- if (currentValue === undefined || value.length > currentValue.length) {
15020
- hierarchicalRefinements.set(hierarchicalFacet.name, value);
15021
- }
15022
- return;
15023
- }
15024
- if (!helper.state.isConjunctiveFacet(attribute) && !helper.state.isDisjunctiveFacet(attribute)) {
15025
- helper.setState(helper.state.addDisjunctiveFacet(attribute));
15026
- }
15027
- helper.toggleFacetRefinement(attribute, value);
15028
- });
15029
- hierarchicalRefinements.forEach(function(value, name) {
15030
- helper.toggleFacetRefinement(name, value);
15031
- });
15032
- }
15033
- if (params.numericFilters) {
15034
- params.numericFilters.forEach(function(filter) {
15035
- var match = filter.match(NUMERIC_FILTER);
15036
- if (!match) {
15037
- return;
15038
- }
15039
- var _match = _sliced_to_array(match, 4), attribute = _match[1], operator = _match[2], value = _match[3];
15040
- helper.addNumericRefinement(attribute, operator, Number(value));
15041
- });
15042
- }
15043
- if (params.query) {
15044
- helper.setQuery(params.query);
15045
- }
15046
- helper.search();
15047
- return helper.state;
15048
- }
15049
15199
  var connectChat = function connectChat(renderFn) {
15050
15200
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
15051
- checkRendering(renderFn, withUsage$r());
15201
+ checkRendering(renderFn, withUsage$s());
15052
15202
  return function(widgetParams) {
15053
15203
  var _ref = widgetParams || {}, _ref_resume = _ref.resume, resume = _ref_resume === void 0 ? false : _ref_resume, tmp = _ref.tools, tools_ = tmp === void 0 ? {} : tmp, _ref_type = _ref.type, type = _ref_type === void 0 ? 'chat' : _ref_type, persistence = _ref.persistence, context = _ref.context, initialUserMessage = _ref.initialUserMessage, initialMessages = _ref.initialMessages, _ref_disableTriggerValidation = _ref.disableTriggerValidation, disableTriggerValidation = _ref_disableTriggerValidation === void 0 ? false : _ref_disableTriggerValidation, _ref_sendAutomaticallyWhen = _ref.sendAutomaticallyWhen, sendAutomaticallyWhen = _ref_sendAutomaticallyWhen === void 0 ? lastAssistantMessageIsCompleteWithToolCalls : _ref_sendAutomaticallyWhen, _ref_requiresSearch = _ref.requiresSearch, requiresSearch = _ref_requiresSearch === void 0 ? true : _ref_requiresSearch, options = _object_without_properties(_ref, [
15054
15204
  "resume",
@@ -15133,6 +15283,35 @@
15133
15283
  setMessages([]);
15134
15284
  _chatInstance.clearError();
15135
15285
  };
15286
+ var adoptConversation = function adoptConversation(param) {
15287
+ var id = param.id, messages = param.messages, source = param.source;
15288
+ var status = _chatInstance.status;
15289
+ if (status === 'submitted' || status === 'streaming') {
15290
+ return false;
15291
+ }
15292
+ var tagged = messages.map(function(message) {
15293
+ return _object_spread_props(_object_spread({}, message), {
15294
+ metadata: _object_spread_props(_object_spread({}, message.metadata), {
15295
+ source: source
15296
+ })
15297
+ });
15298
+ });
15299
+ if (_chatInstance.messages.length === 0) {
15300
+ _chatInstance.setConversationId(id);
15301
+ setMessages(tagged);
15302
+ } else {
15303
+ // The handing-off widget may still be mounted and hand off again
15304
+ // (e.g. a second follow-up suggestion): only new turns are appended.
15305
+ var existingIds = new Set(_chatInstance.messages.map(function(message) {
15306
+ return message.id;
15307
+ }));
15308
+ setMessages(_to_consumable_array(_chatInstance.messages).concat(_to_consumable_array(tagged.filter(function(message) {
15309
+ return !existingIds.has(message.id);
15310
+ }))));
15311
+ }
15312
+ _chatInstance.clearError();
15313
+ return true;
15314
+ };
15136
15315
  var validateEntryPoints = function validateEntryPoints(instantSearchInstance) {
15137
15316
  if (disableTriggerValidation || hasValidatedEntryPoints) {
15138
15317
  return;
@@ -15162,13 +15341,29 @@
15162
15341
  var transport;
15163
15342
  var client = instantSearchInstance.client;
15164
15343
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
15165
- // Filter out custom data parts (like data-suggestions) that the backend doesn't accept
15166
- var filterDataParts = function filterDataParts(messages) {
15344
+ // Shapes the local transcript into what the backend accepts: drops the
15345
+ // custom data parts (like data-suggestions) it does not take, and puts a
15346
+ // prompt-shortcut sentinel back on the user message that displays its
15347
+ // resolved text (see `applyPromptShortcutEcho`). The wire always carries
15348
+ // the sentinel, so a regenerate or a follow-up turn still resolves the
15349
+ // configured prompt exactly like the first send did.
15350
+ var prepareOutgoingMessages = function prepareOutgoingMessages(messages) {
15167
15351
  return messages.map(function(message) {
15352
+ var _message_metadata;
15168
15353
  var _message_parts;
15169
- return _object_spread_props(_object_spread({}, message), {
15354
+ var _ref = (_message_metadata = message.metadata) !== null && _message_metadata !== void 0 ? _message_metadata : {}, promptShortcut = _ref.promptShortcut, metadata = _object_without_properties(_ref, [
15355
+ "promptShortcut"
15356
+ ]);
15357
+ var sentinel = typeof (promptShortcut === null || promptShortcut === void 0 ? void 0 : promptShortcut.sentinel) === 'string' ? promptShortcut.sentinel : undefined;
15358
+ return _object_spread_props(_object_spread({}, message, sentinel ? {
15359
+ metadata: Object.keys(metadata).length ? metadata : undefined
15360
+ } : {}), {
15170
15361
  parts: (_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.filter(function(part) {
15171
15362
  return !('type' in part && part.type.startsWith('data-'));
15363
+ }).map(function(part) {
15364
+ return sentinel && part.type === 'text' ? _object_spread_props(_object_spread({}, part), {
15365
+ text: sentinel
15366
+ }) : part;
15172
15367
  })
15173
15368
  });
15174
15369
  });
@@ -15189,11 +15384,11 @@
15189
15384
  messages: params.messages
15190
15385
  }, params.body)
15191
15386
  };
15192
- // Then filter out data-* parts
15387
+ // Then shape the messages for the wire
15193
15388
  var applyFilter = function applyFilter(prepared) {
15194
15389
  return _object_spread_props(_object_spread({}, prepared), {
15195
15390
  body: _object_spread_props(_object_spread({}, prepared.body), {
15196
- messages: filterDataParts(prepared.body.messages)
15391
+ messages: prepareOutgoingMessages(prepared.body.messages)
15197
15392
  })
15198
15393
  });
15199
15394
  };
@@ -15205,10 +15400,12 @@
15205
15400
  }
15206
15401
  }));
15207
15402
  }
15208
- if ('agentId' in options && options.agentId) {
15403
+ // A custom `transport` wins: `agentId` alongside it only serves feedback
15404
+ // (and, for the result card, the Rule context).
15405
+ if (!transport && 'agentId' in options && options.agentId) {
15209
15406
  var _options_requestOptions, _options_requestOptions1;
15210
15407
  if (!appId || !apiKey) {
15211
- throw new Error(withUsage$r('Could not extract Algolia credentials from the search client.'));
15408
+ throw new Error(withUsage$s('Could not extract Algolia credentials from the search client.'));
15212
15409
  }
15213
15410
  var createApi = function createApi() {
15214
15411
  var bypassCache = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
@@ -15240,17 +15437,74 @@
15240
15437
  body: {
15241
15438
  id: id,
15242
15439
  messageId: messageId,
15243
- messages: filterDataParts(messages)
15440
+ messages: prepareOutgoingMessages(messages)
15244
15441
  }
15245
15442
  };
15246
15443
  }
15247
15444
  });
15248
15445
  }
15249
15446
  if (!transport) {
15250
- throw new Error(withUsage$r('You need to provide either an `agentId` or a `transport`.'));
15251
- }
15447
+ throw new Error(withUsage$s('You need to provide either an `agentId` or a `transport`.'));
15448
+ }
15449
+ // A prompt-shortcut turn (the `compare` widget's
15450
+ // `__ALGOLIA_COMPARISON_<id>__`) sends a sentinel as the user message,
15451
+ // which Agent Studio resolves into the natural-language text it
15452
+ // persists on that row. The local transcript still holds the sentinel,
15453
+ // so the server echoes the persisted text as a transient
15454
+ // `data-prompt-shortcut` part right after `start`. Swapping it into the
15455
+ // message that sent it keeps the live bubble, the session-restored
15456
+ // transcript and a history reload in agreement, without the client
15457
+ // knowing how shortcuts are configured or rendered. The sentinel moves
15458
+ // to the message metadata so `prepareOutgoingMessages` can put it back
15459
+ // on the wire: the backend resolves the shortcut from the token, not
15460
+ // from its display text.
15461
+ var applyPromptShortcutEcho = function applyPromptShortcutEcho(dataPart) {
15462
+ if (dataPart.type !== 'data-prompt-shortcut') return;
15463
+ var data = dataPart.data;
15464
+ var sentinel = data === null || data === void 0 ? void 0 : data.sentinel;
15465
+ var displayText = data === null || data === void 0 ? void 0 : data.displayText;
15466
+ if (typeof sentinel !== 'string' || typeof displayText !== 'string') {
15467
+ return;
15468
+ }
15469
+ setMessages(function(messages) {
15470
+ // Only the message being answered sent this sentinel: earlier turns
15471
+ // were rewritten by their own echo, and the same shortcut resolves
15472
+ // to a different text per selection.
15473
+ for(var index = messages.length - 1; index >= 0; index--){
15474
+ var _message_parts;
15475
+ var message = messages[index];
15476
+ if (message.role !== 'user' || ((_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.length) !== 1) {
15477
+ continue;
15478
+ }
15479
+ var part = message.parts[0];
15480
+ if (part.type !== 'text' || part.text.trim() !== sentinel) {
15481
+ continue;
15482
+ }
15483
+ var updated = _to_consumable_array(messages);
15484
+ updated[index] = _object_spread_props(_object_spread({}, message), {
15485
+ parts: [
15486
+ _object_spread_props(_object_spread({}, part), {
15487
+ text: displayText
15488
+ })
15489
+ ],
15490
+ metadata: _object_spread_props(_object_spread({}, message.metadata), {
15491
+ promptShortcut: {
15492
+ sentinel: sentinel
15493
+ }
15494
+ })
15495
+ });
15496
+ return updated;
15497
+ }
15498
+ return messages;
15499
+ });
15500
+ };
15252
15501
  var canRepairRestoredPendingToolParts = !resume;
15253
15502
  var chat = new Chat(_object_spread_props(_object_spread({}, options), {
15503
+ onData: function onData(dataPart) {
15504
+ var _options_onData;
15505
+ applyPromptShortcutEcho(dataPart);
15506
+ (_options_onData = options.onData) === null || _options_onData === void 0 ? void 0 : _options_onData.call(options, dataPart);
15507
+ },
15254
15508
  persistence: normalizedPersistence.messages,
15255
15509
  sendAutomaticallyWhen: sendAutomaticallyWhen,
15256
15510
  transport: transport,
@@ -15437,7 +15691,7 @@
15437
15691
  if (agentId && feedback) {
15438
15692
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(initOptions.instantSearchInstance.client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
15439
15693
  if (!appId || !apiKey) {
15440
- throw new Error(withUsage$r('Could not extract Algolia credentials from the search client.'));
15694
+ throw new Error(withUsage$s('Could not extract Algolia credentials from the search client.'));
15441
15695
  }
15442
15696
  feedbackAbortController = new AbortController();
15443
15697
  _sendChatMessageFeedback = function _sendChatMessageFeedback(messageId, vote) {
@@ -15539,7 +15793,7 @@
15539
15793
  });
15540
15794
  }
15541
15795
  function applyFilters(params) {
15542
- return updateStateFromSearchToolInput(params, helper);
15796
+ return applyAgentFilters(params, helper);
15543
15797
  }
15544
15798
  // A restored or server-rendered conversation never emitted the messages
15545
15799
  // callback above; collecting is idempotent, so covering it here is free.
@@ -15574,11 +15828,18 @@
15574
15828
  }
15575
15829
  // Resolve once per send; let the server validate the payload and
15576
15830
  // surface any contract violations.
15577
- var turnContext = typeof context === 'function' ? context() : context;
15831
+ var widgetContext = typeof context === 'function' ? context() : context;
15832
+ var metadata = message.metadata;
15833
+ // An entry point (compare bar, prompt suggestions, `openChat`) may
15834
+ // have attached its own turn context to the message — e.g. the
15835
+ // selected products of a comparison. Add the ambient widget context
15836
+ // to it rather than replacing it; on a key both provide, the entry
15837
+ // point knows the turn better.
15838
+ var messageTurnContext = (metadata === null || metadata === void 0 ? void 0 : metadata.turnContext) && _type_of(metadata.turnContext) === 'object' && !Array.isArray(metadata.turnContext) ? metadata.turnContext : undefined;
15578
15839
  return (_chatInstance1 = _chatInstance).sendMessage.apply(_chatInstance1, [
15579
15840
  _object_spread_props(_object_spread({}, message), {
15580
- metadata: _object_spread_props(_object_spread({}, message.metadata), {
15581
- turnContext: turnContext
15841
+ metadata: _object_spread_props(_object_spread({}, metadata), {
15842
+ turnContext: _object_spread({}, widgetContext, messageTurnContext)
15582
15843
  })
15583
15844
  })
15584
15845
  ].concat(_to_consumable_array(rest)));
@@ -15601,6 +15862,7 @@
15601
15862
  setMessages: setMessages,
15602
15863
  suggestions: getSuggestionsFromMessages(_chatInstance.messages),
15603
15864
  clearMessages: clearMessages,
15865
+ adoptConversation: adoptConversation,
15604
15866
  tools: toolsWithAddToolResult,
15605
15867
  records: records,
15606
15868
  sendChatMessageFeedback: _sendChatMessageFeedback,
@@ -16073,20 +16335,20 @@
16073
16335
  };
16074
16336
  }
16075
16337
 
16076
- var withUsage$q = createDocumentationMessageGenerator({
16338
+ var withUsage$r = createDocumentationMessageGenerator({
16077
16339
  name: 'tasks',
16078
16340
  connector: true
16079
16341
  });
16080
16342
  var connectTasks = function connectTasks(renderFn) {
16081
16343
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
16082
- checkRendering(renderFn, withUsage$q());
16344
+ checkRendering(renderFn, withUsage$r());
16083
16345
  return function(widgetParams) {
16084
16346
  var agentId = widgetParams.agentId, transport = widgetParams.transport, task = widgetParams.task, kind = widgetParams.kind, _widgetParams_stream = widgetParams.stream, stream = _widgetParams_stream === void 0 ? true : _widgetParams_stream;
16085
16347
  if (!agentId && !transport) {
16086
- throw new Error(withUsage$q('The `agentId` option is required unless a custom `transport` is provided.'));
16348
+ throw new Error(withUsage$r('The `agentId` option is required unless a custom `transport` is provided.'));
16087
16349
  }
16088
16350
  if (!task && !kind) {
16089
- throw new Error(withUsage$q('Either the `task` or `kind` option is required.'));
16351
+ throw new Error(withUsage$r('Either the `task` or `kind` option is required.'));
16090
16352
  }
16091
16353
  var runner;
16092
16354
  var output;
@@ -16160,7 +16422,7 @@
16160
16422
  if (agentId) {
16161
16423
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(instantSearchInstance.client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
16162
16424
  if (!appId || !apiKey) {
16163
- throw new Error(withUsage$q('Could not extract Algolia credentials from the search client.'));
16425
+ throw new Error(withUsage$r('Could not extract Algolia credentials from the search client.'));
16164
16426
  }
16165
16427
  var taskTransport = createTaskTransport({
16166
16428
  transport: transport,
@@ -16207,14 +16469,14 @@
16207
16469
  };
16208
16470
  };
16209
16471
 
16210
- var withUsage$p = createDocumentationMessageGenerator({
16472
+ var withUsage$q = createDocumentationMessageGenerator({
16211
16473
  name: 'prompt-suggestions',
16212
16474
  connector: true
16213
16475
  });
16214
16476
  var RENDER_STATE_KEY = 'promptSuggestions';
16215
- var CHAT_RENDER_STATE_KEY = 'chat';
16477
+ var CHAT_RENDER_STATE_KEY$1 = 'chat';
16216
16478
  var PROMPT_SUGGESTIONS_TASK_KIND = 'prompt_suggestions';
16217
- var DEBOUNCE_MS = 300;
16479
+ var DEBOUNCE_MS$1 = 300;
16218
16480
  function parseSuggestions(data) {
16219
16481
  var suggestions = data === null || data === void 0 ? void 0 : data.suggestions;
16220
16482
  if (!Array.isArray(suggestions)) {
@@ -16254,63 +16516,18 @@
16254
16516
  return null;
16255
16517
  }
16256
16518
  }
16257
- function stripInternalHitMetadata(hit) {
16258
- var clean = {};
16259
- Object.keys(hit).forEach(function(key) {
16260
- // Strip internal metadata, which is `_`-prefixed
16261
- // (`_highlightResult`, `_rankingInfo`, `__position`, …).
16262
- if (!key.startsWith('_')) {
16263
- clean[key] = hit[key];
16264
- }
16265
- });
16266
- return clean;
16267
- }
16268
16519
  var DEFAULT_TRANSFORM_HITS = function DEFAULT_TRANSFORM_HITS(hits) {
16269
16520
  return hits.slice(0, 5).map(stripInternalHitMetadata);
16270
16521
  };
16271
- function buildFilters(results) {
16272
- var state = results._state;
16273
- if (!state) {
16274
- return undefined;
16275
- }
16276
- var groups = [];
16277
- var disjunctiveGroups = {};
16278
- getRefinements(results, state).forEach(function(refinement) {
16279
- if (refinement.type === 'numeric') {
16280
- groups.push([
16281
- "".concat(refinement.attribute).concat(refinement.operator).concat(refinement.numericValue)
16282
- ]);
16283
- return;
16284
- }
16285
- var value = refinement.type === 'exclude' ? "".concat(refinement.attribute, ":-").concat(refinement.name) : "".concat(refinement.attribute, ":").concat(refinement.name);
16286
- if (refinement.type === 'disjunctive') {
16287
- var group = disjunctiveGroups[refinement.attribute];
16288
- if (group) {
16289
- group.push(value);
16290
- } else {
16291
- var newGroup = [
16292
- value
16293
- ];
16294
- disjunctiveGroups[refinement.attribute] = newGroup;
16295
- groups.push(newGroup);
16296
- }
16297
- return;
16298
- }
16299
- groups.push([
16300
- value
16301
- ]);
16302
- });
16303
- return groups.length > 0 ? groups : undefined;
16304
- }
16305
16522
  var connectPromptSuggestions = function connectPromptSuggestions(renderFn) {
16306
16523
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
16307
- checkRendering(renderFn, withUsage$p());
16524
+ checkRendering(renderFn, withUsage$q());
16308
16525
  return function(widgetParams) {
16309
16526
  var agentId = widgetParams.agentId, configurationId = widgetParams.configurationId, _widgetParams_transformHits = widgetParams.transformHits, transformHits = _widgetParams_transformHits === void 0 ? DEFAULT_TRANSFORM_HITS : _widgetParams_transformHits, context = widgetParams.context, _widgetParams_transformItems = widgetParams.transformItems, transformItems = _widgetParams_transformItems === void 0 ? function(items) {
16310
16527
  return items;
16311
16528
  } : _widgetParams_transformItems, transport = widgetParams.transport;
16312
16529
  if (!agentId && !transport) {
16313
- throw new Error(withUsage$p('The `agentId` option is required unless a custom `transport` is provided.'));
16530
+ throw new Error(withUsage$q('The `agentId` option is required unless a custom `transport` is provided.'));
16314
16531
  }
16315
16532
  var tasksState;
16316
16533
  var suggestions = [];
@@ -16340,12 +16557,12 @@
16340
16557
  // out to send nothing, it has to reconcile the dropped state instead.
16341
16558
  var droppedInnerRender = false;
16342
16559
  var getStateSignature = function getStateSignature(results) {
16343
- var _buildFilters;
16560
+ var _getAgentFilters;
16344
16561
  if (results.queryID) {
16345
16562
  return results.queryID;
16346
16563
  }
16347
16564
  var query = results.query || '';
16348
- var filters = JSON.stringify((_buildFilters = buildFilters(results)) !== null && _buildFilters !== void 0 ? _buildFilters : []);
16565
+ var filters = JSON.stringify((_getAgentFilters = getAgentFilters(results)) !== null && _getAgentFilters !== void 0 ? _getAgentFilters : []);
16349
16566
  var hitIds = (results.hits || []).map(function(hit) {
16350
16567
  return hit.objectID;
16351
16568
  }).join(',');
@@ -16358,7 +16575,7 @@
16358
16575
  if (!indexId || !((_instantSearchInstance_renderState = instantSearchInstance.renderState) === null || _instantSearchInstance_renderState === void 0 ? void 0 : _instantSearchInstance_renderState[indexId])) {
16359
16576
  return undefined;
16360
16577
  }
16361
- return instantSearchInstance.renderState[indexId][CHAT_RENDER_STATE_KEY];
16578
+ return instantSearchInstance.renderState[indexId][CHAT_RENDER_STATE_KEY$1];
16362
16579
  };
16363
16580
  var sendToChat = function sendToChat(renderOptions) {
16364
16581
  return function(prompt) {
@@ -16391,7 +16608,7 @@
16391
16608
  if (!results) {
16392
16609
  return undefined;
16393
16610
  }
16394
- var filters = buildFilters(results);
16611
+ var filters = getAgentFilters(results);
16395
16612
  return _object_spread_props(_object_spread({
16396
16613
  query: results.query || ''
16397
16614
  }, filters ? {
@@ -16590,7 +16807,7 @@
16590
16807
  stream: true
16591
16808
  };
16592
16809
  } else {
16593
- throw new Error(withUsage$p('The `agentId` option is required unless a custom `transport` is provided.'));
16810
+ throw new Error(withUsage$q('The `agentId` option is required unless a custom `transport` is provided.'));
16594
16811
  }
16595
16812
  var tasksWidget = connectTasks(handleInnerRender, noop)(tasksParams);
16596
16813
  return {
@@ -16647,7 +16864,7 @@
16647
16864
  if (latestResults && latestRenderOptions) {
16648
16865
  fetchAndRender(latestResults, latestRenderOptions);
16649
16866
  }
16650
- }, DEBOUNCE_MS);
16867
+ }, DEBOUNCE_MS$1);
16651
16868
  }
16652
16869
  renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(renderOptions)), {
16653
16870
  instantSearchInstance: instantSearchInstance
@@ -16674,6 +16891,363 @@
16674
16891
  return useConnector(connectPromptSuggestions, props, additionalWidgetProperties);
16675
16892
  }
16676
16893
 
16894
+ var withUsage$p = createDocumentationMessageGenerator({
16895
+ name: 'result-card',
16896
+ connector: true
16897
+ });
16898
+ /** `x-algolia-referer` sent with the card's completion requests. */ var RESULT_CARD_REFERER = 'result-card';
16899
+ /** `metadata.source` set on the messages handed off to the chat. */ var RESULT_CARD_SOURCE = 'resultCard';
16900
+ var RULE_CONTEXT_PREFIX = 'agent-studio-result-card-';
16901
+ var CHAT_RENDER_STATE_KEY = 'chat';
16902
+ var DEBOUNCE_MS = 700;
16903
+ var HITS_SAMPLE_SIZE = 5;
16904
+ var MIN_QUERY_WORDS = 2;
16905
+ /**
16906
+ * The Rule context that ties the Rules Agent Studio manages for this agent to
16907
+ * the search. The Dashboard derives the same value, so the two must agree.
16908
+ */ function getResultCardRuleContext(agentId) {
16909
+ return "".concat(RULE_CONTEXT_PREFIX).concat(agentId).replace(/[^A-Za-z0-9_-]/g, '_');
16910
+ }
16911
+ function countWords(query) {
16912
+ return query.trim().split(/\s+/).filter(Boolean).length;
16913
+ }
16914
+ function isActivated(results) {
16915
+ var _results_renderingContent_widgets_resultCard, _results_renderingContent_widgets, _results_renderingContent;
16916
+ return ((_results_renderingContent = results.renderingContent) === null || _results_renderingContent === void 0 ? void 0 : (_results_renderingContent_widgets = _results_renderingContent.widgets) === null || _results_renderingContent_widgets === void 0 ? void 0 : (_results_renderingContent_widgets_resultCard = _results_renderingContent_widgets.resultCard) === null || _results_renderingContent_widgets_resultCard === void 0 ? void 0 : _results_renderingContent_widgets_resultCard.enabled) === true && countWords(results.query || '') >= MIN_QUERY_WORDS;
16917
+ }
16918
+ function computeActivation(results, previous) {
16919
+ var query = results.query || '';
16920
+ var filters = getAgentFilters(results);
16921
+ var base = "".concat(results.index, "|").concat(query, "|").concat(JSON.stringify(filters !== null && filters !== void 0 ? filters : []));
16922
+ // Pagination and "load more" change the hits but not the question, so the
16923
+ // sample settled on the first page is kept and the card stays in place.
16924
+ if (previous && previous.base === base && results.page > 0) {
16925
+ return previous;
16926
+ }
16927
+ // Positions and query ID are attached now: by the time the Insights event
16928
+ // fires, the latest results may be another page of the same question.
16929
+ var hits = addQueryID(addAbsolutePosition(results.hits.slice(0, HITS_SAMPLE_SIZE), results.page, results.hitsPerPage), results.queryID);
16930
+ var hitIds = JSON.stringify(hits.map(function(hit) {
16931
+ return hit.objectID;
16932
+ }));
16933
+ return {
16934
+ base: base,
16935
+ signature: "".concat(base, "|").concat(hitIds),
16936
+ context: {
16937
+ query: query,
16938
+ filters: filters,
16939
+ hits: hits
16940
+ }
16941
+ };
16942
+ }
16943
+ function buildTurnContext(param) {
16944
+ var query = param.query, filters = param.filters, hits = param.hits;
16945
+ // `metadata.turnContext` is a flat string map, so non-string values are
16946
+ // serialized; same keys and shapes as the `promptSuggestions` widget.
16947
+ return _object_spread_props(_object_spread({
16948
+ query: query
16949
+ }, filters ? {
16950
+ filters: JSON.stringify(filters)
16951
+ } : {}), {
16952
+ hitsSample: JSON.stringify(hits.map(stripInternalHitMetadata))
16953
+ });
16954
+ }
16955
+ // Sent as-is, the query reads as a search: the agent re-runs it and answers
16956
+ // "I found…", which the hits below already show. Each clause of the question
16957
+ // heads off a failure mode: "prefer the results provided" answers from
16958
+ // `hitsSample`; "search for better ones" covers a natural-language query that
16959
+ // matched the wrong records; "never display results" stops an agent with that
16960
+ // tool from replying with a `display_results` call and no text, which the
16961
+ // card cannot show. Stopgap until the backend applies a result-card
16962
+ // instruction from the `x-algolia-referer` header.
16963
+ function buildQuestion(query) {
16964
+ return "I'm looking for \"".concat(query, "\". Which of these results would you recommend and why? Prefer the results provided; if they don't answer the question, search for better ones. Always answer in two or three sentences and never display results.");
16965
+ }
16966
+ function hasAssistantMessage(messages) {
16967
+ return Boolean(messages === null || messages === void 0 ? void 0 : messages.some(function(message) {
16968
+ return message.role === 'assistant';
16969
+ }));
16970
+ }
16971
+ var connectResultCard = function connectResultCard(renderFn) {
16972
+ var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
16973
+ checkRendering(renderFn, withUsage$p());
16974
+ return function(widgetParams) {
16975
+ var _ref = widgetParams || {}, agentId = _ref.agentId, transport = _ref.transport, requestOptions = _ref.requestOptions;
16976
+ if (!agentId) {
16977
+ throw new Error(withUsage$p('The `agentId` option is required.'));
16978
+ }
16979
+ if (transport && requestOptions) {
16980
+ throw new Error(withUsage$p('The `transport` and `requestOptions` options are mutually exclusive.'));
16981
+ }
16982
+ var ruleContext = getResultCardRuleContext(agentId);
16983
+ var chatState;
16984
+ var latestRenderOptions = null;
16985
+ var activation = null;
16986
+ var expanded = false;
16987
+ // True between a signature change and the debounced request that follows
16988
+ // it, so the card shows its skeleton rather than the previous answer.
16989
+ var requestPending = false;
16990
+ var debounceTimer;
16991
+ // Set in `dispose()`: a stream can still deliver chunks into the inner
16992
+ // chat afterwards, and those renders must not reach a torn-down container.
16993
+ var disposed = false;
16994
+ var sendEvent;
16995
+ var _obj;
16996
+ var chatParams = _object_spread_props(_object_spread({
16997
+ agentId: agentId
16998
+ }, transport ? {
16999
+ transport: transport
17000
+ } : {
17001
+ requestOptions: requestOptions
17002
+ }), {
17003
+ // The card owns its conversation: nothing shared with the chat panel.
17004
+ persistence: false,
17005
+ type: 'resultCard',
17006
+ disableTriggerValidation: true,
17007
+ // The agent runs its built-in tools server-side and streams their
17008
+ // output. Registering them without `onToolCall` keeps that output; an
17009
+ // unknown tool is answered with "No tool implemented", which drops the
17010
+ // hits the chat's grouped-results needs after the handoff. Same set as
17011
+ // the `chat` widget's defaults.
17012
+ tools: (_obj = {}, _define_property(_obj, SearchIndexToolType, {}), _define_property(_obj, RecommendToolType, {}), _define_property(_obj, GroupedResultsToolType, {}), _define_property(_obj, DisplayResultsToolType, {}), _define_property(_obj, CompareProductsToolType, {}), _define_property(_obj, MemorizeToolType, {}), _define_property(_obj, MemorySearchToolType, {}), _define_property(_obj, PonderToolType, {}), _obj)
17013
+ });
17014
+ var renderOutward = function renderOutward(renderOptions) {
17015
+ if (disposed) return;
17016
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(renderOptions)), {
17017
+ instantSearchInstance: renderOptions.instantSearchInstance
17018
+ }), false);
17019
+ };
17020
+ var rerender = function rerender() {
17021
+ if (latestRenderOptions) {
17022
+ renderOutward(latestRenderOptions);
17023
+ }
17024
+ };
17025
+ // Mirrors every inner render (message deltas, status, errors) into this
17026
+ // widget's own render. The inner widget's `init` render lands before
17027
+ // `latestRenderOptions` is set, so it is stored but not painted.
17028
+ var handleInnerRender = function handleInnerRender(renderState) {
17029
+ chatState = renderState;
17030
+ rerender();
17031
+ };
17032
+ var chatWidget = connectChat(handleInnerRender, noop)(chatParams);
17033
+ var stopGeneration = function stopGeneration() {
17034
+ if (chatState && isChatBusy(chatState)) {
17035
+ chatState.stop();
17036
+ }
17037
+ };
17038
+ var cancelPendingRequest = function cancelPendingRequest() {
17039
+ clearTimeout(debounceTimer);
17040
+ debounceTimer = undefined;
17041
+ requestPending = false;
17042
+ };
17043
+ var send = function send() {
17044
+ cancelPendingRequest();
17045
+ if (disposed || !chatState || !activation) return;
17046
+ var context = activation.context;
17047
+ chatState.clearMessages();
17048
+ chatState.sendMessage({
17049
+ text: buildQuestion(context.query),
17050
+ metadata: {
17051
+ turnContext: buildTurnContext(context)
17052
+ }
17053
+ }, {
17054
+ headers: {
17055
+ 'x-algolia-referer': RESULT_CARD_REFERER
17056
+ }
17057
+ });
17058
+ if (sendEvent) {
17059
+ sendEvent('view:internal', context.hits);
17060
+ }
17061
+ };
17062
+ var scheduleRequest = function scheduleRequest() {
17063
+ cancelPendingRequest();
17064
+ requestPending = true;
17065
+ // Generation is a browser concern: a server render shows the skeleton
17066
+ // and the browser starts the request after hydration.
17067
+ safelyRunOnBrowser(function() {
17068
+ debounceTimer = setTimeout(send, DEBOUNCE_MS);
17069
+ });
17070
+ };
17071
+ // In each transition below the card's own state is settled before
17072
+ // `stopGeneration()`: stopping re-renders synchronously through the inner
17073
+ // chat, and that render must already see the new status.
17074
+ var deactivate = function deactivate() {
17075
+ cancelPendingRequest();
17076
+ activation = null;
17077
+ stopGeneration();
17078
+ };
17079
+ var syncActivation = function syncActivation(results) {
17080
+ if (!isActivated(results)) {
17081
+ if (activation) deactivate();
17082
+ return;
17083
+ }
17084
+ var next = computeActivation(results, activation);
17085
+ if (activation && next.signature === activation.signature) {
17086
+ return;
17087
+ }
17088
+ activation = next;
17089
+ expanded = false;
17090
+ scheduleRequest();
17091
+ stopGeneration();
17092
+ };
17093
+ var retry = function retry() {
17094
+ if (!activation) return;
17095
+ send();
17096
+ };
17097
+ var setExpanded = function setExpanded(nextExpanded) {
17098
+ if (expanded === nextExpanded) return;
17099
+ expanded = nextExpanded;
17100
+ rerender();
17101
+ };
17102
+ var getDefaultChat = function getDefaultChat(renderOptions) {
17103
+ var _instantSearchInstance_renderState_indexId, _instantSearchInstance_renderState, _defaultChat_widgetParams;
17104
+ if (!renderOptions) return undefined;
17105
+ var instantSearchInstance = renderOptions.instantSearchInstance, parent = renderOptions.parent;
17106
+ var indexId = parent ? parent.getIndexId() : '';
17107
+ var defaultChat = (_instantSearchInstance_renderState = instantSearchInstance.renderState) === null || _instantSearchInstance_renderState === void 0 ? void 0 : (_instantSearchInstance_renderState_indexId = _instantSearchInstance_renderState[indexId]) === null || _instantSearchInstance_renderState_indexId === void 0 ? void 0 : _instantSearchInstance_renderState_indexId[CHAT_RENDER_STATE_KEY];
17108
+ // Only the agent is compared: a custom `transport` on either side does
17109
+ // not affect the handoff.
17110
+ if (!(defaultChat === null || defaultChat === void 0 ? void 0 : defaultChat.adoptConversation) || ((_defaultChat_widgetParams = defaultChat.widgetParams) === null || _defaultChat_widgetParams === void 0 ? void 0 : _defaultChat_widgetParams.agentId) !== agentId) {
17111
+ return undefined;
17112
+ }
17113
+ return defaultChat;
17114
+ };
17115
+ var continueInChat = function continueInChat(message) {
17116
+ var defaultChat = getDefaultChat(latestRenderOptions);
17117
+ if (!defaultChat || !chatState || !activation) return;
17118
+ // Keep the inner prompt's directives private when it becomes chat history.
17119
+ var query = activation.context.query;
17120
+ var messages = chatState.messages.map(function(chatMessage) {
17121
+ return chatMessage.role === 'user' ? _object_spread_props(_object_spread({}, chatMessage), {
17122
+ parts: [
17123
+ {
17124
+ type: 'text',
17125
+ text: query
17126
+ }
17127
+ ]
17128
+ }) : chatMessage;
17129
+ });
17130
+ // A chat mid-generation refuses the conversation: opening it anyway
17131
+ // would show an unrelated exchange instead of the card's.
17132
+ var adopted = defaultChat.adoptConversation({
17133
+ id: chatState.id,
17134
+ messages: messages,
17135
+ source: RESULT_CARD_SOURCE
17136
+ });
17137
+ if (!adopted) return;
17138
+ openChat(defaultChat, {
17139
+ message: message,
17140
+ referer: RESULT_CARD_REFERER
17141
+ });
17142
+ };
17143
+ var getStatus = function getStatus(currentActivation) {
17144
+ if (!currentActivation) return 'hidden';
17145
+ // Checked before the inner chat's status: until the debounced request
17146
+ // runs, that status (and its error) still belongs to the previous question.
17147
+ if (requestPending) return 'loading';
17148
+ if ((chatState === null || chatState === void 0 ? void 0 : chatState.status) === 'error') return 'failed';
17149
+ if ((chatState === null || chatState === void 0 ? void 0 : chatState.status) === 'streaming') return 'streaming';
17150
+ if ((chatState === null || chatState === void 0 ? void 0 : chatState.status) === 'submitted') return 'loading';
17151
+ return hasAssistantMessage(chatState === null || chatState === void 0 ? void 0 : chatState.messages) ? 'complete' : 'loading';
17152
+ };
17153
+ var getWidgetRenderState = function getWidgetRenderState(renderOptions) {
17154
+ var _ref, _ref1;
17155
+ var instantSearchInstance = renderOptions.instantSearchInstance, helper = renderOptions.helper;
17156
+ var results = 'results' in renderOptions ? renderOptions.results : undefined;
17157
+ if (!sendEvent) {
17158
+ sendEvent = createSendEventForHits({
17159
+ instantSearchInstance: instantSearchInstance,
17160
+ helper: helper,
17161
+ widgetType: 'ais.resultCard'
17162
+ });
17163
+ }
17164
+ // Before `render` has run (server render, or React computing its initial
17165
+ // state) the activation is derived from the results at hand, without
17166
+ // scheduling anything.
17167
+ var currentActivation = activation !== null && activation !== void 0 ? activation : results && isActivated(results) ? computeActivation(results, null) : null;
17168
+ var status = getStatus(currentActivation);
17169
+ var canContinueInChat = Boolean(getDefaultChat(renderOptions));
17170
+ return {
17171
+ status: status,
17172
+ query: (_ref = currentActivation === null || currentActivation === void 0 ? void 0 : currentActivation.context.query) !== null && _ref !== void 0 ? _ref : '',
17173
+ // Until the debounced request runs, the inner chat still holds the
17174
+ // previous question's answer.
17175
+ messages: requestPending ? [] : (_ref1 = chatState === null || chatState === void 0 ? void 0 : chatState.messages) !== null && _ref1 !== void 0 ? _ref1 : [],
17176
+ // The inner chat keeps the last error around after recovering.
17177
+ error: status === 'failed' ? chatState === null || chatState === void 0 ? void 0 : chatState.error : undefined,
17178
+ suggestions: requestPending ? undefined : chatState === null || chatState === void 0 ? void 0 : chatState.suggestions,
17179
+ retry: retry,
17180
+ canContinueInChat: canContinueInChat,
17181
+ continueInChat: continueInChat,
17182
+ expanded: expanded,
17183
+ setExpanded: setExpanded,
17184
+ sendEvent: sendEvent,
17185
+ widgetParams: widgetParams
17186
+ };
17187
+ };
17188
+ return {
17189
+ $$type: 'ais.resultCard',
17190
+ // "Continue in chat" opens the chat, so this widget counts as an entry
17191
+ // point for `connectChat`'s trigger validation.
17192
+ opensChat: true,
17193
+ init: function init(initOptions) {
17194
+ disposed = false;
17195
+ cancelPendingRequest();
17196
+ chatState = undefined;
17197
+ latestRenderOptions = null;
17198
+ activation = null;
17199
+ expanded = false;
17200
+ sendEvent = undefined;
17201
+ chatWidget.init(initOptions);
17202
+ // After the inner init so its first render is stored, not painted.
17203
+ latestRenderOptions = initOptions;
17204
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(initOptions)), {
17205
+ instantSearchInstance: initOptions.instantSearchInstance
17206
+ }), true);
17207
+ },
17208
+ render: function render(renderOptions) {
17209
+ latestRenderOptions = renderOptions;
17210
+ if (renderOptions.results) {
17211
+ syncActivation(renderOptions.results);
17212
+ }
17213
+ renderOutward(renderOptions);
17214
+ },
17215
+ dispose: function dispose(disposeOptions) {
17216
+ disposed = true;
17217
+ cancelPendingRequest();
17218
+ stopGeneration();
17219
+ chatWidget.dispose();
17220
+ unmountFn();
17221
+ var remaining = (disposeOptions.state.ruleContexts || []).filter(function(context) {
17222
+ return context !== ruleContext;
17223
+ });
17224
+ return disposeOptions.state.setQueryParameter('ruleContexts', remaining.length > 0 ? remaining : undefined);
17225
+ },
17226
+ getWidgetSearchParameters: function getWidgetSearchParameters(state) {
17227
+ var ruleContexts = state.ruleContexts || [];
17228
+ if (ruleContexts.includes(ruleContext)) {
17229
+ return state;
17230
+ }
17231
+ return state.setQueryParameter('ruleContexts', _to_consumable_array(ruleContexts).concat([
17232
+ ruleContext
17233
+ ]));
17234
+ },
17235
+ getRenderState: function getRenderState(renderState, renderOptions) {
17236
+ return _object_spread_props(_object_spread({}, renderState), {
17237
+ resultCard: this.getWidgetRenderState(renderOptions)
17238
+ });
17239
+ },
17240
+ getWidgetRenderState: function getWidgetRenderState1(renderOptions) {
17241
+ return getWidgetRenderState(renderOptions);
17242
+ }
17243
+ };
17244
+ };
17245
+ };
17246
+
17247
+ function useResultCard(props, additionalWidgetProperties) {
17248
+ return useConnector(connectResultCard, props, additionalWidgetProperties);
17249
+ }
17250
+
16677
17251
  var withUsage$o = createDocumentationMessageGenerator({
16678
17252
  name: 'chatTrigger',
16679
17253
  connector: true
@@ -16768,11 +17342,11 @@
16768
17342
  return "__ALGOLIA_COMPARISON_".concat(configurationId, "__");
16769
17343
  }
16770
17344
  /**
16771
- * Builds the default comparison message. It names each selected item so the
16772
- * agent can ground the comparison: it re-retrieves the records with
16773
- * `algolia_search_index` and renders them with the builtin
16774
- * `algolia_compare_products` tool. Works with the default shopping assistant
16775
- * prompt — no agent configuration required.
17345
+ * Builds the default comparison message. It names each selected item; the
17346
+ * records themselves travel in the turn context (`selected_products`), where
17347
+ * both the agent and the builtin `algolia_compare_products` table read them,
17348
+ * so no search round-trip is needed to render the comparison. Works with the
17349
+ * default shopping assistant prompt — no agent configuration required.
16776
17350
  */ function getDefaultComparisonMessage(items) {
16777
17351
  var labels = items.map(function(item) {
16778
17352
  var _item_name;
@@ -17354,7 +17928,7 @@
17354
17928
  objectID: objectID,
17355
17929
  maxRecommendations: limit,
17356
17930
  threshold: threshold,
17357
- // @ts-expect-error until @algolia/recommend types are updated
17931
+ // @ts-ignore until @algolia/recommend types are updated
17358
17932
  fallbackParameters: fallbackParameters ? _object_spread({}, fallbackParameters, escapeHTML ? TAG_PLACEHOLDER : {}) : undefined,
17359
17933
  queryParameters: _object_spread({}, queryParameters, escapeHTML ? TAG_PLACEHOLDER : {}),
17360
17934
  $$id: _this.$$id
@@ -18949,16 +19523,23 @@
18949
19523
  return ruleContexts;
18950
19524
  }
18951
19525
  function applyRuleContexts(event) {
18952
- var _this = this, helper = _this.helper, initialRuleContexts = _this.initialRuleContexts, trackedFilters = _this.trackedFilters, transformRuleContexts = _this.transformRuleContexts;
19526
+ var _this = this;
19527
+ var _this1 = this, helper = _this1.helper, trackedFilters = _this1.trackedFilters, transformRuleContexts = _this1.transformRuleContexts;
18953
19528
  var sharedHelperState = event.state;
18954
19529
  var previousRuleContexts = sharedHelperState.ruleContexts || [];
19530
+ var foreignRuleContexts = previousRuleContexts.filter(function(ruleContext) {
19531
+ return !_this.ownedRuleContexts.includes(ruleContext);
19532
+ });
18955
19533
  var newRuleContexts = getRuleContextsFromTrackedFilters({
18956
19534
  helper: helper,
18957
19535
  sharedHelperState: sharedHelperState,
18958
19536
  trackedFilters: trackedFilters
18959
19537
  });
18960
- var nextRuleContexts = _to_consumable_array(initialRuleContexts).concat(_to_consumable_array(newRuleContexts));
19538
+ var nextRuleContexts = _to_consumable_array(foreignRuleContexts).concat(_to_consumable_array(newRuleContexts));
18961
19539
  var ruleContexts = transformRuleContexts(nextRuleContexts).slice(0, 10);
19540
+ this.ownedRuleContexts = ruleContexts.filter(function(ruleContext) {
19541
+ return !foreignRuleContexts.includes(ruleContext);
19542
+ });
18962
19543
  if (!isEqual(previousRuleContexts, ruleContexts)) {
18963
19544
  helper.overrideStateWithoutTriggeringChangeEvent(_object_spread_props(_object_spread({}, sharedHelperState), {
18964
19545
  ruleContexts: ruleContexts
@@ -18980,21 +19561,19 @@
18980
19561
  }
18981
19562
  });
18982
19563
  var hasTrackedFilters = Object.keys(trackedFilters).length > 0;
18983
- // We store the initial rule contexts applied before creating the widget
18984
- // so that we do not override them with the rules created from `trackedFilters`.
18985
- var initialRuleContexts = [];
19564
+ var tracker;
18986
19565
  var onHelperChange;
18987
19566
  return {
18988
19567
  $$type: 'ais.queryRules',
18989
19568
  init: function init(initOptions) {
18990
19569
  var helper = initOptions.helper, state = initOptions.state, instantSearchInstance = initOptions.instantSearchInstance;
18991
- initialRuleContexts = state.ruleContexts || [];
18992
- onHelperChange = applyRuleContexts.bind({
19570
+ tracker = {
18993
19571
  helper: helper,
18994
- initialRuleContexts: initialRuleContexts,
18995
19572
  trackedFilters: trackedFilters,
18996
- transformRuleContexts: transformRuleContexts
18997
- });
19573
+ transformRuleContexts: transformRuleContexts,
19574
+ ownedRuleContexts: []
19575
+ };
19576
+ onHelperChange = applyRuleContexts.bind(tracker);
18998
19577
  if (hasTrackedFilters) {
18999
19578
  // We need to apply the `ruleContexts` based on the `trackedFilters`
19000
19579
  // before the helper changes state in some cases:
@@ -19040,7 +19619,9 @@
19040
19619
  unmount();
19041
19620
  if (hasTrackedFilters) {
19042
19621
  helper.removeListener('change', onHelperChange);
19043
- return state.setQueryParameter('ruleContexts', initialRuleContexts);
19622
+ return state.setQueryParameter('ruleContexts', (state.ruleContexts || []).filter(function(ruleContext) {
19623
+ return !tracker.ownedRuleContexts.includes(ruleContext);
19624
+ }));
19044
19625
  }
19045
19626
  return state;
19046
19627
  }
@@ -21158,6 +21739,7 @@
21158
21739
  exports.useRange = useRange;
21159
21740
  exports.useRefinementList = useRefinementList;
21160
21741
  exports.useRelatedProducts = useRelatedProducts;
21742
+ exports.useResultCard = useResultCard;
21161
21743
  exports.useSearchBox = useSearchBox;
21162
21744
  exports.useSortBy = useSortBy;
21163
21745
  exports.useStats = useStats;