botium-core 1.13.2 → 1.13.3

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.
Files changed (54) hide show
  1. package/.eslintrc.js +6 -3
  2. package/dist/botium-cjs.js +214 -61
  3. package/dist/botium-cjs.js.map +1 -1
  4. package/dist/botium-es.js +213 -61
  5. package/dist/botium-es.js.map +1 -1
  6. package/package.json +3 -1
  7. package/src/Capabilities.js +2 -1
  8. package/src/containers/plugins/SimpleRestContainer.js +20 -16
  9. package/src/grid/inbound/proxy.js +2 -1
  10. package/src/scripting/Convo.js +16 -10
  11. package/src/scripting/MatchFunctions.js +10 -0
  12. package/src/scripting/ScriptingProvider.js +106 -37
  13. package/src/scripting/logichook/LogicHookConsts.js +1 -1
  14. package/src/scripting/logichook/asserter/WerAsserter.js +59 -0
  15. package/src/scripting/logichook/logichooks/UpdateCustomLogicHook.js +3 -2
  16. package/test/compiler/compilercsv.spec.js +104 -3
  17. package/test/compiler/compilerjson.spec.js +0 -2
  18. package/test/compiler/compilerxlsx.spec.js +1 -1
  19. package/test/compiler/convos/csv/utterances_liveperson2.csv +12 -0
  20. package/test/connectors/simplerest.spec.js +1012 -969
  21. package/test/convo/fillAndApplyScriptingMemory.spec.js +804 -785
  22. package/test/convo/partialconvo.spec.js +345 -339
  23. package/test/driver/capabilities.spec.js +156 -151
  24. package/test/logichooks/hookfromsrc.spec.js +79 -73
  25. package/test/plugins/plugins.spec.js +44 -42
  26. package/test/scripting/asserters/buttonsAsserter.spec.js +257 -240
  27. package/test/scripting/asserters/cardsAsserter.spec.js +214 -212
  28. package/test/scripting/asserters/convos/wer_threshold_nok.yml +7 -0
  29. package/test/scripting/asserters/convos/wer_threshold_ok.yml +7 -0
  30. package/test/scripting/asserters/intentConfidenceAsserter.spec.js +34 -35
  31. package/test/scripting/asserters/jsonpathAsserter.spec.js +307 -308
  32. package/test/scripting/asserters/mediaAsserter.spec.js +236 -234
  33. package/test/scripting/asserters/werAsserter.spec.js +51 -0
  34. package/test/scripting/logichooks/setClearScriptingMemory.spec.js +202 -192
  35. package/test/scripting/matching/matchingmode.spec.js +306 -258
  36. package/test/scripting/scriptingProvider.spec.js +666 -633
  37. package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +299 -281
  38. package/test/scripting/scriptingmemory/useScriptingMemoryForAssertion.spec.js +94 -80
  39. package/test/scripting/userinputs/defaultUserInputs.spec.js +233 -127
  40. package/test/scripting/userinputs/mediaInputConvos.spec.js +409 -403
  41. package/test/scripting/utteranceexpansion/associateByIndex.spec.js +259 -0
  42. package/test/scripting/utteranceexpansion/convos/associate_utterances_by_index.json +33 -0
  43. package/test/scripting/utteranceexpansion/convos/media.convo.txt +19 -0
  44. package/test/scripting/utteranceexpansion/files/step0voice0.wav +0 -0
  45. package/test/scripting/utteranceexpansion/files/step0voice1.wav +0 -0
  46. package/test/scripting/utteranceexpansion/files/step0voice2.wav +0 -0
  47. package/test/scripting/utteranceexpansion/files/step1voice0.wav +0 -0
  48. package/test/scripting/utteranceexpansion/files/step2voice0.wav +0 -0
  49. package/test/scripting/utteranceexpansion/files/step2voice1.wav +0 -0
  50. package/test/scripting/utteranceexpansion/files/step2voice2.wav +0 -0
  51. package/test/scripting/utteranceexpansion/files/step2voice4.wav +0 -0
  52. package/test/scripting/utteranceexpansion/files/step2voice5.wav +0 -0
  53. package/test/security/allowUnsafe.spec.js +274 -268
  54. package/test/utils.spec.js +40 -38
