@memberjunction/server 2.92.0 → 2.94.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.
@@ -1710,6 +1710,13 @@ each time the agent processes a prompt step.`})
1710
1710
  @Field(() => Int, {nullable: true, description: `Effort level that was actually used during this agent run execution (1-100, where 1=minimal effort, 100=maximum effort). This is the resolved effort level after applying the precedence hierarchy: runtime override > agent default > prompt defaults.`})
1711
1711
  EffortLevel?: number;
1712
1712
 
1713
+ @Field({nullable: true, description: `Optional name for the agent run to help identify and tag runs for easier reference`})
1714
+ @MaxLength(510)
1715
+ RunName?: string;
1716
+
1717
+ @Field({nullable: true, description: `Human-readable notes and comments about this agent run`})
1718
+ Comments?: string;
1719
+
1713
1720
  @Field({nullable: true})
1714
1721
  @MaxLength(510)
1715
1722
  Agent?: string;
@@ -1854,6 +1861,12 @@ export class CreateAIAgentRunInput {
1854
1861
 
1855
1862
  @Field(() => Int, { nullable: true })
1856
1863
  EffortLevel: number | null;
1864
+
1865
+ @Field({ nullable: true })
1866
+ RunName: string | null;
1867
+
1868
+ @Field({ nullable: true })
1869
+ Comments: string | null;
1857
1870
  }
1858
1871
 
1859
1872
 
@@ -1967,6 +1980,12 @@ export class UpdateAIAgentRunInput {
1967
1980
  @Field(() => Int, { nullable: true })
1968
1981
  EffortLevel?: number | null;
1969
1982
 
1983
+ @Field({ nullable: true })
1984
+ RunName?: string | null;
1985
+
1986
+ @Field({ nullable: true })
1987
+ Comments?: string | null;
1988
+
1970
1989
  @Field(() => [KeyValuePairInput], { nullable: true })
1971
1990
  OldValues___?: KeyValuePairInput[];
1972
1991
  }
@@ -39142,6 +39161,9 @@ export class ComponentLibrary_ {
39142
39161
  @Field({nullable: true, description: `JSON configuration for library-specific lint rules that are applied during component validation. This field contains structured rules that define how components using this library should be validated, including DOM element requirements, initialization patterns, lifecycle methods, and common error patterns. Example structure: {"initialization": {"constructorName": "Chart", "elementType": "canvas"}, "lifecycle": {"requiredMethods": ["render"], "cleanupMethods": ["destroy"]}}. The linter dynamically applies these rules based on the libraries referenced in a component spec, enabling extensible validation without hardcoding library-specific logic.`})
39143
39162
  LintRules?: string;
39144
39163
 
39164
+ @Field({nullable: true, description: `JSON object defining dependencies for this component library. Format: { "libraryName": "versionSpec", ... }. Version specifications follow NPM-style syntax (e.g., "~1.0.0", "^1.2.3", "2.3.4"). Dependencies are loaded before this library to ensure proper execution context.`})
39165
+ Dependencies?: string;
39166
+
39145
39167
  @Field(() => [ComponentLibraryLink_])
39146
39168
  MJ_ComponentLibraryLinks_LibraryIDArray: ComponentLibraryLink_[]; // Link to MJ_ComponentLibraryLinks
39147
39169
 
@@ -39184,6 +39206,9 @@ export class CreateComponentLibraryInput {
39184
39206
 
39185
39207
  @Field({ nullable: true })
39186
39208
  LintRules: string | null;
39209
+
39210
+ @Field({ nullable: true })
39211
+ Dependencies: string | null;
39187
39212
  }
39188
39213
 
39189
39214
 
@@ -39225,6 +39250,9 @@ export class UpdateComponentLibraryInput {
39225
39250
  @Field({ nullable: true })
39226
39251
  LintRules?: string | null;
39227
39252
 
39253
+ @Field({ nullable: true })
39254
+ Dependencies?: string | null;
39255
+
39228
39256
  @Field(() => [KeyValuePairInput], { nullable: true })
39229
39257
  OldValues___?: KeyValuePairInput[];
39230
39258
  }
@@ -42644,6 +42672,13 @@ export class AIPromptRun_ {
42644
42672
  @Field(() => Int, {nullable: true, description: `Effort level that was actually used during this prompt run execution (1-100, where 1=minimal effort, 100=maximum effort). This is the resolved effort level after applying the precedence hierarchy: runtime override > agent default > prompt default > provider default.`})
42645
42673
  EffortLevel?: number;
42646
42674
 
42675
+ @Field({nullable: true, description: `Optional name for the prompt run to help identify and tag runs for easier reference`})
42676
+ @MaxLength(510)
42677
+ RunName?: string;
42678
+
42679
+ @Field({nullable: true, description: `Human-readable notes and comments about this prompt run`})
42680
+ Comments?: string;
42681
+
42647
42682
  @Field()
42648
42683
  @MaxLength(510)
42649
42684
  Prompt: string;
@@ -42925,6 +42960,12 @@ export class CreateAIPromptRunInput {
42925
42960
 
42926
42961
  @Field(() => Int, { nullable: true })
42927
42962
  EffortLevel: number | null;
42963
+
42964
+ @Field({ nullable: true })
42965
+ RunName: string | null;
42966
+
42967
+ @Field({ nullable: true })
42968
+ Comments: string | null;
42928
42969
  }
42929
42970
 
42930
42971
 
@@ -43170,6 +43211,12 @@ export class UpdateAIPromptRunInput {
43170
43211
  @Field(() => Int, { nullable: true })
43171
43212
  EffortLevel?: number | null;
43172
43213
 
43214
+ @Field({ nullable: true })
43215
+ RunName?: string | null;
43216
+
43217
+ @Field({ nullable: true })
43218
+ Comments?: string | null;
43219
+
43173
43220
  @Field(() => [KeyValuePairInput], { nullable: true })
43174
43221
  OldValues___?: KeyValuePairInput[];
43175
43222
  }
@@ -43363,6 +43410,16 @@ permanently, Warn means validation failed but execution continues.`})
43363
43410
  detailed information about what validation rules failed.`})
43364
43411
  FinalPayloadValidationMessages?: string;
43365
43412
 
43413
+ @Field({nullable: true, description: `Optional reference to parent step for tracking hierarchical relationships like code->test->fix->code cycles`})
43414
+ @MaxLength(16)
43415
+ ParentID?: string;
43416
+
43417
+ @Field({nullable: true, description: `Human-readable notes and comments about this agent run step`})
43418
+ Comments?: string;
43419
+
43420
+ @Field(() => [AIAgentRunStep_])
43421
+ MJ_AIAgentRunSteps_ParentIDArray: AIAgentRunStep_[]; // Link to MJ_AIAgentRunSteps
43422
+
43366
43423
  }
43367
43424
 
43368
43425
  //****************************************************************************
@@ -43423,6 +43480,12 @@ export class CreateAIAgentRunStepInput {
43423
43480
 
43424
43481
  @Field({ nullable: true })
43425
43482
  FinalPayloadValidationMessages: string | null;
43483
+
43484
+ @Field({ nullable: true })
43485
+ ParentID: string | null;
43486
+
43487
+ @Field({ nullable: true })
43488
+ Comments: string | null;
43426
43489
  }
43427
43490
 
43428
43491
 
@@ -43485,6 +43548,12 @@ export class UpdateAIAgentRunStepInput {
43485
43548
  @Field({ nullable: true })
43486
43549
  FinalPayloadValidationMessages?: string | null;
43487
43550
 
43551
+ @Field({ nullable: true })
43552
+ ParentID?: string | null;
43553
+
43554
+ @Field({ nullable: true })
43555
+ Comments?: string | null;
43556
+
43488
43557
  @Field(() => [KeyValuePairInput], { nullable: true })
43489
43558
  OldValues___?: KeyValuePairInput[];
43490
43559
  }
@@ -43547,6 +43616,17 @@ export class AIAgentRunStepResolver extends ResolverBase {
43547
43616
  return result;
43548
43617
  }
43549
43618
 
43619
+ @FieldResolver(() => [AIAgentRunStep_])
43620
+ async MJ_AIAgentRunSteps_ParentIDArray(@Root() aiagentrunstep_: AIAgentRunStep_, @Ctx() { dataSources, userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
43621
+ this.CheckUserReadPermissions('MJ: AI Agent Run Steps', userPayload);
43622
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
43623
+ const connPool = GetReadOnlyDataSource(dataSources, { allowFallbackToReadWrite: true });
43624
+ const sSQL = `SELECT * FROM [${Metadata.Provider.ConfigData.MJCoreSchemaName}].[vwAIAgentRunSteps] WHERE [ParentID]='${aiagentrunstep_.ID}' ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: AI Agent Run Steps', userPayload, EntityPermissionType.Read, 'AND');
43625
+ const rows = await SQLServerDataProvider.ExecuteSQLWithPool(connPool, sSQL, undefined, this.GetUserFromPayload(userPayload));
43626
+ const result = this.ArrayMapFieldNamesToCodeNames('MJ: AI Agent Run Steps', rows);
43627
+ return result;
43628
+ }
43629
+
43550
43630
  @Mutation(() => AIAgentRunStep_)
