@memberjunction/server 6.2.0-edge.1 → 6.2.0-edge.2

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 (93) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/generated/graphql-schemas/__mj.d.ts +1010 -0
  3. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  4. package/dist/generated/graphql-schemas/__mj.js +5178 -3
  5. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  6. package/dist/generic/FireAndForgetHeartbeat.d.ts +15 -1
  7. package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -1
  8. package/dist/generic/FireAndForgetHeartbeat.js +16 -2
  9. package/dist/generic/FireAndForgetHeartbeat.js.map +1 -1
  10. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +31 -0
  11. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -0
  12. package/dist/generic/OrphanedConversationDetailReconciler.js +215 -0
  13. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -0
  14. package/dist/generic/PushStatusResolver.d.ts +38 -0
  15. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  16. package/dist/generic/PushStatusResolver.js +84 -1
  17. package/dist/generic/PushStatusResolver.js.map +1 -1
  18. package/dist/generic/ResolverBase.d.ts +13 -1
  19. package/dist/generic/ResolverBase.d.ts.map +1 -1
  20. package/dist/generic/ResolverBase.js +49 -47
  21. package/dist/generic/ResolverBase.js.map +1 -1
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +90 -2
  25. package/dist/index.js.map +1 -1
  26. package/dist/resolvers/AgentRunStatusPublisher.d.ts +55 -0
  27. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -0
  28. package/dist/resolvers/AgentRunStatusPublisher.js +141 -0
  29. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -0
  30. package/dist/resolvers/ConversationRunEventsResolver.d.ts +90 -0
  31. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -0
  32. package/dist/resolvers/ConversationRunEventsResolver.js +283 -0
  33. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -0
  34. package/dist/resolvers/DuplicateEntryCheckResolver.d.ts +98 -0
  35. package/dist/resolvers/DuplicateEntryCheckResolver.d.ts.map +1 -0
  36. package/dist/resolvers/DuplicateEntryCheckResolver.js +227 -0
  37. package/dist/resolvers/DuplicateEntryCheckResolver.js.map +1 -0
  38. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  39. package/dist/resolvers/FetchEntityVectorsResolver.js +2 -1
  40. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  41. package/dist/resolvers/MergeRecordsResolver.d.ts +7 -0
  42. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  43. package/dist/resolvers/MergeRecordsResolver.js +16 -2
  44. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  45. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +44 -6
  46. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  47. package/dist/resolvers/RealtimeClientSessionResolver.js +90 -34
  48. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  49. package/dist/resolvers/RunAIAgentResolver.d.ts +0 -16
  50. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  51. package/dist/resolvers/RunAIAgentResolver.js +23 -153
  52. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  53. package/dist/resolvers/RunDecisionResolver.d.ts +149 -0
  54. package/dist/resolvers/RunDecisionResolver.d.ts.map +1 -0
  55. package/dist/resolvers/RunDecisionResolver.js +396 -0
  56. package/dist/resolvers/RunDecisionResolver.js.map +1 -0
  57. package/dist/resolvers/VectorizeEntityResolver.d.ts +1 -1
  58. package/dist/resolvers/VectorizeEntityResolver.d.ts.map +1 -1
  59. package/dist/resolvers/VectorizeEntityResolver.js +14 -10
  60. package/dist/resolvers/VectorizeEntityResolver.js.map +1 -1
  61. package/package.json +94 -92
  62. package/src/__tests__/AgentRunStatusPublisher.test.ts +175 -0
  63. package/src/__tests__/DuplicateEntryCheckResolver.test.ts +300 -0
  64. package/src/__tests__/RealtimeClientSessionResolver.test.ts +216 -2
  65. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +7 -1
  66. package/src/__tests__/RemoteBrowserAudioStream.test.ts +7 -1
  67. package/src/__tests__/RemoteBrowserSnapshot.test.ts +7 -1
  68. package/src/__tests__/ResolverBase.clientOldValues.test.ts +52 -0
  69. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +182 -12
  70. package/src/__tests__/RunAIAgentResolver.streaming.test.ts +18 -27
  71. package/src/__tests__/RunDecisionResolver.test.ts +699 -0
  72. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -0
  73. package/src/__tests__/VectorizeEntityResolver.test.ts +106 -0
  74. package/src/__tests__/conversationRunEvents.test.ts +399 -0
  75. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +283 -0
  76. package/src/__tests__/pushStatusFanOut.test.ts +206 -0
  77. package/src/auth/__tests__/backward-compatibility.test.ts +1 -0
  78. package/src/generated/graphql-schemas/__mj.ts +3637 -4
  79. package/src/generic/FireAndForgetHeartbeat.ts +16 -2
  80. package/src/generic/OrphanedConversationDetailReconciler.ts +267 -0
  81. package/src/generic/PushStatusResolver.ts +97 -1
  82. package/src/generic/ResolverBase.ts +49 -49
  83. package/src/index.ts +102 -2
  84. package/src/resolvers/AgentRunStatusPublisher.ts +175 -0
  85. package/src/resolvers/ConversationRunEventsResolver.ts +308 -0
  86. package/src/resolvers/DuplicateEntryCheckResolver.ts +232 -0
  87. package/src/resolvers/FetchEntityVectorsResolver.ts +2 -1
  88. package/src/resolvers/MergeRecordsResolver.ts +14 -2
  89. package/src/resolvers/RealtimeClientSessionResolver.ts +93 -35
  90. package/src/resolvers/RunAIAgentResolver.ts +28 -174
  91. package/src/resolvers/RunDecisionResolver.ts +435 -0
  92. package/src/resolvers/VectorizeEntityResolver.ts +16 -11
  93. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +93 -0
