@memberjunction/server 2.47.0 → 2.49.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 (81) hide show
  1. package/dist/apolloServer/TransactionPlugin.js +7 -7
  2. package/dist/apolloServer/TransactionPlugin.js.map +1 -1
  3. package/dist/auth/index.d.ts +3 -3
  4. package/dist/auth/index.d.ts.map +1 -1
  5. package/dist/auth/index.js.map +1 -1
  6. package/dist/auth/newUsers.d.ts.map +1 -1
  7. package/dist/auth/newUsers.js +2 -13
  8. package/dist/auth/newUsers.js.map +1 -1
  9. package/dist/context.d.ts +3 -3
  10. package/dist/context.d.ts.map +1 -1
  11. package/dist/context.js.map +1 -1
  12. package/dist/generated/generated.d.ts +686 -329
  13. package/dist/generated/generated.d.ts.map +1 -1
  14. package/dist/generated/generated.js +5786 -3286
  15. package/dist/generated/generated.js.map +1 -1
  16. package/dist/generic/ResolverBase.d.ts +17 -17
  17. package/dist/generic/ResolverBase.d.ts.map +1 -1
  18. package/dist/generic/ResolverBase.js +7 -5
  19. package/dist/generic/ResolverBase.js.map +1 -1
  20. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  21. package/dist/generic/RunViewResolver.js +12 -4
  22. package/dist/generic/RunViewResolver.js.map +1 -1
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +17 -14
  26. package/dist/index.js.map +1 -1
  27. package/dist/orm.d.ts +3 -3
  28. package/dist/orm.d.ts.map +1 -1
  29. package/dist/orm.js +14 -15
  30. package/dist/orm.js.map +1 -1
  31. package/dist/resolvers/AskSkipResolver.d.ts +19 -19
  32. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  33. package/dist/resolvers/AskSkipResolver.js +17 -12
  34. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  35. package/dist/resolvers/EntityResolver.d.ts +2 -1
  36. package/dist/resolvers/EntityResolver.d.ts.map +1 -1
  37. package/dist/resolvers/EntityResolver.js +6 -3
  38. package/dist/resolvers/EntityResolver.js.map +1 -1
  39. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  40. package/dist/resolvers/FileCategoryResolver.js +10 -2
  41. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  42. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  43. package/dist/resolvers/GetDataResolver.js +4 -2
  44. package/dist/resolvers/GetDataResolver.js.map +1 -1
  45. package/dist/resolvers/ReportResolver.d.ts.map +1 -1
  46. package/dist/resolvers/ReportResolver.js +5 -3
  47. package/dist/resolvers/ReportResolver.js.map +1 -1
  48. package/dist/resolvers/RunAIAgentResolver.d.ts +19 -0
  49. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -0
  50. package/dist/resolvers/RunAIAgentResolver.js +194 -0
  51. package/dist/resolvers/RunAIAgentResolver.js.map +1 -0
  52. package/dist/resolvers/UserFavoriteResolver.d.ts +2 -2
  53. package/dist/resolvers/UserViewResolver.d.ts +4 -4
  54. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  55. package/dist/resolvers/UserViewResolver.js.map +1 -1
  56. package/dist/types.d.ts +6 -6
  57. package/dist/types.d.ts.map +1 -1
  58. package/dist/types.js.map +1 -1
  59. package/dist/util.d.ts +10 -3
  60. package/dist/util.d.ts.map +1 -1
  61. package/dist/util.js +22 -3
  62. package/dist/util.js.map +1 -1
  63. package/package.json +24 -25
  64. package/src/apolloServer/TransactionPlugin.ts +8 -8
  65. package/src/auth/index.ts +4 -4
  66. package/src/auth/newUsers.ts +0 -1
  67. package/src/context.ts +2 -2
  68. package/src/generated/generated.ts +4759 -3021
  69. package/src/generic/ResolverBase.ts +23 -22
  70. package/src/generic/RunViewResolver.ts +12 -4
  71. package/src/index.ts +18 -15
  72. package/src/orm.ts +18 -17
  73. package/src/resolvers/AskSkipResolver.ts +36 -32
  74. package/src/resolvers/EntityResolver.ts +5 -2
  75. package/src/resolvers/FileCategoryResolver.ts +9 -2
  76. package/src/resolvers/GetDataResolver.ts +4 -2
  77. package/src/resolvers/ReportResolver.ts +5 -3
  78. package/src/resolvers/RunAIAgentResolver.ts +174 -0
  79. package/src/resolvers/UserViewResolver.ts +0 -1
  80. package/src/types.ts +8 -8
  81. package/src/util.ts +31 -6
