botium-core 1.12.5 → 1.13.1

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 (29) hide show
  1. package/dist/botium-cjs.js +277 -171
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +296 -190
  4. package/dist/botium-es.js.map +1 -1
  5. package/package.json +24 -24
  6. package/src/Capabilities.js +7 -0
  7. package/src/helpers/RetryHelper.js +13 -7
  8. package/src/scripting/CompilerCsv.js +150 -102
  9. package/src/scripting/Convo.js +21 -1
  10. package/src/scripting/ScriptingProvider.js +5 -1
  11. package/src/scripting/helper.js +19 -12
  12. package/src/scripting/logichook/LogicHookUtils.js +1 -1
  13. package/src/scripting/logichook/asserter/ButtonsAsserter.js +4 -2
  14. package/src/scripting/logichook/asserter/CardsAsserter.js +4 -2
  15. package/test/compiler/compilercsv.spec.js +363 -12
  16. package/test/compiler/compilertxt.spec.js +13 -0
  17. package/test/compiler/convos/csv/utterances_liveperson.csv +108 -0
  18. package/test/compiler/convos/csv/utterances_multicolumn3col.csv +3 -0
  19. package/test/compiler/convos/csv/utterances_multicolumn5col.csv +3 -0
  20. package/test/compiler/convos/csv/utterances_singlecolumn.csv +3 -0
  21. package/test/compiler/convos/csv/utterances_variable_row_len.csv +3 -0
  22. package/test/compiler/convos/txt/convos_args_escape.convo.txt +2 -0
  23. package/test/convo/convos/continuefailing_timeout.convo.txt +16 -0
  24. package/test/convo/retryconvo.spec.js +134 -0
  25. package/test/convo/transcript.spec.js +18 -1
  26. package/test/logichooks/hookfromsrc.spec.js +1 -1
  27. package/test/scripting/asserters/buttonsAsserter.spec.js +47 -0
  28. package/test/scripting/asserters/cardsAsserter.spec.js +39 -0
  29. package/test/scripting/txt/decompile.spec.js +24 -0
@@ -14,7 +14,7 @@ var randomatic = require('randomatic');
14
14
  var lodash = require('lodash');
15
15
  var boolean$1 = require('boolean');
16
16
  var events = require('events');
17
- var debug$l = require('debug');
17
+ var debug$m = require('debug');
18
18
  var vm2 = require('vm2');
19
19
  var isClass = require('is-class');
20
20
  var crypto = require('crypto');
@@ -53,7 +53,7 @@ var randomatic__default = /*#__PURE__*/_interopDefaultLegacy(randomatic);
53
53
  var lodash__default = /*#__PURE__*/_interopDefaultLegacy(lodash);
54
54
  var boolean__default = /*#__PURE__*/_interopDefaultLegacy(boolean$1);
55
55
  var events__default = /*#__PURE__*/_interopDefaultLegacy(events);
56
- var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug$l);
56
+ var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug$m);
57
57
  var vm2__default = /*#__PURE__*/_interopDefaultLegacy(vm2);
58
58
  var isClass__default = /*#__PURE__*/_interopDefaultLegacy(isClass);
59
59
  var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
@@ -79,7 +79,7 @@ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
79
79
  var bodyParser__default = /*#__PURE__*/_interopDefaultLegacy(bodyParser);
80
80
 
81
81
  var name = "botium-core";
82
- var version$1 = "1.12.5";
82
+ var version$1 = "1.13.1";
83
83
  var description = "The Selenium for Chatbots";
84
84
  var main = "index.js";
85
85
  var module$1 = "dist/botium-es.js";
@@ -111,25 +111,25 @@ var bugs = {
111
111
  };
112
112
  var homepage = "https://www.botium.ai";
