react-instantsearch-core 7.50.1 → 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.1 | © 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.1';
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.4';
3922
+ version$1 = '3.30.0';
3923
3923
  return version$1;
3924
3924
  }
3925
3925
 
@@ -6010,7 +6010,7 @@
6010
6010
  }
6011
6011
  }
6012
6012
 
6013
- var withUsage$y = createDocumentationMessageGenerator({
6013
+ var withUsage$z = createDocumentationMessageGenerator({
6014
6014
  name: 'configure',
6015
6015
  connector: true
6016
6016
  });
@@ -6026,7 +6026,7 @@
6026
6026
  var renderFn = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : noop, unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
6027
6027
  return function(widgetParams) {
6028
6028
  if (!widgetParams || !isPlainObject(widgetParams.searchParameters)) {
6029
- throw new Error(withUsage$y('The `searchParameters` option expects an object.'));
6029
+ throw new Error(withUsage$z('The `searchParameters` option expects an object.'));
6030
6030
  }
6031
6031
  var connectorState = {};
6032
6032
  function refine(helper) {
@@ -7373,7 +7373,11 @@
7373
7373
  parent: parent,
7374
7374
  instantSearchInstance: instantSearchInstance,
7375
7375
  state: helper.state,
7376
- 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
+ },
7377
7381
  templatesConfig: instantSearchInstance.templatesConfig,
7378
7382
  createURL: parent.createURL,
7379
7383
  scopedResults: [],
@@ -7394,7 +7398,10 @@
7394
7398
  results: results,
7395
7399
  scopedResults: parent.getScopedResults(),
7396
7400
  state: results && '_state' in results ? results._state : helper.state,
7397
- renderState: instantSearchInstance.renderState,
7401
+ // Keep renderState live to avoid retaining previous renderState snapshots.
7402
+ get renderState () {
7403
+ return instantSearchInstance.renderState;
7404
+ },
7398
7405
  templatesConfig: instantSearchInstance.templatesConfig,
7399
7406
  createURL: parent.createURL,
7400
7407
  searchMetadata: {
@@ -7863,13 +7870,13 @@
7863
7870
  return null;
7864
7871
  }
7865
7872
 
7866
- var withUsage$x = createDocumentationMessageGenerator({
7873
+ var withUsage$y = createDocumentationMessageGenerator({
7867
7874
  name: 'dynamic-widgets',
7868
7875
  connector: true
7869
7876
  });
7870
7877
  var connectDynamicWidgets = function connectDynamicWidgets(renderFn) {
7871
7878
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
7872
- checkRendering(renderFn, withUsage$x());
7879
+ checkRendering(renderFn, withUsage$y());
7873
7880
  return function(widgetParams) {
7874
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 ? [
7875
7882
  '*'
@@ -7879,10 +7886,10 @@
7879
7886
  if (!(widgets && Array.isArray(widgets) && widgets.every(function(widget) {
7880
7887
  return (typeof widget === "undefined" ? "undefined" : _type_of(widget)) === 'object';
7881
7888
  }))) {
7882
- 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.'));
7883
7890
  }
7884
7891
  if (!Array.isArray(facets)) {
7885
- 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))));
7886
7893
  }
7887
7894
  var localWidgets = new Map();
7888
7895
  return {
@@ -7984,7 +7991,7 @@
7984
7991
  results: results
7985
7992
  });
7986
7993
  if (!Array.isArray(attributesToRender)) {
7987
- 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.'));
7988
7995
  }
7989
7996
  return {
7990
7997
  attributesToRender: attributesToRender,
@@ -8375,19 +8382,19 @@
8375
8382
  return toFeedSearchResults(lastResults._state, raw);
8376
8383
  });
8377
8384
  }
8378
- var withUsage$w = createDocumentationMessageGenerator({
8385
+ var withUsage$x = createDocumentationMessageGenerator({
8379
8386
  name: 'feeds',
8380
8387
  connector: true
8381
8388
  });
8382
8389
  var connectFeeds = function connectFeeds(renderFn) {
8383
8390
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
8384
- checkRendering(renderFn, withUsage$w());
8391
+ checkRendering(renderFn, withUsage$x());
8385
8392
  return function(widgetParams) {
8386
8393
  var isolated = widgetParams.isolated, _widgetParams_transformFeeds = widgetParams.transformFeeds, transformFeeds = _widgetParams_transformFeeds === void 0 ? function(feeds) {
8387
8394
  return feeds;
8388
8395
  } : _widgetParams_transformFeeds;
8389
8396
  if (isolated !== false) {
8390
- 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`.'));
8391
8398
  }
8392
8399
  return {
8393
8400
  $$type: 'ais.feeds',
@@ -8395,7 +8402,7 @@
8395
8402
  init: function init(initOptions) {
8396
8403
  var instantSearchInstance = initOptions.instantSearchInstance;
8397
8404
  if (!instantSearchInstance.compositionID) {
8398
- 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).'));
8399
8406
  }
8400
8407
  hydrateFeedsFromInitialResultsIfNeeded(instantSearchInstance, initOptions.parent);
8401
8408
  renderFn(_object_spread_props(_object_spread({}, this.getWidgetRenderState(initOptions)), {
@@ -8441,12 +8448,12 @@
8441
8448
  ];
8442
8449
  feedIDs = transformFeeds(feedIDs);
8443
8450
  if (!Array.isArray(feedIDs)) {
8444
- 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.'));
8445
8452
  }
8446
8453
  if (!feedIDs.every(function(feedID) {
8447
8454
  return typeof feedID === 'string';
8448
8455
  })) {
8449
- 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).'));
8450
8457
  }
8451
8458
  return {
8452
8459
  feedIDs: feedIDs,
@@ -8557,7 +8564,7 @@
8557
8564
  }));
8558
8565
  }
8559
8566
 
8560
- var withUsage$v = createDocumentationMessageGenerator({
8567
+ var withUsage$w = createDocumentationMessageGenerator({
8561
8568
  name: 'index-widget'
8562
8569
  });
8563
8570
  /**
@@ -8685,7 +8692,7 @@
8685
8692
  var index = function index(widgetParams) {
8686
8693
  var _widgetParams_EXPERIMENTAL_isolated;
8687
8694
  if (widgetParams === undefined || widgetParams.indexName === undefined && !widgetParams.isolated && !widgetParams.EXPERIMENTAL_isolated) {
8688
- throw new Error(withUsage$v('The `indexName` option is required.'));
8695
+ throw new Error(withUsage$w('The `indexName` option is required.'));
8689
8696
  }
8690
8697
  // When isolated=true, we use an empty string as the default indexName.
8691
8698
  // This is intentional: isolated indices do not require a real index name.
@@ -8783,7 +8790,7 @@
8783
8790
  addWidgets: function addWidgets(widgets) {
8784
8791
  var _this = this;
8785
8792
  if (!Array.isArray(widgets)) {
8786
- 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.'));
8787
8794
  }
8788
8795
  var flatWidgets = widgets.reduce(function(acc, w) {
8789
8796
  return acc.concat(Array.isArray(w) ? w : [
@@ -8793,7 +8800,7 @@
8793
8800
  if (flatWidgets.some(function(widget) {
8794
8801
  return typeof widget.init !== 'function' && typeof widget.render !== 'function';
8795
8802
  })) {
8796
- 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.'));
8797
8804
  }
8798
8805
  flatWidgets.forEach(function(widget) {
8799
8806
  widget.parent = _this;
@@ -8843,10 +8850,10 @@
8843
8850
  },
8844
8851
  updateWidget: function updateWidget(previousWidget, nextWidget) {
8845
8852
  if (typeof previousWidget.dispose !== 'function') {
8846
- 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.'));
8847
8854
  }
8848
8855
  if (typeof nextWidget.init !== 'function' && typeof nextWidget.render !== 'function') {
8849
- 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.'));
8850
8857
  }
8851
8858
  // The `uiState` as it is before the previous widget is detached, so that
8852
8859
  // the state it owns can be handed over to the next widget.
@@ -8930,7 +8937,7 @@
8930
8937
  removeWidgets: function removeWidgets(widgets) {
8931
8938
  var _this = this;
8932
8939
  if (!Array.isArray(widgets)) {
8933
- 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.'));
8934
8941
  }
8935
8942
  var flatWidgets = widgets.reduce(function(acc, w) {
8936
8943
  return acc.concat(Array.isArray(w) ? w : [
@@ -8940,7 +8947,7 @@
8940
8947
  if (flatWidgets.some(function(widget) {
8941
8948
  return typeof widget.dispose !== 'function';
8942
8949
  })) {
8943
- 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.'));
8944
8951
  }
8945
8952
  localWidgets = localWidgets.filter(function(widget) {
8946
8953
  return flatWidgets.indexOf(widget) === -1;
@@ -9448,7 +9455,7 @@
9448
9455
  });
9449
9456
  }
9450
9457
 
9451
- var version = '4.118.1';
9458
+ var version = '4.119.0';
9452
9459
 
9453
9460
  var ANONYMOUS_TOKEN_COOKIE_KEY = '_ALGOLIA';
9454
9461
  function getCookie(name) {
@@ -11310,7 +11317,7 @@
11310
11317
  };
11311
11318
  }
11312
11319
 
11313
- var withUsage$u = createDocumentationMessageGenerator({
11320
+ var withUsage$v = createDocumentationMessageGenerator({
11314
11321
  name: 'instantsearch'
11315
11322
  });
11316
11323
  function defaultCreateURL() {
@@ -11381,7 +11388,7 @@
11381
11388
  _this.setMaxListeners(100);
11382
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;
11383
11390
  if (searchClient === null) {
11384
- throw new Error(withUsage$u('The `searchClient` option is required.'));
11391
+ throw new Error(withUsage$v('The `searchClient` option is required.'));
11385
11392
  }
11386
11393
  if (typeof searchClient.search !== 'function') {
11387
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/");
@@ -11390,7 +11397,7 @@
11390
11397
  searchClient.addAlgoliaAgent("instantsearch.js (".concat(version, ")"));
11391
11398
  }
11392
11399
  if (insightsClient && typeof insightsClient !== 'function') {
11393
- 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.'));
11394
11401
  }
11395
11402
  _this._initialOptions = options;
11396
11403
  _this.client = searchClient;
@@ -11539,12 +11546,12 @@
11539
11546
  * @param widgets The array of widgets to add to InstantSearch.
11540
11547
  */ function addWidgets(widgets) {
11541
11548
  if (!Array.isArray(widgets)) {
11542
- 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`.'));
11543
11550
  }
11544
11551
  if (this.compositionID && widgets.some(function(w) {
11545
11552
  return !Array.isArray(w) && isIndexWidget(w) && !w._isolated;
11546
11553
  })) {
11547
- 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.'));
11548
11555
  }
11549
11556
  this.mainIndex.addWidgets(widgets);
11550
11557
  return this;
@@ -11573,7 +11580,7 @@
11573
11580
  * The widgets must implement a `dispose()` method to clear their states.
11574
11581
  */ function removeWidgets(widgets) {
11575
11582
  if (!Array.isArray(widgets)) {
11576
- 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`.'));
11577
11584
  }
11578
11585
  this.mainIndex.removeWidgets(widgets);
11579
11586
  return this;
@@ -11587,7 +11594,7 @@
11587
11594
  */ function start() {
11588
11595
  var _this = this;
11589
11596
  if (this.started) {
11590
- 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.'));
11591
11598
  }
11592
11599
  // This Helper is used for the queries, we don't care about its state. The
11593
11600
  // states are managed at the `index` level. We use this Helper to create
@@ -11795,7 +11802,7 @@
11795
11802
  var _this = this;
11796
11803
  var callOnStateChange = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true;
11797
11804
  if (!this.mainHelper) {
11798
- 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`.'));
11799
11806
  }
11800
11807
  // We refresh the index UI state to update the local UI state that the
11801
11808
  // main index passes to the function form of `setUiState`.
@@ -11832,7 +11839,7 @@
11832
11839
  value: function createURL() {
11833
11840
  var nextState = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
11834
11841
  if (!this.started) {
11835
- 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`.'));
11836
11843
  }
11837
11844
  return this._createURL(nextState);
11838
11845
  }
@@ -11841,7 +11848,7 @@
11841
11848
  key: "refresh",
11842
11849
  value: function refresh() {
11843
11850
  if (!this.mainHelper) {
11844
- 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`.'));
11845
11852
  }
11846
11853
  this.mainHelper.clearCache().search();
11847
11854
  }
@@ -12373,13 +12380,13 @@
12373
12380
  }, children);
12374
12381
  }
12375
12382
 
12376
- var withUsage$t = createDocumentationMessageGenerator({
12383
+ var withUsage$u = createDocumentationMessageGenerator({
12377
12384
  name: 'autocomplete',
12378
12385
  connector: true
12379
12386
  });
12380
12387
  var connectAutocomplete = function connectAutocomplete(renderFn) {
12381
12388
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
12382
- checkRendering(renderFn, withUsage$t());
12389
+ checkRendering(renderFn, withUsage$u());
12383
12390
  return function(widgetParams) {
12384
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) {
12385
12392
  return indices;
@@ -12492,20 +12499,20 @@
12492
12499
  return useConnector(connectAutocomplete, props, additionalWidgetProperties);
12493
12500
  }
12494
12501
 
12495
- var withUsage$s = createDocumentationMessageGenerator({
12502
+ var withUsage$t = createDocumentationMessageGenerator({
12496
12503
  name: 'breadcrumb',
12497
12504
  connector: true
12498
12505
  });
12499
12506
  var connectBreadcrumb = function connectBreadcrumb(renderFn) {
12500
12507
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
12501
- checkRendering(renderFn, withUsage$s());
12508
+ checkRendering(renderFn, withUsage$t());
12502
12509
  var connectorState = {};
12503
12510
  return function(widgetParams) {
12504
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) {
12505
12512
  return items;
12506
12513
  } : _ref_transformItems;
12507
12514
  if (!attributes || !Array.isArray(attributes) || attributes.length === 0) {
12508
- 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.'));
12509
12516
  }
12510
12517
  var _attributes = _sliced_to_array(attributes, 1), hierarchicalFacetName = _attributes[0];
12511
12518
  function getRefinedState(state, facetValue) {
@@ -12720,16 +12727,49 @@
12720
12727
  }
12721
12728
  };
12722
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
+ }
12723
12756
  /**
12724
12757
  * Collects into `store` the records of every completed tool call in `messages`
12725
- * whose output holds `hits`. Matching on the output rather than on a tool name
12726
- * 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.
12727
12764
  *
12728
12765
  * Idempotent, so it can re-run over the whole conversation on every update:
12729
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.
12730
12769
  */ function collectChatRecords(messages) {
12731
12770
  var store = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : createChatRecordsStore();
12732
12771
  messages === null || messages === void 0 ? void 0 : messages.forEach(function(message) {
12772
+ store.merge(selectedRecords(message));
12733
12773
  message.parts.forEach(function(part) {
12734
12774
  var _part_output;
12735
12775
  if (!isPartTool(part) || part.state !== 'output-available') {
@@ -13456,6 +13496,15 @@
13456
13496
  this.conversationId = this.generateId();
13457
13497
  }
13458
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
+ },
13459
13508
  {
13460
13509
  key: "messages",
13461
13510
  get: function get() {
@@ -14742,7 +14791,154 @@
14742
14791
  return (chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.status) === 'submitted' || (chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.status) === 'streaming';
14743
14792
  }
14744
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
+
14745
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';
14746
14942
  /**
14747
14943
  * Whether `toolName` is the search tool as the Algolia MCP Server exposes it:
14748
14944
  * one tool per index, named after the index it searches
@@ -14751,12 +14947,6 @@
14751
14947
  return toolName.startsWith("".concat(SearchIndexToolType, "_"));
14752
14948
  };
14753
14949
 
14754
- function flat(arr) {
14755
- return arr.reduce(function(acc, array) {
14756
- return acc.concat(array);
14757
- }, []);
14758
- }
14759
-
14760
14950
  /**
14761
14951
  * Reads a human-readable message from a failed HTTP response body when the
14762
14952
  * server returns JSON such as `{ "message": "..." }` (the shared
@@ -14947,7 +15137,7 @@
14947
15137
  return DefaultChatTransport;
14948
15138
  }(HttpChatTransport);
14949
15139
 
14950
- var withUsage$r = createDocumentationMessageGenerator({
15140
+ var withUsage$s = createDocumentationMessageGenerator({
14951
15141
  name: 'chat',
14952
15142
  connector: true
14953
15143
  });
@@ -15006,79 +15196,9 @@
15006
15196
  // Storage availability must not block the visible state change.
15007
15197
  }
15008
15198
  }
15009
- function getAttributesToClear$1(param) {
15010
- var results = param.results, helper = param.helper;
15011
- return uniq(getRefinements(results, helper.state, true).map(function(refinement) {
15012
- return refinement.attribute;
15013
- }));
15014
- }
15015
- /**
15016
- * One Algolia `numericFilters` entry: `'price <= 1500'`. The operators are
15017
- * exactly the set `helper.addNumericRefinement` accepts, and exactly the set
15018
- * the Algolia MCP Server emits.
15019
- */ var NUMERIC_FILTER = /^(.+?)\s*(<=|>=|!=|=|<|>)\s*(-?\d+(?:\.\d+)?)$/;
15020
- function updateStateFromSearchToolInput(params, helper) {
15021
- // clear all filters first
15022
- var attributesToClear = getAttributesToClear$1({
15023
- results: helper.lastResults,
15024
- helper: helper
15025
- });
15026
- helper.setState(clearRefinements({
15027
- helper: helper,
15028
- attributesToClear: attributesToClear
15029
- }));
15030
- if (params.facetFilters) {
15031
- var refinements = flat(params.facetFilters).reduce(function(acc, filter) {
15032
- var separatorIndex = filter.indexOf(':');
15033
- if (separatorIndex > 0) {
15034
- acc.push({
15035
- attribute: filter.slice(0, separatorIndex),
15036
- value: filter.slice(separatorIndex + 1)
15037
- });
15038
- }
15039
- return acc;
15040
- }, []);
15041
- var hierarchicalRefinements = new Map();
15042
- refinements.forEach(function(param) {
15043
- var attribute = param.attribute, value = param.value;
15044
- var hierarchicalFacet = helper.state.hierarchicalFacets.find(function(facet) {
15045
- return facet.name === attribute || facet.attributes.includes(attribute);
15046
- });
15047
- if (hierarchicalFacet) {
15048
- var currentValue = hierarchicalRefinements.get(hierarchicalFacet.name);
15049
- if (currentValue === undefined || value.length > currentValue.length) {
15050
- hierarchicalRefinements.set(hierarchicalFacet.name, value);
15051
- }
15052
- return;
15053
- }
15054
- if (!helper.state.isConjunctiveFacet(attribute) && !helper.state.isDisjunctiveFacet(attribute)) {
15055
- helper.setState(helper.state.addDisjunctiveFacet(attribute));
15056
- }
15057
- helper.toggleFacetRefinement(attribute, value);
15058
- });
15059
- hierarchicalRefinements.forEach(function(value, name) {
15060
- helper.toggleFacetRefinement(name, value);
15061
- });
15062
- }
15063
- if (params.numericFilters) {
15064
- params.numericFilters.forEach(function(filter) {
15065
- var match = filter.match(NUMERIC_FILTER);
15066
- if (!match) {
15067
- return;
15068
- }
15069
- var _match = _sliced_to_array(match, 4), attribute = _match[1], operator = _match[2], value = _match[3];
15070
- helper.addNumericRefinement(attribute, operator, Number(value));
15071
- });
15072
- }
15073
- if (params.query) {
15074
- helper.setQuery(params.query);
15075
- }
15076
- helper.search();
15077
- return helper.state;
15078
- }
15079
15199
  var connectChat = function connectChat(renderFn) {
15080
15200
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
15081
- checkRendering(renderFn, withUsage$r());
15201
+ checkRendering(renderFn, withUsage$s());
15082
15202
  return function(widgetParams) {
15083
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, [
15084
15204
  "resume",
@@ -15163,6 +15283,35 @@
15163
15283
  setMessages([]);
15164
15284
  _chatInstance.clearError();
15165
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
+ };
15166
15315
  var validateEntryPoints = function validateEntryPoints(instantSearchInstance) {
15167
15316
  if (disableTriggerValidation || hasValidatedEntryPoints) {
15168
15317
  return;
@@ -15192,13 +15341,29 @@
15192
15341
  var transport;
15193
15342
  var client = instantSearchInstance.client;
15194
15343
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
15195
- // Filter out custom data parts (like data-suggestions) that the backend doesn't accept
15196
- 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) {
15197
15351
  return messages.map(function(message) {
15352
+ var _message_metadata;
15198
15353
  var _message_parts;
15199
- 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
+ } : {}), {
15200
15361
  parts: (_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.filter(function(part) {
15201
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;
15202
15367
  })
15203
15368
  });
15204
15369
  });
@@ -15219,11 +15384,11 @@
15219
15384
  messages: params.messages
15220
15385
  }, params.body)
15221
15386
  };
15222
- // Then filter out data-* parts
15387
+ // Then shape the messages for the wire
15223
15388
  var applyFilter = function applyFilter(prepared) {
15224
15389
  return _object_spread_props(_object_spread({}, prepared), {
15225
15390
  body: _object_spread_props(_object_spread({}, prepared.body), {
15226
- messages: filterDataParts(prepared.body.messages)
15391
+ messages: prepareOutgoingMessages(prepared.body.messages)
15227
15392
  })
15228
15393
  });
15229
15394
  };
@@ -15235,10 +15400,12 @@
15235
15400
  }
15236
15401
  }));
15237
15402
  }
15238
- 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) {
15239
15406
  var _options_requestOptions, _options_requestOptions1;
15240
15407
  if (!appId || !apiKey) {
15241
- 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.'));
15242
15409
  }
15243
15410
  var createApi = function createApi() {
15244
15411
  var bypassCache = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
@@ -15270,17 +15437,74 @@
15270
15437
  body: {
15271
15438
  id: id,
15272
15439
  messageId: messageId,
15273
- messages: filterDataParts(messages)
15440
+ messages: prepareOutgoingMessages(messages)
15274
15441
  }
15275
15442
  };
15276
15443
  }
15277
15444
  });
15278
15445
  }
15279
15446
  if (!transport) {
15280
- throw new Error(withUsage$r('You need to provide either an `agentId` or a `transport`.'));
15281
- }
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
+ };
15282
15501
  var canRepairRestoredPendingToolParts = !resume;
15283
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
+ },
15284
15508
  persistence: normalizedPersistence.messages,