43551
43631
  async CreateAIAgentRunStep(
43552
43632
  @Arg('input', () => CreateAIAgentRunStepInput) input: CreateAIAgentRunStepInput,
@@ -581,7 +581,16 @@ export class RunViewResolver extends ResolverBase {
581
581
  try {
582
582
  const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
583
583
  const rawData = await super.RunViewByNameGeneric(input, provider, userPayload, pubSub);
584
- if (rawData === null) return null;
584
+ if (rawData === null) {
585
+ return {
586
+ Results: [],
587
+ Success: false,
588
+ ErrorMessage: `Failed to execute view: ${input.ViewName}`,
589
+ RowCount: 0,
590
+ TotalRowCount: 0,
591
+ ExecutionTime: 0
592
+ };
593
+ }
585
594
 
586
595
  const entity = provider.Entities.find((e) => e.Name === input.ViewName);
587
596
  const entityId = entity ? entity.ID : null;
@@ -596,8 +605,16 @@ export class RunViewResolver extends ResolverBase {
596
605
  ErrorMessage: rawData?.ErrorMessage,
597
606
  };
598
607
  } catch (err) {
608
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
599
609
  LogError(err);
600
- return null;
610
+ return {
611
+ Results: [],
612
+ Success: false,
613
+ ErrorMessage: errorMessage,
614
+ RowCount: 0,
615
+ TotalRowCount: 0,
616
+ ExecutionTime: 0
617
+ };
601
618
  }
602
619
  }
603
620
 
@@ -611,7 +628,16 @@ export class RunViewResolver extends ResolverBase {
611
628
  try {
612
629
  const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
613
630
  const rawData = await super.RunViewByIDGeneric(input, provider, userPayload, pubSub);
614
- if (rawData === null) return null;
631
+ if (rawData === null) {
632
+ return {
633
+ Results: [],
634
+ Success: false,
635
+ ErrorMessage: `Failed to execute view with ID: ${input.ViewID}`,
636
+ RowCount: 0,
637
+ TotalRowCount: 0,
638
+ ExecutionTime: 0
639
+ };
640
+ }
615
641
 
616
642
  const viewInfo = super.safeFirstArrayElement<UserViewEntityExtended>(await super.findBy<UserViewEntityExtended>(provider, "User Views", { ID: input.ViewID }, userPayload.userRecord));
617
643
  const returnData = this.processRawData(rawData.Results, viewInfo.EntityID);
@@ -625,8 +651,16 @@ export class RunViewResolver extends ResolverBase {
625
651
  ErrorMessage: rawData?.ErrorMessage,
626
652
  };
627
653
  } catch (err) {
654
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
628
655
  LogError(err);
629
- return null;
656
+ return {
657
+ Results: [],
658
+ Success: false,
659
+ ErrorMessage: errorMessage,
660
+ RowCount: 0,
661
+ TotalRowCount: 0,
662
+ ExecutionTime: 0
663
+ };
630
664
  }
631
665
  }
632
666
 
@@ -640,12 +674,29 @@ export class RunViewResolver extends ResolverBase {
640
674
  try {
641
675
  const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
642
676
  const rawData = await super.RunDynamicViewGeneric(input, provider, userPayload, pubSub);
643
- if (rawData === null) return null;
677
+ if (rawData === null) {
678
+ return {
679
+ Results: [],
680
+ Success: false,
681
+ ErrorMessage: 'Failed to execute dynamic view',
682
+ RowCount: 0,
683
+ TotalRowCount: 0,
684
+ ExecutionTime: 0
685
+ };
686
+ }
644
687
 
645
688
  const entity = provider.Entities.find((e) => e.Name === input.EntityName);
646
689
  if (!entity) {
647
- LogError(new Error(`Entity with name ${input.EntityName} not found`));
648
- return null;
690
+ const errorMsg = `Entity ${input.EntityName} not found in metadata`;
691
+ LogError(new Error(errorMsg));
692
+ return {
693
+ Results: [],
694
+ Success: false,
695
+ ErrorMessage: errorMsg,
696
+ RowCount: 0,
697
+ TotalRowCount: 0,
698
+ ExecutionTime: 0
699
+ };
649
700
  }
650
701
  const returnData = this.processRawData(rawData.Results, entity.ID);
651
702
  return {
@@ -658,8 +709,16 @@ export class RunViewResolver extends ResolverBase {
658
709
  ErrorMessage: rawData?.ErrorMessage,
659
710
  };
660
711
  } catch (err) {
712
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
661
713
  LogError(err);
662
- return null;
714
+ return {
715
+ Results: [],
716
+ Success: false,
717
+ ErrorMessage: errorMessage,
718
+ RowCount: 0,
719
+ TotalRowCount: 0,
720
+ ExecutionTime: 0
721
+ };
663
722
  }
664
723
  }
665
724
 
@@ -103,20 +103,19 @@ export class ReportResolverExtended {
103
103
  const title = skipData.title ? skipData.title : skipData.reportTitle ? skipData.reportTitle : 'Untitled Report';
104
104
  report.Name = title;
105
105
  report.Description = skipData.userExplanation ? skipData.userExplanation : '';
106
- report.ConversationID = result[0].ConversationID;
106
+ report.ConversationID = result.recordset[0].ConversationID;
107
107
  report.ConversationDetailID = ConversationDetailID;
108
108
 
109
109
  const dc: DataContext = new DataContext();
110
- await dc.LoadMetadata(result[0].DataContextID, u);
110
+ await dc.LoadMetadata(result.recordset[0].DataContextID, u);
111
111
  const newDataContext = await DataContext.Clone(dc, false, u);
112
112
  if (!newDataContext) throw new Error('Error cloning data context');
113
113
  report.DataContextID = newDataContext.ID;
114
114
 
115
115
  // next, strip out the messags from the SkipData object to put them into our Report Configuration as we dont need to store that information as we have a
116
- // link back to the conversation and conversation detail
117
- const newSkipData: SkipAPIAnalysisCompleteResponse = JSON.parse(JSON.stringify(skipData));
118
- newSkipData.messages = [];
119
- report.Configuration = JSON.stringify(newSkipData);
116
+ // link back to the conversation and conversation detail, skipData can be modified as it is a copy of the data doesn't affect the original source
117
+ skipData.messages = [];
118
+ report.Configuration = JSON.stringify(skipData);
120
119
 
121
120
  report.SharingScope = 'None';
122
121
  report.UserID = u.ID;
@@ -1,4 +1,4 @@
1
- import { Resolver, Mutation, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, Subscription, Root, ResolverFilterData, ID } from 'type-graphql';
1
+ import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, Subscription, Root, ResolverFilterData, ID } from 'type-graphql';
2
2
  import { UserPayload } from '../types.js';
3
3
  import { LogError, LogStatus } from '@memberjunction/core';
4
4
  import { AIAgentEntityExtended } from '@memberjunction/core-entities';
@@ -7,6 +7,7 @@ import { ExecuteAgentResult } from '@memberjunction/ai-core-plus';
7
7
  import { AIEngine } from '@memberjunction/aiengine';
8
8
  import { ResolverBase } from '../generic/ResolverBase.js';
9
9
  import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
10
+ import { RequireSystemUser } from '../directives/RequireSystemUser.js';
10
11
 
11
12
  @ObjectType()
12
13
  export class AIAgentRunResult {
@@ -297,18 +298,22 @@ export class RunAIAgentResolver extends ResolverBase {
297
298
  };
298
299
  }
299
300
 
300
- @Mutation(() => AIAgentRunResult)
301
- async RunAIAgent(
302
- @Arg('agentId') agentId: string,
303
- @Ctx() { userPayload }: { userPayload: UserPayload },
304
- @Arg('messages') messagesJson: string,
305
- @Arg('sessionId') sessionId: string,
306
- @PubSub() pubSub: PubSubEngine,
307
- @Arg('data', { nullable: true }) data?: string,
308
- @Arg('templateData', { nullable: true }) templateData?: string,
309
- @Arg('lastRunId', { nullable: true }) lastRunId?: string,
310
- @Arg('autoPopulateLastRunPayload', { nullable: true }) autoPopulateLastRunPayload?: boolean,
311
- @Arg('configurationId', { nullable: true }) configurationId?: string
301
+ /**
302
+ * Internal method that handles the core AI agent execution logic.
303
+ * This method is called by both the regular and system user resolvers.
304
+ * @private
305
+ */
306
+ private async executeAIAgent(
307
+ agentId: string,
308
+ userPayload: UserPayload,
309
+ messagesJson: string,
310
+ sessionId: string,
311
+ pubSub: PubSubEngine,
312
+ data?: string,
313
+ templateData?: string,
314
+ lastRunId?: string,
315
+ autoPopulateLastRunPayload?: boolean,
316
+ configurationId?: string
312
317
  ): Promise<AIAgentRunResult> {
313
318
  const startTime = Date.now();
314
319
 
@@ -441,5 +446,67 @@ export class RunAIAgentResolver extends ResolverBase {
441
446
  };
442
447
  this.PublishStreamingUpdate(pubSub, completeMsg, userPayload);
443
448
  }
449
+
450
+ /**
451
+ * Public mutation for regular users to run AI agents with authentication.
452
+ */
453
+ @Mutation(() => AIAgentRunResult)
454
+ async RunAIAgent(
455
+ @Arg('agentId') agentId: string,
456
+ @Ctx() { userPayload }: { userPayload: UserPayload },
457
+ @Arg('messages') messagesJson: string,
458
+ @Arg('sessionId') sessionId: string,
459
+ @PubSub() pubSub: PubSubEngine,
460
+ @Arg('data', { nullable: true }) data?: string,
461
+ @Arg('templateData', { nullable: true }) templateData?: string,
462
+ @Arg('lastRunId', { nullable: true }) lastRunId?: string,
463
+ @Arg('autoPopulateLastRunPayload', { nullable: true }) autoPopulateLastRunPayload?: boolean,
464
+ @Arg('configurationId', { nullable: true }) configurationId?: string
465
+ ): Promise<AIAgentRunResult> {
466
+ return this.executeAIAgent(
467
+ agentId,
468
+ userPayload,
469
+ messagesJson,
470
+ sessionId,
471
+ pubSub,
472
+ data,
473
+ templateData,
474
+ lastRunId,
475
+ autoPopulateLastRunPayload,
476
+ configurationId
477
+ );
478
+ }
479
+
480
+ /**
481
+ * System user query for running AI agents with elevated privileges.
482
+ * Requires the @RequireSystemUser decorator to ensure only system users can access.
483
+ */
484
+ @RequireSystemUser()
485
+ @Query(() => AIAgentRunResult)
486
+ async RunAIAgentSystemUser(
487
+ @Arg('agentId') agentId: string,
488
+ @Ctx() { userPayload }: { userPayload: UserPayload },
489
+ @Arg('messages') messagesJson: string,
490
+ @Arg('sessionId') sessionId: string,
491
+ @PubSub() pubSub: PubSubEngine,
492
+ @Arg('data', { nullable: true }) data?: string,
493
+ @Arg('templateData', { nullable: true }) templateData?: string,
494
+ @Arg('lastRunId', { nullable: true }) lastRunId?: string,
495
+ @Arg('autoPopulateLastRunPayload', { nullable: true }) autoPopulateLastRunPayload?: boolean,
496
+ @Arg('configurationId', { nullable: true }) configurationId?: string
497
+ ): Promise<AIAgentRunResult> {
498
+ return this.executeAIAgent(
499
+ agentId,
500
+ userPayload,
501
+ messagesJson,
502
+ sessionId,
503
+ pubSub,
504
+ data,
505
+ templateData,
506
+ lastRunId,
507
+ autoPopulateLastRunPayload,
508
+ configurationId
509
+ );
510
+ }
444
511
 
445
512
  }