113
113
  var dependencies = {
114
- "@babel/runtime": "^7.17.9",
115
- async: "^3.2.3",
114
+ "@babel/runtime": "^7.18.6",
115
+ async: "^3.2.4",
116
116
  "body-parser": "^1.20.0",
117
117
  boolean: "^3.2.0",
118
118
  bottleneck: "^2.19.5",
119
- "csv-parse": "^5.0.4",
119
+ "csv-parse": "^5.3.0",
120
120
  debug: "^4.3.4",
121
121
  esprima: "^4.0.1",
122
- express: "^4.17.3",
122
+ express: "^4.18.1",
123
123
  globby: "11.0.4",
124
- ioredis: "^5.0.4",
124
+ ioredis: "^5.1.0",
125
125
  "is-class": "^0.0.9",
126
126
  "is-json": "^2.0.1",
127
127
  jsonpath: "^1.1.1",
128
128
  lodash: "^4.17.21",
129
- "markdown-it": "^12.3.2",
129
+ "markdown-it": "^13.0.1",
130
130
  "mime-types": "^2.1.35",
131
131
  mkdirp: "^1.0.4",
132
- moment: "^2.29.3",
132
+ moment: "^2.29.4",
133
133
  mustache: "^4.2.0",
134
134
  "promise-retry": "^2.0.1",
135
135
  "promise.allsettled": "^1.0.5",
@@ -138,39 +138,39 @@ var dependencies = {
138
138
  rimraf: "^3.0.2",
139
139
  "sanitize-filename": "^1.6.3",
140
140
  slugify: "^1.6.5",
141
- "socket.io": "^4.4.1",
142
- "socket.io-client": "^4.4.1",
141
+ "socket.io": "^4.5.1",
142
+ "socket.io-client": "^4.5.1",
143
143
  "socketio-auth": "^0.1.1",
144
144
  "swagger-jsdoc": "^6.2.1",
145
- "swagger-ui-express": "^4.3.0",
145
+ "swagger-ui-express": "^4.4.0",
146
146
  uuid: "^8.3.2",
147
- vm2: "^3.9.9",
147
+ vm2: "^3.9.10",
148
148
  "write-yaml": "^1.0.0",
149
149
  xlsx: "^0.18.5",
150
- xregexp: "^5.1.0",
151
- yaml: "^2.0.1"
150
+ xregexp: "^5.1.1",
151
+ yaml: "^2.1.1"
152
152
  };
153
153
  var devDependencies = {
154
- "@babel/core": "^7.17.9",
155
- "@babel/node": "^7.16.8",
156
- "@babel/plugin-transform-runtime": "^7.17.0",
157
- "@babel/preset-env": "^7.16.11",
154
+ "@babel/core": "^7.18.6",
155
+ "@babel/node": "^7.18.6",
156
+ "@babel/plugin-transform-runtime": "^7.18.6",
157
+ "@babel/preset-env": "^7.18.6",
158
158
  chai: "^4.3.6",
159
159
  "chai-as-promised": "^7.1.1",
160
160
  "cross-env": "^7.0.3",
161
- eslint: "^8.13.0",
161
+ eslint: "^8.19.0",
162
162
  "eslint-config-standard": "^17.0.0",
163
163
  "eslint-plugin-import": "^2.26.0",
164
- "eslint-plugin-n": "^15.1.0",
164
+ "eslint-plugin-n": "^15.2.4",
165
165
  "eslint-plugin-promise": "^6.0.0",
166
166
  "eslint-plugin-standard": "^4.1.0",
167
167
  "license-checker": "^25.0.1",
168
168
  "license-compatibility-checker": "^0.3.5",
169
- mocha: "^9.2.2",
170
- nock: "^13.2.4",
171
- "npm-check-updates": "^12.5.9",
169
+ mocha: "^10.0.0",
170
+ nock: "^13.2.8",
171
+ "npm-check-updates": "^15.2.6",
172
172
  nyc: "^15.1.0",
173
- rollup: "^2.70.2",
173
+ rollup: "^2.76.0",
174
174
  "rollup-plugin-babel": "^4.4.0",
175
175
  "rollup-plugin-commonjs": "^10.1.0",
176
176
  "rollup-plugin-json": "^4.0.0",
@@ -319,6 +319,10 @@ var Capabilities = {
319
319
  SCRIPTING_XLSX_SHEETNAMES_PCONVOS: 'SCRIPTING_XLSX_SHEETNAMES_PCONVOS',
320
320
  SCRIPTING_XLSX_SHEETNAMES_UTTERANCES: 'SCRIPTING_XLSX_SHEETNAMES_UTTERANCES',
321
321
  SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY: 'SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY',
322
+ // hidden capability. All newly in Box created testsets will have this as true. CsvCompiler
323
+ // - throws less error (Box reads csv files as utterances, and convo. Compiler cant throw exception if a file is correct, but box tries to load it with incorrect script type)
324
+ // 4 or more colums are compiled just as utterances.
325
+ SCRIPTING_CSV_LEGACY_MODE_OFF: 'SCRIPTING_CSV_LEGACY_MODE_OFF',
322
326
  SCRIPTING_CSV_DELIMITER: 'SCRIPTING_CSV_DELIMITER',
323
327
  SCRIPTING_CSV_SKIP_HEADER: 'SCRIPTING_CSV_SKIP_HEADER',
324
328
  SCRIPTING_CSV_QUOTE: 'SCRIPTING_CSV_QUOTE',
@@ -328,6 +332,9 @@ var Capabilities = {
328
332
  SCRIPTING_CSV_MULTIROW_COLUMN_TEXT: 'SCRIPTING_CSV_MULTIROW_COLUMN_TEXT',
329
333
  SCRIPTING_CSV_QA_COLUMN_QUESTION: 'SCRIPTING_CSV_QA_COLUMN_QUESTION',
330
334
  SCRIPTING_CSV_QA_COLUMN_ANSWER: 'SCRIPTING_CSV_QA_COLUMN_ANSWER',
335
+ SCRIPTING_CSV_UTTERANCE_STARTROW: 'SCRIPTING_CSV_UTTERANCE_STARTROW',
336
+ SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER: 'SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER',
337
+ SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY: 'SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY',
331
338
  SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT',
332
339
  SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
333
340
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
@@ -469,6 +476,7 @@ Capabilities.SCRIPTING_XLSX_SHEETNAMES;
469
476
  Capabilities.SCRIPTING_XLSX_SHEETNAMES_PCONVOS;
470
477
  Capabilities.SCRIPTING_XLSX_SHEETNAMES_UTTERANCES;
471
478
  Capabilities.SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY;
479
+ Capabilities.SCRIPTING_CSV_LEGACY_MODE_OFF;
472
480
  Capabilities.SCRIPTING_CSV_DELIMITER;
473
481
  Capabilities.SCRIPTING_CSV_SKIP_HEADER;
474
482
  Capabilities.SCRIPTING_CSV_QUOTE;
@@ -478,6 +486,9 @@ Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER;
478
486
  Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT;
479
487
  Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION;
480
488
  Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER;
489
+ Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW;
490
+ Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER;
491
+ Capabilities.SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY;
481
492
  Capabilities.SCRIPTING_NORMALIZE_TEXT;
482
493
  Capabilities.SCRIPTING_ENABLE_MEMORY;
483
494
  Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS;
@@ -1220,7 +1231,7 @@ LogicHookConsts.DEFAULT_USER_INPUTS;
1220
1231
  const {
1221
1232
  NodeVM: NodeVM$2
1222
1233
  } = vm2__default["default"];
1223
- const debug$k = debug__default["default"]('botium-core-asserterUtils');
1234
+ const debug$l = debug__default["default"]('botium-core-asserterUtils');
1224
1235
  const {
1225
1236
  BotiumError: BotiumError$6
1226
1237
  } = BotiumError_1;
@@ -1285,7 +1296,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1285
1296
  _fetchAsserters() {
1286
1297
  this.caps[Capabilities.ASSERTERS].forEach(asserter => {
1287
1298
  if (this.asserters[asserter.ref]) {
1288
- debug$k(`${asserter.ref} asserter already exists, overwriting.`);
1299
+ debug$l(`${asserter.ref} asserter already exists, overwriting.`);
1289
1300
  }
1290
1301
 
1291
1302
  this.asserters[asserter.ref] = this._loadClass(asserter, 'asserter');
@@ -1299,7 +1310,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1299
1310
  _fetchLogicHooks() {
1300
1311
  this.caps[Capabilities.LOGIC_HOOKS].forEach(logicHook => {
1301
1312
  if (this.logicHooks[logicHook.ref]) {
1302
- debug$k(`${logicHook.ref} logic hook already exists, overwriting.`);
1313
+ debug$l(`${logicHook.ref} logic hook already exists, overwriting.`);
1303
1314
  }
1304
1315
 
1305
1316
  this.logicHooks[logicHook.ref] = this._loadClass(logicHook, 'logichook');
@@ -1313,7 +1324,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1313
1324
  _fetchUserInputs() {
1314
1325
  this.caps[Capabilities.USER_INPUTS].forEach(userInput => {
1315
1326
  if (this.userInputs[userInput.ref]) {
1316
- debug$k(`${userInput.ref} userinput already exists, overwriting.`);
1327
+ debug$l(`${userInput.ref} userinput already exists, overwriting.`);
1317
1328
  }
1318
1329
 
1319
1330
  this.userInputs[userInput.ref] = this._loadClass(userInput, 'userinput');
@@ -1456,7 +1467,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1456
1467
  });
1457
1468
  return vm.run(script);
1458
1469
  } catch (err) {
1459
- throw new Error(`${err.message || err}`);
1470
+ throw new Error(`Script ${key} is not valid - ${err.message || err}`);
1460
1471
  }
1461
1472
  } else {
1462
1473
  throw new Error(`Script "${key}" is not valid - only functions and javascript code accepted`);
@@ -1547,7 +1558,7 @@ var LogicHookUtils_1 = class LogicHookUtils {
1547
1558
  }
1548
1559
  }
1549
1560
 
1550
- loadErr.forEach(debug$k);
1561
+ loadErr.forEach(debug$l);
1551
1562
  }
1552
1563
 
1553
1564
  throw new Error(`Failed to fetch ${ref} ${hookType}, no idea how to load ...`);
@@ -1863,6 +1874,14 @@ const flatString = str => {
1863
1874
  return str ? str.split('\n').map(s => s.trim()).join(' ') : '';
1864
1875
  };
1865
1876
 
1877
+ const _formatAppendArgs = args => {
1878
+ return args ? ` ${args.map(a => lodash__default["default"].isString(a) ? a.replace(/\|/g, '\\|') : `${a}`).join('|')}` : '';
1879
+ };
1880
+
1881
+ const _parseArgs = str => {
1882
+ return str && str.length > 0 && str.replace(/\\\|/g, '###ESCAPESPLIT###').split('|').map(s => s.replace(/###ESCAPESPLIT###/g, '|').trim()) || [];
1883
+ };
1884
+
1866
1885
  const linesToConvoStep$5 = (lines, sender, context, eol, singleLineMode = false) => {
1867
1886
  if (!validateSender$1(sender)) throw new Error(`Failed to parse conversation. Section "${sender}" unknown.`);
1868
1887
  const convoStep = {
@@ -1904,7 +1923,7 @@ const linesToConvoStep$5 = (lines, sender, context, eol, singleLineMode = false)
1904
1923
  const name = logicLine.split(' ')[0];
1905
1924
 
1906
1925
  if (sender !== 'me' && context.IsAsserterValid(name)) {
1907
- const args = logicLine.length > name.length ? logicLine.substr(name.length + 1).split('|').map(a => a.trim()) : [];
1926
+ const args = logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [];
1908
1927
  convoStep.asserters.push({
1909
1928
  name,
1910
1929
  args,
@@ -1912,14 +1931,14 @@ const linesToConvoStep$5 = (lines, sender, context, eol, singleLineMode = false)
1912
1931
  optional
1913
1932
  });
1914
1933
  } else if (sender === 'me' && context.IsUserInputValid(name)) {
1915
- const args = logicLine.length > name.length ? logicLine.substr(name.length + 1).split('|').map(a => a.trim()) : [];
1934
+ const args = logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [];
1916
1935
  convoStep.userInputs.push({
1917
1936
  name,
1918
1937
  args
1919
1938
  });
1920
1939
  textLinesAccepted = false;
1921
1940
  } else if (context.IsLogicHookValid(name)) {
1922
- const args = logicLine.length > name.length ? logicLine.substr(name.length + 1).split('|').map(a => a.trim()) : [];
1941
+ const args = logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [];
1923
1942
  convoStep.logicHooks.push({
1924
1943
  name,
1925
1944
  args
@@ -2269,7 +2288,7 @@ const convoStepToLines$2 = step => {
2269
2288
 
2270
2289
  if (step.sender === 'me') {
2271
2290
  step.forms && step.forms.filter(form => form.value).forEach(form => {
2272
- lines.push(`FORM ${form.name}|${form.value}`);
2291
+ lines.push(`FORM${_formatAppendArgs([form.name, form.value])}`);
2273
2292
  });
2274
2293
 
2275
2294
  if (step.buttons && step.buttons.length > 0) {
@@ -2281,18 +2300,18 @@ const convoStepToLines$2 = step => {
2281
2300
  }
2282
2301
 
2283
2302
  step.userInputs && step.userInputs.forEach(userInput => {
2284
- lines.push(userInput.name + (userInput.args ? ' ' + userInput.args.join('|') : ''));
2303
+ lines.push(userInput.name + _formatAppendArgs(userInput.args));
2285
2304
  });
2286
2305
  step.logicHooks && step.logicHooks.forEach(logicHook => {
2287
- lines.push(logicHook.name + (logicHook.args ? ' ' + logicHook.args.join('|') : ''));
2306
+ lines.push(logicHook.name + _formatAppendArgs(logicHook.args));
2288
2307
  });
2289
2308
  } else {
2290
2309
  if (step.messageText) {
2291
2310
  lines.push((step.optional ? '?' : '') + (step.not ? '!' : '') + step.messageText);
2292
2311
  }
2293
2312
 
2294
- if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS ' + step.buttons.filter(b => b.text).map(b => flatString(b.text)).join('|'));
2295
- if (step.media && step.media.length > 0) lines.push('MEDIA ' + step.media.filter(m => !m.buffer && m.mediaUri).map(m => m.mediaUri).join('|'));
2313
+ if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS' + _formatAppendArgs(step.buttons.filter(b => b.text).map(b => flatString(b.text))));
2314
+ if (step.media && step.media.length > 0) lines.push('MEDIA' + _formatAppendArgs(step.media.filter(m => !m.buffer && m.mediaUri).map(m => m.mediaUri)));
2296
2315
 
2297
2316
  if (step.cards && step.cards.length > 0) {
2298
2317
  step.cards.forEach(c => {
@@ -2300,17 +2319,17 @@ const convoStepToLines$2 = step => {
2300
2319
  if (c.text) cardTexts = cardTexts.concat(lodash__default["default"].isArray(c.text) ? c.text : [c.text]);
2301
2320
  if (c.subtext) cardTexts = cardTexts.concat(lodash__default["default"].isArray(c.subtext) ? c.subtext : [c.subtext]);
2302
2321
  if (c.content) cardTexts = cardTexts.concat(lodash__default["default"].isArray(c.content) ? c.content : [c.content]);
2303
- if (cardTexts.length > 0) lines.push('CARDS ' + cardTexts.map(c => flatString(c)).join('|'));
2304
- if (c.buttons && c.buttons.length > 0) lines.push('BUTTONS ' + c.buttons.filter(b => b.text).map(b => flatString(b.text)).join('|'));
2322
+ if (cardTexts.length > 0) lines.push('CARDS' + _formatAppendArgs(cardTexts.map(c => flatString(c))));
2323
+ if (c.buttons && c.buttons.length > 0) lines.push('BUTTONS' + _formatAppendArgs(c.buttons.filter(b => b.text).map(b => flatString(b.text))));
2305
2324
  if (c.image && !c.image.buffer && c.image.mediaUri) lines.push('MEDIA ' + c.image.mediaUri);
2306
2325
  });
2307
2326
  }
2308
2327
 
2309
2328
  step.asserters && step.asserters.forEach(asserter => {
2310
- lines.push((asserter.optional ? '?' : '') + (asserter.not ? '!' : '') + asserter.name + (asserter.args ? ' ' + asserter.args.join('|') : ''));
2329
+ lines.push((asserter.optional ? '?' : '') + (asserter.not ? '!' : '') + asserter.name + _formatAppendArgs(asserter.args));
2311
2330
  });
2312
2331
  step.logicHooks && step.logicHooks.forEach(logicHook => {
2313
- lines.push(logicHook.name + (logicHook.args ? ' ' + logicHook.args.join('|') : ''));
2332
+ lines.push(logicHook.name + _formatAppendArgs(logicHook.args));
2314
2333
  });
2315
2334
  }
2316
2335
 
@@ -2398,7 +2417,7 @@ var helper = {
2398
2417
  linesToScriptingMemories: linesToScriptingMemories$2
2399
2418
  };
2400
2419
 
2401
- const debug$j = debug__default["default"]('botium-core-ScriptingMemory');
2420
+ const debug$k = debug__default["default"]('botium-core-ScriptingMemory');
2402
2421
  const {
2403
2422
  v1: uuidv1
2404
2423
  } = uuid__default["default"];
@@ -2656,7 +2675,7 @@ const extractVarNames = text => {
2656
2675
  };
2657
2676
 
2658
2677
  const fill = (container, scriptingMemory, result, utterance, scriptingEvents) => {
2659
- debug$j(`fill start: ${util__default["default"].inspect(scriptingMemory)}`);
2678
+ debug$k(`fill start: ${util__default["default"].inspect(scriptingMemory)}`);
2660
2679
  let varRegex;
2661
2680
 
2662
2681
  switch (container.caps[Capabilities.SCRIPTING_MEMORY_MATCHING_MODE]) {
@@ -2700,14 +2719,14 @@ const fill = (container, scriptingMemory, result, utterance, scriptingEvents) =>
2700
2719
  const varName = varMatches[i - 1];
2701
2720
 
2702
2721
  if (RESERVED_WORDS.indexOf(varName) >= 0) {
2703
- debug$j(`fill Variable "${varName}" is not overwritten, because it is reserved word. `);
2722
+ debug$k(`fill Variable "${varName}" is not overwritten, because it is reserved word. `);
2704
2723
  } else {
2705
2724
  scriptingMemory[varName] = resultMatches[i];
2706
2725
  }
2707
2726
  }
2708
2727
  }
2709
2728
  });
2710
- debug$j(`fill end: ${util__default["default"].inspect(scriptingMemory)}`);
2729
+ debug$k(`fill end: ${util__default["default"].inspect(scriptingMemory)}`);
2711
2730
  }
2712
2731
  };
2713
2732
 
@@ -2726,6 +2745,47 @@ ScriptingMemory.extractVarNames;
2726
2745
  ScriptingMemory.RESERVED_WORDS;
2727
2746
  ScriptingMemory.SCRIPTING_FUNCTIONS;
2728
2747
 
2748
+ const debug$j = debug__default["default"]('botium-core-RetryHelper');
2749
+ var RetryHelper_1 = class RetryHelper {
2750
+ constructor(caps, section, options = {}) {
2751
+ this.retryErrorPatterns = [];
2752
+ const onErrorRegexp = caps[`RETRY_${section.toUpperCase()}_ONERROR_REGEXP`] || [];
2753
+
2754
+ if (onErrorRegexp) {
2755
+ if (lodash__default["default"].isArray(onErrorRegexp)) {
2756
+ onErrorRegexp.forEach(r => {
2757
+ if (lodash__default["default"].isString(r)) this.retryErrorPatterns.push(new RegExp(r, 'i'));else this.retryErrorPatterns.push(r);
2758
+ });
2759
+ } else if (lodash__default["default"].isString(onErrorRegexp)) {
2760
+ this.retryErrorPatterns.push(new RegExp(onErrorRegexp, 'i'));
2761
+ } else {
2762
+ this.retryErrorPatterns.push(onErrorRegexp);
2763
+ }
2764
+ } // to turn on retries, NUMRETRIES or ONERROR_REGEXP has to be set
2765
+
2766
+
2767
+ this.retrySettings = {
2768
+ retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || (!lodash__default["default"].isNil(options.numRetries) ? options.numRetries : this.retryErrorPatterns.length === 0 ? 0 : 1),
2769
+ factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || (lodash__default["default"].isNil(options.factor) ? 1 : options.factor),
2770
+ minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || (lodash__default["default"].isNil(options.minTimeout) ? 1000 : options.minTimeout)
2771
+ };
2772
+ debug$j(`Retry for ${section} is ${this.retrySettings.retries > 0 ? 'enabled' : 'disabled'}. Settings: ${JSON.stringify(this.retrySettings)} Patterns: ${JSON.stringify(this.retryErrorPatterns.map(r => r.toString()))}`);
2773
+ }
2774
+
2775
+ shouldRetry(err) {
2776
+ if (!err) return false;
2777
+ if (this.retryErrorPatterns.length === 0) return true;
2778
+ const errString = util__default["default"].inspect(err);
2779
+
2780
+ for (const re of this.retryErrorPatterns) {
2781
+ if (errString.match(re)) return true;
2782
+ }
2783
+
2784
+ return false;
2785
+ }
2786
+
2787
+ };
2788
+
2729
2789
  const debug$i = debug__default["default"]('botium-core-Convo');
2730
2790
  const {
2731
2791
  BotiumError: BotiumError$4,
@@ -2951,6 +3011,26 @@ class Convo$6 {
2951
3011
  }
2952
3012
 
2953
3013
  async Run(container) {
3014
+ const retryHelper = new RetryHelper_1(container.caps, 'CONVO');
3015
+ return promiseRetry__default["default"](async (retry, number) => {
3016
+ return this.RunImpl(container).catch(err => {
3017
+ const retryRemaining = retryHelper.retrySettings.retries - number + 1;
3018
+
3019
+ if (retryHelper.shouldRetry(err)) {
3020
+ debug$i(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry ${retryRemaining > 0 ? 'enabled' : 'disabled'} (remaining #${retryRemaining}/${retryHelper.retrySettings.retries}, criterion matches)`);
3021
+ retry(err);
3022
+ } else {
3023
+ if (retryHelper.retryErrorPatterns.length > 0) {
3024
+ debug$i(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry 'disabled' (remaining (#${retryRemaining}/${retryHelper.retrySettings.retries}), criterion does not match)`);
3025
+ }
3026
+
3027
+ throw err;
3028
+ }
3029
+ });
3030
+ }, retryHelper.retrySettings);
3031
+ }
3032
+
3033
+ async RunImpl(container) {
2954
3034
  const transcript = new Transcript({
2955
3035
  steps: [],
2956
3036
  attachments: [],
@@ -3436,7 +3516,7 @@ class Convo$6 {
3436
3516
  const transcriptStepErrs = transcript.steps.filter(s => s.err).map(s => s.err);
3437
3517
 
3438
3518
  if (transcriptStepErrs && transcriptStepErrs.length > 0) {
3439
- transcript.err = botiumErrorFromList$1([transcriptStepErrs, transcript.err].filter(e => e), {});
3519
+ transcript.err = botiumErrorFromList$1([...transcriptStepErrs.filter(err => err !== transcript.err), transcript.err].filter(e => e), {});
3440
3520
  }
3441
3521
  }
3442
3522
  }
@@ -3622,42 +3702,6 @@ var Convo_1 = {
3622
3702
  ConvoStep: ConvoStep$1
3623
3703
  };
3624
3704
 
3625
- var RetryHelper_1 = class RetryHelper {
3626
- constructor(caps, section) {
3627
- this.retrySettings = {
3628
- retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || 1,
3629
- factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || 1,
3630
- minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || 1000
3631
- };
3632
- this.retryErrorPatterns = [];
3633
- const onErrorRegexp = caps[`RETRY_${section.toUpperCase()}_ONERROR_REGEXP`] || [];
3634
-
3635
- if (onErrorRegexp) {
3636
- if (lodash__default["default"].isArray(onErrorRegexp)) {
3637
- onErrorRegexp.forEach(r => {
3638
- if (lodash__default["default"].isString(r)) this.retryErrorPatterns.push(new RegExp(r, 'i'));else this.retryErrorPatterns.push(r);
3639
- });
3640
- } else if (lodash__default["default"].isString(onErrorRegexp)) {
3641
- this.retryErrorPatterns.push(new RegExp(onErrorRegexp, 'i'));
3642
- } else {
3643
- this.retryErrorPatterns.push(onErrorRegexp);
3644
- }
3645
- }
3646
- }
3647
-
3648
- shouldRetry(err) {
3649
- if (!err || this.retryErrorPatterns.length === 0) return false;
3650
- const errString = util__default["default"].inspect(err);
3651
-
3652
- for (const re of this.retryErrorPatterns) {
3653
- if (errString.match(re)) return true;
3654
- }
3655
-
3656
- return false;
3657
- }
3658
-
3659
- };
3660
-
3661
3705
  const {
3662
3706
  toString,
3663
3707
  quoteRegexpString
@@ -5330,6 +5374,8 @@ var CompilerCsv_1 = class CompilerCsv extends CompilerBase_1 {
5330
5374
  return [];
5331
5375
  }
5332
5376
 
5377
+ const legacyModeOn = !this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_LEGACY_MODE_OFF, false);
5378
+
5333
5379
  let delimiter = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_DELIMITER);
5334
5380
 
5335
5381
  if (!delimiter) {
@@ -5357,7 +5403,8 @@ var CompilerCsv_1 = class CompilerCsv extends CompilerBase_1 {
5357
5403
  delimiter,
5358
5404
  escape: this.caps[Capabilities.SCRIPTING_CSV_ESCAPE],
5359
5405
  quote: this.caps[Capabilities.SCRIPTING_CSV_QUOTE],
5360
- columns: false
5406
+ columns: false,
5407
+ relax_column_count: true
5361
5408
  });
5362
5409
  } catch (err) {
5363
5410
  throw new Error(`Invalid CSV: ${err.message || err}`);
@@ -5367,120 +5414,175 @@ var CompilerCsv_1 = class CompilerCsv extends CompilerBase_1 {
5367
5414
  return [];
5368
5415
  }
5369
5416
 
5370
- if (rows[0].length === 1) {
5371
- debug$c('Found 1-column CSV file, treating it as utterance file');
5417
+ const columnCount = rows[0].length;
5418
+ debug$c(`Legacy mode ${legacyModeOn ? 'on' : 'off'} rows ${rows.length} columns ${columnCount}`);
5372
5419
 
5373
- if (scriptType === Constants.SCRIPTING_TYPE_UTTERANCES) {
5374
- const result = [{
5375
- name: rows[0][0],
5376
- utterances: rows.slice(1).map(r => r[0])
5377
- }];
5378
- this.context.AddUtterances(result);
5379
- return result;
5380
- } else {
5420
+ if (scriptType === Constants.SCRIPTING_TYPE_CONVO || scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
5421
+ if (columnCount === 1 || !legacyModeOn && columnCount > 3) {
5422
+ debug$c(`Invalid column count '${columnCount}' in convo mode`);
5381
5423
  return [];
5382
5424
  }
5383
- }
5384
5425
 
5385
- if (scriptType !== Constants.SCRIPTING_TYPE_CONVO && scriptType !== Constants.SCRIPTING_TYPE_PCONVO) {
5386
- return [];
5387
- }
5426
+ let header = null;
5388
5427
 
5389
- let header = null;
5428
+ if (rows.length > 0 && this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER]) {
5429
+ header = rows[0];
5430
+ rows = rows.slice(1);
5431
+ }
5390
5432
 
5391
- if (rows.length > 0 && this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER]) {
5392
- header = rows[0];
5393
- rows = rows.slice(1);
5394
- }
5433
+ if (rows.length === 0) {
5434
+ debug$c('Datarows not found in convo mode');
5435
+ return [];
5436
+ }
5395
5437
 
5396
- if (rows.length === 0) {
5397
- return [];
5398
- }
5438
+ const lineNumberBase = this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER] ? 2 : 1;
5399
5439
 
5400
- const lineNumberBase = this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER] ? 2 : 1;
5440
+ if (columnCount === 2) {
5441
+ let colQuestion = DEFAULT_QA_COLUMN_QUESTION;
5442
+ let colAnswer = DEFAULT_QA_COLUMN_ANSWER;
5401
5443
 
5402
- if (rows[0].length === 2) {
5403
- debug$c('Found 2-column CSV file, treating it as question/answer file');
5404
- let colQuestion = DEFAULT_QA_COLUMN_QUESTION;
5405
- let colAnswer = DEFAULT_QA_COLUMN_ANSWER;
5444
+ if (header) {
5445
+ if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION] !== undefined) {
5446
+ colQuestion = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION]);
5447
+ }
5406
5448
 
5407
- if (header) {
5408
- if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION] !== undefined) {
5409
- colQuestion = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION]);
5449
+ if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER] !== undefined) {
5450
+ colAnswer = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER]);
5451
+ }
5410
5452
  }
5411
5453
 
5412
- if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER] !== undefined) {
5413
- colAnswer = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER]);
5414
- }
5415
- }
5454
+ const convos = rows.map((row, i) => new Convo$3(this.context, {
5455
+ header: {
5456
+ name: `L${i + lineNumberBase}`
5457
+ },
5458
+ conversation: [Object.assign({}, linesToConvoStep$2([row[colQuestion]], 'me', this.context, undefined, true), {
5459
+ stepTag: `L${i + lineNumberBase}-Question`
5460
+ }), Object.assign({}, linesToConvoStep$2([row[colAnswer]], 'bot', this.context, undefined, true), {
5461
+ stepTag: `L${i + lineNumberBase}-Answer`
5462
+ })]
5463
+ }));
5416
5464
 
5417
- const convos = rows.map((row, i) => new Convo$3(this.context, {
5418
- header: {
5419
- name: `L${i + lineNumberBase}`
5420
- },
5421
- conversation: [Object.assign({}, linesToConvoStep$2([row[colQuestion]], 'me', this.context, undefined, true), {
5422
- stepTag: `L${i + lineNumberBase}-Question`
5423
- }), Object.assign({}, linesToConvoStep$2([row[colAnswer]], 'bot', this.context, undefined, true), {
5424
- stepTag: `L${i + lineNumberBase}-Answer`
5425
- })]
5426
- }));
5465
+ if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
5466
+ this.context.AddConvos(convos);
5467
+ } else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
5468
+ this.context.AddPartialConvos(convos);
5469
+ }
5427
5470
 
5428
- if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
5429
- this.context.AddConvos(convos);
5430
- } else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
5431
- this.context.AddPartialConvos(convos);
5471
+ debug$c(`Found 2-column CSV file, treating it as question/answer file, extracted ${convos.length} convos`);
5472
+ return convos;
5432
5473
  }
5433
5474
 
5434
- return convos;
5435
- }
5475
+ if (columnCount >= 3) {
5476
+ let colConversationId = DEFAULT_MULTIROW_COLUMN_CONVERSATION;
5477
+ let colSender = DEFAULT_MULTIROW_COLUMN_SENDER;
5478
+ let colText = DEFAULT_MULTIROW_COLUMN_TEXT;
5436
5479
 
5437
- if (rows[0].length >= 3) {
5438
- debug$c('Found 3-column CSV file, treating it as multi-row conversation file');
5439
- let colConversationId = DEFAULT_MULTIROW_COLUMN_CONVERSATION;
5440
- let colSender = DEFAULT_MULTIROW_COLUMN_SENDER;
5441
- let colText = DEFAULT_MULTIROW_COLUMN_TEXT;
5480
+ if (header) {
5481
+ if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID] !== undefined) {
5482
+ colConversationId = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID]);
5483
+ }
5442
5484
 
