@memberjunction/server 2.112.0 → 2.113.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.
Files changed (250) hide show
  1. package/dist/agents/skip-agent.d.ts +4 -4
  2. package/dist/agents/skip-agent.d.ts.map +1 -1
  3. package/dist/agents/skip-agent.js +951 -808
  4. package/dist/agents/skip-agent.js.map +1 -1
  5. package/dist/agents/skip-sdk.d.ts +1 -1
  6. package/dist/agents/skip-sdk.d.ts.map +1 -1
  7. package/dist/agents/skip-sdk.js +43 -53
  8. package/dist/agents/skip-sdk.js.map +1 -1
  9. package/dist/apolloServer/index.js +1 -1
  10. package/dist/auth/AuthProviderFactory.d.ts +1 -1
  11. package/dist/auth/AuthProviderFactory.d.ts.map +1 -1
  12. package/dist/auth/AuthProviderFactory.js +3 -1
  13. package/dist/auth/AuthProviderFactory.js.map +1 -1
  14. package/dist/auth/BaseAuthProvider.d.ts +1 -1
  15. package/dist/auth/BaseAuthProvider.d.ts.map +1 -1
  16. package/dist/auth/BaseAuthProvider.js +2 -3
  17. package/dist/auth/BaseAuthProvider.js.map +1 -1
  18. package/dist/auth/IAuthProvider.d.ts +1 -1
  19. package/dist/auth/IAuthProvider.d.ts.map +1 -1
  20. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  21. package/dist/auth/exampleNewUserSubClass.js +1 -1
  22. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  23. package/dist/auth/index.d.ts +1 -1
  24. package/dist/auth/index.d.ts.map +1 -1
  25. package/dist/auth/index.js +6 -6
  26. package/dist/auth/index.js.map +1 -1
  27. package/dist/auth/initializeProviders.js +1 -1
  28. package/dist/auth/initializeProviders.js.map +1 -1
  29. package/dist/auth/newUsers.d.ts +1 -1
  30. package/dist/auth/newUsers.d.ts.map +1 -1
  31. package/dist/auth/newUsers.js +7 -7
  32. package/dist/auth/newUsers.js.map +1 -1
  33. package/dist/auth/providers/Auth0Provider.d.ts +1 -1
  34. package/dist/auth/providers/Auth0Provider.d.ts.map +1 -1
  35. package/dist/auth/providers/Auth0Provider.js +1 -1
  36. package/dist/auth/providers/Auth0Provider.js.map +1 -1
  37. package/dist/auth/providers/CognitoProvider.d.ts +1 -1
  38. package/dist/auth/providers/CognitoProvider.d.ts.map +1 -1
  39. package/dist/auth/providers/CognitoProvider.js +6 -3
  40. package/dist/auth/providers/CognitoProvider.js.map +1 -1
  41. package/dist/auth/providers/GoogleProvider.d.ts +1 -1
  42. package/dist/auth/providers/GoogleProvider.d.ts.map +1 -1
  43. package/dist/auth/providers/GoogleProvider.js +1 -1
  44. package/dist/auth/providers/GoogleProvider.js.map +1 -1
  45. package/dist/auth/providers/MSALProvider.d.ts +1 -1
  46. package/dist/auth/providers/MSALProvider.d.ts.map +1 -1
  47. package/dist/auth/providers/MSALProvider.js +1 -1
  48. package/dist/auth/providers/MSALProvider.js.map +1 -1
  49. package/dist/auth/providers/OktaProvider.d.ts +1 -1
  50. package/dist/auth/providers/OktaProvider.d.ts.map +1 -1
  51. package/dist/auth/providers/OktaProvider.js +1 -1
  52. package/dist/auth/providers/OktaProvider.js.map +1 -1
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +10 -22
  55. package/dist/config.js.map +1 -1
  56. package/dist/context.d.ts +1 -1
  57. package/dist/context.d.ts.map +1 -1
  58. package/dist/context.js +7 -9
  59. package/dist/context.js.map +1 -1
  60. package/dist/entitySubclasses/entityPermissions.server.d.ts +1 -1
  61. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
  62. package/dist/entitySubclasses/entityPermissions.server.js +1 -1
  63. package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
  64. package/dist/generated/generated.d.ts +788 -658
  65. package/dist/generated/generated.d.ts.map +1 -1
  66. package/dist/generated/generated.js +2050 -3054
  67. package/dist/generated/generated.js.map +1 -1
  68. package/dist/generic/KeyInputOutputTypes.d.ts +1 -1
  69. package/dist/generic/KeyInputOutputTypes.d.ts.map +1 -1
  70. package/dist/generic/KeyInputOutputTypes.js +1 -1
  71. package/dist/generic/KeyInputOutputTypes.js.map +1 -1
  72. package/dist/generic/ResolverBase.d.ts +1 -1
  73. package/dist/generic/ResolverBase.d.ts.map +1 -1
  74. package/dist/generic/ResolverBase.js +10 -15
  75. package/dist/generic/ResolverBase.js.map +1 -1
  76. package/dist/generic/RunViewResolver.d.ts +1 -1
  77. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  78. package/dist/generic/RunViewResolver.js +15 -15
  79. package/dist/generic/RunViewResolver.js.map +1 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +13 -18
  82. package/dist/index.js.map +1 -1
  83. package/dist/resolvers/ActionResolver.d.ts +2 -2
  84. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  85. package/dist/resolvers/ActionResolver.js +30 -28
  86. package/dist/resolvers/ActionResolver.js.map +1 -1
  87. package/dist/resolvers/AskSkipResolver.d.ts +2 -2
  88. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  89. package/dist/resolvers/AskSkipResolver.js +50 -60
  90. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  91. package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
  92. package/dist/resolvers/ComponentRegistryResolver.js +38 -36
  93. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  94. package/dist/resolvers/CreateQueryResolver.d.ts +1 -1
  95. package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
  96. package/dist/resolvers/CreateQueryResolver.js +40 -43
  97. package/dist/resolvers/CreateQueryResolver.js.map +1 -1
  98. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  99. package/dist/resolvers/DatasetResolver.js +1 -1
  100. package/dist/resolvers/DatasetResolver.js.map +1 -1
  101. package/dist/resolvers/EntityRecordNameResolver.d.ts +1 -1
  102. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  103. package/dist/resolvers/EntityRecordNameResolver.js +1 -1
  104. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  105. package/dist/resolvers/EntityResolver.d.ts.map +1 -1
  106. package/dist/resolvers/EntityResolver.js +1 -1
  107. package/dist/resolvers/EntityResolver.js.map +1 -1
  108. package/dist/resolvers/FileCategoryResolver.js +1 -1
  109. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  110. package/dist/resolvers/FileResolver.js +1 -1
  111. package/dist/resolvers/FileResolver.js.map +1 -1
  112. package/dist/resolvers/GetDataContextDataResolver.d.ts +1 -1
  113. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  114. package/dist/resolvers/GetDataContextDataResolver.js +5 -5
  115. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  116. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  117. package/dist/resolvers/GetDataResolver.js +6 -8
  118. package/dist/resolvers/GetDataResolver.js.map +1 -1
  119. package/dist/resolvers/MergeRecordsResolver.d.ts +3 -3
  120. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  121. package/dist/resolvers/MergeRecordsResolver.js +3 -3
  122. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  123. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +1 -1
  124. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +1 -1
  125. package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
  126. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
  127. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  128. package/dist/resolvers/QueryResolver.js +11 -11
  129. package/dist/resolvers/QueryResolver.js.map +1 -1
  130. package/dist/resolvers/ReportResolver.js +1 -1
  131. package/dist/resolvers/ReportResolver.js.map +1 -1
  132. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  133. package/dist/resolvers/RunAIAgentResolver.js +28 -27
  134. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  135. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  136. package/dist/resolvers/RunAIPromptResolver.js +31 -31
  137. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  138. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  139. package/dist/resolvers/RunTemplateResolver.js +9 -9
  140. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  141. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  142. package/dist/resolvers/SqlLoggingConfigResolver.js +10 -10
  143. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  144. package/dist/resolvers/SyncDataResolver.d.ts +1 -1
  145. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  146. package/dist/resolvers/SyncDataResolver.js +14 -15
  147. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  148. package/dist/resolvers/SyncRolesUsersResolver.d.ts +1 -1
  149. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  150. package/dist/resolvers/SyncRolesUsersResolver.js +44 -48
  151. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  152. package/dist/resolvers/TaskResolver.d.ts.map +1 -1
  153. package/dist/resolvers/TaskResolver.js +7 -7
  154. package/dist/resolvers/TaskResolver.js.map +1 -1
  155. package/dist/resolvers/TransactionGroupResolver.d.ts +1 -1
  156. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  157. package/dist/resolvers/TransactionGroupResolver.js +12 -12
  158. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  159. package/dist/resolvers/UserFavoriteResolver.d.ts +1 -1
  160. package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
  161. package/dist/resolvers/UserFavoriteResolver.js +1 -1
  162. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  163. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  164. package/dist/resolvers/UserViewResolver.js.map +1 -1
  165. package/dist/rest/EntityCRUDHandler.d.ts +1 -1
  166. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  167. package/dist/rest/EntityCRUDHandler.js +16 -14
  168. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  169. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  170. package/dist/rest/RESTEndpointHandler.js +25 -23
  171. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  172. package/dist/rest/ViewOperationsHandler.d.ts +1 -1
  173. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  174. package/dist/rest/ViewOperationsHandler.js +21 -17
  175. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  176. package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
  177. package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
  178. package/dist/services/ScheduledJobsService.d.ts.map +1 -1
  179. package/dist/services/ScheduledJobsService.js +6 -4
  180. package/dist/services/ScheduledJobsService.js.map +1 -1
  181. package/dist/services/TaskOrchestrator.d.ts +1 -1
  182. package/dist/services/TaskOrchestrator.d.ts.map +1 -1
  183. package/dist/services/TaskOrchestrator.js +30 -30
  184. package/dist/services/TaskOrchestrator.js.map +1 -1
  185. package/dist/types.d.ts +3 -3
  186. package/dist/types.d.ts.map +1 -1
  187. package/dist/types.js +1 -0
  188. package/dist/types.js.map +1 -1
  189. package/dist/util.d.ts +1 -1
  190. package/dist/util.d.ts.map +1 -1
  191. package/dist/util.js +2 -2
  192. package/dist/util.js.map +1 -1
  193. package/package.json +39 -36
  194. package/src/agents/skip-agent.ts +1200 -1067
  195. package/src/agents/skip-sdk.ts +851 -877
  196. package/src/apolloServer/index.ts +2 -2
  197. package/src/auth/AuthProviderFactory.ts +14 -8
  198. package/src/auth/BaseAuthProvider.ts +4 -5
  199. package/src/auth/IAuthProvider.ts +2 -2
  200. package/src/auth/exampleNewUserSubClass.ts +2 -9
  201. package/src/auth/index.ts +26 -31
  202. package/src/auth/initializeProviders.ts +3 -3
  203. package/src/auth/newUsers.ts +134 -166
  204. package/src/auth/providers/Auth0Provider.ts +5 -5
  205. package/src/auth/providers/CognitoProvider.ts +10 -7
  206. package/src/auth/providers/GoogleProvider.ts +5 -4
  207. package/src/auth/providers/MSALProvider.ts +5 -5
  208. package/src/auth/providers/OktaProvider.ts +7 -6
  209. package/src/config.ts +54 -63
  210. package/src/context.ts +30 -42
  211. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  212. package/src/generated/generated.ts +40442 -48106
  213. package/src/generic/KeyInputOutputTypes.ts +6 -3
  214. package/src/generic/ResolverBase.ts +78 -119
  215. package/src/generic/RunViewResolver.ts +23 -27
  216. package/src/index.ts +48 -66
  217. package/src/resolvers/ActionResolver.ts +57 -46
  218. package/src/resolvers/AskSkipResolver.ts +533 -607
  219. package/src/resolvers/ComponentRegistryResolver.ts +562 -547
  220. package/src/resolvers/CreateQueryResolver.ts +655 -683
  221. package/src/resolvers/DatasetResolver.ts +6 -5
  222. package/src/resolvers/EntityCommunicationsResolver.ts +1 -1
  223. package/src/resolvers/EntityRecordNameResolver.ts +5 -9
  224. package/src/resolvers/EntityResolver.ts +7 -9
  225. package/src/resolvers/FileCategoryResolver.ts +2 -2
  226. package/src/resolvers/FileResolver.ts +4 -4
  227. package/src/resolvers/GetDataContextDataResolver.ts +118 -106
  228. package/src/resolvers/GetDataResolver.ts +205 -194
  229. package/src/resolvers/MergeRecordsResolver.ts +5 -5
  230. package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
  231. package/src/resolvers/QueryResolver.ts +78 -95
  232. package/src/resolvers/ReportResolver.ts +2 -2
  233. package/src/resolvers/RunAIAgentResolver.ts +828 -818
  234. package/src/resolvers/RunAIPromptResolver.ts +709 -693
  235. package/src/resolvers/RunTemplateResolver.ts +103 -105
  236. package/src/resolvers/SqlLoggingConfigResolver.ts +72 -69
  237. package/src/resolvers/SyncDataResolver.ts +352 -386
  238. package/src/resolvers/SyncRolesUsersResolver.ts +350 -387
  239. package/src/resolvers/TaskResolver.ts +115 -110
  240. package/src/resolvers/TransactionGroupResolver.ts +138 -143
  241. package/src/resolvers/UserFavoriteResolver.ts +8 -17
  242. package/src/resolvers/UserViewResolver.ts +12 -17
  243. package/src/rest/EntityCRUDHandler.ts +268 -291
  244. package/src/rest/RESTEndpointHandler.ts +776 -782
  245. package/src/rest/ViewOperationsHandler.ts +195 -191
  246. package/src/scheduler/LearningCycleScheduler.ts +52 -8
  247. package/src/services/ScheduledJobsService.ts +132 -129
  248. package/src/services/TaskOrchestrator.ts +776 -792
  249. package/src/types.ts +9 -15
  250. package/src/util.ts +109 -112
