@memberjunction/server 5.11.0 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skip-sdk.d.ts.map +1 -1
- package/dist/agents/skip-sdk.js +1 -0
- package/dist/agents/skip-sdk.js.map +1 -1
- package/dist/generated/generated.d.ts +163 -0
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +897 -0
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.js +3 -3
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +121 -90
- package/dist/index.js.map +1 -1
- package/dist/middleware/BaseServerMiddleware.d.ts +103 -0
- package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -0
- package/dist/middleware/BaseServerMiddleware.js +104 -0
- package/dist/middleware/BaseServerMiddleware.js.map +1 -0
- package/dist/middleware/MJTenantFilterMiddleware.d.ts +22 -0
- package/dist/middleware/MJTenantFilterMiddleware.d.ts.map +1 -0
- package/dist/middleware/MJTenantFilterMiddleware.js +41 -0
- package/dist/middleware/MJTenantFilterMiddleware.js.map +1 -0
- package/dist/middleware/index.d.ts +3 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +3 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +8 -0
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/CreateQueryResolver.d.ts +2 -0
- package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/CreateQueryResolver.js +11 -0
- package/dist/resolvers/CreateQueryResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +16 -2
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts +2 -0
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +20 -0
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +24 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +264 -1
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/package.json +59 -59
- package/src/__tests__/bcsaas-integration.test.ts +1 -1
- package/src/agents/skip-sdk.ts +1 -0
- package/src/generated/generated.ts +623 -1
- package/src/generic/ResolverBase.ts +3 -3
- package/src/index.ts +139 -96
- package/src/middleware/BaseServerMiddleware.ts +141 -0
- package/src/middleware/MJTenantFilterMiddleware.ts +39 -0
- package/src/middleware/index.ts +2 -0
- package/src/resolvers/AdhocQueryResolver.ts +8 -0
- package/src/resolvers/CreateQueryResolver.ts +9 -0
- package/src/resolvers/GetDataResolver.ts +18 -2
- package/src/resolvers/QueryResolver.ts +18 -0
- package/src/resolvers/RunAIAgentResolver.ts +301 -2
- package/dist/hooks.d.ts +0 -65
- package/dist/hooks.d.ts.map +0 -1
- package/dist/hooks.js +0 -14
- package/dist/hooks.js.map +0 -1
- package/dist/test-dynamic-plugin.d.ts +0 -6
- package/dist/test-dynamic-plugin.d.ts.map +0 -1
- package/dist/test-dynamic-plugin.js +0 -18
- package/dist/test-dynamic-plugin.js.map +0 -1
- package/src/hooks.ts +0 -77
- package/src/test-dynamic-plugin.ts +0 -36
|
@@ -61,6 +61,12 @@ export class RunQueryResultType {
|
|
|
61
61
|
@Field()
|
|
62
62
|
TotalRowCount: number;
|
|
63
63
|
|
|
64
|
+
@Field(() => Int, { nullable: true })
|
|
65
|
+
PageNumber?: number;
|
|
66
|
+
|
|
67
|
+
@Field(() => Int, { nullable: true })
|
|
68
|
+
PageSize?: number;
|
|
69
|
+
|
|
64
70
|
@Field()
|
|
65
71
|
ExecutionTime: number;
|
|
66
72
|
|
|
@@ -233,6 +239,8 @@ export class RunQueryResolver extends ResolverBase {
|
|
|
233
239
|
ExecutionTime: result.ExecutionTime ?? 0,
|
|
234
240
|
ErrorMessage: result.ErrorMessage || '',
|
|
235
241
|
AppliedParameters: result.AppliedParameters ? JSON.stringify(result.AppliedParameters) : undefined,
|
|
242
|
+
PageNumber: result.PageNumber,
|
|
243
|
+
PageSize: result.PageSize,
|
|
236
244
|
CacheHit: (result as any).CacheHit,
|
|
237
245
|
CacheTTLRemaining: (result as any).CacheTTLRemaining
|
|
238
246
|
};
|
|
@@ -276,6 +284,8 @@ export class RunQueryResolver extends ResolverBase {
|
|
|
276
284
|
ExecutionTime: result.ExecutionTime ?? 0,
|
|
277
285
|
ErrorMessage: result.ErrorMessage || '',
|
|
278
286
|
AppliedParameters: result.AppliedParameters ? JSON.stringify(result.AppliedParameters) : undefined,
|
|
287
|
+
PageNumber: result.PageNumber,
|
|
288
|
+
PageSize: result.PageSize,
|
|
279
289
|
CacheHit: (result as any).CacheHit,
|
|
280
290
|
CacheTTLRemaining: (result as any).CacheTTLRemaining
|
|
281
291
|
};
|
|
@@ -332,6 +342,8 @@ export class RunQueryResolver extends ResolverBase {
|
|
|
332
342
|
ExecutionTime: result.ExecutionTime ?? 0,
|
|
333
343
|
ErrorMessage: result.ErrorMessage || '',
|
|
334
344
|
AppliedParameters: result.AppliedParameters ? JSON.stringify(result.AppliedParameters) : undefined,
|
|
345
|
+
PageNumber: result.PageNumber,
|
|
346
|
+
PageSize: result.PageSize,
|
|
335
347
|
CacheHit: (result as any).CacheHit,
|
|
336
348
|
CacheTTLRemaining: (result as any).CacheTTLRemaining
|
|
337
349
|
};
|
|
@@ -374,6 +386,8 @@ export class RunQueryResolver extends ResolverBase {
|
|
|
374
386
|
ExecutionTime: result.ExecutionTime ?? 0,
|
|
375
387
|
ErrorMessage: result.ErrorMessage || '',
|
|
376
388
|
AppliedParameters: result.AppliedParameters ? JSON.stringify(result.AppliedParameters) : undefined,
|
|
389
|
+
PageNumber: result.PageNumber,
|
|
390
|
+
PageSize: result.PageSize,
|
|
377
391
|
CacheHit: (result as any).CacheHit,
|
|
378
392
|
CacheTTLRemaining: (result as any).CacheTTLRemaining
|
|
379
393
|
};
|
|
@@ -424,6 +438,8 @@ export class RunQueryResolver extends ResolverBase {
|
|
|
424
438
|
ExecutionTime: result.ExecutionTime ?? 0,
|
|
425
439
|
ErrorMessage: result.ErrorMessage || '',
|
|
426
440
|
AppliedParameters: result.AppliedParameters ? JSON.stringify(result.AppliedParameters) : undefined,
|
|
441
|
+
PageNumber: result.PageNumber,
|
|
442
|
+
PageSize: result.PageSize,
|
|
427
443
|
CacheHit: (result as Record<string, unknown>).CacheHit as boolean | undefined,
|
|
428
444
|
CacheTTLRemaining: (result as Record<string, unknown>).CacheTTLRemaining as number | undefined
|
|
429
445
|
};
|
|
@@ -471,6 +487,8 @@ export class RunQueryResolver extends ResolverBase {
|
|
|
471
487
|
ExecutionTime: result.ExecutionTime ?? 0,
|
|
472
488
|
ErrorMessage: result.ErrorMessage || '',
|
|
473
489
|
AppliedParameters: result.AppliedParameters ? JSON.stringify(result.AppliedParameters) : undefined,
|
|
490
|
+
PageNumber: result.PageNumber,
|
|
491
|
+
PageSize: result.PageSize,
|
|
474
492
|
CacheHit: (result as Record<string, unknown>).CacheHit as boolean | undefined,
|
|
475
493
|
CacheTTLRemaining: (result as Record<string, unknown>).CacheTTLRemaining as number | undefined
|
|
476
494
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, Subscription, Root, ResolverFilterData, ID, Int } from 'type-graphql';
|
|
2
2
|
import { AppContext, UserPayload } from '../types.js';
|
|
3
3
|
import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo } from '@memberjunction/core';
|
|
4
|
-
import { MJConversationDetailEntity, MJConversationDetailAttachmentEntity } from '@memberjunction/core-entities';
|
|
4
|
+
import { MJConversationDetailEntity, MJConversationDetailAttachmentEntity, MJAIAgentRequestEntity } from '@memberjunction/core-entities';
|
|
5
5
|
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
6
6
|
import { MJAIAgentEntityExtended, MJAIAgentRunEntityExtended, ExecuteAgentResult, ConversationUtility, AttachmentData } from '@memberjunction/ai-core-plus';
|
|
7
7
|
import { AIEngine } from '@memberjunction/aiengine';
|
|
@@ -154,7 +154,8 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
154
154
|
errorMessage: result.agentRun?.ErrorMessage,
|
|
155
155
|
finalStep: result.agentRun?.FinalStep,
|
|
156
156
|
cancelled: result.agentRun?.Status === 'Cancelled',
|
|
157
|
-
cancellationReason: result.agentRun?.CancellationReason
|
|
157
|
+
cancellationReason: result.agentRun?.CancellationReason,
|
|
158
|
+
feedbackRequestId: result.feedbackRequestId
|
|
158
159
|
};
|
|
159
160
|
|
|
160
161
|
// Safely extract agent run data using GetAll() for proper serialization
|
|
@@ -433,6 +434,21 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
433
434
|
|
|
434
435
|
const executionTime = Date.now() - startTime;
|
|
435
436
|
|
|
437
|
+
// Sync feedback request if this is a continuation run (user responded via conversation)
|
|
438
|
+
if (lastRunId && result.agentRun?.ID) {
|
|
439
|
+
await this.syncFeedbackRequestFromConversation(
|
|
440
|
+
lastRunId,
|
|
441
|
+
result.agentRun.ID,
|
|
442
|
+
userMessage,
|
|
443
|
+
currentUser
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Send notification if agent created a feedback request (Chat step)
|
|
448
|
+
if (result.feedbackRequestId) {
|
|
449
|
+
await this.sendFeedbackRequestNotification(result, currentUser, pubSub, userPayload);
|
|
450
|
+
}
|
|
451
|
+
|
|
436
452
|
// Create notification if enabled and artifact was created successfully
|
|
437
453
|
if (createNotification && result.success && artifactInfo && artifactInfo.artifactId && artifactInfo.versionId && artifactInfo.versionNumber) {
|
|
438
454
|
await this.createCompletionNotification(
|
|
@@ -752,6 +768,112 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
752
768
|
}
|
|
753
769
|
}
|
|
754
770
|
|
|
771
|
+
/**
|
|
772
|
+
* When a continuation run completes (lastRunId was provided), sync the corresponding
|
|
773
|
+
* AIAgentRequest by marking it as responded. This keeps the dashboard accurate when
|
|
774
|
+
* users respond to Chat steps via the conversation UI.
|
|
775
|
+
*
|
|
776
|
+
* Called server-side in the resolver so the conversation UI doesn't need any changes.
|
|
777
|
+
*/
|
|
778
|
+
private async syncFeedbackRequestFromConversation(
|
|
779
|
+
lastRunId: string,
|
|
780
|
+
newRunId: string,
|
|
781
|
+
userMessage: string | undefined,
|
|
782
|
+
contextUser: UserInfo
|
|
783
|
+
): Promise<void> {
|
|
784
|
+
try {
|
|
785
|
+
const rv = new RunView();
|
|
786
|
+
const result = await rv.RunView<MJAIAgentRequestEntity>({
|
|
787
|
+
EntityName: 'MJ: AI Agent Requests',
|
|
788
|
+
ExtraFilter: `OriginatingAgentRunID='${lastRunId}' AND Status='Requested'`,
|
|
789
|
+
MaxRows: 1,
|
|
790
|
+
ResultType: 'entity_object'
|
|
791
|
+
}, contextUser);
|
|
792
|
+
|
|
793
|
+
if (!result.Success || !result.Results || result.Results.length === 0) {
|
|
794
|
+
return; // No pending request for this run — normal for non-Chat continuations
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const request = result.Results[0];
|
|
798
|
+
request.Status = 'Responded';
|
|
799
|
+
request.RespondedAt = new Date();
|
|
800
|
+
request.ResponseByUserID = contextUser.ID;
|
|
801
|
+
request.ResumingAgentRunID = newRunId;
|
|
802
|
+
if (userMessage) {
|
|
803
|
+
request.Response = userMessage;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const saved = await request.Save();
|
|
807
|
+
if (saved) {
|
|
808
|
+
LogStatus(`📋 Synced feedback request ${request.ID} → Responded (via conversation)`);
|
|
809
|
+
} else {
|
|
810
|
+
LogError(`Failed to save feedback request sync for ${request.ID}`);
|
|
811
|
+
}
|
|
812
|
+
} catch (error) {
|
|
813
|
+
// Don't let sync failure break the agent execution
|
|
814
|
+
LogError(`Error syncing feedback request: ${(error as Error).message}`);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Sends a notification when an agent creates a feedback request (Chat step).
|
|
820
|
+
* Called after execution completes if the result contains a feedbackRequestId.
|
|
821
|
+
*/
|
|
822
|
+
private async sendFeedbackRequestNotification(
|
|
823
|
+
result: ExecuteAgentResult,
|
|
824
|
+
contextUser: UserInfo,
|
|
825
|
+
pubSub: PubSubEngine,
|
|
826
|
+
userPayload: UserPayload
|
|
827
|
+
): Promise<void> {
|
|
828
|
+
if (!result.feedbackRequestId) {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
try {
|
|
833
|
+
// Get agent name
|
|
834
|
+
await AIEngine.Instance.Config(false, contextUser);
|
|
835
|
+
const agent = AIEngine.Instance.Agents.find(a => UUIDsEqual(a.ID, result.agentRun?.AgentID));
|
|
836
|
+
const agentName = agent?.Name || 'Agent';
|
|
837
|
+
|
|
838
|
+
// Truncate message for notification
|
|
839
|
+
const message = result.agentRun?.Message || 'Agent needs your input';
|
|
840
|
+
const truncatedMessage = message.length > 200 ? message.substring(0, 197) + '...' : message;
|
|
841
|
+
|
|
842
|
+
const notificationEngine = NotificationEngine.Instance;
|
|
843
|
+
await notificationEngine.Config(false, contextUser);
|
|
844
|
+
const notifResult = await notificationEngine.SendNotification({
|
|
845
|
+
userId: contextUser.ID,
|
|
846
|
+
typeNameOrId: 'Agent Feedback Request',
|
|
847
|
+
title: `${agentName} needs your input`,
|
|
848
|
+
message: truncatedMessage,
|
|
849
|
+
resourceConfiguration: {
|
|
850
|
+
type: 'agent-request',
|
|
851
|
+
requestId: result.feedbackRequestId
|
|
852
|
+
}
|
|
853
|
+
}, contextUser);
|
|
854
|
+
|
|
855
|
+
if (notifResult.success && notifResult.inAppNotificationId) {
|
|
856
|
+
LogStatus(`📬 Feedback request notification sent (ID: ${notifResult.inAppNotificationId})`);
|
|
857
|
+
|
|
858
|
+
// Publish real-time notification event
|
|
859
|
+
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
860
|
+
userPayload: JSON.stringify(userPayload),
|
|
861
|
+
message: JSON.stringify({
|
|
862
|
+
type: 'notification',
|
|
863
|
+
notificationId: notifResult.inAppNotificationId,
|
|
864
|
+
action: 'create',
|
|
865
|
+
title: `${agentName} needs your input`,
|
|
866
|
+
message: truncatedMessage
|
|
867
|
+
})
|
|
868
|
+
});
|
|
869
|
+
} else if (!notifResult.success) {
|
|
870
|
+
LogError(`Feedback request notification failed: ${notifResult.errors?.join(', ')}`);
|
|
871
|
+
}
|
|
872
|
+
} catch (error) {
|
|
873
|
+
LogError(`Error sending feedback request notification: ${(error as Error).message}`);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
755
877
|
/**
|
|
756
878
|
* Optimized mutation that loads conversation history server-side.
|
|
757
879
|
* This avoids sending large attachment data from client to server.
|
|
@@ -854,6 +976,183 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
854
976
|
}
|
|
855
977
|
}
|
|
856
978
|
|
|
979
|
+
/**
|
|
980
|
+
* Respond to a pending AIAgentRequest from the dashboard or API.
|
|
981
|
+
* Updates the request record with the response and optionally spawns a
|
|
982
|
+
* new agent run to resume execution with the human's input.
|
|
983
|
+
*/
|
|
984
|
+
@Mutation(() => AIAgentRunResult)
|
|
985
|
+
async RespondToAgentRequest(
|
|
986
|
+
@Arg('requestId') requestId: string,
|
|
987
|
+
@Arg('status') status: string,
|
|
988
|
+
@Ctx() { userPayload, providers, dataSource }: AppContext,
|
|
989
|
+
@Arg('response', { nullable: true }) response?: string,
|
|
990
|
+
@Arg('responseData', { nullable: true }) responseData?: string,
|
|
991
|
+
@Arg('resumeAgent', { nullable: true }) resumeAgent?: boolean
|
|
992
|
+
): Promise<AIAgentRunResult> {
|
|
993
|
+
const startTime = Date.now();
|
|
994
|
+
try {
|
|
995
|
+
const currentUser = this.GetUserFromPayload(userPayload);
|
|
996
|
+
if (!currentUser) {
|
|
997
|
+
throw new Error('Unable to determine current user');
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
const md = new Metadata();
|
|
1001
|
+
const request = await md.GetEntityObject<MJAIAgentRequestEntity>(
|
|
1002
|
+
'MJ: AI Agent Requests',
|
|
1003
|
+
currentUser
|
|
1004
|
+
);
|
|
1005
|
+
if (!(await request.Load(requestId))) {
|
|
1006
|
+
throw new Error(`Agent request ${requestId} not found`);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
if (request.Status !== 'Requested') {
|
|
1010
|
+
throw new Error(`Request ${requestId} is already ${request.Status}, cannot respond`);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// Validate status
|
|
1014
|
+
const validStatuses = ['Approved', 'Rejected', 'Responded'];
|
|
1015
|
+
if (!validStatuses.includes(status)) {
|
|
1016
|
+
throw new Error(`Invalid status "${status}". Must be one of: ${validStatuses.join(', ')}`);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// Update the request
|
|
1020
|
+
request.Status = status as 'Approved' | 'Rejected' | 'Responded';
|
|
1021
|
+
request.Response = response || null;
|
|
1022
|
+
request.ResponseData = responseData || null;
|
|
1023
|
+
request.RespondedAt = new Date();
|
|
1024
|
+
request.ResponseByUserID = currentUser.ID;
|
|
1025
|
+
|
|
1026
|
+
const saved = await request.Save();
|
|
1027
|
+
if (!saved) {
|
|
1028
|
+
throw new Error(`Failed to save response for request ${requestId}`);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
LogStatus(`📋 Agent request ${requestId} → ${status} by ${currentUser.Email || currentUser.ID}`);
|
|
1032
|
+
|
|
1033
|
+
const executionTime = Date.now() - startTime;
|
|
1034
|
+
return {
|
|
1035
|
+
success: true,
|
|
1036
|
+
executionTimeMs: executionTime,
|
|
1037
|
+
result: JSON.stringify({
|
|
1038
|
+
success: true,
|
|
1039
|
+
requestId: requestId,
|
|
1040
|
+
status: status,
|
|
1041
|
+
resumed: false
|
|
1042
|
+
})
|
|
1043
|
+
};
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
const executionTime = Date.now() - startTime;
|
|
1046
|
+
const errorMessage = (error as Error).message || 'Unknown error';
|
|
1047
|
+
LogError(`RespondToAgentRequest failed: ${errorMessage}`, undefined, error);
|
|
1048
|
+
return {
|
|
1049
|
+
success: false,
|
|
1050
|
+
errorMessage,
|
|
1051
|
+
executionTimeMs: executionTime,
|
|
1052
|
+
result: JSON.stringify({ success: false, errorMessage })
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Reassign an agent request to a different user.
|
|
1059
|
+
* Updates RequestForUserID and sends a notification to the new assignee.
|
|
1060
|
+
*/
|
|
1061
|
+
@Mutation(() => AIAgentRunResult)
|
|
1062
|
+
async ReassignAgentRequest(
|
|
1063
|
+
@Arg('requestId') requestId: string,
|
|
1064
|
+
@Arg('newUserID') newUserID: string,
|
|
1065
|
+
@Ctx() { userPayload }: AppContext,
|
|
1066
|
+
@Arg('note', { nullable: true }) note?: string
|
|
1067
|
+
): Promise<AIAgentRunResult> {
|
|
1068
|
+
const startTime = Date.now();
|
|
1069
|
+
try {
|
|
1070
|
+
const currentUser = this.GetUserFromPayload(userPayload);
|
|
1071
|
+
if (!currentUser) {
|
|
1072
|
+
throw new Error('Unable to determine current user');
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
const md = new Metadata();
|
|
1076
|
+
const request = await md.GetEntityObject<MJAIAgentRequestEntity>(
|
|
1077
|
+
'MJ: AI Agent Requests',
|
|
1078
|
+
currentUser
|
|
1079
|
+
);
|
|
1080
|
+
if (!(await request.Load(requestId))) {
|
|
1081
|
+
throw new Error(`Agent request ${requestId} not found`);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
if (request.Status !== 'Requested') {
|
|
1085
|
+
throw new Error(`Request ${requestId} is ${request.Status} and cannot be reassigned`);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
const previousUserID = request.RequestForUserID;
|
|
1089
|
+
request.RequestForUserID = newUserID;
|
|
1090
|
+
|
|
1091
|
+
// Append reassignment note to Comments
|
|
1092
|
+
if (note || previousUserID) {
|
|
1093
|
+
const timestamp = new Date().toISOString();
|
|
1094
|
+
const reassignEntry = `[${timestamp} by ${currentUser.Email || currentUser.ID}] Reassigned from ${previousUserID || '(unassigned)'} to ${newUserID}${note ? ` — "${note}"` : ''}`;
|
|
1095
|
+
request.Comments = request.Comments
|
|
1096
|
+
? `${request.Comments}\n${reassignEntry}`
|
|
1097
|
+
: reassignEntry;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
const saved = await request.Save();
|
|
1101
|
+
if (!saved) {
|
|
1102
|
+
throw new Error(`Failed to save reassignment for request ${requestId}`);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// Send notification to new assignee
|
|
1106
|
+
try {
|
|
1107
|
+
await AIEngine.Instance.Config(false, currentUser);
|
|
1108
|
+
const agent = AIEngine.Instance.Agents.find(a => UUIDsEqual(a.ID, request.AgentID));
|
|
1109
|
+
const agentName = agent?.Name || 'Agent';
|
|
1110
|
+
const truncatedRequest = request.Request.length > 200
|
|
1111
|
+
? request.Request.substring(0, 197) + '...'
|
|
1112
|
+
: request.Request;
|
|
1113
|
+
|
|
1114
|
+
const notificationEngine = NotificationEngine.Instance;
|
|
1115
|
+
await notificationEngine.Config(false, currentUser);
|
|
1116
|
+
await notificationEngine.SendNotification({
|
|
1117
|
+
userId: newUserID,
|
|
1118
|
+
typeNameOrId: 'Agent Feedback Request',
|
|
1119
|
+
title: `${agentName} request assigned to you`,
|
|
1120
|
+
message: truncatedRequest,
|
|
1121
|
+
resourceConfiguration: {
|
|
1122
|
+
type: 'agent-request',
|
|
1123
|
+
requestId: requestId
|
|
1124
|
+
}
|
|
1125
|
+
}, currentUser);
|
|
1126
|
+
} catch (notifError) {
|
|
1127
|
+
LogError(`Failed to send reassignment notification: ${(notifError as Error).message}`);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
LogStatus(`📋 Agent request ${requestId} reassigned to ${newUserID}`);
|
|
1131
|
+
|
|
1132
|
+
const executionTime = Date.now() - startTime;
|
|
1133
|
+
return {
|
|
1134
|
+
success: true,
|
|
1135
|
+
executionTimeMs: executionTime,
|
|
1136
|
+
result: JSON.stringify({
|
|
1137
|
+
success: true,
|
|
1138
|
+
requestId,
|
|
1139
|
+
newUserID,
|
|
1140
|
+
reassigned: true
|
|
1141
|
+
})
|
|
1142
|
+
};
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
const executionTime = Date.now() - startTime;
|
|
1145
|
+
const errorMessage = (error as Error).message || 'Unknown error';
|
|
1146
|
+
LogError(`ReassignAgentRequest failed: ${errorMessage}`, undefined, error);
|
|
1147
|
+
return {
|
|
1148
|
+
success: false,
|
|
1149
|
+
errorMessage,
|
|
1150
|
+
executionTimeMs: executionTime,
|
|
1151
|
+
result: JSON.stringify({ success: false, errorMessage })
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
857
1156
|
/**
|
|
858
1157
|
* Execute agent in background (fire-and-forget).
|
|
859
1158
|
* Handles errors by publishing error completion events via PubSub,
|
package/dist/hooks.d.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server-level extensibility types for MJServer.
|
|
3
|
-
*
|
|
4
|
-
* Provider-level hook types (PreRunViewHook, PostRunViewHook, PreSaveHook)
|
|
5
|
-
* are defined and exported from @memberjunction/core (hookRegistry.ts) so
|
|
6
|
-
* that ProviderBase and BaseEntity can reference them without depending
|
|
7
|
-
* on @memberjunction/server.
|
|
8
|
-
*
|
|
9
|
-
* This file re-exports those types for convenience and adds the
|
|
10
|
-
* server-specific extensibility options (Express middleware, Apollo plugins,
|
|
11
|
-
* schema transformers).
|
|
12
|
-
*/
|
|
13
|
-
export type { PreRunViewHook, PostRunViewHook, PreSaveHook, HookName, HookRegistrationOptions } from '@memberjunction/core';
|
|
14
|
-
import type { PreRunViewHook, PostRunViewHook, PreSaveHook } from '@memberjunction/core';
|
|
15
|
-
import type { RequestHandler, ErrorRequestHandler, Application } from 'express';
|
|
16
|
-
import type { ApolloServerPlugin } from '@apollo/server';
|
|
17
|
-
import type { GraphQLSchema } from 'graphql';
|
|
18
|
-
/**
|
|
19
|
-
* A hook entry that includes the hook function plus optional registration metadata.
|
|
20
|
-
* Dynamic packages use this format to declare hooks with priority and namespace.
|
|
21
|
-
*/
|
|
22
|
-
export interface HookWithOptions<T> {
|
|
23
|
-
hook: T;
|
|
24
|
-
Priority?: number;
|
|
25
|
-
Namespace?: string;
|
|
26
|
-
}
|
|
27
|
-
/** A hook value is either a plain function or a function with registration options */
|
|
28
|
-
export type HookOrEntry<T> = T | HookWithOptions<T>;
|
|
29
|
-
/**
|
|
30
|
-
* Extensibility options that can be passed to `serve()` (via MJServerOptions)
|
|
31
|
-
* or to `createMJServer()` (via MJServerConfig).
|
|
32
|
-
*
|
|
33
|
-
* All properties are optional — when omitted, zero behavior change (backward compatible).
|
|
34
|
-
*/
|
|
35
|
-
export interface ServerExtensibilityOptions {
|
|
36
|
-
/** Express middleware applied after compression but before OAuth/REST/GraphQL routes */
|
|
37
|
-
ExpressMiddlewareBefore?: RequestHandler[];
|
|
38
|
-
/** Express middleware/error handlers applied after all routes (catch-alls, error handlers) */
|
|
39
|
-
ExpressMiddlewareAfter?: (RequestHandler | ErrorRequestHandler)[];
|
|
40
|
-
/** Escape hatch for advanced Express app configuration (CORS, trust proxy, etc.) */
|
|
41
|
-
ConfigureExpressApp?: (app: Application) => void | Promise<void>;
|
|
42
|
-
/** Additional Apollo Server plugins merged with the built-in drain/cleanup plugins */
|
|
43
|
-
ApolloPlugins?: ApolloServerPlugin[];
|
|
44
|
-
/** Schema transformers applied after built-in directive transformers */
|
|
45
|
-
SchemaTransformers?: ((schema: GraphQLSchema) => GraphQLSchema)[];
|
|
46
|
-
/**
|
|
47
|
-
* Express middleware that runs AFTER authentication has resolved UserInfo
|
|
48
|
-
* onto the request. Use this for middleware that needs the authenticated
|
|
49
|
-
* user (e.g., tenant context resolution, org membership loading).
|
|
50
|
-
*
|
|
51
|
-
* The authenticated user payload is available at `req.userPayload`.
|
|
52
|
-
* Middleware in this array runs in registration order.
|
|
53
|
-
*/
|
|
54
|
-
ExpressMiddlewarePostAuth?: RequestHandler[];
|
|
55
|
-
/** Hooks that modify RunViewParams before query execution (e.g., tenant filter injection).
|
|
56
|
-
* Each entry can be a plain hook function or a `{ hook, Priority, Namespace }` object. */
|
|
57
|
-
PreRunViewHooks?: HookOrEntry<PreRunViewHook>[];
|
|
58
|
-
/** Hooks that modify RunViewResult after query execution (e.g., data masking).
|
|
59
|
-
* Each entry can be a plain hook function or a `{ hook, Priority, Namespace }` object. */
|
|
60
|
-
PostRunViewHooks?: HookOrEntry<PostRunViewHook>[];
|
|
61
|
-
/** Hooks that validate/reject Save operations before they hit the database.
|
|
62
|
-
* Each entry can be a plain hook function or a `{ hook, Priority, Namespace }` object. */
|
|
63
|
-
PreSaveHooks?: HookOrEntry<PreSaveHook>[];
|
|
64
|
-
}
|
|
65
|
-
//# sourceMappingURL=hooks.d.ts.map
|
package/dist/hooks.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE5H,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAA2B,MAAM,sBAAsB,CAAC;AAClH,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;GAGG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,sFAAsF;AACtF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,wFAAwF;IACxF,uBAAuB,CAAC,EAAE,cAAc,EAAE,CAAC;IAE3C,8FAA8F;IAC9F,sBAAsB,CAAC,EAAE,CAAC,cAAc,GAAG,mBAAmB,CAAC,EAAE,CAAC;IAElE,oFAAoF;IACpF,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjE,sFAAsF;IACtF,aAAa,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAErC,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,aAAa,KAAK,aAAa,CAAC,EAAE,CAAC;IAElE;;;;;;;OAOG;IACH,yBAAyB,CAAC,EAAE,cAAc,EAAE,CAAC;IAE7C;8FAC0F;IAC1F,eAAe,CAAC,EAAE,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC;IAEhD;8FAC0F;IAC1F,gBAAgB,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC;IAElD;8FAC0F;IAC1F,YAAY,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;CAC3C"}
|
package/dist/hooks.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server-level extensibility types for MJServer.
|
|
3
|
-
*
|
|
4
|
-
* Provider-level hook types (PreRunViewHook, PostRunViewHook, PreSaveHook)
|
|
5
|
-
* are defined and exported from @memberjunction/core (hookRegistry.ts) so
|
|
6
|
-
* that ProviderBase and BaseEntity can reference them without depending
|
|
7
|
-
* on @memberjunction/server.
|
|
8
|
-
*
|
|
9
|
-
* This file re-exports those types for convenience and adds the
|
|
10
|
-
* server-specific extensibility options (Express middleware, Apollo plugins,
|
|
11
|
-
* schema transformers).
|
|
12
|
-
*/
|
|
13
|
-
export {};
|
|
14
|
-
//# sourceMappingURL=hooks.js.map
|
package/dist/hooks.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-dynamic-plugin.d.ts","sourceRoot":"","sources":["../src/test-dynamic-plugin.ts"],"names":[],"mappings":"AA0BA;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKxD"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/** Post-auth middleware that logs authenticated requests */
|
|
2
|
-
const testPostAuthMiddleware = (req, _res, next) => {
|
|
3
|
-
const userPayload = req.userPayload;
|
|
4
|
-
const email = userPayload?.email ?? 'unknown';
|
|
5
|
-
console.log(`[TestPlugin] Post-auth middleware: ${req.method} ${req.path} (user: ${email})`);
|
|
6
|
-
next();
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Startup function called by DynamicPackageLoader.
|
|
10
|
-
* Returns extensibility config that gets merged into server options.
|
|
11
|
-
*/
|
|
12
|
-
export function LoadTestPlugin() {
|
|
13
|
-
console.log('[TestPlugin] Startup function called');
|
|
14
|
-
return {
|
|
15
|
-
ExpressMiddlewarePostAuth: [testPostAuthMiddleware],
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=test-dynamic-plugin.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-dynamic-plugin.js","sourceRoot":"","sources":["../src/test-dynamic-plugin.ts"],"names":[],"mappings":"AAkBA,4DAA4D;AAC5D,MAAM,sBAAsB,GAAmB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IACjE,MAAM,WAAW,GAAI,GAAqC,CAAC,WAAW,CAAC;IACvE,MAAM,KAAK,GAAG,WAAW,EAAE,KAAK,IAAI,SAAS,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,sCAAsC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,WAAW,KAAK,GAAG,CAAC,CAAC;IAC7F,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACpD,OAAO;QACL,yBAAyB,EAAE,CAAC,sBAAsB,CAAC;KACpD,CAAC;AACJ,CAAC"}
|
package/src/hooks.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server-level extensibility types for MJServer.
|
|
3
|
-
*
|
|
4
|
-
* Provider-level hook types (PreRunViewHook, PostRunViewHook, PreSaveHook)
|
|
5
|
-
* are defined and exported from @memberjunction/core (hookRegistry.ts) so
|
|
6
|
-
* that ProviderBase and BaseEntity can reference them without depending
|
|
7
|
-
* on @memberjunction/server.
|
|
8
|
-
*
|
|
9
|
-
* This file re-exports those types for convenience and adds the
|
|
10
|
-
* server-specific extensibility options (Express middleware, Apollo plugins,
|
|
11
|
-
* schema transformers).
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export type { PreRunViewHook, PostRunViewHook, PreSaveHook, HookName, HookRegistrationOptions } from '@memberjunction/core';
|
|
15
|
-
|
|
16
|
-
import type { PreRunViewHook, PostRunViewHook, PreSaveHook, HookRegistrationOptions } from '@memberjunction/core';
|
|
17
|
-
import type { RequestHandler, ErrorRequestHandler, Application } from 'express';
|
|
18
|
-
import type { ApolloServerPlugin } from '@apollo/server';
|
|
19
|
-
import type { GraphQLSchema } from 'graphql';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* A hook entry that includes the hook function plus optional registration metadata.
|
|
23
|
-
* Dynamic packages use this format to declare hooks with priority and namespace.
|
|
24
|
-
*/
|
|
25
|
-
export interface HookWithOptions<T> {
|
|
26
|
-
hook: T;
|
|
27
|
-
Priority?: number;
|
|
28
|
-
Namespace?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** A hook value is either a plain function or a function with registration options */
|
|
32
|
-
export type HookOrEntry<T> = T | HookWithOptions<T>;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Extensibility options that can be passed to `serve()` (via MJServerOptions)
|
|
36
|
-
* or to `createMJServer()` (via MJServerConfig).
|
|
37
|
-
*
|
|
38
|
-
* All properties are optional — when omitted, zero behavior change (backward compatible).
|
|
39
|
-
*/
|
|
40
|
-
export interface ServerExtensibilityOptions {
|
|
41
|
-
/** Express middleware applied after compression but before OAuth/REST/GraphQL routes */
|
|
42
|
-
ExpressMiddlewareBefore?: RequestHandler[];
|
|
43
|
-
|
|
44
|
-
/** Express middleware/error handlers applied after all routes (catch-alls, error handlers) */
|
|
45
|
-
ExpressMiddlewareAfter?: (RequestHandler | ErrorRequestHandler)[];
|
|
46
|
-
|
|
47
|
-
/** Escape hatch for advanced Express app configuration (CORS, trust proxy, etc.) */
|
|
48
|
-
ConfigureExpressApp?: (app: Application) => void | Promise<void>;
|
|
49
|
-
|
|
50
|
-
/** Additional Apollo Server plugins merged with the built-in drain/cleanup plugins */
|
|
51
|
-
ApolloPlugins?: ApolloServerPlugin[];
|
|
52
|
-
|
|
53
|
-
/** Schema transformers applied after built-in directive transformers */
|
|
54
|
-
SchemaTransformers?: ((schema: GraphQLSchema) => GraphQLSchema)[];
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Express middleware that runs AFTER authentication has resolved UserInfo
|
|
58
|
-
* onto the request. Use this for middleware that needs the authenticated
|
|
59
|
-
* user (e.g., tenant context resolution, org membership loading).
|
|
60
|
-
*
|
|
61
|
-
* The authenticated user payload is available at `req.userPayload`.
|
|
62
|
-
* Middleware in this array runs in registration order.
|
|
63
|
-
*/
|
|
64
|
-
ExpressMiddlewarePostAuth?: RequestHandler[];
|
|
65
|
-
|
|
66
|
-
/** Hooks that modify RunViewParams before query execution (e.g., tenant filter injection).
|
|
67
|
-
* Each entry can be a plain hook function or a `{ hook, Priority, Namespace }` object. */
|
|
68
|
-
PreRunViewHooks?: HookOrEntry<PreRunViewHook>[];
|
|
69
|
-
|
|
70
|
-
/** Hooks that modify RunViewResult after query execution (e.g., data masking).
|
|
71
|
-
* Each entry can be a plain hook function or a `{ hook, Priority, Namespace }` object. */
|
|
72
|
-
PostRunViewHooks?: HookOrEntry<PostRunViewHook>[];
|
|
73
|
-
|
|
74
|
-
/** Hooks that validate/reject Save operations before they hit the database.
|
|
75
|
-
* Each entry can be a plain hook function or a `{ hook, Priority, Namespace }` object. */
|
|
76
|
-
PreSaveHooks?: HookOrEntry<PreSaveHook>[];
|
|
77
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test Dynamic Plugin for Phase 2 DynamicPackageLoader testing.
|
|
3
|
-
*
|
|
4
|
-
* This simple plugin validates the DynamicPackageLoader → MJServer pipeline:
|
|
5
|
-
* - Startup function is called and returns extensibility config
|
|
6
|
-
* - Post-auth middleware is injected and runs on each authenticated request
|
|
7
|
-
* - Middleware has access to the authenticated user via req.userPayload
|
|
8
|
-
*
|
|
9
|
-
* To enable: add to dynamicPackages.server in packages/MJAPI/mj.config.cjs
|
|
10
|
-
* To disable: set Enabled: false or remove the entry
|
|
11
|
-
*/
|
|
12
|
-
import type { RequestHandler } from 'express';
|
|
13
|
-
|
|
14
|
-
interface UserPayload {
|
|
15
|
-
email?: string;
|
|
16
|
-
userRecord?: { ID?: string };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Post-auth middleware that logs authenticated requests */
|
|
20
|
-
const testPostAuthMiddleware: RequestHandler = (req, _res, next) => {
|
|
21
|
-
const userPayload = (req as { userPayload?: UserPayload }).userPayload;
|
|
22
|
-
const email = userPayload?.email ?? 'unknown';
|
|
23
|
-
console.log(`[TestPlugin] Post-auth middleware: ${req.method} ${req.path} (user: ${email})`);
|
|
24
|
-
next();
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Startup function called by DynamicPackageLoader.
|
|
29
|
-
* Returns extensibility config that gets merged into server options.
|
|
30
|
-
*/
|
|
31
|
-
export function LoadTestPlugin(): Record<string, unknown> {
|
|
32
|
-
console.log('[TestPlugin] Startup function called');
|
|
33
|
-
return {
|
|
34
|
-
ExpressMiddlewarePostAuth: [testPostAuthMiddleware],
|
|
35
|
-
};
|
|
36
|
-
}
|