react-instantsearch 7.48.0 → 7.49.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 7.48.0 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
1
+ /*! React InstantSearch 7.49.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.48.0';
27
+ var version$2 = '7.49.0';
28
28
 
29
29
  function _define_property(obj, key, value) {
30
30
  if (key in obj) {
@@ -9392,7 +9392,7 @@
9392
9392
  });
9393
9393
  }
9394
9394
 
9395
- var version = '4.115.0';
9395
+ var version = '4.116.0';
9396
9396
 
9397
9397
  var ANONYMOUS_TOKEN_COOKIE_KEY = '_ALGOLIA';
9398
9398
  function getCookie(name) {
@@ -14580,6 +14580,7 @@
14580
14580
  var translations = _object_spread({
14581
14581
  messageLabel: 'Message',
14582
14582
  actionsLabel: 'Message actions',
14583
+ toolErrorRetryText: 'Retry',
14583
14584
  reasoningLabel: 'Reasoning'
14584
14585
  }, userTranslations);
14585
14586
  // A message rendered without a connector-attached store falls back to
@@ -14615,7 +14616,11 @@
14615
14616
  var isReasoningStreaming = reasoningParts.some(function(part) {
14616
14617
  return part.isStreaming;
14617
14618
  });
14618
- var showActions = Boolean(actions.length > 0 || ActionsComponent) && status === 'ready';
14619
+ // `status` is the chat's, not the row's: gating every row on it took the
14620
+ // actions off completed answers the moment a follow-up turn started,
14621
+ // reflowing rows the turn hasn't touched. Only the row the turn is
14622
+ // producing waits for the status to settle.
14623
+ var showActions = Boolean(actions.length > 0 || ActionsComponent) && (status === 'ready' || !isCurrentMessage);
14619
14624
  var cssClasses = {
14620
14625
  root: cx('ais-ChatMessage', "ais-ChatMessage--".concat(side), "ais-ChatMessage--".concat(variant), autoHideActions && 'ais-ChatMessage--auto-hide-actions', classNames.root),
14621
14626
  container: cx('ais-ChatMessage-container', classNames.container),
@@ -14674,11 +14679,7 @@
14674
14679
  });
14675
14680
  }
14676
14681
  if (part.type === 'text') {
14677
- // Back-compat shim for sessions started before the move from a
14678
- // `<context>{...}</context>` text part to `metadata.turnContext`.
14679
- // Safe to remove once existing sessionStorage transcripts have
14680
- // rolled over (~2 weeks after release).
14681
- if (part.text.startsWith('<context>') && part.text.endsWith('</context>')) {
14682
+ if (isPartTextEmpty(part)) {
14682
14683
  return null;
14683
14684
  }
14684
14685
  if (TextComponent) {
@@ -14740,7 +14741,30 @@
14740
14741
  return null;
14741
14742
  }
14742
14743
  if (!ToolLayoutComponent) {
14743
- return null;
14744
+ if (toolMessage.state !== 'output-error') {
14745
+ return null;
14746
+ }
14747
+ var messageIndex = messages.findIndex(function(candidate) {
14748
+ return candidate.id === message.id;
14749
+ });
14750
+ var canRetry = tool.retryOnError === true && Boolean(tool.onToolCall) && status === 'ready' && messageIndex >= 0 && !messages.slice(messageIndex + 1).some(function(candidate) {
14751
+ return candidate.role === 'user';
14752
+ });
14753
+ return /*#__PURE__*/ createElement("div", {
14754
+ key: "".concat(message.id, "-").concat(index),
14755
+ className: "ais-ChatMessage-tool ais-ChatMessage-toolError"
14756
+ }, /*#__PURE__*/ createElement("span", {
14757
+ className: "ais-ChatMessage-toolError-message"
14758
+ }, toolMessage.errorText || 'Tool call failed.'), canRetry && /*#__PURE__*/ createElement(Button, {
14759
+ variant: "primary",
14760
+ size: "sm",
14761
+ className: "ais-ChatMessage-toolError-action",
14762
+ onClick: function onClick() {
14763
+ return context.onReload(message.id);
14764
+ }
14765
+ }, /*#__PURE__*/ createElement(ReloadIcon, {
14766
+ createElement: createElement
14767
+ }), translations.toolErrorRetryText));
14744
14768
  }
14745
14769
  var toolSendEvent = tool.sendEvent || function() {};
14746
14770
  var agentId = (_tool_insightsEventContext = tool.insightsEventContext) === null || _tool_insightsEventContext === void 0 ? void 0 : _tool_insightsEventContext.agentId;
@@ -14776,6 +14800,13 @@
14776
14800
  }
14777
14801
  return null;
14778
14802
  }
