@scout9/app 1.0.0-alpha.0.8.8 → 1.0.0-alpha.0.8.9

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.
package/dist/spirits.cjs CHANGED
@@ -2,8 +2,1104 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var spirits = require("./spirits-2d7d7919.cjs");
5
+ var _rollupPluginBabelHelpers = require("./_rollupPluginBabelHelpers-606d8129.cjs");
6
6
 
7
+ var _excluded = ["keywords"];
8
+ /**
9
+ * @typedef {Object} Document
10
+ * @property {string} id
11
+ */
7
12
 
13
+ /**
14
+ * Represents a change with before and after states of a given type.
15
+ * @template Type The type of the before and after properties.
16
+ * @typedef {Object} Change
17
+ * @property {Type} before - The state before the change.
18
+ * @property {Type} after - The state after the change.
19
+ */
8
20
 
9
- exports.Spirits = spirits.Spirits;
21
+ /**
22
+ * @typedef {Object} ConversationData
23
+ * @property {import('@scout9/app').Scout9ProjectBuildConfig} config - used to define generation and extract persona metadata
24
+ * @property {import('@scout9/app').Conversation} conversation
25
+ * @property {Array<import('@scout9/admin').Message>} messages
26
+ * @property {import('@scout9/admin').Message} message - the message sent by the customer (should exist in messages)
27
+ * @property {import('@scout9/app').Customer} customer
28
+ * @property {import('@scout9/app').ConversationProgress} progress - progress checklist for manual/auto ingress workflows
29
+ * @property {any} context - event context
30
+ */
31
+
32
+ /**
33
+ * @typedef {Object} ParseOutput
34
+ * @property {Array<import('@scout9/admin').Message>} messages
35
+ * @property {import('@scout9/app').Conversation} conversation
36
+ * @property {import('@scout9/admin').Message} message
37
+ * @property {any} context
38
+ */
39
+
40
+ /**
41
+ * @typedef {Object} WorkflowOutput
42
+ * @property {Array<import('@scout9/app').WorkflowResponseSlot>} slots
43
+ * @property {Array<import('@scout9/admin').Message>} messages
44
+ * @property {import('@scout9/app').Conversation} conversation
45
+ * @property {any} context
46
+ */
47
+
48
+ /**
49
+ * @typedef {Object} GenerateOutput
50
+ * @property {import('@scout9/admin').GenerateResponse | undefined} generate
51
+ * @property {Array<import('@scout9/admin').Message>} messages
52
+ * @property {import('@scout9/app').Conversation} conversation
53
+ * @property {any} context
54
+ */
55
+
56
+ /**
57
+ * @callback ParseFun
58
+ * @param {string} message - message to send
59
+ * @param {string | undefined} language - language to parse in, defaults to "en" for english
60
+ * @returns {Promise<import('@scout9/admin').ParseResponse>}
61
+ */
62
+
63
+ /**
64
+ * @callback ContextualizerFun
65
+ * @param {Pick<import('@scout9/app').WorkflowEvent, 'messages' | 'conversation'>} args - message to send
66
+ * @returns {Promise<import('@scout9/app').WorkflowEvent['messages']>}
67
+ */
68
+
69
+ /**
70
+ * @callback WorkflowFun
71
+ * @param {import('@scout9/app').WorkflowEvent} event - conversation data
72
+ * @returns {Promise<import('@scout9/app').WorkflowResponse>}
73
+ */
74
+
75
+ /**
76
+ * @callback GenerateFun
77
+ * @param {import('@scout9/admin').GenerateRequestOneOf1} data - data to generate from
78
+ * @returns {Promise<import('@scout9/admin').GenerateResponse>}
79
+ */
80
+
81
+ /**
82
+ * @callback TransformerFun
83
+ * @param {import('@scout9/admin').PmtTransformRequest} data - data to generate from
84
+ * @returns {Promise<import('@scout9/admin').PmtTransformResponse>}
85
+ */
86
+
87
+ /**
88
+ * @callback IdGeneratorFun
89
+ * @param {import('@scout9/admin').Message['role']} prefix
90
+ * @returns {string}
91
+ */
92
+
93
+ /**
94
+ * @callback StatusCallback
95
+ * @param {string} message
96
+ * @param {'info' | 'warn' | 'error' | 'success' | undefined} [level]
97
+ * @param {string | undefined} [type]
98
+ * @param {any | undefined} [payload]
99
+ * @returns {void}
100
+ */
101
+
102
+ /**
103
+ * @typedef {Object} CustomerSpiritCallbacks
104
+ * @property {ParseFun} parser
105
+ * @property {ContextualizerFun} contextualizer
106
+ * @property {WorkflowFun} workflow
107
+ * @property {GenerateFun} generator
108
+ * @property {TransformerFun} transformer
109
+ * @property {IdGeneratorFun} idGenerator
110
+ * @property {StatusCallback | undefined} [progress]
111
+ */
112
+
113
+ /**
114
+ * @typedef {Object} ConversationEvent
115
+ * @property {(Change<import('@scout9/app').Conversation> & {
116
+ * forwardNote?: string;
117
+ * forward?: import('@scout9/app').WorkflowResponseSlot['forward'];
118
+ * })} conversation
119
+ * @property {Change<Array<import('@scout9/admin').Message>>} messages
120
+ * @property {Change<any>} context
121
+ * @property {Change<import('@scout9/admin').Message>} message
122
+ * @property {Array<import('@scout9/app').Followup>} followup
123
+ * @property {Array<import('@scout9/app').EntityContextUpsert>} entityContextUpsert
124
+ */
125
+ var Spirits = {
126
+ /**
127
+ * Customer message
128
+ * @param {ConversationData & CustomerSpiritCallbacks} input
129
+ * @param {(error: Error) => void} onError
130
+ * @returns {Promise<ConversationEvent>}
131
+ */
132
+ customer: function () {
133
+ var _customer = _rollupPluginBabelHelpers._asyncToGenerator( /*#__PURE__*/_rollupPluginBabelHelpers._regeneratorRuntime().mark(function _callee(input) {
134
+ var _recentUserMessage;
135
+ var onError,
136
+ customer,
137
+ config,
138
+ parser,
139
+ contextualizer,
140
+ workflow,
141
+ generator,
142
+ transformer,
143
+ idGenerator,
144
+ _input$progress,
145
+ progress,
146
+ messageBefore,
147
+ contextBefore,
148
+ messagesBefore,
149
+ conversationBefore,
150
+ conversation,
151
+ messages,
152
+ context,
153
+ message,
154
+ followup,
155
+ entityContextUpsert,
156
+ updateConversation,
157
+ updateContext,
158
+ userMessages,
159
+ recentUserMessage,
160
+ lockConversation,
161
+ incrementLockAttempt,
162
+ _addInstruction,
163
+ addInstruction,
164
+ persona,
165
+ invalidRoles,
166
+ parsePayload,
167
+ index,
168
+ _message,
169
+ previousUserMessages,
170
+ oldKeyCount,
171
+ newKeyCount,
172
+ noNewContext,
173
+ _messages2,
174
+ newContextMessages,
175
+ _iterator,
176
+ _step,
177
+ _loop,
178
+ slots,
179
+ hasNoInstructions,
180
+ hasNoCustomMessage,
181
+ messagesToTransform,
182
+ previousLockAttempt,
183
+ resettedIntent,
184
+ _forward,
185
+ _forwardNote,
186
+ _tasks,
187
+ _iterator2,
188
+ _step2,
189
+ _step2$value,
190
+ forward,
191
+ forwardNote,
192
+ instructions,
193
+ removeInstructions,
194
+ manualMessage,
195
+ scheduled,
196
+ resetIntent,
197
+ secondsDelay,
198
+ contextUpsert,
199
+ anticipate,
200
+ slotFollowup,
201
+ slotEntityContextUpsert,
202
+ tasks,
203
+ _slots,
204
+ map,
205
+ i,
206
+ _anticipate$i,
207
+ keywords,
208
+ _slot,
209
+ slotId,
210
+ _tasks2,
211
+ _iterator5,
212
+ _step5,
213
+ instruction,
214
+ _iterator6,
215
+ _step6,
216
+ _loop2,
217
+ manualMessageObj,
218
+ now,
219
+ generatorInput,
220
+ generatorPayload,
221
+ _generatorPayload$err,
222
+ _generatorPayload$err2,
223
+ _generatorPayload$err3,
224
+ _generatorPayload$mes,
225
+ agentMessages,
226
+ lastAgentMessage,
227
+ addedMessages,
228
+ _iterator3,
229
+ _step3,
230
+ newMessage,
231
+ _iterator4,
232
+ _step4,
233
+ transformResponse,
234
+ _agentMessages,
235
+ _lastAgentMessage,
236
+ _args3 = arguments;
237
+ return _rollupPluginBabelHelpers._regeneratorRuntime().wrap(function _callee$(_context3) {
238
+ while (1) switch (_context3.prev = _context3.next) {
239
+ case 0:
240
+ onError = _args3.length > 1 && _args3[1] !== undefined ? _args3[1] : function () {};
241
+ customer = input.customer, config = input.config, parser = input.parser, contextualizer = input.contextualizer, workflow = input.workflow, generator = input.generator, transformer = input.transformer, idGenerator = input.idGenerator, _input$progress = input.progress, progress = _input$progress === void 0 ? function (message, level, type, payload) {} : _input$progress, messageBefore = input.message, contextBefore = input.context, messagesBefore = input.messages, conversationBefore = input.conversation;
242
+ conversation = input.conversation, messages = input.messages, context = input.context, message = input.message; // Storing post process events here
243
+ followup = [];
244
+ entityContextUpsert = []; // 0. Setup Helpers
245
+ updateConversation = function updateConversation(previousConversation, conversationUpdates) {
246
+ progress('Update conversation', 'info', 'UPDATE_CONVERSATION', conversationUpdates);
247
+ return _rollupPluginBabelHelpers._objectSpread2(_rollupPluginBabelHelpers._objectSpread2({}, previousConversation), conversationUpdates);
248
+ };
249
+ updateContext = function updateContext(previousContext, newContext) {
250
+ progress('Update context', 'info', 'UPDATE_CONTEXT', newContext);
251
+ return _rollupPluginBabelHelpers._objectSpread2(_rollupPluginBabelHelpers._objectSpread2({}, previousContext), newContext);
252
+ };
253
+ userMessages = function userMessages(_messages) {
254
+ return _messages.filter(function (m) {
255
+ return m.role === 'customer' || m.role === 'user';
256
+ });
257
+ };
258
+ recentUserMessage = function recentUserMessage(_messages) {
259
+ var _userMessages = userMessages(_messages);
260
+ return _userMessages[_userMessages.length - 1];
261
+ };
262
+ lockConversation = function lockConversation(_conversation, reason) {
263
+ return updateConversation(_conversation, {
264
+ locked: true,
265
+ lockedReason: conversation.lockedReason || reason || 'Unknown'
266
+ });
267
+ };
268
+ incrementLockAttempt = function incrementLockAttempt(_conversation, _config) {
269
+ if (typeof _conversation.lockAttempts !== 'number') {
270
+ _conversation.lockAttempts = 0;
271
+ }
272
+ _conversation.lockAttempts++;
273
+ if (_conversation.lockAttempts > ((_config === null || _config === void 0 ? void 0 : _config.maxLockAttempts) || 3)) {
274
+ _conversation.locked = true;
275
+ _conversation.lockedReason = "Max lock attempts exceeded (".concat(_conversation.lockAttempts, " > ").concat((_config === null || _config === void 0 ? void 0 : _config.maxLockAttempts) || 3, ")");
276
+ }
277
+ progress('Incremented lock attempt', 'info', 'UPDATE_CONVERSATION', {
278
+ lockAttempts: _conversation.lockAttempts,
279
+ locked: _conversation.locked,
280
+ lockedReason: _conversation.lockedReason || ''
281
+ });
282
+ return _conversation;
283
+ };
284
+ _addInstruction = function _addInstruction(instruction, _messages, _conversation, _config, previousLockAttempt, id) {
285
+ var systemMessages = _messages.filter(function (m) {
286
+ return m.role === 'system';
287
+ });
288
+ var lastSystemMessage = systemMessages[systemMessages.length - 1];
289
+ var addedMessage = false;
290
+ var changedConversation = false;
291
+
292
+ // If instruction does not equal previous system message, add it, otherwise lock attempt
293
+ if (!lastSystemMessage || instruction !== lastSystemMessage.content) {
294
+ _messages.push({
295
+ id: id,
296
+ role: 'system',
297
+ content: instruction,
298
+ time: new Date().toISOString()
299
+ });
300
+ addedMessage = true;
301
+ } else {
302
+ // Handle repeated instruction
303
+ // Increment lock attempt if instructions are repeated and we haven't already incremented lock attempt (for example if a forward is provided)
304
+ if (previousLockAttempt === (conversation.lockAttempts || 0)) {
305
+ _conversation = incrementLockAttempt(_conversation, _config);
306
+ changedConversation = true;
307
+ }
308
+ }
309
+ return {
310
+ conversation: _conversation,
311
+ messages: _messages,
312
+ addedMessage: addedMessage,
313
+ changedConversation: changedConversation
314
+ };
315
+ };
316
+ addInstruction = function addInstruction(instruction, previousLockAttempt) {
317
+ var id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : idGenerator('sys');
318
+ var _addInstruction2 = _addInstruction(instruction, messages, conversation, config, previousLockAttempt, id),
319
+ newConversation = _addInstruction2.conversation,
320
+ newMessages = _addInstruction2.messages,
321
+ addedMessage = _addInstruction2.addedMessage,
322
+ changedConversation = _addInstruction2.changedConversation;
323
+ conversation = newConversation;
324
+ messages = newMessages;
325
+ if (addedMessage) {
326
+ progress('Added instruction', 'info', 'ADD_MESSAGE', newMessages[newMessages.length - 1]);
327
+ }
328
+ if (changedConversation) {
329
+ progress('Updated conversation', 'info', 'UPDATE_CONVERSATION', newConversation);
330
+ }
331
+ }; // 1. Check inputs
332
+ if (conversation.$agent) {
333
+ _context3.next = 15;
334
+ break;
335
+ }
336
+ throw new Error("SpiritsError: No agent found in conversation, must define \".$agent\" in the conversation");
337
+ case 15:
338
+ persona = (config.persona || config.personas || config.agents).find(function (p) {
339
+ return p.id === conversation.$agent;
340
+ });
341
+ if (persona) {
342
+ _context3.next = 20;
343
+ break;
344
+ }
345
+ if (!(config.persona || config.personas || config.agents).some(function (a) {
346
+ return !a.id;
347
+ })) {
348
+ _context3.next = 19;
349
+ break;
350
+ }
351
+ throw new Error("SpiritsError: No persona found (\"".concat(conversation.$agent, "\") in provided config, some persona's did not contain an \"id\" (Internal Mapping Error)"));
352
+ case 19:
353
+ throw new Error("SpiritsError: No persona found (\"".concat(conversation.$agent, "\") in provided config"));
354
+ case 20:
355
+ if (messages.every(function (m) {
356
+ return !!m.id;
357
+ })) {
358
+ _context3.next = 22;
359
+ break;
360
+ }
361
+ throw new Error("SpiritsError: Every message must have an \".id\", ensure all messages have an id assigned before running");
362
+ case 22:
363
+ if (messages.every(function (m) {
364
+ return m.role === 'customer' || m.role === 'agent' || m.role === 'system' || m.role === 'tool';
365
+ })) {
366
+ _context3.next = 25;
367
+ break;
368
+ }
369
+ invalidRoles = messages.filter(function (m) {
370
+ return m.role !== 'customer' && m.role !== 'agent' && m.role !== 'system' && m.role !== 'tool';
371
+ });
372
+ throw new Error("SpiritsError: Every message must have a role of \"customer\", \"agent\", or \"system\". Got invalid roles: ".concat(invalidRoles.map(function (m) {
373
+ return m.role;
374
+ }).join(', ')));
375
+ case 25:
376
+ // if message is not in messages, then add it
377
+ if (!messages.find(function (m) {
378
+ return m.id === input.message.id;
379
+ })) {
380
+ messages.push(input.message);
381
+ }
382
+
383
+ // 2. Parse the message
384
+ progress('Parsing message', 'info', 'SET_PROCESSING', 'user');
385
+ _context3.next = 29;
386
+ return parser(message.content, 'en');
387
+ case 29:
388
+ parsePayload = _context3.sent;
389
+ if (parsePayload.intent) {
390
+ message.intent = parsePayload.intent;
391
+ }
392
+ if (typeof parsePayload.intentScore === 'number') {
393
+ message.intentScore = parsePayload.intentScore;
394
+ }
395
+ message.context = parsePayload.context;
396
+ message.entities = parsePayload.entities;
397
+ index = messages.findIndex(function (m) {
398
+ return m.content === message.content || m.id === message.id;
399
+ });
400
+ if (index === -1) {
401
+ _message = {
402
+ id: idGenerator('customer'),
403
+ role: 'customer',
404
+ content: message,
405
+ context: parsePayload.context,
406
+ entities: parsePayload.entities,
407
+ time: new Date().toISOString()
408
+ };
409
+ if (parsePayload.intent) {
410
+ _message.intent = parsePayload.intent;
411
+ }
412
+ if (typeof parsePayload.intentScore === 'number') {
413
+ _message.intentScore = parsePayload.intentScore;
414
+ }
415
+ message = _message;
416
+ messages.push(_message);
417
+ progress('Added message', 'info', 'ADD_MESSAGE', _message);
418
+ } else {
419
+ messages[index].context = parsePayload.context;
420
+ messages[index].entities = parsePayload.entities;
421
+ if (parsePayload.intent) {
422
+ messages[index].intent = parsePayload.intent;
423
+ }
424
+ if (typeof parsePayload.intentScore === 'number') {
425
+ messages[index].intentScore = parsePayload.intentScore;
426
+ }
427
+ message = messages[index];
428
+ progress('Parsed message', 'success', 'UPDATE_MESSAGE', message);
429
+ }
430
+ // If this is the first user message, then update conversations intent
431
+ previousUserMessages = messages.filter(function (m) {
432
+ return m.role === 'customer' && m.content !== message.content;
433
+ });
434
+ if (!conversation.intent || previousUserMessages.length === 0 && parsePayload.intent) {
435
+ conversation.intent = parsePayload.intent;
436
+ conversation.intentScore = (parsePayload === null || parsePayload === void 0 ? void 0 : parsePayload.intentScore) || 0;
437
+ progress('Updated conversation intent', 'info', 'UPDATE_CONVERSATION', {
438
+ intent: parsePayload.intent,
439
+ intentScore: (parsePayload === null || parsePayload === void 0 ? void 0 : parsePayload.intentScore) || 0
440
+ });
441
+ }
442
+ oldKeyCount = Object.keys(context).length;
443
+ context = updateContext(context, parsePayload.context);
444
+ newKeyCount = Object.keys(context).length;
445
+ if (!conversation.locked && newKeyCount > oldKeyCount) {
446
+ // Reset lock attempts
447
+ conversation.locked = false;
448
+ conversation.lockAttempts = 0;
449
+ conversation.lockedReason = '';
450
+ progress('Reset lock', 'info', 'UPDATE_CONVERSATION', {
451
+ locked: false,
452
+ lockAttempts: 0,
453
+ lockedReason: ''
454
+ });
455
+ }
456
+ noNewContext = Object.keys(parsePayload.context).length === 0; // upsert parse system messages
457
+ if (parsePayload.contextMessages.length) {
458
+ (_messages2 = messages).push.apply(_messages2, _rollupPluginBabelHelpers._toConsumableArray(parsePayload.contextMessages.reduce(function (accumulator, text) {
459
+ if (!messages.find(function (mes) {
460
+ return mes.content === text;
461
+ })) {
462
+ accumulator.push({
463
+ id: idGenerator('sys'),
464
+ role: 'system',
465
+ content: text,
466
+ time: new Date().toISOString()
467
+ });
468
+ } else {
469
+ progress("Already have system context, skipping", 'info');
470
+ }
471
+ return accumulator;
472
+ }, [])));
473
+ }
474
+
475
+ // 3. Run the contextualizer
476
+ progress('Running contextualizer', 'info', 'SET_PROCESSING', 'system');
477
+ _context3.next = 47;
478
+ return contextualizer({
479
+ conversation: conversation,
480
+ messages: messages
481
+ });
482
+ case 47:
483
+ newContextMessages = _context3.sent;
484
+ _iterator = _rollupPluginBabelHelpers._createForOfIteratorHelper(newContextMessages);
485
+ _context3.prev = 49;
486
+ _loop = /*#__PURE__*/_rollupPluginBabelHelpers._regeneratorRuntime().mark(function _loop() {
487
+ var contextMessage;
488
+ return _rollupPluginBabelHelpers._regeneratorRuntime().wrap(function _loop$(_context) {
489
+ while (1) switch (_context.prev = _context.next) {
490
+ case 0:
491
+ contextMessage = _step.value;
492
+ if (!messages.find(function (mes) {
493
+ return mes.content === contextMessage.content;
494
+ })) {
495
+ messages.push(contextMessage);
496
+ progress("Added context", 'info', 'ADD_MESSAGE', messages[messages.length - 1]);
497
+ } else {
498
+ progress("Already have system context, skipping", 'info');
499
+ }
500
+ case 2:
501
+ case "end":
502
+ return _context.stop();
503
+ }
504
+ }, _loop);
505
+ });
506
+ _iterator.s();
507
+ case 52:
508
+ if ((_step = _iterator.n()).done) {
509
+ _context3.next = 56;
510
+ break;
511
+ }
512
+ return _context3.delegateYield(_loop(), "t0", 54);
513
+ case 54:
514
+ _context3.next = 52;
515
+ break;
516
+ case 56:
517
+ _context3.next = 61;
518
+ break;
519
+ case 58:
520
+ _context3.prev = 58;
521
+ _context3.t1 = _context3["catch"](49);
522
+ _iterator.e(_context3.t1);
523
+ case 61:
524
+ _context3.prev = 61;
525
+ _iterator.f();
526
+ return _context3.finish(61);
527
+ case 64:
528
+ // 4. Run the workflow
529
+ progress('Running workflow', 'info', 'SET_PROCESSING', 'system');
530
+ _context3.next = 67;
531
+ return workflow({
532
+ messages: messages,
533
+ conversation: conversation,
534
+ context: context,
535
+ message: message,
536
+ agent: persona,
537
+ customer: customer,
538
+ intent: {
539
+ current: ((_recentUserMessage = recentUserMessage(messages)) === null || _recentUserMessage === void 0 ? void 0 : _recentUserMessage.intent) || null,
540
+ flow: messages.map(function (m) {
541
+ return m.intent;
542
+ }).filter(Boolean),
543
+ initial: conversation.intent || null
544
+ },
545
+ stagnationCount: conversation.lockAttempts || 0
546
+ }).then(function (res) {
547
+ return Array.isArray(res) ? res : [res];
548
+ }).then(function (slots) {
549
+ return slots.reduce(function (accumulator, slot) {
550
+ if ('toJSON' in slot) {
551
+ var slotJson = slot.toJSON();
552
+ accumulator.push.apply(accumulator, _rollupPluginBabelHelpers._toConsumableArray(Array.isArray(slotJson) ? slotJson : [slotJson]));
553
+ } else {
554
+ accumulator.push(slot);
555
+ }
556
+ return accumulator;
557
+ }, []);
558
+ });
559
+ case 67:
560
+ slots = _context3.sent;
561
+ hasNoInstructions = slots.every(function (s) {
562
+ return !s.instructions || Array.isArray(s.instructions) && s.instructions.length === 0;
563
+ });
564
+ hasNoCustomMessage = slots.every(function (s) {
565
+ return !s.message;
566
+ });
567
+ messagesToTransform = slots.filter(function (s) {
568
+ return !!s.message && _rollupPluginBabelHelpers._typeof(s.message) === 'object' && !!s.message.transform;
569
+ });
570
+ previousLockAttempt = conversation.lockAttempts || 0; // Used to track
571
+ if (hasNoInstructions && noNewContext) {
572
+ conversation = incrementLockAttempt(conversation, config);
573
+ } else {
574
+ conversation.lockAttempts = 0;
575
+ conversation.locked = false;
576
+ conversation.lockedReason = '';
577
+ progress('Reset lock', 'info', 'UPDATE_CONVERSATION', {
578
+ lockAttempts: 0,
579
+ locked: false,
580
+ lockedReason: ''
581
+ });
582
+ }
583
+ resettedIntent = false;
584
+ /** @type {Array<string> | undefined} */
585
+ _iterator2 = _rollupPluginBabelHelpers._createForOfIteratorHelper(slots);
586
+ _context3.prev = 75;
587
+ _iterator2.s();
588
+ case 77:
589
+ if ((_step2 = _iterator2.n()).done) {
590
+ _context3.next = 148;
591
+ break;
592
+ }
593
+ _step2$value = _step2.value, forward = _step2$value.forward, forwardNote = _step2$value.forwardNote, instructions = _step2$value.instructions, removeInstructions = _step2$value.removeInstructions, manualMessage = _step2$value.message, scheduled = _step2$value.scheduled, resetIntent = _step2$value.resetIntent, secondsDelay = _step2$value.secondsDelay, contextUpsert = _step2$value.contextUpsert, anticipate = _step2$value.anticipate, slotFollowup = _step2$value.followup, slotEntityContextUpsert = _step2$value.entityContextUpsert, tasks = _step2$value.tasks;
594
+ if (!anticipate) {
595
+ _context3.next = 92;
596
+ break;
597
+ }
598
+ if (!Array.isArray(anticipate)) {
599
+ _context3.next = 87;
600
+ break;
601
+ }
602
+ // 'literal' anticipation
603
+ _slots = {};
604
+ map = [];
605
+ for (i = 0; i < anticipate.length; i++) {
606
+ _anticipate$i = anticipate[i], keywords = _anticipate$i.keywords, _slot = _rollupPluginBabelHelpers._objectWithoutProperties(_anticipate$i, _excluded);
607
+ slotId = "".concat(i);
608
+ _slots[slotId] = _slot;
609
+ map.push({
610
+ slot: slotId,
611
+ keywords: keywords
612
+ });
613
+ }
614
+ updateConversation(conversation, {
615
+ type: 'literal',
616
+ slots: _slots,
617
+ map: map
618
+ });
619
+ _context3.next = 92;
620
+ break;
621
+ case 87:
622
+ if (!('yes' in anticipate && 'no' in anticipate && 'did' in anticipate)) {
623
+ _context3.next = 91;
624
+ break;
625
+ }
626
+ // "did" anticipation
627
+ updateConversation(conversation, {
628
+ type: 'did',
629
+ slots: {
630
+ yes: anticipate.yes,
631
+ no: anticipate.no
632
+ },
633
+ did: anticipate.did
634
+ });
635
+ _context3.next = 92;
636
+ break;
637
+ case 91:
638
+ throw new Error("Invalid anticipate payload \"".concat(JSON.stringify(anticipate), "\""));
639
+ case 92:
640
+ // tasks from auto/manual ingress to execute
641
+ if (!!tasks && Array.isArray(tasks) && !!tasks.length) {
642
+ if (!_tasks) _tasks = [];
643
+ (_tasks2 = _tasks).push.apply(_tasks2, _rollupPluginBabelHelpers._toConsumableArray(tasks));
644
+ }
645
+ if (slotFollowup) {
646
+ followup.push(slotFollowup);
647
+ }
648
+ if (slotEntityContextUpsert && slotEntityContextUpsert.length) {
649
+ entityContextUpsert.push.apply(entityContextUpsert, _rollupPluginBabelHelpers._toConsumableArray(slotEntityContextUpsert));
650
+ }
651
+
652
+ // Forward to agent or other agent
653
+ if (forward) {
654
+ conversation = lockConversation(conversation, 'App instructed forward');
655
+ _forward = forward;
656
+ _forwardNote = forwardNote;
657
+ if (typeof forward === 'string') {
658
+ updateConversation(conversation, {
659
+ forwarded: forward
660
+ });
661
+ messages.push({
662
+ id: idGenerator('sys'),
663
+ role: 'system',
664
+ content: "forwarded to \"".concat(forward, "\""),
665
+ time: new Date().toISOString()
666
+ });
667
+ progress("Forwarded to \"".concat(forward, "\""), 'info', 'ADD_MESSAGE', messages[messages.length - 1]);
668
+ } else if (typeof forward === 'boolean') {
669
+ updateConversation(conversation, {
670
+ forwarded: conversation.$agent
671
+ });
672
+ messages.push({
673
+ id: idGenerator('sys'),
674
+ role: 'system',
675
+ content: "forwarded to \"".concat(forward, "\""),
676
+ time: new Date().toISOString()
677
+ });
678
+ progress("Forwarded to agent", 'info', 'ADD_MESSAGE', messages[messages.length - 1]);
679
+ } else {
680
+ messages.push({
681
+ id: idGenerator('sys'),
682
+ role: 'system',
683
+ content: "forwarded to \"".concat(forward.to, "\" ").concat(forward.mode ? ' (' + forward.mode + ')' : ''),
684
+ time: new Date().toISOString()
685
+ });
686
+ progress("Forwarded to \"".concat(forward.to, "\" ").concat(forward.mode ? ' (' + forward.mode + ')' : ''), 'info', 'ADD_MESSAGE', messages[messages.length - 1]);
687
+ updateConversation(conversation, {
688
+ forwarded: forward.to
689
+ });
690
+ }
691
+ }
692
+
693
+ // Insert instructions context
694
+ if (!instructions) {
695
+ _context3.next = 111;
696
+ break;
697
+ }
698
+ if (!(typeof instructions === 'string')) {
699
+ _context3.next = 101;
700
+ break;
701
+ }
702
+ addInstruction(instructions, previousLockAttempt);
703
+ _context3.next = 111;
704
+ break;
705
+ case 101:
706
+ if (!Array.isArray(instructions)) {
707
+ _context3.next = 106;
708
+ break;
709
+ }
710
+ _iterator5 = _rollupPluginBabelHelpers._createForOfIteratorHelper(instructions);
711
+ try {
712
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
713
+ instruction = _step5.value;
714
+ if (typeof instruction === 'string') {
715
+ addInstruction(instruction, previousLockAttempt);
716
+ } else {
717
+ addInstruction(instruction.content, previousLockAttempt, instruction.id);
718
+ }
719
+ }
720
+ } catch (err) {
721
+ _iterator5.e(err);
722
+ } finally {
723
+ _iterator5.f();
724
+ }
725
+ _context3.next = 111;
726
+ break;
727
+ case 106:
728
+ if (!(_rollupPluginBabelHelpers._typeof(instructions) === 'object' && 'content' in instructions)) {
729
+ _context3.next = 110;
730
+ break;
731
+ }
732
+ addInstruction(instructions.content, previousLockAttempt, instructions.id);
733
+ _context3.next = 111;
734
+ break;
735
+ case 110:
736
+ throw new Error("SpiritsError: instructions must be a string or array or {content: \"<instruction>\"}, got: ".concat(JSON.stringify(instructions)));
737
+ case 111:
738
+ if (!removeInstructions) {
739
+ _context3.next = 128;
740
+ break;
741
+ }
742
+ _iterator6 = _rollupPluginBabelHelpers._createForOfIteratorHelper(removeInstructions);
743
+ _context3.prev = 113;
744
+ _loop2 = /*#__PURE__*/_rollupPluginBabelHelpers._regeneratorRuntime().mark(function _loop2() {
745
+ var instructionId, index;
746
+ return _rollupPluginBabelHelpers._regeneratorRuntime().wrap(function _loop2$(_context2) {
747
+ while (1) switch (_context2.prev = _context2.next) {
748
+ case 0:
749
+ instructionId = _step6.value;
750
+ index = messages.findIndex(function (m) {
751
+ return m.id === instructionId;
752
+ });
753
+ if (index > -1) {
754
+ messages.splice(index, 1);
755
+ progress('Remove instruction', 'info', 'REMOVE_MESSAGE', instructionId);
756
+ } else {
757
+ console.log("Instruction not found \"".concat(instructionId, "\", other ids: ").concat(messages.map(function (m) {
758
+ return "\"".concat(m.id, "\"");
759
+ }).join(', ')));
760
+ }
761
+ case 3:
762
+ case "end":
763
+ return _context2.stop();
764
+ }
765
+ }, _loop2);
766
+ });
767
+ _iterator6.s();
768
+ case 116:
769
+ if ((_step6 = _iterator6.n()).done) {
770
+ _context3.next = 120;
771
+ break;
772
+ }
773
+ return _context3.delegateYield(_loop2(), "t2", 118);
774
+ case 118:
775
+ _context3.next = 116;
776
+ break;
777
+ case 120:
778
+ _context3.next = 125;
779
+ break;
780
+ case 122:
781
+ _context3.prev = 122;
782
+ _context3.t3 = _context3["catch"](113);
783
+ _iterator6.e(_context3.t3);
784
+ case 125:
785
+ _context3.prev = 125;
786
+ _iterator6.f();
787
+ return _context3.finish(125);
788
+ case 128:
789
+ if (!manualMessage) {
790
+ _context3.next = 144;
791
+ break;
792
+ }
793
+ /** @type {import('@scout9/admin').Message} */
794
+ manualMessageObj = {
795
+ id: idGenerator('persona'),
796
+ role: 'agent',
797
+ // @TODO switch role to persona
798
+ content: '',
799
+ time: new Date().toISOString()
800
+ };
801
+ if (!(_rollupPluginBabelHelpers._typeof(manualMessage) === 'object')) {
802
+ _context3.next = 136;
803
+ break;
804
+ }
805
+ Object.assign(manualMessageObj, manualMessage);
806
+ manualMessageObj.role = 'agent';
807
+ manualMessageObj.time = new Date().toISOString();
808
+ _context3.next = 141;
809
+ break;
810
+ case 136:
811
+ if (!(typeof manualMessage === 'string')) {
812
+ _context3.next = 140;
813
+ break;
814
+ }
815
+ manualMessageObj.content = manualMessage;
816
+ _context3.next = 141;
817
+ break;
818
+ case 140:
819
+ throw new Error('Manual message must be of type "string" or "DirectMessage"');
820
+ case 141:
821
+ if (scheduled) {
822
+ manualMessageObj.time = new Date(scheduled * 1000).toISOString();
823
+ manualMessageObj.scheduled = manualMessageObj.time;
824
+ } else if (secondsDelay) {
825
+ now = new Date();
826
+ now.setSeconds(now.getSeconds() + secondsDelay);
827
+ manualMessageObj.time = now.toISOString();
828
+ manualMessageObj.delayInSeconds = secondsDelay;
829
+ }
830
+ messages.push(manualMessageObj);
831
+ progress('Added manual message', 'info', 'ADD_MESSAGE', messages[messages.length - 1]);
832
+ case 144:
833
+ if (contextUpsert) {
834
+ context = updateContext(context, contextUpsert);
835
+ progress('Upserted context', 'info', 'UPDATE_CONTEXT', contextUpsert);
836
+ }
837
+ if (resetIntent) {
838
+ resettedIntent = true;
839
+ }
840
+ case 146:
841
+ _context3.next = 77;
842
+ break;
843
+ case 148:
844
+ _context3.next = 153;
845
+ break;
846
+ case 150:
847
+ _context3.prev = 150;
848
+ _context3.t4 = _context3["catch"](75);
849
+ _iterator2.e(_context3.t4);
850
+ case 153:
851
+ _context3.prev = 153;
852
+ _iterator2.f();
853
+ return _context3.finish(153);
854
+ case 156:
855
+ if (resettedIntent && !_forward) {
856
+ conversation.intent = null;
857
+ conversation.intentScore = null;
858
+ conversation.locked = false;
859
+ conversation.lockedReason = '';
860
+ conversation.lockAttempts = 0;
861
+ progress('Reset conversation intent', 'info', 'UPDATE_CONVERSATION', {
862
+ intent: null,
863
+ intentScore: null,
864
+ locked: false,
865
+ lockAttempts: 0,
866
+ lockedReason: ''
867
+ });
868
+ }
869
+
870
+ // 5. Generate response
871
+ // If conversation previously locked, don't generate
872
+ if (input.conversation.locked) {
873
+ _context3.next = 208;
874
+ break;
875
+ }
876
+ if (!((!conversation.locked || !hasNoInstructions) && !!hasNoCustomMessage)) {
877
+ _context3.next = 174;
878
+ break;
879
+ }
880
+ _context3.prev = 159;
881
+ progress('Generating message', 'info', 'SET_PROCESSING', 'system');
882
+
883
+ /** @type {import('@scout9/admin').GenerateRequestOneOf1} */
884
+ generatorInput = {
885
+ messages: messages,
886
+ persona: persona,
887
+ context: context,
888
+ llm: config.llm,
889
+ pmt: config.pmt
890
+ };
891
+ if (!!_tasks && Array.isArray(_tasks) && !!_tasks.length) {
892
+ generatorInput.tasks = _tasks;
893
+ }
894
+ _context3.next = 165;
895
+ return generator(generatorInput);
896
+ case 165:
897
+ generatorPayload = _context3.sent;
898
+ if (!generatorPayload.send) {
899
+ progress('Generated response', 'failed', undefined, {
900
+ error: ((_generatorPayload$err = generatorPayload.errors) === null || _generatorPayload$err === void 0 ? void 0 : _generatorPayload$err.join('\n\n')) || 'Unknown Reason'
901
+ });
902
+ console.error("Locking conversation, api returned send false: ".concat(generatorPayload.messages), ((_generatorPayload$err2 = generatorPayload.errors) === null || _generatorPayload$err2 === void 0 ? void 0 : _generatorPayload$err2.join('\n\n')) || generatorPayload.forwardNote || 'Unknown Reason');
903
+ conversation = lockConversation(conversation, 'API: ' + ((_generatorPayload$err3 = generatorPayload.errors) === null || _generatorPayload$err3 === void 0 ? void 0 : _generatorPayload$err3.join('\n\n')) || generatorPayload.forwardNote || 'Unknown Reason');
904
+ } else {
905
+ progress('Generated response', 'success', undefined, undefined);
906
+ // Check if already had message
907
+ agentMessages = messages.filter(function (m) {
908
+ return m.role === 'agent';
909
+ });
910
+ lastAgentMessage = agentMessages[agentMessages.length - 1]; // Build addedMessages from generatorPayload.messages
911
+ addedMessages = ((_generatorPayload$mes = generatorPayload === null || generatorPayload === void 0 ? void 0 : generatorPayload.messages) !== null && _generatorPayload$mes !== void 0 ? _generatorPayload$mes : []).map(function (message) {
912
+ // Normalize time → ISO string
913
+ var t = message.time;
914
+ var isoTime;
915
+ if (typeof t === "string") {
916
+ isoTime = t;
917
+ } else if (t instanceof Date) {
918
+ isoTime = t.toISOString();
919
+ } else if (t && typeof t.toDate === "function") {
920
+ // Firestore Timestamp
921
+ isoTime = t.toDate().toISOString();
922
+ } else {
923
+ progress("Message \"".concat(message.content, "\" wasn't given a usable timestamp (").concat(JSON.stringify(t), "), defaulting to now"));
924
+ isoTime = new Date().toISOString();
925
+ }
926
+
927
+ // Base fields we guarantee
928
+ var base = {
929
+ role: message.role,
930
+ content: message.content,
931
+ id: idGenerator(message.role),
932
+ time: isoTime
933
+ };
934
+
935
+ // Copy any other non-nullish fields without overwriting base
936
+ return Object.entries(message).reduce(function (acc, _ref) {
937
+ var _ref2 = _rollupPluginBabelHelpers._slicedToArray(_ref, 2),
938
+ key = _ref2[0],
939
+ value = _ref2[1];
940
+ if (!Object.prototype.hasOwnProperty.call(acc, key) && value != null) {
941
+ acc[key] = value;
942
+ }
943
+ return acc;
944
+ }, base);
945
+ })
946
+ // De-dupe by content (change the key if you want stricter uniqueness)
947
+ .reduce(function (acc, msg) {
948
+ var key = String(msg.content); // e.g. `${msg.role}::${msg.content}` for stronger uniqueness
949
+ if (!acc.seen.has(key)) {
950
+ acc.seen.add(key);
951
+ acc.items.push(msg);
952
+ }
953
+ return acc;
954
+ }, {
955
+ seen: new Set(),
956
+ items: []
957
+ }).items;
958
+ if (lastAgentMessage && lastAgentMessage.content && addedMessages.some(function (message) {
959
+ return message.content === lastAgentMessage.content;
960
+ })) {
961
+ // Error should not have happened
962
+ conversation = lockConversation(conversation, 'Duplicate message');
963
+ } else {
964
+ _iterator3 = _rollupPluginBabelHelpers._createForOfIteratorHelper(addedMessages);
965
+ try {
966
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
967
+ newMessage = _step3.value;
968
+ messages.push(newMessage);
969
+ progress('Added agent message', 'info', 'ADD_MESSAGE', messages[messages.length - 1]);
970
+ }
971
+ } catch (err) {
972
+ _iterator3.e(err);
973
+ } finally {
974
+ _iterator3.f();
975
+ }
976
+ }
977
+
978
+ // Check if conversation was marked for forward (generator message still allowed to be sent)
979
+ if (generatorPayload.forward) {
980
+ conversation = lockConversation(conversation, 'API: ' + generatorPayload.forwardNote || 'Forwarded by API');
981
+ if (!_forward) {
982
+ _forward = generatorPayload.forward;
983
+ _forwardNote = generatorPayload.forwardNote;
984
+ }
985
+ }
986
+ }
987
+ _context3.next = 174;
988
+ break;
989
+ case 169:
990
+ _context3.prev = 169;
991
+ _context3.t5 = _context3["catch"](159);
992
+ onError(_context3.t5);
993
+ console.error("Spirits: Locking conversation, error generating response: ".concat(_context3.t5.message));
994
+ conversation = lockConversation(conversation, 'API: ' + _context3.t5.message);
995
+ case 174:
996
+ if (!(messagesToTransform.length && transformer)) {
997
+ _context3.next = 207;
998
+ break;
999
+ }
1000
+ _context3.prev = 175;
1001
+ _iterator4 = _rollupPluginBabelHelpers._createForOfIteratorHelper(messagesToTransform);
1002
+ _context3.prev = 177;
1003
+ _iterator4.s();
1004
+ case 179:
1005
+ if ((_step4 = _iterator4.n()).done) {
1006
+ _context3.next = 190;
1007
+ break;
1008
+ }
1009
+ _step4.value;
1010
+ _context3.next = 183;
1011
+ return transformer({
1012
+ message: messagesToTransform,
1013
+ persona: persona,
1014
+ customer: customer.id,
1015
+ messages: messages,
1016
+ context: context
1017
+ });
1018
+ case 183:
1019
+ transformResponse = _context3.sent;
1020
+ progress('Generated response', 'success', undefined, undefined);
1021
+ // Check if already had message
1022
+ _agentMessages = messages.filter(function (m) {
1023
+ return m.role === 'agent';
1024
+ });
1025
+ _lastAgentMessage = _agentMessages[_agentMessages.length - 1];
1026
+ if (_lastAgentMessage && _lastAgentMessage.content === transformResponse.message) {
1027
+ // Error should not have happened
1028
+ conversation = lockConversation(conversation, 'Duplicate message');
1029
+ } else {
1030
+ messages.push({
1031
+ id: idGenerator('agent'),
1032
+ role: 'agent',
1033
+ content: transformResponse.message,
1034
+ time: new Date().toISOString()
1035
+ });
1036
+ progress('Added agent message', 'info', 'ADD_MESSAGE', messages[messages.length - 1]);
1037
+ }
1038
+ case 188:
1039
+ _context3.next = 179;
1040
+ break;
1041
+ case 190:
1042
+ _context3.next = 195;
1043
+ break;
1044
+ case 192:
1045
+ _context3.prev = 192;
1046
+ _context3.t6 = _context3["catch"](177);
1047
+ _iterator4.e(_context3.t6);
1048
+ case 195:
1049
+ _context3.prev = 195;
1050
+ _iterator4.f();
1051
+ return _context3.finish(195);
1052
+ case 198:
1053
+ _context3.next = 205;
1054
+ break;
1055
+ case 200:
1056
+ _context3.prev = 200;
1057
+ _context3.t7 = _context3["catch"](175);
1058
+ console.error("Locking conversation, error transforming response: ".concat(_context3.t7.message));
1059
+ conversation = lockConversation(conversation, 'API: ' + _context3.t7.message);
1060
+ onError(_context3.t7);
1061
+ case 205:
1062
+ _context3.next = 208;
1063
+ break;
1064
+ case 207:
1065
+ if (messagesToTransform.length) {
1066
+ console.warn("No transformer provided");
1067
+ }
1068
+ case 208:
1069
+ progress('Parsing message', 'info', 'SET_PROCESSING', null);
1070
+ return _context3.abrupt("return", {
1071
+ conversation: {
1072
+ before: conversationBefore,
1073
+ after: conversation,
1074
+ forward: _forward || null,
1075
+ forwardNote: _forwardNote || ''
1076
+ },
1077
+ messages: {
1078
+ before: messagesBefore,
1079
+ after: messages
1080
+ },
1081
+ message: {
1082
+ before: messageBefore,
1083
+ after: message
1084
+ },
1085
+ context: {
1086
+ before: contextBefore,
1087
+ after: context
1088
+ },
1089
+ followup: followup,
1090
+ entityContextUpsert: entityContextUpsert
1091
+ });
1092
+ case 210:
1093
+ case "end":
1094
+ return _context3.stop();
1095
+ }
1096
+ }, _callee, null, [[49, 58, 61, 64], [75, 150, 153, 156], [113, 122, 125, 128], [159, 169], [175, 200], [177, 192, 195, 198]]);
1097
+ }));
1098
+ function customer(_x) {
1099
+ return _customer.apply(this, arguments);
1100
+ }
1101
+ return customer;
1102
+ }()
1103
+ };
1104
+
1105
+ exports.Spirits = Spirits;