package/.eslintrc.js CHANGED
@@ -1,6 +1,9 @@
1
1
  module.exports = {
2
- "extends": "standard",
2
+ "extends": ["standard", "plugin:mocha/recommended"],
3
3
  "env": {
4
4
  "mocha": true
5
- }
6
- };
5
+ },
6
+ rules: {
7
+ 'mocha/no-setup-in-describe': 'warn',
8
+ },
9
+ }
@@ -24,6 +24,7 @@ var promise = require('promise.allsettled');
24
24
  var uuid = require('uuid');
25
25
  var jsonpath = require('jsonpath');
26
26
  var isJson$1 = require('is-json');
27
+ var wordErrorRate = require('word-error-rate');
27
28
  var esprima = require('esprima');
28
29
  var markdownIt = require('markdown-it');
29
30
  var xlsx = require('xlsx');
@@ -63,6 +64,7 @@ var promise__default = /*#__PURE__*/_interopDefaultLegacy(promise);
63
64
  var uuid__default = /*#__PURE__*/_interopDefaultLegacy(uuid);
64
65
  var jsonpath__default = /*#__PURE__*/_interopDefaultLegacy(jsonpath);
65
66
  var isJson__default = /*#__PURE__*/_interopDefaultLegacy(isJson$1);
67
+ var wordErrorRate__default = /*#__PURE__*/_interopDefaultLegacy(wordErrorRate);
66
68
  var esprima__default = /*#__PURE__*/_interopDefaultLegacy(esprima);
67
69
  var markdownIt__default = /*#__PURE__*/_interopDefaultLegacy(markdownIt);
68
70
  var xlsx__default = /*#__PURE__*/_interopDefaultLegacy(xlsx);
@@ -79,7 +81,7 @@ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
79
81
  var bodyParser__default = /*#__PURE__*/_interopDefaultLegacy(bodyParser);
80
82
 
81
83
  var name = "botium-core";
82
- var version$1 = "1.13.2";
84
+ var version$1 = "1.13.3";
83
85
  var description = "The Selenium for Chatbots";
84
86
  var main = "index.js";
85
87
  var module$1 = "dist/botium-es.js";