@@ -4,10 +4,11 @@ import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo,
4
4
  import { MJConversationDetailEntity, MJConversationDetailAttachmentEntity, MJConversationDetailArtifactEntity, MJArtifactVersionEntity, MJAIAgentRequestEntity, ArtifactMetadataEngine, ConversationEngine } from '@memberjunction/core-entities';
5
5
  import { RouteArtifact } from './artifact-routing.js';
6
6
  import { AgentRunner, ArtifactToolManager } from '@memberjunction/ai-agents';
7
- import { MJAIAgentEntityExtended, MJAIAgentRunEntityExtended, ExecuteAgentResult, ConversationUtility, AttachmentData, AgentExecutionStreamingCallback } from '@memberjunction/ai-core-plus';
7
+ import { MJAIAgentEntityExtended, MJAIAgentRunEntityExtended, ExecuteAgentResult, ConversationUtility, AttachmentData } from '@memberjunction/ai-core-plus';
8
8
  import { AIEngine } from '@memberjunction/aiengine';
9
9
  import { ChatMessage, ChatMessageContent } from '@memberjunction/ai';
10
10
  import { ResolverBase } from '../generic/ResolverBase.js';
11
+ import { AgentRunStatusPublisher } from './AgentRunStatusPublisher.js';
11
12
  import { StartLivenessPulse } from '../generic/FireAndForgetHeartbeat.js';
12
13
  import { RequireSystemUser } from '../directives/RequireSystemUser.js';
13
14
  import { GetReadWriteProvider } from '../util.js';
@@ -25,6 +26,11 @@ import { NotificationEngine } from '@memberjunction/notifications';
25
26
  */
26
27
  const INLINE_SIZE_CAP = 100 * 1024;
27
28
 
29
+ /** Progress metadata is an untyped bag. A run is an object that carries an ID field. */
30
+ function isAgentRunEntity(value: unknown): value is MJAIAgentRunEntityExtended {
31
+ return typeof value === 'object' && value !== null && 'ID' in value;
32
+ }
33
+
28
34
  @ObjectType()