5443
- if (header) {
5444
- if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID] !== undefined) {
5445
- colConversationId = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID]);
5446
- }
5485
+ if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER] !== undefined) {
5486
+ colSender = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER]);
5487
+ }
5447
5488
 
5448
- if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER] !== undefined) {
5449
- colSender = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER]);
5489
+ if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT] !== undefined) {
5490
+ colText = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT]);
5491
+ }
5450
5492
  }
5451
5493
 
5452
- if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT] !== undefined) {
5453
- colText = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT]);
5494
+ const conversationIds = lodash__default["default"].uniq(rows.map(r => r[colConversationId]));
5495
+
5496
+ const convos = conversationIds.map(conversationId => {
5497
+ const convoRows = rows.map((row, i) => {
5498
+ if (row[colConversationId] === conversationId) {
5499
+ return Object.assign({}, linesToConvoStep$2([row[colText]], row[colSender], this.context, undefined, true), {
5500
+ stepTag: `L${i + lineNumberBase}`
5501
+ });
5502
+ }
5503
+
5504
+ return null;
5505
+ }).filter(c => c);
5506
+ return new Convo$3(this.context, {
5507
+ header: {
5508
+ name: conversationId
5509
+ },
5510
+ conversation: convoRows
5511
+ });
5512
+ });
5513
+
5514
+ if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
5515
+ this.context.AddConvos(convos);
5516
+ } else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
5517
+ this.context.AddPartialConvos(convos);
5454
5518
  }
