@memberjunction/server 5.2.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/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/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/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/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/package.json +53 -52
- package/src/agents/skip-sdk.ts +130 -5
- package/src/generated/generated.ts +126 -28
- package/src/resolvers/RunAIAgentResolver.ts +94 -11
- package/src/resolvers/RunTestResolver.ts +41 -3
|
@@ -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
|
/**
|
|
@@ -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.
|
|
@@ -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({
|