@@ -145,6 +147,7 @@ var dependencies = {
145
147
  "swagger-ui-express": "^4.4.0",
146
148
  uuid: "^8.3.2",
147
149
  vm2: "^3.9.10",
150
+ "word-error-rate": "0.0.7",
148
151
  "write-yaml": "^1.0.0",
149
152
  xlsx: "^0.18.5",
150
153
  xregexp: "^5.1.1",
@@ -161,6 +164,7 @@ var devDependencies = {
161
164
  eslint: "^8.19.0",
162
165
  "eslint-config-standard": "^17.0.0",
163
166
  "eslint-plugin-import": "^2.26.0",
167
+ "eslint-plugin-mocha": "^10.1.0",
164
168
  "eslint-plugin-n": "^15.2.4",
165
169
  "eslint-plugin-promise": "^6.0.0",
166
170
  "eslint-plugin-standard": "^4.1.0",
@@ -340,8 +344,9 @@ var Capabilities = {
340
344
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
341
345
  SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS: 'SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS',
342
346
  SCRIPTING_FORCE_BOT_CONSUMED: 'SCRIPTING_FORCE_BOT_CONSUMED',
343
- // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
347
+ // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase, wer
344
348
  SCRIPTING_MATCHING_MODE: 'SCRIPTING_MATCHING_MODE',
349
+ SCRIPTING_MATCHING_MODE_ARGS: 'SCRIPTING_MATCHING_MODE_ARGS',
345
350
  // all, first, random
346
351
  SCRIPTING_UTTEXPANSION_MODE: 'SCRIPTING_UTTEXPANSION_MODE',
347
352
  SCRIPTING_UTTEXPANSION_RANDOM_COUNT: 'SCRIPTING_UTTEXPANSION_RANDOM_COUNT',
@@ -495,6 +500,7 @@ Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS;
495
500
  Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS;
496
501
  Capabilities.SCRIPTING_FORCE_BOT_CONSUMED;
497
502
  Capabilities.SCRIPTING_MATCHING_MODE;
503
+ Capabilities.SCRIPTING_MATCHING_MODE_ARGS;
498
504
  Capabilities.SCRIPTING_UTTEXPANSION_MODE;
499
505
  Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT;
500
506
  Capabilities.SCRIPTING_UTTEXPANSION_INCOMPREHENSION;
@@ -1180,6 +1186,9 @@ var LogicHookConsts = {
1180
1186
  }, {
1181
1187
  name: 'TEXT_IC',
1182
1188
  className: 'TextEqualsAnyICAsserter'
1189
+ }, {
1190
+ name: 'TEXT_WER',
1191
+ className: 'WerAsserter'
1183
1192
  }, {
1184
1193
  name: 'BOT_CONSUMED',
1185
1194
  className: 'BotRepliesConsumedAsserter'
@@ -2882,7 +2891,7 @@ class ConvoStep$1 {
2882
2891
  }
2883
2892
 
2884
2893
  toString() {
2885
- return (this.stepTag ? this.stepTag + ': ' : '') + '#' + this.sender + ' - ' + (this.optional ? '?' : '') + (this.not ? '!' : '') + (this.messageText || '') + (this.asserters && this.asserters.length > 0 ? ' ' + this.asserters.map(a => a.toString()).join(' ASS: ') : '') + (this.logicHooks && this.logicHooks.length > 0 ? ' ' + this.logicHooks.map(l => l.toString()).join(' LH: ') : '') + (this.userInputs && this.userInputs.length > 0 ? ' ' + this.userInputs.map(u => u.toString()).join(' UI: ') : '');
2894
+ return (this.stepTag ? this.stepTag + ': ' : '') + '#' + this.sender + ' - ' + (this.optional ? '?' : '') + (this.not ? '!' : '') + (this.messageText || '') + (this.asserters && this.asserters.length > 0 ? ' ' + this.asserters.map(a => a.toString()).join(' ') : '') + (this.logicHooks && this.logicHooks.length > 0 ? ' ' + this.logicHooks.map(l => l.toString()).join(' ') : '') + (this.userInputs && this.userInputs.length > 0 ? ' ' + this.userInputs.map(u => u.toString()).join(' ') : '');
2886
2895
  }
2887
2896
 
2888
2897
  }
@@ -2917,6 +2926,16 @@ class Transcript {
2917
2926
 
2918
2927
  }
2919
2928
 
2929
+ class TranscriptAttachment {
2930
+ constructor(fromJson = {}) {
2931
+ this.name = fromJson.name;
2932
+ this.mimeType = fromJson.mimeType;
2933
+ this.base64 = fromJson.base64;
2934
+ this.href = fromJson.href;
2935
+ }
2936
+
2937
+ }
2938
+
2920
2939
  class TranscriptStep {
2921
2940
  constructor({
2922
2941
  expected,
@@ -3697,9 +3716,16 @@ class Convo$6 {
3697
3716
  }
3698
3717
 
3699
3718
  var Convo_1 = {
3700
- ConvoHeader: ConvoHeader$1,
3701
3719
  Convo: Convo$6,
3702
- ConvoStep: ConvoStep$1
3720
+ ConvoHeader: ConvoHeader$1,
3721
+ ConvoStep: ConvoStep$1,
3722
+ ConvoStepAssert,
3723
+ ConvoStepLogicHook,
3724
+ ConvoStepUserInput,
3725
+ Transcript,
3726
+ TranscriptAttachment,
3727
+ TranscriptStep,
3728
+ TranscriptError
3703
3729
  };
3704
3730
 
3705
3731
  const {
@@ -3787,6 +3813,12 @@ const equals = ignoreCase => (botresponse, utterance) => {
3787
3813
  return botresponse === utterance;
3788
3814
  };
3789
3815
 
3816
+ const wer = () => (botresponse, utterance, args) => {
3817
+ botresponse = _normalize(botresponse || '');
3818
+ utterance = toString(utterance || '');
3819
+ return wordErrorRate__default["default"].wordErrorRate(botresponse, utterance) <= args[0];
3820
+ };
3821
+
3790
3822
  const getMatchFunction$1 = matchingMode => {
3791
3823
  if (matchingMode === 'regexp' || matchingMode === 'regexpIgnoreCase') {
3792
3824
  return regexp(matchingMode === 'regexpIgnoreCase');
@@ -3798,6 +3830,8 @@ const getMatchFunction$1 = matchingMode => {
3798
3830
  return include(matchingMode === 'includeIgnoreCase' || matchingMode === 'includeLowerCase');
3799
3831
  } else if (matchingMode === 'equals' || matchingMode === 'equalsIgnoreCase') {
3800
3832
  return equals(matchingMode === 'equalsIgnoreCase');
3833
+ } else if (matchingMode === 'wer') {
3834
+ return wer();
3801
3835
  } else {
3802
3836
  return equals(false);
3803
3837
  }
@@ -3809,6 +3843,7 @@ var MatchFunctions = {
3809
3843
  wildcardExact,
3810
3844
  include,
3811
3845
  equals,
3846
+ wer,
3812
3847
  getMatchFunction: getMatchFunction$1
3813
3848
  };
3814
3849
 
@@ -6173,7 +6208,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
6173
6208
 
6174
6209
  debug$9(`assertBotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} = ${tomatch} ...`);
6175
6210
 
6176
- const found = lodash__default["default"].find(tomatch, utt => this.matchFn(botresponse, utt));
6211
+ const found = lodash__default["default"].find(tomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
6212
+
6213
+ const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'WerAsserter' : 'TextMatchAsserter';
6177
6214
 
6178
6215
  if (lodash__default["default"].isNil(found)) {
6179
6216
  let message = `${stepTag}: Bot response `;
@@ -6184,7 +6221,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6184
6221
  message += `${tomatch.map(e => e ? '"' + e + '"' : '<any response>').join(', ')}`;
6185
6222
  throw new BotiumError$2(message, {
6186
6223
  type: 'asserter',
6187
- source: 'TextMatchAsserter',
6224
+ source: asserterType,
6188
6225
  context: {
6189
6226
  stepTag
6190
6227
  },
@@ -6203,7 +6240,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
6203
6240
 
6204
6241
  debug$9(`assertBotNotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} != ${nottomatch} ...`);
6205
6242
 
6206
- const found = lodash__default["default"].find(nottomatch, utt => this.matchFn(botresponse, utt));
6243
+ const found = lodash__default["default"].find(nottomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
6244
+
6245
+ const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'WerAsserter' : 'TextMatchAsserter';
6207
6246
 
6208
6247
  if (!lodash__default["default"].isNil(found)) {
6209
6248
  let message = `${stepTag}: Bot response `;
@@ -6214,7 +6253,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6214
6253
  message += `${nottomatch.map(e => e ? '"' + e + '"' : '<any response>').join(', ')}`;
6215
6254
  throw new BotiumError$2(message, {
6216
6255
  type: 'asserter',
6217
- source: 'TextMatchAsserter',
6256
+ source: asserterType,
6218
6257
  context: {
6219
6258
  stepTag
6220
6259
  },
@@ -6461,7 +6500,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6461
6500
  }
6462
6501
 
6463
6502
  Match(botresponse, utterance) {
6464
- return this.matchFn(botresponse, utterance);
6503
+ return this.matchFn(botresponse, utterance, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]);
6465
6504
  }
6466
6505
 
6467
6506
  Compile(scriptBuffer, scriptFormat, scriptType) {
@@ -6981,11 +7020,12 @@ var ScriptingProvider_1 = class ScriptingProvider {
6981
7020
  * @param currentConvo
6982
7021
  * @param convoStepIndex
6983
7022
  * @param convoStepsStack list of ConvoSteps
7023
+ * @param context {width: }
6984
7024
  * @private
6985
7025
  */
6986
7026
 
6987
7027
 
6988
- _expandConvo(expandedConvos, currentConvo, convoStepIndex = 0, convoStepsStack = []) {
7028
+ _expandConvo(expandedConvos, currentConvo, convoStepIndex = 0, convoStepsStack = [], context = {}) {
6989
7029
  const utterancePostfix = (lineTag, uttOrUserInput) => {
6990
7030
  const naming = this.caps[Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE] || Defaults$1.capabilities[Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE];
6991
7031
 
@@ -7012,7 +7052,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
7012
7052
  const currentStepsStack = convoStepsStack.slice();
7013
7053
  currentStepsStack.push(lodash__default["default"].cloneDeep(currentStep));
7014
7054
 
7015
- this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack);
7055
+ this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack, context);
7016
7056
  } else if (currentStep.sender === 'me') {
7017
7057
  let useUnexpanded = true;
7018
7058
 
@@ -7033,39 +7073,87 @@ var ScriptingProvider_1 = class ScriptingProvider {
7033
7073
 
7034
7074
  if (this.utterances[uttName]) {
7035
7075
  const allutterances = this.utterances[uttName].utterances;
7036
- let sampleutterances = allutterances;
7037
-
7038
- if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
7039
- sampleutterances = [allutterances[0]];
7040
- } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
7041
- sampleutterances = allutterances.map(x => ({
7042
- x,
7043
- r: Math.random()
7044
- })).sort((a, b) => a.r - b.r).map(a => a.x).slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]);
7045
- }
7046
7076
 
7047
- sampleutterances.forEach((utt, index) => {
7048
- const lineTag = `${index + 1}`.padStart(`${sampleutterances.length}`.length, '0');
7077
+ const processSampleUtterances = (sampleutterances, myContext) => {
7078
+ sampleutterances.forEach((utt, index) => {
7079
+ processSampleUtterance(utt, sampleutterances.length, index, Object.assign({
7080
+ indexExpansionModeIndex: index
7081
+ }, myContext || context));
7082
+ });
7083
+ };
7084
+
7085
+ const processSampleUtterance = (sampleutterance, length, index, myContext) => {
7086
+ const lineTag = `${index + 1}`.padStart(`${length}`.length, '0');
7049
7087
  const currentStepsStack = convoStepsStack.slice();
7050
7088
 
7051
7089
  if (uttArgs) {
7052
- utt = util__default["default"].format(utt, ...uttArgs);
7090
+ sampleutterance = util__default["default"].format(sampleutterance, ...uttArgs);
7053
7091
  }
7054
7092
 
7055
7093
  currentStepsStack.push(Object.assign(lodash__default["default"].cloneDeep(currentStep), {
7056
- messageText: utt
7094
+ messageText: sampleutterance
7057
7095
  }));
7058
7096
 
7059
7097
  const currentConvoLabeled = lodash__default["default"].cloneDeep(currentConvo);
7060
7098
 
7061
7099
  Object.assign(currentConvoLabeled.header, {
7062
- name: `${currentConvo.header.name}/${uttName}-${utterancePostfix(lineTag, utt)}`
7100
+ name: `${currentConvo.header.name}/${uttName}-${utterancePostfix(lineTag, sampleutterance)}`
7063
7101
  });
7064
7102
  if (!currentConvoLabeled.sourceTag) currentConvoLabeled.sourceTag = {};
7065
7103
  if (!currentConvoLabeled.sourceTag.origConvoName) currentConvoLabeled.sourceTag.origConvoName = currentConvo.header.name;
7066
7104
 
7067
- this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack);
7068
- });
7105
+ this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack, myContext || context);
7106
+ };
7107
+
7108
+ if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
7109
+ if (lodash__default["default"].isNil(context.indexExpansionModeWidth)) {
7110
+ // executed for the first found utterance
7111
+ processSampleUtterances(allutterances, Object.assign({}, context, {
7112
+ indexExpansionModeWidth: allutterances.length
7113
+ }));
7114
+ } else {
7115
+ if (lodash__default["default"].isNil(context.indexExpansionModeIndex)) {
7116
+ throw new Error('indexExpansionModeIndex must be set!');
7117
+ } // executing the current 'thread', if current utterance has no example to current index, fallback to the last one
7118
+
7119
+
7120
+ const localIndex = Math.min(context.indexExpansionModeIndex, allutterances.length - 1);
7121
+
7122
+ if (localIndex < context.indexExpansionModeIndex && context.indexExpansionModeIndex === context.indexExpansionModeWidth - 1) {
7123
+ debug$9(`While expanding convos by index found in utterance "${uttName}" less examples (${allutterances.length}) as expected (${context.indexExpansionModeWidth})`);
7124
+ }
7125
+
7126
+ const myContext = Object.assign({}, context, {
7127
+ indexExpansionModeWidth: Math.max(allutterances.length, context.indexExpansionModeWidth)
7128
+ });
7129
+ processSampleUtterance(allutterances[localIndex], allutterances.length, localIndex, myContext);
7130
+
7131
+ if (allutterances.length > context.indexExpansionModeWidth && context.indexExpansionModeIndex + 1 === context.indexExpansionModeWidth) {
7132
+ debug$9(`While expanding convos by index found in utterance "${uttName}" more examples (${allutterances.length}) as expected (${context.indexExpansionModeWidth})`);
7133
+
7134
+ for (let i = context.indexExpansionModeWidth; i < allutterances.length; i++) {
7135
+ // if we found a utterance with more examples as any utterances before, we have to start new 'thread'
7136
+ const myContext = Object.assign({}, context, {
7137
+ indexExpansionModeWidth: allutterances.length,
7138
+ indexExpansionModeIndex: i
7139
+ });
7140
+ processSampleUtterance(allutterances[i], allutterances.length, i, myContext);
7141
+ }
7142
+ }
7143
+ }
7144
+ } else {
7145
+ if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
7146
+ processSampleUtterances([allutterances[0]]);
7147
+ } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
7148
+ processSampleUtterances(allutterances.map(x => ({
7149
+ x,
7150
+ r: Math.random()
7151
+ })).sort((a, b) => a.r - b.r).map(a => a.x).slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]));
7152
+ } else {
7153
+ processSampleUtterances(allutterances);
7154
+ }
7155
+ }
7156
+
7069
7157
  useUnexpanded = false;
7070
7158
  }
7071
7159
  }
@@ -7082,19 +7170,17 @@ var ScriptingProvider_1 = class ScriptingProvider {
7082
7170
  });
7083
7171
 
7084
7172
  if (expandedUserInputs && expandedUserInputs.length > 0) {
7085
- let sampleinputs = expandedUserInputs;
7086
-
7087
- if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
7088
- sampleinputs = [expandedUserInputs[0]];
7089
- } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
7090
- sampleinputs = expandedUserInputs.map(x => ({
7091
- x,
7092
- r: Math.random()
7093
- })).sort((a, b) => a.r - b.r).map(a => a.x).slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]);
7094
- }
7173
+ // let sampleinputs = expandedUserInputs
7174
+ const processSampleInputs = (sampleinputs, myContext, uiIndex) => {
7175
+ sampleinputs.forEach((input, index) => {
7176
+ processSampleInput(input, sampleinputs.length, index, Object.assign({
7177
+ indexExpansionModeIndex: index
7178
+ }, myContext || context), uiIndex);
7179
+ });
7180
+ };
7095
7181
 
7096
- sampleinputs.forEach((sampleinput, index) => {
7097
- const lineTag = `${index + 1}`.padStart(`${sampleinputs.length}`.length, '0');
7182
+ const processSampleInput = (sampleinput, length, index, myContext, uiIndex) => {
7183
+ const lineTag = `${index + 1}`.padStart(`${length}`.length, '0');
7098
7184
  const currentStepsStack = convoStepsStack.slice();
7099
7185
 
7100
7186
  const currentStepMod = lodash__default["default"].cloneDeep(currentStep);
@@ -7108,8 +7194,56 @@ var ScriptingProvider_1 = class ScriptingProvider {
7108
7194
  name: `${currentConvo.header.name}/${ui.name}-${utterancePostfix(lineTag, sampleinput.args && sampleinput.args.length ? sampleinput.args.join(', ') : 'no-args')}`
7109
7195
  });
7110
7196
 
7111
- this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack);
7112
- });
7197
+ this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack, myContext || context);
7198
+ };
7199
+
7200
+ if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
7201
+ if (lodash__default["default"].isNil(context.indexExpansionModeWidth)) {
7202
+ processSampleInputs(expandedUserInputs, Object.assign({}, context, {
7203
+ indexExpansionModeWidth: expandedUserInputs.length
7204
+ }), uiIndex);
7205
+ } else {
7206
+ if (lodash__default["default"].isNil(context.indexExpansionModeIndex)) {
7207
+ throw new Error('indexExpansionModeIndex must be set!');
7208
+ } // executing the current 'thread', if current utterance has no example to current index, fallback to the last one
7209
+
7210
+
7211
+ const localIndex = Math.min(context.indexExpansionModeIndex, expandedUserInputs.length - 1);
7212
+
7213
+ if (localIndex < context.indexExpansionModeIndex && context.indexExpansionModeIndex === context.indexExpansionModeWidth - 1) {
7214
+ debug$9(`While expanding convos by index found user input "${ui.name}, ${ui.args}" less examples (${expandedUserInputs.length}) as expected (${context.indexExpansionModeWidth})`);
7215
+ }
7216
+
7217
+ const myContext = Object.assign({}, context, {
7218
+ indexExpansionModeWidth: Math.max(expandedUserInputs.length, context.indexExpansionModeWidth)
7219
+ });
7220
+ processSampleInput(expandedUserInputs[localIndex], expandedUserInputs.length, localIndex, myContext, uiIndex);
7221
+
7222
+ if (expandedUserInputs.length > context.indexExpansionModeWidth && context.indexExpansionModeIndex + 1 === context.indexExpansionModeWidth) {
7223
+ debug$9(`While expanding convos by index found user input "${ui.name}, ${ui.args}" more examples (${expandedUserInputs.length}) as expected (${context.indexExpansionModeWidth})`);
7224
+
7225
+ for (let i = context.indexExpansionModeWidth; i < expandedUserInputs.length; i++) {
7226
+ const myContext = Object.assign({}, context, {
7227
+ indexExpansionModeWidth: expandedUserInputs.length,
7228
+ indexExpansionModeIndex: i
7229
+ });
7230
+ processSampleInput(expandedUserInputs[i], expandedUserInputs.length, i, myContext, uiIndex);
7231
+ }
7232
+ }
7233
+ }
7234
+ } else {
7235
+ if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
7236
+ processSampleInputs([expandedUserInputs[0]], context, uiIndex);
7237
+ } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
7238
+ processSampleInputs(expandedUserInputs.map(x => ({
7239
+ x,
7240
+ r: Math.random()
7241
+ })).sort((a, b) => a.r - b.r).map(a => a.x).slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]), context, uiIndex);
7242
+ } else {
7243
+ processSampleInputs(expandedUserInputs, context, uiIndex);
7244
+ }
7245
+ }
7246
+
7113
7247
  useUnexpanded = false;