15285
15509
  sendAutomaticallyWhen: sendAutomaticallyWhen,
15286
15510
  transport: transport,
@@ -15467,7 +15691,7 @@
15467
15691
  if (agentId && feedback) {
15468
15692
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(initOptions.instantSearchInstance.client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
15469
15693
  if (!appId || !apiKey) {
15470
- 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.'));
15471
15695
  }
15472
15696
  feedbackAbortController = new AbortController();
15473
15697
  _sendChatMessageFeedback = function _sendChatMessageFeedback(messageId, vote) {
@@ -15569,7 +15793,7 @@
15569
15793
  });
15570
15794
  }
15571
15795
  function applyFilters(params) {
15572
- return updateStateFromSearchToolInput(params, helper);
15796
+ return applyAgentFilters(params, helper);
15573
15797
  }
15574
15798
  // A restored or server-rendered conversation never emitted the messages
15575
15799
  // callback above; collecting is idempotent, so covering it here is free.
@@ -15604,11 +15828,18 @@
15604
15828
  }
15605
15829
  // Resolve once per send; let the server validate the payload and
15606
15830
  // surface any contract violations.
15607
- 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;
15608
15839
  return (_chatInstance1 = _chatInstance).sendMessage.apply(_chatInstance1, [
15609
15840
  _object_spread_props(_object_spread({}, message), {
15610
- metadata: _object_spread_props(_object_spread({}, message.metadata), {
15611
- turnContext: turnContext
15841
+ metadata: _object_spread_props(_object_spread({}, metadata), {
15842
+ turnContext: _object_spread({}, widgetContext, messageTurnContext)
15612
15843
  })
15613
15844
  })
15614
15845
  ].concat(_to_consumable_array(rest)));
@@ -15631,6 +15862,7 @@
15631
15862
  setMessages: setMessages,
15632
15863
  suggestions: getSuggestionsFromMessages(_chatInstance.messages),
15633
15864
  clearMessages: clearMessages,
15865
+ adoptConversation: adoptConversation,
15634
15866
  tools: toolsWithAddToolResult,
15635
15867
  records: records,
15636
15868
  sendChatMessageFeedback: _sendChatMessageFeedback,
@@ -16103,20 +16335,20 @@
16103
16335
  };
16104
16336
  }