5519
+
5520
+ debug$c(`Found 3-column CSV file, treating it as multi-row conversation file, extracted ${convos.length} convos`);
5521
+ return convos;
5522
+ }
5523
+ } else if (scriptType === Constants.SCRIPTING_TYPE_UTTERANCES) {
5524
+ if (columnCount === 2 || columnCount === 3 || legacyModeOn && columnCount > 4) {
5525
+ debug$c(`Invalid column count '${columnCount}' in utterances mode`);
5526
+ return [];
5455
5527
  }
5456
5528
 
5457
- const conversationIds = lodash__default["default"].uniq(rows.map(r => r[colConversationId]));
5529
+ const result = [];
5530
+ const startRow = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW, 2) - 1;
5458
5531
 
5459
- const convos = conversationIds.map(conversationId => {
5460
- const convoRows = rows.map((row, i) => {
5461
- if (row[colConversationId] === conversationId) {
5462
- return Object.assign({}, linesToConvoStep$2([row[colText]], row[colSender], this.context, undefined, true), {
5463
- stepTag: `L${i + lineNumberBase}`
5464
- });
5532
+ const startRowHeader = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER);
5533
+
5534
+ const stopOnEmpty = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY);
5535
+
5536
+ for (let col = 0; col < columnCount; col++) {
5537
+ const name = rows[0][col];
5538
+
5539
+ if (!name || name.trim().length === 0) {
5540
+ debug$c(`Column ${col + 1} has no header, skipping`);
5541
+ continue;
5542
+ }
5543
+
5544
+ const uttStruct = {
5545
+ name,
5546
+ utterances: []
5547
+ };
5548
+ let skip = !!startRowHeader;
5549
+
5550
+ const getData = row => {
5551
+ return rows[row][col] ? rows[row][col].trim() : false;
5552
+ }; //
5553
+
5554
+
5555
+ for (let row = startRow; row < rows.length && (skip || !stopOnEmpty || !!getData(row)); row++) {
5556
+ // eslint-disable-line no-unmodified-loop-condition
5557
+ const data = getData(row);
5558
+
5559
+ if (!data) {
5560
+ continue;
5465
5561
  }
5466
5562
 
5467
- return null;
5468
- }).filter(c => c);
5469
- return new Convo$3(this.context, {
5470
- header: {
5471
- name: conversationId
5472
- },
5473
- conversation: convoRows
5474
- });
5475
- });
5563
+ if (!skip) {
5564
+ uttStruct.utterances.push(data);
5565
+ } else {
5566
+ if (startRowHeader === rows[row][col]) {
5567
+ skip = false;
5568
+ }
5569
+ }
5570
+ }
5476
5571
 
