mojulo 0.0.0 → 0.1.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.
- package/README.md +54 -4
- package/lib/audit-logger-new.js +11 -0
- package/lib/auth/gate.js +25 -0
- package/lib/auth/service.js +17 -0
- package/lib/auth/session.js +63 -0
- package/lib/builder/chat-processor.js +607 -0
- package/lib/builder/composer-bridge.js +82 -0
- package/lib/builder/evaluator.js +159 -0
- package/lib/builder/executor.js +252 -0
- package/lib/builder/index.js +48 -0
- package/lib/builder/session.js +248 -0
- package/lib/builder/system-prompt.js +422 -0
- package/lib/builder/tone-presets.js +75 -0
- package/lib/builder/tool-executors.js +1527 -0
- package/lib/builder/tools.js +338 -0
- package/lib/builder/validators.js +239 -0
- package/lib/composer/composer.js +225 -0
- package/lib/composer/index.js +40 -0
- package/lib/composer/protocols/00_base.txt +19 -0
- package/lib/composer/protocols/01_knowledge.txt +9 -0
- package/lib/composer/protocols/02_form-gathering.txt +32 -0
- package/lib/composer/protocols/03_appointments.txt +16 -0
- package/lib/composer/protocols/04_triage.txt +15 -0
- package/lib/composer/protocols/05_optical-read.txt +22 -0
- package/lib/composer/response-builder.js +98 -0
- package/lib/config-builder.js +650 -0
- package/lib/db/ids.js +10 -0
- package/lib/db/index.js +179 -0
- package/lib/db/repositories/apiKeys.js +72 -0
- package/lib/db/repositories/auditLogs.js +12 -0
- package/lib/db/repositories/botSpaces.js +12 -0
- package/lib/db/repositories/builderSessions.js +312 -0
- package/lib/db/repositories/deploymentEvents.js +12 -0
- package/lib/db/repositories/deployments.js +385 -0
- package/lib/db/repositories/documents.js +68 -0
- package/lib/db/repositories/mcpJobs.js +84 -0
- package/lib/deployers/bot-fleet.js +110 -0
- package/lib/deployers/bot-proxy.js +72 -0
- package/lib/deployers/build.js +89 -0
- package/lib/deployers/cloud-deploy.js +310 -0
- package/lib/deployers/docker.js +439 -0
- package/lib/deployers/fly.js +432 -0
- package/lib/deployers/index.js +38 -0
- package/lib/deployment-auth.js +36 -0
- package/lib/document-parser.js +171 -0
- package/lib/embedder/chunker.js +93 -0
- package/lib/embedder/local.js +101 -0
- package/lib/embedder/preview-rag.js +93 -0
- package/lib/envelope-schema.js +54 -0
- package/lib/fleet/scoped-sql.js +342 -0
- package/lib/form-schema-config/base.js +135 -0
- package/lib/form-schema-config/index.js +286 -0
- package/lib/form-schema-config/locales/af-ZA.js +153 -0
- package/lib/form-schema-config/locales/ar-AE.js +142 -0
- package/lib/form-schema-config/locales/ar-SA.js +164 -0
- package/lib/form-schema-config/locales/de-DE.js +152 -0
- package/lib/form-schema-config/locales/en-AU.js +161 -0
- package/lib/form-schema-config/locales/en-CA.js +115 -0
- package/lib/form-schema-config/locales/en-GB.js +132 -0
- package/lib/form-schema-config/locales/en-IN.js +219 -0
- package/lib/form-schema-config/locales/en-MY.js +171 -0
- package/lib/form-schema-config/locales/en-NG.js +198 -0
- package/lib/form-schema-config/locales/en-PH.js +186 -0
- package/lib/form-schema-config/locales/en-SG.js +153 -0
- package/lib/form-schema-config/locales/en-US.js +138 -0
- package/lib/form-schema-config/locales/es-ES.js +171 -0
- package/lib/form-schema-config/locales/es-MX.js +193 -0
- package/lib/form-schema-config/locales/fr-CA.js +138 -0
- package/lib/form-schema-config/locales/fr-FR.js +155 -0
- package/lib/form-schema-config/locales/hi-IN.js +219 -0
- package/lib/form-schema-config/locales/it-IT.js +157 -0
- package/lib/form-schema-config/locales/ja-JP.js +169 -0
- package/lib/form-schema-config/locales/ko-KR.js +140 -0
- package/lib/form-schema-config/locales/nl-NL.js +149 -0
- package/lib/form-schema-config/locales/pt-BR.js +168 -0
- package/lib/form-schema-config/locales/zh-CN.js +172 -0
- package/lib/form-schema-config/locales/zh-HK.js +142 -0
- package/lib/form-structure-schema.js +191 -0
- package/lib/llm-providers.js +828 -0
- package/lib/markdown.js +197 -0
- package/lib/mcp/catalysts/appointment-to-calendar.md +84 -0
- package/lib/mcp/catalysts/conversations-to-channel-digest.md +104 -0
- package/lib/mcp/catalysts/document-extract-to-store.md +92 -0
- package/lib/mcp/catalysts/knowledge-gap-miner.md +96 -0
- package/lib/mcp/catalysts/loader.js +144 -0
- package/lib/mcp/catalysts/qualify-lead-to-crm.md +83 -0
- package/lib/mcp/catalysts/scan-conversations-for-signal.md +92 -0
- package/lib/mcp/catalysts/submission-to-ticket.md +83 -0
- package/lib/mcp/catalysts/submissions-to-warehouse.md +103 -0
- package/lib/mcp/catalysts/weekly-submissions-digest.md +82 -0
- package/lib/mcp/jobs.js +64 -0
- package/lib/mcp/server.js +184 -0
- package/lib/mcp/session-binding.js +130 -0
- package/lib/mcp/tools/build.js +123 -0
- package/lib/mcp/tools/catalysts.js +477 -0
- package/lib/mcp/tools/context.js +325 -0
- package/lib/mcp/tools/fleet.js +391 -0
- package/lib/mcp/tools/jobs-tools.js +240 -0
- package/lib/mcp/tools/operate.js +314 -0
- package/lib/preview/build-preview-config.js +136 -0
- package/lib/rate-limiter.js +11 -0
- package/lib/resolve-api-key.js +142 -0
- package/lib/storage/index.js +40 -0
- package/messages/de.json +2136 -0
- package/messages/en.json +2136 -0
- package/messages/es.json +2136 -0
- package/messages/fr.json +2136 -0
- package/messages/it.json +2136 -0
- package/messages/ja.json +2136 -0
- package/messages/ko.json +2136 -0
- package/messages/nl.json +2136 -0
- package/messages/pl.json +2136 -0
- package/messages/pt.json +2136 -0
- package/messages/ru.json +2136 -0
- package/messages/uk.json +2136 -0
- package/messages/zh.json +2136 -0
- package/package.json +68 -5
- package/scripts/mcp-config.mjs +162 -0
- package/scripts/mcp-stdio-loader.mjs +42 -0
- package/scripts/mcp-stdio.mjs +108 -0
- package/scripts/mojulo-paths.mjs +48 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder Tools for Inverted Flow
|
|
3
|
+
*
|
|
4
|
+
* Tool definitions for the "Claude proposes, User disposes" architecture.
|
|
5
|
+
* These tools allow Claude to:
|
|
6
|
+
* - Process documents and generate RAG summaries
|
|
7
|
+
* - Infer user intent from context
|
|
8
|
+
* - Recommend appropriate protocols
|
|
9
|
+
* - Generate configurations for each protocol
|
|
10
|
+
* - Compose bot identity
|
|
11
|
+
* - Deploy the final bot
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Tool definitions for Claude's inverted builder flow
|
|
16
|
+
*/
|
|
17
|
+
export const BUILDER_TOOLS = [
|
|
18
|
+
{
|
|
19
|
+
name: 'process_documents',
|
|
20
|
+
description: 'Parse uploaded documents and embed them locally via the bundled multilingual-e5-small ONNX model. Call this first when documents are attached.',
|
|
21
|
+
input_schema: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
documentIds: {
|
|
25
|
+
type: 'array',
|
|
26
|
+
items: { type: 'string' },
|
|
27
|
+
description: 'Array of document IDs to process',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ['documentIds'],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'infer_intent',
|
|
35
|
+
description: 'Analyze the user message and document digest to determine the bot type and required capabilities. Returns intent classification with confidence score.',
|
|
36
|
+
input_schema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
userMessage: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'The user\'s original message describing what they want',
|
|
42
|
+
},
|
|
43
|
+
domainDigest: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Build-time digest of processed documents (if any) — produced by process_documents',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ['userMessage'],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'recommend_protocols',
|
|
53
|
+
description: 'Based on the inferred intent and context, recommend which protocols (Knowledge, Forms, Appointments, Triage) should be enabled. Provides reasoning for each recommendation.',
|
|
54
|
+
input_schema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
intent: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'The inferred intent (e.g., support_bot, lead_gen, appointment_scheduler)',
|
|
60
|
+
},
|
|
61
|
+
domainDigest: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
description: 'Build-time digest of processed documents — produced by process_documents',
|
|
64
|
+
},
|
|
65
|
+
userMessage: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: 'Original user message for additional context',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
required: ['intent'],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'generate_form_schema',
|
|
75
|
+
description: 'Generate a form schema for data collection based on the bot\'s purpose. Creates field definitions for ghost forms.',
|
|
76
|
+
input_schema: {
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties: {
|
|
79
|
+
description: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
description: 'Description of what data the form should collect',
|
|
82
|
+
},
|
|
83
|
+
formType: {
|
|
84
|
+
type: 'string',
|
|
85
|
+
enum: ['lead_capture', 'support_ticket', 'feedback', 'booking_info', 'custom'],
|
|
86
|
+
description: 'Type of form to generate',
|
|
87
|
+
},
|
|
88
|
+
locale: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'Locale for field labels (e.g., en, es, fr)',
|
|
91
|
+
default: 'en',
|
|
92
|
+
},
|
|
93
|
+
afterSubmitChatMessage: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
description: 'Message shown to users after they submit the form. If not provided, a contextual message will be generated based on the form purpose.',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
required: ['description'],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'generate_appointment_config',
|
|
103
|
+
description: 'Generate appointment/booking configuration for calendar integrations.',
|
|
104
|
+
input_schema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
domainDigest: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
description: 'Build-time digest of documents — used to extract appointment types/services',
|
|
110
|
+
},
|
|
111
|
+
businessType: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
description: 'Type of business (e.g., healthcare, salon, consulting)',
|
|
114
|
+
},
|
|
115
|
+
calendarProviders: {
|
|
116
|
+
type: 'array',
|
|
117
|
+
items: { type: 'string' },
|
|
118
|
+
description: 'Available calendar providers (google, outlook, etc.)',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'generate_triage_config',
|
|
125
|
+
description: 'Generate triage routing configuration for multi-bot orchestration. Defines destinations where users can be routed based on their intent. Each route has a name, description (for RAG matching), and target URL.',
|
|
126
|
+
input_schema: {
|
|
127
|
+
type: 'object',
|
|
128
|
+
properties: {
|
|
129
|
+
routes: {
|
|
130
|
+
type: 'array',
|
|
131
|
+
items: {
|
|
132
|
+
type: 'object',
|
|
133
|
+
properties: {
|
|
134
|
+
name: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
description: 'Display name for the route (e.g., "Sales Support", "Technical Help")',
|
|
137
|
+
},
|
|
138
|
+
description: {
|
|
139
|
+
type: 'string',
|
|
140
|
+
description: 'Description of when users should be routed here - used for RAG matching (e.g., "Users asking about pricing, quotes, or purchasing")',
|
|
141
|
+
},
|
|
142
|
+
url: {
|
|
143
|
+
type: 'string',
|
|
144
|
+
description: 'Target URL to route users to',
|
|
145
|
+
},
|
|
146
|
+
deploymentId: {
|
|
147
|
+
type: 'string',
|
|
148
|
+
description: 'Optional deployment ID if routing to another bot. If not provided, will be generated from the name.',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
required: ['name', 'description', 'url'],
|
|
152
|
+
},
|
|
153
|
+
description: 'Array of routing destinations',
|
|
154
|
+
},
|
|
155
|
+
domainDigest: {
|
|
156
|
+
type: 'string',
|
|
157
|
+
description: 'Build-time digest of documents — used to infer appropriate routes',
|
|
158
|
+
},
|
|
159
|
+
userMessage: {
|
|
160
|
+
type: 'string',
|
|
161
|
+
description: 'Original user message for context about routing needs',
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
required: ['routes'],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'generate_optical_read_config',
|
|
169
|
+
description:
|
|
170
|
+
'Generate Optical Read extraction fields for the bot. The user names slots they want pulled out of an uploaded image; the model resolves them against its own visual prior. The optional hint primes location ("bottom-right of card frame") or format ("MM/DD/YYYY") and is the load-bearing tuning primitive.',
|
|
171
|
+
input_schema: {
|
|
172
|
+
type: 'object',
|
|
173
|
+
properties: {
|
|
174
|
+
fields: {
|
|
175
|
+
type: 'array',
|
|
176
|
+
items: {
|
|
177
|
+
type: 'object',
|
|
178
|
+
properties: {
|
|
179
|
+
label: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
description: 'Display label for the field (e.g., "Date of Birth")',
|
|
182
|
+
},
|
|
183
|
+
idName: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
description:
|
|
186
|
+
'snake_case key used in extractedFields. If omitted, slugified from label.',
|
|
187
|
+
},
|
|
188
|
+
hint: {
|
|
189
|
+
type: 'string',
|
|
190
|
+
description:
|
|
191
|
+
'Optional priming hint — location or format. Examples: "MM/DD/YYYY", "bottom-right of card frame", "all caps".',
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
required: ['label'],
|
|
195
|
+
},
|
|
196
|
+
description: 'Array of extraction fields',
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
required: ['fields'],
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'compose_identity',
|
|
204
|
+
description: 'Generate the bot\'s identity including name, objective, first message, and suggested prompts. Pass userMessage and domainDigest for contextual LLM-generated identity.',
|
|
205
|
+
input_schema: {
|
|
206
|
+
type: 'object',
|
|
207
|
+
properties: {
|
|
208
|
+
intent: {
|
|
209
|
+
type: 'string',
|
|
210
|
+
description: 'The inferred bot intent/type',
|
|
211
|
+
},
|
|
212
|
+
domainDigest: {
|
|
213
|
+
type: 'string',
|
|
214
|
+
description: 'Build-time document digest — used to generate contextual firstMessage and objective',
|
|
215
|
+
},
|
|
216
|
+
userMessage: {
|
|
217
|
+
type: 'string',
|
|
218
|
+
description: 'The original user message describing what they want - used with domainDigest to generate contextual identity',
|
|
219
|
+
},
|
|
220
|
+
organizationName: {
|
|
221
|
+
type: 'string',
|
|
222
|
+
description: 'Name of the organization deploying the bot',
|
|
223
|
+
},
|
|
224
|
+
enabledProtocols: {
|
|
225
|
+
type: 'object',
|
|
226
|
+
description: 'Which protocols are enabled',
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
required: ['intent'],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'set_suggested_prompts',
|
|
234
|
+
description: 'Set the suggested prompts for the bot. Call this AFTER compose_identity to provide localized prompts in the same language as the documents/user. Generate 3 short, specific prompts based on the document content.',
|
|
235
|
+
input_schema: {
|
|
236
|
+
type: 'object',
|
|
237
|
+
properties: {
|
|
238
|
+
prompts: {
|
|
239
|
+
type: 'array',
|
|
240
|
+
items: { type: 'string' },
|
|
241
|
+
minItems: 1,
|
|
242
|
+
maxItems: 5,
|
|
243
|
+
description: 'Array of 3 suggested prompts in the SAME LANGUAGE as the documents. Each prompt should be short (max 8 words), specific to the content, and start with an action word.',
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
required: ['prompts'],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'generate_bot_summary',
|
|
251
|
+
description: 'Generate a summarized description of the bot, will be used as meta-data. Call this after all configurations are complete, before deployment. The summary describes what the bot does, what knowledge it has, and its capabilities.',
|
|
252
|
+
input_schema: {
|
|
253
|
+
type: 'object',
|
|
254
|
+
properties: {},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: 'save_modular_bot',
|
|
259
|
+
description:
|
|
260
|
+
"Save the bot's composed configuration to a deployment row in SQLite. Only call after user confirms the recommended protocols. This does NOT build the artifact — the user clicks 'Build & Download' afterward.",
|
|
261
|
+
input_schema: {
|
|
262
|
+
type: 'object',
|
|
263
|
+
properties: {
|
|
264
|
+
sessionId: {
|
|
265
|
+
type: 'string',
|
|
266
|
+
description: 'The modular session ID',
|
|
267
|
+
},
|
|
268
|
+
confirmedProtocols: {
|
|
269
|
+
type: 'object',
|
|
270
|
+
description: 'User-confirmed protocol selections',
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
required: ['sessionId', 'confirmedProtocols'],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Tool name to display label mapping
|
|
280
|
+
*/
|
|
281
|
+
export const TOOL_LABELS = {
|
|
282
|
+
process_documents: 'Processing documents',
|
|
283
|
+
infer_intent: 'Analyzing intent',
|
|
284
|
+
recommend_protocols: 'Recommending protocols',
|
|
285
|
+
generate_form_schema: 'Generating form',
|
|
286
|
+
generate_appointment_config: 'Configuring appointments',
|
|
287
|
+
generate_triage_config: 'Configuring triage routes',
|
|
288
|
+
generate_optical_read_config: 'Configuring optical read fields',
|
|
289
|
+
compose_identity: 'Composing identity',
|
|
290
|
+
set_suggested_prompts: 'Setting suggested prompts',
|
|
291
|
+
generate_bot_summary: 'Generating bot summary',
|
|
292
|
+
save_modular_bot: 'Saving bot configuration',
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Tool name to icon mapping (for UI)
|
|
297
|
+
*/
|
|
298
|
+
export const TOOL_ICONS = {
|
|
299
|
+
process_documents: 'document',
|
|
300
|
+
infer_intent: 'target',
|
|
301
|
+
recommend_protocols: 'puzzle',
|
|
302
|
+
generate_form_schema: 'form',
|
|
303
|
+
generate_appointment_config: 'calendar',
|
|
304
|
+
generate_triage_config: 'split',
|
|
305
|
+
generate_optical_read_config: 'eye',
|
|
306
|
+
compose_identity: 'robot',
|
|
307
|
+
set_suggested_prompts: 'message-square',
|
|
308
|
+
generate_bot_summary: 'sparkles',
|
|
309
|
+
save_modular_bot: 'save',
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get tool definition by name
|
|
314
|
+
*/
|
|
315
|
+
export function getToolByName(name) {
|
|
316
|
+
return BUILDER_TOOLS.find(tool => tool.name === name);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Validate tool input against schema
|
|
321
|
+
*/
|
|
322
|
+
export function validateToolInput(toolName, input) {
|
|
323
|
+
const tool = getToolByName(toolName);
|
|
324
|
+
if (!tool) {
|
|
325
|
+
return { valid: false, error: `Unknown tool: ${toolName}` };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const schema = tool.input_schema;
|
|
329
|
+
const required = schema.required || [];
|
|
330
|
+
|
|
331
|
+
for (const field of required) {
|
|
332
|
+
if (input[field] === undefined || input[field] === null) {
|
|
333
|
+
return { valid: false, error: `Missing required field: ${field}` };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return { valid: true };
|
|
338
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol Validators for Builder Flow
|
|
3
|
+
*
|
|
4
|
+
* Validates protocol-specific configuration before deployment.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Protocol-specific validators
|
|
9
|
+
*/
|
|
10
|
+
export const PROTOCOL_VALIDATORS = {
|
|
11
|
+
/**
|
|
12
|
+
* Validate knowledge protocol configuration
|
|
13
|
+
* @param {Object} data - Knowledge protocol data
|
|
14
|
+
* @param {Object} options - Validation options
|
|
15
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
16
|
+
*/
|
|
17
|
+
knowledge: (data, options = {}) => {
|
|
18
|
+
if (options.skipRag) {
|
|
19
|
+
return { valid: true };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// All builds are vector-only — process_documents embeds locally and
|
|
23
|
+
// stashes the embedding blob on the session. Documents are the only
|
|
24
|
+
// valid source; a hand-typed summary alone can no longer satisfy the
|
|
25
|
+
// step because there's nothing to embed.
|
|
26
|
+
const hasDocuments = data.documents && data.documents.length > 0;
|
|
27
|
+
if (!hasDocuments) {
|
|
28
|
+
return { valid: false, error: 'At least one document is required' };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { valid: true };
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate form gathering protocol configuration
|
|
36
|
+
* @param {Object} data - Form gathering protocol data
|
|
37
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
38
|
+
*/
|
|
39
|
+
formGathering: (data) => {
|
|
40
|
+
if (!data.generatedFormJson) {
|
|
41
|
+
return { valid: false, error: 'Form structure is required' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const parsed =
|
|
46
|
+
typeof data.generatedFormJson === 'string'
|
|
47
|
+
? JSON.parse(data.generatedFormJson)
|
|
48
|
+
: data.generatedFormJson;
|
|
49
|
+
|
|
50
|
+
if (!parsed.sections || !Array.isArray(parsed.sections)) {
|
|
51
|
+
return { valid: false, error: 'Form must have sections array' };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { valid: true };
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return { valid: false, error: 'Invalid form JSON structure' };
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Validate appointments protocol configuration
|
|
62
|
+
* @param {Object} data - Appointments protocol data
|
|
63
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
64
|
+
*/
|
|
65
|
+
appointments: (data) => {
|
|
66
|
+
if (!data.destinations || data.destinations.length === 0) {
|
|
67
|
+
return { valid: false, error: 'At least one calendar destination is required' };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const dest of data.destinations) {
|
|
71
|
+
if (!dest.id || !dest.provider || !dest.popupUrl) {
|
|
72
|
+
return {
|
|
73
|
+
valid: false,
|
|
74
|
+
error: 'All calendars must have id, provider, and popupUrl',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const validProviders = ['calendly', 'cal.com'];
|
|
79
|
+
if (!validProviders.includes(dest.provider)) {
|
|
80
|
+
return {
|
|
81
|
+
valid: false,
|
|
82
|
+
error: `Invalid provider: ${dest.provider}. Must be one of: ${validProviders.join(', ')}`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { valid: true };
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Validate triage protocol configuration
|
|
92
|
+
* @param {Object} data - Triage protocol data with routes array
|
|
93
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
94
|
+
*/
|
|
95
|
+
triage: (data) => {
|
|
96
|
+
// Support both { routes: [...] } structure and direct array (legacy)
|
|
97
|
+
const routes = Array.isArray(data) ? data : data?.routes;
|
|
98
|
+
|
|
99
|
+
if (!routes || !Array.isArray(routes) || routes.length === 0) {
|
|
100
|
+
return { valid: false, error: 'At least one triage route is required' };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (const route of routes) {
|
|
104
|
+
if (!route.name || !route.description || !route.url) {
|
|
105
|
+
return {
|
|
106
|
+
valid: false,
|
|
107
|
+
error: 'All routes must have name, description, and url',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!route.deploymentId) {
|
|
112
|
+
return {
|
|
113
|
+
valid: false,
|
|
114
|
+
error: `Route "${route.name}" is missing deploymentId`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { valid: true };
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Validate core configuration
|
|
125
|
+
* @param {Object} config - Core configuration
|
|
126
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
127
|
+
*/
|
|
128
|
+
export function validateCoreConfig(config) {
|
|
129
|
+
const errors = [];
|
|
130
|
+
|
|
131
|
+
if (!config.provider) errors.push('Provider is required');
|
|
132
|
+
if (!config.model) errors.push('Model is required');
|
|
133
|
+
// For inverted flow, apiKey is fetched at deployment time using apiKeyId
|
|
134
|
+
// Only require apiKey if not using inverted flow
|
|
135
|
+
if (!config._invertedFlow && !config.apiKey) {
|
|
136
|
+
errors.push('API Key is required');
|
|
137
|
+
}
|
|
138
|
+
// For inverted flow, require apiKeyId instead
|
|
139
|
+
if (config._invertedFlow && !config.apiKeyId) {
|
|
140
|
+
errors.push('API Key ID is required');
|
|
141
|
+
}
|
|
142
|
+
if (!config.botName) errors.push('Bot Name is required');
|
|
143
|
+
|
|
144
|
+
if (config.botName && !/^[a-z0-9-]+$/.test(config.botName)) {
|
|
145
|
+
errors.push('Bot name must contain only lowercase letters, numbers, and hyphens');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
valid: errors.length === 0,
|
|
150
|
+
errors,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Validate identity configuration
|
|
156
|
+
* @param {Object} config - Identity configuration
|
|
157
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
158
|
+
*/
|
|
159
|
+
export function validateIdentityConfig(config) {
|
|
160
|
+
const errors = [];
|
|
161
|
+
|
|
162
|
+
if (!config.objective) errors.push('Objective is required');
|
|
163
|
+
if (!config.firstMessage) errors.push('First Message is required');
|
|
164
|
+
if (!config.chatDisplayName) errors.push('Chat Display Name is required');
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
valid: errors.length === 0,
|
|
168
|
+
errors,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Validate that at least one protocol is enabled
|
|
174
|
+
* @param {Object} enabledProtocols - Protocol toggles
|
|
175
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
176
|
+
*/
|
|
177
|
+
export function validateEnabledProtocols(enabledProtocols) {
|
|
178
|
+
const hasEnabled =
|
|
179
|
+
enabledProtocols.knowledge ||
|
|
180
|
+
enabledProtocols.formGathering ||
|
|
181
|
+
enabledProtocols.appointments ||
|
|
182
|
+
enabledProtocols.triage;
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
valid: hasEnabled,
|
|
186
|
+
error: hasEnabled ? null : 'At least one protocol must be enabled',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Validate session is ready for deployment
|
|
192
|
+
* @param {Object} session - Modular session object
|
|
193
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
194
|
+
*/
|
|
195
|
+
export function validateSessionForDeployment(session) {
|
|
196
|
+
const errors = [];
|
|
197
|
+
|
|
198
|
+
// Check core config
|
|
199
|
+
if (!session.coreConfig) {
|
|
200
|
+
errors.push('Core configuration is missing');
|
|
201
|
+
} else {
|
|
202
|
+
const coreValidation = validateCoreConfig(session.coreConfig);
|
|
203
|
+
if (!coreValidation.valid) {
|
|
204
|
+
errors.push(...coreValidation.errors);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Check identity config
|
|
209
|
+
if (!session.identityConfig) {
|
|
210
|
+
errors.push('Identity configuration is missing');
|
|
211
|
+
} else {
|
|
212
|
+
const identityValidation = validateIdentityConfig(session.identityConfig);
|
|
213
|
+
if (!identityValidation.valid) {
|
|
214
|
+
errors.push(...identityValidation.errors);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Check enabled protocols
|
|
219
|
+
const protocolValidation = validateEnabledProtocols(session.enabledProtocols);
|
|
220
|
+
if (!protocolValidation.valid) {
|
|
221
|
+
errors.push(protocolValidation.error);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Validate each enabled protocol's data
|
|
225
|
+
for (const [protocol, enabled] of Object.entries(session.enabledProtocols)) {
|
|
226
|
+
if (enabled && PROTOCOL_VALIDATORS[protocol]) {
|
|
227
|
+
const protocolData = session.protocolData[protocol] || {};
|
|
228
|
+
const validation = PROTOCOL_VALIDATORS[protocol](protocolData);
|
|
229
|
+
if (!validation.valid) {
|
|
230
|
+
errors.push(`${protocol}: ${validation.error}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
valid: errors.length === 0,
|
|
237
|
+
errors,
|
|
238
|
+
};
|
|
239
|
+
}
|