29
35
  export class AIAgentRunResult {
30
36
  @Field()
@@ -249,111 +255,6 @@ export class RunAIAgentResolver extends ResolverBase {
249
255
  return agentEntity;
250
256
  }
251
257
 
252
- /**
253
- * Create streaming progress callback
254
- */
255
- private createProgressCallback(pubSub: PubSubEngine, sessionId: string, userPayload: UserPayload, agentRunRef: { current: any }) {
256
- return (progress: any) => {
257
- // Capture the agent run into the ref as soon as any progress event carries it (even
258
- // "noise" steps), so the fire-and-forget liveness pulse can read its id/status mid-run
259
- // rather than only after RunAgentInConversation returns.
260
- if (progress.metadata?.agentRun) {
261
- agentRunRef.current = progress.metadata.agentRun;
262
- }
263
-
264
- // Only publish progress for significant steps (not initialization noise)
265
- const significantSteps = ['prompt_execution', 'action_execution', 'subagent_execution', 'decision_processing'];
266
- if (!significantSteps.includes(progress.step)) {
267
- console.log(`🔇 Skipping noise progress: ${progress.step}`);
268
- return;
269
- }
270
-
271
- // Get the agent run from the progress metadata or use the ref
272
- const agentRun = progress.metadata?.agentRun || agentRunRef.current;
273
- if (!agentRun) {
274
- console.error('❌ No agent run available for progress callback');
275
- return;
276
- }
277
-
278
- console.log('📡 Publishing progress update:', {
279
- step: progress.step,
280
- percentage: progress.percentage,
281
- message: progress.message,
282
- sessionId,
283
- agentRunId: agentRun.ID
284
- });
285
-
286
- // Publish progress updates with the full serialized agent run
287
- const progressMsg: AgentExecutionStreamMessage = {
288
- sessionId,
289
- agentRunId: agentRun.ID,
290
- type: 'progress',
291
- agentRun: agentRun.GetAll(), // Serialize the full agent run
292
- progress: {
293
- currentStep: progress.step,
294
- percentage: progress.percentage,
295
- message: progress.message,
296
- agentName: (progress.metadata as any)?.agentName || undefined,
297
- agentType: (progress.metadata as any)?.agentType || undefined,
298
- stepCount: (progress.metadata as any)?.stepCount || undefined,
299
- hierarchicalStep: (progress.metadata as any)?.hierarchicalStep || undefined
300
- },
301
- timestamp: new Date()
302
- };
303
- this.publishProgressUpdate(pubSub, progressMsg, userPayload);
304
- };
305
- }
306
-
307
- private publishProgressUpdate(pubSub: PubSubEngine, data: any, userPayload: UserPayload) {
308
- this.PublishStatusUpdate(pubSub, userPayload.sessionId, JSON.stringify({
309
- resolver: 'RunAIAgentResolver',
310
- type: 'ExecutionProgress',
311
- status: 'ok',
312
- data,
313
- }), userPayload);
314
- }
315
-
316
-
317
- private publishStreamingUpdate(pubSub: PubSubEngine, data: any, userPayload: UserPayload) {
318
- this.PublishStatusUpdate(pubSub, userPayload.sessionId, JSON.stringify({
319
- resolver: 'RunAIAgentResolver',
320
- type: 'StreamingContent',
321
- status: 'ok',
322
- data,
323
- }), userPayload);
324
- }
325
-
326
- /**
327
- * Create streaming content callback
328
- */
329
- private createStreamingCallback(pubSub: PubSubEngine, sessionId: string, userPayload: UserPayload, agentRunRef: { current: MJAIAgentRunEntityExtended | null }): AgentExecutionStreamingCallback {
330
- return (chunk) => {
331
- // Use the agent run from the ref
332
- const agentRun = agentRunRef.current;
333
- if (!agentRun) {
334
- console.error('❌ No agent run available for streaming callback');
335
- return;
336
- }
337
-
338
- // Publish streaming content with the full serialized agent run
339
- const streamMsg: AgentExecutionStreamMessage = {
340
- sessionId,
341
- agentRunId: agentRun.ID,
342
- type: 'streaming',
343
- agentRun: agentRun.GetAll(), // Include the full serialized agent run
344
- streaming: {
345
- content: chunk.content,
346
- isPartial: !chunk.isComplete,
347
- stepName: chunk.stepType,
348
- agentName: chunk.modelName,
349
- kind: chunk.kind
350
- },
351
- timestamp: new Date()
352
- };
353
- this.publishStreamingUpdate(pubSub, streamMsg, userPayload);
354
- };
355
- }
356
-
357
258
  /**
358
259
  * Internal method that handles the core AI agent execution logic.
359
260
  * This method is called by both the regular and system user resolvers.
@@ -434,6 +335,11 @@ export class RunAIAgentResolver extends ResolverBase {
434
335
 
435
336
  console.log(`🚀 Starting agent execution with sessionId: ${sessionId}`);
436
337
 
338
+ // The publisher owns every status message. The resolver still copies the run into
339
+ // agentRunRef, including noise steps, for the fire-and-forget liveness pulse.
340
+ const statusPublisher = new AgentRunStatusPublisher(pubSub, userPayload, sessionId);
341
+ const publishProgress = statusPublisher.OnProgress;
342
+
437
343
  // Execute the agent in conversation context - handles conversation, artifacts, etc.
438
344
  const conversationResult = await agentRunner.RunAgentInConversation({
439
345
  agent: agentEntity,
@@ -441,8 +347,14 @@ export class RunAIAgentResolver extends ResolverBase {
441
347
  payload: payload ? SafeJSONParse(payload) : undefined,
442
348
  contextUser: currentUser,
443
349
  sessionID: sessionId,
444
- onProgress: this.createProgressCallback(pubSub, sessionId, userPayload, agentRunRef),
445
- onStreaming: this.createStreamingCallback(pubSub, sessionId, userPayload, agentRunRef),
350
+ onProgress: (progress) => {
351
+ const fromEvent = progress.metadata?.agentRun;
352
+ if (isAgentRunEntity(fromEvent)) {
353
+ agentRunRef.current = fromEvent;
354
+ }
355
+ publishProgress(progress);
356
+ },
357
+ onStreaming: statusPublisher.OnStreaming,
446
358
  lastRunId: lastRunId,
447
359
  autoPopulateLastRunPayload: autoPopulateLastRunPayload,
448
360
  configurationId: configurationId,
@@ -526,7 +438,7 @@ export class RunAIAgentResolver extends ResolverBase {
526
438
  const returnResult = JSON.stringify(sanitizedResult);
527
439
 
528
440
  // Publish final events with enriched result data for fire-and-forget clients
529
- this.publishFinalEvents(pubSub, sessionId, userPayload, result, returnResult);
441
+ statusPublisher.PublishFinal(result, result.agentRun?.ConversationDetailID, returnResult);
530
442
 
531
443
  // Log completion
532
444
  if (result.success) {
@@ -602,7 +514,10 @@ export class RunAIAgentResolver extends ResolverBase {
602
514
  detail.Message = errorMessage;
603
515
  detail.Error = errorMessage;
604
516
  if (!(await detail.Save())) {
605
- LogError(`Failed to persist Error on conversation detail ${conversationDetailId}`);
517
+ LogError(
518
+ `Failed to persist Error on conversation detail ${conversationDetailId}: ` +
519
+ `${detail.LatestResult?.CompleteMessage?.trim() || 'no failure detail recorded'}`,
520
+ );
606
521
  }
607
522
  }
608
523
  }
@@ -611,58 +526,6 @@ export class RunAIAgentResolver extends ResolverBase {
611
526
  }
612
527
  }
613
528
 
614
- /**
615
- * Publish final streaming events (partial result and completion).
616
- * The completion event includes the full result JSON so clients using
617
- * fire-and-forget mode can receive the result via WebSocket.
618
- */
619
- private publishFinalEvents(
620
- pubSub: PubSubEngine,
621
- sessionId: string,
622
- userPayload: UserPayload,
623
- result: ExecuteAgentResult,
624
- resultJson?: string
625
- ) {
626
- if (result.agentRun) {
627
- // Get the last step from agent run
628
- let lastStep = 'Completed';
629
- if (result.agentRun?.Steps && result.agentRun.Steps.length > 0) {
630
- // Get the last step from the Steps array
631
- const lastStepEntity = result.agentRun.Steps[result.agentRun.Steps.length - 1];
632
- lastStep = lastStepEntity?.StepName || 'Completed';
633
- }
634
-
635
- // Publish partial result
636
- const partialResult: AgentPartialResult = {
637
- currentStep: lastStep,
638
- partialOutput: result.payload || undefined
639
- };
640
-
641
- const partialMsg: AgentExecutionStreamMessage = {
642
- sessionId,
643
- agentRunId: result.agentRun.ID,
644
- type: 'partial_result',
645
- partialResult,
646
- timestamp: new Date()
647
- };
648
- this.publishStreamingUpdate(pubSub, partialMsg, userPayload);
649
- }
650
-
651
- // Publish completion with conversationDetailId for client-side routing.
652
- // Include result data so fire-and-forget clients can receive the full result via WebSocket.
653
- const completionData: Record<string, unknown> = {
654
- sessionId,
655
- agentRunId: result.agentRun?.ID || 'unknown',
656
- type: 'complete',
657
- timestamp: new Date(),
658
- conversationDetailId: result.agentRun?.ConversationDetailID,
659
- success: result.success,
660
- errorMessage: result.agentRun?.ErrorMessage || undefined,
661
- result: resultJson || undefined
662
- };
663
- this.publishStreamingUpdate(pubSub, completionData, userPayload);
664
- }
665
-
666
529
  /**
667
530
  * Public mutation for regular users to run AI agents with authentication.
668
531
  * Supports fire-and-forget mode to avoid Azure proxy timeouts on long-running agent executions.
@@ -1381,18 +1244,9 @@ export class RunAIAgentResolver extends ResolverBase {
1381
1244
  const errorMessage = (error instanceof Error) ? error.message : 'Unknown background execution error';
1382
1245
  LogError(`🔥 Fire-and-forget background execution failed: ${errorMessage}`, undefined, error);
1383
1246
 
1384
- // Publish error completion event so the client knows the agent failed
1385
- const errorCompletionData: Record<string, unknown> = {
1386
- sessionId,
1387
- agentRunId: 'unknown',
1388
- type: 'complete',
1389
- timestamp: new Date(),
1390
- conversationDetailId,
1391
- success: false,
1392
- errorMessage,
1393
- result: JSON.stringify({ success: false, errorMessage })
1394
- };
1395
- this.publishStreamingUpdate(pubSub, errorCompletionData, userPayload);
1247
+ // Publish error completion event so the client knows the agent failed.
1248
+ // The publisher is created inside executeAIAgent, which rejected before returning one.
1249
+ new AgentRunStatusPublisher(pubSub, userPayload, sessionId).PublishFailure(conversationDetailId, errorMessage);
1396
1250
  }).finally(() => pulse.Stop());
1397
1251
  }
1398
1252