@@ -7,35 +7,26 @@
7
7
  */
8
8
 
9
9
  import {
10
- SkipAPIRequest,
11
- SkipAPIResponse,
12
- SkipMessage,
13
- SkipAPIAnalysisCompleteResponse,
14
- SkipAPIClarifyingQuestionResponse,
15
- SkipRequestPhase,
16
- SkipAPIRequestAPIKey,
17
- SkipQueryInfo,
18
- SkipEntityInfo,
19
- SkipEntityFieldInfo,
20
- SkipEntityFieldValueInfo,
21
- SkipEntityRelationshipInfo,
22
- SkipAPIAgentNote,
23
- SkipAPIAgentNoteType,
24
- SkipAPIArtifact,
25
- SkipAPIArtifactVersion,
26
- SkipAPIArtifactType,
10
+ SkipAPIRequest,
11
+ SkipAPIResponse,
12
+ SkipMessage,
13
+ SkipAPIAnalysisCompleteResponse,
14
+ SkipAPIClarifyingQuestionResponse,
15
+ SkipRequestPhase,
16
+ SkipAPIRequestAPIKey,
17
+ SkipQueryInfo,
18
+ SkipEntityInfo,
19
+ SkipEntityFieldInfo,
20
+ SkipEntityFieldValueInfo,
21
+ SkipEntityRelationshipInfo,
22
+ SkipAPIAgentNote,
23
+ SkipAPIAgentNoteType,
24
+ SkipAPIArtifact,
25
+ SkipAPIArtifactVersion,
26
+ SkipAPIArtifactType
27
27
  } from '@memberjunction/skip-types';
28
28
  import { DataContext } from '@memberjunction/data-context';
29
- import {
30
- UserInfo,
31
- LogStatus,
32
- LogError,
33
- Metadata,
34
- RunQuery,
35
- EntityInfo,
36
- EntityFieldInfo,
37
- EntityRelationshipInfo,
38
- } from '@memberjunction/global';
29
+ import { UserInfo, LogStatus, LogError, Metadata, RunQuery, EntityInfo, EntityFieldInfo, EntityRelationshipInfo } from '@memberjunction/core';
39
30
  import { sendPostRequest } from '../util.js';
40
31
  import { configInfo, baseUrl, publicUrl, graphqlPort, graphqlRootPath, apiKey as callbackAPIKey } from '../config.js';
41
32
  import { GetAIAPIKey } from '@memberjunction/ai';
@@ -50,125 +41,125 @@ import { take } from 'rxjs/operators';
50
41
  * Configuration options for Skip SDK
51
42
  */
52
43
  export interface SkipSDKConfig {
53
- /**
54
- * Skip API base URL (e.g., 'https://skip.memberjunction.com')
55
- */
56
- apiUrl?: string;
57
-
58
- /**
59
- * Skip API key for authentication
60
- */
61
- apiKey?: string;
62
-
63
- /**
64
- * Organization ID
65
- */
66
- organizationId?: string;
67
-
68
- /**
69
- * Optional organization context information
70
- */
71
- organizationInfo?: string;
44
+ /**
45
+ * Skip API base URL (e.g., 'https://skip.memberjunction.com')
46
+ */
47
+ apiUrl?: string;
48
+
49
+ /**
50
+ * Skip API key for authentication
51
+ */
52
+ apiKey?: string;
53
+
54
+ /**
55
+ * Organization ID
56
+ */
57
+ organizationId?: string;
58
+
59
+ /**
60
+ * Optional organization context information
61
+ */
62
+ organizationInfo?: string;
72
63
  }
73
64
 
74
65
  /**
75
66
  * Options for making a Skip API call
76
67
  */
77
68
  export interface SkipCallOptions {
78
- /**
79
- * Conversation messages (user/assistant)
80
- */
81
- messages: SkipMessage[];
82
-
83
- /**
84
- * Conversation ID for tracking
85
- */
86
- conversationId: string;
87
-
88
- /**
89
- * Data context to provide to Skip
90
- */
91
- dataContext?: DataContext;
92
-
93
- /**
94
- * Request phase (initial_request, clarify_question_response, etc.)
95
- */
96
- requestPhase?: SkipRequestPhase;
97
-
98
- /**
99
- * Context user for permissions and metadata
100
- */
101
- contextUser: UserInfo;
102
-
103
- /**
104
- * Database connection for metadata queries
105
- */
106
- dataSource: mssql.ConnectionPool;
107
-
108
- /**
109
- * Include entity metadata in request
110
- */
111
- includeEntities?: boolean;
112
-
113
- /**
114
- * Include saved queries in request
115
- */
116
- includeQueries?: boolean;
117
-
118
- /**
119
- * Include agent notes in request
120
- */
121
- includeNotes?: boolean;
122
-
123
- /**
124
- * Include agent requests in request
125
- */
126
- includeRequests?: boolean;
127
-
128
- /**
129
- * Force refresh of entity metadata cache
130
- */
131
- forceEntityRefresh?: boolean;
132
-
133
- /**
134
- * Include callback API key and access token for Skip to call back to MJ
135
- */
136
- includeCallbackAuth?: boolean;
137
-
138
- /**
139
- * Callback for streaming status updates during execution
140
- */
141
- onStatusUpdate?: (message: string, responsePhase?: string) => void;
69
+ /**
70
+ * Conversation messages (user/assistant)
71
+ */
72
+ messages: SkipMessage[];
73
+
74
+ /**
75
+ * Conversation ID for tracking
76
+ */
77
+ conversationId: string;
78
+
79
+ /**
80
+ * Data context to provide to Skip
81
+ */
82
+ dataContext?: DataContext;
83
+
84
+ /**
85
+ * Request phase (initial_request, clarify_question_response, etc.)
86
+ */
87
+ requestPhase?: SkipRequestPhase;
88
+
89
+ /**
90
+ * Context user for permissions and metadata
91
+ */
92
+ contextUser: UserInfo;
93
+
94
+ /**
95
+ * Database connection for metadata queries
96
+ */
97
+ dataSource: mssql.ConnectionPool;
98
+
99
+ /**
100
+ * Include entity metadata in request
101
+ */
102
+ includeEntities?: boolean;
103
+
104
+ /**
105
+ * Include saved queries in request
106
+ */
107
+ includeQueries?: boolean;
108
+
109
+ /**
110
+ * Include agent notes in request
111
+ */
112
+ includeNotes?: boolean;
113
+
114
+ /**
115
+ * Include agent requests in request
116
+ */
117
+ includeRequests?: boolean;
118
+
119
+ /**
120
+ * Force refresh of entity metadata cache
121
+ */
122
+ forceEntityRefresh?: boolean;
123
+
124
+ /**
125
+ * Include callback API key and access token for Skip to call back to MJ
126
+ */
127
+ includeCallbackAuth?: boolean;
128
+
129
+ /**
130
+ * Callback for streaming status updates during execution
131
+ */
132
+ onStatusUpdate?: (message: string, responsePhase?: string) => void;
142
133
  }
143
134
 
144
135
  /**
145
136
  * Result from a Skip API call
146
137
  */