7114
7248
  }
7115
7249
  }
@@ -7120,7 +7254,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
7120
7254
  const currentStepsStack = convoStepsStack.slice();
7121
7255
  currentStepsStack.push(lodash__default["default"].cloneDeep(currentStep));
7122
7256
 
7123
- this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack);
7257
+ this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack, context);
7124
7258
  }
7125
7259
  }
7126
7260
  } else {
@@ -8176,7 +8310,8 @@ const setupEndpoints = ({
8176
8310
  processEvent({
8177
8311
  originalUrl: req.originalUrl,
8178
8312
  originalMethod: req.method,
8179
- body: req.body
8313
+ body: req.body,
8314
+ headers: req.headers
8180
8315
  });
8181
8316
  res.status(200).json({}).end();
8182
8317
  } else {
@@ -8281,7 +8416,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8281
8416
  });
8282
8417
 
8283
8418
  for (const event of sortedEvents) {
8284
- setTimeout(() => this._processBodyAsync(event.body, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0);
8419
+ setTimeout(() => this._processBodyAsync(event.body, event.headers, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0);
8285
8420
  }
8286
8421
  }, debounceTimeout);
8287
8422
  }
@@ -8376,12 +8511,15 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8376
8511
  this._startPolling().then(() => startPollingComplete()).catch(startPollingComplete);
