snow-flow 1.2.1 → 1.2.2

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.
@@ -6,8 +6,9 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.FlowBuilderAgent = void 0;
8
8
  const base_agent_1 = require("./base-agent");
9
+ const mcp_execution_bridge_1 = require("../queen/mcp-execution-bridge");
9
10
  class FlowBuilderAgent extends base_agent_1.BaseAgent {
10
- constructor(config) {
11
+ constructor(config, mcpBridge) {
11
12
  super({
12
13
  type: 'flow-builder',
13
14
  capabilities: [
@@ -32,504 +33,165 @@ class FlowBuilderAgent extends base_agent_1.BaseAgent {
32
33
  ],
33
34
  ...config
34
35
  });
36
+ // Initialize MCP bridge for real ServiceNow deployment
37
+ this.mcpBridge = mcpBridge || new mcp_execution_bridge_1.MCPExecutionBridge(this.memory);
38
+ }
39
+ /**
40
+ * Create MCP recommendation for tool execution
41
+ */
42
+ createMCPRecommendation(tool, params, action) {
43
+ return {
44
+ agentId: this.id,
45
+ agentType: this.type,
46
+ action: action,
47
+ tool: tool,
48
+ server: this.getServerForTool(tool),
49
+ params: params,
50
+ reasoning: `Flow builder executing ${action}`,
51
+ confidence: 0.9,
52
+ dependencies: []
53
+ };
54
+ }
55
+ /**
56
+ * Map MCP tools to their respective servers
57
+ */
58
+ getServerForTool(tool) {
59
+ const toolServerMap = {
60
+ 'snow_create_flow': 'flow-composer',
61
+ 'snow_test_flow_with_mock': 'operations',
62
+ 'snow_comprehensive_flow_test': 'intelligent',
63
+ 'snow_validate_flow_definition': 'deployment',
64
+ 'snow_discover_existing_flows': 'intelligent',
65
+ 'snow_link_catalog_to_flow': 'operations'
66
+ };
67
+ return toolServerMap[tool] || 'deployment';
68
+ }
69
+ /**
70
+ * Execute MCP tool with error handling and fallbacks
71
+ */
72
+ async executeWithFallback(tool, params, action) {
73
+ const recommendation = this.createMCPRecommendation(tool, params, action);
74
+ try {
75
+ const result = await this.mcpBridge.executeAgentRecommendation({ id: this.id, type: this.type }, recommendation);
76
+ if (!result.success) {
77
+ return {
78
+ success: false,
79
+ error: result.error || `MCP tool ${tool} execution failed`
80
+ };
81
+ }
82
+ if (result.fallbackUsed) {
83
+ console.warn(`⚠️ MCP tool ${tool} used fallback strategy`);
84
+ }
85
+ return {
86
+ success: true,
87
+ result: result.toolResult
88
+ };
89
+ }
90
+ catch (error) {
91
+ return {
92
+ success: false,
93
+ error: error instanceof Error ? error.message : String(error)
94
+ };
95
+ }
35
96
  }
36
97
  async execute(instruction, context) {
37
98
  try {
38
99
  this.setStatus('working');
39
- await this.reportProgress('Starting flow creation', 0);
40
- // Analyze flow requirements
41
- const requirements = await this.analyzeFlowRequirements(instruction);
42
- await this.reportProgress('Analyzed flow requirements', 15);
43
- // Check for existing flows
44
- const existingFlows = await this.checkExistingFlows(requirements);
45
- if (existingFlows.length > 0) {
46
- await this.reportProgress('Found existing flows for reference', 25);
100
+ await this.reportProgress('🚀 Starting MCP-powered flow creation', 0);
101
+ // 🔍 STEP 1: Check for existing flows to avoid duplication
102
+ await this.reportProgress('🔍 Discovering existing flows', 10);
103
+ const discoveryResult = await this.executeWithFallback('snow_discover_existing_flows', { flow_purpose: instruction }, 'discover-existing-flows');
104
+ let existingFlowsCount = 0;
105
+ if (discoveryResult.success && discoveryResult.result?.found) {
106
+ existingFlowsCount = discoveryResult.result.found.length;
107
+ if (existingFlowsCount > 0) {
108
+ await this.reportProgress(`📊 Found ${existingFlowsCount} similar flows for reference`, 20);
109
+ }
110
+ }
111
+ // 🧠 STEP 2: CREATE FLOW VIA MCP TOOL (This is the key fix!)
112
+ await this.reportProgress('🧠 Creating flow via intelligent MCP system', 30);
113
+ const createResult = await this.executeWithFallback('snow_create_flow', {
114
+ instruction: instruction,
115
+ deploy_immediately: true,
116
+ enable_intelligent_analysis: true,
117
+ create_missing_artifacts: true,
118
+ validation_level: 'standard'
119
+ }, 'create-flow');
120
+ if (!createResult.success) {
121
+ throw new Error(`❌ Flow creation via MCP failed: ${createResult.error}`);
122
+ }
123
+ await this.reportProgress('✅ Flow created and deployed to ServiceNow!', 60);
124
+ // 🧪 STEP 3: Test the created flow with mock data
125
+ await this.reportProgress('🧪 Testing flow with mock data', 70);
126
+ const testResult = await this.executeWithFallback('snow_test_flow_with_mock', {
127
+ flow_id: createResult.result.sys_id || createResult.result.flow_sys_id,
128
+ create_test_user: true,
129
+ cleanup_after_test: true,
130
+ simulate_approvals: true
131
+ }, 'test-flow');
132
+ let testStatus = '❓ Test status unknown';
133
+ if (testResult.success) {
134
+ testStatus = '✅ Flow tested successfully';
135
+ }
136
+ else {
137
+ testStatus = `⚠️ Flow test failed: ${testResult.error}`;
47
138
  }
48
- // Design flow structure
49
- const flowStructure = await this.designFlowStructure(requirements);
50
- await this.reportProgress('Designed flow structure', 40);
51
- // Create flow definition
52
- const flowDefinition = await this.createFlowDefinition(requirements, flowStructure);
53
- await this.reportProgress('Created flow definition', 60);
54
- // Configure triggers
55
- const triggers = await this.configureTriggers(requirements);
56
- await this.reportProgress('Configured flow triggers', 70);
57
- // Set up actions
58
- const actions = await this.setupActions(requirements, flowStructure);
59
- await this.reportProgress('Set up flow actions', 80);
60
- // Create flow artifact
139
+ // 🎯 STEP 4: Create artifact with REAL ServiceNow data
140
+ const realFlowData = createResult.result;
61
141
  const artifact = {
62
142
  type: 'flow',
63
- name: requirements.name,
143
+ name: realFlowData.name || realFlowData.flow_name || 'created_flow',
144
+ sys_id: realFlowData.sys_id || realFlowData.flow_sys_id, // ✅ REAL sys_id from ServiceNow!
64
145
  config: {
65
- definition: flowDefinition,
66
- triggers,
67
- actions,
146
+ // Store the actual flow definition returned from ServiceNow
147
+ definition: realFlowData.definition || realFlowData,
148
+ deployed: true,
149
+ tested: testResult.success,
150
+ created_via: 'snow_create_flow_mcp',
68
151
  metadata: {
69
- description: requirements.description,
70
- category: requirements.category,
71
- type: requirements.flowType
152
+ description: instruction,
153
+ type: realFlowData.type || 'flow',
154
+ active: realFlowData.active || true
72
155
  }
73
156
  },
74
- dependencies: requirements.dependencies
157
+ dependencies: realFlowData.dependencies || []
75
158
  };
76
159
  // Store artifact for other agents
77
160
  await this.storeArtifact(artifact);
78
- await this.reportProgress('Flow artifact created and stored', 90);
79
- // Prepare deployment instructions
80
- const deploymentInstructions = this.prepareDeploymentInstructions(requirements, artifact);
81
- await this.reportProgress('Flow creation completed', 100);
161
+ await this.reportProgress('📦 Flow artifact stored with real ServiceNow data', 90);
162
+ await this.reportProgress('🎉 MCP-powered flow creation completed!', 100);
82
163
  this.setStatus('completed');
83
- await this.logActivity('flow_creation', true, {
84
- flowName: requirements.name,
85
- flowType: requirements.flowType,
86
- triggerCount: triggers.length,
87
- actionCount: actions.length
164
+ await this.logActivity('flow_creation_mcp', true, {
165
+ flowName: artifact.name,
166
+ sys_id: artifact.sys_id,
167
+ tested: testResult.success,
168
+ deployedToServiceNow: true,
169
+ discoveredSimilar: existingFlowsCount
88
170
  });
89
171
  return {
90
172
  success: true,
91
173
  artifacts: [artifact],
92
- message: `Flow "${requirements.name}" created successfully`,
174
+ message: `✅ Flow "${artifact.name}" created and deployed to ServiceNow successfully! ${testStatus}`,
93
175
  metadata: {
94
- deploymentInstructions,
95
- flowType: requirements.flowType,
96
- triggers: triggers.length,
97
- actions: actions.length,
98
- hasApprovals: requirements.needsApproval,
99
- hasIntegrations: requirements.needsIntegration
176
+ sys_id: artifact.sys_id, // ✅ Real ServiceNow sys_id!
177
+ deployed_to_servicenow: true,
178
+ tested: testResult.success,
179
+ test_details: testResult.result,
180
+ discovered_similar_flows: existingFlowsCount,
181
+ mcp_powered: true,
182
+ has_content: true // ✅ No more empty flows!
100
183
  }
101
184
  };
102
185
  }
103
186
  catch (error) {
104
187
  this.setStatus('failed');
105
- await this.logActivity('flow_creation', false, { error: error.message });
188
+ await this.logActivity('flow_creation_mcp', false, { error: error.message });
106
189
  return {
107
190
  success: false,
108
191
  error: error,
109
- message: `Failed to create flow: ${error.message}`
192
+ message: `❌ MCP-powered flow creation failed: ${error.message}`
110
193
  };
111
194
  }
112
195
  }
113
- async analyzeFlowRequirements(instruction) {
114
- const requirements = {
115
- name: '',
116
- description: instruction,
117
- flowType: 'flow', // flow, subflow, or action
118
- category: 'custom',
119
- triggerType: '',
120
- targetTable: '',
121
- needsApproval: false,
122
- needsNotification: false,
123
- needsIntegration: false,
124
- needsCatalogLink: false,
125
- conditions: [],
126
- dependencies: [],
127
- steps: []
128
- };
129
- // Extract flow name
130
- const nameMatch = instruction.match(/(?:create|build|design)\s+(?:a\s+)?([a-zA-Z0-9_\s]+)\s*(?:flow|workflow|process)/i);
131
- if (nameMatch) {
132
- requirements.name = nameMatch[1].trim().toLowerCase().replace(/\s+/g, '_') + '_flow';
133
- }
134
- // Determine flow type
135
- if (/subflow/i.test(instruction)) {
136
- requirements.flowType = 'subflow';
137
- }
138
- else if (/action/i.test(instruction)) {
139
- requirements.flowType = 'action';
140
- }
141
- // Detect trigger type
142
- if (/when\s+(?:a\s+)?(?:new\s+)?record\s+is\s+created/i.test(instruction)) {
143
- requirements.triggerType = 'record_created';
144
- }
145
- else if (/when\s+(?:a\s+)?record\s+is\s+updated/i.test(instruction)) {
146
- requirements.triggerType = 'record_updated';
147
- }
148
- else if (/scheduled|daily|weekly|monthly/i.test(instruction)) {
149
- requirements.triggerType = 'scheduled';
150
- }
151
- else if (/manual|on[\s-]?demand/i.test(instruction)) {
152
- requirements.triggerType = 'manual';
153
- }
154
- // Detect target table
155
- if (/incident/i.test(instruction)) {
156
- requirements.targetTable = 'incident';
157
- requirements.category = 'itsm';
158
- }
159
- else if (/request|catalog/i.test(instruction)) {
160
- requirements.targetTable = 'sc_request';
161
- requirements.category = 'service_catalog';
162
- requirements.needsCatalogLink = true;
163
- }
164
- else if (/change/i.test(instruction)) {
165
- requirements.targetTable = 'change_request';
166
- requirements.category = 'change_management';
167
- }
168
- else if (/task/i.test(instruction)) {
169
- requirements.targetTable = 'task';
170
- requirements.category = 'task_management';
171
- }
172
- // Feature detection
173
- if (/approval|approve|manager/i.test(instruction)) {
174
- requirements.needsApproval = true;
175
- requirements.steps.push('approval');
176
- requirements.dependencies.push('sys_user_group');
177
- }
178
- if (/notify|notification|email|send\s+message/i.test(instruction)) {
179
- requirements.needsNotification = true;
180
- requirements.steps.push('notification');
181
- }
182
- if (/integrate|api|external|rest/i.test(instruction)) {
183
- requirements.needsIntegration = true;
184
- requirements.steps.push('integration');
185
- requirements.dependencies.push('sys_rest_message');
186
- }
187
- // Extract conditions
188
- const conditionMatches = instruction.match(/(?:if|when|condition)\s+([^,\.]+)/gi);
189
- if (conditionMatches) {
190
- requirements.conditions = conditionMatches.map(match => match.replace(/^(if|when|condition)\s+/i, '').trim());
191
- }
192
- return requirements;
193
- }
194
- async checkExistingFlows(requirements) {
195
- // Would use snow_discover_existing_flows MCP tool
196
- // For now, return empty array
197
- return [];
198
- }
199
- async designFlowStructure(requirements) {
200
- const structure = {
201
- stages: [],
202
- decisionPoints: [],
203
- parallelPaths: false,
204
- errorHandling: true
205
- };
206
- // Start stage
207
- structure.stages.push({
208
- name: 'Start',
209
- type: 'trigger',
210
- position: { x: 100, y: 100 }
211
- });
212
- let currentX = 300;
213
- const currentY = 100;
214
- const stepSpacing = 200;
215
- // Add validation stage if complex flow
216
- if (requirements.conditions.length > 0) {
217
- structure.stages.push({
218
- name: 'Validate Input',
219
- type: 'action',
220
- actionType: 'script',
221
- position: { x: currentX, y: currentY }
222
- });
223
- currentX += stepSpacing;
224
- }
225
- // Add approval stage if needed
226
- if (requirements.needsApproval) {
227
- structure.stages.push({
228
- name: 'Request Approval',
229
- type: 'approval',
230
- position: { x: currentX, y: currentY }
231
- });
232
- currentX += stepSpacing;
233
- // Add decision point for approval result
234
- structure.decisionPoints.push({
235
- name: 'Approval Decision',
236
- condition: 'approval.result == "approved"',
237
- position: { x: currentX, y: currentY },
238
- truePath: 'Continue',
239
- falsePath: 'Rejection Handler'
240
- });
241
- currentX += stepSpacing;
242
- }
243
- // Add main action stage
244
- structure.stages.push({
245
- name: 'Execute Main Action',
246
- type: 'action',
247
- actionType: requirements.needsIntegration ? 'integration' : 'script',
248
- position: { x: currentX, y: currentY }
249
- });
250
- currentX += stepSpacing;
251
- // Add notification stage if needed
252
- if (requirements.needsNotification) {
253
- structure.stages.push({
254
- name: 'Send Notification',
255
- type: 'notification',
256
- position: { x: currentX, y: currentY }
257
- });
258
- currentX += stepSpacing;
259
- }
260
- // End stage
261
- structure.stages.push({
262
- name: 'End',
263
- type: 'end',
264
- position: { x: currentX, y: currentY }
265
- });
266
- // Add error handling path
267
- if (structure.errorHandling) {
268
- structure.stages.push({
269
- name: 'Error Handler',
270
- type: 'error_handler',
271
- position: { x: 300, y: 250 }
272
- });
273
- }
274
- return structure;
275
- }
276
- async createFlowDefinition(requirements, structure) {
277
- const definition = {
278
- name: requirements.name,
279
- description: requirements.description,
280
- type: requirements.flowType,
281
- category: requirements.category,
282
- active: true,
283
- stages: structure.stages,
284
- connections: [],
285
- variables: [],
286
- settings: {
287
- run_as: 'system',
288
- error_handling: 'stop_on_error',
289
- logging_level: 'info'
290
- }
291
- };
292
- // Create connections between stages
293
- for (let i = 0; i < structure.stages.length - 1; i++) {
294
- const fromStage = structure.stages[i];
295
- const toStage = structure.stages[i + 1];
296
- // Skip if this is a decision point
297
- if (structure.decisionPoints.some(dp => dp.name === fromStage.name)) {
298
- continue;
299
- }
300
- definition.connections.push({
301
- from: fromStage.name,
302
- to: toStage.name,
303
- condition: 'always'
304
- });
305
- }
306
- // Add decision point connections
307
- for (const decisionPoint of structure.decisionPoints) {
308
- definition.connections.push({
309
- from: decisionPoint.name,
310
- to: decisionPoint.truePath,
311
- condition: decisionPoint.condition
312
- });
313
- definition.connections.push({
314
- from: decisionPoint.name,
315
- to: decisionPoint.falsePath,
316
- condition: `!(${decisionPoint.condition})`
317
- });
318
- }
319
- // Define flow variables
320
- if (requirements.targetTable) {
321
- definition.variables.push({
322
- name: 'current_record',
323
- type: 'reference',
324
- reference_table: requirements.targetTable,
325
- description: 'The record that triggered this flow'
326
- });
327
- }
328
- if (requirements.needsApproval) {
329
- definition.variables.push({
330
- name: 'approval_result',
331
- type: 'string',
332
- description: 'Result of the approval request'
333
- });
334
- definition.variables.push({
335
- name: 'approver',
336
- type: 'reference',
337
- reference_table: 'sys_user',
338
- description: 'User who approved/rejected the request'
339
- });
340
- }
341
- return definition;
342
- }
343
- async configureTriggers(requirements) {
344
- const triggers = [];
345
- switch (requirements.triggerType) {
346
- case 'record_created':
347
- triggers.push({
348
- type: 'record_created',
349
- table: requirements.targetTable,
350
- conditions: requirements.conditions,
351
- active: true,
352
- order: 100
353
- });
354
- break;
355
- case 'record_updated':
356
- triggers.push({
357
- type: 'record_updated',
358
- table: requirements.targetTable,
359
- conditions: requirements.conditions,
360
- fields_to_watch: ['state', 'priority', 'assignment_group'],
361
- active: true,
362
- order: 100
363
- });
364
- break;
365
- case 'scheduled':
366
- triggers.push({
367
- type: 'scheduled',
368
- schedule: {
369
- type: 'daily',
370
- time: '00:00',
371
- timezone: 'UTC'
372
- },
373
- active: true,
374
- order: 100
375
- });
376
- break;
377
- case 'manual':
378
- triggers.push({
379
- type: 'manual',
380
- roles: ['admin', 'itil'],
381
- ui_action: true,
382
- active: true,
383
- order: 100
384
- });
385
- break;
386
- default:
387
- // Default to record created trigger
388
- triggers.push({
389
- type: 'record_created',
390
- table: requirements.targetTable || 'task',
391
- active: true,
392
- order: 100
393
- });
394
- }
395
- return triggers;
396
- }
397
- async setupActions(requirements, structure) {
398
- const actions = [];
399
- for (const stage of structure.stages) {
400
- if (stage.type === 'action' || stage.type === 'approval' || stage.type === 'notification') {
401
- const action = await this.createAction(stage, requirements);
402
- if (action) {
403
- actions.push(action);
404
- }
405
- }
406
- }
407
- return actions;
408
- }
409
- async createAction(stage, requirements) {
410
- const action = {
411
- name: stage.name,
412
- type: stage.type,
413
- order: 100,
414
- active: true
415
- };
416
- switch (stage.type) {
417
- case 'approval':
418
- action.config = {
419
- approval_type: 'user',
420
- approvers: 'current_record.assignment_group.manager',
421
- approval_message: `Please approve ${requirements.targetTable} ${requirements.name}`,
422
- due_date: '3 business days',
423
- reminders: true,
424
- escalation: {
425
- enabled: true,
426
- after: '2 business days',
427
- escalate_to: 'current_record.assignment_group.manager.manager'
428
- }
429
- };
430
- break;
431
- case 'notification':
432
- action.config = {
433
- recipients: ['current_record.requested_for', 'current_record.assigned_to'],
434
- template: 'flow_notification',
435
- subject: `${requirements.name} - Action Completed`,
436
- message: 'The flow has completed processing your request.',
437
- include_record_link: true
438
- };
439
- break;
440
- case 'action':
441
- if (stage.actionType === 'integration') {
442
- action.config = {
443
- rest_message: 'External System Integration',
444
- endpoint: '/api/process',
445
- method: 'POST',
446
- headers: {
447
- 'Content-Type': 'application/json'
448
- },
449
- body: {
450
- record_number: '${current_record.number}',
451
- record_type: requirements.targetTable,
452
- action: 'process'
453
- }
454
- };
455
- }
456
- else {
457
- action.config = {
458
- script: `
459
- // ${stage.name} Script
460
- (function execute(inputs, outputs) {
461
- // Get the current record
462
- var current = inputs.current_record;
463
-
464
- // Perform the main action
465
- gs.info('Executing ${stage.name} for ' + current.getDisplayValue());
466
-
467
- // Add your business logic here
468
- current.work_notes = 'Processed by ${requirements.name} flow';
469
- current.update();
470
-
471
- // Set outputs
472
- outputs.success = true;
473
- outputs.message = 'Action completed successfully';
474
-
475
- })(inputs, outputs);`
476
- };
477
- }
478
- break;
479
- case 'error_handler':
480
- action.config = {
481
- script: `
482
- // Error Handler Script
483
- (function handleError(inputs, outputs) {
484
- var errorMessage = inputs.error_message || 'Unknown error';
485
- var current = inputs.current_record;
486
-
487
- // Log the error
488
- gs.error('Flow error in ${requirements.name}: ' + errorMessage);
489
-
490
- // Update the record with error information
491
- if (current) {
492
- current.work_notes = 'Flow error: ' + errorMessage;
493
- current.update();
494
- }
495
-
496
- // Send error notification if configured
497
- if (${requirements.needsNotification}) {
498
- // Send error notification logic
499
- }
500
-
501
- })(inputs, outputs);`
502
- };
503
- break;
504
- }
505
- return action;
506
- }
507
- prepareDeploymentInstructions(requirements, artifact) {
508
- return {
509
- primaryTool: 'snow_create_flow',
510
- instruction: `Create ${requirements.flowType} for ${requirements.targetTable || 'general'} with ${requirements.needsApproval ? 'approval' : 'automated'} processing`,
511
- testingTools: [
512
- {
513
- tool: 'snow_test_flow_with_mock',
514
- purpose: 'Test flow with mock data before deployment'
515
- },
516
- {
517
- tool: 'snow_comprehensive_flow_test',
518
- purpose: 'Run comprehensive tests including edge cases'
519
- }
520
- ],
521
- validationSteps: [
522
- 'Verify trigger configuration',
523
- 'Test all decision branches',
524
- 'Validate approval routing if applicable',
525
- 'Check error handling paths',
526
- 'Verify notifications are sent correctly'
527
- ],
528
- catalogLinking: requirements.needsCatalogLink ? {
529
- tool: 'snow_link_catalog_to_flow',
530
- instruction: 'Link this flow to relevant catalog items'
531
- } : null
532
- };
533
- }
534
196
  }
535
197
  exports.FlowBuilderAgent = FlowBuilderAgent;