botium-core 1.14.1 → 1.14.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 (30) hide show
  1. package/dist/botium-cjs.js +268 -77
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +268 -77
  4. package/dist/botium-es.js.map +1 -1
  5. package/package.json +11 -11
  6. package/src/Capabilities.js +2 -0
  7. package/src/scripting/BotiumError.js +40 -3
  8. package/src/scripting/Convo.js +113 -19
  9. package/src/scripting/ScriptingMemory.js +7 -0
  10. package/src/scripting/ScriptingProvider.js +79 -30
  11. package/src/scripting/logichook/LogicHookConsts.js +5 -2
  12. package/src/scripting/logichook/LogicHookUtils.js +8 -6
  13. package/src/scripting/logichook/logichooks/ConvoStepParametersLogicHook.js +6 -0
  14. package/src/scripting/logichook/logichooks/OrderedListToButtonLogicHook.js +37 -0
  15. package/test/convo/fillAndApplyScriptingMemory.spec.js +11 -0
  16. package/test/logichooks/orderedListToButton.spec.js +35 -0
  17. package/test/scripting/asserters/convoStepParameters.spec.js +140 -0
  18. package/test/scripting/asserters/convos/TEXT_GOOD.convo.txt +6 -0
  19. package/test/scripting/asserters/convos/convo_step_parameter_matchmode_failed.convo.txt +8 -0
  20. package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_all_good.convo.txt +9 -0
  21. package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_botium_timeout.convo.txt +9 -0
  22. package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_good.convo.txt +9 -0
  23. package/test/scripting/asserters/convos/convo_step_parameter_retry_asserters_good_global.convo.txt +9 -0
  24. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_and_asserter.convo.txt +10 -0
  25. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_botium_timeout.convo.txt +9 -0
  26. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_but_no_button.convo.txt +10 -0
  27. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_good.convo.txt +9 -0
  28. package/test/scripting/asserters/convos/convo_step_parameter_retry_main_good_begin.convo.txt +11 -0
  29. package/test/scripting/matching/matchingmode.spec.js +4 -1
  30. package/test/scripting/scriptingProvider.spec.js +38 -12
@@ -77,7 +77,7 @@ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
77
77
  var bodyParser__default = /*#__PURE__*/_interopDefaultLegacy(bodyParser);
78
78
 
79
79
  var name = "botium-core";
80
- var version$1 = "1.14.1";
80
+ var version$1 = "1.14.3";
81
81
  var description = "The Selenium for Chatbots";
82
82
  var main = "index.js";
83
83
  var module$1 = "dist/botium-es.js";
@@ -108,12 +108,12 @@ var bugs = {
108
108
  };
109
109
  var homepage = "https://www.botium.ai";