147
138
  export interface SkipCallResult {
148
- /**
149
- * Whether the call was successful
150
- */
151
- success: boolean;
152
-
153
- /**
154
- * The final Skip API response
155
- */
156
- response?: SkipAPIResponse;
157
-
158
- /**
159
- * Response phase (analysis_complete, clarifying_question, status_update)
160
- */
161
- responsePhase?: string;
162
-
163
- /**
164
- * Error message if failed
165
- */
166
- error?: string;
167
-
168
- /**
169
- * All streaming responses received (including intermediate status updates)
170
- */
171
- allResponses?: any[];
139
+ /**
140
+ * Whether the call was successful
141
+ */
142
+ success: boolean;
143
+
144
+ /**
145
+ * The final Skip API response
146
+ */
147
+ response?: SkipAPIResponse;
148
+
149
+ /**
150
+ * Response phase (analysis_complete, clarifying_question, status_update)
151
+ */
152
+ responsePhase?: string;
153
+
154
+ /**
155
+ * Error message if failed
156
+ */
157
+ error?: string;
158
+
159
+ /**
160
+ * All streaming responses received (including intermediate status updates)
161
+ */
162
+ allResponses?: any[];
172
163
  }
173
164
 
174
165
  /**
@@ -176,777 +167,760 @@ export interface SkipCallResult {
176
167
  * Provides a clean interface for calling the Skip SaaS API
177
168
  */
