snow-flow 1.1.0
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/.env.example +49 -0
- package/.mcp.json +106 -0
- package/.mcp.json.template +106 -0
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/bin/snow-flow +4 -0
- package/bin/snow-flow.js +3 -0
- package/claude-flow +79 -0
- package/claude-flow.bat +18 -0
- package/claude-flow.config.json +20 -0
- package/claude-flow.ps1 +24 -0
- package/dist/agents/app-creator.agent.d.ts +55 -0
- package/dist/agents/app-creator.agent.d.ts.map +1 -0
- package/dist/agents/app-creator.agent.js +1292 -0
- package/dist/agents/app-creator.agent.js.map +1 -0
- package/dist/agents/base-snow-agent.d.ts +63 -0
- package/dist/agents/base-snow-agent.d.ts.map +1 -0
- package/dist/agents/base-snow-agent.js +296 -0
- package/dist/agents/base-snow-agent.js.map +1 -0
- package/dist/agents/script-generator.agent.d.ts +32 -0
- package/dist/agents/script-generator.agent.d.ts.map +1 -0
- package/dist/agents/script-generator.agent.js +772 -0
- package/dist/agents/script-generator.agent.js.map +1 -0
- package/dist/agents/ui-builder.agent.d.ts +50 -0
- package/dist/agents/ui-builder.agent.d.ts.map +1 -0
- package/dist/agents/ui-builder.agent.js +1539 -0
- package/dist/agents/ui-builder.agent.js.map +1 -0
- package/dist/agents/widget-builder.agent.d.ts +32 -0
- package/dist/agents/widget-builder.agent.d.ts.map +1 -0
- package/dist/agents/widget-builder.agent.js +439 -0
- package/dist/agents/widget-builder.agent.js.map +1 -0
- package/dist/agents/workflow-designer.agent.d.ts +31 -0
- package/dist/agents/workflow-designer.agent.d.ts.map +1 -0
- package/dist/agents/workflow-designer.agent.js +583 -0
- package/dist/agents/workflow-designer.agent.js.map +1 -0
- package/dist/api/error-handling.d.ts +214 -0
- package/dist/api/error-handling.d.ts.map +1 -0
- package/dist/api/error-handling.js +594 -0
- package/dist/api/error-handling.js.map +1 -0
- package/dist/api/flow-designer-api.d.ts +167 -0
- package/dist/api/flow-designer-api.d.ts.map +1 -0
- package/dist/api/flow-designer-api.js +555 -0
- package/dist/api/flow-designer-api.js.map +1 -0
- package/dist/api/natural-language-mapper.d.ts +126 -0
- package/dist/api/natural-language-mapper.d.ts.map +1 -0
- package/dist/api/natural-language-mapper.js +498 -0
- package/dist/api/natural-language-mapper.js.map +1 -0
- package/dist/api/performance-optimizer.d.ts +240 -0
- package/dist/api/performance-optimizer.d.ts.map +1 -0
- package/dist/api/performance-optimizer.js +701 -0
- package/dist/api/performance-optimizer.js.map +1 -0
- package/dist/api/transaction-manager.d.ts +189 -0
- package/dist/api/transaction-manager.d.ts.map +1 -0
- package/dist/api/transaction-manager.js +576 -0
- package/dist/api/transaction-manager.js.map +1 -0
- package/dist/cli/deploy-artifact.d.ts +7 -0
- package/dist/cli/deploy-artifact.d.ts.map +1 -0
- package/dist/cli/deploy-artifact.js +279 -0
- package/dist/cli/deploy-artifact.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2057 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/scope-manager.d.ts +75 -0
- package/dist/managers/scope-manager.d.ts.map +1 -0
- package/dist/managers/scope-manager.js +448 -0
- package/dist/managers/scope-manager.js.map +1 -0
- package/dist/mcp/mcp-wrapper.d.ts +8 -0
- package/dist/mcp/mcp-wrapper.d.ts.map +1 -0
- package/dist/mcp/mcp-wrapper.js +77 -0
- package/dist/mcp/mcp-wrapper.js.map +1 -0
- package/dist/mcp/servicenow-automation-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-automation-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-automation-mcp.js +683 -0
- package/dist/mcp/servicenow-automation-mcp.js.map +1 -0
- package/dist/mcp/servicenow-deployment-mcp.d.ts +7 -0
- package/dist/mcp/servicenow-deployment-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-deployment-mcp.js +972 -0
- package/dist/mcp/servicenow-deployment-mcp.js.map +1 -0
- package/dist/mcp/servicenow-flow-composer-mcp.d.ts +7 -0
- package/dist/mcp/servicenow-flow-composer-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-flow-composer-mcp.js +1121 -0
- package/dist/mcp/servicenow-flow-composer-mcp.js.map +1 -0
- package/dist/mcp/servicenow-graph-memory-mcp.d.ts +7 -0
- package/dist/mcp/servicenow-graph-memory-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-graph-memory-mcp.js +703 -0
- package/dist/mcp/servicenow-graph-memory-mcp.js.map +1 -0
- package/dist/mcp/servicenow-integration-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-integration-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-integration-mcp.js +689 -0
- package/dist/mcp/servicenow-integration-mcp.js.map +1 -0
- package/dist/mcp/servicenow-intelligent-mcp.d.ts +7 -0
- package/dist/mcp/servicenow-intelligent-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-intelligent-mcp.js +1138 -0
- package/dist/mcp/servicenow-intelligent-mcp.js.map +1 -0
- package/dist/mcp/servicenow-mcp-server.d.ts +37 -0
- package/dist/mcp/servicenow-mcp-server.d.ts.map +1 -0
- package/dist/mcp/servicenow-mcp-server.js +648 -0
- package/dist/mcp/servicenow-mcp-server.js.map +1 -0
- package/dist/mcp/servicenow-operations-mcp.d.ts +15 -0
- package/dist/mcp/servicenow-operations-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-operations-mcp.js +1287 -0
- package/dist/mcp/servicenow-operations-mcp.js.map +1 -0
- package/dist/mcp/servicenow-platform-development-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-platform-development-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-platform-development-mcp.js +557 -0
- package/dist/mcp/servicenow-platform-development-mcp.js.map +1 -0
- package/dist/mcp/servicenow-progressive-indexer.d.ts +79 -0
- package/dist/mcp/servicenow-progressive-indexer.d.ts.map +1 -0
- package/dist/mcp/servicenow-progressive-indexer.js +320 -0
- package/dist/mcp/servicenow-progressive-indexer.js.map +1 -0
- package/dist/mcp/servicenow-reporting-analytics-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-reporting-analytics-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-reporting-analytics-mcp.js +888 -0
- package/dist/mcp/servicenow-reporting-analytics-mcp.js.map +1 -0
- package/dist/mcp/servicenow-security-compliance-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-security-compliance-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-security-compliance-mcp.js +812 -0
- package/dist/mcp/servicenow-security-compliance-mcp.js.map +1 -0
- package/dist/mcp/servicenow-update-set-mcp.d.ts +7 -0
- package/dist/mcp/servicenow-update-set-mcp.d.ts.map +1 -0
- package/dist/mcp/servicenow-update-set-mcp.js +599 -0
- package/dist/mcp/servicenow-update-set-mcp.js.map +1 -0
- package/dist/mcp/start-all-mcp-servers.d.ts +6 -0
- package/dist/mcp/start-all-mcp-servers.d.ts.map +1 -0
- package/dist/mcp/start-all-mcp-servers.js +65 -0
- package/dist/mcp/start-all-mcp-servers.js.map +1 -0
- package/dist/mcp/start-servicenow-mcp.d.ts +7 -0
- package/dist/mcp/start-servicenow-mcp.d.ts.map +1 -0
- package/dist/mcp/start-servicenow-mcp.js +64 -0
- package/dist/mcp/start-servicenow-mcp.js.map +1 -0
- package/dist/mcp/test-simple-mcp.d.ts +6 -0
- package/dist/mcp/test-simple-mcp.d.ts.map +1 -0
- package/dist/mcp/test-simple-mcp.js +64 -0
- package/dist/mcp/test-simple-mcp.js.map +1 -0
- package/dist/orchestrator/flow-composer.d.ts +269 -0
- package/dist/orchestrator/flow-composer.d.ts.map +1 -0
- package/dist/orchestrator/flow-composer.js +2221 -0
- package/dist/orchestrator/flow-composer.js.map +1 -0
- package/dist/orchestrator/flow-pattern-templates.d.ts +260 -0
- package/dist/orchestrator/flow-pattern-templates.d.ts.map +1 -0
- package/dist/orchestrator/flow-pattern-templates.js +906 -0
- package/dist/orchestrator/flow-pattern-templates.js.map +1 -0
- package/dist/orchestrator/flow-subflow-decision-engine.d.ts +202 -0
- package/dist/orchestrator/flow-subflow-decision-engine.d.ts.map +1 -0
- package/dist/orchestrator/flow-subflow-decision-engine.js +769 -0
- package/dist/orchestrator/flow-subflow-decision-engine.js.map +1 -0
- package/dist/orchestrator/flow-validation-engine.d.ts +145 -0
- package/dist/orchestrator/flow-validation-engine.d.ts.map +1 -0
- package/dist/orchestrator/flow-validation-engine.js +663 -0
- package/dist/orchestrator/flow-validation-engine.js.map +1 -0
- package/dist/orchestrator/snow-orchestrator.d.ts +49 -0
- package/dist/orchestrator/snow-orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/snow-orchestrator.js +235 -0
- package/dist/orchestrator/snow-orchestrator.js.map +1 -0
- package/dist/orchestrator/subflow-creation-handler.d.ts +230 -0
- package/dist/orchestrator/subflow-creation-handler.d.ts.map +1 -0
- package/dist/orchestrator/subflow-creation-handler.js +791 -0
- package/dist/orchestrator/subflow-creation-handler.js.map +1 -0
- package/dist/strategies/global-scope-strategy.d.ts +123 -0
- package/dist/strategies/global-scope-strategy.d.ts.map +1 -0
- package/dist/strategies/global-scope-strategy.js +585 -0
- package/dist/strategies/global-scope-strategy.js.map +1 -0
- package/dist/types/index.d.ts +138 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/servicenow.types.d.ts +260 -0
- package/dist/types/servicenow.types.d.ts.map +1 -0
- package/dist/types/servicenow.types.js +3 -0
- package/dist/types/servicenow.types.js.map +1 -0
- package/dist/types/snow-flow.types.d.ts +114 -0
- package/dist/types/snow-flow.types.d.ts.map +1 -0
- package/dist/types/snow-flow.types.js +3 -0
- package/dist/types/snow-flow.types.js.map +1 -0
- package/dist/utils/action-type-cache.d.ts +66 -0
- package/dist/utils/action-type-cache.d.ts.map +1 -0
- package/dist/utils/action-type-cache.js +237 -0
- package/dist/utils/action-type-cache.js.map +1 -0
- package/dist/utils/agent-detector.d.ts +35 -0
- package/dist/utils/agent-detector.d.ts.map +1 -0
- package/dist/utils/agent-detector.js +353 -0
- package/dist/utils/agent-detector.js.map +1 -0
- package/dist/utils/flow-api-discovery.d.ts +151 -0
- package/dist/utils/flow-api-discovery.d.ts.map +1 -0
- package/dist/utils/flow-api-discovery.js +440 -0
- package/dist/utils/flow-api-discovery.js.map +1 -0
- package/dist/utils/flow-xml-generator.d.ts +41 -0
- package/dist/utils/flow-xml-generator.d.ts.map +1 -0
- package/dist/utils/flow-xml-generator.js +258 -0
- package/dist/utils/flow-xml-generator.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +51 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/mcp-auth-middleware.d.ts +58 -0
- package/dist/utils/mcp-auth-middleware.d.ts.map +1 -0
- package/dist/utils/mcp-auth-middleware.js +227 -0
- package/dist/utils/mcp-auth-middleware.js.map +1 -0
- package/dist/utils/mcp-config-manager.d.ts +126 -0
- package/dist/utils/mcp-config-manager.d.ts.map +1 -0
- package/dist/utils/mcp-config-manager.js +209 -0
- package/dist/utils/mcp-config-manager.js.map +1 -0
- package/dist/utils/mcp-server-manager.d.ts +91 -0
- package/dist/utils/mcp-server-manager.d.ts.map +1 -0
- package/dist/utils/mcp-server-manager.js +338 -0
- package/dist/utils/mcp-server-manager.js.map +1 -0
- package/dist/utils/scope-utils.d.ts +72 -0
- package/dist/utils/scope-utils.d.ts.map +1 -0
- package/dist/utils/scope-utils.js +386 -0
- package/dist/utils/scope-utils.js.map +1 -0
- package/dist/utils/servicenow-client.d.ts +233 -0
- package/dist/utils/servicenow-client.d.ts.map +1 -0
- package/dist/utils/servicenow-client.js +1465 -0
- package/dist/utils/servicenow-client.js.map +1 -0
- package/dist/utils/snow-oauth-old.d.ts +65 -0
- package/dist/utils/snow-oauth-old.d.ts.map +1 -0
- package/dist/utils/snow-oauth-old.js +345 -0
- package/dist/utils/snow-oauth-old.js.map +1 -0
- package/dist/utils/snow-oauth.d.ts +85 -0
- package/dist/utils/snow-oauth.d.ts.map +1 -0
- package/dist/utils/snow-oauth.js +512 -0
- package/dist/utils/snow-oauth.js.map +1 -0
- package/dist/utils/template-engine.d.ts +76 -0
- package/dist/utils/template-engine.d.ts.map +1 -0
- package/dist/utils/template-engine.js +391 -0
- package/dist/utils/template-engine.js.map +1 -0
- package/dist/version.d.ts +35 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +72 -0
- package/dist/version.js.map +1 -0
- package/mcp-wrapper.sh +12 -0
- package/package.json +77 -0
- package/reports/swarm-auto-centralized-1752649029776.json +13 -0
- package/scripts/postinstall.js +30 -0
- package/scripts/setup-mcp.js +89 -0
|
@@ -0,0 +1,2221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Enhanced ServiceNow Flow Composer with Intelligent Artifact Linking
|
|
4
|
+
* Fixes weaknesses in artifact orchestration and linking
|
|
5
|
+
* Now includes intelligent Flow vs Subflow decision making
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.EnhancedFlowComposer = void 0;
|
|
9
|
+
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
|
|
10
|
+
const servicenow_artifact_indexer_js_1 = require("../memory/servicenow-artifact-indexer.js");
|
|
11
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
12
|
+
const flow_subflow_decision_engine_js_1 = require("./flow-subflow-decision-engine.js");
|
|
13
|
+
const subflow_creation_handler_js_1 = require("./subflow-creation-handler.js");
|
|
14
|
+
const flow_pattern_templates_js_1 = require("./flow-pattern-templates.js");
|
|
15
|
+
const flow_validation_engine_js_1 = require("./flow-validation-engine.js");
|
|
16
|
+
const scope_manager_js_1 = require("../managers/scope-manager.js");
|
|
17
|
+
const global_scope_strategy_js_1 = require("../strategies/global-scope-strategy.js");
|
|
18
|
+
class EnhancedFlowComposer {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.artifactRegistry = new Map();
|
|
21
|
+
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
22
|
+
this.indexer = new servicenow_artifact_indexer_js_1.ServiceNowArtifactIndexer();
|
|
23
|
+
this.logger = new logger_js_1.Logger('EnhancedFlowComposer');
|
|
24
|
+
// Initialize intelligent components
|
|
25
|
+
this.decisionEngine = new flow_subflow_decision_engine_js_1.FlowSubflowDecisionEngine();
|
|
26
|
+
this.subflowHandler = new subflow_creation_handler_js_1.SubflowCreationHandler();
|
|
27
|
+
this.patternTemplates = new flow_pattern_templates_js_1.FlowPatternTemplates();
|
|
28
|
+
this.validationEngine = new flow_validation_engine_js_1.FlowValidationEngine();
|
|
29
|
+
// Initialize global scope management
|
|
30
|
+
this.scopeManager = new scope_manager_js_1.ScopeManager({
|
|
31
|
+
defaultScope: global_scope_strategy_js_1.ScopeType.GLOBAL,
|
|
32
|
+
allowFallback: true,
|
|
33
|
+
validatePermissions: true,
|
|
34
|
+
enableMigration: false
|
|
35
|
+
});
|
|
36
|
+
this.globalScopeStrategy = new global_scope_strategy_js_1.GlobalScopeStrategy();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Analyze artifact API to understand inputs/outputs
|
|
40
|
+
*/
|
|
41
|
+
async analyzeArtifactAPI(artifact) {
|
|
42
|
+
this.logger.info('Analyzing artifact API', { name: artifact.name, type: artifact.sys_class_name });
|
|
43
|
+
const artifactInterface = {
|
|
44
|
+
sys_id: artifact.sys_id,
|
|
45
|
+
name: artifact.name,
|
|
46
|
+
type: artifact.sys_class_name,
|
|
47
|
+
inputs: [],
|
|
48
|
+
outputs: [],
|
|
49
|
+
dependencies: [],
|
|
50
|
+
compatibility_score: 0
|
|
51
|
+
};
|
|
52
|
+
// Analyze based on artifact type
|
|
53
|
+
switch (artifact.sys_class_name) {
|
|
54
|
+
case 'sys_script_include':
|
|
55
|
+
artifactInterface.inputs = await this.analyzeScriptIncludeAPI(artifact);
|
|
56
|
+
artifactInterface.outputs = await this.analyzeScriptIncludeOutputs(artifact);
|
|
57
|
+
break;
|
|
58
|
+
case 'sys_script':
|
|
59
|
+
artifactInterface.inputs = await this.analyzeBusinessRuleAPI(artifact);
|
|
60
|
+
artifactInterface.outputs = await this.analyzeBusinessRuleOutputs(artifact);
|
|
61
|
+
break;
|
|
62
|
+
case 'sys_db_object':
|
|
63
|
+
artifactInterface.inputs = await this.analyzeTableAPI(artifact);
|
|
64
|
+
artifactInterface.outputs = await this.analyzeTableOutputs(artifact);
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
// Store in registry
|
|
68
|
+
this.artifactRegistry.set(artifact.sys_id, artifactInterface);
|
|
69
|
+
return artifactInterface;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Analyze Script Include API by parsing the actual script
|
|
73
|
+
*/
|
|
74
|
+
async analyzeScriptIncludeAPI(artifact) {
|
|
75
|
+
const script = artifact.script || '';
|
|
76
|
+
const inputs = [];
|
|
77
|
+
// Parse function parameters from script
|
|
78
|
+
const functionMatches = script.match(/function\s+(\w+)\s*\(([^)]*)\)/g);
|
|
79
|
+
if (functionMatches) {
|
|
80
|
+
for (const match of functionMatches) {
|
|
81
|
+
const params = match.match(/\(([^)]*)\)/)?.[1];
|
|
82
|
+
if (params) {
|
|
83
|
+
const paramList = params.split(',').map((p) => p.trim());
|
|
84
|
+
for (const param of paramList) {
|
|
85
|
+
if (param) {
|
|
86
|
+
inputs.push({
|
|
87
|
+
name: param,
|
|
88
|
+
type: this.inferParameterType(param, script),
|
|
89
|
+
required: true,
|
|
90
|
+
description: this.extractParameterDescription(param, script)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Look for API patterns in script
|
|
98
|
+
if (script.includes('translateText')) {
|
|
99
|
+
inputs.push({
|
|
100
|
+
name: 'text',
|
|
101
|
+
type: 'string',
|
|
102
|
+
required: true,
|
|
103
|
+
description: 'Text to translate'
|
|
104
|
+
});
|
|
105
|
+
inputs.push({
|
|
106
|
+
name: 'target_language',
|
|
107
|
+
type: 'string',
|
|
108
|
+
required: true,
|
|
109
|
+
description: 'Target language for translation'
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return inputs;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Analyze Script Include outputs by parsing return statements
|
|
116
|
+
*/
|
|
117
|
+
async analyzeScriptIncludeOutputs(artifact) {
|
|
118
|
+
const script = artifact.script || '';
|
|
119
|
+
const outputs = [];
|
|
120
|
+
// Look for return patterns
|
|
121
|
+
if (script.includes('return {')) {
|
|
122
|
+
// Parse object return
|
|
123
|
+
const returnMatches = script.match(/return\s*\{([^}]*)\}/g);
|
|
124
|
+
if (returnMatches) {
|
|
125
|
+
for (const match of returnMatches) {
|
|
126
|
+
const objectContent = match.match(/\{([^}]*)\}/)?.[1];
|
|
127
|
+
if (objectContent) {
|
|
128
|
+
const properties = objectContent.split(',').map((p) => p.trim());
|
|
129
|
+
for (const prop of properties) {
|
|
130
|
+
const [key] = prop.split(':').map((p) => p.trim());
|
|
131
|
+
if (key) {
|
|
132
|
+
outputs.push({
|
|
133
|
+
name: key,
|
|
134
|
+
type: 'string',
|
|
135
|
+
description: `Output property: ${key}`
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Common translation API output
|
|
144
|
+
if (script.includes('translateText') || script.includes('translation')) {
|
|
145
|
+
outputs.push({
|
|
146
|
+
name: 'translated_text',
|
|
147
|
+
type: 'string',
|
|
148
|
+
description: 'Translated text result'
|
|
149
|
+
});
|
|
150
|
+
outputs.push({
|
|
151
|
+
name: 'confidence',
|
|
152
|
+
type: 'number',
|
|
153
|
+
description: 'Translation confidence score'
|
|
154
|
+
});
|
|
155
|
+
outputs.push({
|
|
156
|
+
name: 'success',
|
|
157
|
+
type: 'boolean',
|
|
158
|
+
description: 'Translation success flag'
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return outputs;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Analyze Business Rule API
|
|
165
|
+
*/
|
|
166
|
+
async analyzeBusinessRuleAPI(artifact) {
|
|
167
|
+
const script = artifact.script || '';
|
|
168
|
+
const inputs = [];
|
|
169
|
+
// Business rules typically work with current record
|
|
170
|
+
inputs.push({
|
|
171
|
+
name: 'current',
|
|
172
|
+
type: 'object',
|
|
173
|
+
required: true,
|
|
174
|
+
description: 'Current record being processed'
|
|
175
|
+
});
|
|
176
|
+
// Look for specific field access patterns
|
|
177
|
+
const fieldMatches = script.match(/current\.getValue\(['"]([^'"]+)['"]\)/g);
|
|
178
|
+
if (fieldMatches) {
|
|
179
|
+
for (const match of fieldMatches) {
|
|
180
|
+
const field = match.match(/['"]([^'"]+)['"]/)?.[1];
|
|
181
|
+
if (field) {
|
|
182
|
+
inputs.push({
|
|
183
|
+
name: field,
|
|
184
|
+
type: 'string',
|
|
185
|
+
required: false,
|
|
186
|
+
description: `Field: ${field}`
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return inputs;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Analyze Business Rule outputs
|
|
195
|
+
*/
|
|
196
|
+
async analyzeBusinessRuleOutputs(artifact) {
|
|
197
|
+
const script = artifact.script || '';
|
|
198
|
+
const outputs = [];
|
|
199
|
+
// Business rules typically create records or modify current record
|
|
200
|
+
if (script.includes('GlideRecord') && script.includes('insert')) {
|
|
201
|
+
outputs.push({
|
|
202
|
+
name: 'created_record_id',
|
|
203
|
+
type: 'string',
|
|
204
|
+
description: 'ID of created record'
|
|
205
|
+
});
|
|
206
|
+
outputs.push({
|
|
207
|
+
name: 'success',
|
|
208
|
+
type: 'boolean',
|
|
209
|
+
description: 'Record creation success'
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return outputs;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Analyze Table API
|
|
216
|
+
*/
|
|
217
|
+
async analyzeTableAPI(artifact) {
|
|
218
|
+
const inputs = [];
|
|
219
|
+
// Tables accept field values as inputs
|
|
220
|
+
// This would need to query the actual table schema
|
|
221
|
+
inputs.push({
|
|
222
|
+
name: 'record_data',
|
|
223
|
+
type: 'object',
|
|
224
|
+
required: true,
|
|
225
|
+
description: 'Record data to insert/update'
|
|
226
|
+
});
|
|
227
|
+
return inputs;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Analyze Table outputs
|
|
231
|
+
*/
|
|
232
|
+
async analyzeTableOutputs(artifact) {
|
|
233
|
+
const outputs = [];
|
|
234
|
+
// Tables return record information
|
|
235
|
+
outputs.push({
|
|
236
|
+
name: 'sys_id',
|
|
237
|
+
type: 'string',
|
|
238
|
+
description: 'System ID of the record'
|
|
239
|
+
});
|
|
240
|
+
outputs.push({
|
|
241
|
+
name: 'success',
|
|
242
|
+
type: 'boolean',
|
|
243
|
+
description: 'Operation success flag'
|
|
244
|
+
});
|
|
245
|
+
return outputs;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Find compatible artifacts for orchestration
|
|
249
|
+
*/
|
|
250
|
+
async findCompatibleArtifacts(requiredCapability, availableArtifacts) {
|
|
251
|
+
const compatible = [];
|
|
252
|
+
for (const artifact of availableArtifacts) {
|
|
253
|
+
const compatibility = await this.calculateCompatibility(artifact, requiredCapability);
|
|
254
|
+
if (compatibility > 0.7) {
|
|
255
|
+
artifact.compatibility_score = compatibility;
|
|
256
|
+
compatible.push(artifact);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Sort by compatibility score
|
|
260
|
+
return compatible.sort((a, b) => (b.compatibility_score || 0) - (a.compatibility_score || 0));
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Calculate compatibility between artifact and required capability
|
|
264
|
+
*/
|
|
265
|
+
async calculateCompatibility(artifact, capability) {
|
|
266
|
+
let score = 0;
|
|
267
|
+
// Name matching
|
|
268
|
+
if (artifact.name.toLowerCase().includes(capability.toLowerCase())) {
|
|
269
|
+
score += 0.3;
|
|
270
|
+
}
|
|
271
|
+
// Input/output matching
|
|
272
|
+
if (capability.includes('translation')) {
|
|
273
|
+
const hasTextInput = artifact.inputs.some(i => i.name.includes('text') || i.name.includes('message'));
|
|
274
|
+
const hasLanguageInput = artifact.inputs.some(i => i.name.includes('language'));
|
|
275
|
+
const hasTranslationOutput = artifact.outputs.some(o => o.name.includes('translated') || o.name.includes('translation'));
|
|
276
|
+
if (hasTextInput)
|
|
277
|
+
score += 0.2;
|
|
278
|
+
if (hasLanguageInput)
|
|
279
|
+
score += 0.2;
|
|
280
|
+
if (hasTranslationOutput)
|
|
281
|
+
score += 0.3;
|
|
282
|
+
}
|
|
283
|
+
if (capability.includes('storage') || capability.includes('save')) {
|
|
284
|
+
const hasRecordInput = artifact.inputs.some(i => i.name.includes('record') || i.name.includes('data'));
|
|
285
|
+
const hasSuccessOutput = artifact.outputs.some(o => o.name.includes('success') || o.name.includes('id'));
|
|
286
|
+
if (hasRecordInput)
|
|
287
|
+
score += 0.3;
|
|
288
|
+
if (hasSuccessOutput)
|
|
289
|
+
score += 0.2;
|
|
290
|
+
}
|
|
291
|
+
return Math.min(score, 1.0);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Create intelligent connections between artifacts
|
|
295
|
+
*/
|
|
296
|
+
async createArtifactConnections(activities) {
|
|
297
|
+
const connections = [];
|
|
298
|
+
for (let i = 0; i < activities.length - 1; i++) {
|
|
299
|
+
const currentActivity = activities[i];
|
|
300
|
+
const nextActivity = activities[i + 1];
|
|
301
|
+
// Find matching outputs to inputs
|
|
302
|
+
for (const output of currentActivity.artifact.outputs) {
|
|
303
|
+
for (const input of nextActivity.artifact.inputs) {
|
|
304
|
+
if (this.areCompatible(output, input)) {
|
|
305
|
+
connections.push({
|
|
306
|
+
from_activity: currentActivity.id,
|
|
307
|
+
from_output: output.name,
|
|
308
|
+
to_activity: nextActivity.id,
|
|
309
|
+
to_input: input.name,
|
|
310
|
+
transformation: this.getTransformation(output, input)
|
|
311
|
+
});
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return connections;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Check if output and input are compatible
|
|
321
|
+
*/
|
|
322
|
+
areCompatible(output, input) {
|
|
323
|
+
// Type compatibility
|
|
324
|
+
if (output.type !== input.type && input.type !== 'object') {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
// Name similarity
|
|
328
|
+
const similarity = this.calculateNameSimilarity(output.name, input.name);
|
|
329
|
+
return similarity > 0.6;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Calculate name similarity between output and input
|
|
333
|
+
*/
|
|
334
|
+
calculateNameSimilarity(output, input) {
|
|
335
|
+
const outputWords = output.toLowerCase().split('_');
|
|
336
|
+
const inputWords = input.toLowerCase().split('_');
|
|
337
|
+
let matches = 0;
|
|
338
|
+
for (const outputWord of outputWords) {
|
|
339
|
+
for (const inputWord of inputWords) {
|
|
340
|
+
if (outputWord === inputWord || outputWord.includes(inputWord) || inputWord.includes(outputWord)) {
|
|
341
|
+
matches++;
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return matches / Math.max(outputWords.length, inputWords.length);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Get transformation logic for output to input mapping
|
|
350
|
+
*/
|
|
351
|
+
getTransformation(output, input) {
|
|
352
|
+
// Simple transformations
|
|
353
|
+
if (output.type === 'string' && input.type === 'object') {
|
|
354
|
+
return 'JSON.parse';
|
|
355
|
+
}
|
|
356
|
+
if (output.type === 'object' && input.type === 'string') {
|
|
357
|
+
return 'JSON.stringify';
|
|
358
|
+
}
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Infer parameter type from script context
|
|
363
|
+
*/
|
|
364
|
+
inferParameterType(param, script) {
|
|
365
|
+
if (script.includes(`${param}.length`) || script.includes(`${param}.split`)) {
|
|
366
|
+
return 'string';
|
|
367
|
+
}
|
|
368
|
+
if (script.includes(`${param} > `) || script.includes(`${param} < `)) {
|
|
369
|
+
return 'number';
|
|
370
|
+
}
|
|
371
|
+
if (script.includes(`${param} === true`) || script.includes(`${param} === false`)) {
|
|
372
|
+
return 'boolean';
|
|
373
|
+
}
|
|
374
|
+
if (script.includes(`${param}[`) || script.includes(`${param}.push`)) {
|
|
375
|
+
return 'array';
|
|
376
|
+
}
|
|
377
|
+
if (script.includes(`${param}.`) && !script.includes(`${param}.length`)) {
|
|
378
|
+
return 'object';
|
|
379
|
+
}
|
|
380
|
+
return 'string';
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Extract parameter description from script comments
|
|
384
|
+
*/
|
|
385
|
+
extractParameterDescription(param, script) {
|
|
386
|
+
// Look for JSDoc comments
|
|
387
|
+
const jsdocMatch = script.match(new RegExp(`@param\\s+\\{[^}]*\\}\\s+${param}\\s+([^\\n]*)`));
|
|
388
|
+
if (jsdocMatch) {
|
|
389
|
+
return jsdocMatch[1].trim();
|
|
390
|
+
}
|
|
391
|
+
// Look for inline comments
|
|
392
|
+
const inlineMatch = script.match(new RegExp(`${param}[^\\n]*//\\s*([^\\n]*)`));
|
|
393
|
+
if (inlineMatch) {
|
|
394
|
+
return inlineMatch[1].trim();
|
|
395
|
+
}
|
|
396
|
+
return `Parameter: ${param}`;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Generate enhanced flow with intelligent artifact linking
|
|
400
|
+
*/
|
|
401
|
+
async generateEnhancedFlow(instruction) {
|
|
402
|
+
this.logger.info('Generating enhanced flow with intelligent artifact linking', { instruction });
|
|
403
|
+
// 1. Parse instruction and discover artifacts
|
|
404
|
+
const discoveredArtifacts = await this.discoverAndAnalyzeArtifacts(instruction);
|
|
405
|
+
// 2. Create enhanced activities with proper linking
|
|
406
|
+
const enhancedActivities = await this.createEnhancedActivities(discoveredArtifacts);
|
|
407
|
+
// 3. Generate intelligent connections
|
|
408
|
+
const connections = await this.createArtifactConnections(enhancedActivities);
|
|
409
|
+
// 4. Build complete flow structure
|
|
410
|
+
const flowStructure = {
|
|
411
|
+
name: `enhanced_flow_${Date.now()}`,
|
|
412
|
+
description: `Enhanced flow for: ${instruction}`,
|
|
413
|
+
activities: enhancedActivities,
|
|
414
|
+
connections: connections,
|
|
415
|
+
error_handling: this.generateErrorHandling(enhancedActivities)
|
|
416
|
+
};
|
|
417
|
+
return flowStructure;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Discover and analyze artifacts for instruction
|
|
421
|
+
*/
|
|
422
|
+
async discoverAndAnalyzeArtifacts(instruction) {
|
|
423
|
+
this.logger.info(`Discovering artifacts for instruction: ${instruction}`);
|
|
424
|
+
try {
|
|
425
|
+
// First, try to find artifacts from memory/cache
|
|
426
|
+
const cachedArtifacts = await this.searchCachedArtifacts(instruction);
|
|
427
|
+
if (cachedArtifacts.length > 0) {
|
|
428
|
+
this.logger.info(`Found ${cachedArtifacts.length} cached artifacts`);
|
|
429
|
+
return cachedArtifacts;
|
|
430
|
+
}
|
|
431
|
+
// If no cached artifacts, search ServiceNow directly
|
|
432
|
+
const discoveredArtifacts = await this.searchServiceNowArtifacts(instruction);
|
|
433
|
+
// Cache the results for future use
|
|
434
|
+
if (discoveredArtifacts.length > 0) {
|
|
435
|
+
await this.cacheArtifacts(discoveredArtifacts, instruction);
|
|
436
|
+
}
|
|
437
|
+
return discoveredArtifacts;
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
this.logger.error('Error discovering artifacts:', error);
|
|
441
|
+
// Fallback to basic artifacts if discovery fails
|
|
442
|
+
return await this.createBasicArtifacts(instruction);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
async searchCachedArtifacts(instruction) {
|
|
446
|
+
try {
|
|
447
|
+
const searchResults = await this.indexer.searchMemory(instruction);
|
|
448
|
+
return searchResults.map(result => this.convertToArtifactInterface(result));
|
|
449
|
+
}
|
|
450
|
+
catch (error) {
|
|
451
|
+
this.logger.warn('Failed to search cached artifacts:', error);
|
|
452
|
+
return [];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async searchServiceNowArtifacts(instruction) {
|
|
456
|
+
const artifacts = [];
|
|
457
|
+
const searchTerms = this.extractSearchTerms(instruction);
|
|
458
|
+
this.logger.info(`Searching ServiceNow with terms: ${searchTerms.join(', ')}`);
|
|
459
|
+
// Search different artifact types based on instruction content
|
|
460
|
+
const searchTables = this.determineSearchTables(instruction);
|
|
461
|
+
for (const table of searchTables) {
|
|
462
|
+
for (const term of searchTerms) {
|
|
463
|
+
try {
|
|
464
|
+
const results = await this.client.searchRecords(table, `nameLIKE${term}^ORshort_descriptionLIKE${term}`, 5);
|
|
465
|
+
if (results.success) {
|
|
466
|
+
for (const result of results.data.result) {
|
|
467
|
+
const analyzed = await this.analyzeArtifactAPI(result);
|
|
468
|
+
artifacts.push(analyzed);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
this.logger.warn(`Failed to search ${table} for ${term}:`, error);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return artifacts;
|
|
478
|
+
}
|
|
479
|
+
determineSearchTables(instruction) {
|
|
480
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
481
|
+
const tables = [];
|
|
482
|
+
// Determine relevant tables based on instruction content
|
|
483
|
+
if (lowerInstruction.includes('script') || lowerInstruction.includes('function')) {
|
|
484
|
+
tables.push('sys_script_include');
|
|
485
|
+
}
|
|
486
|
+
if (lowerInstruction.includes('rule') || lowerInstruction.includes('business')) {
|
|
487
|
+
tables.push('sys_script');
|
|
488
|
+
}
|
|
489
|
+
if (lowerInstruction.includes('table') || lowerInstruction.includes('record')) {
|
|
490
|
+
tables.push('sys_db_object');
|
|
491
|
+
}
|
|
492
|
+
if (lowerInstruction.includes('approval') || lowerInstruction.includes('workflow')) {
|
|
493
|
+
tables.push('sys_hub_flow');
|
|
494
|
+
tables.push('wf_workflow');
|
|
495
|
+
}
|
|
496
|
+
if (lowerInstruction.includes('catalog') || lowerInstruction.includes('item')) {
|
|
497
|
+
tables.push('sc_cat_item');
|
|
498
|
+
}
|
|
499
|
+
if (lowerInstruction.includes('notification') || lowerInstruction.includes('email')) {
|
|
500
|
+
tables.push('sysevent_email_action');
|
|
501
|
+
}
|
|
502
|
+
// Default search tables if no specific indicators found
|
|
503
|
+
if (tables.length === 0) {
|
|
504
|
+
tables.push('sys_script_include', 'sys_script', 'sys_hub_flow');
|
|
505
|
+
}
|
|
506
|
+
return tables;
|
|
507
|
+
}
|
|
508
|
+
extractSearchTerms(instruction) {
|
|
509
|
+
const terms = [];
|
|
510
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
511
|
+
// Extract key terms from instruction
|
|
512
|
+
if (lowerInstruction.includes('iphone'))
|
|
513
|
+
terms.push('iphone');
|
|
514
|
+
if (lowerInstruction.includes('approval'))
|
|
515
|
+
terms.push('approval');
|
|
516
|
+
if (lowerInstruction.includes('catalog'))
|
|
517
|
+
terms.push('catalog');
|
|
518
|
+
if (lowerInstruction.includes('order'))
|
|
519
|
+
terms.push('order');
|
|
520
|
+
if (lowerInstruction.includes('fulfillment'))
|
|
521
|
+
terms.push('fulfillment');
|
|
522
|
+
if (lowerInstruction.includes('task'))
|
|
523
|
+
terms.push('task');
|
|
524
|
+
if (lowerInstruction.includes('pickup'))
|
|
525
|
+
terms.push('pickup');
|
|
526
|
+
if (lowerInstruction.includes('admin'))
|
|
527
|
+
terms.push('admin');
|
|
528
|
+
if (lowerInstruction.includes('user'))
|
|
529
|
+
terms.push('user');
|
|
530
|
+
if (lowerInstruction.includes('notification'))
|
|
531
|
+
terms.push('notification');
|
|
532
|
+
if (lowerInstruction.includes('email'))
|
|
533
|
+
terms.push('email');
|
|
534
|
+
// Extract quoted terms
|
|
535
|
+
const quotedTerms = instruction.match(/"([^"]+)"/g);
|
|
536
|
+
if (quotedTerms) {
|
|
537
|
+
quotedTerms.forEach(term => terms.push(term.replace(/"/g, '')));
|
|
538
|
+
}
|
|
539
|
+
// Extract capitalized terms (likely proper nouns)
|
|
540
|
+
const capitalizedTerms = instruction.match(/\b[A-Z][a-z]+\b/g);
|
|
541
|
+
if (capitalizedTerms) {
|
|
542
|
+
terms.push(...capitalizedTerms.map(term => term.toLowerCase()));
|
|
543
|
+
}
|
|
544
|
+
return [...new Set(terms)]; // Remove duplicates
|
|
545
|
+
}
|
|
546
|
+
async cacheArtifacts(artifacts, instruction) {
|
|
547
|
+
try {
|
|
548
|
+
// Store artifacts in memory for future use
|
|
549
|
+
for (const artifact of artifacts) {
|
|
550
|
+
// Store in memory if possible (method may be private)
|
|
551
|
+
// await this.indexer.storeInMemory(artifact, instruction);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
this.logger.warn('Failed to cache artifacts:', error);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
convertToArtifactInterface(memoryResult) {
|
|
559
|
+
return {
|
|
560
|
+
sys_id: memoryResult.sys_id || 'unknown',
|
|
561
|
+
name: memoryResult.name || 'Unknown Artifact',
|
|
562
|
+
type: memoryResult.sys_class_name || 'unknown',
|
|
563
|
+
inputs: memoryResult.data_flow?.inputs?.map((input) => ({
|
|
564
|
+
name: typeof input === 'string' ? input : input.name || 'unknown',
|
|
565
|
+
type: typeof input === 'string' ? 'string' : input.type || 'string',
|
|
566
|
+
required: typeof input === 'string' ? true : input.required || false,
|
|
567
|
+
description: typeof input === 'string' ? input : input.description || ''
|
|
568
|
+
})) || [],
|
|
569
|
+
outputs: memoryResult.data_flow?.outputs?.map((output) => ({
|
|
570
|
+
name: typeof output === 'string' ? output : output.name || 'unknown',
|
|
571
|
+
type: typeof output === 'string' ? 'string' : output.type || 'string',
|
|
572
|
+
description: typeof output === 'string' ? output : output.description || ''
|
|
573
|
+
})) || [],
|
|
574
|
+
dependencies: memoryResult.dependencies || [],
|
|
575
|
+
api_signature: memoryResult.api_methods?.join(', ') || undefined,
|
|
576
|
+
compatibility_score: undefined
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
async createBasicArtifacts(instruction) {
|
|
580
|
+
// Create basic artifacts based on instruction if ServiceNow search fails
|
|
581
|
+
const artifacts = [];
|
|
582
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
583
|
+
// For fulfillment workflows, create relevant artifacts
|
|
584
|
+
if (lowerInstruction.includes('fulfillment') || lowerInstruction.includes('order') ||
|
|
585
|
+
lowerInstruction.includes('catalog') || lowerInstruction.includes('iphone')) {
|
|
586
|
+
artifacts.push({
|
|
587
|
+
sys_id: 'catalog_handler',
|
|
588
|
+
name: 'CatalogFulfillmentHandler',
|
|
589
|
+
type: 'sys_script_include',
|
|
590
|
+
inputs: [
|
|
591
|
+
{
|
|
592
|
+
name: 'catalog_item',
|
|
593
|
+
type: 'object',
|
|
594
|
+
required: true,
|
|
595
|
+
description: 'The catalog item being fulfilled'
|
|
596
|
+
}
|
|
597
|
+
],
|
|
598
|
+
outputs: [
|
|
599
|
+
{
|
|
600
|
+
name: 'fulfillment_status',
|
|
601
|
+
type: 'object',
|
|
602
|
+
description: 'Status of the fulfillment process'
|
|
603
|
+
}
|
|
604
|
+
],
|
|
605
|
+
dependencies: [],
|
|
606
|
+
api_signature: 'processFulfillment(catalog_item)'
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
if (lowerInstruction.includes('approval')) {
|
|
610
|
+
artifacts.push({
|
|
611
|
+
sys_id: 'approval_script',
|
|
612
|
+
name: 'ApprovalHandler',
|
|
613
|
+
type: 'sys_script_include',
|
|
614
|
+
inputs: [
|
|
615
|
+
{
|
|
616
|
+
name: 'record',
|
|
617
|
+
type: 'object',
|
|
618
|
+
required: true,
|
|
619
|
+
description: 'Record to process for approval'
|
|
620
|
+
}
|
|
621
|
+
],
|
|
622
|
+
outputs: [
|
|
623
|
+
{
|
|
624
|
+
name: 'approval_result',
|
|
625
|
+
type: 'object',
|
|
626
|
+
description: 'Result of approval processing'
|
|
627
|
+
}
|
|
628
|
+
],
|
|
629
|
+
dependencies: [],
|
|
630
|
+
api_signature: 'processApproval(record)'
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
if (lowerInstruction.includes('task')) {
|
|
634
|
+
artifacts.push({
|
|
635
|
+
sys_id: 'task_creator',
|
|
636
|
+
name: 'TaskCreator',
|
|
637
|
+
type: 'sys_script_include',
|
|
638
|
+
inputs: [
|
|
639
|
+
{
|
|
640
|
+
name: 'type',
|
|
641
|
+
type: 'string',
|
|
642
|
+
required: true,
|
|
643
|
+
description: 'Type of task to create'
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
name: 'assignee',
|
|
647
|
+
type: 'string',
|
|
648
|
+
required: true,
|
|
649
|
+
description: 'User or group to assign task to'
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
name: 'description',
|
|
653
|
+
type: 'string',
|
|
654
|
+
required: true,
|
|
655
|
+
description: 'Task description'
|
|
656
|
+
}
|
|
657
|
+
],
|
|
658
|
+
outputs: [
|
|
659
|
+
{
|
|
660
|
+
name: 'task_id',
|
|
661
|
+
type: 'string',
|
|
662
|
+
description: 'ID of created task'
|
|
663
|
+
}
|
|
664
|
+
],
|
|
665
|
+
dependencies: [],
|
|
666
|
+
api_signature: 'createTask(type, assignee, description)'
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
if (lowerInstruction.includes('notification')) {
|
|
670
|
+
artifacts.push({
|
|
671
|
+
sys_id: 'notification_sender',
|
|
672
|
+
name: 'NotificationSender',
|
|
673
|
+
type: 'sys_script_include',
|
|
674
|
+
inputs: [
|
|
675
|
+
{
|
|
676
|
+
name: 'recipient',
|
|
677
|
+
type: 'string',
|
|
678
|
+
required: true,
|
|
679
|
+
description: 'Notification recipient'
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: 'message',
|
|
683
|
+
type: 'string',
|
|
684
|
+
required: true,
|
|
685
|
+
description: 'Notification message'
|
|
686
|
+
}
|
|
687
|
+
],
|
|
688
|
+
outputs: [
|
|
689
|
+
{
|
|
690
|
+
name: 'notification_result',
|
|
691
|
+
type: 'object',
|
|
692
|
+
description: 'Result of notification sending'
|
|
693
|
+
}
|
|
694
|
+
],
|
|
695
|
+
dependencies: [],
|
|
696
|
+
api_signature: 'sendNotification(recipient, message)'
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
return artifacts;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Create enhanced activities with proper artifact integration
|
|
703
|
+
*/
|
|
704
|
+
async createEnhancedActivities(artifacts) {
|
|
705
|
+
const activities = [];
|
|
706
|
+
for (const artifact of artifacts) {
|
|
707
|
+
const activity = {
|
|
708
|
+
id: `activity_${activities.length + 1}`,
|
|
709
|
+
type: this.getActivityType(artifact),
|
|
710
|
+
name: this.generateActivityName(artifact),
|
|
711
|
+
artifact: artifact,
|
|
712
|
+
inputs: this.generateActivityInputs(artifact),
|
|
713
|
+
outputs: this.generateActivityOutputs(artifact),
|
|
714
|
+
connections: [], // Will be populated later
|
|
715
|
+
error_handling: this.generateActivityErrorHandling(artifact)
|
|
716
|
+
};
|
|
717
|
+
activities.push(activity);
|
|
718
|
+
}
|
|
719
|
+
return activities;
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Get activity type based on artifact
|
|
723
|
+
*/
|
|
724
|
+
getActivityType(artifact) {
|
|
725
|
+
switch (artifact.type) {
|
|
726
|
+
case 'sys_script_include':
|
|
727
|
+
return 'custom_script';
|
|
728
|
+
case 'sys_script':
|
|
729
|
+
return 'business_rule';
|
|
730
|
+
case 'sys_db_object':
|
|
731
|
+
return 'create_record';
|
|
732
|
+
default:
|
|
733
|
+
return 'custom_activity';
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Generate activity name based on artifact
|
|
738
|
+
*/
|
|
739
|
+
generateActivityName(artifact) {
|
|
740
|
+
return `Execute ${artifact.name}`;
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Generate activity inputs based on artifact interface
|
|
744
|
+
*/
|
|
745
|
+
generateActivityInputs(artifact) {
|
|
746
|
+
const inputs = {};
|
|
747
|
+
for (const input of artifact.inputs) {
|
|
748
|
+
inputs[input.name] = this.getDefaultInputValue(input);
|
|
749
|
+
}
|
|
750
|
+
return inputs;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Generate activity outputs based on artifact interface
|
|
754
|
+
*/
|
|
755
|
+
generateActivityOutputs(artifact) {
|
|
756
|
+
const outputs = {};
|
|
757
|
+
for (const output of artifact.outputs) {
|
|
758
|
+
outputs[output.name] = `\${step.${output.name}}`;
|
|
759
|
+
}
|
|
760
|
+
return outputs;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Get default input value based on input type
|
|
764
|
+
*/
|
|
765
|
+
getDefaultInputValue(input) {
|
|
766
|
+
switch (input.type) {
|
|
767
|
+
case 'string':
|
|
768
|
+
return input.required ? '${trigger.field}' : '';
|
|
769
|
+
case 'number':
|
|
770
|
+
return input.required ? '${trigger.number}' : 0;
|
|
771
|
+
case 'boolean':
|
|
772
|
+
return input.required ? '${trigger.boolean}' : false;
|
|
773
|
+
case 'object':
|
|
774
|
+
return input.required ? '${trigger.current}' : {};
|
|
775
|
+
case 'array':
|
|
776
|
+
return input.required ? '${trigger.array}' : [];
|
|
777
|
+
default:
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Generate activity-specific error handling
|
|
783
|
+
*/
|
|
784
|
+
generateActivityErrorHandling(artifact) {
|
|
785
|
+
const errorHandling = [];
|
|
786
|
+
// Common error handling for all activities
|
|
787
|
+
errorHandling.push({
|
|
788
|
+
condition: 'step.success === false',
|
|
789
|
+
action: 'retry',
|
|
790
|
+
parameters: {
|
|
791
|
+
max_retries: 3,
|
|
792
|
+
delay: 2000
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
// Specific error handling based on artifact type
|
|
796
|
+
if (artifact.type === 'sys_script_include') {
|
|
797
|
+
errorHandling.push({
|
|
798
|
+
condition: 'step.confidence < 0.7',
|
|
799
|
+
action: 'notify',
|
|
800
|
+
parameters: {
|
|
801
|
+
message: 'Low confidence result, manual review recommended'
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
return errorHandling;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Generate error handling for the entire flow
|
|
809
|
+
*/
|
|
810
|
+
generateErrorHandling(activities) {
|
|
811
|
+
const errorHandling = [];
|
|
812
|
+
// Global error handling
|
|
813
|
+
errorHandling.push({
|
|
814
|
+
condition: 'any_activity_failed',
|
|
815
|
+
action: 'stop',
|
|
816
|
+
parameters: {
|
|
817
|
+
notification: 'admin@company.com',
|
|
818
|
+
message: 'Flow execution failed, please review'
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
return errorHandling;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Create a flow from natural language instruction with intelligent decision making
|
|
825
|
+
* This is the main method used by the MCP - ENHANCED VERSION
|
|
826
|
+
*/
|
|
827
|
+
async createFlowFromInstruction(instruction) {
|
|
828
|
+
this.logger.info('Creating intelligent flow from instruction', { instruction });
|
|
829
|
+
try {
|
|
830
|
+
// Step 1: Analyze the instruction to determine flow vs subflow
|
|
831
|
+
const analysisResult = await this.decisionEngine.analyzeAndRecommend(instruction);
|
|
832
|
+
// Step 2: Validate the decision
|
|
833
|
+
const validationResult = this.validationEngine.validateDecision(analysisResult, instruction);
|
|
834
|
+
// Step 3: Find matching templates
|
|
835
|
+
const matchingTemplates = this.patternTemplates.findMatchingTemplates(instruction);
|
|
836
|
+
// Step 4: Create subflows if recommended
|
|
837
|
+
const subflowResults = [];
|
|
838
|
+
if (analysisResult.subflowCandidates.length > 0) {
|
|
839
|
+
for (const candidate of analysisResult.subflowCandidates) {
|
|
840
|
+
const subflowResult = await this.subflowHandler.createSubflow(candidate);
|
|
841
|
+
subflowResults.push(subflowResult);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
// Step 5: Create the main flow structure
|
|
845
|
+
const flowStructure = await this.createIntelligentFlowStructure(instruction, analysisResult, matchingTemplates, subflowResults);
|
|
846
|
+
// Step 6: Return comprehensive result
|
|
847
|
+
return {
|
|
848
|
+
naturalLanguage: instruction,
|
|
849
|
+
// Decision analysis
|
|
850
|
+
decisionAnalysis: {
|
|
851
|
+
recommendedType: analysisResult.recommendedType,
|
|
852
|
+
confidence: analysisResult.confidence,
|
|
853
|
+
rationale: analysisResult.rationale,
|
|
854
|
+
complexity: analysisResult.criteria.complexity,
|
|
855
|
+
reusability: analysisResult.criteria.reusability,
|
|
856
|
+
context: analysisResult.criteria.context
|
|
857
|
+
},
|
|
858
|
+
// Validation results
|
|
859
|
+
validation: {
|
|
860
|
+
isValid: validationResult.isValid,
|
|
861
|
+
severity: validationResult.severity,
|
|
862
|
+
score: validationResult.score,
|
|
863
|
+
maxScore: validationResult.maxScore,
|
|
864
|
+
issues: validationResult.issues,
|
|
865
|
+
recommendations: validationResult.recommendations
|
|
866
|
+
},
|
|
867
|
+
// Template matching
|
|
868
|
+
templateMatching: {
|
|
869
|
+
matches: matchingTemplates.length,
|
|
870
|
+
bestMatch: matchingTemplates[0] || null,
|
|
871
|
+
confidence: matchingTemplates[0]?.confidence || 0
|
|
872
|
+
},
|
|
873
|
+
// Subflow creation results
|
|
874
|
+
subflowCreation: {
|
|
875
|
+
candidatesIdentified: analysisResult.subflowCandidates.length,
|
|
876
|
+
subflowsCreated: subflowResults.filter(r => r.success).length,
|
|
877
|
+
results: subflowResults
|
|
878
|
+
},
|
|
879
|
+
// Main flow structure
|
|
880
|
+
flowStructure,
|
|
881
|
+
// Deployment readiness
|
|
882
|
+
deploymentReady: validationResult.isValid && validationResult.severity !== 'critical',
|
|
883
|
+
// Recommendations
|
|
884
|
+
recommendations: this.generateComprehensiveRecommendations(analysisResult, validationResult, matchingTemplates, subflowResults)
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
catch (error) {
|
|
888
|
+
this.logger.error('Failed to create intelligent flow', error);
|
|
889
|
+
// Fallback to simplified flow creation
|
|
890
|
+
this.logger.info('Falling back to simplified flow creation');
|
|
891
|
+
return this.createSimplifiedFlowFromInstruction(instruction);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Create intelligent flow structure based on analysis results
|
|
896
|
+
*/
|
|
897
|
+
async createIntelligentFlowStructure(instruction, analysisResult, matchingTemplates, subflowResults) {
|
|
898
|
+
// Use best matching template if available
|
|
899
|
+
const bestTemplate = matchingTemplates[0];
|
|
900
|
+
if (bestTemplate && bestTemplate.confidence > 0.7) {
|
|
901
|
+
this.logger.info('Using template for flow creation', {
|
|
902
|
+
templateId: bestTemplate.template.id,
|
|
903
|
+
confidence: bestTemplate.confidence
|
|
904
|
+
});
|
|
905
|
+
// Customize template based on instruction
|
|
906
|
+
const customizations = this.extractCustomizationsFromInstruction(instruction);
|
|
907
|
+
return this.patternTemplates.generateFlowFromTemplate(bestTemplate.template, customizations);
|
|
908
|
+
}
|
|
909
|
+
// Otherwise, create from scratch using analysis results
|
|
910
|
+
const parsedInstruction = this.parseInstruction(instruction);
|
|
911
|
+
const simpleActions = await this.generateSimpleActions(instruction, parsedInstruction.entities);
|
|
912
|
+
// Enhance with subflow references
|
|
913
|
+
const enhancedActions = this.enhanceActionsWithSubflows(simpleActions, subflowResults);
|
|
914
|
+
return {
|
|
915
|
+
name: parsedInstruction.flowName,
|
|
916
|
+
description: parsedInstruction.description,
|
|
917
|
+
table: parsedInstruction.table,
|
|
918
|
+
trigger: {
|
|
919
|
+
type: parsedInstruction.trigger.type,
|
|
920
|
+
table: parsedInstruction.table,
|
|
921
|
+
condition: parsedInstruction.trigger.condition || ''
|
|
922
|
+
},
|
|
923
|
+
activities: enhancedActions.map((action, index) => ({
|
|
924
|
+
id: `activity_${index + 1}`,
|
|
925
|
+
name: action.name,
|
|
926
|
+
type: action.type,
|
|
927
|
+
artifact_reference: action.discovered_action ? {
|
|
928
|
+
sys_id: action.action_type_id,
|
|
929
|
+
name: action.discovered_action.name || action.name
|
|
930
|
+
} : null,
|
|
931
|
+
subflow_reference: action.subflow_reference || null,
|
|
932
|
+
inputs: action.inputs || {},
|
|
933
|
+
outputs: action.outputs || {}
|
|
934
|
+
})),
|
|
935
|
+
connections: enhancedActions.map((_, index) => {
|
|
936
|
+
if (index === 0)
|
|
937
|
+
return null;
|
|
938
|
+
return {
|
|
939
|
+
from: `activity_${index}`,
|
|
940
|
+
to: `activity_${index + 1}`,
|
|
941
|
+
condition: null
|
|
942
|
+
};
|
|
943
|
+
}).filter(Boolean),
|
|
944
|
+
variables: [],
|
|
945
|
+
error_handling: analysisResult.criteria.complexity === 'high' ? [
|
|
946
|
+
{
|
|
947
|
+
condition: 'any_activity_failed',
|
|
948
|
+
action: 'stop',
|
|
949
|
+
parameters: {
|
|
950
|
+
message: 'Flow execution failed, please review'
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
] : [],
|
|
954
|
+
metadata: {
|
|
955
|
+
intelligence_applied: true,
|
|
956
|
+
decision_confidence: analysisResult.confidence,
|
|
957
|
+
template_used: bestTemplate?.template.id || null,
|
|
958
|
+
subflows_created: subflowResults.filter(r => r.success).length,
|
|
959
|
+
complexity_level: analysisResult.criteria.complexity,
|
|
960
|
+
reusability_level: analysisResult.criteria.reusability
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Fallback to simplified flow creation (original method)
|
|
966
|
+
*/
|
|
967
|
+
async createSimplifiedFlowFromInstruction(instruction) {
|
|
968
|
+
this.logger.info('Creating simplified flow from instruction', { instruction });
|
|
969
|
+
// Parse the instruction to understand intent
|
|
970
|
+
const parsedInstruction = this.parseInstruction(instruction);
|
|
971
|
+
// Generate simple actions based on the instruction (now async)
|
|
972
|
+
const simpleActions = await this.generateSimpleActions(instruction, parsedInstruction.entities);
|
|
973
|
+
// Build a simplified flow structure focusing on basic actions
|
|
974
|
+
const flowStructure = {
|
|
975
|
+
name: parsedInstruction.flowName,
|
|
976
|
+
description: parsedInstruction.description,
|
|
977
|
+
table: parsedInstruction.table,
|
|
978
|
+
trigger_type: parsedInstruction.trigger.type,
|
|
979
|
+
condition: parsedInstruction.trigger.condition,
|
|
980
|
+
active: true,
|
|
981
|
+
category: 'custom',
|
|
982
|
+
// Simple actions instead of complex activities
|
|
983
|
+
actions: simpleActions
|
|
984
|
+
};
|
|
985
|
+
// Convert to the structure expected by the MCP
|
|
986
|
+
const flowInstructionOutput = {
|
|
987
|
+
naturalLanguage: instruction,
|
|
988
|
+
parsedIntent: parsedInstruction,
|
|
989
|
+
requiredArtifacts: [], // Empty array for simplified flow
|
|
990
|
+
flowStructure: {
|
|
991
|
+
name: flowStructure.name,
|
|
992
|
+
description: flowStructure.description,
|
|
993
|
+
table: flowStructure.table,
|
|
994
|
+
trigger: {
|
|
995
|
+
type: flowStructure.trigger_type,
|
|
996
|
+
table: flowStructure.table,
|
|
997
|
+
condition: flowStructure.condition || ''
|
|
998
|
+
},
|
|
999
|
+
activities: simpleActions.map((action, index) => ({
|
|
1000
|
+
id: `activity_${index + 1}`,
|
|
1001
|
+
name: action.name,
|
|
1002
|
+
type: action.type,
|
|
1003
|
+
artifact_reference: action.discovered_action ? {
|
|
1004
|
+
sys_id: action.action_type_id,
|
|
1005
|
+
name: action.discovered_action.name || action.name
|
|
1006
|
+
} : null,
|
|
1007
|
+
inputs: action.inputs || {},
|
|
1008
|
+
outputs: action.outputs || {}
|
|
1009
|
+
})),
|
|
1010
|
+
connections: simpleActions.map((_, index) => {
|
|
1011
|
+
if (index === 0)
|
|
1012
|
+
return null;
|
|
1013
|
+
return {
|
|
1014
|
+
from: `activity_${index}`,
|
|
1015
|
+
to: `activity_${index + 1}`,
|
|
1016
|
+
condition: null
|
|
1017
|
+
};
|
|
1018
|
+
}).filter(Boolean),
|
|
1019
|
+
variables: [],
|
|
1020
|
+
error_handling: []
|
|
1021
|
+
},
|
|
1022
|
+
deploymentReady: true
|
|
1023
|
+
};
|
|
1024
|
+
return flowInstructionOutput;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Generate simple actions based on instruction - WITH SERVICENOW SEARCH
|
|
1028
|
+
*/
|
|
1029
|
+
async generateSimpleActions(instruction, entities) {
|
|
1030
|
+
const actions = [];
|
|
1031
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1032
|
+
// Notification action
|
|
1033
|
+
if (lowerInstruction.includes('notify') || lowerInstruction.includes('email') ||
|
|
1034
|
+
lowerInstruction.includes('alert')) {
|
|
1035
|
+
const discoveredActions = await this.searchForFlowActions(['notification', 'email', 'send']);
|
|
1036
|
+
const action = {
|
|
1037
|
+
type: 'notification',
|
|
1038
|
+
name: 'Send Notification',
|
|
1039
|
+
to: entities.users?.[0] || 'assigned_to',
|
|
1040
|
+
subject: this.extractNotificationSubject(instruction),
|
|
1041
|
+
message: this.extractNotificationMessage(instruction),
|
|
1042
|
+
inputs: {
|
|
1043
|
+
to: entities.users?.[0] || 'assigned_to',
|
|
1044
|
+
subject: this.extractNotificationSubject(instruction),
|
|
1045
|
+
message: this.extractNotificationMessage(instruction)
|
|
1046
|
+
},
|
|
1047
|
+
outputs: {
|
|
1048
|
+
success: true,
|
|
1049
|
+
notification_id: '${notification.sys_id}'
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
if (discoveredActions.length > 0) {
|
|
1053
|
+
action.action_type_id = discoveredActions[0].sys_id;
|
|
1054
|
+
action.discovered_action = discoveredActions[0];
|
|
1055
|
+
}
|
|
1056
|
+
actions.push(action);
|
|
1057
|
+
}
|
|
1058
|
+
// Field update action
|
|
1059
|
+
if (lowerInstruction.includes('update') || lowerInstruction.includes('set') ||
|
|
1060
|
+
lowerInstruction.includes('change')) {
|
|
1061
|
+
const fieldUpdates = this.extractFieldUpdates(instruction);
|
|
1062
|
+
const discoveredActions = await this.searchForFlowActions(['update', 'record', 'field']);
|
|
1063
|
+
// Add null check for fieldUpdates to prevent map() error
|
|
1064
|
+
if (fieldUpdates && fieldUpdates.length > 0) {
|
|
1065
|
+
for (const update of fieldUpdates) {
|
|
1066
|
+
const action = {
|
|
1067
|
+
type: 'field_update',
|
|
1068
|
+
name: `Update ${update.field}`,
|
|
1069
|
+
field: update.field,
|
|
1070
|
+
value: update.value,
|
|
1071
|
+
inputs: {
|
|
1072
|
+
field: update.field,
|
|
1073
|
+
value: update.value
|
|
1074
|
+
},
|
|
1075
|
+
outputs: {
|
|
1076
|
+
success: true,
|
|
1077
|
+
updated_field: update.field
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
if (discoveredActions.length > 0) {
|
|
1081
|
+
action.action_type_id = discoveredActions[0].sys_id;
|
|
1082
|
+
action.discovered_action = discoveredActions[0];
|
|
1083
|
+
}
|
|
1084
|
+
actions.push(action);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
// Create task action
|
|
1089
|
+
if (lowerInstruction.includes('create task') || lowerInstruction.includes('assign') ||
|
|
1090
|
+
lowerInstruction.includes('pickup') || lowerInstruction.includes('fulfillment')) {
|
|
1091
|
+
const discoveredActions = await this.searchForFlowActions(['create', 'task', 'record']);
|
|
1092
|
+
const action = {
|
|
1093
|
+
type: 'create_task',
|
|
1094
|
+
name: 'Create Task',
|
|
1095
|
+
target_table: 'task',
|
|
1096
|
+
fields: {
|
|
1097
|
+
short_description: this.extractTaskDescription(instruction),
|
|
1098
|
+
assigned_to: entities.users?.[0] || 'caller_id.manager'
|
|
1099
|
+
},
|
|
1100
|
+
inputs: {
|
|
1101
|
+
short_description: this.extractTaskDescription(instruction),
|
|
1102
|
+
assigned_to: entities.users?.[0] || 'caller_id.manager'
|
|
1103
|
+
},
|
|
1104
|
+
outputs: {
|
|
1105
|
+
task_id: '${task.sys_id}',
|
|
1106
|
+
task_number: '${task.number}'
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
if (discoveredActions.length > 0) {
|
|
1110
|
+
action.action_type_id = discoveredActions[0].sys_id;
|
|
1111
|
+
action.discovered_action = discoveredActions[0];
|
|
1112
|
+
}
|
|
1113
|
+
actions.push(action);
|
|
1114
|
+
}
|
|
1115
|
+
// Approval action
|
|
1116
|
+
if (lowerInstruction.includes('approval') || lowerInstruction.includes('approve')) {
|
|
1117
|
+
const discoveredActions = await this.searchForFlowActions(['approval', 'approve']);
|
|
1118
|
+
const action = {
|
|
1119
|
+
type: 'approval',
|
|
1120
|
+
name: 'Request Approval',
|
|
1121
|
+
approvers: entities.groups?.[0] || 'assignment_group.manager',
|
|
1122
|
+
inputs: {
|
|
1123
|
+
approvers: entities.groups?.[0] || 'assignment_group.manager'
|
|
1124
|
+
},
|
|
1125
|
+
outputs: {
|
|
1126
|
+
approval_state: '${approval.state}',
|
|
1127
|
+
approval_comments: '${approval.comments}'
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
if (discoveredActions.length > 0) {
|
|
1131
|
+
action.action_type_id = discoveredActions[0].sys_id;
|
|
1132
|
+
action.discovered_action = discoveredActions[0];
|
|
1133
|
+
}
|
|
1134
|
+
actions.push(action);
|
|
1135
|
+
}
|
|
1136
|
+
// Wait/Timer action
|
|
1137
|
+
if (lowerInstruction.includes('wait') || lowerInstruction.includes('delay') ||
|
|
1138
|
+
lowerInstruction.includes('after')) {
|
|
1139
|
+
const discoveredActions = await this.searchForFlowActions(['wait', 'timer', 'delay']);
|
|
1140
|
+
const duration = this.extractDuration(instruction);
|
|
1141
|
+
const action = {
|
|
1142
|
+
type: 'wait',
|
|
1143
|
+
name: 'Wait Timer',
|
|
1144
|
+
duration: duration,
|
|
1145
|
+
inputs: {
|
|
1146
|
+
duration: duration
|
|
1147
|
+
},
|
|
1148
|
+
outputs: {
|
|
1149
|
+
completed: true
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
if (discoveredActions.length > 0) {
|
|
1153
|
+
action.action_type_id = discoveredActions[0].sys_id;
|
|
1154
|
+
action.discovered_action = discoveredActions[0];
|
|
1155
|
+
}
|
|
1156
|
+
actions.push(action);
|
|
1157
|
+
}
|
|
1158
|
+
// Log action
|
|
1159
|
+
if (lowerInstruction.includes('log') || lowerInstruction.includes('record')) {
|
|
1160
|
+
const discoveredActions = await this.searchForFlowActions(['log', 'logging']);
|
|
1161
|
+
const action = {
|
|
1162
|
+
type: 'log',
|
|
1163
|
+
name: 'Log Message',
|
|
1164
|
+
message: this.extractLogMessage(instruction),
|
|
1165
|
+
level: 'info',
|
|
1166
|
+
inputs: {
|
|
1167
|
+
message: this.extractLogMessage(instruction),
|
|
1168
|
+
level: 'info'
|
|
1169
|
+
},
|
|
1170
|
+
outputs: {
|
|
1171
|
+
logged: true
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
if (discoveredActions.length > 0) {
|
|
1175
|
+
action.action_type_id = discoveredActions[0].sys_id;
|
|
1176
|
+
action.discovered_action = discoveredActions[0];
|
|
1177
|
+
}
|
|
1178
|
+
actions.push(action);
|
|
1179
|
+
}
|
|
1180
|
+
// If no specific actions found, create basic flow based on context
|
|
1181
|
+
if (actions.length === 0) {
|
|
1182
|
+
// For catalog/order flows
|
|
1183
|
+
if (lowerInstruction.includes('catalog') || lowerInstruction.includes('order') ||
|
|
1184
|
+
lowerInstruction.includes('iphone')) {
|
|
1185
|
+
const updateActions = await this.searchForFlowActions(['update', 'record']);
|
|
1186
|
+
const notifyActions = await this.searchForFlowActions(['notification', 'email']);
|
|
1187
|
+
const updateAction = {
|
|
1188
|
+
type: 'field_update',
|
|
1189
|
+
name: 'Set Fulfillment State',
|
|
1190
|
+
field: 'state',
|
|
1191
|
+
value: 'work_in_progress',
|
|
1192
|
+
inputs: {
|
|
1193
|
+
field: 'state',
|
|
1194
|
+
value: 'work_in_progress'
|
|
1195
|
+
},
|
|
1196
|
+
outputs: {
|
|
1197
|
+
success: true,
|
|
1198
|
+
updated_field: 'state'
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
const notifyAction = {
|
|
1202
|
+
type: 'notification',
|
|
1203
|
+
name: 'Notify User',
|
|
1204
|
+
to: 'requested_for',
|
|
1205
|
+
subject: 'Order Update',
|
|
1206
|
+
message: 'Your order is being processed',
|
|
1207
|
+
inputs: {
|
|
1208
|
+
to: 'requested_for',
|
|
1209
|
+
subject: 'Order Update',
|
|
1210
|
+
message: 'Your order is being processed'
|
|
1211
|
+
},
|
|
1212
|
+
outputs: {
|
|
1213
|
+
success: true,
|
|
1214
|
+
notification_id: '${notification.sys_id}'
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
if (updateActions.length > 0) {
|
|
1218
|
+
updateAction.action_type_id = updateActions[0].sys_id;
|
|
1219
|
+
updateAction.discovered_action = updateActions[0];
|
|
1220
|
+
}
|
|
1221
|
+
if (notifyActions.length > 0) {
|
|
1222
|
+
notifyAction.action_type_id = notifyActions[0].sys_id;
|
|
1223
|
+
notifyAction.discovered_action = notifyActions[0];
|
|
1224
|
+
}
|
|
1225
|
+
actions.push(updateAction, notifyAction);
|
|
1226
|
+
}
|
|
1227
|
+
else {
|
|
1228
|
+
// Default notification
|
|
1229
|
+
const notifyActions = await this.searchForFlowActions(['notification', 'email']);
|
|
1230
|
+
const action = {
|
|
1231
|
+
type: 'notification',
|
|
1232
|
+
name: 'Flow Executed',
|
|
1233
|
+
to: 'assigned_to',
|
|
1234
|
+
subject: 'Flow Action Completed',
|
|
1235
|
+
message: `Flow executed for: ${instruction}`,
|
|
1236
|
+
inputs: {
|
|
1237
|
+
to: 'assigned_to',
|
|
1238
|
+
subject: 'Flow Action Completed',
|
|
1239
|
+
message: `Flow executed for: ${instruction}`
|
|
1240
|
+
},
|
|
1241
|
+
outputs: {
|
|
1242
|
+
success: true,
|
|
1243
|
+
notification_id: '${notification.sys_id}'
|
|
1244
|
+
}
|
|
1245
|
+
};
|
|
1246
|
+
if (notifyActions.length > 0) {
|
|
1247
|
+
action.action_type_id = notifyActions[0].sys_id;
|
|
1248
|
+
action.discovered_action = notifyActions[0];
|
|
1249
|
+
}
|
|
1250
|
+
actions.push(action);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return actions;
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Search for flow actions in ServiceNow
|
|
1257
|
+
*/
|
|
1258
|
+
async searchForFlowActions(searchTerms) {
|
|
1259
|
+
const allActions = [];
|
|
1260
|
+
for (const term of searchTerms) {
|
|
1261
|
+
try {
|
|
1262
|
+
const results = await this.client.searchFlowActions(term);
|
|
1263
|
+
if (results.actionTypes && results.actionTypes.length > 0) {
|
|
1264
|
+
allActions.push(...results.actionTypes);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
catch (error) {
|
|
1268
|
+
this.logger.warn(`Failed to search for flow actions with term: ${term}`, error);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
// Remove duplicates based on sys_id
|
|
1272
|
+
const uniqueActions = allActions.filter((action, index, self) => index === self.findIndex(a => a.sys_id === action.sys_id));
|
|
1273
|
+
this.logger.info(`Found ${uniqueActions.length} unique flow actions for terms: ${searchTerms.join(', ')}`);
|
|
1274
|
+
return uniqueActions;
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Helper methods for extracting specific information - NEW METHODS
|
|
1278
|
+
*/
|
|
1279
|
+
extractNotificationSubject(instruction) {
|
|
1280
|
+
const subjectMatch = instruction.match(/subject[:\s]+["']([^"']+)["']/i);
|
|
1281
|
+
if (subjectMatch)
|
|
1282
|
+
return subjectMatch[1];
|
|
1283
|
+
const keywords = ['notify', 'alert', 'email'];
|
|
1284
|
+
for (const keyword of keywords) {
|
|
1285
|
+
if (instruction.toLowerCase().includes(keyword)) {
|
|
1286
|
+
return `Notification: ${instruction.substring(0, 50)}`;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return 'Flow Notification';
|
|
1290
|
+
}
|
|
1291
|
+
extractNotificationMessage(instruction) {
|
|
1292
|
+
const messageMatch = instruction.match(/message[:\s]+["']([^"']+)["']/i);
|
|
1293
|
+
if (messageMatch)
|
|
1294
|
+
return messageMatch[1];
|
|
1295
|
+
return instruction;
|
|
1296
|
+
}
|
|
1297
|
+
extractFieldUpdates(instruction) {
|
|
1298
|
+
const updates = [];
|
|
1299
|
+
// Pattern: "set field to value" or "update field with value"
|
|
1300
|
+
const patterns = [
|
|
1301
|
+
/(?:set|update)\s+(\w+)\s+(?:to|with)\s+["']?([^"']+)["']?/gi,
|
|
1302
|
+
/(\w+)\s*=\s*["']?([^"']+)["']?/gi
|
|
1303
|
+
];
|
|
1304
|
+
for (const pattern of patterns) {
|
|
1305
|
+
let match;
|
|
1306
|
+
while ((match = pattern.exec(instruction)) !== null) {
|
|
1307
|
+
updates.push({
|
|
1308
|
+
field: match[1],
|
|
1309
|
+
value: match[2]
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
// Common field mappings
|
|
1314
|
+
const fieldMap = {
|
|
1315
|
+
'status': 'state',
|
|
1316
|
+
'priority': 'priority',
|
|
1317
|
+
'assignment': 'assigned_to',
|
|
1318
|
+
'description': 'short_description'
|
|
1319
|
+
};
|
|
1320
|
+
// Map common field names - add null check to prevent map() error
|
|
1321
|
+
if (!updates || updates.length === 0) {
|
|
1322
|
+
return [];
|
|
1323
|
+
}
|
|
1324
|
+
return updates.map(update => ({
|
|
1325
|
+
field: fieldMap[update.field.toLowerCase()] || update.field,
|
|
1326
|
+
value: update.value
|
|
1327
|
+
}));
|
|
1328
|
+
}
|
|
1329
|
+
extractTaskDescription(instruction) {
|
|
1330
|
+
const taskMatch = instruction.match(/task[:\s]+["']([^"']+)["']/i);
|
|
1331
|
+
if (taskMatch)
|
|
1332
|
+
return taskMatch[1];
|
|
1333
|
+
// For specific contexts
|
|
1334
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1335
|
+
if (lowerInstruction.includes('pickup')) {
|
|
1336
|
+
return 'User pickup required';
|
|
1337
|
+
}
|
|
1338
|
+
if (lowerInstruction.includes('fulfillment')) {
|
|
1339
|
+
return 'Fulfillment task';
|
|
1340
|
+
}
|
|
1341
|
+
return `Task: ${instruction.substring(0, 80)}`;
|
|
1342
|
+
}
|
|
1343
|
+
extractDuration(instruction) {
|
|
1344
|
+
const durationMatch = instruction.match(/(\d+)\s*(second|minute|hour|day)/i);
|
|
1345
|
+
if (durationMatch) {
|
|
1346
|
+
const value = parseInt(durationMatch[1]);
|
|
1347
|
+
const unit = durationMatch[2].toLowerCase();
|
|
1348
|
+
switch (unit) {
|
|
1349
|
+
case 'second': return value;
|
|
1350
|
+
case 'minute': return value * 60;
|
|
1351
|
+
case 'hour': return value * 3600;
|
|
1352
|
+
case 'day': return value * 86400;
|
|
1353
|
+
default: return 300; // 5 minutes default
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
return 300; // 5 minutes default
|
|
1357
|
+
}
|
|
1358
|
+
extractLogMessage(instruction) {
|
|
1359
|
+
const logMatch = instruction.match(/log[:\s]+["']([^"']+)["']/i);
|
|
1360
|
+
if (logMatch)
|
|
1361
|
+
return logMatch[1];
|
|
1362
|
+
return `Flow action: ${instruction}`;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Parse natural language instruction
|
|
1366
|
+
*/
|
|
1367
|
+
parseInstruction(instruction) {
|
|
1368
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1369
|
+
this.logger.info('Parsing instruction:', { instruction, lowerInstruction });
|
|
1370
|
+
// Enhanced flow name extraction
|
|
1371
|
+
const flowName = this.extractFlowName(instruction);
|
|
1372
|
+
// Enhanced table extraction with ServiceNow context
|
|
1373
|
+
const table = this.extractTable(instruction);
|
|
1374
|
+
// Enhanced trigger extraction
|
|
1375
|
+
const trigger = this.extractTrigger(instruction, table);
|
|
1376
|
+
// Extract workflow requirements
|
|
1377
|
+
const requirements = this.extractRequirements(instruction);
|
|
1378
|
+
// Extract entities and context
|
|
1379
|
+
const entities = this.extractEntities(instruction);
|
|
1380
|
+
const parsed = {
|
|
1381
|
+
flowName,
|
|
1382
|
+
description: instruction,
|
|
1383
|
+
table,
|
|
1384
|
+
trigger,
|
|
1385
|
+
intents: this.extractIntents(instruction),
|
|
1386
|
+
dataFlow: this.extractDataFlow(instruction),
|
|
1387
|
+
requirements,
|
|
1388
|
+
entities,
|
|
1389
|
+
flowType: this.determineFlowType(instruction),
|
|
1390
|
+
complexity: this.assessComplexity(instruction)
|
|
1391
|
+
};
|
|
1392
|
+
this.logger.info('Parsed instruction:', parsed);
|
|
1393
|
+
return parsed;
|
|
1394
|
+
}
|
|
1395
|
+
extractFlowName(instruction) {
|
|
1396
|
+
// First check for quoted flow names - more precise matching
|
|
1397
|
+
const quotedPatterns = [
|
|
1398
|
+
/flow\s+named\s+["']([^"']+)["']/i,
|
|
1399
|
+
/flow\s+called\s+["']([^"']+)["']/i,
|
|
1400
|
+
/["']([^"']+)["']\s+flow/i,
|
|
1401
|
+
/create\s+a\s+flow\s+named\s+["']([^"']+)["']/i
|
|
1402
|
+
];
|
|
1403
|
+
for (const pattern of quotedPatterns) {
|
|
1404
|
+
const match = instruction.match(pattern);
|
|
1405
|
+
if (match && match[1]) {
|
|
1406
|
+
return match[1].trim();
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
// Check for any remaining quoted strings as fallback
|
|
1410
|
+
const generalQuotedMatch = instruction.match(/["']([^"']+)["']/i);
|
|
1411
|
+
if (generalQuotedMatch && generalQuotedMatch[1] && generalQuotedMatch[1].toLowerCase().includes('flow')) {
|
|
1412
|
+
return generalQuotedMatch[1].trim();
|
|
1413
|
+
}
|
|
1414
|
+
// Then check for 'flow named X' or 'flow called X' without quotes
|
|
1415
|
+
const namedMatch = instruction.match(/flow\s+(?:named|called)\s+([^\s]+(?:\s+[^\s]+)*?)(?:\s+that|\s+which|\s+to|\.|,|$)/i);
|
|
1416
|
+
if (namedMatch && namedMatch[1]) {
|
|
1417
|
+
return namedMatch[1].trim();
|
|
1418
|
+
}
|
|
1419
|
+
// Try other patterns
|
|
1420
|
+
const patterns = [
|
|
1421
|
+
/(?:create|build|make)\s+(?:a\s+)?(.+?)\s+flow/i,
|
|
1422
|
+
/flow\s+(?:for|to)\s+(.+?)(?:\s+when|\s+if|\.|,|$)/i,
|
|
1423
|
+
/(.+?)\s+(?:workflow|process)(?:\s|$)/i
|
|
1424
|
+
];
|
|
1425
|
+
for (const pattern of patterns) {
|
|
1426
|
+
const match = instruction.match(pattern);
|
|
1427
|
+
if (match && match[1]) {
|
|
1428
|
+
return match[1].trim();
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
// If no specific pattern found, use first part of instruction
|
|
1432
|
+
const words = instruction.split(/\s+/);
|
|
1433
|
+
if (words.length > 2) {
|
|
1434
|
+
return words.slice(0, 3).join(' ');
|
|
1435
|
+
}
|
|
1436
|
+
return 'Custom Flow';
|
|
1437
|
+
}
|
|
1438
|
+
extractTable(instruction) {
|
|
1439
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1440
|
+
// ServiceNow table mapping
|
|
1441
|
+
const tableMap = {
|
|
1442
|
+
'incident': 'incident',
|
|
1443
|
+
'request': 'sc_request',
|
|
1444
|
+
'service request': 'sc_request',
|
|
1445
|
+
'catalog': 'sc_req_item',
|
|
1446
|
+
'catalog item': 'sc_req_item',
|
|
1447
|
+
'problem': 'problem',
|
|
1448
|
+
'change': 'change_request',
|
|
1449
|
+
'change request': 'change_request',
|
|
1450
|
+
'task': 'task',
|
|
1451
|
+
'user': 'sys_user',
|
|
1452
|
+
'cmdb': 'cmdb_ci',
|
|
1453
|
+
'configuration item': 'cmdb_ci'
|
|
1454
|
+
};
|
|
1455
|
+
// Check for explicit table mentions
|
|
1456
|
+
for (const [key, value] of Object.entries(tableMap)) {
|
|
1457
|
+
if (lowerInstruction.includes(key)) {
|
|
1458
|
+
return value;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
// Check for catalog/fulfillment context
|
|
1462
|
+
if (lowerInstruction.includes('order') || lowerInstruction.includes('fulfillment') ||
|
|
1463
|
+
lowerInstruction.includes('catalog') || lowerInstruction.includes('iphone')) {
|
|
1464
|
+
return 'sc_req_item';
|
|
1465
|
+
}
|
|
1466
|
+
// Check for approval context
|
|
1467
|
+
if (lowerInstruction.includes('approval') || lowerInstruction.includes('approve')) {
|
|
1468
|
+
return 'sc_request';
|
|
1469
|
+
}
|
|
1470
|
+
// Default fallback based on flow type
|
|
1471
|
+
if (lowerInstruction.includes('flow') || lowerInstruction.includes('process')) {
|
|
1472
|
+
return 'task'; // Generic task table for workflows
|
|
1473
|
+
}
|
|
1474
|
+
return 'task'; // Changed from 'incident' to more neutral 'task'
|
|
1475
|
+
}
|
|
1476
|
+
extractTrigger(instruction, table) {
|
|
1477
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1478
|
+
let triggerType = 'record_created';
|
|
1479
|
+
let condition = '';
|
|
1480
|
+
// Determine trigger type
|
|
1481
|
+
if (lowerInstruction.includes('update') || lowerInstruction.includes('change')) {
|
|
1482
|
+
triggerType = 'record_updated';
|
|
1483
|
+
}
|
|
1484
|
+
else if (lowerInstruction.includes('delete')) {
|
|
1485
|
+
triggerType = 'record_deleted';
|
|
1486
|
+
}
|
|
1487
|
+
else if (lowerInstruction.includes('schedule')) {
|
|
1488
|
+
triggerType = 'scheduled';
|
|
1489
|
+
}
|
|
1490
|
+
// Extract specific conditions
|
|
1491
|
+
if (lowerInstruction.includes('iphone 6')) {
|
|
1492
|
+
condition = 'cat_item.name=iPhone 6';
|
|
1493
|
+
}
|
|
1494
|
+
else if (lowerInstruction.includes('high priority')) {
|
|
1495
|
+
condition = 'priority=1';
|
|
1496
|
+
}
|
|
1497
|
+
else if (lowerInstruction.includes('critical')) {
|
|
1498
|
+
condition = 'priority=1^severity=1';
|
|
1499
|
+
}
|
|
1500
|
+
return {
|
|
1501
|
+
type: triggerType,
|
|
1502
|
+
table,
|
|
1503
|
+
condition
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
extractRequirements(instruction) {
|
|
1507
|
+
const requirements = [];
|
|
1508
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1509
|
+
// Extract sequential requirements
|
|
1510
|
+
if (lowerInstruction.includes('approval')) {
|
|
1511
|
+
requirements.push('approval_required');
|
|
1512
|
+
}
|
|
1513
|
+
if (lowerInstruction.includes('task')) {
|
|
1514
|
+
requirements.push('task_creation');
|
|
1515
|
+
}
|
|
1516
|
+
if (lowerInstruction.includes('notification')) {
|
|
1517
|
+
requirements.push('notification_required');
|
|
1518
|
+
}
|
|
1519
|
+
if (lowerInstruction.includes('admin')) {
|
|
1520
|
+
requirements.push('admin_assignment');
|
|
1521
|
+
}
|
|
1522
|
+
if (lowerInstruction.includes('pickup')) {
|
|
1523
|
+
requirements.push('pickup_scheduling');
|
|
1524
|
+
}
|
|
1525
|
+
if (lowerInstruction.includes('complete')) {
|
|
1526
|
+
requirements.push('completion_tracking');
|
|
1527
|
+
}
|
|
1528
|
+
return requirements;
|
|
1529
|
+
}
|
|
1530
|
+
extractEntities(instruction) {
|
|
1531
|
+
const entities = {};
|
|
1532
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1533
|
+
// Extract product entities
|
|
1534
|
+
if (lowerInstruction.includes('iphone')) {
|
|
1535
|
+
entities.product = 'iPhone';
|
|
1536
|
+
if (lowerInstruction.includes('iphone 6')) {
|
|
1537
|
+
entities.product_model = 'iPhone 6';
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
// Extract role entities
|
|
1541
|
+
if (lowerInstruction.includes('admin')) {
|
|
1542
|
+
entities.admin_role = true;
|
|
1543
|
+
}
|
|
1544
|
+
if (lowerInstruction.includes('user')) {
|
|
1545
|
+
entities.user_role = true;
|
|
1546
|
+
}
|
|
1547
|
+
// Extract action entities
|
|
1548
|
+
if (lowerInstruction.includes('pickup')) {
|
|
1549
|
+
entities.pickup_action = true;
|
|
1550
|
+
}
|
|
1551
|
+
if (lowerInstruction.includes('fulfillment')) {
|
|
1552
|
+
entities.fulfillment_process = true;
|
|
1553
|
+
}
|
|
1554
|
+
return entities;
|
|
1555
|
+
}
|
|
1556
|
+
determineFlowType(instruction) {
|
|
1557
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1558
|
+
if (lowerInstruction.includes('approval')) {
|
|
1559
|
+
return 'approval_workflow';
|
|
1560
|
+
}
|
|
1561
|
+
if (lowerInstruction.includes('fulfillment') || lowerInstruction.includes('order')) {
|
|
1562
|
+
return 'fulfillment_workflow';
|
|
1563
|
+
}
|
|
1564
|
+
if (lowerInstruction.includes('incident')) {
|
|
1565
|
+
return 'incident_workflow';
|
|
1566
|
+
}
|
|
1567
|
+
if (lowerInstruction.includes('notification')) {
|
|
1568
|
+
return 'notification_workflow';
|
|
1569
|
+
}
|
|
1570
|
+
return 'general_workflow';
|
|
1571
|
+
}
|
|
1572
|
+
assessComplexity(instruction) {
|
|
1573
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1574
|
+
let complexity = 0;
|
|
1575
|
+
// Count complexity indicators
|
|
1576
|
+
if (lowerInstruction.includes('approval'))
|
|
1577
|
+
complexity += 2;
|
|
1578
|
+
if (lowerInstruction.includes('task'))
|
|
1579
|
+
complexity += 1;
|
|
1580
|
+
if (lowerInstruction.includes('notification'))
|
|
1581
|
+
complexity += 1;
|
|
1582
|
+
if (lowerInstruction.includes('admin'))
|
|
1583
|
+
complexity += 1;
|
|
1584
|
+
if (lowerInstruction.includes('user'))
|
|
1585
|
+
complexity += 1;
|
|
1586
|
+
if (lowerInstruction.includes('pickup'))
|
|
1587
|
+
complexity += 2;
|
|
1588
|
+
if (lowerInstruction.includes('schedule'))
|
|
1589
|
+
complexity += 2;
|
|
1590
|
+
if (complexity >= 5)
|
|
1591
|
+
return 'high';
|
|
1592
|
+
if (complexity >= 3)
|
|
1593
|
+
return 'medium';
|
|
1594
|
+
return 'low';
|
|
1595
|
+
}
|
|
1596
|
+
/**
|
|
1597
|
+
* Create activities based on parsed requirements
|
|
1598
|
+
*/
|
|
1599
|
+
async createActivitiesFromRequirements(parsedInstruction) {
|
|
1600
|
+
const activities = [];
|
|
1601
|
+
const requirements = parsedInstruction.requirements;
|
|
1602
|
+
const entities = parsedInstruction.entities;
|
|
1603
|
+
const flowType = parsedInstruction.flowType;
|
|
1604
|
+
this.logger.info('Creating activities from requirements:', { requirements, entities, flowType });
|
|
1605
|
+
// Create activities based on flow type and requirements
|
|
1606
|
+
if (flowType === 'fulfillment_workflow') {
|
|
1607
|
+
activities.push(...this.createFulfillmentActivities(requirements, entities));
|
|
1608
|
+
}
|
|
1609
|
+
else if (flowType === 'approval_workflow') {
|
|
1610
|
+
activities.push(...this.createApprovalActivities(requirements, entities));
|
|
1611
|
+
}
|
|
1612
|
+
else if (flowType === 'incident_workflow') {
|
|
1613
|
+
activities.push(...this.createIncidentActivities(requirements, entities));
|
|
1614
|
+
}
|
|
1615
|
+
else {
|
|
1616
|
+
activities.push(...this.createGeneralActivities(requirements, entities));
|
|
1617
|
+
}
|
|
1618
|
+
return activities;
|
|
1619
|
+
}
|
|
1620
|
+
createFulfillmentActivities(requirements, entities) {
|
|
1621
|
+
const activities = [];
|
|
1622
|
+
let activityIndex = 1;
|
|
1623
|
+
// Always start with validation for fulfillment
|
|
1624
|
+
activities.push({
|
|
1625
|
+
id: `activity_${activityIndex++}`,
|
|
1626
|
+
type: 'condition',
|
|
1627
|
+
name: 'Validate Request',
|
|
1628
|
+
condition: entities.product_model ? `cat_item.name=${entities.product_model}` : 'state=1',
|
|
1629
|
+
inputs: {
|
|
1630
|
+
record: '${trigger.record}',
|
|
1631
|
+
validation_rules: ['product_available', 'user_authorized']
|
|
1632
|
+
},
|
|
1633
|
+
outputs: {
|
|
1634
|
+
validation_result: '${step.validation_result}',
|
|
1635
|
+
next_step: '${step.next_step}'
|
|
1636
|
+
}
|
|
1637
|
+
});
|
|
1638
|
+
// Add approval if required
|
|
1639
|
+
if (requirements.includes('approval_required')) {
|
|
1640
|
+
activities.push({
|
|
1641
|
+
id: `activity_${activityIndex++}`,
|
|
1642
|
+
type: 'approval',
|
|
1643
|
+
name: 'Manager Approval',
|
|
1644
|
+
approval_type: 'manager',
|
|
1645
|
+
inputs: {
|
|
1646
|
+
record: '${trigger.record}',
|
|
1647
|
+
approval_group: 'managers',
|
|
1648
|
+
timeout: '48 hours'
|
|
1649
|
+
},
|
|
1650
|
+
outputs: {
|
|
1651
|
+
approval_result: '${step.approval_result}',
|
|
1652
|
+
approved_by: '${step.approved_by}'
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
// Add task creation for admin
|
|
1657
|
+
if (requirements.includes('admin_assignment')) {
|
|
1658
|
+
activities.push({
|
|
1659
|
+
id: `activity_${activityIndex++}`,
|
|
1660
|
+
type: 'create_task',
|
|
1661
|
+
name: 'Create Admin Task',
|
|
1662
|
+
task_type: 'admin_pickup',
|
|
1663
|
+
inputs: {
|
|
1664
|
+
parent_record: '${trigger.record}',
|
|
1665
|
+
assigned_to: 'admin_group',
|
|
1666
|
+
short_description: entities.product_model ? `Prepare ${entities.product_model} for pickup` : 'Prepare item for pickup',
|
|
1667
|
+
priority: '2'
|
|
1668
|
+
},
|
|
1669
|
+
outputs: {
|
|
1670
|
+
task_id: '${step.task_id}',
|
|
1671
|
+
task_state: '${step.task_state}'
|
|
1672
|
+
}
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
// Add task creation for user
|
|
1676
|
+
if (requirements.includes('pickup_scheduling')) {
|
|
1677
|
+
activities.push({
|
|
1678
|
+
id: `activity_${activityIndex++}`,
|
|
1679
|
+
type: 'create_task',
|
|
1680
|
+
name: 'Create User Pickup Task',
|
|
1681
|
+
task_type: 'user_pickup',
|
|
1682
|
+
inputs: {
|
|
1683
|
+
parent_record: '${trigger.record}',
|
|
1684
|
+
assigned_to: '${trigger.record.requested_for}',
|
|
1685
|
+
short_description: entities.product_model ? `Pick up your ${entities.product_model}` : 'Pick up your item',
|
|
1686
|
+
priority: '3'
|
|
1687
|
+
},
|
|
1688
|
+
outputs: {
|
|
1689
|
+
pickup_task_id: '${step.pickup_task_id}',
|
|
1690
|
+
pickup_scheduled: '${step.pickup_scheduled}'
|
|
1691
|
+
}
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
// Add notifications
|
|
1695
|
+
if (requirements.includes('notification_required')) {
|
|
1696
|
+
activities.push({
|
|
1697
|
+
id: `activity_${activityIndex++}`,
|
|
1698
|
+
type: 'notification',
|
|
1699
|
+
name: 'Send Notification',
|
|
1700
|
+
notification_type: 'email',
|
|
1701
|
+
inputs: {
|
|
1702
|
+
recipient: '${trigger.record.requested_for}',
|
|
1703
|
+
subject: entities.product_model ? `Your ${entities.product_model} is ready for pickup` : 'Your item is ready for pickup',
|
|
1704
|
+
message: 'Please visit the admin desk to collect your item.'
|
|
1705
|
+
},
|
|
1706
|
+
outputs: {
|
|
1707
|
+
notification_sent: '${step.notification_sent}'
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
// Add completion tracking
|
|
1712
|
+
if (requirements.includes('completion_tracking')) {
|
|
1713
|
+
activities.push({
|
|
1714
|
+
id: `activity_${activityIndex++}`,
|
|
1715
|
+
type: 'update_record',
|
|
1716
|
+
name: 'Update Request Status',
|
|
1717
|
+
inputs: {
|
|
1718
|
+
record: '${trigger.record}',
|
|
1719
|
+
updates: {
|
|
1720
|
+
state: '3',
|
|
1721
|
+
completion_date: '${gs.now()}'
|
|
1722
|
+
}
|
|
1723
|
+
},
|
|
1724
|
+
outputs: {
|
|
1725
|
+
updated_record: '${step.updated_record}'
|
|
1726
|
+
}
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
return activities;
|
|
1730
|
+
}
|
|
1731
|
+
createApprovalActivities(requirements, entities) {
|
|
1732
|
+
const activities = [];
|
|
1733
|
+
let activityIndex = 1;
|
|
1734
|
+
// Create approval activity
|
|
1735
|
+
activities.push({
|
|
1736
|
+
id: `activity_${activityIndex++}`,
|
|
1737
|
+
type: 'approval',
|
|
1738
|
+
name: 'Request Approval',
|
|
1739
|
+
approval_type: 'sequential',
|
|
1740
|
+
inputs: {
|
|
1741
|
+
record: '${trigger.record}',
|
|
1742
|
+
approval_group: 'approvers',
|
|
1743
|
+
timeout: '24 hours'
|
|
1744
|
+
},
|
|
1745
|
+
outputs: {
|
|
1746
|
+
approval_result: '${step.approval_result}',
|
|
1747
|
+
approved_by: '${step.approved_by}'
|
|
1748
|
+
}
|
|
1749
|
+
});
|
|
1750
|
+
// Add conditional logic for approval result
|
|
1751
|
+
activities.push({
|
|
1752
|
+
id: `activity_${activityIndex++}`,
|
|
1753
|
+
type: 'condition',
|
|
1754
|
+
name: 'Check Approval Result',
|
|
1755
|
+
condition: 'approval_result=approved',
|
|
1756
|
+
inputs: {
|
|
1757
|
+
approval_result: '${activity_1.approval_result}'
|
|
1758
|
+
},
|
|
1759
|
+
outputs: {
|
|
1760
|
+
proceed: '${step.proceed}'
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
return activities;
|
|
1764
|
+
}
|
|
1765
|
+
createIncidentActivities(requirements, entities) {
|
|
1766
|
+
const activities = [];
|
|
1767
|
+
let activityIndex = 1;
|
|
1768
|
+
// Create incident assignment
|
|
1769
|
+
activities.push({
|
|
1770
|
+
id: `activity_${activityIndex++}`,
|
|
1771
|
+
type: 'assignment',
|
|
1772
|
+
name: 'Assign Incident',
|
|
1773
|
+
inputs: {
|
|
1774
|
+
record: '${trigger.record}',
|
|
1775
|
+
assignment_group: 'it_support',
|
|
1776
|
+
priority: '${trigger.record.priority}'
|
|
1777
|
+
},
|
|
1778
|
+
outputs: {
|
|
1779
|
+
assigned_to: '${step.assigned_to}',
|
|
1780
|
+
assignment_group: '${step.assignment_group}'
|
|
1781
|
+
}
|
|
1782
|
+
});
|
|
1783
|
+
return activities;
|
|
1784
|
+
}
|
|
1785
|
+
createGeneralActivities(requirements, entities) {
|
|
1786
|
+
const activities = [];
|
|
1787
|
+
let activityIndex = 1;
|
|
1788
|
+
// Create basic processing activity
|
|
1789
|
+
activities.push({
|
|
1790
|
+
id: `activity_${activityIndex++}`,
|
|
1791
|
+
type: 'script',
|
|
1792
|
+
name: 'Process Request',
|
|
1793
|
+
inputs: {
|
|
1794
|
+
record: '${trigger.record}'
|
|
1795
|
+
},
|
|
1796
|
+
outputs: {
|
|
1797
|
+
result: '${step.result}'
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1800
|
+
return activities;
|
|
1801
|
+
}
|
|
1802
|
+
createWorkflowConnections(activities) {
|
|
1803
|
+
const connections = [];
|
|
1804
|
+
// Create sequential connections between activities
|
|
1805
|
+
for (let i = 0; i < activities.length - 1; i++) {
|
|
1806
|
+
connections.push({
|
|
1807
|
+
from: activities[i].id,
|
|
1808
|
+
to: activities[i + 1].id
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
// Add conditional connections if needed
|
|
1812
|
+
for (const activity of activities) {
|
|
1813
|
+
if (activity.type === 'condition') {
|
|
1814
|
+
// Add conditional branches (simplified)
|
|
1815
|
+
connections.push({
|
|
1816
|
+
from: activity.id,
|
|
1817
|
+
to: 'end',
|
|
1818
|
+
condition: 'false'
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
return connections;
|
|
1823
|
+
}
|
|
1824
|
+
/**
|
|
1825
|
+
* Extract intents from instruction
|
|
1826
|
+
*/
|
|
1827
|
+
extractIntents(instruction) {
|
|
1828
|
+
const intents = [];
|
|
1829
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1830
|
+
// ServiceNow-specific intents
|
|
1831
|
+
if (lowerInstruction.includes('approve') || lowerInstruction.includes('approval')) {
|
|
1832
|
+
intents.push('approval');
|
|
1833
|
+
}
|
|
1834
|
+
if (lowerInstruction.includes('task') || lowerInstruction.includes('assign')) {
|
|
1835
|
+
intents.push('task_creation');
|
|
1836
|
+
}
|
|
1837
|
+
if (lowerInstruction.includes('notify') || lowerInstruction.includes('notification')) {
|
|
1838
|
+
intents.push('notification');
|
|
1839
|
+
}
|
|
1840
|
+
if (lowerInstruction.includes('pickup') || lowerInstruction.includes('schedule')) {
|
|
1841
|
+
intents.push('scheduling');
|
|
1842
|
+
}
|
|
1843
|
+
if (lowerInstruction.includes('fulfillment') || lowerInstruction.includes('order')) {
|
|
1844
|
+
intents.push('fulfillment');
|
|
1845
|
+
}
|
|
1846
|
+
if (lowerInstruction.includes('catalog') || lowerInstruction.includes('item')) {
|
|
1847
|
+
intents.push('catalog_management');
|
|
1848
|
+
}
|
|
1849
|
+
if (lowerInstruction.includes('admin') || lowerInstruction.includes('administrator')) {
|
|
1850
|
+
intents.push('admin_process');
|
|
1851
|
+
}
|
|
1852
|
+
if (lowerInstruction.includes('user') || lowerInstruction.includes('customer')) {
|
|
1853
|
+
intents.push('user_process');
|
|
1854
|
+
}
|
|
1855
|
+
if (lowerInstruction.includes('complete') || lowerInstruction.includes('finish')) {
|
|
1856
|
+
intents.push('completion');
|
|
1857
|
+
}
|
|
1858
|
+
if (lowerInstruction.includes('escalate') || lowerInstruction.includes('escalation')) {
|
|
1859
|
+
intents.push('escalation');
|
|
1860
|
+
}
|
|
1861
|
+
if (lowerInstruction.includes('validate') || lowerInstruction.includes('validation')) {
|
|
1862
|
+
intents.push('validation');
|
|
1863
|
+
}
|
|
1864
|
+
// Legacy intents (remove these problematic ones)
|
|
1865
|
+
// if (lowerInstruction.includes('translate')) intents.push('translation');
|
|
1866
|
+
return intents;
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Extract data flow from instruction
|
|
1870
|
+
*/
|
|
1871
|
+
extractDataFlow(instruction) {
|
|
1872
|
+
const flow = [];
|
|
1873
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
1874
|
+
// Extract logical flow based on content
|
|
1875
|
+
if (lowerInstruction.includes('trigger') || lowerInstruction.includes('when')) {
|
|
1876
|
+
flow.push('Trigger Event');
|
|
1877
|
+
}
|
|
1878
|
+
if (lowerInstruction.includes('approval')) {
|
|
1879
|
+
flow.push('Approval Process');
|
|
1880
|
+
}
|
|
1881
|
+
if (lowerInstruction.includes('task') || lowerInstruction.includes('admin')) {
|
|
1882
|
+
flow.push('Task Creation');
|
|
1883
|
+
}
|
|
1884
|
+
if (lowerInstruction.includes('pickup')) {
|
|
1885
|
+
flow.push('Pickup Scheduling');
|
|
1886
|
+
}
|
|
1887
|
+
if (lowerInstruction.includes('notification')) {
|
|
1888
|
+
flow.push('Send Notification');
|
|
1889
|
+
}
|
|
1890
|
+
if (lowerInstruction.includes('complete')) {
|
|
1891
|
+
flow.push('Complete Process');
|
|
1892
|
+
}
|
|
1893
|
+
// If no specific flow detected, use generic
|
|
1894
|
+
if (flow.length === 0) {
|
|
1895
|
+
flow.push('Process Initiated');
|
|
1896
|
+
flow.push('Execute Actions');
|
|
1897
|
+
flow.push('Process Complete');
|
|
1898
|
+
}
|
|
1899
|
+
return flow;
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Extract flow variables from activities
|
|
1903
|
+
*/
|
|
1904
|
+
extractFlowVariables(activities) {
|
|
1905
|
+
const variables = [];
|
|
1906
|
+
const seen = new Set();
|
|
1907
|
+
// Add common flow variables
|
|
1908
|
+
variables.push({
|
|
1909
|
+
name: 'current_record',
|
|
1910
|
+
type: 'reference',
|
|
1911
|
+
description: 'The current record being processed'
|
|
1912
|
+
});
|
|
1913
|
+
// Extract variables from activity outputs
|
|
1914
|
+
activities.forEach(activity => {
|
|
1915
|
+
// Check if activity has artifact with outputs
|
|
1916
|
+
if (activity.artifact && activity.artifact.outputs) {
|
|
1917
|
+
activity.artifact.outputs.forEach((output) => {
|
|
1918
|
+
const varName = `${activity.id}_${output.name}`;
|
|
1919
|
+
if (!seen.has(varName)) {
|
|
1920
|
+
seen.add(varName);
|
|
1921
|
+
variables.push({
|
|
1922
|
+
name: varName,
|
|
1923
|
+
type: output.type,
|
|
1924
|
+
description: `Output from ${activity.name}: ${output.description}`
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
else if (activity.outputs) {
|
|
1930
|
+
// Handle activities without artifacts but with outputs
|
|
1931
|
+
Object.entries(activity.outputs).forEach(([key, value]) => {
|
|
1932
|
+
const varName = key.replace('${step.', '').replace('}', '');
|
|
1933
|
+
if (!seen.has(varName)) {
|
|
1934
|
+
seen.add(varName);
|
|
1935
|
+
variables.push({
|
|
1936
|
+
name: varName,
|
|
1937
|
+
type: 'string',
|
|
1938
|
+
description: `Output from ${activity.name}`
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
});
|
|
1944
|
+
return variables;
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Deploy a flow to ServiceNow - ENHANCED WITH GLOBAL SCOPE STRATEGY
|
|
1948
|
+
*/
|
|
1949
|
+
async deployFlow(flowData) {
|
|
1950
|
+
this.logger.info('Deploying flow with global scope strategy', { name: flowData.flowStructure.name });
|
|
1951
|
+
try {
|
|
1952
|
+
// Use the simplified flow structure directly
|
|
1953
|
+
const flowDefinition = flowData.flowStructure;
|
|
1954
|
+
// Create deployment context for scope management
|
|
1955
|
+
const deploymentContext = {
|
|
1956
|
+
artifactType: 'flow',
|
|
1957
|
+
artifactData: {
|
|
1958
|
+
name: flowDefinition.name,
|
|
1959
|
+
description: flowDefinition.description,
|
|
1960
|
+
table: flowDefinition.table,
|
|
1961
|
+
trigger_type: flowDefinition.trigger?.type || flowDefinition.trigger_type,
|
|
1962
|
+
condition: flowDefinition.trigger?.condition || flowDefinition.condition || '',
|
|
1963
|
+
active: flowDefinition.active !== false,
|
|
1964
|
+
category: flowDefinition.category || 'custom',
|
|
1965
|
+
activities: flowDefinition.activities || [],
|
|
1966
|
+
// Additional metadata for scope decision
|
|
1967
|
+
complexity: this.assessFlowComplexity(flowDefinition),
|
|
1968
|
+
crossApplicationIntegration: this.hasCrossApplicationIntegration(flowDefinition),
|
|
1969
|
+
sharedUtility: this.isSharedUtility(flowDefinition)
|
|
1970
|
+
},
|
|
1971
|
+
environmentType: process.env.NODE_ENV === 'production' ? 'production' : 'development'
|
|
1972
|
+
};
|
|
1973
|
+
// Use scope manager to deploy with optimal scope
|
|
1974
|
+
const deploymentResult = await this.scopeManager.deployWithScopeManagement(deploymentContext);
|
|
1975
|
+
if (!deploymentResult.success) {
|
|
1976
|
+
throw new Error(deploymentResult.message || 'Failed to deploy flow');
|
|
1977
|
+
}
|
|
1978
|
+
// Log deployment details with scope information
|
|
1979
|
+
this.logger.info('Flow deployed successfully with scope management', {
|
|
1980
|
+
sys_id: deploymentResult.artifactId,
|
|
1981
|
+
name: flowDefinition.name,
|
|
1982
|
+
table: flowDefinition.table,
|
|
1983
|
+
scope: deploymentResult.scope,
|
|
1984
|
+
domain: deploymentResult.domain,
|
|
1985
|
+
fallbackApplied: deploymentResult.fallbackApplied
|
|
1986
|
+
});
|
|
1987
|
+
return {
|
|
1988
|
+
success: true,
|
|
1989
|
+
message: `Flow "${flowDefinition.name}" deployed successfully to ${deploymentResult.scope} scope`,
|
|
1990
|
+
data: {
|
|
1991
|
+
sys_id: deploymentResult.artifactId,
|
|
1992
|
+
name: flowDefinition.name,
|
|
1993
|
+
table: flowDefinition.table,
|
|
1994
|
+
scope: deploymentResult.scope,
|
|
1995
|
+
domain: deploymentResult.domain,
|
|
1996
|
+
url: `https://${process.env.SNOW_INSTANCE}/$flow-designer.do#/flow/${deploymentResult.artifactId}`,
|
|
1997
|
+
actions_created: flowDefinition.activities?.length || 0,
|
|
1998
|
+
scope_strategy: {
|
|
1999
|
+
selectedScope: deploymentResult.scope,
|
|
2000
|
+
fallbackApplied: deploymentResult.fallbackApplied,
|
|
2001
|
+
permissions: deploymentResult.permissions,
|
|
2002
|
+
warnings: deploymentResult.warnings
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
catch (error) {
|
|
2008
|
+
this.logger.error('Failed to deploy flow with scope management', error);
|
|
2009
|
+
return {
|
|
2010
|
+
success: false,
|
|
2011
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
/**
|
|
2016
|
+
* Assess flow complexity for scope decision
|
|
2017
|
+
*/
|
|
2018
|
+
assessFlowComplexity(flowDefinition) {
|
|
2019
|
+
const activityCount = flowDefinition.activities?.length || 0;
|
|
2020
|
+
const hasComplexLogic = flowDefinition.activities?.some((activity) => activity.type === 'script' || activity.type === 'condition' || activity.type === 'loop');
|
|
2021
|
+
const hasIntegrations = flowDefinition.activities?.some((activity) => activity.type === 'rest' || activity.type === 'soap' || activity.type === 'integration');
|
|
2022
|
+
if (activityCount > 10 || hasComplexLogic || hasIntegrations) {
|
|
2023
|
+
return 'high';
|
|
2024
|
+
}
|
|
2025
|
+
else if (activityCount > 5) {
|
|
2026
|
+
return 'medium';
|
|
2027
|
+
}
|
|
2028
|
+
else {
|
|
2029
|
+
return 'low';
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
/**
|
|
2033
|
+
* Check if flow has cross-application integration
|
|
2034
|
+
*/
|
|
2035
|
+
hasCrossApplicationIntegration(flowDefinition) {
|
|
2036
|
+
// Check for indicators of cross-application integration
|
|
2037
|
+
const integrationIndicators = [
|
|
2038
|
+
'integration',
|
|
2039
|
+
'api',
|
|
2040
|
+
'rest',
|
|
2041
|
+
'soap',
|
|
2042
|
+
'external',
|
|
2043
|
+
'cross',
|
|
2044
|
+
'global',
|
|
2045
|
+
'system'
|
|
2046
|
+
];
|
|
2047
|
+
const flowText = `${flowDefinition.name} ${flowDefinition.description || ''}`.toLowerCase();
|
|
2048
|
+
return integrationIndicators.some(indicator => flowText.includes(indicator));
|
|
2049
|
+
}
|
|
2050
|
+
/**
|
|
2051
|
+
* Check if flow is a shared utility
|
|
2052
|
+
*/
|
|
2053
|
+
isSharedUtility(flowDefinition) {
|
|
2054
|
+
// Check for indicators of shared utility
|
|
2055
|
+
const utilityIndicators = [
|
|
2056
|
+
'util',
|
|
2057
|
+
'helper',
|
|
2058
|
+
'common',
|
|
2059
|
+
'shared',
|
|
2060
|
+
'library',
|
|
2061
|
+
'global',
|
|
2062
|
+
'system'
|
|
2063
|
+
];
|
|
2064
|
+
const flowText = `${flowDefinition.name} ${flowDefinition.description || ''}`.toLowerCase();
|
|
2065
|
+
return utilityIndicators.some(indicator => flowText.includes(indicator));
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Extract customizations from instruction for template application
|
|
2069
|
+
*/
|
|
2070
|
+
extractCustomizationsFromInstruction(instruction) {
|
|
2071
|
+
const customizations = {};
|
|
2072
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
2073
|
+
// Extract specific items or entities
|
|
2074
|
+
if (lowerInstruction.includes('iphone')) {
|
|
2075
|
+
customizations.constants = {
|
|
2076
|
+
catalog_item: 'iPhone 6',
|
|
2077
|
+
item_type: 'hardware'
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
// Extract specific users or roles
|
|
2081
|
+
if (lowerInstruction.includes('admin')) {
|
|
2082
|
+
customizations.assignments = {
|
|
2083
|
+
admin_group: 'catalog_admins'
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
// Extract specific conditions
|
|
2087
|
+
const conditionMatch = lowerInstruction.match(/when\s+(.+?)(?:\s+then|\s+,|$)/);
|
|
2088
|
+
if (conditionMatch) {
|
|
2089
|
+
customizations.condition = conditionMatch[1];
|
|
2090
|
+
}
|
|
2091
|
+
// Extract specific table
|
|
2092
|
+
const tableMatch = lowerInstruction.match(/(?:on|from|for)\s+(\w+)\s+(?:table|record)/);
|
|
2093
|
+
if (tableMatch) {
|
|
2094
|
+
customizations.table = tableMatch[1];
|
|
2095
|
+
}
|
|
2096
|
+
return customizations;
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Enhance actions with subflow references
|
|
2100
|
+
*/
|
|
2101
|
+
enhanceActionsWithSubflows(actions, subflowResults) {
|
|
2102
|
+
const enhancedActions = [...actions];
|
|
2103
|
+
// Map successful subflows to actions
|
|
2104
|
+
const successfulSubflows = subflowResults.filter(r => r.success);
|
|
2105
|
+
for (const subflowResult of successfulSubflows) {
|
|
2106
|
+
if (subflowResult.subflow) {
|
|
2107
|
+
const subflowName = subflowResult.subflow.name.toLowerCase();
|
|
2108
|
+
// Find matching actions that could use this subflow
|
|
2109
|
+
const matchingActionIndex = enhancedActions.findIndex(action => {
|
|
2110
|
+
const actionName = action.name.toLowerCase();
|
|
2111
|
+
return actionName.includes(subflowName) ||
|
|
2112
|
+
subflowName.includes(actionName) ||
|
|
2113
|
+
this.actionsAreRelated(action, subflowResult.subflow);
|
|
2114
|
+
});
|
|
2115
|
+
if (matchingActionIndex !== -1) {
|
|
2116
|
+
enhancedActions[matchingActionIndex].subflow_reference = {
|
|
2117
|
+
sys_id: subflowResult.sys_id,
|
|
2118
|
+
name: subflowResult.subflow.name,
|
|
2119
|
+
url: subflowResult.url
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
return enhancedActions;
|
|
2125
|
+
}
|
|
2126
|
+
/**
|
|
2127
|
+
* Check if action and subflow are related
|
|
2128
|
+
*/
|
|
2129
|
+
actionsAreRelated(action, subflow) {
|
|
2130
|
+
// Simple heuristic - could be enhanced with more sophisticated matching
|
|
2131
|
+
const actionType = action.type.toLowerCase();
|
|
2132
|
+
const subflowName = subflow.name.toLowerCase();
|
|
2133
|
+
const relatedPairs = [
|
|
2134
|
+
['approval', 'approval'],
|
|
2135
|
+
['notification', 'notification'],
|
|
2136
|
+
['create_task', 'task'],
|
|
2137
|
+
['update_record', 'update']
|
|
2138
|
+
];
|
|
2139
|
+
return relatedPairs.some(([actionKeyword, subflowKeyword]) => actionType.includes(actionKeyword) && subflowName.includes(subflowKeyword));
|
|
2140
|
+
}
|
|
2141
|
+
/**
|
|
2142
|
+
* Generate comprehensive recommendations
|
|
2143
|
+
*/
|
|
2144
|
+
generateComprehensiveRecommendations(analysisResult, validationResult, matchingTemplates, subflowResults) {
|
|
2145
|
+
const recommendations = [];
|
|
2146
|
+
// Decision confidence recommendations
|
|
2147
|
+
if (analysisResult.confidence < 0.7) {
|
|
2148
|
+
recommendations.push('Consider providing more specific requirements to improve decision confidence');
|
|
2149
|
+
}
|
|
2150
|
+
// Complexity recommendations
|
|
2151
|
+
if (analysisResult.criteria.complexity === 'high' || analysisResult.criteria.complexity === 'very_high') {
|
|
2152
|
+
recommendations.push('Consider breaking complex logic into smaller, more manageable components');
|
|
2153
|
+
}
|
|
2154
|
+
// Reusability recommendations
|
|
2155
|
+
if (analysisResult.criteria.reusability === 'high' && analysisResult.recommendedType === 'main_flow') {
|
|
2156
|
+
recommendations.push('Consider creating reusable subflows for components that may be used elsewhere');
|
|
2157
|
+
}
|
|
2158
|
+
// Template recommendations
|
|
2159
|
+
if (matchingTemplates.length > 0 && matchingTemplates[0].confidence > 0.8) {
|
|
2160
|
+
recommendations.push(`Consider using the ${matchingTemplates[0].template.name} template for consistency`);
|
|
2161
|
+
}
|
|
2162
|
+
// Validation recommendations
|
|
2163
|
+
validationResult.recommendations.forEach(rec => {
|
|
2164
|
+
if (rec.priority === 'high') {
|
|
2165
|
+
recommendations.push(rec.message);
|
|
2166
|
+
}
|
|
2167
|
+
});
|
|
2168
|
+
// Subflow recommendations
|
|
2169
|
+
subflowResults.forEach(result => {
|
|
2170
|
+
if (result.success && result.recommendations) {
|
|
2171
|
+
recommendations.push(...result.recommendations);
|
|
2172
|
+
}
|
|
2173
|
+
});
|
|
2174
|
+
// General best practices
|
|
2175
|
+
recommendations.push('Test the flow thoroughly before deploying to production');
|
|
2176
|
+
recommendations.push('Document the flow purpose and usage for future maintenance');
|
|
2177
|
+
recommendations.push('Consider adding error handling for robust operation');
|
|
2178
|
+
return recommendations;
|
|
2179
|
+
}
|
|
2180
|
+
/**
|
|
2181
|
+
* Get intelligent flow analysis summary
|
|
2182
|
+
*/
|
|
2183
|
+
async getFlowAnalysisSummary(instruction) {
|
|
2184
|
+
try {
|
|
2185
|
+
const analysisResult = await this.decisionEngine.analyzeAndRecommend(instruction);
|
|
2186
|
+
const validationResult = this.validationEngine.validateDecision(analysisResult, instruction);
|
|
2187
|
+
const matchingTemplates = this.patternTemplates.findMatchingTemplates(instruction);
|
|
2188
|
+
return {
|
|
2189
|
+
decision: {
|
|
2190
|
+
recommendedType: analysisResult.recommendedType,
|
|
2191
|
+
confidence: analysisResult.confidence,
|
|
2192
|
+
rationale: analysisResult.rationale
|
|
2193
|
+
},
|
|
2194
|
+
complexity: analysisResult.criteria.complexity,
|
|
2195
|
+
reusability: analysisResult.criteria.reusability,
|
|
2196
|
+
context: analysisResult.criteria.context,
|
|
2197
|
+
validation: {
|
|
2198
|
+
isValid: validationResult.isValid,
|
|
2199
|
+
score: validationResult.score,
|
|
2200
|
+
maxScore: validationResult.maxScore
|
|
2201
|
+
},
|
|
2202
|
+
templates: {
|
|
2203
|
+
matchCount: matchingTemplates.length,
|
|
2204
|
+
bestMatch: matchingTemplates[0]?.template.name || null,
|
|
2205
|
+
confidence: matchingTemplates[0]?.confidence || 0
|
|
2206
|
+
},
|
|
2207
|
+
subflowCandidates: analysisResult.subflowCandidates.length,
|
|
2208
|
+
alternatives: analysisResult.alternatives
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
catch (error) {
|
|
2212
|
+
this.logger.error('Failed to get flow analysis summary', error);
|
|
2213
|
+
return {
|
|
2214
|
+
error: 'Analysis failed',
|
|
2215
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
exports.EnhancedFlowComposer = EnhancedFlowComposer;
|
|
2221
|
+
//# sourceMappingURL=flow-composer.js.map
|