110
110
  var dependencies = {
111
- "@babel/runtime": "^7.23.2",
111
+ "@babel/runtime": "^7.23.5",
112
112
  async: "^3.2.5",
113
113
  "body-parser": "^1.20.2",
114
114
  boolean: "^3.2.0",
115
115
  bottleneck: "^2.19.5",
116
- "csv-parse": "^5.5.2",
116
+ "csv-parse": "^5.5.3",
117
117
  debug: "^4.3.4",
118
118
  express: "^4.18.2",
119
119
  globby: "11.0.4",
@@ -122,7 +122,7 @@ var dependencies = {
122
122
  "is-json": "^2.0.1",
123
123
  jsonpath: "^1.1.1",
124
124
  lodash: "^4.17.21",
125
- "markdown-it": "^13.0.2",
125
+ "markdown-it": "^14.0.0",
126
126
  "mime-types": "^2.1.35",
127
127
  mkdirp: "^3.0.1",
128
128
  moment: "^2.29.4",
@@ -148,23 +148,23 @@ var dependencies = {
148
148
  yaml: "^2.3.4"
149
149
  };
150
150
  var devDependencies = {
151
- "@babel/core": "^7.23.3",
151
+ "@babel/core": "^7.23.5",
152
152
  "@babel/node": "^7.22.19",
153
- "@babel/plugin-transform-runtime": "^7.23.3",
154
- "@babel/preset-env": "^7.23.3",
153
+ "@babel/plugin-transform-runtime": "^7.23.4",
154
+ "@babel/preset-env": "^7.23.5",
155
155
  chai: "^4.3.10",
156
156
  "chai-as-promised": "^7.1.1",
157
157
  "cross-env": "^7.0.3",
158
- eslint: "^8.53.0",
158
+ eslint: "^8.55.0",
159
159
  "eslint-config-standard": "^17.1.0",
160
160
  "eslint-plugin-import": "^2.29.0",
161
161
  "eslint-plugin-mocha": "^10.2.0",
162
- "eslint-plugin-n": "^16.3.1",
162
+ "eslint-plugin-n": "^16.4.0",
163
163
  "eslint-plugin-promise": "^6.1.1",
164
164
  "eslint-plugin-standard": "^4.1.0",
165
165
  mocha: "^10.2.0",
166
- nock: "^13.3.8",
167
- "npm-check-updates": "^16.14.6",
166
+ nock: "^13.4.0",
167
+ "npm-check-updates": "^16.14.11",
168
168
  nyc: "^15.1.0",
169
169
  rollup: "2.79.1",
170
170
  "rollup-plugin-babel": "^4.4.0",
@@ -367,6 +367,8 @@ var Capabilities = {
367
367
  // varnames, testcasenames
368
368
  SCRIPTING_MEMORY_COLUMN_MODE: 'SCRIPTING_MEMORY_COLUMN_MODE',
369
369
  // Botium Lifecycle Hooks
370
+ SCRIPTING_CONVO_STEP_PARAMETERS: 'SCRIPTING_CONVO_STEP_PARAMETERS',
371
+ // Botium Lifecycle Hooks
370
372
  CUSTOMHOOK_ONBUILD: 'CUSTOMHOOK_ONBUILD',
371
373
  CUSTOMHOOK_ONSTART: 'CUSTOMHOOK_ONSTART',
372
374
  CUSTOMHOOK_ONUSERSAYS: 'CUSTOMHOOK_ONUSERSAYS',
@@ -528,6 +530,7 @@ Capabilities.SCRIPTING_UTTEXPANSION_NAMING_UTTERANCE_MAX;
528
530
  Capabilities.SCRIPTING_MEMORYEXPANSION_KEEP_ORIG;
529
531
  Capabilities.SCRIPTING_MEMORY_MATCHING_MODE;
530
532
  Capabilities.SCRIPTING_MEMORY_COLUMN_MODE;
533
+ Capabilities.SCRIPTING_CONVO_STEP_PARAMETERS;
531
534
  Capabilities.CUSTOMHOOK_ONBUILD;
532
535
  Capabilities.CUSTOMHOOK_ONSTART;
533
536
  Capabilities.CUSTOMHOOK_ONUSERSAYS;
@@ -1076,6 +1079,12 @@ var LogicHookConsts = {
1076
1079
  }, {
1077
1080
  name: 'CONDITIONAL_STEP_JSON_PATH_BASED',
1078
1081
  className: 'ConditionalJsonPathBasedLogicHook.js'
1082
+ }, {
1083
+ name: 'CONVO_STEP_PARAMETERS',
1084
+ className: 'ConvoStepParametersLogicHook.js'
1085
+ }, {
1086
+ name: 'ORDERED_LIST_TO_BUTTON',
1087
+ className: 'OrderedListToButtonLogicHook'
1079
1088
  }],
1080
1089
  DEFAULT_USER_INPUTS: [{
1081
1090
  name: 'BUTTON',
@@ -1086,12 +1095,14 @@ var LogicHookConsts = {
1086
1095
  }, {
1087
1096
  name: 'FORM',
1088
1097
  className: 'FormInput'
1089
- }]
1098
+ }],
1099
+ LOGIC_HOOK_EVENTS: ['onConvoBegin', 'onMeStart', 'onMePrepare', 'onMeEnd', 'onBotStart', 'onBotEnd', 'onBotPrepare', 'onConvoEnd']
1090
1100
  };
1091
1101
  LogicHookConsts.LOGIC_HOOK_INCLUDE;
1092
1102
  LogicHookConsts.DEFAULT_ASSERTERS;
1093
1103
  LogicHookConsts.DEFAULT_LOGIC_HOOKS;
1094
1104
  LogicHookConsts.DEFAULT_USER_INPUTS;
1105
+ LogicHookConsts.LOGIC_HOOK_EVENTS;
1095
1106
 
1096
1107
  const debug$m = debug__default["default"]('botium-core-asserterUtils');
1097
1108
  const {
@@ -1242,6 +1253,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1242
1253
  throw new Error(`Logic Hook specification ${ref} ${hookType} (${packageName}) invalid: ${err.message}`);
1243
1254
  }
1244
1255
  }
1256
+ const typeAsText = hookType === 'asserter' ? 'Asserter' : hookType === 'logichook' ? 'Logic Hook' : hookType === 'userinput' ? 'User Input' : 'Unknown';
1245
1257
  if (isClass__default["default"](src)) {
1246
1258
  try {
1247
1259
  const CheckClass = src;
@@ -1250,7 +1262,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1250
1262
  ...this.buildScriptContext
1251
1263
  }, this.caps, args);
1252
1264
  } catch (err) {
1253
- throw new Error(`Logic Hook specification ${ref} from class invalid: ${err.message}`);
1265
+ throw new Error(`${typeAsText} specification ${ref} from class invalid: ${err.message}`);
1254
1266
  }
1255
1267
  }
1256
1268
  if (lodash__default["default"].isFunction(src)) {
@@ -1260,7 +1272,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1260
1272
  ...this.buildScriptContext
1261
1273
  }, this.caps, args);
1262
1274
  } catch (err) {
1263
- throw new Error(`Logic Hook specification ${ref} from function invalid: ${err.message}`);
1275
+ throw new Error(`${typeAsText} specification ${ref} from function invalid: ${err.message}`);
1264
1276
  }
1265
1277
  }
1266
1278
  if (lodash__default["default"].isObject(src) && !lodash__default["default"].isString(src)) {
@@ -1278,7 +1290,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1278
1290
  }, {});
1279
1291
  return hookObject;