178
169
  export class SkipSDK {
179
- private config: SkipSDKConfig;
180
-
181
- // Static cache for Skip entities (shared across all instances)
182
- private static __skipEntitiesCache$: BehaviorSubject<Promise<SkipEntityInfo[]> | null> = new BehaviorSubject<Promise<
183
- SkipEntityInfo[]
184
- > | null>(null);
185
- private static __lastRefreshTime: number = 0;
186
-
187
- constructor(config?: SkipSDKConfig) {
188
- // Use provided config or fall back to MJ server config
189
- this.config = {
190
- apiUrl: config?.apiUrl || configInfo.askSkip?.chatURL,
191
- apiKey: config?.apiKey || configInfo.askSkip?.apiKey,
192
- organizationId: config?.organizationId || configInfo.askSkip?.orgID,
193
- organizationInfo: config?.organizationInfo || configInfo.askSkip?.organizationInfo,
194
- };
195
- }
196
-
197
- /**
198
- * Call the Skip chat API
199
- */
200
- async chat(options: SkipCallOptions): Promise<SkipCallResult> {
201
- LogStatus(`[SkipSDK] Sending request to Skip API: ${this.config.apiUrl}`);
202
-
203
- try {
204
- // Build the Skip API request
205
- const skipRequest = await this.buildSkipRequest(options);
206
-
207
- // Call Skip API with streaming support
208
- const responses = await sendPostRequest(
209
- this.config.apiUrl,
210
- skipRequest,
211
- true, // useCompression
212
- this.buildHeaders(),
213
- (streamMessage: any) => {
214
- // Handle streaming status updates
215
- if (streamMessage.type === 'status_update' && options.onStatusUpdate) {
216
- const statusContent = streamMessage.value?.messages?.[0]?.content;
217
- const responsePhase = streamMessage.value?.responsePhase;
218
- if (statusContent) {
219
- options.onStatusUpdate(statusContent, responsePhase);
170
+ private config: SkipSDKConfig;
171
+
172
+ // Static cache for Skip entities (shared across all instances)
173
+ private static __skipEntitiesCache$: BehaviorSubject<Promise<SkipEntityInfo[]> | null> = new BehaviorSubject<Promise<SkipEntityInfo[]> | null>(null);
174
+ private static __lastRefreshTime: number = 0;
175
+
176
+ constructor(config?: SkipSDKConfig) {
177
+ // Use provided config or fall back to MJ server config
178
+ this.config = {
179
+ apiUrl: config?.apiUrl || configInfo.askSkip?.chatURL,
180
+ apiKey: config?.apiKey || configInfo.askSkip?.apiKey,
181
+ organizationId: config?.organizationId || configInfo.askSkip?.orgID,
182
+ organizationInfo: config?.organizationInfo || configInfo.askSkip?.organizationInfo
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Call the Skip chat API
188
+ */
189
+ async chat(options: SkipCallOptions): Promise<SkipCallResult> {
190
+ LogStatus(`[SkipSDK] Sending request to Skip API: ${this.config.apiUrl}`);
191
+
192
+ try {
193
+ // Build the Skip API request
194
+ const skipRequest = await this.buildSkipRequest(options);
195
+
196
+ // Call Skip API with streaming support
197
+ const responses = await sendPostRequest(
198
+ this.config.apiUrl,
199
+ skipRequest,
200
+ true, // useCompression
201
+ this.buildHeaders(),
202
+ (streamMessage: any) => {
203
+ // Handle streaming status updates
204
+ if (streamMessage.type === 'status_update' && options.onStatusUpdate) {
205
+ const statusContent = streamMessage.value?.messages?.[0]?.content;
206
+ const responsePhase = streamMessage.value?.responsePhase;
207
+ if (statusContent) {
208
+ options.onStatusUpdate(statusContent, responsePhase);
209
+ }
210
+ }
211
+ }
212
+ );
213
+
214
+ // The last response is the final one
215
+ if (responses && responses.length > 0) {
216
+ const finalResponse = responses[responses.length - 1].value as SkipAPIResponse;
217
+
218
+ return {
219
+ success: true,
220
+ response: finalResponse,
221
+ responsePhase: finalResponse.responsePhase,
222
+ allResponses: responses
223
+ };
224
+ } else {
225
+ return {
226
+ success: false,
227
+ error: 'No response received from Skip API'
228
+ };
220
229
  }
221
- }
222
- }
223
- );
224
230
 
225
- // The last response is the final one
226
- if (responses && responses.length > 0) {
227
- const finalResponse = responses[responses.length - 1].value as SkipAPIResponse;
231
+ } catch (error) {
232
+ LogError(`[SkipSDK] Error calling Skip API: ${error}`);
233
+ return {
234
+ success: false,
235
+ error: String(error)
236
+ };
237
+ }
238
+ }
228
239
 
229
- return {
230
- success: true,
231
- response: finalResponse,
232
- responsePhase: finalResponse.responsePhase,
233
- allResponses: responses,
240
+ /**
241
+ * Build the Skip API request object
242
+ */
243
+ private async buildSkipRequest(options: SkipCallOptions): Promise<SkipAPIRequest> {
244
+ const {
245
+ messages,
246
+ conversationId,
247
+ dataContext,
248
+ requestPhase = 'initial_request',
249
+ contextUser,
250
+ dataSource,
251
+ includeEntities = true,
252
+ includeQueries = true,
253
+ includeNotes = true,
254
+ includeRequests = false,
255
+ forceEntityRefresh = false,
256
+ includeCallbackAuth = true
257
+ } = options;
258
+
259
+ // Build base request with metadata
260
+ const baseRequest = await this.buildBaseRequest(
261
+ contextUser,
262
+ dataSource,
263
+ includeEntities,
264
+ includeQueries,
265
+ includeNotes,
266
+ includeRequests,
267
+ forceEntityRefresh,
268
+ includeCallbackAuth,
269
+ { conversationId, requestPhase }
270
+ );
271
+
272
+ // Build artifacts for this conversation
273
+ const artifacts = await this.buildArtifacts(contextUser, dataSource, conversationId);
274
+
275
+ // Process messages: filter delegation messages and enrich with metadata
276
+ const processedMessages = this.processMessages(messages);
277
+
278
+ // Construct the full Skip API request
279
+ const request: SkipAPIRequest = {
280
+ messages: processedMessages,
281
+ conversationID: conversationId,
282
+ dataContext: dataContext ? CopyScalarsAndArrays(dataContext) as DataContext : undefined,
283
+ requestPhase,
284
+ artifacts,
285
+ entities: baseRequest.entities || [],
286
+ queries: baseRequest.queries || [],
287
+ notes: baseRequest.notes,
288
+ noteTypes: baseRequest.noteTypes,
289
+ userEmail: baseRequest.userEmail,
290
+ organizationID: baseRequest.organizationID,
291
+ organizationInfo: baseRequest.organizationInfo,
292
+ apiKeys: baseRequest.apiKeys,
293
+ callingServerURL: baseRequest.callingServerURL,
294
+ callingServerAPIKey: baseRequest.callingServerAPIKey,
295
+ callingServerAccessToken: baseRequest.callingServerAccessToken
234
296
  };
235
- } else {
297
+
298
+ return request;
299
+ }
300
+
301
+ /**
302
+ * Build base request with metadata, API keys, and callback auth
303
+ */
304
+ private async buildBaseRequest(
305
+ contextUser: UserInfo,
306
+ dataSource: mssql.ConnectionPool,
307
+ includeEntities: boolean,
308
+ includeQueries: boolean,
309
+ includeNotes: boolean,
310
+ includeRequests: boolean,
311
+ forceEntityRefresh: boolean,
312
+ includeCallbackAuth: boolean,
313
+ additionalTokenInfo: any = {}
314
+ ): Promise<Partial<SkipAPIRequest>> {
315
+ const entities = includeEntities ? await this.buildEntities(dataSource, forceEntityRefresh) : [];
316
+ const queries = includeQueries ? this.buildQueries() : [];
317
+ const { notes, noteTypes } = includeNotes ? await this.buildAgentNotes(contextUser) : { notes: [], noteTypes: [] };
318
+ // Note: requests would be built here if includeRequests is true
319
+
320
+ // Setup access token for Skip callbacks if needed
321
+ let accessToken: GetDataAccessToken | undefined;
322
+ if (includeCallbackAuth) {
323
+ const tokenInfo = {
324
+ type: 'skip_api_request',
325
+ userEmail: contextUser.Email,
326
+ userName: contextUser.Name,
327
+ userID: contextUser.ID,
328
+ ...additionalTokenInfo
329
+ };
330
+
331
+ accessToken = registerAccessToken(
332
+ undefined,
333
+ 1000 * 60 * 10, // 10 minutes
334
+ tokenInfo
335
+ );
336
+ }
337
+
236
338
  return {
237
- success: false,
238
- error: 'No response received from Skip API',
339
+ entities,
340
+ queries,
341
+ notes,
342
+ noteTypes,
343
+ userEmail: contextUser.Email,
344
+ organizationID: this.config.organizationId,
345
+ organizationInfo: this.config.organizationInfo,
346
+ apiKeys: this.buildAPIKeys(),
347
+ callingServerURL: accessToken ? (publicUrl || `${baseUrl}:${graphqlPort}${graphqlRootPath}`) : undefined,
348
+ callingServerAPIKey: accessToken ? callbackAPIKey : undefined,
349
+ callingServerAccessToken: accessToken ? accessToken.Token : undefined
239
350
  };
240
- }
241
- } catch (error) {
242
- LogError(`[SkipSDK] Error calling Skip API: ${error}`);
243
- return {
244
- success: false,
245
- error: String(error),
246
- };
247
351
  }
248
- }
249
-
250
- /**
251
- * Build the Skip API request object
252
- */
253
- private async buildSkipRequest(options: SkipCallOptions): Promise<SkipAPIRequest> {
254
- const {
255
- messages,
256
- conversationId,
257
- dataContext,
258
- requestPhase = 'initial_request',
259
- contextUser,
260
- dataSource,
261
- includeEntities = true,
262
- includeQueries = true,
263
- includeNotes = true,
264
- includeRequests = false,
265
- forceEntityRefresh = false,
266
- includeCallbackAuth = true,
267
- } = options;
268
-
269
- // Build base request with metadata
270
- const baseRequest = await this.buildBaseRequest(
271
- contextUser,
272
- dataSource,
273
- includeEntities,
274
- includeQueries,
275
- includeNotes,
276
- includeRequests,
277
- forceEntityRefresh,
278
- includeCallbackAuth,
279
- { conversationId, requestPhase }
280
- );
281
-
282
- // Build artifacts for this conversation
283
- const artifacts = await this.buildArtifacts(contextUser, dataSource, conversationId);
284
-
285
- // Process messages: filter delegation messages and enrich with metadata
286
- const processedMessages = this.processMessages(messages);
287
-
288
- // Construct the full Skip API request
289
- const request: SkipAPIRequest = {
290
- messages: processedMessages,
291
- conversationID: conversationId,
292
- dataContext: dataContext ? (CopyScalarsAndArrays(dataContext) as DataContext) : undefined,
293
- requestPhase,
294
- artifacts,
295
- entities: baseRequest.entities || [],
296
- queries: baseRequest.queries || [],
297
- notes: baseRequest.notes,
298
- noteTypes: baseRequest.noteTypes,
299
- userEmail: baseRequest.userEmail,
300
- organizationID: baseRequest.organizationID,
301
- organizationInfo: baseRequest.organizationInfo,
302
- apiKeys: baseRequest.apiKeys,
303
- callingServerURL: baseRequest.callingServerURL,
304
- callingServerAPIKey: baseRequest.callingServerAPIKey,
305
- callingServerAccessToken: baseRequest.callingServerAccessToken,
306
- };
307
-
308
- return request;
309
- }
310
-
311
- /**
312
- * Build base request with metadata, API keys, and callback auth
313
- */
314
- private async buildBaseRequest(
315
- contextUser: UserInfo,
316
- dataSource: mssql.ConnectionPool,
317
- includeEntities: boolean,
318
- includeQueries: boolean,
319
- includeNotes: boolean,
320
- includeRequests: boolean,
321
- forceEntityRefresh: boolean,
322
- includeCallbackAuth: boolean,
323
- additionalTokenInfo: any = {}
324
- ): Promise<Partial<SkipAPIRequest>> {
325
- const entities = includeEntities ? await this.buildEntities(dataSource, forceEntityRefresh) : [];
326
- const queries = includeQueries ? this.buildQueries() : [];
327
- const { notes, noteTypes } = includeNotes ? await this.buildAgentNotes(contextUser) : { notes: [], noteTypes: [] };
328
- // Note: requests would be built here if includeRequests is true
329
-
330
- // Setup access token for Skip callbacks if needed
331
- let accessToken: GetDataAccessToken | undefined;
332
- if (includeCallbackAuth) {
333
- const tokenInfo = {
334
- type: 'skip_api_request',
335
- userEmail: contextUser.Email,
336
- userName: contextUser.Name,
337
- userID: contextUser.ID,
338
- ...additionalTokenInfo,
339
- };
340
-
341
- accessToken = registerAccessToken(
342
- undefined,
343
- 1000 * 60 * 10, // 10 minutes
344
- tokenInfo
345
- );
352
+
353
+ /**
354
+ * Build entity metadata for Skip
355
+ * Copied from AskSkipResolver.BuildSkipEntities - uses cached metadata with refresh logic
356
+ */
357
+ private async buildEntities(dataSource: mssql.ConnectionPool, forceRefresh: boolean, refreshIntervalMinutes: number = 15): Promise<SkipEntityInfo[]> {
358
+ try {
359
+ const now = Date.now();
360
+ const cacheExpired = (now - SkipSDK.__lastRefreshTime) > (refreshIntervalMinutes * 60 * 1000);
361
+
362
+ // If force refresh is requested OR cache expired OR cache is empty, refresh
363
+ if (forceRefresh || cacheExpired || SkipSDK.__skipEntitiesCache$.value === null) {
364
+ LogStatus(`[SkipSDK] Refreshing Skip entities cache (force: ${forceRefresh}, expired: ${cacheExpired})`);
365
+ const newData = this.refreshSkipEntities(dataSource);
366
+ SkipSDK.__skipEntitiesCache$.next(newData);
367
+ }
368
+
369
+ return SkipSDK.__skipEntitiesCache$.pipe(take(1)).toPromise();
370
+ }
371
+ catch (e) {
372
+ LogError(`[SkipSDK] buildEntities error: ${e}`);
373
+ return [];
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Build saved queries for Skip
379
+ */
380
+ private buildQueries(status: "Pending" | "In-Review" | "Approved" | "Rejected" | "Obsolete" = 'Approved'): SkipQueryInfo[] {
381
+ const md = new Metadata();
382
+ const approvedQueries = md.Queries.filter((q) => q.Status === status);
383
+
384
+ return approvedQueries.map((q) => ({
385
+ id: q.ID,
386
+ name: q.Name,
387
+ description: q.Description,
388
+ category: q.Category,
389
+ categoryPath: this.buildQueryCategoryPath(md, q.CategoryID),
390
+ sql: q.SQL,
391
+ originalSQL: q.OriginalSQL,
392
+ feedback: q.Feedback,
393
+ status: q.Status,
394
+ qualityRank: q.QualityRank,
395
+ createdAt: q.__mj_CreatedAt,
396
+ updatedAt: q.__mj_UpdatedAt,
397
+ categoryID: q.CategoryID,
398
+ embeddingVector: q.EmbeddingVector,
399
+ embeddingModelID: q.EmbeddingModelID,
400
+ embeddingModelName: q.EmbeddingModel,
401
+ fields: q.Fields.map((f) => ({
402
+ id: f.ID,
403
+ queryID: f.QueryID,
404
+ sequence: f.Sequence,
405
+ name: f.Name,
406
+ description: f.Description,
407
+ sqlBaseType: f.SQLBaseType,
408
+ sqlFullType: f.SQLFullType,
409
+ sourceEntityID: f.SourceEntityID,
410
+ sourceEntity: f.SourceEntity,
411
+ sourceFieldName: f.SourceFieldName,
412
+ isComputed: f.IsComputed,
413
+ computationDescription: f.ComputationDescription,
414
+ isSummary: f.IsSummary,
415
+ summaryDescription: f.SummaryDescription,
416
+ createdAt: f.__mj_CreatedAt,
417
+ updatedAt: f.__mj_UpdatedAt
418
+ })),
419
+ params: q.Parameters.map((p) => ({
420
+ id: p.ID,
421
+ queryID: p.QueryID,
422
+ name: p.Name,
423
+ description: p.Description,
424
+ type: p.Type,
425
+ isRequired: p.IsRequired,
426
+ // LinkedParameterName and LinkedParameterType may not exist on QueryParameterInfo
427
+ defaultValue: p.DefaultValue,
428
+ createdAt: p.__mj_CreatedAt,
429
+ updatedAt: p.__mj_UpdatedAt
430
+ }))
431
+ }));
432
+ }
433
+
434
+ /**
435
+ * Recursively build category path for a query
436
+ */
437
+ private buildQueryCategoryPath(md: Metadata, categoryID: string): string {
438
+ const cat = md.QueryCategories.find((c) => c.ID === categoryID);
439
+ if (!cat) return '';
440
+ if (!cat.ParentID) return cat.Name;
441
+ const parentPath = this.buildQueryCategoryPath(md, cat.ParentID);
442
+ return parentPath ? `${parentPath}/${cat.Name}` : cat.Name;
346
443
  }
347
444
 
348
- return {
349
- entities,
350
- queries,
351
- notes,
352
- noteTypes,
353
- userEmail: contextUser.Email,
354
- organizationID: this.config.organizationId,
355
- organizationInfo: this.config.organizationInfo,
356
- apiKeys: this.buildAPIKeys(),
357
- callingServerURL: accessToken ? publicUrl || `${baseUrl}:${graphqlPort}${graphqlRootPath}` : undefined,
358
- callingServerAPIKey: accessToken ? callbackAPIKey : undefined,
359
- callingServerAccessToken: accessToken ? accessToken.Token : undefined,
360
- };
361
- }
362
-
363
- /**
364
- * Build entity metadata for Skip
365
- * Copied from AskSkipResolver.BuildSkipEntities - uses cached metadata with refresh logic
366
- */
367
- private async buildEntities(
368
- dataSource: mssql.ConnectionPool,
369
- forceRefresh: boolean,
370
- refreshIntervalMinutes: number = 15
371
- ): Promise<SkipEntityInfo[]> {
372
- try {
373
- const now = Date.now();
374
- const cacheExpired = now - SkipSDK.__lastRefreshTime > refreshIntervalMinutes * 60 * 1000;
375
-
376
- // If force refresh is requested OR cache expired OR cache is empty, refresh
377
- if (forceRefresh || cacheExpired || SkipSDK.__skipEntitiesCache$.value === null) {
378
- LogStatus(`[SkipSDK] Refreshing Skip entities cache (force: ${forceRefresh}, expired: ${cacheExpired})`);
379
- const newData = this.refreshSkipEntities(dataSource);
380
- SkipSDK.__skipEntitiesCache$.next(newData);
381
- }
382
-
383
- return SkipSDK.__skipEntitiesCache$.pipe(take(1)).toPromise();
384
- } catch (e) {
385
- LogError(`[SkipSDK] buildEntities error: ${e}`);
386
- return [];
445
+ /**
446
+ * Build agent notes for Skip
447
+ */
448
+ private async buildAgentNotes(contextUser: UserInfo): Promise<{ notes: SkipAPIAgentNote[], noteTypes: SkipAPIAgentNoteType[] }> {
449
+ // TODO: Implement agent notes building
450
+ // This would query AIAgentNote entities and convert to SkipAPIAgentNote format
451
+ // For now, returning empty arrays
452
+ return { notes: [], noteTypes: [] };
387
453
  }
388
- }
389
-
390
- /**
391
- * Build saved queries for Skip
392
- */
393
- private buildQueries(status: 'Pending' | 'In-Review' | 'Approved' | 'Rejected' | 'Obsolete' = 'Approved'): SkipQueryInfo[] {
394
- const md = new Metadata();
395
- const approvedQueries = md.Queries.filter((q) => q.Status === status);
396
-
397
- return approvedQueries.map((q) => ({
398
- id: q.ID,
399
- name: q.Name,
400
- description: q.Description,
401
- category: q.Category,
402
- categoryPath: this.buildQueryCategoryPath(md, q.CategoryID),
403
- sql: q.SQL,
404
- originalSQL: q.OriginalSQL,
405
- feedback: q.Feedback,
406
- status: q.Status,
407
- qualityRank: q.QualityRank,
408
- createdAt: q.__mj_CreatedAt,
409
- updatedAt: q.__mj_UpdatedAt,
410
- categoryID: q.CategoryID,
411
- embeddingVector: q.EmbeddingVector,
412
- embeddingModelID: q.EmbeddingModelID,
413
- embeddingModelName: q.EmbeddingModel,
414
- fields: q.Fields.map((f) => ({
415
- id: f.ID,
416
- queryID: f.QueryID,
417
- sequence: f.Sequence,
418
- name: f.Name,
419
- description: f.Description,
420
- sqlBaseType: f.SQLBaseType,
421
- sqlFullType: f.SQLFullType,
422
- sourceEntityID: f.SourceEntityID,
423
- sourceEntity: f.SourceEntity,
424
- sourceFieldName: f.SourceFieldName,
425
- isComputed: f.IsComputed,
426
- computationDescription: f.ComputationDescription,
427
- isSummary: f.IsSummary,
428
- summaryDescription: f.SummaryDescription,
429
- createdAt: f.__mj_CreatedAt,
430
- updatedAt: f.__mj_UpdatedAt,
431
- })),
432
- params: q.Parameters.map((p) => ({
433
- id: p.ID,
434
- queryID: p.QueryID,
435
- name: p.Name,
436
- description: p.Description,
437
- type: p.Type,
438
- isRequired: p.IsRequired,
439
- // LinkedParameterName and LinkedParameterType may not exist on QueryParameterInfo
440
- defaultValue: p.DefaultValue,
441
- createdAt: p.__mj_CreatedAt,
442
- updatedAt: p.__mj_UpdatedAt,
443
- })),
444
- }));
445
- }
446
-
447
- /**
448
- * Recursively build category path for a query
449
- */
450
- private buildQueryCategoryPath(md: Metadata, categoryID: string): string {
451
- const cat = md.QueryCategories.find((c) => c.ID === categoryID);
452
- if (!cat) return '';
453
- if (!cat.ParentID) return cat.Name;
454
- const parentPath = this.buildQueryCategoryPath(md, cat.ParentID);
455
- return parentPath ? `${parentPath}/${cat.Name}` : cat.Name;
456
- }
457
-
458
- /**
459
- * Build agent notes for Skip
460
- */
461
- private async buildAgentNotes(contextUser: UserInfo): Promise<{ notes: SkipAPIAgentNote[]; noteTypes: SkipAPIAgentNoteType[] }> {
462
- // TODO: Implement agent notes building
463
- // This would query AIAgentNote entities and convert to SkipAPIAgentNote format
464
- // For now, returning empty arrays
465
- return { notes: [], noteTypes: [] };
466
- }
467
-
468
- /**
469
- * Build artifacts for a conversation using optimized query
470
- * Uses GetConversationArtifactsForAgent query which joins through ConversationDetailArtifact
471
- * to get artifacts that were outputs from Skip agent's conversation details
472
- */
473
- private async buildArtifacts(
474
- contextUser: UserInfo,
475
- dataSource: mssql.ConnectionPool,
476
- conversationId: string
477
- ): Promise<SkipAPIArtifact[]> {
478
- try {
479
- const rq = new RunQuery();
480
-
481
- // Ensure AIEngine is configured and get Skip agent ID
482
- await AIEngine.Instance.Config(false, contextUser);
483
- const skipAgent = AIEngine.Instance.GetAgentByName('Skip');
484
- const skipAgentId = skipAgent?.ID;
485
-
486
- if (!skipAgentId) {
487
- LogError('[SkipSDK] Skip agent not found in AIEngine');
488
- }
489
-
490
- // Use optimized query that replaces 4 RunView calls with 1 query
491
- // This query includes Configuration field needed for component spec extraction
492
- // Filter by Skip agent ID to only get artifacts created by Skip (not delegation agents)
493
- const result = await rq.RunQuery(
494
- {
495
- QueryName: 'GetConversationArtifactsForAgent',
496
- CategoryPath: 'MJ/Conversations',
497
- Parameters: {
498
- ConversationID: conversationId,
499
- AgentID: skipAgentId, // Filter to only artifacts created by Skip agent
500
- },
501
- },
502
- contextUser
503
- );
504
-
505
- if (!result.Success || !result.Results || result.Results.length === 0) {
506
- return [];
507
- }
508
-
509
- // Query returns flat result set: one row per artifact version
510
- // Group by ArtifactID to build SkipAPIArtifact objects with their versions
511
- const artifactMap = new Map<
512
- string,
513
- {
514
- artifact: any;
515
- artifactType: SkipAPIArtifactType;
516
- versions: SkipAPIArtifactVersion[];
454
+
455
+ /**
456
+ * Build artifacts for a conversation using optimized query
457
+ * Uses GetConversationArtifactsForAgent query which joins through ConversationDetailArtifact
458
+ * to get artifacts that were outputs from Skip agent's conversation details
459
+ */
460
+ private async buildArtifacts(contextUser: UserInfo, dataSource: mssql.ConnectionPool, conversationId: string): Promise<SkipAPIArtifact[]> {
461
+ try {
462
+ const rq = new RunQuery();
463
+
464
+ // Ensure AIEngine is configured and get Skip agent ID
465
+ await AIEngine.Instance.Config(false, contextUser);
466
+ const skipAgent = AIEngine.Instance.GetAgentByName('Skip');
467
+ const skipAgentId = skipAgent?.ID;
468
+
469
+ if (!skipAgentId) {
470
+ LogError('[SkipSDK] Skip agent not found in AIEngine');
471
+ }
472
+
473
+ // Use optimized query that replaces 4 RunView calls with 1 query
474
+ // This query includes Configuration field needed for component spec extraction
475
+ // Filter by Skip agent ID to only get artifacts created by Skip (not delegation agents)
476
+ const result = await rq.RunQuery({
477
+ QueryName: 'GetConversationArtifactsForAgent',
478
+ CategoryPath: 'MJ/Conversations',
479
+ Parameters: {
480
+ ConversationID: conversationId,
481
+ AgentID: skipAgentId // Filter to only artifacts created by Skip agent
482
+ }
483
+ }, contextUser);
484
+
485
+ if (!result.Success || !result.Results || result.Results.length === 0) {
486
+ return [];
487
+ }
488
+
489
+ // Query returns flat result set: one row per artifact version
490
+ // Group by ArtifactID to build SkipAPIArtifact objects with their versions
491
+ const artifactMap = new Map<string, {
492
+ artifact: any,
493
+ artifactType: SkipAPIArtifactType,
494
+ versions: SkipAPIArtifactVersion[]
495
+ }>();
496
+
497
+ // Process each row (represents one version)
498
+ for (const row of result.Results) {
499
+ const artifactId = row.ArtifactID;
500
+
501
+ // Initialize artifact entry if not exists
502
+ if (!artifactMap.has(artifactId)) {
503
+ // Map database sharingScope values to SkipAPIArtifact expected values
504
+ let sharingScope: 'None' | 'SpecificUsers' | 'Everyone' | 'Public' = 'None';
505
+ const dbSharingScope = (row.SharingScope || '').toLowerCase();
506
+ if (dbSharingScope === 'always' || dbSharingScope === 'everyone') {
507
+ sharingScope = 'Everyone';
508
+ } else if (dbSharingScope === 'public') {
509
+ sharingScope = 'Public';
510
+ } else if (dbSharingScope === 'specific users' || dbSharingScope === 'specificusers') {
511
+ sharingScope = 'SpecificUsers';
512
+ }
513
+
514
+ artifactMap.set(artifactId, {
515
+ artifact: {
516
+ id: artifactId,
517
+ conversationId: conversationId,
518
+ name: row.ArtifactName,
519
+ description: row.ArtifactDescription || '',
520
+ sharingScope: sharingScope,
521
+ comments: row.ArtifactComments || '',
522
+ createdAt: new Date(row.ArtifactCreatedAt),
523
+ updatedAt: new Date(row.ArtifactUpdatedAt)
524
+ },
525
+ artifactType: {
526
+ id: row.ArtifactTypeID,
527
+ name: row.ArtifactTypeName,
528
+ description: row.ArtifactTypeDescription,
529
+ contentType: row.ArtifactTypeContentType,
530
+ enabled: true,
531
+ createdAt: new Date(row.ArtifactTypeCreatedAt),
532
+ updatedAt: new Date(row.ArtifactTypeUpdatedAt)
533
+ },
534
+ versions: []
535
+ });
536
+ }
537
+
538
+ // Add this version to the artifact
539
+ const entry = artifactMap.get(artifactId)!;
540
+ entry.versions.push({
541
+ id: row.VersionID,
542
+ artifactId: artifactId,
543
+ conversationDetailID: row.ConversationDetailID, // Direct from join table!
544
+ version: row.Version,
545
+ configuration: row.Configuration || '',
546
+ content: row.Content || '',
547
+ comments: row.VersionComments || '',
548
+ createdAt: new Date(row.VersionCreatedAt),
549
+ updatedAt: new Date(row.VersionUpdatedAt)
550
+ });
551
+ }
552
+
553
+ // Convert map to SkipAPIArtifact array
554
+ const artifacts: SkipAPIArtifact[] = Array.from(artifactMap.values()).map(entry => ({
555
+ ...entry.artifact,
556
+ artifactType: entry.artifactType,
557
+ versions: entry.versions
558
+ }));
559
+
560
+ return artifacts;
561
+ } catch (error) {
562
+ LogError(`Failed to build artifacts for conversation ${conversationId}: ${error}`);
563
+ return [];
517
564
  }
518
- >();
519
-
520
- // Process each row (represents one version)
521
- for (const row of result.Results) {
522
- const artifactId = row.ArtifactID;
523
-
524
- // Initialize artifact entry if not exists
525
- if (!artifactMap.has(artifactId)) {
526
- // Map database sharingScope values to SkipAPIArtifact expected values
527
- let sharingScope: 'None' | 'SpecificUsers' | 'Everyone' | 'Public' = 'None';
528
- const dbSharingScope = (row.SharingScope || '').toLowerCase();
529
- if (dbSharingScope === 'always' || dbSharingScope === 'everyone') {
530
- sharingScope = 'Everyone';
531
- } else if (dbSharingScope === 'public') {
532
- sharingScope = 'Public';
533
- } else if (dbSharingScope === 'specific users' || dbSharingScope === 'specificusers') {
534
- sharingScope = 'SpecificUsers';
535
- }
536
-
537
- artifactMap.set(artifactId, {
538
- artifact: {
539
- id: artifactId,
540
- conversationId: conversationId,
541
- name: row.ArtifactName,
542
- description: row.ArtifactDescription || '',
543
- sharingScope: sharingScope,
544
- comments: row.ArtifactComments || '',
545
- createdAt: new Date(row.ArtifactCreatedAt),
546
- updatedAt: new Date(row.ArtifactUpdatedAt),
565
+ }
566
+
567
+ /**
568
+ * Build API keys for AI services
569
+ */
570
+ private buildAPIKeys(): SkipAPIRequestAPIKey[] {
571
+ return [
572
+ {
573
+ vendorDriverName: 'OpenAILLM',
574
+ apiKey: GetAIAPIKey('OpenAILLM')
547
575
  },
548
- artifactType: {
549
- id: row.ArtifactTypeID,
550
- name: row.ArtifactTypeName,
551
- description: row.ArtifactTypeDescription,
552
- contentType: row.ArtifactTypeContentType,
553
- enabled: true,
554
- createdAt: new Date(row.ArtifactTypeCreatedAt),
555
- updatedAt: new Date(row.ArtifactTypeUpdatedAt),
576
+ {
577
+ vendorDriverName: 'AnthropicLLM',
578
+ apiKey: GetAIAPIKey('AnthropicLLM')
556
579
  },
557
- versions: [],
558
- });
559
- }
580
+ {
581
+ vendorDriverName: 'GeminiLLM',
582
+ apiKey: GetAIAPIKey('GeminiLLM')
583
+ },
584
+ {
585
+ vendorDriverName: 'GroqLLM',
586
+ apiKey: GetAIAPIKey('GroqLLM')
587
+ },
588
+ {
589
+ vendorDriverName: 'MistralLLM',
590
+ apiKey: GetAIAPIKey('MistralLLM')
591
+ },
592
+ {
593
+ vendorDriverName: 'CerebrasLLM',
594
+ apiKey: GetAIAPIKey('CerebrasLLM')
595
+ }
596
+ ];
597
+ }
560
598
 
561
- // Add this version to the artifact
562
- const entry = artifactMap.get(artifactId)!;
563
- entry.versions.push({
564
- id: row.VersionID,
565
- artifactId: artifactId,
566
- conversationDetailID: row.ConversationDetailID, // Direct from join table!
567
- version: row.Version,
568
- configuration: row.Configuration || '',
569
- content: row.Content || '',
570
- comments: row.VersionComments || '',
571
- createdAt: new Date(row.VersionCreatedAt),
572
- updatedAt: new Date(row.VersionUpdatedAt),
573
- });
574
- }
575
-
576
- // Convert map to SkipAPIArtifact array
577
- const artifacts: SkipAPIArtifact[] = Array.from(artifactMap.values()).map((entry) => ({
578
- ...entry.artifact,
579
- artifactType: entry.artifactType,
580
- versions: entry.versions,
581
- }));
582
-
583
- return artifacts;
584
- } catch (error) {
585
- LogError(`Failed to build artifacts for conversation ${conversationId}: ${error}`);
586
- return [];
599
+ /**
600
+ * Build HTTP headers for Skip API requests
601
+ */
602
+ private buildHeaders(): Record<string, string> {
603
+ return {
604
+ 'x-api-key': this.config.apiKey || '',
605
+ 'Content-Type': 'application/json'
606
+ };
587
607
  }
588
- }
589
-
590
- /**
591
- * Build API keys for AI services
592
- */
593
- private buildAPIKeys(): SkipAPIRequestAPIKey[] {
594
- return [
595
- {
596
- vendorDriverName: 'OpenAILLM',
597
- apiKey: GetAIAPIKey('OpenAILLM'),
598
- },
599
- {
600
- vendorDriverName: 'AnthropicLLM',
601
- apiKey: GetAIAPIKey('AnthropicLLM'),
602
- },
603
- {
604
- vendorDriverName: 'GeminiLLM',
605
- apiKey: GetAIAPIKey('GeminiLLM'),
606
- },
607
- {
608
- vendorDriverName: 'GroqLLM',
609
- apiKey: GetAIAPIKey('GroqLLM'),
610
- },
611
- {
612
- vendorDriverName: 'MistralLLM',
613
- apiKey: GetAIAPIKey('MistralLLM'),
614
- },
615
- {
616
- vendorDriverName: 'CerebrasLLM',
617
- apiKey: GetAIAPIKey('CerebrasLLM'),
618
- },
619
- ];
620
- }
621
-
622
- /**
623
- * Build HTTP headers for Skip API requests
624
- */
625
- private buildHeaders(): Record<string, string> {
626
- return {
627
- 'x-api-key': this.config.apiKey || '',
628
- 'Content-Type': 'application/json',
629
- };
630
- }
631
-
632
- /**
633
- * Refreshes the Skip entities cache
634
- * Rebuilds the entity information that is provided to Skip
635
- * Copied from AskSkipResolver.refreshSkipEntities
636
- */
637
- private async refreshSkipEntities(dataSource: mssql.ConnectionPool): Promise<SkipEntityInfo[]> {
638
- try {
639
- const md = new Metadata();
640
- const skipSpecialIncludeEntities = (configInfo.askSkip?.entitiesToSend?.includeEntitiesFromExcludedSchemas ?? []).map((e) =>
641
- e.trim().toLowerCase()
642
- );
643
-
644
- // Get the list of entities
645
- const entities = md.Entities.filter((e) => {
646
- if (
647
- !configInfo.askSkip.entitiesToSend.excludeSchemas.includes(e.SchemaName) ||
648
- skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase())
649
- ) {
650
- const sd = e.ScopeDefault?.trim();
651
- if (sd && sd.length > 0) {
652
- const scopes = sd.split(',').map((s) => s.trim().toLowerCase()) ?? ['all'];
653
- return (
654
- !scopes ||
655
- scopes.length === 0 ||
656
- scopes.includes('all') ||
657
- scopes.includes('ai') ||
658
- skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase())
659
- );
660
- } else {
661
- return true; // no scope, so include it
662
- }
663
- }
664
- return false;
665
- });
666
608
 
667
- // Now we have our list of entities, pack em up
668
- const result = await Promise.all(entities.map((e) => this.packSingleSkipEntityInfo(e, dataSource)));
609
+ /**
610
+ * Refreshes the Skip entities cache
611
+ * Rebuilds the entity information that is provided to Skip
612
+ * Copied from AskSkipResolver.refreshSkipEntities
613
+ */
614
+ private async refreshSkipEntities(dataSource: mssql.ConnectionPool): Promise<SkipEntityInfo[]> {
615
+ try {
616
+ const md = new Metadata();
617
+ const skipSpecialIncludeEntities = (configInfo.askSkip?.entitiesToSend?.includeEntitiesFromExcludedSchemas ?? [])
618
+ .map((e) => e.trim().toLowerCase());
619
+
620
+ // Get the list of entities
621
+ const entities = md.Entities.filter((e) => {
622
+ if (!configInfo.askSkip.entitiesToSend.excludeSchemas.includes(e.SchemaName) ||
623
+ skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase())) {
624
+ const sd = e.ScopeDefault?.trim();
625
+ if (sd && sd.length > 0) {
626
+ const scopes = sd.split(',').map((s) => s.trim().toLowerCase()) ?? ['all'];
627
+ return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai') || skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase());
628
+ }
629
+ else {
630
+ return true; // no scope, so include it
631
+ }
632
+ }
633
+ return false;
634
+ });
635
+
636
+ // Now we have our list of entities, pack em up
637
+ const result = await Promise.all(entities.map((e) => this.packSingleSkipEntityInfo(e, dataSource)));
669
638
 
670
- SkipSDK.__lastRefreshTime = Date.now(); // Update last refresh time
671
- return result;
672
- } catch (e) {
673
- LogError(`[SkipSDK] refreshSkipEntities error: ${e}`);
674
- return [];
639
+ SkipSDK.__lastRefreshTime = Date.now(); // Update last refresh time
640
+ return result;
641
+ }
642
+ catch (e) {
643
+ LogError(`[SkipSDK] refreshSkipEntities error: ${e}`);
644
+ return [];
645
+ }
675
646
  }
676
- }
677
-
678
- /**
679
- * Packs information about a single entity for Skip
680
- * Includes fields, relationships, and sample data
681
- * Copied from AskSkipResolver.PackSingleSkipEntityInfo
682
- */
683
- private async packSingleSkipEntityInfo(e: EntityInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityInfo> {
684
- try {
685
- const ret: SkipEntityInfo = {
686
- id: e.ID,
687
- name: e.Name,
688
- schemaName: e.SchemaName,
689
- baseView: e.BaseView,
690
- description: e.Description,
691
-
692
- fields: await Promise.all(
693
- e.Fields.filter((f) => {
694
- // we want to check the scopes for the field level and make sure it is either All or AI or has both
695
- const scopes = f.ScopeDefault?.split(',').map((s) => s.trim().toLowerCase());
696
- return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai');
697
- }).map((f) => {
698
- return this.packSingleSkipEntityField(f, dataSource);
699
- })
700
- ),
701
-
702
- relatedEntities: e.RelatedEntities.map((r) => {
703
- return this.packSingleSkipEntityRelationship(r);
704
- }),
705
-
706
- rowsPacked: e.RowsToPackWithSchema,
707
- rowsSampleMethod: e.RowsToPackSampleMethod,
708
- rows: await this.packEntityRows(e, dataSource),
709
- };
710
- return ret;
711
- } catch (e) {
712
- LogError(`[SkipSDK] packSingleSkipEntityInfo error: ${e}`);
713
- return null;
647
+
648
+ /**
649
+ * Packs information about a single entity for Skip
650
+ * Includes fields, relationships, and sample data
651
+ * Copied from AskSkipResolver.PackSingleSkipEntityInfo
652
+ */
653
+ private async packSingleSkipEntityInfo(e: EntityInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityInfo> {
654
+ try {
655
+ const ret: SkipEntityInfo = {
656
+ id: e.ID,
657
+ name: e.Name,
658
+ schemaName: e.SchemaName,
659
+ baseView: e.BaseView,
660
+ description: e.Description,
661
+
662
+ fields: await Promise.all(e.Fields.filter(f => {
663
+ // we want to check the scopes for the field level and make sure it is either All or AI or has both
664
+ const scopes = f.ScopeDefault?.split(',').map((s) => s.trim().toLowerCase());
665
+ return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai');
666
+ }).map(f => {
667
+ return this.packSingleSkipEntityField(f, dataSource);
668
+ })),
669
+
670
+ relatedEntities: e.RelatedEntities.map((r) => {
671
+ return this.packSingleSkipEntityRelationship(r);
672
+ }),
673
+
674
+ rowsPacked: e.RowsToPackWithSchema,
675
+ rowsSampleMethod: e.RowsToPackSampleMethod,
676
+ rows: await this.packEntityRows(e, dataSource)
677
+ };
678
+ return ret;
679
+ }
680
+ catch (e) {
681
+ LogError(`[SkipSDK] packSingleSkipEntityInfo error: ${e}`);
682
+ return null;
683
+ }
714
684
  }
715
- }
716
-
717
- /**
718
- * Packs information about a single entity relationship
719
- * These relationships help Skip understand the data model
720
- * Copied from AskSkipResolver.PackSingleSkipEntityRelationship
721
- */
722
- private packSingleSkipEntityRelationship(r: EntityRelationshipInfo): SkipEntityRelationshipInfo {
723
- try {
724
- return {
725
- entityID: r.EntityID,
726
- relatedEntityID: r.RelatedEntityID,
727
- type: r.Type,
728
- entityKeyField: r.EntityKeyField,
729
- relatedEntityJoinField: r.RelatedEntityJoinField,
730
- joinView: r.JoinView,
731
- joinEntityJoinField: r.JoinEntityJoinField,
732
- joinEntityInverseJoinField: r.JoinEntityInverseJoinField,
733
- entity: r.Entity,
734
- entityBaseView: r.EntityBaseView,
735
- relatedEntity: r.RelatedEntity,
736
- relatedEntityBaseView: r.RelatedEntityBaseView,
737
- };
738
- } catch (e) {
739
- LogError(`[SkipSDK] packSingleSkipEntityRelationship error: ${e}`);
740
- return null;
685
+
686
+ /**
687
+ * Packs information about a single entity relationship
688
+ * These relationships help Skip understand the data model
689
+ * Copied from AskSkipResolver.PackSingleSkipEntityRelationship
690
+ */
691
+ private packSingleSkipEntityRelationship(r: EntityRelationshipInfo): SkipEntityRelationshipInfo {
692
+ try {
693
+ return {
694
+ entityID: r.EntityID,
695
+ relatedEntityID: r.RelatedEntityID,
696
+ type: r.Type,
697
+ entityKeyField: r.EntityKeyField,
698
+ relatedEntityJoinField: r.RelatedEntityJoinField,
699
+ joinView: r.JoinView,
700
+ joinEntityJoinField: r.JoinEntityJoinField,
701
+ joinEntityInverseJoinField: r.JoinEntityInverseJoinField,
702
+ entity: r.Entity,
703
+ entityBaseView: r.EntityBaseView,
704
+ relatedEntity: r.RelatedEntity,
705
+ relatedEntityBaseView: r.RelatedEntityBaseView,
706
+ };
707
+ }
708
+ catch (e) {
709
+ LogError(`[SkipSDK] packSingleSkipEntityRelationship error: ${e}`);
710
+ return null;
711
+ }
741
712
  }
742
- }
743
-
744
- /**
745
- * Packs information about a single entity field
746
- * Includes metadata and possible values
747
- * Copied from AskSkipResolver.PackSingleSkipEntityField
748
- */
749
- private async packSingleSkipEntityField(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldInfo> {
750
- try {
751
- return {
752
- entityID: f.EntityID,
753
- sequence: f.Sequence,
754
- name: f.Name,
755
- displayName: f.DisplayName,
756
- category: f.Category,
757
- type: f.Type,
758
- description: f.Description,
759
- isPrimaryKey: f.IsPrimaryKey,
760
- allowsNull: f.AllowsNull,
761
- isUnique: f.IsUnique,
762
- length: f.Length,
763
- precision: f.Precision,
764
- scale: f.Scale,
765
- sqlFullType: f.SQLFullType,
766
- defaultValue: f.DefaultValue,
767
- autoIncrement: f.AutoIncrement,
768
- valueListType: f.ValueListType,
769
- extendedType: f.ExtendedType,
770
- defaultInView: f.DefaultInView,
771
- defaultColumnWidth: f.DefaultColumnWidth,
772
- isVirtual: f.IsVirtual,
773
- isNameField: f.IsNameField,
774
- relatedEntityID: f.RelatedEntityID,
775
- relatedEntityFieldName: f.RelatedEntityFieldName,
776
- relatedEntity: f.RelatedEntity,
777
- relatedEntitySchemaName: f.RelatedEntitySchemaName,
778
- relatedEntityBaseView: f.RelatedEntityBaseView,
779
- possibleValues: await this.packFieldPossibleValues(f, dataSource),
780
- };
781
- } catch (e) {
782
- LogError(`[SkipSDK] packSingleSkipEntityField error: ${e}`);
783
- return null;
713
+
714
+ /**
715
+ * Packs information about a single entity field
716
+ * Includes metadata and possible values
717
+ * Copied from AskSkipResolver.PackSingleSkipEntityField
718
+ */
719
+ private async packSingleSkipEntityField(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldInfo> {
720
+ try {
721
+ return {
722
+ entityID: f.EntityID,
723
+ sequence: f.Sequence,
724
+ name: f.Name,
725
+ displayName: f.DisplayName,
726
+ category: f.Category,
727
+ type: f.Type,
728
+ description: f.Description,
729
+ isPrimaryKey: f.IsPrimaryKey,
730
+ allowsNull: f.AllowsNull,
731
+ isUnique: f.IsUnique,
732
+ length: f.Length,
733
+ precision: f.Precision,
734
+ scale: f.Scale,
735
+ sqlFullType: f.SQLFullType,
736
+ defaultValue: f.DefaultValue,
737
+ autoIncrement: f.AutoIncrement,
738
+ valueListType: f.ValueListType,
739
+ extendedType: f.ExtendedType,
740
+ defaultInView: f.DefaultInView,
741
+ defaultColumnWidth: f.DefaultColumnWidth,
742
+ isVirtual: f.IsVirtual,
743
+ isNameField: f.IsNameField,
744
+ relatedEntityID: f.RelatedEntityID,
745
+ relatedEntityFieldName: f.RelatedEntityFieldName,
746
+ relatedEntity: f.RelatedEntity,
747
+ relatedEntitySchemaName: f.RelatedEntitySchemaName,
748
+ relatedEntityBaseView: f.RelatedEntityBaseView,
749
+ possibleValues: await this.packFieldPossibleValues(f, dataSource),
750
+ };
751
+ }
752
+ catch (e) {
753
+ LogError(`[SkipSDK] packSingleSkipEntityField error: ${e}`);
754
+ return null;
755
+ }
784
756
  }
785
- }
786
-
787
- /**
788
- * Packs entity rows (sample data)
789
- * Copied from AskSkipResolver.PackEntityRows
790
- */
791
- private async packEntityRows(e: EntityInfo, dataSource: mssql.ConnectionPool): Promise<any[]> {
792
- try {
793
- if (e.RowsToPackWithSchema === 'None') return [];
794
-
795
- // only include columns that have a scopes including either All and/or AI or have Null for ScopeDefault
796
- const fields = e.Fields.filter((f) => {
797
- const scopes = f.ScopeDefault?.split(',').map((s) => s.trim().toLowerCase());
798
- return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai');
799
- })
800
- .map((f) => `[${f.Name}]`)
801
- .join(',');
802
-
803
- // now run the query based on the row packing method
804
- let sql: string = '';
805
- switch (e.RowsToPackWithSchema) {
806
- case 'All':
807
- sql = `SELECT ${fields} FROM ${e.SchemaName}.${e.BaseView}`;
808
- break;
809
- case 'Sample':
810
- switch (e.RowsToPackSampleMethod) {
811
- case 'random':
812
- sql = `SELECT TOP ${e.RowsToPackSampleCount} ${fields} FROM [${e.SchemaName}].[${e.BaseView}] ORDER BY newid()`;
813
- break;
814
- case 'top n':
815
- const orderBy = e.RowsToPackSampleOrder ? ` ORDER BY [${e.RowsToPackSampleOrder}]` : '';
816
- sql = `SELECT TOP ${e.RowsToPackSampleCount} ${fields} FROM [${e.SchemaName}].[${e.BaseView}]${orderBy}`;
817
- break;
818
- case 'bottom n':
819
- const firstPrimaryKey = e.FirstPrimaryKey.Name;
820
- const innerOrderBy = e.RowsToPackSampleOrder ? `[${e.RowsToPackSampleOrder}]` : `[${firstPrimaryKey}] DESC`;
821
- sql = `SELECT * FROM (
757
+
758
+ /**
759
+ * Packs entity rows (sample data)
760
+ * Copied from AskSkipResolver.PackEntityRows
761
+ */
762
+ private async packEntityRows(e: EntityInfo, dataSource: mssql.ConnectionPool): Promise<any[]> {
763
+ try {
764
+ if (e.RowsToPackWithSchema === 'None')
765
+ return [];
766
+
767
+ // only include columns that have a scopes including either All and/or AI or have Null for ScopeDefault
768
+ const fields = e.Fields.filter((f) => {
769
+ const scopes = f.ScopeDefault?.split(',').map((s) => s.trim().toLowerCase());
770
+ return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai');
771
+ }).map(f => `[${f.Name}]`).join(',');
772
+
773
+ // now run the query based on the row packing method
774
+ let sql: string = '';
775
+ switch (e.RowsToPackWithSchema) {
776
+ case 'All':
777
+ sql = `SELECT ${fields} FROM ${e.SchemaName}.${e.BaseView}`;
778
+ break;
779
+ case 'Sample':
780
+ switch (e.RowsToPackSampleMethod) {
781
+ case 'random':
782
+ sql = `SELECT TOP ${e.RowsToPackSampleCount} ${fields} FROM [${e.SchemaName}].[${e.BaseView}] ORDER BY newid()`;
783
+ break;
784
+ case 'top n':
785
+ const orderBy = e.RowsToPackSampleOrder ? ` ORDER BY [${e.RowsToPackSampleOrder}]` : '';
786
+ sql = `SELECT TOP ${e.RowsToPackSampleCount} ${fields} FROM [${e.SchemaName}].[${e.BaseView}]${orderBy}`;
787
+ break;
788
+ case 'bottom n':
789
+ const firstPrimaryKey = e.FirstPrimaryKey.Name;
790
+ const innerOrderBy = e.RowsToPackSampleOrder ? `[${e.RowsToPackSampleOrder}]` : `[${firstPrimaryKey}] DESC`;
791
+ sql = `SELECT * FROM (
822
792
  SELECT TOP ${e.RowsToPackSampleCount} ${fields}
823
793
  FROM [${e.SchemaName}].[${e.BaseView}]
824
794
  ORDER BY ${innerOrderBy}
825
795
  ) sub
826
796
  ORDER BY [${firstPrimaryKey}] ASC;`;
827
- break;
828
- }
829
- }
830
- const request = new mssql.Request(dataSource);
831
- const result = await request.query(sql);
832
- if (!result || !result.recordset) {
833
- return [];
834
- } else {
835
- return result.recordset;
836
- }
837
- } catch (e) {
838
- LogError(`[SkipSDK] packEntityRows error: ${e}`);
839
- return [];
797
+ break;
798
+ }
799
+ }
800
+ const request = new mssql.Request(dataSource);
801
+ const result = await request.query(sql);
802
+ if (!result || !result.recordset) {
803
+ return [];
804
+ }
805
+ else {
806
+ return result.recordset;
807
+ }
808
+ }
809
+ catch (e) {
810
+ LogError(`[SkipSDK] packEntityRows error: ${e}`);
811
+ return [];
812
+ }
840
813
  }
841
- }
842
-
843
- /**
844
- * Packs possible values for an entity field
845
- * These values help Skip understand the domain and valid values for fields
846
- * Copied from AskSkipResolver.PackFieldPossibleValues
847
- */
848
- private async packFieldPossibleValues(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldValueInfo[]> {
849
- try {
850
- if (f.ValuesToPackWithSchema === 'None') {
851
- return []; // don't pack anything
852
- } else if (f.ValuesToPackWithSchema === 'All') {
853
- // wants ALL of the distinct values
854
- return await this.getFieldDistinctValues(f, dataSource);
855
- } else if (f.ValuesToPackWithSchema === 'Auto') {
856
- // default setting - pack based on the ValueListType
857
- if (f.ValueListTypeEnum === 'List') {
858
- // simple list of values in the Entity Field Values table
859
- return f.EntityFieldValues.map((v) => {
860
- return { value: v.Value, displayValue: v.Value };
861
- });
862
- } else if (f.ValueListTypeEnum === 'ListOrUserEntry') {
863
- // could be a user provided value, OR the values in the list of possible values.
864
- // get the distinct list of values from the DB and concat that with the f.EntityFieldValues array - deduped and return
865
- const values = await this.getFieldDistinctValues(f, dataSource);
866
- if (!values || values.length === 0) {
867
- // no result, just return the EntityFieldValues
868
- return f.EntityFieldValues.map((v) => {
869
- return { value: v.Value, displayValue: v.Value };
870
- });
871
- } else {
872
- return [
873
- ...new Set([
874
- ...f.EntityFieldValues.map((v) => {
875
- return { value: v.Value, displayValue: v.Value };
876
- }),
877
- ...values,
878
- ]),
879
- ];
880
- }
814
+
815
+ /**
816
+ * Packs possible values for an entity field
817
+ * These values help Skip understand the domain and valid values for fields
818
+ * Copied from AskSkipResolver.PackFieldPossibleValues
819
+ */
820
+ private async packFieldPossibleValues(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldValueInfo[]> {
821
+ try {
822
+ if (f.ValuesToPackWithSchema === 'None') {
823
+ return []; // don't pack anything
824
+ }
825
+ else if (f.ValuesToPackWithSchema === 'All') {
826
+ // wants ALL of the distinct values
827
+ return await this.getFieldDistinctValues(f, dataSource);
828
+ }
829
+ else if (f.ValuesToPackWithSchema === 'Auto') {
830
+ // default setting - pack based on the ValueListType
831
+ if (f.ValueListTypeEnum === 'List') {
832
+ // simple list of values in the Entity Field Values table
833
+ return f.EntityFieldValues.map((v) => {
834
+ return { value: v.Value, displayValue: v.Value };
835
+ });
836
+ }
837
+ else if (f.ValueListTypeEnum === 'ListOrUserEntry') {
838
+ // could be a user provided value, OR the values in the list of possible values.
839
+ // get the distinct list of values from the DB and concat that with the f.EntityFieldValues array - deduped and return
840
+ const values = await this.getFieldDistinctValues(f, dataSource);
841
+ if (!values || values.length === 0) {
842
+ // no result, just return the EntityFieldValues
843
+ return f.EntityFieldValues.map((v) => {
844
+ return { value: v.Value, displayValue: v.Value };
845
+ });
846
+ }
847
+ else {
848
+ return [...new Set([...f.EntityFieldValues.map((v) => {
849
+ return { value: v.Value, displayValue: v.Value };
850
+ }), ...values])];
851
+ }
852
+ }
853
+ }
854
+ return []; // if we get here, nothing to pack
855
+ }
856
+ catch (e) {
857
+ LogError(`[SkipSDK] packFieldPossibleValues error: ${e}`);
858
+ return [];
881
859
  }
882
- }
883
- return []; // if we get here, nothing to pack
884
- } catch (e) {
885
- LogError(`[SkipSDK] packFieldPossibleValues error: ${e}`);
886
- return [];
887
860
  }
888
- }
889
-
890
- /**
891
- * Gets distinct values for a field from the database
892
- * Used to provide Skip with information about the possible values
893
- * Copied from AskSkipResolver.GetFieldDistinctValues
894
- */
895
- private async getFieldDistinctValues(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldValueInfo[]> {
896
- try {
897
- const sql = `SELECT DISTINCT ${f.Name} FROM ${f.SchemaName}.${f.BaseView}`;
898
- const request = new mssql.Request(dataSource);
899
- const result = await request.query(sql);
900
- if (!result || !result.recordset) {
901
- return [];
902
- } else {
903
- return result.recordset.map((r) => {
904
- return {
905
- value: r[f.Name],
906
- displayValue: r[f.Name],
907
- };
908
- });
909
- }
910
- } catch (e) {
911
- LogError(`[SkipSDK] getFieldDistinctValues error: ${e}`);
912
- return [];
861
+
862
+ /**
863
+ * Gets distinct values for a field from the database
864
+ * Used to provide Skip with information about the possible values
865
+ * Copied from AskSkipResolver.GetFieldDistinctValues
866
+ */
867
+ private async getFieldDistinctValues(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldValueInfo[]> {
868
+ try {
869
+ const sql = `SELECT DISTINCT ${f.Name} FROM ${f.SchemaName}.${f.BaseView}`;
870
+ const request = new mssql.Request(dataSource);
871
+ const result = await request.query(sql);
872
+ if (!result || !result.recordset) {
873
+ return [];
874
+ }
875
+ else {
876
+ return result.recordset.map((r) => {
877
+ return {
878
+ value: r[f.Name],
879
+ displayValue: r[f.Name]
880
+ };
881
+ });
882
+ }
883
+ }
884
+ catch (e) {
885
+ LogError(`[SkipSDK] getFieldDistinctValues error: ${e}`);
886
+ return [];
887
+ }
888
+ }
889
+
890
+ /**
891
+ * Process messages: filter delegation messages and add metadata fields
892
+ * Messages coming in should already have conversationDetailID if they exist in the database
893
+ */
894
+ private processMessages(messages: SkipMessage[]): SkipMessage[] {
895
+ // Filter out delegation messages (administrative messages that shouldn't go to Skip)
896
+ const filteredMessages = messages.filter(msg => !this.isDelegationMessage(msg.content));
897
+
898
+ // Enrich messages with default metadata if not already present
899
+ return filteredMessages.map(msg => ({
900
+ ...msg,
901
+ // Add default metadata fields if not already present
902
+ // Messages from DB already have conversationDetailID, temp messages get temp-X
903
+ hiddenToUser: msg.hiddenToUser ?? false,
904
+ userRating: msg.userRating ?? null,
905
+ userFeedback: msg.userFeedback ?? null,
906
+ reflectionInsights: msg.reflectionInsights ?? null,
907
+ summaryOfEarlierConveration: msg.summaryOfEarlierConveration ?? null
908
+ }));
909
+ }
910
+
911
+ /**
912
+ * Check if a message is a delegation message that should be filtered out
913
+ * Uses flexible pattern matching to detect variations of delegation messages
914
+ */
915
+ private isDelegationMessage(content: string): boolean {
916
+ if (!content) return false;
917
+
918
+ const lowerContent = content.toLowerCase();
919
+
920
+ // Check for both "delegating" or "delegate" AND "skip" in any order
921
+ const hasDelegatingOrDelegate = lowerContent.includes('delegating') || lowerContent.includes('delegate');
922
+ const hasSkip = lowerContent.includes('skip');
923
+
924
+ return hasDelegatingOrDelegate && hasSkip;
913
925
  }
914
- }
915
-
916
- /**
917
- * Process messages: filter delegation messages and add metadata fields
918
- * Messages coming in should already have conversationDetailID if they exist in the database
919
- */
920
- private processMessages(messages: SkipMessage[]): SkipMessage[] {
921
- // Filter out delegation messages (administrative messages that shouldn't go to Skip)
922
- const filteredMessages = messages.filter((msg) => !this.isDelegationMessage(msg.content));
923
-
924
- // Enrich messages with default metadata if not already present
925
- return filteredMessages.map((msg) => ({
926
- ...msg,
927
- // Add default metadata fields if not already present
928
- // Messages from DB already have conversationDetailID, temp messages get temp-X
929
- hiddenToUser: msg.hiddenToUser ?? false,
930
- userRating: msg.userRating ?? null,
931
- userFeedback: msg.userFeedback ?? null,
932
- reflectionInsights: msg.reflectionInsights ?? null,
933
- summaryOfEarlierConveration: msg.summaryOfEarlierConveration ?? null,
934
- }));
935
- }
936
-
937
- /**
938
- * Check if a message is a delegation message that should be filtered out
939
- * Uses flexible pattern matching to detect variations of delegation messages
940
- */
941
- private isDelegationMessage(content: string): boolean {
942
- if (!content) return false;
943
-
944
- const lowerContent = content.toLowerCase();
945
-
946
- // Check for both "delegating" or "delegate" AND "skip" in any order
947
- const hasDelegatingOrDelegate = lowerContent.includes('delegating') || lowerContent.includes('delegate');
948
- const hasSkip = lowerContent.includes('skip');
949
-
950
- return hasDelegatingOrDelegate && hasSkip;
951
- }
952
926
  }