8377
8512
  }, pingComplete => {
8378
8513
  if (this.caps[Capabilities.SIMPLEREST_PING_URL]) {
8379
- this._makeCall('SIMPLEREST_PING').then(body => {
8514
+ this._makeCall('SIMPLEREST_PING').then(({
8515
+ body,
8516
+ headers
8517
+ }) => {
8380
8518
  if (this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT] || this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE]) {
8381
8519
  return this._parseResponseBody(body).then(body => {
8382
8520
  if (body) {
8383
8521
  debug$4(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} returned JSON response: ${Utils.shortenJsonString(body)}`);
8384
- return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT]);
8522
+ return this._processBodyAsync(body, headers, !!this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT]);
8385
8523
  } else {
8386
8524
  debug$4(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} didn't return JSON response, ignoring it.`);
8387
8525
  }
@@ -8409,12 +8547,15 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8409
8547
  this.processInbound = true;
8410
8548
 
8411
8549
  if (this.caps[Capabilities.SIMPLEREST_START_URL]) {
8412
- this._makeCall('SIMPLEREST_START').then(body => {
8550
+ this._makeCall('SIMPLEREST_START').then(({
8551
+ body,
8552
+ headers
8553
+ }) => {
8413
8554
  if (this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT] || this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE]) {
8414
8555
  return this._parseResponseBody(body).then(body => {
8415
8556
  if (body) {
8416
8557
  debug$4(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} returned JSON response: ${Utils.shortenJsonString(body)}`);
8417
- return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT]);
8558
+ return this._processBodyAsync(body, headers, !!this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT]);
8418
8559
  } else {
8419
8560
  debug$4(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} didn't return JSON response, ignoring it.`);
8420
8561
  }
@@ -8466,10 +8607,10 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8466
8607
  } // Separated just for better module testing
8467
8608
 
8468
8609
 
8469
- async _processBodyAsync(body, isFromUser, updateContext) {
8610
+ async _processBodyAsync(body, headers, isFromUser, updateContext) {
8470
8611
  const p = async () => {
8471
8612
  try {
8472
- const results = await this._processBodyAsyncImpl(body, isFromUser, updateContext);
8613
+ const results = await this._processBodyAsyncImpl(body, headers, isFromUser, updateContext);
8473
8614
 
8474
8615
  if (results) {
8475
8616
  for (const result of results) {
@@ -8500,7 +8641,12 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8500
8641
  } // Separated just for better module testing
8501
8642
 
8502
8643
 
8503
- async _processBodyAsyncImpl(body, isFromUser, updateContext) {
8644
+ async _processBodyAsyncImpl(body, headers, isFromUser, updateContext) {
8645
+ this.view.response = {
8646
+ body,
8647
+ headers
8648
+ };
8649
+
8504
8650
  if (updateContext) {
8505
8651
  const mergeMode = this.caps[Capabilities.SIMPLEREST_CONTEXT_MERGE_OR_REPLACE];
8506
8652
  const jsonPathsContext = getAllCapValues(Capabilities.SIMPLEREST_CONTEXT_JSONPATH, this.caps);
@@ -8710,7 +8856,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8710
8856
  }
8711
8857
 
8712
8858
  if (body) {
8713
- debug$4(`got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}`);
8859
+ debug$4(`got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8714
8860
 
8715
8861
  this._storeCookiesFromResponse(response);
8716
8862
 
@@ -8726,7 +8872,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8726
8872
  }
8727
8873
 
8728
8874
  if (body) {
8729
- this._processBodyAsync(body, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue());
8875
+ this._processBodyAsync(body, response.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue());
8730
8876
  } else {
8731
8877
  debug$4('ignoring response body (no string and no JSON object)');
8732
8878
  resolve(this);
@@ -8890,10 +9036,13 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8890
9036
  this._storeCookiesFromResponse(response);
8891
9037
 
8892
9038
  if (debug$4.enabled && body) {
8893
- debug$4(Utils.shortenJsonString(body));
9039
+ debug$4(`body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8894
9040
  }
8895
9041
 
8896
- return body;
9042
+ return {
9043
+ body,
9044
+ headers: response.headers
9045
+ };
8897
9046
  }
8898
9047
  }
8899
9048
  }
@@ -8937,7 +9086,11 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8937
9086
  try {
8938
9087
  return JSON.parse(raw);
8939
9088
  } catch (err) {
8940
- return new Error(`JSON parsing failed - try to use {{#fnc.jsonify}}{{xxx}}{{/fnc.jsonify}} to escape JSON special characters (ERR: ${err.message})`);
9089
+ if (debug$4.enabled) {
9090
+ debug$4(`JSON parsing failed (${err.message}) for: ${Utils.shortenJsonString(raw)}`);
9091
+ }
9092
+
9093
+ throw new Error(`JSON parsing failed - try to use {{#fnc.jsonify}}{{xxx}}{{/fnc.jsonify}} to escape JSON special characters (ERR: ${err.message})`);
8941
9094
  }
8942
9095
  } else {
8943
9096
  return raw;
@@ -8982,7 +9135,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8982
9135
 
8983
9136
  this._processOrderedInboundEventsArrayAsync();
8984
9137
  } else {
8985
- setTimeout(() => this._processBodyAsync(event.body, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0);
9138
+ setTimeout(() => this._processBodyAsync(event.body, event.headers, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0);
8986
9139
  }
8987
9140
  }
8988
9141
 
@@ -9123,7 +9276,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
9123
9276
  debug$4(Utils.shortenJsonString(body));
9124
9277
  }
9125
9278
  } else if (body) {
9126
- debug$4(`_runPolling: got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}`);
9279
+ debug$4(`_runPolling: got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
9127
9280
 
9128
9281
  this._storeCookiesFromResponse(response);
9129
9282
 
@@ -9135,7 +9288,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
9135
9288
  }
9136
9289
 
9137
9290
  if (body) {
9138
- setTimeout(() => this._processBodyAsync(body, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0);
9291
+ setTimeout(() => this._processBodyAsync(body, response.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0);
9139
9292
  } else {
9140
9293
  debug$4('_runPolling: ignoring response body (no string and no JSON object)');
9141
9294
  }