@memberjunction/server 5.1.0 → 5.3.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/README.md +2 -1
- package/dist/agents/skip-sdk.d.ts +7 -0
- package/dist/agents/skip-sdk.d.ts.map +1 -1
- package/dist/agents/skip-sdk.js +105 -4
- package/dist/agents/skip-sdk.js.map +1 -1
- package/dist/entitySubclasses/{entityPermissions.server.d.ts → MJEntityPermissionEntityServer.server.d.ts} +2 -2
- package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.d.ts.map +1 -0
- package/dist/entitySubclasses/{entityPermissions.server.js → MJEntityPermissionEntityServer.server.js} +9 -9
- package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js.map +1 -0
- package/dist/generated/generated.d.ts +36 -10
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +160 -36
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +2 -2
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts +28 -0
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -0
- package/dist/resolvers/AdhocQueryResolver.js +140 -0
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -0
- package/dist/resolvers/CreateQueryResolver.js +2 -2
- package/dist/resolvers/RunAIAgentResolver.d.ts +11 -2
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +55 -11
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/RunTestResolver.d.ts +5 -0
- package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTestResolver.js +32 -2
- package/dist/resolvers/RunTestResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/services/TaskOrchestrator.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +53 -52
- package/src/__tests__/AdhocQueryResolver.test.ts +175 -0
- package/src/agents/skip-sdk.ts +130 -5
- package/src/entitySubclasses/{entityPermissions.server.ts → MJEntityPermissionEntityServer.server.ts} +3 -3
- package/src/generated/generated.ts +126 -28
- package/src/generic/ResolverBase.ts +9 -9
- package/src/generic/RunViewResolver.ts +4 -4
- package/src/index.ts +1 -1
- package/src/resolvers/AdhocQueryResolver.ts +126 -0
- package/src/resolvers/CreateQueryResolver.ts +5 -5
- package/src/resolvers/RunAIAgentResolver.ts +98 -15
- package/src/resolvers/RunAIPromptResolver.ts +7 -7
- package/src/resolvers/RunTemplateResolver.ts +2 -2
- package/src/resolvers/RunTestResolver.ts +41 -3
- package/src/resolvers/UserViewResolver.ts +2 -2
- package/src/services/TaskOrchestrator.ts +5 -5
- package/src/types.ts +2 -2
- package/dist/entitySubclasses/entityPermissions.server.d.ts.map +0 -1
- package/dist/entitySubclasses/entityPermissions.server.js.map +0 -1
|
@@ -4,7 +4,7 @@ import { ResolverBase } from './ResolverBase.js';
|
|
|
4
4
|
import { LogError, LogStatus, EntityInfo, RunViewWithCacheCheckResult, RunViewsWithCacheCheckResponse, RunViewWithCacheCheckParams, AggregateResult } from '@memberjunction/core';
|
|
5
5
|
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
|
|
6
6
|
import { GetReadOnlyProvider } from '../util.js';
|
|
7
|
-
import {
|
|
7
|
+
import { MJUserViewEntityExtended } from '@memberjunction/core-entities';
|
|
8
8
|
import { KeyValuePairOutputType } from './KeyInputOutputTypes.js';
|
|
9
9
|
import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
|
|
10
10
|
|
|
@@ -652,7 +652,7 @@ export class RunViewResolver extends ResolverBase {
|
|
|
652
652
|
if (rawData === null)
|
|
653
653
|
return null;
|
|
654
654
|
|
|
655
|
-
const viewInfo = super.safeFirstArrayElement<
|
|
655
|
+
const viewInfo = super.safeFirstArrayElement<MJUserViewEntityExtended>(await super.findBy<MJUserViewEntityExtended>(provider, "MJ: User Views", { Name: input.ViewName }, userPayload.userRecord));
|
|
656
656
|
const entity = provider.Entities.find((e) => e.ID === viewInfo.EntityID);
|
|
657
657
|
const returnData = this.processRawData(rawData.Results, viewInfo.EntityID, entity);
|
|
658
658
|
return {
|
|
@@ -683,7 +683,7 @@ export class RunViewResolver extends ResolverBase {
|
|
|
683
683
|
if (rawData === null)
|
|
684
684
|
return null;
|
|
685
685
|
|
|
686
|
-
const viewInfo = super.safeFirstArrayElement<
|
|
686
|
+
const viewInfo = super.safeFirstArrayElement<MJUserViewEntityExtended>(await super.findBy<MJUserViewEntityExtended>(provider, "MJ: User Views", { ID: input.ViewID }, userPayload.userRecord));
|
|
687
687
|
const entity = provider.Entities.find((e) => e.ID === viewInfo.EntityID);
|
|
688
688
|
const returnData = this.processRawData(rawData.Results, viewInfo.EntityID, entity);
|
|
689
689
|
return {
|
|
@@ -837,7 +837,7 @@ export class RunViewResolver extends ResolverBase {
|
|
|
837
837
|
};
|
|
838
838
|
}
|
|
839
839
|
|
|
840
|
-
const viewInfo = super.safeFirstArrayElement<
|
|
840
|
+
const viewInfo = super.safeFirstArrayElement<MJUserViewEntityExtended>(await super.findBy<MJUserViewEntityExtended>(provider, "MJ: User Views", { ID: input.ViewID }, userPayload.userRecord));
|
|
841
841
|
const entity = provider.Entities.find((e) => e.ID === viewInfo.EntityID);
|
|
842
842
|
const returnData = this.processRawData(rawData.Results, viewInfo.EntityID, entity);
|
|
843
843
|
return {
|
package/src/index.ts
CHANGED
|
@@ -44,7 +44,7 @@ export * from 'type-graphql';
|
|
|
44
44
|
export { NewUserBase } from './auth/newUsers.js';
|
|
45
45
|
export { configInfo, DEFAULT_SERVER_CONFIG } from './config.js';
|
|
46
46
|
export * from './directives/index.js';
|
|
47
|
-
export * from './entitySubclasses/
|
|
47
|
+
export * from './entitySubclasses/MJEntityPermissionEntityServer.server.js';
|
|
48
48
|
export * from './types.js';
|
|
49
49
|
export {
|
|
50
50
|
TokenExpiredError,
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Arg, Ctx, Query, Resolver, Field, Int, InputType } from 'type-graphql';
|
|
2
|
+
import { LogError } from '@memberjunction/core';
|
|
3
|
+
import { SQLExpressionValidator } from '@memberjunction/global';
|
|
4
|
+
import { AppContext } from '../types.js';
|
|
5
|
+
import { GetReadOnlyDataSource } from '../util.js';
|
|
6
|
+
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
7
|
+
import { RunQueryResultType } from './QueryResolver.js';
|
|
8
|
+
import sql from 'mssql';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Input type for executing ad-hoc SQL queries directly.
|
|
12
|
+
* The SQL is validated server-side to ensure it's a safe SELECT/WITH statement.
|
|
13
|
+
*/
|
|
14
|
+
@InputType()
|
|
15
|
+
class AdhocQueryInput {
|
|
16
|
+
@Field(() => String, { description: 'SQL query to execute. Must be a SELECT or WITH (CTE) statement.' })
|
|
17
|
+
SQL: string;
|
|
18
|
+
|
|
19
|
+
@Field(() => Int, { nullable: true, description: 'Query timeout in seconds. Defaults to 30.' })
|
|
20
|
+
TimeoutSeconds?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolver for executing ad-hoc (unsaved) SQL queries.
|
|
25
|
+
*
|
|
26
|
+
* Security:
|
|
27
|
+
* - SQL validated via SQLExpressionValidator (full_query context) — blocks mutations, dangerous operations
|
|
28
|
+
* - Executes on read-only connection pool only (no fallback to read-write)
|
|
29
|
+
* - Configurable timeout (default 30s)
|
|
30
|
+
* - Requires authenticated user (standard GraphQL auth, no @RequireSystemUser)
|
|
31
|
+
*
|
|
32
|
+
* Auto-discovered by MJServer's dynamic resolver import.
|
|
33
|
+
*/
|
|
34
|
+
@Resolver()
|
|
35
|
+
export class AdhocQueryResolver extends ResolverBase {
|
|
36
|
+
@Query(() => RunQueryResultType)
|
|
37
|
+
async ExecuteAdhocQuery(
|
|
38
|
+
@Arg('input', () => AdhocQueryInput) input: AdhocQueryInput,
|
|
39
|
+
@Ctx() context: AppContext
|
|
40
|
+
): Promise<RunQueryResultType> {
|
|
41
|
+
const startTime = Date.now();
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// 1. Security: validate SQL using SQLExpressionValidator
|
|
45
|
+
const validator = SQLExpressionValidator.Instance;
|
|
46
|
+
const validation = validator.validateFullQuery(input.SQL);
|
|
47
|
+
if (!validation.valid) {
|
|
48
|
+
return this.buildErrorResult(validation.error || 'SQL validation failed');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 2. Get READ-ONLY data source (no fallback to read-write)
|
|
52
|
+
let readOnlyDS: sql.ConnectionPool;
|
|
53
|
+
try {
|
|
54
|
+
readOnlyDS = GetReadOnlyDataSource(context.dataSources, { allowFallbackToReadWrite: false });
|
|
55
|
+
} catch {
|
|
56
|
+
return this.buildErrorResult('No read-only data source available for ad-hoc query execution');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 3. Execute with timeout
|
|
60
|
+
const timeoutMs = (input.TimeoutSeconds ?? 30) * 1000;
|
|
61
|
+
const request = new sql.Request(readOnlyDS);
|
|
62
|
+
|
|
63
|
+
const result = await Promise.race([
|
|
64
|
+
request.query(input.SQL),
|
|
65
|
+
new Promise<never>((_, reject) =>
|
|
66
|
+
setTimeout(() => reject(new Error('Query timeout exceeded')), timeoutMs)
|
|
67
|
+
)
|
|
68
|
+
]);
|
|
69
|
+
const executionTimeMs = Date.now() - startTime;
|
|
70
|
+
|
|
71
|
+
// 4. Return as RunQueryResultType
|
|
72
|
+
return {
|
|
73
|
+
QueryID: '',
|
|
74
|
+
QueryName: 'Ad-Hoc Query',
|
|
75
|
+
Success: true,
|
|
76
|
+
Results: JSON.stringify(result.recordset ?? []),
|
|
77
|
+
RowCount: result.recordset?.length ?? 0,
|
|
78
|
+
TotalRowCount: result.recordset?.length ?? 0,
|
|
79
|
+
ExecutionTime: executionTimeMs,
|
|
80
|
+
ErrorMessage: ''
|
|
81
|
+
};
|
|
82
|
+
} catch (err: unknown) {
|
|
83
|
+
const executionTimeMs = Date.now() - startTime;
|
|
84
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
85
|
+
|
|
86
|
+
// Handle timeout
|
|
87
|
+
if (errorMessage.includes('timeout') || errorMessage.includes('Timeout')) {
|
|
88
|
+
return {
|
|
89
|
+
QueryID: '',
|
|
90
|
+
QueryName: 'Ad-Hoc Query',
|
|
91
|
+
Success: false,
|
|
92
|
+
Results: '[]',
|
|
93
|
+
RowCount: 0,
|
|
94
|
+
TotalRowCount: 0,
|
|
95
|
+
ExecutionTime: executionTimeMs,
|
|
96
|
+
ErrorMessage: `Query execution exceeded ${input.TimeoutSeconds ?? 30} second timeout`
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
LogError(`Ad-hoc query execution failed: ${errorMessage}`);
|
|
101
|
+
return {
|
|
102
|
+
QueryID: '',
|
|
103
|
+
QueryName: 'Ad-Hoc Query',
|
|
104
|
+
Success: false,
|
|
105
|
+
Results: '[]',
|
|
106
|
+
RowCount: 0,
|
|
107
|
+
TotalRowCount: 0,
|
|
108
|
+
ExecutionTime: executionTimeMs,
|
|
109
|
+
ErrorMessage: `Query execution failed: ${errorMessage}`
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private buildErrorResult(errorMessage: string): RunQueryResultType {
|
|
115
|
+
return {
|
|
116
|
+
QueryID: '',
|
|
117
|
+
QueryName: 'Ad-Hoc Query',
|
|
118
|
+
Success: false,
|
|
119
|
+
Results: '[]',
|
|
120
|
+
RowCount: 0,
|
|
121
|
+
TotalRowCount: 0,
|
|
122
|
+
ExecutionTime: 0,
|
|
123
|
+
ErrorMessage: errorMessage
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -6,7 +6,7 @@ import { MJQueryCategoryEntity, MJQueryPermissionEntity } from '@memberjunction/
|
|
|
6
6
|
import { MJQueryResolver } from '../generated/generated.js';
|
|
7
7
|
import { GetReadOnlyProvider, GetReadWriteProvider } from '../util.js';
|
|
8
8
|
import { DeleteOptionsInput } from '../generic/DeleteOptionsInput.js';
|
|
9
|
-
import {
|
|
9
|
+
import { MJQueryEntityServer } from '@memberjunction/core-entities-server';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Query status enumeration for GraphQL
|
|
@@ -425,8 +425,8 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
425
425
|
};
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
-
// Use
|
|
429
|
-
const record = await provider.GetEntityObject<
|
|
428
|
+
// Use MJQueryEntityServer which handles AI processing
|
|
429
|
+
const record = await provider.GetEntityObject<MJQueryEntityServer>("MJ: Queries", context.userPayload.userRecord);
|
|
430
430
|
|
|
431
431
|
// Set the fields from input, handling CategoryPath resolution
|
|
432
432
|
const fieldsToSet = {
|
|
@@ -638,9 +638,9 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
638
638
|
@PubSub() pubSub: PubSubEngine
|
|
639
639
|
): Promise<UpdateQueryResultType> {
|
|
640
640
|
try {
|
|
641
|
-
// Load the existing query using
|
|
641
|
+
// Load the existing query using MJQueryEntityServer
|
|
642
642
|
const provider = GetReadWriteProvider(context.providers);
|
|
643
|
-
const queryEntity = await provider.GetEntityObject<
|
|
643
|
+
const queryEntity = await provider.GetEntityObject<MJQueryEntityServer>('MJ: Queries', context.userPayload.userRecord);
|
|
644
644
|
if (!queryEntity || !await queryEntity.Load(input.ID)) {
|
|
645
645
|
return {
|
|
646
646
|
Success: false,
|
|
@@ -3,7 +3,7 @@ import { AppContext, UserPayload } from '../types.js';
|
|
|
3
3
|
import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo } from '@memberjunction/core';
|
|
4
4
|
import { MJConversationDetailEntity, MJConversationDetailAttachmentEntity } from '@memberjunction/core-entities';
|
|
5
5
|
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
6
|
-
import {
|
|
6
|
+
import { MJAIAgentEntityExtended, MJAIAgentRunEntityExtended, ExecuteAgentResult, ConversationUtility, AttachmentData } from '@memberjunction/ai-core-plus';
|
|
7
7
|
import { AIEngine } from '@memberjunction/aiengine';
|
|
8
8
|
import { ChatMessage } from '@memberjunction/ai';
|
|
9
9
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
@@ -207,12 +207,12 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
207
207
|
/**
|
|
208
208
|
* Validate the agent entity
|
|
209
209
|
*/
|
|
210
|
-
private async validateAgent(agentId: string, currentUser: any): Promise<
|
|
210
|
+
private async validateAgent(agentId: string, currentUser: any): Promise<MJAIAgentEntityExtended> {
|
|
211
211
|
// Use AIEngine to get cached agent data
|
|
212
212
|
await AIEngine.Instance.Config(false, currentUser);
|
|
213
213
|
|
|
214
214
|
// Find agent in cached collection
|
|
215
|
-
const agentEntity = AIEngine.Instance.Agents.find((a:
|
|
215
|
+
const agentEntity = AIEngine.Instance.Agents.find((a: MJAIAgentEntityExtended) => a.ID === agentId);
|
|
216
216
|
|
|
217
217
|
if (!agentEntity) {
|
|
218
218
|
throw new Error(`AI Agent with ID ${agentId} not found`);
|
|
@@ -433,9 +433,6 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
433
433
|
|
|
434
434
|
const executionTime = Date.now() - startTime;
|
|
435
435
|
|
|
436
|
-
// Publish final events
|
|
437
|
-
this.publishFinalEvents(pubSub, sessionId, userPayload, result);
|
|
438
|
-
|
|
439
436
|
// Create notification if enabled and artifact was created successfully
|
|
440
437
|
if (createNotification && result.success && artifactInfo && artifactInfo.artifactId && artifactInfo.versionId && artifactInfo.versionNumber) {
|
|
441
438
|
await this.createCompletionNotification(
|
|
@@ -456,6 +453,9 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
456
453
|
const sanitizedResult = this.sanitizeAgentResult(result);
|
|
457
454
|
const returnResult = JSON.stringify(sanitizedResult);
|
|
458
455
|
|
|
456
|
+
// Publish final events with enriched result data for fire-and-forget clients
|
|
457
|
+
this.publishFinalEvents(pubSub, sessionId, userPayload, result, returnResult);
|
|
458
|
+
|
|
459
459
|
// Log completion
|
|
460
460
|
if (result.success) {
|
|
461
461
|
LogStatus(`=== AI AGENT RUN COMPLETED FOR: ${agentEntity.Name} (${executionTime}ms) ===`);
|
|
@@ -491,9 +491,17 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
/**
|
|
494
|
-
* Publish final streaming events (partial result and completion)
|
|
494
|
+
* Publish final streaming events (partial result and completion).
|
|
495
|
+
* The completion event includes the full result JSON so clients using
|
|
496
|
+
* fire-and-forget mode can receive the result via WebSocket.
|
|
495
497
|
*/
|
|
496
|
-
private publishFinalEvents(
|
|
498
|
+
private publishFinalEvents(
|
|
499
|
+
pubSub: PubSubEngine,
|
|
500
|
+
sessionId: string,
|
|
501
|
+
userPayload: UserPayload,
|
|
502
|
+
result: ExecuteAgentResult,
|
|
503
|
+
resultJson?: string
|
|
504
|
+
) {
|
|
497
505
|
if (result.agentRun) {
|
|
498
506
|
// Get the last step from agent run
|
|
499
507
|
let lastStep = 'Completed';
|
|
@@ -519,15 +527,19 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
519
527
|
this.PublishStreamingUpdate(pubSub, partialMsg, userPayload);
|
|
520
528
|
}
|
|
521
529
|
|
|
522
|
-
// Publish completion with conversationDetailId for client-side routing
|
|
523
|
-
|
|
530
|
+
// Publish completion with conversationDetailId for client-side routing.
|
|
531
|
+
// Include result data so fire-and-forget clients can receive the full result via WebSocket.
|
|
532
|
+
const completionData: Record<string, unknown> = {
|
|
524
533
|
sessionId,
|
|
525
534
|
agentRunId: result.agentRun?.ID || 'unknown',
|
|
526
535
|
type: 'complete',
|
|
527
536
|
timestamp: new Date(),
|
|
528
|
-
conversationDetailId: result.agentRun?.ConversationDetailID
|
|
537
|
+
conversationDetailId: result.agentRun?.ConversationDetailID,
|
|
538
|
+
success: result.success,
|
|
539
|
+
errorMessage: result.agentRun?.ErrorMessage || undefined,
|
|
540
|
+
result: resultJson || undefined
|
|
529
541
|
};
|
|
530
|
-
this.PublishStreamingUpdate(pubSub,
|
|
542
|
+
this.PublishStreamingUpdate(pubSub, completionData, userPayload);
|
|
531
543
|
}
|
|
532
544
|
|
|
533
545
|
/**
|
|
@@ -630,7 +642,7 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
630
642
|
* Notification includes navigation link back to the conversation
|
|
631
643
|
*/
|
|
632
644
|
private async createCompletionNotification(
|
|
633
|
-
agentRun:
|
|
645
|
+
agentRun: MJAIAgentRunEntityExtended,
|
|
634
646
|
artifactInfo: { artifactId: string; versionId: string; versionNumber: number },
|
|
635
647
|
conversationDetailId: string,
|
|
636
648
|
contextUser: UserInfo,
|
|
@@ -742,7 +754,8 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
742
754
|
@Arg('createArtifacts', { nullable: true }) createArtifacts?: boolean,
|
|
743
755
|
@Arg('createNotification', { nullable: true }) createNotification?: boolean,
|
|
744
756
|
@Arg('sourceArtifactId', { nullable: true }) sourceArtifactId?: string,
|
|
745
|
-
@Arg('sourceArtifactVersionId', { nullable: true }) sourceArtifactVersionId?: string
|
|
757
|
+
@Arg('sourceArtifactVersionId', { nullable: true }) sourceArtifactVersionId?: string,
|
|
758
|
+
@Arg('fireAndForget', { nullable: true }) fireAndForget?: boolean
|
|
746
759
|
): Promise<AIAgentRunResult> {
|
|
747
760
|
// Check API key scope authorization for agent execution
|
|
748
761
|
await this.CheckAPIKeyScopeAuthorization('agent:execute', agentId, userPayload);
|
|
@@ -769,7 +782,25 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
769
782
|
// Convert to JSON string for the existing executeAIAgent method
|
|
770
783
|
const messagesJson = JSON.stringify(messages);
|
|
771
784
|
|
|
772
|
-
|
|
785
|
+
if (fireAndForget) {
|
|
786
|
+
// Fire-and-forget mode: start execution in background, return immediately.
|
|
787
|
+
// The client will receive the result via WebSocket PubSub completion event.
|
|
788
|
+
this.executeAgentInBackground(
|
|
789
|
+
p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub,
|
|
790
|
+
data, payload, lastRunId, autoPopulateLastRunPayload, configurationId,
|
|
791
|
+
conversationDetailId, createArtifacts || false, createNotification || false,
|
|
792
|
+
sourceArtifactId, sourceArtifactVersionId
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
LogStatus(`🔥 Fire-and-forget: Agent ${agentId} execution started in background for session ${sessionId}`);
|
|
796
|
+
|
|
797
|
+
return {
|
|
798
|
+
success: true,
|
|
799
|
+
result: JSON.stringify({ accepted: true, fireAndForget: true })
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Synchronous mode (default): wait for execution to complete
|
|
773
804
|
return this.executeAIAgent(
|
|
774
805
|
p,
|
|
775
806
|
dataSource,
|
|
@@ -801,6 +832,58 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
801
832
|
}
|
|
802
833
|
}
|
|
803
834
|
|
|
835
|
+
/**
|
|
836
|
+
* Execute agent in background (fire-and-forget).
|
|
837
|
+
* Handles errors by publishing error completion events via PubSub,
|
|
838
|
+
* so the client receives them via WebSocket even though the HTTP response
|
|
839
|
+
* has already been sent.
|
|
840
|
+
*/
|
|
841
|
+
private executeAgentInBackground(
|
|
842
|
+
p: DatabaseProviderBase,
|
|
843
|
+
dataSource: unknown,
|
|
844
|
+
agentId: string,
|
|
845
|
+
userPayload: UserPayload,
|
|
846
|
+
messagesJson: string,
|
|
847
|
+
sessionId: string,
|
|
848
|
+
pubSub: PubSubEngine,
|
|
849
|
+
data?: string,
|
|
850
|
+
payload?: string,
|
|
851
|
+
lastRunId?: string,
|
|
852
|
+
autoPopulateLastRunPayload?: boolean,
|
|
853
|
+
configurationId?: string,
|
|
854
|
+
conversationDetailId?: string,
|
|
855
|
+
createArtifacts: boolean = false,
|
|
856
|
+
createNotification: boolean = false,
|
|
857
|
+
sourceArtifactId?: string,
|
|
858
|
+
sourceArtifactVersionId?: string
|
|
859
|
+
): void {
|
|
860
|
+
// Execute in background - errors are handled within, not propagated
|
|
861
|
+
this.executeAIAgent(
|
|
862
|
+
p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub,
|
|
863
|
+
data, payload, undefined, lastRunId, autoPopulateLastRunPayload,
|
|
864
|
+
configurationId, conversationDetailId, createArtifacts, createNotification,
|
|
865
|
+
sourceArtifactId, sourceArtifactVersionId
|
|
866
|
+
).catch((error: unknown) => {
|
|
867
|
+
// Background execution failed unexpectedly (executeAIAgent has its own try-catch,
|
|
868
|
+
// so this would only fire for truly unexpected errors).
|
|
869
|
+
const errorMessage = (error instanceof Error) ? error.message : 'Unknown background execution error';
|
|
870
|
+
LogError(`🔥 Fire-and-forget background execution failed: ${errorMessage}`, undefined, error);
|
|
871
|
+
|
|
872
|
+
// Publish error completion event so the client knows the agent failed
|
|
873
|
+
const errorCompletionData: Record<string, unknown> = {
|
|
874
|
+
sessionId,
|
|
875
|
+
agentRunId: 'unknown',
|
|
876
|
+
type: 'complete',
|
|
877
|
+
timestamp: new Date(),
|
|
878
|
+
conversationDetailId,
|
|
879
|
+
success: false,
|
|
880
|
+
errorMessage,
|
|
881
|
+
result: JSON.stringify({ success: false, errorMessage })
|
|
882
|
+
};
|
|
883
|
+
this.PublishStreamingUpdate(pubSub, errorCompletionData, userPayload);
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
|
|
804
887
|
/**
|
|
805
888
|
* Load conversation history with attachments from database.
|
|
806
889
|
* Builds ChatMessage[] with multimodal content blocks for attachments.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, Field, Int } from 'type-graphql';
|
|
2
2
|
import { AppContext, UserPayload } from '../types.js';
|
|
3
3
|
import { DatabaseProviderBase, LogError, LogStatus, Metadata } from '@memberjunction/core';
|
|
4
|
-
import {
|
|
4
|
+
import { MJAIPromptEntityExtended, MJAIModelEntityExtended } from '@memberjunction/ai-core-plus';
|
|
5
5
|
import { AIPromptRunner } from '@memberjunction/ai-prompts';
|
|
6
6
|
import { AIPromptParams } from '@memberjunction/ai-core-plus';
|
|
7
7
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
@@ -156,7 +156,7 @@ export class RunAIPromptResolver extends ResolverBase {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// Load the AI prompt entity
|
|
159
|
-
const promptEntity = await p.GetEntityObject<
|
|
159
|
+
const promptEntity = await p.GetEntityObject<MJAIPromptEntityExtended>('MJ: AI Prompts', currentUser);
|
|
160
160
|
await promptEntity.Load(promptId);
|
|
161
161
|
|
|
162
162
|
if (!promptEntity.IsSaved) {
|
|
@@ -402,7 +402,7 @@ export class RunAIPromptResolver extends ResolverBase {
|
|
|
402
402
|
preferredModels: string[] | undefined,
|
|
403
403
|
modelPower: string,
|
|
404
404
|
contextUser: any
|
|
405
|
-
): Promise<
|
|
405
|
+
): Promise<MJAIModelEntityExtended> {
|
|
406
406
|
// Ensure AI Engine is configured
|
|
407
407
|
await AIEngine.Instance.Config(false, contextUser);
|
|
408
408
|
|
|
@@ -413,7 +413,7 @@ export class RunAIPromptResolver extends ResolverBase {
|
|
|
413
413
|
);
|
|
414
414
|
|
|
415
415
|
// Filter to only models with valid API keys
|
|
416
|
-
const modelsWithKeys:
|
|
416
|
+
const modelsWithKeys: MJAIModelEntityExtended[] = [];
|
|
417
417
|
for (const model of allModels) {
|
|
418
418
|
const apiKey = GetAIAPIKey(model.DriverClass);
|
|
419
419
|
if (apiKey && apiKey.trim().length > 0) {
|
|
@@ -443,7 +443,7 @@ export class RunAIPromptResolver extends ResolverBase {
|
|
|
443
443
|
// Sort by PowerRank for power-based selection
|
|
444
444
|
modelsWithKeys.sort((a, b) => (b.PowerRank || 0) - (a.PowerRank || 0));
|
|
445
445
|
|
|
446
|
-
let selectedModel:
|
|
446
|
+
let selectedModel: MJAIModelEntityExtended;
|
|
447
447
|
switch (modelPower) {
|
|
448
448
|
case 'lowest':
|
|
449
449
|
selectedModel = modelsWithKeys[modelsWithKeys.length - 1];
|
|
@@ -466,7 +466,7 @@ export class RunAIPromptResolver extends ResolverBase {
|
|
|
466
466
|
* Helper method to select an embedding model by size
|
|
467
467
|
* @private
|
|
468
468
|
*/
|
|
469
|
-
private selectEmbeddingModelBySize(modelSize: string):
|
|
469
|
+
private selectEmbeddingModelBySize(modelSize: string): MJAIModelEntityExtended {
|
|
470
470
|
const localModels = AIEngine.Instance.LocalEmbeddingModels;
|
|
471
471
|
|
|
472
472
|
if (!localModels || localModels.length === 0) {
|
|
@@ -541,7 +541,7 @@ export class RunAIPromptResolver extends ResolverBase {
|
|
|
541
541
|
* Helper method to format simple prompt result
|
|
542
542
|
* @private
|
|
543
543
|
*/
|
|
544
|
-
private formatSimpleResult(chatResult: any, model:
|
|
544
|
+
private formatSimpleResult(chatResult: any, model: MJAIModelEntityExtended, executionTime: number): SimplePromptResult {
|
|
545
545
|
if (!chatResult || !chatResult.success) {
|
|
546
546
|
return {
|
|
547
547
|
success: false,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Resolver, Mutation, Arg, Ctx, ObjectType, Field } from 'type-graphql';
|
|
2
2
|
import { AppContext, UserPayload } from '../types.js';
|
|
3
3
|
import { LogError, LogStatus, Metadata, RunView } from '@memberjunction/core';
|
|
4
|
-
import { MJTemplateContentEntity,
|
|
4
|
+
import { MJTemplateContentEntity, MJTemplateEntityExtended } from '@memberjunction/core-entities';
|
|
5
5
|
import { TemplateEngineServer } from '@memberjunction/templates';
|
|
6
6
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
7
7
|
import { GetReadWriteProvider } from '../util.js';
|
|
@@ -63,7 +63,7 @@ export class RunTemplateResolver extends ResolverBase {
|
|
|
63
63
|
|
|
64
64
|
const p = GetReadWriteProvider(providers);
|
|
65
65
|
// Load the template entity
|
|
66
|
-
const templateEntity = await p.GetEntityObject<
|
|
66
|
+
const templateEntity = await p.GetEntityObject<MJTemplateEntityExtended>('MJ: Templates', currentUser);
|
|
67
67
|
await templateEntity.Load(templateId);
|
|
68
68
|
|
|
69
69
|
if (!templateEntity.IsSaved) {
|
|
@@ -16,7 +16,7 @@ import { LogError, LogStatus } from '@memberjunction/core';
|
|
|
16
16
|
import { TestEngine } from '@memberjunction/testing-engine';
|
|
17
17
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
18
18
|
import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
|
|
19
|
-
import { TestRunVariables } from '@memberjunction/testing-engine-base';
|
|
19
|
+
import { TestRunVariables, TestLogMessage } from '@memberjunction/testing-engine-base';
|
|
20
20
|
|
|
21
21
|
// ===== GraphQL Types =====
|
|
22
22
|
|
|
@@ -131,6 +131,11 @@ export class RunTestResolver extends ResolverBase {
|
|
|
131
131
|
this.createProgressCallback(pubSub, userPayload, testId) :
|
|
132
132
|
undefined;
|
|
133
133
|
|
|
134
|
+
// Create log callback to stream driver/engine logs to the UI in real-time
|
|
135
|
+
const logCallback = pubSub ?
|
|
136
|
+
this.createLogCallback(pubSub, userPayload, testId) :
|
|
137
|
+
undefined;
|
|
138
|
+
|
|
134
139
|
// Parse variables from JSON string if provided
|
|
135
140
|
let parsedVariables: TestRunVariables | undefined;
|
|
136
141
|
if (variables) {
|
|
@@ -147,7 +152,8 @@ export class RunTestResolver extends ResolverBase {
|
|
|
147
152
|
environment,
|
|
148
153
|
tags,
|
|
149
154
|
variables: parsedVariables,
|
|
150
|
-
progressCallback
|
|
155
|
+
progressCallback,
|
|
156
|
+
logCallback
|
|
151
157
|
};
|
|
152
158
|
|
|
153
159
|
const result = await engine.RunTest(testId, options, user);
|
|
@@ -251,6 +257,11 @@ export class RunTestResolver extends ResolverBase {
|
|
|
251
257
|
this.createProgressCallback(pubSub, userPayload, suiteId) :
|
|
252
258
|
undefined;
|
|
253
259
|
|
|
260
|
+
// Create log callback to stream driver/engine logs to the UI in real-time
|
|
261
|
+
const logCallback = pubSub ?
|
|
262
|
+
this.createLogCallback(pubSub, userPayload, suiteId) :
|
|
263
|
+
undefined;
|
|
264
|
+
|
|
254
265
|
// Parse selectedTestIds from JSON string if provided
|
|
255
266
|
let parsedSelectedTestIds: string[] | undefined;
|
|
256
267
|
if (selectedTestIds) {
|
|
@@ -280,7 +291,8 @@ export class RunTestResolver extends ResolverBase {
|
|
|
280
291
|
selectedTestIds: parsedSelectedTestIds,
|
|
281
292
|
sequenceStart,
|
|
282
293
|
sequenceEnd,
|
|
283
|
-
progressCallback
|
|
294
|
+
progressCallback,
|
|
295
|
+
logCallback
|
|
284
296
|
};
|
|
285
297
|
|
|
286
298
|
const result = await engine.RunSuite(suiteId, options, user);
|
|
@@ -364,6 +376,32 @@ export class RunTestResolver extends ResolverBase {
|
|
|
364
376
|
};
|
|
365
377
|
}
|
|
366
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Create log callback that streams driver/engine log messages to the UI
|
|
381
|
+
* as progress updates, so they appear in the execution log in real-time.
|
|
382
|
+
*/
|
|
383
|
+
private createLogCallback(
|
|
384
|
+
pubSub: PubSubEngine,
|
|
385
|
+
userPayload: UserPayload,
|
|
386
|
+
testId: string
|
|
387
|
+
) {
|
|
388
|
+
return (message: TestLogMessage) => {
|
|
389
|
+
const progressMsg: TestExecutionStreamMessage = {
|
|
390
|
+
sessionId: userPayload.sessionId || '',
|
|
391
|
+
testRunId: testId,
|
|
392
|
+
type: 'progress',
|
|
393
|
+
progress: {
|
|
394
|
+
currentStep: 'driver_log',
|
|
395
|
+
percentage: -1, // Signal that percentage should not be updated
|
|
396
|
+
message: message.message,
|
|
397
|
+
},
|
|
398
|
+
timestamp: message.timestamp
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
this.publishProgress(pubSub, progressMsg, userPayload);
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
367
405
|
private publishProgress(pubSub: PubSubEngine, data: TestExecutionStreamMessage, userPayload: UserPayload) {
|
|
368
406
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
369
407
|
message: JSON.stringify({
|
|
@@ -3,7 +3,7 @@ import { EntitySaveOptions, Metadata } from '@memberjunction/core';
|
|
|
3
3
|
import { AppContext, Arg, Ctx, Int, Query, Resolver, UserPayload } from '@memberjunction/server';
|
|
4
4
|
import { MJUserView_, MJUserViewResolverBase } from '../generated/generated.js';
|
|
5
5
|
import { UserResolver } from './UserResolver.js';
|
|
6
|
-
import { MJUserViewEntity,
|
|
6
|
+
import { MJUserViewEntity, MJUserViewEntityExtended } from '@memberjunction/core-entities';
|
|
7
7
|
import { GetReadOnlyProvider } from '../util.js';
|
|
8
8
|
|
|
9
9
|
@Resolver(MJUserView_)
|
|
@@ -54,7 +54,7 @@ export class UserViewResolver extends MJUserViewResolverBase {
|
|
|
54
54
|
// filter state which in turn will be used to update the where clause in the entity sub-class.
|
|
55
55
|
const p = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true});
|
|
56
56
|
const u = this.GetUserFromPayload(userPayload);
|
|
57
|
-
const viewEntity = <
|
|
57
|
+
const viewEntity = <MJUserViewEntityExtended>await p.GetEntityObject('MJ: User Views', u);
|
|
58
58
|
await viewEntity.Load(ID);
|
|
59
59
|
viewEntity.UpdateWhereClause();
|
|
60
60
|
|
|
@@ -5,7 +5,7 @@ import { ChatMessageRole } from '@memberjunction/ai';
|
|
|
5
5
|
import { PubSubEngine } from 'type-graphql';
|
|
6
6
|
import { UserPayload } from '../types.js';
|
|
7
7
|
import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
|
|
8
|
-
import {
|
|
8
|
+
import { MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Task definition from LLM response
|
|
@@ -268,9 +268,9 @@ export class TaskOrchestrator {
|
|
|
268
268
|
/**
|
|
269
269
|
* Find agent by name
|
|
270
270
|
*/
|
|
271
|
-
private async findAgentByName(agentName: string): Promise<
|
|
271
|
+
private async findAgentByName(agentName: string): Promise<MJAIAgentEntityExtended | null> {
|
|
272
272
|
const rv = new RunView();
|
|
273
|
-
const result = await rv.RunView<
|
|
273
|
+
const result = await rv.RunView<MJAIAgentEntityExtended>({
|
|
274
274
|
EntityName: 'MJ: AI Agents',
|
|
275
275
|
ExtraFilter: `Name='${agentName.replace(/'/g, "''")}'`,
|
|
276
276
|
ResultType: 'entity_object'
|
|
@@ -475,7 +475,7 @@ export class TaskOrchestrator {
|
|
|
475
475
|
|
|
476
476
|
// Load the agent entity
|
|
477
477
|
const md = new Metadata();
|
|
478
|
-
const agentEntity = await md.GetEntityObject<
|
|
478
|
+
const agentEntity = await md.GetEntityObject<MJAIAgentEntityExtended>('MJ: AI Agents', this.contextUser);
|
|
479
479
|
const loaded = await agentEntity.Load(task.AgentID!);
|
|
480
480
|
if (!loaded) {
|
|
481
481
|
throw new Error(`Agent with ID ${task.AgentID} not found`);
|
|
@@ -695,7 +695,7 @@ export class TaskOrchestrator {
|
|
|
695
695
|
private async createArtifactFromOutput(
|
|
696
696
|
output: { type: 'message' | 'payload', content: any },
|
|
697
697
|
conversationDetailId: string,
|
|
698
|
-
agent:
|
|
698
|
+
agent: MJAIAgentEntityExtended,
|
|
699
699
|
taskName: string
|
|
700
700
|
): Promise<void> {
|
|
701
701
|
try {
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AggregateExpression, DatabaseProviderBase, UserInfo } from '@memberjunction/core';
|
|
2
|
-
import {
|
|
2
|
+
import { MJUserViewEntityExtended } from '@memberjunction/core-entities';
|
|
3
3
|
import { GraphQLSchema } from 'graphql';
|
|
4
4
|
import sql from 'mssql';
|
|
5
5
|
import { getSystemUser } from './auth/index.js';
|
|
@@ -68,7 +68,7 @@ export type DirectiveBuilder = {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
export type RunViewGenericParams = {
|
|
71
|
-
viewInfo:
|
|
71
|
+
viewInfo: MJUserViewEntityExtended;
|
|
72
72
|
provider: DatabaseProviderBase;
|
|
73
73
|
extraFilter: string;
|
|
74
74
|
orderBy: string;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entityPermissions.server.d.ts","sourceRoot":"","sources":["../../src/entitySubclasses/entityPermissions.server.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAIzE;;;;;;GAMG;AACH,qBACa,8BAA+B,SAAQ,wBAAwB;IAC1E,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,CAAM;IAC/C,SAAS,CAAC,MAAM,CAAC,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAQ;IACvD,SAAS,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAQ;IAChE,SAAS,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAgC;IACzE,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAoB;IACrD,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAqB;IACnD,SAAS,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAA6B;IAGlE,SAAS,CAAC,MAAM,CAAC,gBAAgB,IAAI,MAAM;IAI3C,WAAkB,aAAa,IAAI,MAAM,EAAE,CAE1C;WAEa,UAAU,IAAI,IAAI;WAIlB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMhD,SAAS,CAAC,MAAM,CAAC,yBAAyB,IAAI,IAAI;IAUlD,SAAS,CAAC,MAAM,CAAC,oBAAoB,IAAI,IAAI;qBAMtB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B1C,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAO7C,MAAM,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;CAQtE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entityPermissions.server.js","sourceRoot":"","sources":["../../src/entitySubclasses/entityPermissions.server.ts"],"names":[],"mappings":"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"}
|