16105
16337
 
16106
- var withUsage$q = createDocumentationMessageGenerator({
16338
+ var withUsage$r = createDocumentationMessageGenerator({
16107
16339
  name: 'tasks',
16108
16340
  connector: true
16109
16341
  });
16110
16342
  var connectTasks = function connectTasks(renderFn) {
16111
16343
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
16112
- checkRendering(renderFn, withUsage$q());
16344
+ checkRendering(renderFn, withUsage$r());
16113
16345
  return function(widgetParams) {
16114
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;
16115
16347
  if (!agentId && !transport) {
16116
- 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.'));
16117
16349
  }
16118
16350
  if (!task && !kind) {
16119
- 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.'));
16120
16352
  }
16121
16353
  var runner;
16122
16354
  var output;
@@ -16190,7 +16422,7 @@
16190
16422
  if (agentId) {
16191
16423
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(instantSearchInstance.client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
16192
16424
  if (!appId || !apiKey) {
16193
- 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.'));
16194
16426
  }
16195
16427
  var taskTransport = createTaskTransport({
16196
16428
  transport: transport,
@@ -16237,14 +16469,14 @@
16237
16469
  };
16238
16470
  };
16239
16471
 
16240
- var withUsage$p = createDocumentationMessageGenerator({
16472
+ var withUsage$q = createDocumentationMessageGenerator({
16241
16473
  name: 'prompt-suggestions',
16242
16474
  connector: true
16243
16475
  });
16244
16476
  var RENDER_STATE_KEY = 'promptSuggestions';
16245
- var CHAT_RENDER_STATE_KEY = 'chat';
16477
+ var CHAT_RENDER_STATE_KEY$1 = 'chat';
16246
16478
  var PROMPT_SUGGESTIONS_TASK_KIND = 'prompt_suggestions';
16247
- var DEBOUNCE_MS = 300;
16479
+ var DEBOUNCE_MS$1 = 300;
16248
16480
  function parseSuggestions(data) {
16249
16481
  var suggestions = data === null || data === void 0 ? void 0 : data.suggestions;
16250
16482
  if (!Array.isArray(suggestions)) {
@@ -16284,63 +16516,18 @@
16284
16516
  return null;
16285
16517
  }
16286
16518
  }
16287
- function stripInternalHitMetadata(hit) {
16288
- var clean = {};
16289
- Object.keys(hit).forEach(function(key) {
16290
- // Strip internal metadata, which is `_`-prefixed
16291
- // (`_highlightResult`, `_rankingInfo`, `__position`, …).
16292
- if (!key.startsWith('_')) {
16293
- clean[key] = hit[key];
16294
- }
16295
- });
16296
- return clean;
16297
- }
16298
16519
  var DEFAULT_TRANSFORM_HITS = function DEFAULT_TRANSFORM_HITS(hits) {
16299
16520
  return hits.slice(0, 5).map(stripInternalHitMetadata);
16300
16521
  };
16301
- function buildFilters(results) {
16302
- var state = results._state;
16303
- if (!state) {
16304
- return undefined;
16305
- }
16306
- var groups = [];
16307
- var disjunctiveGroups = {};
16308
- getRefinements(results, state).forEach(function(refinement) {
16309
- if (refinement.type === 'numeric') {
16310
- groups.push([
16311
- "".concat(refinement.attribute).concat(refinement.operator).concat(refinement.numericValue)
16312
- ]);
16313
- return;
16314
- }
16315
- var value = refinement.type === 'exclude' ? "".concat(refinement.attribute, ":-").concat(refinement.name) : "".concat(refinement.attribute, ":").concat(refinement.name);
16316
- if (refinement.type === 'disjunctive') {
16317
- var group = disjunctiveGroups[refinement.attribute];
16318
- if (group) {
16319
- group.push(value);
16320
- } else {
16321
- var newGroup = [
16322
- value
16323
- ];
16324
- disjunctiveGroups[refinement.attribute] = newGroup;
16325
- groups.push(newGroup);
16326
- }
16327
- return;
16328
- }
16329
- groups.push([
16330
- value
16331
- ]);
16332
- });
16333
- return groups.length > 0 ? groups : undefined;
16334
- }
16335
16522
  var connectPromptSuggestions = function connectPromptSuggestions(renderFn) {
16336
16523
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
16337
- checkRendering(renderFn, withUsage$p());
16524
+ checkRendering(renderFn, withUsage$q());
16338
16525
  return function(widgetParams) {
16339
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) {
16340
16527
  return items;
16341
16528
  } : _widgetParams_transformItems, transport = widgetParams.transport;
16342
16529
  if (!agentId && !transport) {
16343
- 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.'));
16344
16531
  }
16345
16532
  var tasksState;
16346
16533
  var suggestions = [];
@@ -16370,12 +16557,12 @@
16370
16557
  // out to send nothing, it has to reconcile the dropped state instead.
16371
16558
  var droppedInnerRender = false;
16372
16559
  var getStateSignature = function getStateSignature(results) {
16373
- var _buildFilters;
16560
+ var _getAgentFilters;
16374
16561
  if (results.queryID) {
16375
16562
  return results.queryID;
16376
16563
  }
16377
16564
  var query = results.query || '';
16378
- 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 : []);
16379
16566
  var hitIds = (results.hits || []).map(function(hit) {
16380
16567
  return hit.objectID;
16381
16568
  }).join(',');
@@ -16388,7 +16575,7 @@
16388
16575
  if (!indexId || !((_instantSearchInstance_renderState = instantSearchInstance.renderState) === null || _instantSearchInstance_renderState === void 0 ? void 0 : _instantSearchInstance_renderState[indexId])) {
16389
16576
  return undefined;
16390
16577
  }
16391
- return instantSearchInstance.renderState[indexId][CHAT_RENDER_STATE_KEY];
16578
+ return instantSearchInstance.renderState[indexId][CHAT_RENDER_STATE_KEY$1];
16392
16579
  };
16393
16580
  var sendToChat = function sendToChat(renderOptions) {
16394
16581
  return function(prompt) {
@@ -16421,7 +16608,7 @@
16421
16608
  if (!results) {
16422
16609
  return undefined;
16423
16610
  }
16424
- var filters = buildFilters(results);
16611
+ var filters = getAgentFilters(results);
16425
16612
  return _object_spread_props(_object_spread({
16426
16613
  query: results.query || ''
16427
16614
  }, filters ? {
@@ -16620,7 +16807,7 @@
16620
16807
  stream: true
16621
16808
  };
16622
16809
  } else {
16623
- 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.'));
16624
16811
  }
16625
16812
  var tasksWidget = connectTasks(handleInnerRender, noop)(tasksParams);
16626
16813
  return {
@@ -16677,7 +16864,7 @@
16677
16864
  if (latestResults && latestRenderOptions) {
16678
16865
  fetchAndRender(latestResults, latestRenderOptions);
16679
16866
  }
16680
- }, DEBOUNCE_MS);
16867
+ }, DEBOUNCE_MS$1);
16681
16868
  }
16682
16869
  renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(renderOptions)), {
16683
16870
  instantSearchInstance: instantSearchInstance
@@ -16704,6 +16891,363 @@
16704
16891
  return useConnector(connectPromptSuggestions, props, additionalWidgetProperties);
16705
16892
  }
16706
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
+
16707
17251
  var withUsage$o = createDocumentationMessageGenerator({
16708
17252
  name: 'chatTrigger',
16709
17253
  connector: true
@@ -16798,11 +17342,11 @@
16798
17342
  return "__ALGOLIA_COMPARISON_".concat(configurationId, "__");
16799
17343
  }
16800
17344
  /**
16801
- * Builds the default comparison message. It names each selected item so the
16802
- * agent can ground the comparison: it re-retrieves the records with
16803
- * `algolia_search_index` and renders them with the builtin
16804
- * `algolia_compare_products` tool. Works with the default shopping assistant
16805
- * 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.
16806
17350
  */ function getDefaultComparisonMessage(items) {
16807
17351
  var labels = items.map(function(item) {
16808
17352
  var _item_name;
@@ -18979,16 +19523,23 @@
18979
19523
  return ruleContexts;
18980
19524
  }
18981
19525
  function applyRuleContexts(event) {
18982
- 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;
18983
19528
  var sharedHelperState = event.state;
18984
19529
  var previousRuleContexts = sharedHelperState.ruleContexts || [];
19530
+ var foreignRuleContexts = previousRuleContexts.filter(function(ruleContext) {
19531
+ return !_this.ownedRuleContexts.includes(ruleContext);
19532
+ });
18985
19533
  var newRuleContexts = getRuleContextsFromTrackedFilters({
18986
19534
  helper: helper,
18987
19535
  sharedHelperState: sharedHelperState,
18988
19536
  trackedFilters: trackedFilters
18989
19537
  });
18990
- var nextRuleContexts = _to_consumable_array(initialRuleContexts).concat(_to_consumable_array(newRuleContexts));
19538
+ var nextRuleContexts = _to_consumable_array(foreignRuleContexts).concat(_to_consumable_array(newRuleContexts));
18991
19539
  var ruleContexts = transformRuleContexts(nextRuleContexts).slice(0, 10);
19540
+ this.ownedRuleContexts = ruleContexts.filter(function(ruleContext) {
19541
+ return !foreignRuleContexts.includes(ruleContext);
19542
+ });
18992
19543
  if (!isEqual(previousRuleContexts, ruleContexts)) {
18993
19544
  helper.overrideStateWithoutTriggeringChangeEvent(_object_spread_props(_object_spread({}, sharedHelperState), {
18994
19545
  ruleContexts: ruleContexts
@@ -19010,21 +19561,19 @@
19010
19561
  }
19011
19562
  });
19012
19563
  var hasTrackedFilters = Object.keys(trackedFilters).length > 0;
19013
- // We store the initial rule contexts applied before creating the widget
19014
- // so that we do not override them with the rules created from `trackedFilters`.
19015
- var initialRuleContexts = [];
19564
+ var tracker;
19016
19565
  var onHelperChange;
19017
19566
  return {
19018
19567
  $$type: 'ais.queryRules',
19019
19568
  init: function init(initOptions) {
19020
19569
  var helper = initOptions.helper, state = initOptions.state, instantSearchInstance = initOptions.instantSearchInstance;
19021
- initialRuleContexts = state.ruleContexts || [];
19022
- onHelperChange = applyRuleContexts.bind({
19570
+ tracker = {
19023
19571
  helper: helper,
19024
- initialRuleContexts: initialRuleContexts,
19025
19572
  trackedFilters: trackedFilters,
19026
- transformRuleContexts: transformRuleContexts
19027
- });
19573
+ transformRuleContexts: transformRuleContexts,
19574
+ ownedRuleContexts: []
19575
+ };
19576
+ onHelperChange = applyRuleContexts.bind(tracker);
19028
19577
  if (hasTrackedFilters) {
19029
19578
  // We need to apply the `ruleContexts` based on the `trackedFilters`
19030
19579
  // before the helper changes state in some cases:
@@ -19070,7 +19619,9 @@
19070
19619
  unmount();
19071
19620
  if (hasTrackedFilters) {
19072
19621
  helper.removeListener('change', onHelperChange);
19073
- return state.setQueryParameter('ruleContexts', initialRuleContexts);
19622
+ return state.setQueryParameter('ruleContexts', (state.ruleContexts || []).filter(function(ruleContext) {
19623
+ return !tracker.ownedRuleContexts.includes(ruleContext);
19624
+ }));
19074
19625
  }
19075
19626
  return state;
19076
19627
  }
@@ -21188,6 +21739,7 @@
21188
21739
  exports.useRange = useRange;
21189
21740
  exports.useRefinementList = useRefinementList;
21190
21741
  exports.useRelatedProducts = useRelatedProducts;
21742
+ exports.useResultCard = useResultCard;
21191
21743
  exports.useSearchBox = useSearchBox;
21192
21744
  exports.useSortBy = useSortBy;
21193
21745
  exports.useStats = useStats;