@scout9/app 1.0.0-alpha.0.1.92 → 1.0.0-alpha.0.1.93
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/{exports-dfabefaf.cjs → exports-83755281.cjs} +511 -23
- package/dist/index.cjs +111 -2
- package/dist/{multipart-parser-52e1536f.cjs → multipart-parser-af15f044.cjs} +2 -2
- package/dist/{spirits-6a709255.cjs → spirits-32395ac4.cjs} +23 -1
- package/dist/spirits.cjs +1 -1
- package/dist/testing-tools.cjs +2 -2
- package/package.json +1 -1
- package/src/core/config/agents.js +1 -1
- package/src/exports.js +1 -0
- package/src/runtime/client/utils.js +1 -0
- package/src/runtime/client/workflow.js +82 -24
- package/src/runtime/index.js +1 -0
- package/src/runtime/macros/builder.js +43 -0
- package/src/runtime/macros/event.js +234 -0
- package/src/runtime/macros/globals.js +14 -0
- package/src/runtime/macros/index.js +3 -0
- package/src/runtime/macros/schemas.js +12 -0
- package/src/runtime/macros/utils.js +31 -0
- package/src/testing-tools/spirits.js +1 -1
- package/src/utils/configs/agents.js +1 -1
- package/types/index.d.ts +18426 -332
- package/types/index.d.ts.map +9 -2
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { WorkflowResponseSlotBaseSchema, WorkflowResponseSlotSchema } from '../client/workflow.js';
|
|
2
|
+
import { MacroUtils } from './utils.js';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
function EventMacros() {
|
|
7
|
+
|
|
8
|
+
let slots = [];
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sets context into the conversation context for later use
|
|
14
|
+
* @param {Record<string, any>} updates
|
|
15
|
+
* @return {this}
|
|
16
|
+
*/
|
|
17
|
+
upsert(updates) {
|
|
18
|
+
const slot = {contextUpsert: updates}
|
|
19
|
+
slots.push(WorkflowResponseSlotSchema.parse(slot));
|
|
20
|
+
return this;
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Similar to `instruction` except that it requires a schedule time parameter that determines when to follow up (and is not an event output macro). This will fire another run job with a new insert system context message, if `options.literal` is set to true, it will be an appended agent message prior to running the workflow app.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} instruction
|
|
27
|
+
*
|
|
28
|
+
* @overload
|
|
29
|
+
* @param {Date | string} options
|
|
30
|
+
*
|
|
31
|
+
* @overload
|
|
32
|
+
* @param {Object} options
|
|
33
|
+
* @param {Date | string} options.scheduled
|
|
34
|
+
* @param {Record<string, any>} [options.cancelIf]
|
|
35
|
+
* @param {boolean} [options.literal]
|
|
36
|
+
* @param {boolean} [options.overrideLock]
|
|
37
|
+
*
|
|
38
|
+
* @return {this}
|
|
39
|
+
*/
|
|
40
|
+
followup(instruction, options) {
|
|
41
|
+
let slot;
|
|
42
|
+
if (!options) {
|
|
43
|
+
throw new Error('Missing second argument in followup(instruction, options) \'options\' argument needs to be included')
|
|
44
|
+
}
|
|
45
|
+
// Check if it's date value
|
|
46
|
+
const {success, ...rest} = MacroUtils.scheduledToUnixSafe(options);
|
|
47
|
+
if (!success) {
|
|
48
|
+
if (!('scheduled' in options)) {
|
|
49
|
+
throw new Error('.scheduled was not included in options and needs to be required');
|
|
50
|
+
}
|
|
51
|
+
if (typeof options !== 'object') {
|
|
52
|
+
throw new Error('second argument options in followup is not an object type');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Advance object
|
|
56
|
+
slot = {
|
|
57
|
+
followup: {
|
|
58
|
+
scheduled: MacroUtils.scheduledToUnix(options.scheduled)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (options.literal) {
|
|
62
|
+
slot.followup.message = instruction;
|
|
63
|
+
} else {
|
|
64
|
+
slot.followup.instructions = instruction;
|
|
65
|
+
}
|
|
66
|
+
if (typeof options.overrideLock === 'boolean') {
|
|
67
|
+
slot.followup.overrideLock = options.overrideLock;
|
|
68
|
+
}
|
|
69
|
+
if (options.cancelIf) {
|
|
70
|
+
slot.followup.cancelIf = options.cancelIf;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
} else {
|
|
74
|
+
// Simple follow up
|
|
75
|
+
slot = {
|
|
76
|
+
followup: {
|
|
77
|
+
instructions: instruction,
|
|
78
|
+
scheduled: rest.data
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
slots.push(WorkflowResponseSlotSchema.parse(slot));
|
|
84
|
+
return this;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Similar to `instruct` except that it requires a schedule time parameter that determines when to follow up (and is not an event output macro). This will fire another run job with a new insert system context message, if `options.literal` is set to true, it will be an appended agent message prior to running the workflow app.
|
|
89
|
+
* @overload
|
|
90
|
+
* @param {string} instruction - The instruction to be anticipated as a string. When this is a string, `yes` and `no` must be provided as objects.
|
|
91
|
+
* @param {object} yes - The object to process if the instruction is a string and the condition is met.
|
|
92
|
+
* @param {object} no - The object to process if the instruction is a string and the condition is not met.
|
|
93
|
+
*
|
|
94
|
+
* @overload
|
|
95
|
+
* @param {(Array<import('zod').infer<typeof import('../runtime/client/workflow.js').WorkflowResponseSlotBaseSchema> & {keywords: string[]}>)} instruction
|
|
96
|
+
*
|
|
97
|
+
* @return {EventMacros}
|
|
98
|
+
*/
|
|
99
|
+
anticipate(instruction, yes, no) {
|
|
100
|
+
const slot = {
|
|
101
|
+
anticipate: []
|
|
102
|
+
}
|
|
103
|
+
if (Array.isArray(instruction)) {
|
|
104
|
+
for (const _slot of instruction) {
|
|
105
|
+
if (!Array.isArray(_slot.keywords) || _slot.keywords.length < 1) {
|
|
106
|
+
throw new Error(`Anticipate field .keywords should be an array of string, with more than one value`);
|
|
107
|
+
}
|
|
108
|
+
slot.anticipate.push(_slot);
|
|
109
|
+
}
|
|
110
|
+
} else if (typeof instruction === 'string') {
|
|
111
|
+
if (!yes) {
|
|
112
|
+
throw new Error('Missing "yes" option for anticipate');
|
|
113
|
+
}
|
|
114
|
+
if (!no) {
|
|
115
|
+
throw new Error('Missing "no" option for anticipate');
|
|
116
|
+
}
|
|
117
|
+
slot.anticipate = {
|
|
118
|
+
did: instruction,
|
|
119
|
+
yes: WorkflowResponseSlotBaseSchema.parse(yes),
|
|
120
|
+
no: WorkflowResponseSlotBaseSchema.parse(no)
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
throw new Error(`Instruction is not of type "string" or "array"`);
|
|
124
|
+
}
|
|
125
|
+
slots.push(WorkflowResponseSlotSchema.parse(slot));
|
|
126
|
+
return this;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
*
|
|
131
|
+
* @param {string} instruction
|
|
132
|
+
* @param {Object} [options]
|
|
133
|
+
* @param {string} [options.id] - Unique ID for the instruction, this is used to remove the instruction later
|
|
134
|
+
* @param {string} [options.persist] - if true, the instruction persists the conversation, if false the instruction will only last for 1 auto reply
|
|
135
|
+
* @return {EventMacros}
|
|
136
|
+
*/
|
|
137
|
+
instruct(instruction, options = {}) {
|
|
138
|
+
const slot = {};
|
|
139
|
+
if (Object.keys(options).length > 0) {
|
|
140
|
+
const instructionObj = {
|
|
141
|
+
content: instruction,
|
|
142
|
+
}
|
|
143
|
+
if (options.id) {
|
|
144
|
+
instructionObj.id = options.id
|
|
145
|
+
}
|
|
146
|
+
if (typeof options.persist === 'boolean') {
|
|
147
|
+
instructionObj.persist = options.persist
|
|
148
|
+
}
|
|
149
|
+
slot.instructions = instructionObj;
|
|
150
|
+
} else {
|
|
151
|
+
slot.instructions = instruction;
|
|
152
|
+
}
|
|
153
|
+
slots.push(WorkflowResponseSlotSchema.parse(slot));
|
|
154
|
+
return this;
|
|
155
|
+
},
|
|
156
|
+
/**
|
|
157
|
+
* If a manual message must be sent, you can use the `reply` macro
|
|
158
|
+
* @param {string} message - the message to manually send to the user
|
|
159
|
+
* @param {Object} [options]
|
|
160
|
+
* @param {Date | string} [options.scheduled] - this will schedule the date to a specific provided date
|
|
161
|
+
* @param {string} [options.delay] - delays the message return in seconds
|
|
162
|
+
* @return {this}
|
|
163
|
+
*/
|
|
164
|
+
reply(message, options = {}) {
|
|
165
|
+
const slot = {
|
|
166
|
+
message
|
|
167
|
+
};
|
|
168
|
+
if (Object.keys(options).length) {
|
|
169
|
+
if ('scheduled' in options) {
|
|
170
|
+
slot.scheduled = MacroUtils.scheduledToUnix(options.scheduled);
|
|
171
|
+
}
|
|
172
|
+
if ('delay' in options) {
|
|
173
|
+
const delay = options.delay;
|
|
174
|
+
if (typeof delay !== 'number') {
|
|
175
|
+
throw new Error('.delay is not of type "number"');
|
|
176
|
+
}
|
|
177
|
+
slot.secondsDelay = delay;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
slots.push(WorkflowResponseSlotSchema.parse(slot));
|
|
181
|
+
return this;
|
|
182
|
+
},
|
|
183
|
+
/**
|
|
184
|
+
* This macro ends the conversation and forwards it the owner of the persona to manually handle the flow. If your app returns undefined or no event, then a default forward is generated.
|
|
185
|
+
* @param {string} message - the message to forward to owner of persona
|
|
186
|
+
* @param {Object} [options]
|
|
187
|
+
* @param {'after-reply' | 'immediately'} [options.mode] - sets forward mode, defaults to "immediately"
|
|
188
|
+
* @param {string} [options.to] - another phone or email to forward to instead of owner
|
|
189
|
+
* @return {this}
|
|
190
|
+
*/
|
|
191
|
+
forward(message, options = {}) {
|
|
192
|
+
let slot;
|
|
193
|
+
if (options && Object.keys(options).length) {
|
|
194
|
+
slot = {
|
|
195
|
+
forward: {
|
|
196
|
+
note: message
|
|
197
|
+
},
|
|
198
|
+
forwardNote: message
|
|
199
|
+
}
|
|
200
|
+
if (options.to) {
|
|
201
|
+
slot.forward.to = options.to;
|
|
202
|
+
}
|
|
203
|
+
if (options.mode) {
|
|
204
|
+
slot.forward.mode = options.mode;
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
slot = {
|
|
208
|
+
forward: true,
|
|
209
|
+
forwardNote: message
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
slots.push(WorkflowResponseSlotSchema.parse(slot));
|
|
213
|
+
return this;
|
|
214
|
+
},
|
|
215
|
+
/**
|
|
216
|
+
*
|
|
217
|
+
* @return {Array<(import('zod').infer<typeof import('../runtime/client/workflow.js').WorkflowResponseSlotSchema>)>}
|
|
218
|
+
*/
|
|
219
|
+
toJSON(flush = true) {
|
|
220
|
+
if (flush) {
|
|
221
|
+
const copy = [...slots];
|
|
222
|
+
slots =[];
|
|
223
|
+
return copy;
|
|
224
|
+
} else {
|
|
225
|
+
return slots;
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const eventMacros = EventMacros();
|
|
232
|
+
export const instruct = eventMacros.instruct.bind(eventMacros);
|
|
233
|
+
export const forward = eventMacros.forward.bind(eventMacros);
|
|
234
|
+
export const reply = eventMacros.reply.bind(eventMacros);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export default class MacroGlobals {
|
|
4
|
+
|
|
5
|
+
static $convo() {
|
|
6
|
+
const $convo = globalThis?.SCOUT9?.$convo;
|
|
7
|
+
if (!$convo) {
|
|
8
|
+
throw new Error(`$convo not found in runtime context, ${MacroGlobals.#hint}`);
|
|
9
|
+
}
|
|
10
|
+
return $convo;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static #hint = `make sure the context is properly instantiated before running workflow.`;
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const ContextExampleWithTrainingDataSchema = z.object({
|
|
4
|
+
input: z.string(),
|
|
5
|
+
output: z.array(z.record(z.any())),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const ContextExampleSchema = z.union([
|
|
9
|
+
z.array(ContextExampleWithTrainingDataSchema),
|
|
10
|
+
z.array(z.record(z.any())),
|
|
11
|
+
]);
|
|
12
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function MacroUtilsFactory() {
|
|
2
|
+
|
|
3
|
+
return {
|
|
4
|
+
dateToUnix(date) { return parseInt((date.getTime() / 1000).toFixed(0))},
|
|
5
|
+
scheduledToUnixSafe(scheduled) {
|
|
6
|
+
if (scheduled instanceof Date) {
|
|
7
|
+
return {data: this.dateToUnix(scheduled), success: true}
|
|
8
|
+
} else if (typeof scheduled === 'string') {
|
|
9
|
+
const timestamp = Date.parse(scheduled);
|
|
10
|
+
if (isNaN(timestamp) === false) {
|
|
11
|
+
return {data: this.dateToUnix(new Date(timestamp)), success: true };
|
|
12
|
+
} else {
|
|
13
|
+
return {error: '.scheduled is an invalid date string', success: false};
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
return {error: `.scheduled was neither a Date or ISO string`, success: false};
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
scheduledToUnix(scheduled) {
|
|
20
|
+
const {success, ...rest} = this.scheduledToUnixSafe(scheduled);
|
|
21
|
+
if (success) {
|
|
22
|
+
return rest.data;
|
|
23
|
+
} else {
|
|
24
|
+
throw new Error(rest.error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const MacroUtils = MacroUtilsFactory();
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
* @typedef {Object} ConversationEvent
|
|
93
93
|
* @property {Change<import('../runtime/client/workflow.js').IConversation> & {forwardNote?: string; forward?: import('../runtime/client/message.js').IWorkflowResponseSlot['forward']}} conversation
|
|
94
94
|
* @property {Change<Array<import('../runtime/client/message.js').IMessage>>} messages
|
|
95
|
-
* @property {Change<
|
|
95
|
+
* @property {Change<any>} context
|
|
96
96
|
* @property {Change<import('../runtime/client/message.js').IMessage>} message
|
|
97
97
|
*/
|
|
98
98
|
export const Spirits = {
|
|
@@ -16,7 +16,7 @@ export function validateAgentConfig(agents) {
|
|
|
16
16
|
}
|
|
17
17
|
if (!agent.programmablePhoneNumber) {
|
|
18
18
|
const userName = agent.firstName ? `${agent.firstName}${agent.lastName ? ' ' + agent.lastName : ''}` : agent.forwardPhone;
|
|
19
|
-
cb(`⚠️${colors.yellow('Warning')}: ${userName} does not have a masked phone number to do auto replies
|
|
19
|
+
cb(`⚠️${colors.yellow('Warning')}: ${userName} does not have a masked phone number to do auto replies.\nYou can register one at ${colors.cyan('https://scout9.com/b')} under ${colors.green('users')} > ${colors.green(userName)}. Then run ${colors.cyan('scout9 sync')} to update.`);
|
|
20
20
|
}
|
|
21
21
|
if (agent.forwardPhone && agents.filter(a => a.forwardPhone && (a.forwardPhone === agent.forwardPhone)).length > 1) {
|
|
22
22
|
throw new Error(`src/entities/agents.js|ts: ".forwardPhone: ${agent.forwardPhone}" can only be associated to one agent within your project`);
|