1280
1292
  } catch (err) {
1281
- throw new Error(`Logic Hook specification ${ref} ${hookType} from provided src (${util__default["default"].inspect(src)}) invalid: ${err.message}`);
1293
+ throw new Error(`${typeAsText} specification ${ref} ${hookType} from provided src (${util__default["default"].inspect(src)}) invalid: ${err.message}`);
1282
1294
  }
1283
1295
  }
1284
1296
  if (lodash__default["default"].isString(src)) {
@@ -1339,7 +1351,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1339
1351
  try {
1340
1352
  return tryLoadFromSource(tryLoadFile, tryLoad.tryLoadAsserterByName);
1341
1353
  } catch (err) {
1342
- loadErr.push(`Logic Hook specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `);
1354
+ loadErr.push(`${typeAsText} specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `);
1343
1355
  }
1344
1356
  }
1345
1357
  }
@@ -1348,13 +1360,13 @@ var LogicHookUtils_1 = class LogicHookUtils {
1348
1360
  try {
1349
1361
  return tryLoadFromSource(tryLoad.tryLoadPackageName, tryLoad.tryLoadAsserterByName);
1350
1362
  } catch (err) {
1351
- loadErr.push(`Logic Hook specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `);
1363
+ loadErr.push(`${typeAsText} specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `);
1352
1364
  }
1353
1365
  }
1354
1366
  }
1355
1367
  loadErr.forEach(debug$m);
1356
1368
  }
1357
- throw new Error(`Logic Hook specification ${ref} ${hookType} from "${util__default["default"].inspect(src)}" invalid : no loader available`);
1369
+ throw new Error(`${typeAsText} specification ${ref} ${hookType} from "${util__default["default"].inspect(src)}" invalid : no loader available`);
1358
1370
  }
1359
1371
  };
1360
1372
 
@@ -2247,6 +2259,44 @@ const BotiumError$4 = class BotiumError extends Error {
2247
2259
  return null;
2248
2260
  }
2249
2261
  }
2262
+ hasError({
2263
+ type,
2264
+ source
2265
+ }) {
2266
+ if (this.context) {
2267
+ const errArr = lodash__default["default"].isArray(this.context) ? this.context : [this.context];
2268
+ for (const err of errArr) {
2269
+ if (err.type === 'list') {
2270
+ for (const internal of err.errors) {
2271
+ if ((!type || internal.type === type) && (!source || internal.source === source)) {
2272
+ return true;
2273
+ }
2274
+ }
2275
+ }
2276
+ if ((!type || err.type === type) && (!source || err.source === source)) {
2277
+ return true;
2278
+ }
2279
+ }
2280
+ } else {
2281
+ return false;
2282
+ }
2283
+ }
2284
+ toArray() {
2285
+ if (this.context) {
2286
+ let result = [];
2287
+ const errArr = lodash__default["default"].isArray(this.context) ? this.context : [this.context];
2288
+ for (const err of errArr) {
2289
+ if (err.type === 'list') {
2290
+ result = result.concat(err.errors);
2291
+ } else {
2292
+ result.push(err);
2293
+ }
2294
+ }
2295
+ return result;
2296
+ } else {
2297
+ return [];
2298
+ }
2299
+ }
2250
2300
  };
2251
2301
  const _getChildErrorsFromContext = context => {
2252
2302
  if (context && context.errors && lodash__default["default"].isArray(context.errors)) {
@@ -2254,12 +2304,16 @@ const _getChildErrorsFromContext = context => {
2254
2304
  }
2255
2305
  return false;
2256
2306
  };
2257
- const botiumErrorFromErr$2 = (message, err) => {
2307
+ const botiumErrorFromErr$2 = (message, err, context = {}) => {
2258
2308
  if (err instanceof BotiumError$4) {
2259
- return new BotiumError$4(message, err.context, true);
2309
+ return new BotiumError$4(message, {
2310
+ ...err.context,
2311
+ ...context
2312
+ }, true);
2260
2313
  } else {
2261
2314
  return new BotiumError$4(message, {
2262
- err
2315
+ err,
2316
+ ...context
2263
2317
  }, true);
2264
2318
  }
2265
2319
  };
@@ -2534,6 +2588,13 @@ const _apply = (scriptingMemory, str, caps, mockMsg) => {
2534
2588
  return arg;
2535
2589
  }
2536
2590
  });
2591
+ args = args.map(arg => {
2592
+ const argStr = `${arg}`;
2593
+ if (argStr.startsWith('$')) {
2594
+ return scriptingMemory[argStr.substring(1)] || arg;
2595
+ }
2596
+ return arg;
2597
+ });
2537
2598
  str = str.replace(match, SCRIPTING_FUNCTIONS$1[key].handler(caps, ...args, mockMsg));