@@ -6,6 +6,7 @@ import { SkipAPIAnalysisCompleteResponse } from '@memberjunction/skip-types';
6
6
  import { DataContext } from '@memberjunction/data-context';
7
7
  import { UserCache } from '@memberjunction/sqlserver-dataprovider';
8
8
  import { z } from 'zod';
9
+ import mssql from 'mssql';
9
10
 
10
11
  @ObjectType()
11
12
  export class RunReportResultType {
@@ -90,9 +91,10 @@ export class ReportResolverExtended {
90
91
  WHERE
91
92
  cd.ID='${ConversationDetailID}'`;
92
93
 
93
- const result = await dataSource.query(sql);
94
- if (!result || result.length === 0) throw new Error('Unable to retrieve converation details');
95
- const skipData = <SkipAPIAnalysisCompleteResponse>JSON.parse(result[0].Message);
94
+ const request = new mssql.Request(dataSource);
95
+ const result = await request.query(sql);
96
+ if (!result || !result.recordset || result.recordset.length === 0) throw new Error('Unable to retrieve converation details');
97
+ const skipData = <SkipAPIAnalysisCompleteResponse>JSON.parse(result.recordset[0].Message);
96
98
 
97
99
  const report = await md.GetEntityObject<ReportEntity>('Reports', u);
98
100
  report.NewRecord();
@@ -0,0 +1,174 @@
1
+ import { Resolver, Mutation, Arg, Ctx, ObjectType, Field } from 'type-graphql';
2
+ import { UserPayload } from '../types.js';
3
+ import { LogError, LogStatus, Metadata } from '@memberjunction/core';
4
+ import { AIAgentEntity } from '@memberjunction/core-entities';
5
+ import { AgentRunner } from '@memberjunction/ai-agents';
6
+ import { ResolverBase } from '../generic/ResolverBase.js';
7
+
8
+ @ObjectType()
9
+ export class AIAgentRunResult {
10
+ @Field()
11
+ success: boolean;
12
+
13
+ @Field({ nullable: true })
14
+ output?: string;
15
+
16
+ @Field({ nullable: true })
17
+ parsedResult?: string;
18
+
19
+ @Field({ nullable: true })
20
+ error?: string;
21
+
22
+ @Field({ nullable: true })
23
+ executionTimeMs?: number;
24
+
25
+ @Field({ nullable: true })
26
+ tokensUsed?: number;
27
+
28
+ @Field({ nullable: true })
29
+ agentRunId?: string;
30
+
31
+ @Field({ nullable: true })
32
+ rawResult?: string;
33
+
34
+ @Field({ nullable: true })
35
+ nextStep?: string;
36
+ }
37
+
38
+ @Resolver()
39
+ export class RunAIAgentResolver extends ResolverBase {
40
+ @Mutation(() => AIAgentRunResult)
41
+ async RunAIAgent(
42
+ @Arg('agentId') agentId: string,
43
+ @Ctx() { userPayload }: { userPayload: UserPayload },
44
+ @Arg('messages') messagesJson: string,
45
+ @Arg('data', { nullable: true }) data?: string,
46
+ @Arg('templateData', { nullable: true }) templateData?: string
47
+ ): Promise<AIAgentRunResult> {
48
+ const startTime = Date.now();
49
+
50
+ try {
51
+ LogStatus(`=== RUNNING AI AGENT FOR ID: ${agentId} ===`);
52
+
53
+ // Parse messages (required)
54
+ let parsedMessages;
55
+ try {
56
+ parsedMessages = JSON.parse(messagesJson);
57
+ if (!Array.isArray(parsedMessages)) {
58
+ throw new Error('Messages must be an array');
59
+ }
60
+ } catch (parseError) {
61
+ return {
62
+ success: false,
63
+ error: `Invalid JSON in messages: ${(parseError as Error).message}`,
64
+ executionTimeMs: Date.now() - startTime
65
+ };
66
+ }
67
+
68
+ // Parse data contexts (JSON strings)
69
+ let parsedData = {};
70
+ let parsedTemplateData = {};
71
+
72
+ if (data) {
73
+ try {
74
+ parsedData = JSON.parse(data);
75
+ } catch (parseError) {
76
+ return {
77
+ success: false,
78
+ error: `Invalid JSON in data: ${(parseError as Error).message}`,
79
+ executionTimeMs: Date.now() - startTime
80
+ };
81
+ }
82
+ }
83
+
84
+ if (templateData) {
85
+ try {
86
+ parsedTemplateData = JSON.parse(templateData);
87
+ } catch (parseError) {
88
+ return {
89
+ success: false,
90
+ error: `Invalid JSON in template data: ${(parseError as Error).message}`,
91
+ executionTimeMs: Date.now() - startTime
92
+ };
93
+ }
94
+ }
95
+
96
+ // Get current user from payload
97
+ const currentUser = this.GetUserFromPayload(userPayload);
98
+ if (!currentUser) {
99
+ return {
100
+ success: false,
101
+ error: 'Unable to determine current user',
102
+ executionTimeMs: Date.now() - startTime
103
+ };
104
+ }
105
+
106
+ const md = new Metadata();
107
+
108
+ // Load the AI agent entity
109
+ const agentEntity = await md.GetEntityObject<AIAgentEntity>('AI Agents', currentUser);
110
+ await agentEntity.Load(agentId);
111
+
112
+ if (!agentEntity.IsSaved) {
113
+ return {
114
+ success: false,
115
+ error: `AI Agent with ID ${agentId} not found`,
116
+ executionTimeMs: Date.now() - startTime
117
+ };
118
+ }
119
+
120
+ // Check if agent is active
121
+ if (agentEntity.Status !== 'Active') {
122
+ return {
123
+ success: false,
124
+ error: `AI Agent "${agentEntity.Name}" is not active (Status: ${agentEntity.Status})`,
125
+ executionTimeMs: Date.now() - startTime
126
+ };
127
+ }
128
+
129
+ // Create AI agent runner and execute
130
+ const agentRunner = new AgentRunner();
131
+
132
+ // Execute the agent with the parsed messages and optional data
133
+ const result = await agentRunner.RunAgent({
134
+ agent: agentEntity,
135
+ conversationMessages: parsedMessages,
136
+ contextUser: currentUser,
137
+ ...(Object.keys(parsedData).length > 0 && { data: parsedData }),
138
+ ...(Object.keys(parsedTemplateData).length > 0 && { templateData: parsedTemplateData })
139
+ });
140
+
141
+ const executionTime = Date.now() - startTime;
142
+
143
+ if (result.nextStep !== 'failed') {
144
+ LogStatus(`=== AI AGENT RUN COMPLETED FOR: ${agentEntity.Name} (${executionTime}ms) ===`);
145
+
146
+ return {
147
+ success: true,
148
+ output: result.rawResult,
149
+ parsedResult: typeof result.returnValue === 'string' ? result.returnValue : JSON.stringify(result.returnValue),
150
+ rawResult: result.rawResult,
151
+ executionTimeMs: executionTime,
152
+ nextStep: result.nextStep
153
+ };
154
+ } else {
155
+ LogError(`AI Agent run failed for ${agentEntity.Name}: ${result.errorMessage}`);
156
+ return {
157
+ success: false,
158
+ error: result.errorMessage,
159
+ executionTimeMs: executionTime,
160
+ nextStep: result.nextStep
161
+ };
162
+ }
163
+
164
+ } catch (error) {
165
+ const executionTime = Date.now() - startTime;
166
+ LogError(`AI Agent run failed:`, undefined, error);
167
+ return {
168
+ success: false,
169
+ error: (error as Error).message || 'Unknown error occurred',
170
+ executionTimeMs: executionTime
171
+ };
172
+ }
173
+ }
174
+ }
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { Metadata } from '@memberjunction/core';
3
3
  import { AppContext, Arg, Ctx, Int, Query, Resolver, UserPayload } from '@memberjunction/server';
4
- import { DataSource } from 'typeorm';
5
4
  import { UserView_, UserViewResolverBase } from '../generated/generated.js';
6
5
  import { UserResolver } from './UserResolver.js';
7
6
  import { UserViewEntity, UserViewEntityExtended } from '@memberjunction/core-entities';
package/src/types.ts CHANGED
@@ -2,7 +2,7 @@ import { UserInfo } from '@memberjunction/core';
2
2
  import { UserViewEntity } from '@memberjunction/core-entities';
3
3
  import { GraphQLSchema } from 'graphql';
4
4
  import { PubSubEngine } from 'type-graphql';
5
- import { DataSource, QueryRunner } from 'typeorm';
5
+ import sql from 'mssql';
6
6
  import { getSystemUser } from './auth/index.js';
7
7
  import { MJEvent, MJEventType, MJGlobal } from '@memberjunction/global';
8
8
 
@@ -19,19 +19,19 @@ export type UserPayload = {
19
19
  */
20
20
  export type AppContext = {
21
21
  /**
22
- * The default and backwards compatible data source.
22
+ * The default and backwards compatible connection pool.
23
23
  */
24
- dataSource: DataSource;
24
+ dataSource: sql.ConnectionPool;
25
25
  userPayload: UserPayload;
26
- queryRunner?: QueryRunner;
26
+ queryRunner?: sql.Request;
27
27
  /**
28
- * Array of data sources that have additional information about their intended use e.g. Admin, Read-Write, Read-Only.
28
+ * Array of connection pools that have additional information about their intended use e.g. Admin, Read-Write, Read-Only.
29
29
  */
30
30
  dataSources: DataSourceInfo[];
31
31
  };
32
32
 
33
33
  export class DataSourceInfo {
34
- dataSource: DataSource;
34
+ dataSource: sql.ConnectionPool;
35
35
  host: string;
36
36
  port: number;
37
37
  instance?: string;
@@ -39,7 +39,7 @@ export class DataSourceInfo {
39
39
  userName: string;
40
40
  type: "Admin" | "Read-Write" | "Read-Only" | "Other";
41
41
 
42
- constructor(init: {dataSource: DataSource, type: "Admin" | "Read-Write" | "Read-Only" | "Other", host: string, port: number, database: string, userName: string} ) {
42
+ constructor(init: {dataSource: sql.ConnectionPool, type: "Admin" | "Read-Write" | "Read-Only" | "Other", host: string, port: number, database: string, userName: string} ) {
43
43
  this.dataSource = init.dataSource;
44
44
  this.host = init.host;
45
45
  this.port = init.port;
@@ -56,7 +56,7 @@ export type DirectiveBuilder = {
56
56
 
57
57
  export type RunViewGenericParams = {
58
58
  viewInfo: UserViewEntity;
59
- dataSource: DataSource;
59
+ dataSource: sql.ConnectionPool;
60
60
  extraFilter: string;
61
61
  orderBy: string;
62
62
  userSearchString: string;
package/src/util.ts CHANGED
@@ -5,7 +5,7 @@ import { promisify } from 'util';
5
5
  import { URL } from 'url';
6
6
  import { z } from 'zod';
7
7
  import { DataSourceInfo } from './types';
8
- import { DataSource } from 'typeorm';
8
+ import sql from 'mssql';
9
9
 
10
10
  const gzip = promisify(gzipCallback);
11
11
 
@@ -120,17 +120,17 @@ export async function sendPostRequest(url: string, payload: any, useCompression:
120
120
  * @param options
121
121
  * @returns
122
122
  */
123
- export function GetReadOnlyDataSource(dataSources: DataSourceInfo[], options?: {allowFallbackToReadWrite: boolean}): DataSource {
123
+ export function GetReadOnlyDataSource(dataSources: DataSourceInfo[], options?: {allowFallbackToReadWrite: boolean}): sql.ConnectionPool & { query: (sql: string, params?: any) => Promise<any[]> } {
124
124
  const readOnlyDataSource = dataSources.find((ds) => ds.type === 'Read-Only');
125
125
  if (readOnlyDataSource) {
126
- return readOnlyDataSource.dataSource;
126
+ return extendConnectionPoolWithQuery(readOnlyDataSource.dataSource);
127
127
  }
128
128
  else if (!options || options.allowFallbackToReadWrite) {
129
129
  // default behavior for backward compatibility prior to MJ 2.22.3 where we introduced this functionality was to have a single
130
130
  // connection, so for back-compatability, if we don't have a read-only data source, we'll fall back to the read-write data source
131
131
  const readWriteDataSource = dataSources.find((ds) => ds.type === 'Read-Write');
132
132
  if (readWriteDataSource) {
133
- return readWriteDataSource.dataSource;
133
+ return extendConnectionPoolWithQuery(readWriteDataSource.dataSource);
134
134
  }
135
135
  }
136
136
  throw new Error('No suitable data source found');
@@ -141,10 +141,35 @@ export async function sendPostRequest(url: string, payload: any, useCompression:
141
141
  * @param dataSources
142
142
  * @returns
143
143
  */
144
- export function GetReadWriteDataSource(dataSources: DataSourceInfo[]): DataSource {
144
+ export function GetReadWriteDataSource(dataSources: DataSourceInfo[]): sql.ConnectionPool & { query: (sql: string, params?: any) => Promise<any[]> } {
145
145
  const readWriteDataSource = dataSources.find((ds) => ds.type === 'Read-Write');
146
146
  if (readWriteDataSource) {
147
- return readWriteDataSource.dataSource;
147
+ return extendConnectionPoolWithQuery(readWriteDataSource.dataSource);
148
148
  }
149
149
  throw new Error('No suitable read-write data source found');
150
+ }
151
+
152
+ /**
153
+ * Extends a ConnectionPool with a query method that returns results in the format expected by generated code
154
+ * This provides backwards compatibility with code that expects TypeORM-style query results
155
+ */
156
+ export function extendConnectionPoolWithQuery(pool: sql.ConnectionPool): sql.ConnectionPool & { query: (sql: string, params?: any) => Promise<any[]> } {
157
+ const extendedPool = pool as any;
158
+ extendedPool.query = async (sqlQuery: string, parameters?: any): Promise<any[]> => {
159
+ const request = new sql.Request(pool);
160
+ // Add parameters if provided
161
+ if (parameters) {
162
+ if (Array.isArray(parameters)) {
163
+ parameters.forEach((value, index) => {
164
+ request.input(`p${index}`, value);
165
+ });
166
+ // Replace ? with @p0, @p1, etc. in the query
167
+ let paramIndex = 0;
168
+ sqlQuery = sqlQuery.replace(/\?/g, () => `@p${paramIndex++}`);
169
+ }
170
+ }
171
+ const result = await request.query(sqlQuery);
172
+ return result.recordset || [];
173
+ };
174
+ return extendedPool;
150
175
  }