5477
- if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
5478
- this.context.AddConvos(convos);
5479
- } else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
5480
- this.context.AddPartialConvos(convos);
5572
+ if (uttStruct.utterances.length === 0) {
5573
+ // liveperson, skipping meta intents
5574
+ debug$c(`Column ${col + 1} has no utterances, skipping`);
5575
+ continue;
5576
+ }
5577
+
5578
+ result.push(uttStruct);
5481
5579
  }
5482
5580
 
5483
- return convos;
5581
+ debug$c(`Multi-column utterance file, extracted ${result.length} utterances`);
5582
+ this.context.AddUtterances(result);
5583
+ return result;
5584
+ } else {
5585
+ return [];
5484
5586
  }
5485
5587
  }
5486
5588
 
@@ -5807,7 +5909,7 @@ const {
5807
5909
  const {
5808
5910
  getMatchFunction
5809
5911
  } = MatchFunctions;
5810
- const globPattern = '**/+(*.convo.txt|*.utterances.txt|*.pconvo.txt|*.scriptingmemory.txt|*.xlsx|*.xlsm|*.convo.csv|*.pconvo.csv|*.yaml|*.yml|*.json|*.md|*.markdown)';
5912
+ const globPattern = '**/+(*.convo.txt|*.utterances.txt|*.pconvo.txt|*.scriptingmemory.txt|*.xlsx|*.xlsm|*.convo.csv|*.pconvo.csv|*.utterances.csv|*.yaml|*.yml|*.json|*.md|*.markdown)';
5811
5913
  const skipPattern = /^skip[.\-_]/i;
5812
5914
 
5813
5915
  const p = (retryHelper, fn) => {
@@ -6502,6 +6604,10 @@ var ScriptingProvider_1 = class ScriptingProvider {
6502
6604
  result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_CONVO);
6503
6605
  } else if (filename.endsWith('.pconvo.csv')) {
6504
6606
  result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_PCONVO);
6607
+ } else if (filename.endsWith('.pconvo.csv')) {
6608
+ result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_PCONVO);
6609
+ } else if (filename.endsWith('.utterance.csv')) {
6610
+ result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_UTTERANCES);
6505
6611
  } else if (filename.endsWith('.yaml') || filename.endsWith('.yml')) {
6506
6612
  result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_YAML, [Constants.SCRIPTING_TYPE_UTTERANCES, Constants.SCRIPTING_TYPE_PCONVO, Constants.SCRIPTING_TYPE_CONVO, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY]);
6507
6613
  } else if (filename.endsWith('.json')) {