2538
2599
  } else {
2539
2600
  str = str.replace(match, SCRIPTING_FUNCTIONS$1[key].handler(caps));
@@ -2913,8 +2974,52 @@ class Convo$6 {
2913
2974
  let skipTranscriptStep = false;
2914
2975
  let conditionalGroupId = null;
2915
2976
  let conditionMetInGroup = false;
2916
- for (let i = 0; i < this.conversation.length; i++) {
2977
+ let globalConvoStepParameters = container.caps[Capabilities.SCRIPTING_CONVO_STEP_PARAMETERS] || {};
2978
+ let retryBotMessageTimeoutEnd = null;
2979
+ let retryBotMessageConvoId = null;
2980
+ let retryBotMessageDropBotResponse = false;
2981
+ for (let i = 0; i < this.conversation.length; i = retryBotMessageDropBotResponse ? i : i + 1) {
2982
+ retryBotMessageDropBotResponse = false;
2917
2983
  const convoStep = this.conversation[i];
2984
+ const rawConvoStepParameters = convoStep.logicHooks.find(lh => lh.name === 'CONVO_STEP_PARAMETERS')?.args;
2985
+ let convoStepParameters = {};
2986
+ if (rawConvoStepParameters && rawConvoStepParameters.length) {
2987
+ let params;
2988
+ if (rawConvoStepParameters[0].trim().startsWith('{')) {
2989
+ try {
2990
+ params = JSON.parse(rawConvoStepParameters[0]);
2991
+ } catch (e) {
2992
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Failed to parse convo step parameters from JSON ${rawConvoStepParameters[0]}`);
2993
+ }
2994
+ }
2995
+ if (!params || !Object.keys(params).length) {
2996
+ params = {};
2997
+ for (const param of rawConvoStepParameters) {
2998
+ const semicolon = param.indexOf(':');
2999
+ if (semicolon) {
3000
+ try {
3001
+ const name = param.substring(0, semicolon);
3002
+ const value = param.substring(semicolon + 1);
3003
+ params[name] = value;
3004
+ } catch (e) {
3005
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Failed to parse convo step parameter from arg ${param}`);
3006
+ }
3007
+ }
3008
+ }
3009
+ }
3010
+ if (convoStep.sender === 'begin') {
3011
+ globalConvoStepParameters = Object.assign({}, globalConvoStepParameters || {}, params);
3012
+ } else {
3013
+ convoStepParameters = Object.assign({}, globalConvoStepParameters || {}, params);
3014
+ }
3015
+ } else {
3016
+ if (convoStep.sender !== 'begin') {
3017
+ convoStepParameters = globalConvoStepParameters;
3018
+ }
3019
+ }
3020
+ if (Object.keys(convoStepParameters).length) {
3021
+ debug$j(`${this.header.name}: using convo step parameters ${JSON.stringify(convoStepParameters)}`);
3022
+ }
2918
3023
  const currentStepIndex = i;
2919
3024
  container.eventEmitter.emit(Events.CONVO_STEP_NEXT, container, convoStep, i);
2920
3025
  skipTranscriptStep = false;
@@ -2978,8 +3083,8 @@ class Convo$6 {
2978
3083
  const coreMsg = lodash__default["default"].omit(removeBuffers(meMsg), ['sourceData']);
2979
3084
  debug$j(`${this.header.name}/${convoStep.stepTag}: user says (cleaned by binary and base64 data and sourceData) ${JSON.stringify(coreMsg, null, 2)}`);
2980
3085
  await new Promise(resolve => {
2981
- if (container.caps.SIMULATE_WRITING_SPEED && meMsg.messageText && meMsg.messageText.length) {
2982
- setTimeout(() => resolve(), container.caps.SIMULATE_WRITING_SPEED * meMsg.messageText.length);
3086
+ if (container.caps[Capabilities.SIMULATE_WRITING_SPEED] && meMsg.messageText && meMsg.messageText.length) {
3087
+ setTimeout(() => resolve(), container.caps[Capabilities.SIMULATE_WRITING_SPEED] * meMsg.messageText.length);
2983
3088
  } else {
2984
3089
  resolve();
2985
3090
  }
@@ -3133,10 +3238,29 @@ class Convo$6 {
3133
3238
  }
3134
3239
  const isErrorHandledWithOptionConvoStep = err => {
3135
3240
  const nextConvoStep = this.conversation[i + 1];
3241
+ const retryConfig = convoStepParameters?.ignoreNotMatchedBotResponses;
3242
+ const retryOn = convoStep.sender === 'bot' && retryConfig && retryConfig.timeout && retryConfig.mainAsserter;
3136
3243
  if (convoStep.optional && nextConvoStep && nextConvoStep.sender === 'bot') {
3244
+ if (retryOn) {
3245
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Retry failed asserter is ignored on optional convo`);
3246
+ }
3137
3247
  waitForBotSays = false;
3138
3248
  skipTranscriptStep = true;
3139
3249
  return true;
3250
+ } else if (retryOn) {
3251
+ if (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag) {
3252
+ retryBotMessageTimeoutEnd = transcriptStep.stepBegin.getTime() + +retryConfig.timeout;
3253
+ retryBotMessageConvoId = convoStep.stepTag;
3254
+ }
3255
+ const now = new Date().getTime();
3256
+ const timeoutRemaining = retryBotMessageTimeoutEnd - now;
3257
+ if (timeoutRemaining > 0) {
3258
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, timeout remaining: ${timeoutRemaining}, error: "${err.message}"`);
3259
+ retryBotMessageDropBotResponse = true;
3260
+ return false;
3261
+ } else {
3262
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, but timeout is over. error: "${err.message}"`);
3263
+ }
3140
3264
  }
3141
3265
  if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
3142
3266
  assertErrors.push(err);
@@ -3154,7 +3278,7 @@ class Convo$6 {
3154
3278
  const tomatch = this._resolveUtterancesToMatch(container, Object.assign({}, scriptingMemoryUpdate, scriptingMemory), messageText, botMsg);
3155
3279
  if (convoStep.not) {
3156
3280
  try {
3157
- this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep);
3281
+ this.scriptingEvents.assertBotNotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
3158
3282
  } catch (err) {
3159
3283
  if (isErrorHandledWithOptionConvoStep(err)) {
3160
3284
  continue;
@@ -3162,7 +3286,7 @@ class Convo$6 {
3162
3286
  }
3163
3287
  } else {
3164
3288
  try {
3165
- this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep);
3289
+ this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, lastMeConvoStep, convoStepParameters);
3166
3290
  } catch (err) {
3167
3291
  if (isErrorHandledWithOptionConvoStep(err)) {
3168
3292
  continue;
@@ -3171,7 +3295,7 @@ class Convo$6 {
3171
3295
  }
3172
3296
  } else if (convoStep.sourceData) {
3173
3297
  try {
3174
- this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg);
3298
+ this._compareObject(container, scriptingMemory, convoStep, botMsg.sourceData, convoStep.sourceData, botMsg, convoStepParameters);
3175
3299
  } catch (err) {
3176
3300
  if (isErrorHandledWithOptionConvoStep(err)) {
3177
3301
  continue;
@@ -3205,19 +3329,42 @@ class Convo$6 {
3205
3329
  skipTranscriptStep = true;
3206
3330
  continue;
3207
3331
  }
3208
- const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: assertion error - ${err.message || err}`, err);
3209
- debug$j(failErr);
3210
- try {
3211
- this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3212
- } catch (failErr) {}
3213
- if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS] && err instanceof BotiumError$2) {
3214
- assertErrors.push(err);
3332
+ const errors = err.toArray ? err.toArray() : [];
3333
+ const retryConfig = convoStepParameters?.ignoreNotMatchedBotResponses;
3334
+ const retryOn = convoStep.sender === 'bot' && retryConfig && retryConfig.timeout && errors.length && errors.filter(({
3335
+ type,
3336
+ source,
3337
+ asserter
3338
+ }) => type === 'asserter' && (retryConfig.allAsserters || retryConfig.asserters && retryConfig.asserters.includes(asserter))).length;
3339
+ if (retryOn && (!retryBotMessageTimeoutEnd || retryBotMessageConvoId !== convoStep.stepTag)) {
3340
+ retryBotMessageTimeoutEnd = transcriptStep.stepBegin.getTime() + +retryConfig.timeout;
3341
+ retryBotMessageConvoId = convoStep.stepTag;
3342
+ }
3343
+ const now = new Date().getTime();
3344
+ const timeoutRemaining = retryOn && retryBotMessageTimeoutEnd - now;
3345
+ if (retryOn && timeoutRemaining > 0) {
3346
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, timeout remaining: ${timeoutRemaining}, error: "${err.message}"`);
3347
+ retryBotMessageDropBotResponse = true;
3215
3348
  } else {
3216
- throw failErr;
3349
+ if (retryOn && timeoutRemaining <= 0) {
3350
+ debug$j(`${this.header.name}/${convoStep.stepTag}: Convo step retry on, but timeout is over. error: "${err.message}"`);
3351
+ }
3352
+ const failErr = botiumErrorFromErr$1(`${this.header.name}/${convoStep.stepTag}: assertion error - ${err.message || err}`, err);
3353
+ debug$j(failErr);
3354
+ try {
3355
+ this.scriptingEvents.fail && this.scriptingEvents.fail(failErr, lastMeConvoStep);
3356
+ } catch (failErr) {}
3357
+ if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS] && err instanceof BotiumError$2) {
3358
+ assertErrors.push(err);
3359
+ } else {
3360
+ throw failErr;
3361
+ }
3217
3362
  }
3218
3363
  }
3219
3364
  if (container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
3220
3365
  if (assertErrors.length > 0) {
3366
+ // this has no effect, but logically it has to be false
3367
+ retryBotMessageDropBotResponse = false;
3221
3368
  throw botiumErrorFromList$1(assertErrors, {});
3222
3369
  }
3223
3370
  } else {
@@ -3277,7 +3424,7 @@ class Convo$6 {
3277
3424
  }
3278
3425
  }
3279
3426
  }
3280
- _compareObject(container, scriptingMemory, convoStep, result, expected, botMsg) {
3427
+ _compareObject(container, scriptingMemory, convoStep, result, expected, botMsg, convoStepParameters) {
3281
3428
  if (expected === null || expected === undefined) return;
3282
3429
  if (lodash__default["default"].isArray(expected)) {
3283
3430
  if (!lodash__default["default"].isArray(result)) {
@@ -3287,12 +3434,12 @@ class Convo$6 {
3287
3434
  throw new BotiumError$2(`${this.header.name}/${convoStep.stepTag}: bot response expected array length ${expected.length}, got ${result.length}`);
3288
3435
  }
3289
3436
  for (let i = 0; i < expected.length; i++) {
3290
- this._compareObject(container, scriptingMemory, convoStep, result[i], expected[i]);
3437
+ this._compareObject(container, scriptingMemory, convoStep, result[i], expected[i], null, convoStepParameters);
3291
3438
  }
3292
3439
  } else if (lodash__default["default"].isObject(expected)) {
3293
3440
  lodash__default["default"].forOwn(expected, (value, key) => {
3294
3441
  if (Object.prototype.hasOwnProperty.call(result, key)) {
3295
- this._compareObject(container, scriptingMemory, convoStep, result[key], expected[key]);
3442
+ this._compareObject(container, scriptingMemory, convoStep, result[key], expected[key], null, convoStepParameters);
3296
3443
  } else {
3297
3444
  throw new BotiumError$2(`${this.header.name}/${convoStep.stepTag}: bot response "${result}" missing expected property: ${key}`);
3298
3445
  }
@@ -3301,7 +3448,7 @@ class Convo$6 {
3301
3448
  ScriptingMemory.fill(container, scriptingMemory, result, expected, this.scriptingEvents);
3302
3449
  const response = this._checkNormalizeText(container, result);
3303
3450
  const tomatch = this._resolveUtterancesToMatch(container, scriptingMemory, expected, botMsg);
3304
- this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`);
3451
+ this.scriptingEvents.assertBotResponse(response, tomatch, `${this.header.name}/${convoStep.stepTag}`, null, convoStepParameters);
3305
3452
  }
3306
3453
  }
3307
3454
  GetScriptingMemoryAllVariables(container) {
@@ -3361,7 +3508,7 @@ class Convo$6 {
3361
3508
  }, []);
3362
3509
  }
3363
3510
  _checkBotRepliesConsumed(container) {
3364
- if (container.caps.SCRIPTING_FORCE_BOT_CONSUMED) {
3511
+ if (container.caps[Capabilities.SCRIPTING_FORCE_BOT_CONSUMED]) {
3365
3512
  const queueLength = container._QueueLength();
3366
3513
  if (queueLength === 1) {
3367
3514
  throw new Error('There is an unread bot reply in queue');
@@ -5405,7 +5552,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5405
5552
  }) => {
5406
5553
  return this._createLogicHookPromises({
5407
5554
  hookType: 'onConvoBegin',
5408
- logicHooks: convo.beginLogicHook || [],
5555
+ logicHooks: convo?.beginLogicHook || [],
5409
5556
  convo,
5410
5557
  convoStep,
5411
5558
  scriptingMemory,
@@ -5420,7 +5567,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5420
5567
  }) => {
5421
5568
  return this._createLogicHookPromises({
5422
5569
  hookType: 'onConvoEnd',
5423
- logicHooks: convo.endLogicHook || [],
5570
+ logicHooks: convo?.endLogicHook || [],
5424
5571
  convo,
5425
5572
  convoStep,
5426
5573
  scriptingMemory,
@@ -5435,7 +5582,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5435
5582
  }) => {
5436
5583
  return this._createLogicHookPromises({
5437
5584
  hookType: 'onMeStart',
5438
- logicHooks: convoStep.logicHooks || [],
5585
+ logicHooks: convoStep?.logicHooks || [],
5439
5586
  convo,
5440
5587
  convoStep,
5441
5588
  scriptingMemory,
@@ -5450,7 +5597,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5450
5597
  }) => {
5451
5598
  return this._createLogicHookPromises({
5452
5599
  hookType: 'onMePrepare',
5453
- logicHooks: convoStep.logicHooks || [],
5600
+ logicHooks: convoStep?.logicHooks || [],
5454
5601
  convo,
5455
5602
  convoStep,
5456
5603
  scriptingMemory,
@@ -5465,7 +5612,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5465
5612
  }) => {
5466
5613
  return this._createLogicHookPromises({
5467
5614
  hookType: 'onMeEnd',
5468
- logicHooks: convoStep.logicHooks || [],
5615
+ logicHooks: convoStep?.logicHooks || [],
5469
5616
  convo,
5470
5617
  convoStep,
5471
5618
  scriptingMemory,
@@ -5480,7 +5627,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5480
5627
  }) => {
5481
5628
  return this._createLogicHookPromises({
5482
5629
  hookType: 'onBotStart',
5483
- logicHooks: convoStep.logicHooks || [],
5630
+ logicHooks: convoStep?.logicHooks || [],
5484
5631
  convo,
5485
5632
  convoStep,
5486
5633
  scriptingMemory,
@@ -5495,7 +5642,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5495
5642
  }) => {
5496
5643
  return this._createLogicHookPromises({
5497
5644
  hookType: 'onBotPrepare',
5498
- logicHooks: convoStep.logicHooks || [],
5645
+ logicHooks: convoStep?.logicHooks || [],
5499
5646
  convo,
5500
5647
  convoStep,
5501
5648
  scriptingMemory,
@@ -5510,7 +5657,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5510
5657
  }) => {
5511
5658
  return this._createLogicHookPromises({
5512
5659
  hookType: 'onBotEnd',
5513
- logicHooks: convoStep.logicHooks || [],
5660
+ logicHooks: convoStep?.logicHooks || [],
5514
5661
  convo,
5515
5662
  convoStep,
5516
5663
  scriptingMemory,
@@ -5525,7 +5672,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5525
5672
  }) => {
5526
5673
  return this._createAsserterPromises({
5527
5674
  asserterType: 'assertConvoBegin',
5528
- asserters: convo.beginAsserter || [],
5675
+ asserters: convo?.beginAsserter || [],
5529
5676
  convo,
5530
5677
  convoStep,
5531
5678
  scriptingMemory,
@@ -5540,7 +5687,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5540
5687
  }) => {
5541
5688
  return this._createAsserterPromises({
5542
5689
  asserterType: 'assertConvoStep',
5543
- asserters: convoStep.asserters || [],
5690
+ asserters: convoStep?.asserters || [],
5544
5691
  convo,
5545
5692
  convoStep,
5546
5693
  scriptingMemory,
@@ -5555,7 +5702,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5555
5702
  }) => {
5556
5703
  return this._createAsserterPromises({
5557
5704
  asserterType: 'assertConvoEnd',
5558
- asserters: convo.endAsserter || [],
5705
+ asserters: convo?.endAsserter || [],
5559
5706
  convo,
5560
5707
  convoStep,
5561
5708
  scriptingMemory,
@@ -5584,12 +5731,13 @@ var ScriptingProvider_1 = class ScriptingProvider {
5584
5731
  resolveEmptyIfUnknown
5585
5732
  });
5586
5733
  },
5587
- assertBotResponse: (botresponse, tomatch, stepTag, meMsg) => {
5734
+ assertBotResponse: (botresponse, tomatch, stepTag, meMsg, convoStepParameters) => {
5588
5735
  if (!lodash__default["default"].isArray(tomatch)) {
5589
5736
  tomatch = [tomatch];
5590
5737
  }
5591
5738
  debug$9(`assertBotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} = ${tomatch} ...`);
5592
- const found = lodash__default["default"].find(tomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5739
+ const matchFn = convoStepParameters.matchingMode ? getMatchFunction(convoStepParameters.matchingMode) || this.matchFn : this.matchFn;
5740
+ const found = lodash__default["default"].find(tomatch, utt => matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5593
5741
  const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'Word Error Rate Asserter' : 'Text Match Asserter';
5594
5742
  if (lodash__default["default"].isNil(found)) {
5595
5743
  if (this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer') {
@@ -5639,12 +5787,13 @@ var ScriptingProvider_1 = class ScriptingProvider {
5639
5787
  }
5640
5788
  }
5641
5789
  },
5642
- assertBotNotResponse: (botresponse, nottomatch, stepTag, meMsg) => {
5790
+ assertBotNotResponse: (botresponse, nottomatch, stepTag, meMsg, convoStepParameters) => {
5643
5791
  if (!lodash__default["default"].isArray(nottomatch)) {
5644
5792
  nottomatch = [nottomatch];
5645
5793
  }
5646
5794
  debug$9(`assertBotNotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} != ${nottomatch} ...`);
5647
- const found = lodash__default["default"].find(nottomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5795
+ const matchFn = convoStepParameters.matchingMode ? getMatchFunction(convoStepParameters.matchingMode) || this.matchFn : this.matchFn;
5796
+ const found = lodash__default["default"].find(nottomatch, utt => matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
5648
5797
  const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'Word Error Rate Asserter' : 'Text Match Asserter';
5649
5798
  if (!lodash__default["default"].isNil(found)) {
5650
5799
  if (this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer') {
@@ -5718,6 +5867,22 @@ var ScriptingProvider_1 = class ScriptingProvider {
5718
5867
  assertConvoStep: 'assertNotConvoStep',
5719
5868
  assertConvoEnd: 'assertNotConvoEnd'
5720
5869
  };
5870
+ const updateExceptionContext = (promise, asserter) => {
5871
+ const updateError = err => {
5872
+ if (err instanceof BotiumError$1) {
5873
+ if (!err.context) {
5874
+ err.context = {};
5875
+ }
5876
+ err.context.asserter = asserter.name;
5877
+ throw err;
5878
+ } else {
5879
+ throw botiumErrorFromErr(lodash__default["default"].isString(err) ? err : err.message, err, {
5880
+ asserter: asserter.name
5881
+ });
5882
+ }
5883
+ };
5884
+ return promise.catch(err => updateError(err));
5885
+ };
5721
5886
  const callAsserter = (asserterSpec, asserter, params) => {
5722
5887
  if (asserterSpec.not) {
5723
5888
  const notAsserterType = mapNot[asserterType];
@@ -5741,24 +5906,36 @@ var ScriptingProvider_1 = class ScriptingProvider {
5741
5906
  return p(this.retryHelperAsserter, () => asserter[asserterType](params));
5742
5907
  }
5743
5908
  };
5744
- const convoAsserter = asserters.filter(a => this.asserters[a.name][asserterType]).map(a => callAsserter(a, this.asserters[a.name], {
5745
- convo,
5746
- convoStep,
5747
- scriptingMemory,
5748
- container,
5749
- args: ScriptingMemory.applyToArgs(a.args, scriptingMemory, container.caps, rest.botMsg),
5750
- isGlobal: false,
5751
- ...rest
5752
- }));
5753
- const globalAsserter = Object.values(this.globalAsserter).filter(a => a[asserterType]).map(a => p(this.retryHelperAsserter, () => a[asserterType]({
5754
- convo,
5755
- convoStep,
5756
- scriptingMemory,
5757
- container,
5758
- args: [],
5759
- isGlobal: true,
5760
- ...rest
5761
- })));
5909
+ const convoAsserter = asserters.filter(a => this.asserters[a.name][asserterType]).map(a => ({
5910
+ asserter: a,
5911
+ promise: callAsserter(a, this.asserters[a.name], {
5912
+ convo,
5913
+ convoStep,
5914
+ scriptingMemory,
5915
+ container,
5916
+ args: ScriptingMemory.applyToArgs(a.args, scriptingMemory, container.caps, rest.botMsg),
5917
+ isGlobal: false,
5918
+ ...rest
5919
+ })
5920
+ })).map(({
5921
+ promise,
5922
+ asserter
5923
+ }) => updateExceptionContext(promise, asserter));
5924
+ const globalAsserter = Object.values(this.globalAsserter).filter(a => a[asserterType]).map(a => ({
5925
+ asserter: a,
5926
+ promise: p(this.retryHelperAsserter, () => a[asserterType]({
5927
+ convo,
5928
+ convoStep,
5929
+ scriptingMemory,
5930
+ container,
5931
+ args: [],
5932
+ isGlobal: true,
5933
+ ...rest
5934
+ }))
5935
+ })).map(({
5936
+ promise,
5937
+ asserter
5938
+ }) => updateExceptionContext(promise, asserter));
5762
5939
  const allPromises = [...convoAsserter, ...globalAsserter];
5763
5940
  if (this.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
5764
5941
  return Promise.allSettled(allPromises).then(results => {
@@ -5784,7 +5961,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
5784
5961
  if (hookType !== 'onMeStart' && hookType !== 'onMePrepare' && hookType !== 'onMeEnd' && hookType !== 'onBotStart' && hookType !== 'onBotPrepare' && hookType !== 'onBotEnd' && hookType !== 'onConvoBegin' && hookType !== 'onConvoEnd') {
5785
5962
  throw Error(`Unknown hookType ${hookType}`);
5786
5963
  }
5787
- const convoStepPromises = (logicHooks || []).filter(l => this.logicHooks[l.name][hookType]).map(l => p(this.retryHelperLogicHook, () => this.logicHooks[l.name][hookType]({
5964
+ const localHooks = (logicHooks || []).filter(l => this.logicHooks[l.name][hookType]);
5965
+ const convoStepPromises = localHooks.map(l => p(this.retryHelperLogicHook, () => this.logicHooks[l.name][hookType]({
5788
5966
  convo,
5789
5967
  convoStep,
5790
5968
  scriptingMemory,
@@ -5793,7 +5971,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
5793
5971
  isGlobal: false,
5794
5972
  ...rest
5795
5973
  })));
5796
- const globalPromises = Object.values(this.globalLogicHook).filter(l => l[hookType]).map(l => p(this.retryHelperLogicHook, () => l[hookType]({
5974
+ const globalHooks = Object.values(this.globalLogicHook).filter(l => l[hookType]);
5975
+ const globalPromises = globalHooks.map(l => p(this.retryHelperLogicHook, () => l[hookType]({
5797
5976
  convo,
5798
5977
  convoStep,
5799
5978
  scriptingMemory,
@@ -5803,7 +5982,14 @@ var ScriptingProvider_1 = class ScriptingProvider {
5803
5982
  ...rest
5804
5983
  })));
5805
5984
  const allPromises = [...convoStepPromises, ...globalPromises];
5806
- if (allPromises.length > 0) return Promise.all(allPromises).then(() => true);
5985
+ if (allPromises.length > 0) {
5986
+ return Promise.all(allPromises).then(() => {
5987
+ return {
5988
+ // just returning some humanreadable
5989
+ hooks: [...localHooks, ...globalHooks].map(h => h.name || h.context?.ref || JSON.stringify(h))
5990
+ };
5991
+ });
5992
+ }
5807
5993
  return Promise.resolve(false);
5808
5994
  }
5809
5995
  _createUserInputPromises({
@@ -5813,7 +5999,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
5813
5999
  container,
5814
6000
  ...rest
5815
6001
  }) {
5816
- const convoStepPromises = (convoStep.userInputs || []).filter(ui => this.userInputs[ui.name]).map(ui => p(this.retryHelperUserInput, () => this.userInputs[ui.name].setUserInput({
6002
+ const convoStepPromises = (convoStep?.userInputs || []).filter(ui => this.userInputs[ui.name]).map(ui => p(this.retryHelperUserInput, () => this.userInputs[ui.name].setUserInput({
5817
6003
  convo,
5818
6004
  convoStep,
5819
6005
  scriptingMemory,
@@ -5875,6 +6061,11 @@ var ScriptingProvider_1 = class ScriptingProvider {
5875
6061
  }
5876
6062
  };
5877
6063
  }
6064
+
6065
+ // Livechat, and crawler using logichooks too. So they need script context
6066
+ BuildScriptContext() {
6067
+ return this._buildScriptContext();
6068
+ }
5878
6069
  Build() {
5879
6070
  const CompilerXlsx = CompilerXlsx_1;
5880
6071
  this.compilers[Constants.SCRIPTING_FORMAT_XSLX] = new CompilerXlsx(this._buildScriptContext(), this.caps);