14803
+ var renderedParts = message.parts.map(renderMessagePart);
14804
+ var hasRenderedParts = renderedParts.some(function(part) {
14805
+ return part != null;
14806
+ });
14807
+ if (!hasRenderedParts && !loaderElement && !(ActionsComponent && showActions) && !FooterComponent) {
14808
+ return suggestionsElement !== null && suggestionsElement !== void 0 ? suggestionsElement : null;
14809
+ }
14779
14810
  return /*#__PURE__*/ createElement("article", _object_spread_props(_object_spread({}, props), {
14780
14811
  className: cx(cssClasses.root, props.className),
14781
14812
  "aria-label": translations.messageLabel
@@ -14787,7 +14818,7 @@
14787
14818
  className: cx(cssClasses.content)
14788
14819
  }, /*#__PURE__*/ createElement("div", {
14789
14820
  className: cx(cssClasses.message)
14790
- }, message.parts.map(renderMessagePart), loaderElement), suggestionsElement, showActions && /*#__PURE__*/ createElement("div", {
14821
+ }, renderedParts, loaderElement), suggestionsElement, showActions && /*#__PURE__*/ createElement("div", {
14791
14822
  className: cx(cssClasses.actions),
14792
14823
  "aria-label": translations.actionsLabel
14793
14824
  }, ActionsComponent ? /*#__PURE__*/ createElement(ActionsComponent, {
@@ -15527,9 +15558,13 @@
15527
15558
  if (status !== 'submitted' && status !== 'streaming') return false;
15528
15559
  if (status === 'submitted') return true;
15529
15560
  var lastMessage = messages[messages.length - 1];
15561
+ // `processStream` sets `streaming` before the `start` chunk pushes the
15562
+ // assistant. Until then the last message is still the user's, and its text
15563
+ // must not be read as the answer or the loader hides and comes back.
15564
+ if ((lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.role) !== 'assistant') return true;
15530
15565
  // Parts that render nothing must not answer for the turn's progress, or the
15531
15566
  // loader flips on a part that changed nothing on screen.
15532
- var lastPart = findLastProgressPart(lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.parts);
15567
+ var lastPart = findLastProgressPart(lastMessage.parts);
15533
15568
  if (!lastPart) return true;
15534
15569
  // An active disclosure carries its own progress affordance, so the loader would
15535
15570
  // double it. Settled reasoning still shows it: the answer has not started.
@@ -17919,6 +17954,26 @@
17919
17954
  var _computedKey$1;
17920
17955
  var defaultGuardrailFallbackResponse = 'Sorry, we are not able to generate a response at the moment.';
17921
17956
  var TOOL_CALL_CANCELLED_ERROR_TEXT = 'The tool call was cancelled: the conversation moved on before a result was provided.';
17957
+ var RESTORED_TOOL_CALL_ERROR_TEXT = 'The page was reloaded before a tool result was received. The operation may have completed.';
17958
+ var RESTORED_TOOL_INPUT_ERROR_TEXT = 'The page was reloaded before the tool input was complete. The operation was not started.';
17959
+ function getTerminalToolState(options) {
17960
+ if (options.state === 'output-error') {
17961
+ return {
17962
+ state: 'output-error',
17963
+ errorText: options.errorText || 'Tool call failed.'
17964
+ };
17965
+ }
17966
+ if (options.state === undefined && 'errorText' in options && typeof options.errorText === 'string') {
17967
+ return {
17968
+ state: 'output-error',
17969
+ errorText: options.errorText || 'Tool call failed.'
17970
+ };
17971
+ }
17972
+ return {
17973
+ state: 'output-available',
17974
+ output: options.output
17975
+ };
17976
+ }
17922
17977
  function getToolName(part) {
17923
17978
  if (part.type === 'dynamic-tool') {
17924
17979
  var _part_toolName;
@@ -17956,7 +18011,7 @@
17956
18011
  */ var AbstractChat = /*#__PURE__*/ function() {
17957
18012
  function AbstractChat(param) {
17958
18013
  var _this = this;
17959
- var _param_generateId = param.generateId, generateId$1 = _param_generateId === void 0 ? generateId : _param_generateId, _param_id = param.id, id = _param_id === void 0 ? generateId$1() : _param_id, transport = param.transport, state = param.state, onError = param.onError, onToolCall = param.onToolCall, onFinish = param.onFinish, onData = param.onData, sendAutomaticallyWhen = param.sendAutomaticallyWhen, shouldRepairToolInput = param.shouldRepairToolInput, resolveCancelledToolOutput = param.resolveCancelledToolOutput;
18014
+ var _param_generateId = param.generateId, generateId$1 = _param_generateId === void 0 ? generateId : _param_generateId, _param_id = param.id, id = _param_id === void 0 ? generateId$1() : _param_id, transport = param.transport, state = param.state, onError = param.onError, onToolCall = param.onToolCall, onFinish = param.onFinish, onData = param.onData, sendAutomaticallyWhen = param.sendAutomaticallyWhen, shouldRepairToolInput = param.shouldRepairToolInput, shouldRepairRestoredPendingToolPart = param.shouldRepairRestoredPendingToolPart, resolveCancelledToolOutput = param.resolveCancelledToolOutput;
17960
18015
  var _this1 = this;
17961
18016
  _class_call_check(this, AbstractChat);
17962
18017
  _define_property(this, "conversationId", void 0);
@@ -17969,11 +18024,13 @@
17969
18024
  _define_property(this, "onData", void 0);
17970
18025
  _define_property(this, "sendAutomaticallyWhen", void 0);
17971
18026
  _define_property(this, "shouldRepairToolInput", void 0);
18027
+ _define_property(this, "shouldRepairRestoredPendingToolPart", void 0);
17972
18028
  _define_property(this, "resolveCancelledToolOutput", void 0);
17973
18029
  _define_property(this, "activeResponse", null);
17974
18030
  _define_property(this, "latestResponse", null);
17975
18031
  _define_property(this, "responsesByToolCallId", new Map());
17976
18032
  _define_property(this, "responseByMessage", new WeakMap());
18033
+ _define_property(this, "restoredToolCalls", void 0);
17977
18034
  // Once tool ownership is discarded, an identifier-only result cannot prove
17978
18035
  // which response generation submitted it. Scoped submissions remain safe.
17979
18036
  _define_property(this, "acceptsIdentifierOnlyToolResults", true);
@@ -18141,7 +18198,7 @@
18141
18198
  warnInvalidGlobalToolResult(options.toolCallId);
18142
18199
  return Promise.resolve();
18143
18200
  }
18144
- if (!_this.commit(options.toolCallId, options.output, message.id, undefined, message)) {
18201
+ if (!_this.commit(options.toolCallId, getTerminalToolState(options), message.id, undefined, message)) {
18145
18202
  return Promise.resolve();
18146
18203
  }
18147
18204
  return _this.continueResponse();
@@ -18188,6 +18245,7 @@
18188
18245
  this.onData = onData;
18189
18246
  this.sendAutomaticallyWhen = sendAutomaticallyWhen;
18190
18247
  this.shouldRepairToolInput = shouldRepairToolInput;
18248
+ this.shouldRepairRestoredPendingToolPart = shouldRepairRestoredPendingToolPart;
18191
18249
  this.resolveCancelledToolOutput = resolveCancelledToolOutput;
18192
18250
  }
18193
18251
  _create_class(AbstractChat, [
@@ -18360,7 +18418,7 @@
18360
18418
  },
18361
18419
  {
18362
18420
  key: "commit",
18363
- value: function commit(toolCallId, output, messageId, response, expectedMessage) {
18421
+ value: function commit(toolCallId, terminalState, messageId, response, expectedMessage) {
18364
18422
  var isTargetPart = function isTargetPart(part) {
18365
18423
  return 'toolCallId' in part && part.toolCallId === toolCallId;
18366
18424
  };
@@ -18375,10 +18433,7 @@
18375
18433
  return false;
18376
18434
  }
18377
18435
  var updatedParts = _to_consumable_array(message.parts);
18378
- updatedParts[partIndex] = withTerminalToolState(part, {
18379
- state: 'output-available',
18380
- output: output
18381
- });
18436
+ updatedParts[partIndex] = withTerminalToolState(part, terminalState);
18382
18437
  var updatedMessage = _object_spread_props(_object_spread({}, message), {
18383
18438
  parts: updatedParts
18384
18439
  });
@@ -18386,6 +18441,60 @@
18386
18441
  return true;
18387
18442
  }
18388
18443
  },
18444
+ {
18445
+ key: "captureRestoredToolCalls",
18446
+ value: function captureRestoredToolCalls() {
18447
+ var restoredToolCalls = new Map();
18448
+ this.messages.forEach(function(message) {
18449
+ message.parts.forEach(function(part) {
18450
+ var _restoredToolCalls_get;
18451
+ if (!isPendingToolPart(part)) return;
18452
+ var toolCallIds = (_restoredToolCalls_get = restoredToolCalls.get(message.id)) !== null && _restoredToolCalls_get !== void 0 ? _restoredToolCalls_get : new Set();
18453
+ toolCallIds.add(part.toolCallId);
18454
+ restoredToolCalls.set(message.id, toolCallIds);
18455
+ });
18456
+ });
18457
+ this.restoredToolCalls = restoredToolCalls;
18458
+ }
18459
+ },
18460
+ {
18461
+ key: "clearRestoredToolCalls",
18462
+ value: function clearRestoredToolCalls() {
18463
+ this.restoredToolCalls = undefined;
18464
+ }
18465
+ },
18466
+ {
18467
+ key: "repairRestoredPendingToolParts",
18468
+ value: function repairRestoredPendingToolParts() {
18469
+ var _this = this;
18470
+ var shouldRepairRestoredPendingToolPart = this.shouldRepairRestoredPendingToolPart;
18471
+ if (!shouldRepairRestoredPendingToolPart) return;
18472
+ this.messages.forEach(function(message, messageIndex) {
18473
+ message.parts.forEach(function(part) {
18474
+ var _this_restoredToolCalls_get;
18475
+ if (!isPendingToolPart(part)) return;
18476
+ if (_this.restoredToolCalls && !((_this_restoredToolCalls_get = _this.restoredToolCalls.get(message.id)) === null || _this_restoredToolCalls_get === void 0 ? void 0 : _this_restoredToolCalls_get.has(part.toolCallId))) {
18477
+ return;
18478
+ }
18479
+ var shouldRepair = false;
18480
+ try {
18481
+ shouldRepair = shouldRepairRestoredPendingToolPart({
18482
+ toolName: getToolName(part),
18483
+ toolCallId: part.toolCallId,
18484
+ input: 'input' in part ? part.input : undefined
18485
+ });
18486
+ } catch (unused) {
18487
+ return;
18488
+ }
18489
+ if (!shouldRepair) return;
18490
+ _this.commit(part.toolCallId, {
18491
+ state: 'output-error',
18492
+ errorText: part.state === 'input-streaming' ? RESTORED_TOOL_INPUT_ERROR_TEXT : RESTORED_TOOL_CALL_ERROR_TEXT
18493
+ }, message.id, undefined, _this.messages[messageIndex]);
18494
+ });
18495
+ });
18496
+ }
18497
+ },
18389
18498
  {
18390
18499
  key: "getOutboundMessages",
18391
18500
  value: /**
@@ -18520,18 +18629,18 @@
18520
18629
  },
18521
18630
  {
18522
18631
  key: "submitToolResult",
18523
- value: function submitToolResult(response, param) {
18632
+ value: function submitToolResult(response, options) {
18524
18633
  var _this = this;
18525
- var toolCallId = param.toolCallId, output = param.output, messageId = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : response === null || response === void 0 ? void 0 : response.messageId, expectedMessage = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : messageId ? this.messages.find(function(message) {
18634
+ var messageId = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : response === null || response === void 0 ? void 0 : response.messageId, expectedMessage = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : messageId ? this.messages.find(function(message) {
18526
18635
  return message.id === messageId;
18527
18636
  }) : undefined;
18528
18637
  if (response === null || response === void 0 ? void 0 : response.isRetired) return Promise.resolve();
18529
18638
  var commitResult = function commitResult() {
18530
- if (!_this.commit(toolCallId, output, messageId, response, expectedMessage)) {
18639
+ if (!_this.commit(options.toolCallId, getTerminalToolState(options), messageId, response, expectedMessage)) {
18531
18640
  return Promise.resolve();
18532
18641
  }
18533
- if (response === null || response === void 0 ? void 0 : response.requiredToolCallIds.has(toolCallId)) {
18534
- response.resolvedToolCallIds.add(toolCallId);
18642
+ if (response === null || response === void 0 ? void 0 : response.requiredToolCallIds.has(options.toolCallId)) {
18643
+ response.resolvedToolCallIds.add(options.toolCallId);
18535
18644
  }
18536
18645
  return _this.continueResponse(response);
18537
18646
  };
@@ -18915,13 +19024,16 @@
18915
19024
  _this.responsesByToolCallId.set(chunk.toolCallId, owners);
18916
19025
  response.pendingToolCallbacks++;
18917
19026
  try {
19027
+ var _this_restoredToolCalls_get, _this_restoredToolCalls;
19028
+ (_this_restoredToolCalls = _this.restoredToolCalls) === null || _this_restoredToolCalls === void 0 ? void 0 : (_this_restoredToolCalls_get = _this_restoredToolCalls.get(currentMessage.id)) === null || _this_restoredToolCalls_get === void 0 ? void 0 : _this_restoredToolCalls_get.delete(chunk.toolCallId);
18918
19029
  var result = _this.onToolCall({
18919
19030
  toolCall: {
18920
19031
  toolName: chunk.toolName,
18921
19032
  toolCallId: chunk.toolCallId,
18922
19033
  input: chunk.input,
18923
19034
  dynamic: 'dynamic' in chunk ? chunk.dynamic : undefined
18924
- }
19035
+ },
19036
+ signal: response.abortController.signal
18925
19037
  }, function(options) {
18926
19038
  return response.isRetired ? Promise.resolve() : _this.submitToolResult(response, options);
18927
19039
  });
@@ -19241,7 +19353,7 @@
19241
19353
  return AbstractChat;
19242
19354
  }();
19243
19355
 
19244
- var _computedKey, _computedKey1, _computedKey2, _computedKey3, _computedKey4, _computedKey5;
19356
+ var _computedKey, _computedKey1, _computedKey2, _computedKey3, _computedKey4, _computedKey5, _computedKey6, _computedKey7;
19245
19357
  var CACHE_KEY = 'instantsearch-chat-initial-messages';
19246
19358
  // Message history is a browser concern, and a server render constructs a Chat
19247
19359
  // too. Reading storage there throws during rendering; the write below only
@@ -19263,8 +19375,8 @@
19263
19375
  }
19264
19376
  });
19265
19377
  }
19266
- _computedKey = '~registerMessagesCallback', _computedKey1 = '~registerStatusCallback', _computedKey2 = '~registerErrorCallback';
19267
- var _computedKey6 = _computedKey, _computedKey7 = _computedKey1, _computedKey8 = _computedKey2;
19378
+ _computedKey = '~persistMessages', _computedKey1 = '~registerMessagesCallback', _computedKey2 = '~registerStatusCallback', _computedKey3 = '~registerErrorCallback';
19379
+ var _computedKey8 = _computedKey, _computedKey9 = _computedKey1, _computedKey10 = _computedKey2, _computedKey11 = _computedKey3;
19268
19380
  var ChatState = /*#__PURE__*/ function() {
19269
19381
  function ChatState() {
19270
19382
  var _this = this;
@@ -19276,6 +19388,7 @@
19276
19388
  _define_property(this, "_messagesCallbacks", new Set());
19277
19389
  _define_property(this, "_statusCallbacks", new Set());
19278
19390
  _define_property(this, "_errorCallbacks", new Set());
19391
+ _define_property(this, "persistenceKey", void 0);
19279
19392
  _define_property(this, "pushMessage", function(message) {
19280
19393
  _this._messages = _this._messages.concat(message);
19281
19394
  _this._callMessagesCallbacks();
@@ -19294,20 +19407,31 @@
19294
19407
  _define_property(this, "snapshot", function(thing) {
19295
19408
  return JSON.parse(JSON.stringify(thing));
19296
19409
  });
19297
- _define_property(this, _computedKey6, function(onChange) {
19410
+ _define_property(this, _computedKey8, function() {
19411
+ var persistenceKey = _this.persistenceKey;
19412
+ if (!persistenceKey) return;
19413
+ safelyRunOnBrowser(function() {
19414
+ try {
19415
+ sessionStorage.setItem(persistenceKey, JSON.stringify(_this.messages));
19416
+ } catch (e) {
19417
+ // Do nothing if sessionStorage is not available or full
19418
+ }
19419
+ });
19420
+ });
19421
+ _define_property(this, _computedKey9, function(onChange) {
19298
19422
  var callback = onChange;
19299
19423
  _this._messagesCallbacks.add(callback);
19300
19424
  return function() {
19301
19425
  _this._messagesCallbacks.delete(callback);
19302
19426
  };
19303
19427
  });
19304
- _define_property(this, _computedKey7, function(onChange) {
19428
+ _define_property(this, _computedKey10, function(onChange) {
19305
19429
  _this._statusCallbacks.add(onChange);
19306
19430
  return function() {
19307
19431
  _this._statusCallbacks.delete(onChange);
19308
19432
  };
19309
19433
  });
19310
- _define_property(this, _computedKey8, function(onChange) {
19434
+ _define_property(this, _computedKey11, function(onChange) {
19311
19435
  _this._errorCallbacks.add(onChange);
19312
19436
  return function() {
19313
19437
  _this._errorCallbacks.delete(onChange);
@@ -19338,19 +19462,14 @@
19338
19462
  if (!persistence) {
19339
19463
  return;
19340
19464
  }
19341
- var saveMessagesInLocalStorage = function saveMessagesInLocalStorage() {
19465
+ this.persistenceKey = CACHE_KEY + (id ? "-".concat(id) : '');
19466
+ var saveMessagesInSessionStorage = function saveMessagesInSessionStorage() {
19342
19467
  if (_this.status === 'ready') {
19343
- safelyRunOnBrowser(function() {
19344
- try {
19345
- sessionStorage.setItem(CACHE_KEY + (id ? "-".concat(id) : ''), JSON.stringify(_this.messages));
19346
- } catch (e) {
19347
- // Do nothing if sessionStorage is not available or full
19348
- }
19349
- });
19468
+ _this['~persistMessages']();
19350
19469
  }
19351
19470
  };
19352
- this['~registerMessagesCallback'](saveMessagesInLocalStorage);
19353
- this['~registerStatusCallback'](saveMessagesInLocalStorage);
19471
+ this['~registerMessagesCallback'](saveMessagesInSessionStorage);
19472
+ this['~registerStatusCallback'](saveMessagesInSessionStorage);
19354
19473
  }
19355
19474
  _create_class(ChatState, [
19356
19475
  {
@@ -19386,8 +19505,8 @@
19386
19505
  ]);
19387
19506
  return ChatState;
19388
19507
  }();
19389
- _computedKey3 = '~registerMessagesCallback', _computedKey4 = '~registerStatusCallback', _computedKey5 = '~registerErrorCallback';
19390
- var _computedKey9 = _computedKey3, _computedKey10 = _computedKey4, _computedKey11 = _computedKey5;
19508
+ _computedKey4 = '~repairRestoredPendingToolParts', _computedKey5 = '~registerMessagesCallback', _computedKey6 = '~registerStatusCallback', _computedKey7 = '~registerErrorCallback';
19509
+ var _computedKey12 = _computedKey4, _computedKey13 = _computedKey5, _computedKey14 = _computedKey6, _computedKey15 = _computedKey7;
19391
19510
  var Chat$1 = /*#__PURE__*/ function(AbstractChat) {
19392
19511
  _inherits(Chat, AbstractChat);
19393
19512
  function Chat(_0) {
@@ -19403,14 +19522,25 @@
19403
19522
  _object_spread_props(_object_spread({}, init), {
19404
19523
  state: state
19405
19524
  })
19406
- ]), _define_property(_this, "_state", void 0), _define_property(_this, _computedKey9, function(onChange) {
19525
+ ]), _define_property(_this, "_state", void 0), _define_property(_this, "hasRestoredMessages", false), _define_property(_this, _computedKey12, function() {
19526
+ if (!_this.hasRestoredMessages) return;
19527
+ _this.hasRestoredMessages = false;
19528
+ _this.repairRestoredPendingToolParts();
19529
+ _this.clearRestoredToolCalls();
19530
+ _this._state['~persistMessages']();
19531
+ }), _define_property(_this, _computedKey13, function(onChange) {
19407
19532
  return _this._state['~registerMessagesCallback'](onChange);
19408
- }), _define_property(_this, _computedKey10, function(onChange) {
19533
+ }), _define_property(_this, _computedKey14, function(onChange) {
19409
19534
  return _this._state['~registerStatusCallback'](onChange);
19410
- }), _define_property(_this, _computedKey11, function(onChange) {
19535
+ }), _define_property(_this, _computedKey15, function(onChange) {
19411
19536
  return _this._state['~registerErrorCallback'](onChange);
19412
19537
  });
19413
19538
  _this._state = state;
19539
+ if (messages === undefined && persistence) {
19540
+ _this.hasRestoredMessages = true;
19541
+ _this.captureRestoredToolCalls();
19542
+ _this.repairRestoredPendingToolParts();
19543
+ }
19414
19544
  return _this;
19415
19545
  }
19416
19546
  return Chat;
@@ -19673,6 +19803,9 @@
19673
19803
  name: 'chat',
19674
19804
  connector: true
19675
19805
  });
19806
+ var DEFAULT_TOOL_TIMEOUT = 20000;
19807
+ var TOOL_TIMEOUT_ERROR_TEXT = 'The tool call timed out before a result was received. The operation may have completed.';
19808
+ var TOOL_ABORTED_ERROR_TEXT = 'The tool call was cancelled before a result was received.';
19676
19809
  var OPEN_STATE_CACHE_KEY = 'instantsearch-chat-open-state';
19677
19810
  function normalizePersistence(persistence, hasCustomChat) {
19678
19811
  if (hasCustomChat) {
@@ -20020,7 +20153,8 @@
20020
20153
  if (!transport) {
20021
20154
  throw new Error(withUsage$q('You need to provide either an `agentId` or a `transport`.'));
20022
20155
  }
20023
- return new Chat$1(_object_spread_props(_object_spread({}, options), {
20156
+ var canRepairRestoredPendingToolParts = !resume;
20157
+ var chat = new Chat$1(_object_spread_props(_object_spread({}, options), {
20024
20158
  persistence: normalizedPersistence.messages,
20025
20159
  sendAutomaticallyWhen: sendAutomaticallyWhen,
20026
20160
  transport: transport,
@@ -20029,6 +20163,11 @@
20029
20163
  if (!tool) return true;
20030
20164
  return Boolean(tool.streamInput);
20031
20165
  },
20166
+ shouldRepairRestoredPendingToolPart: function shouldRepairRestoredPendingToolPart(param) {
20167
+ var toolName = param.toolName;
20168
+ var tool = findTool(toolName, tools);
20169
+ return Boolean(canRepairRestoredPendingToolParts && (tool === null || tool === void 0 ? void 0 : tool.onToolCall) && tool.timeout !== false);
20170
+ },
20032
20171
  resolveCancelledToolOutput: function resolveCancelledToolOutput(param) {
20033
20172
  var toolName = param.toolName, toolCallId = param.toolCallId, input = param.input;
20034
20173
  var _findTool;
@@ -20048,7 +20187,7 @@
20048
20187
  }
20049
20188
  },
20050
20189
  onToolCall: function onToolCall(param, submitToolResult) {
20051
- var toolCall = param.toolCall;
20190
+ var toolCall = param.toolCall, signal = param.signal;
20052
20191
  var tool = findTool(toolCall.toolName, tools);
20053
20192
  if (!tool) {
20054
20193
  return submitToolResult({
@@ -20058,21 +20197,103 @@
20058
20197
  });
20059
20198
  }
20060
20199
  if (tool.onToolCall) {
20061
- var addToolResult = function addToolResult(param) {
20062
- var output = param.output;
20063
- return submitToolResult({
20064
- output: output,
20200
+ var toolAbortController = new AbortController();
20201
+ var timeoutId;
20202
+ var settled = false;
20203
+ var resolveExecution;
20204
+ var rejectExecution;
20205
+ var execution = new Promise(function(resolve, reject) {
20206
+ resolveExecution = resolve;
20207
+ rejectExecution = reject;
20208
+ });
20209
+ var cleanup = function cleanup() {
20210
+ if (timeoutId !== undefined) {
20211
+ clearTimeout(timeoutId);
20212
+ }
20213
+ signal.removeEventListener('abort', abortToolCall);
20214
+ };
20215
+ var addToolResult = function addToolResult(options) {
20216
+ if (settled) return Promise.resolve();
20217
+ settled = true;
20218
+ cleanup();
20219
+ var submission = submitToolResult(_object_spread_props(_object_spread({}, options), {
20065
20220
  tool: toolCall.toolName,
20066
20221
  toolCallId: toolCall.toolCallId
20222
+ }));
20223
+ submission.then(resolveExecution, rejectExecution);
20224
+ return submission;
20225
+ };
20226
+ var addToolError = function addToolError(error) {
20227
+ var errorText = _instanceof(error, Error) ? error.message : String(error !== null && error !== void 0 ? error : '');
20228
+ return addToolResult({
20229
+ state: 'output-error',
20230
+ errorText: errorText || 'Tool call failed.'
20067
20231
  });
20068
20232
  };
20069
- return tool.onToolCall(_object_spread_props(_object_spread({}, toolCall), {
20070
- addToolResult: addToolResult
20071
- }));
20233
+ function abortToolCall() {
20234
+ var submission = addToolResult({
20235
+ state: 'output-error',
20236
+ errorText: TOOL_ABORTED_ERROR_TEXT
20237
+ });
20238
+ toolAbortController.abort();
20239
+ void submission.catch(function() {
20240
+ return undefined;
20241
+ });
20242
+ }
20243
+ if (signal.aborted) {
20244
+ abortToolCall();
20245
+ return execution;
20246
+ }
20247
+ signal.addEventListener('abort', abortToolCall, {
20248
+ once: true
20249
+ });
20250
+ if (tool.timeout !== false) {
20251
+ var _tool_timeout;
20252
+ timeoutId = setTimeout(function() {
20253
+ var submission = addToolResult({
20254
+ state: 'output-error',
20255
+ errorText: TOOL_TIMEOUT_ERROR_TEXT
20256
+ });
20257
+ toolAbortController.abort();
20258
+ void submission.catch(function() {
20259
+ return undefined;
20260
+ });
20261
+ }, (_tool_timeout = tool.timeout) !== null && _tool_timeout !== void 0 ? _tool_timeout : DEFAULT_TOOL_TIMEOUT);
20262
+ }
20263
+ var toolExecution;
20264
+ try {
20265
+ toolExecution = tool.onToolCall(_object_spread_props(_object_spread({}, toolCall), {
20266
+ addToolResult: addToolResult,
20267
+ signal: toolAbortController.signal
20268
+ }));
20269
+ } catch (error) {
20270
+ return addToolError(error);
20271
+ }
20272
+ var returnedExecution = Promise.resolve(toolExecution).catch(addToolError);
20273
+ if (tool.timeout === false) {
20274
+ return Promise.race([
20275
+ returnedExecution.then(cleanup),
20276
+ execution
20277
+ ]);
20278
+ }
20279
+ void returnedExecution.catch(function() {
20280
+ return undefined;
20281
+ });
20282
+ return execution;
20072
20283
  }
20073
20284
  return Promise.resolve();
20074
20285
  }
20075
20286
  }));
20287
+ if (resume) {
20288
+ var resumeStream = chat.resumeStream;
20289
+ chat.resumeStream = function(requestOptions) {
20290
+ return resumeStream(requestOptions).finally(function() {
20291
+ canRepairRestoredPendingToolParts = true;
20292
+ chat['~repairRestoredPendingToolParts']();
20293
+ });
20294
+ };
20295
+ }
20296
+ return chat;
20076
20297
  };
20077
20298
  return {
20078
20299
  $$type: 'ais.chat',
@@ -20903,9 +21124,6 @@
20903
21124
  return null;
20904
21125
  }
20905
21126
  }
20906
- function buildSuggestionMessage(suggestion) {
20907
- return "The user clicked this on-page suggestion. Use the current page context first, then search only if needed.\n\nSuggestion: ".concat(suggestion);
20908
- }
20909
21127
  function stripInternalHitMetadata(hit) {
20910
21128
  var clean = {};
20911
21129
  Object.keys(hit).forEach(function(key) {
@@ -20979,6 +21197,9 @@
20979
21197
  // the widget is unmounted; this guard stops those late callbacks from
20980
21198
  // calling `renderFn` into a torn-down container.
20981
21199
  var disposed = false;
21200
+ // Whether the "is a chat widget mounted on this index?" question has been
21201
+ // settled — see `validateChat`.
21202
+ var hasValidatedChat = false;
20982
21203
  // True between a state-signature change and the debounced refetch that
20983
21204
  // follows it. While pending, the search state has already moved on, so a
20984
21205
  // still-in-flight request from the previous state must not paint its
@@ -21011,21 +21232,26 @@
21011
21232
  };
21012
21233
  var sendToChat = function sendToChat(renderOptions) {
21013
21234
  return function(prompt) {
21014
- var _ref, _ref1;
21235
+ var _ref, _getLatestResults;
21015
21236
  var chatRenderState = getChatRenderState(renderOptions);
21016
21237
  if (!chatRenderState || !chatRenderState.sendMessage) {
21017
21238
  return false;
21018
21239
  }
21019
- var results = (_ref = (_ref1 = latestRenderOptions === null || latestRenderOptions === void 0 ? void 0 : latestRenderOptions.results) !== null && _ref1 !== void 0 ? _ref1 : 'results' in renderOptions ? renderOptions.results : null) !== null && _ref !== void 0 ? _ref : null;
21240
+ var results = (_ref = (_getLatestResults = getLatestResults()) !== null && _getLatestResults !== void 0 ? _getLatestResults : 'results' in renderOptions ? renderOptions.results : null) !== null && _ref !== void 0 ? _ref : null;
21020
21241
  return openChat(chatRenderState, {
21021
- message: buildSuggestionMessage(prompt),
21242
+ message: prompt,
21022
21243
  referer: 'prompt-suggestions-widget',
21023
21244
  turnContext: buildTurnContext(results)
21024
21245
  });
21025
21246
  };
21026
21247
  };
21027
- var resolvePageContext = function resolvePageContext(results) {
21028
- var resolvedContext = typeof context === 'function' ? context() : context;
21248
+ // `context` may be a function, so the option alone says nothing about what
21249
+ // a given fetch actually has to send. Resolve it once per fetch and let
21250
+ // callers read the value rather than the option.
21251
+ var resolveContext = function resolveContext() {
21252
+ return typeof context === 'function' ? context() : context;
21253
+ };
21254
+ var buildPageContext = function buildPageContext(resolvedContext, results) {
21029
21255
  // Explicit context replaces auto-extraction; otherwise derive it from
21030
21256
  // the current search state. The task's server-owned instructions decide
21031
21257
  // how to interpret the shape — the client doesn't label it.
@@ -21044,15 +21270,11 @@
21044
21270
  hitsSample: transformHits(results.hits)
21045
21271
  });
21046
21272
  };
21047
- var buildInput = function buildInput(results) {
21048
- var _resolvePageContext;
21049
- return (_resolvePageContext = resolvePageContext(results)) !== null && _resolvePageContext !== void 0 ? _resolvePageContext : {};
21050
- };
21051
21273
  // The same page context, flattened for the chat handoff: `turnContext` is
21052
21274
  // a flat `Record<string, string>` per the Agent Studio contract, so
21053
21275
  // non-string values (e.g. `hitsSample`) are serialized.
21054
21276
  var buildTurnContext = function buildTurnContext(results) {
21055
- var pageContext = resolvePageContext(results);
21277
+ var pageContext = buildPageContext(resolveContext(), results);
21056
21278
  if (!pageContext) {
21057
21279
  return undefined;
21058
21280
  }
@@ -21068,6 +21290,16 @@
21068
21290
  });
21069
21291
  return entries.length > 0 ? Object.fromEntries(entries) : undefined;
21070
21292
  };
21293
+ // `latestRenderOptions` may be the `init()` options, which carry no
21294
+ // `results` key at all, so the read has to be narrowed rather than
21295
+ // optional-chained.
21296
+ var getLatestResults = function getLatestResults() {
21297
+ var _latestRenderOptions_results;
21298
+ if (!latestRenderOptions || !('results' in latestRenderOptions)) {
21299
+ return null;
21300
+ }
21301
+ return (_latestRenderOptions_results = latestRenderOptions.results) !== null && _latestRenderOptions_results !== void 0 ? _latestRenderOptions_results : null;
21302
+ };
21071
21303
  var renderOutward = function renderOutward(renderOptions) {
21072
21304
  if (disposed) return;
21073
21305
  renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(renderOptions)), {
@@ -21093,13 +21325,20 @@
21093
21325
  };
21094
21326
  var fetchAndRender = function fetchAndRender(results, renderOptions) {
21095
21327
  var _ref = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}, _ref_force = _ref.force, force = _ref_force === void 0 ? false : _ref_force;
21328
+ var _buildPageContext;
21096
21329
  var _results_hits;
21097
21330
  if (disposed || !tasksState) return;
21098
21331
  var hadDroppedInnerRender = droppedInnerRender;
21099
21332
  refetchPending = false;
21100
21333
  droppedInnerRender = false;
21101
- var hasContext = context !== undefined;
21102
- if (!hasContext && !(results === null || results === void 0 ? void 0 : (_results_hits = results.hits) === null || _results_hits === void 0 ? void 0 : _results_hits.length)) {
21334
+ var resolvedContext = resolveContext();
21335
+ var input = (_buildPageContext = buildPageContext(resolvedContext, results)) !== null && _buildPageContext !== void 0 ? _buildPageContext : {};
21336
+ // Two ways there is nothing worth sending: no context for this fetch and
21337
+ // no hits to derive one from, or a context that resolved to something
21338
+ // empty. The first reads the resolved value rather than the option, so
21339
+ // `context: () => undefined` is gated exactly like an omitted `context`.
21340
+ // Sending an empty input spends a model call on no information.
21341
+ if (resolvedContext === undefined && !(results === null || results === void 0 ? void 0 : (_results_hits = results.hits) === null || _results_hits === void 0 ? void 0 : _results_hits.length) || Object.keys(input).length === 0) {
21103
21342
  tasksState.invalidate();
21104
21343
  suggestions = [];
21105
21344
  isLoading = false;
@@ -21108,7 +21347,6 @@
21108
21347
  renderOutward(renderOptions);
21109
21348
  return;
21110
21349
  }
21111
- var input = buildInput(results);
21112
21350
  var payload = taskPayloadKey(input);
21113
21351
  // A moved search state does not always mean a different question: with an
21114
21352
  // explicit `context` the payload ignores the results entirely, so a new
@@ -21133,10 +21371,14 @@
21133
21371
  };
21134
21372
  var refresh = function refresh() {
21135
21373
  if (isLoading) return;
21136
- var results = latestRenderOptions === null || latestRenderOptions === void 0 ? void 0 : latestRenderOptions.results;
21137
- if (!results || !latestRenderOptions) return;
21374
+ if (!latestRenderOptions) return;
21375
+ var results = getLatestResults();
21138
21376
  clearTimeout(debounceTimer);
21139
- lastStateSignature = getStateSignature(results);
21377
+ // Only meaningful with results: without them there is no signature for a
21378
+ // later automatic fetch to compare against.
21379
+ if (results) {
21380
+ lastStateSignature = getStateSignature(results);
21381
+ }
21140
21382
  fetchAndRender(results, latestRenderOptions, {
21141
21383
  force: true
21142
21384
  });
@@ -21150,12 +21392,17 @@
21150
21392
  var chatRenderState = getChatRenderState(renderOptions);
21151
21393
  var isChatBusy$1 = chatRenderState ? !chatRenderState.sendMessage || isChatBusy(chatRenderState) : false;
21152
21394
  var send = sendToChat(renderOptions);
21395
+ // `sendToChat` is withheld only once the mount window has closed: a chat
21396
+ // widget further down a React tree is added after this widget's `init`,
21397
+ // so until `validateChat` runs its absence is not yet conclusive and
21398
+ // withholding the handler would make a valid setup look broken.
21399
+ var canSendToChat = Boolean(chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.sendMessage) || !hasValidatedChat;
21153
21400
  return {
21154
21401
  suggestions: transformed,
21155
21402
  isLoading: isLoading,
21156
21403
  error: error,
21157
- onSuggestionClick: send,
21158
- sendToChat: send,
21404
+ onSuggestionClick: canSendToChat ? send : noop,
21405
+ sendToChat: canSendToChat ? send : undefined,
21159
21406
  refresh: refresh,
21160
21407
  isChatBusy: isChatBusy$1,
21161
21408
  widgetParams: widgetParams
@@ -21178,6 +21425,24 @@
21178
21425
  }
21179
21426
  adoptInnerState(renderState);
21180
21427
  };
21428
+ // Deferred because a chat widget can be registered after this one: React
21429
+ // adds widgets in mount order, so a `chat` further down the tree only
21430
+ // lands after this widget's `init` has run. Until this closes the window,
21431
+ // `sendToChat` is handed out optimistically.
21432
+ var validateChat = defer(function(renderOptions) {
21433
+ var _getChatRenderState;
21434
+ if (hasValidatedChat) return;
21435
+ hasValidatedChat = true;
21436
+ if ((_getChatRenderState = getChatRenderState(renderOptions)) === null || _getChatRenderState === void 0 ? void 0 : _getChatRenderState.sendMessage) return;
21437
+ // A render may already have gone out with the optimistic `sendToChat`;
21438
+ // re-render so the widget layer sees the settled answer. The `init()`
21439
+ // options don't count: nothing has painted yet, so the next render
21440
+ // carries it — and a mount that never renders results shows no pills
21441
+ // to mislead anyone.
21442
+ if (latestRenderOptions && 'results' in latestRenderOptions) {
21443
+ renderOutward(latestRenderOptions);
21444
+ }
21445
+ });
21181
21446
  var tasksParams;
21182
21447
  if (agentId) {
21183
21448
  tasksParams = {
@@ -21200,12 +21465,37 @@
21200
21465
  var tasksWidget = connectTasks(handleInnerRender, noop)(tasksParams);
21201
21466
  return {
21202
21467
  $$type: 'ais.promptSuggestions',
21468
+ // Clicking a suggestion opens the chat, so this widget counts as an
21469
+ // entry point for `connectChat`'s trigger validation.
21470
+ opensChat: true,
21203
21471
  init: function init(initOptions) {
21204
21472
  var instantSearchInstance = initOptions.instantSearchInstance;
21205
21473
  tasksWidget.init(initOptions);
21474
+ // Set after the inner init so its initial no-op render is still
21475
+ // ignored, and before the first outward render so `refresh()` has a
21476
+ // render target even on a mount where `render()` never sees results.
21477
+ latestRenderOptions = initOptions;
21478
+ validateChat(initOptions);
21206
21479
  renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(initOptions)), {
21207
21480
  instantSearchInstance: instantSearchInstance
21208
21481
  }), true);
21482
+ // With an explicit `context` the payload never reads the results, so
21483
+ // waiting for a search is waiting on something the request does not
21484
+ // use — and a page that triggers no search (a `searchFunction` query
21485
+ // gate, or a mount made only for the widgets) would wait forever.
21486
+ // Fetch here instead, after the first render so the `isFirstRender`
21487
+ // one still arrives first.
21488
+ //
21489
+ // There are no results yet by definition, so "is there anything to
21490
+ // send" reduces to "did the context resolve to something". Checking it
21491
+ // here rather than letting `fetchAndRender` reject the call keeps init
21492
+ // out of the "nothing to send" branch, whose `invalidate()` would add
21493
+ // two outward renders to every mount that has no context — leaving the
21494
+ // auto-extracted path, the one that does need results, untouched.
21495
+ var initialContext = resolveContext();
21496
+ if (initialContext && Object.keys(initialContext).length > 0) {
21497
+ fetchAndRender(null, initOptions);
21498
+ }
21209
21499
  },
21210
21500
  render: function render(renderOptions) {
21211
21501
  var results = renderOptions.results, instantSearchInstance = renderOptions.instantSearchInstance;
@@ -21223,8 +21513,9 @@
21223
21513
  error = undefined;
21224
21514
  clearTimeout(debounceTimer);
21225
21515
  debounceTimer = setTimeout(function() {
21226
- if (latestRenderOptions === null || latestRenderOptions === void 0 ? void 0 : latestRenderOptions.results) {
21227
- fetchAndRender(latestRenderOptions.results, latestRenderOptions);
21516
+ var latestResults = getLatestResults();
21517
+ if (latestResults && latestRenderOptions) {
21518
+ fetchAndRender(latestResults, latestRenderOptions);
21228
21519
  }
21229
21520
  }, DEBOUNCE_MS);
21230
21521
  }
@@ -21235,6 +21526,7 @@
21235
21526
  dispose: function dispose(disposeOptions) {
21236
21527
  disposed = true;
21237
21528
  clearTimeout(debounceTimer);
21529
+ validateChat.cancel();
21238
21530
  tasksWidget.dispose(disposeOptions);
21239
21531
  unmountFn();
21240
21532
  },
@@ -27193,9 +27485,16 @@
27193
27485
  }), {
27194
27486
  $$widgetType: 'ais.promptSuggestions'
27195
27487
  }), suggestions = _usePromptSuggestions.suggestions, isLoading = _usePromptSuggestions.isLoading, error = _usePromptSuggestions.error, onSuggestionClick = _usePromptSuggestions.onSuggestionClick, isChatBusy = _usePromptSuggestions.isChatBusy, sendToChat = _usePromptSuggestions.sendToChat;
27488
+ if (!sendToChat && !onSuggestionClickOverride) {
27489
+ throw new Error('No <Chat> widget is mounted on this index, so there is nothing to send a clicked suggestion to. Mount a <Chat> on the same index, or pass `onSuggestionClick` to handle the click yourself.');
27490
+ }
27196
27491
  var handleClick = onSuggestionClickOverride ? function(prompt) {
27197
27492
  return onSuggestionClickOverride(prompt, {
27198
- sendToChat: sendToChat
27493
+ // `sendToChat` is only absent when the override above owns the click,
27494
+ // in which case falling through to the chat is a no-op.
27495
+ sendToChat: sendToChat !== null && sendToChat !== void 0 ? sendToChat : function() {
27496
+ return false;
27497
+ }
27199
27498
  });
27200
27499
  } : onSuggestionClick;
27201
27500
  if (LayoutComponent) {