@memberjunction/server 6.1.0-edge.1 → 6.1.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 (136) hide show
  1. package/dist/auth/exampleNewUserSubClass.js +1 -1
  2. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  3. package/dist/auth/index.d.ts.map +1 -1
  4. package/dist/auth/index.js +9 -5
  5. package/dist/auth/index.js.map +1 -1
  6. package/dist/auth/magicLink/MagicLinkService.js +1 -1
  7. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  8. package/dist/auth/newUsers.js +1 -1
  9. package/dist/auth/newUsers.js.map +1 -1
  10. package/dist/context.js +2 -2
  11. package/dist/context.js.map +1 -1
  12. package/dist/generated/generated.d.ts +45 -4
  13. package/dist/generated/generated.d.ts.map +1 -1
  14. package/dist/generated/generated.js +229 -21
  15. package/dist/generated/generated.js.map +1 -1
  16. package/dist/generic/ResolverBase.d.ts +19 -3
  17. package/dist/generic/ResolverBase.d.ts.map +1 -1
  18. package/dist/generic/ResolverBase.js +51 -17
  19. package/dist/generic/ResolverBase.js.map +1 -1
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +85 -41
  23. package/dist/index.js.map +1 -1
  24. package/dist/logging/secretRedactor.d.ts +9 -0
  25. package/dist/logging/secretRedactor.d.ts.map +1 -1
  26. package/dist/logging/secretRedactor.js +39 -0
  27. package/dist/logging/secretRedactor.js.map +1 -1
  28. package/dist/middleware/BaseServerMiddleware.d.ts +5 -0
  29. package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
  30. package/dist/middleware/BaseServerMiddleware.js +5 -0
  31. package/dist/middleware/BaseServerMiddleware.js.map +1 -1
  32. package/dist/realtimeWidget/WidgetSessionService.js +1 -1
  33. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  34. package/dist/realtimeWidget/widgetGuestElevation.js +1 -1
  35. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  36. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  37. package/dist/resolvers/ActionResolver.js +12 -0
  38. package/dist/resolvers/ActionResolver.js.map +1 -1
  39. package/dist/resolvers/ComponentRegistryResolver.js +1 -1
  40. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  41. package/dist/resolvers/EntityPermissionResolver.js +1 -1
  42. package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
  43. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +9 -0
  44. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  45. package/dist/resolvers/IntegrationDiscoveryResolver.js +3 -1
  46. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  47. package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
  48. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
  49. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -0
  50. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  51. package/dist/resolvers/RealtimeClientSessionResolver.js +5 -0
  52. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  53. package/dist/resolvers/SearchEntitiesResolver.js +1 -1
  54. package/dist/resolvers/SearchEntitiesResolver.js.map +1 -1
  55. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  56. package/dist/resolvers/SqlLoggingConfigResolver.js +1 -1
  57. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  58. package/dist/resolvers/SyncRolesUsersResolver.js +2 -2
  59. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  60. package/dist/resolvers/UserFavoriteResolver.js +1 -1
  61. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  62. package/dist/rest/OAuthCallbackHandler.js +1 -1
  63. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  64. package/dist/rest/SignatureWebhookHandler.js +1 -1
  65. package/dist/rest/SignatureWebhookHandler.js.map +1 -1
  66. package/dist/services/DurableEntityActionTaskSubmitter.d.ts +26 -0
  67. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -0
  68. package/dist/services/DurableEntityActionTaskSubmitter.js +55 -0
  69. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -0
  70. package/dist/services/ScheduledJobsService.js +1 -1
  71. package/dist/services/ScheduledJobsService.js.map +1 -1
  72. package/dist/services/StartTaskGraphDispatcher.d.ts.map +1 -1
  73. package/dist/services/StartTaskGraphDispatcher.js +16 -1
  74. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  75. package/dist/services/TaskGraphActionRunner.d.ts +34 -0
  76. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -0
  77. package/dist/services/TaskGraphActionRunner.js +95 -0
  78. package/dist/services/TaskGraphActionRunner.js.map +1 -0
  79. package/dist/services/TaskGraphAgentRunner.d.ts +13 -0
  80. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  81. package/dist/services/TaskGraphAgentRunner.js +30 -0
  82. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  83. package/dist/services/TaskGraphPromptRunner.d.ts +31 -0
  84. package/dist/services/TaskGraphPromptRunner.d.ts.map +1 -0
  85. package/dist/services/TaskGraphPromptRunner.js +134 -0
  86. package/dist/services/TaskGraphPromptRunner.js.map +1 -0
  87. package/dist/telephony/RingCentralTelephonyService.js +1 -1
  88. package/dist/telephony/RingCentralTelephonyService.js.map +1 -1
  89. package/dist/telephony/TwilioTelephonyRouter.js +1 -1
  90. package/dist/telephony/TwilioTelephonyRouter.js.map +1 -1
  91. package/dist/telephony/VonageTelephonyRouter.js +1 -1
  92. package/dist/telephony/VonageTelephonyRouter.js.map +1 -1
  93. package/package.json +90 -90
  94. package/src/__tests__/RealtimeClientSessionResolver.test.ts +1 -1
  95. package/src/__tests__/ResolverBase.frozenRecordMapping.test.ts +133 -0
  96. package/src/__tests__/ResolverBase.transportMapping.test.ts +289 -0
  97. package/src/__tests__/SessionManager.test.ts +1 -1
  98. package/src/__tests__/WidgetSessionService.test.ts +1 -1
  99. package/src/__tests__/databaseAbstraction.test.ts +1 -1
  100. package/src/__tests__/magicLinkService.provisionLog.test.ts +1 -1
  101. package/src/__tests__/resolverBase.rls.test.ts +3 -0
  102. package/src/__tests__/secretRedactor.failOpen.test.ts +135 -0
  103. package/src/__tests__/widgetGuestElevation.test.ts +1 -1
  104. package/src/auth/exampleNewUserSubClass.ts +1 -1
  105. package/src/auth/index.ts +10 -6
  106. package/src/auth/magicLink/MagicLinkService.ts +1 -1
  107. package/src/auth/newUsers.ts +1 -1
  108. package/src/context.ts +2 -2
  109. package/src/generated/generated.ts +166 -17
  110. package/src/generic/ResolverBase.ts +52 -17
  111. package/src/index.ts +92 -42
  112. package/src/logging/secretRedactor.ts +43 -0
  113. package/src/middleware/BaseServerMiddleware.ts +5 -0
  114. package/src/realtimeWidget/WidgetSessionService.ts +1 -1
  115. package/src/realtimeWidget/widgetGuestElevation.ts +1 -1
  116. package/src/resolvers/ActionResolver.ts +13 -0
  117. package/src/resolvers/ComponentRegistryResolver.ts +1 -1
  118. package/src/resolvers/EntityPermissionResolver.ts +1 -1
  119. package/src/resolvers/IntegrationDiscoveryResolver.ts +12 -2
  120. package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
  121. package/src/resolvers/RealtimeClientSessionResolver.ts +13 -0
  122. package/src/resolvers/SearchEntitiesResolver.ts +1 -1
  123. package/src/resolvers/SqlLoggingConfigResolver.ts +1 -2
  124. package/src/resolvers/SyncRolesUsersResolver.ts +2 -2
  125. package/src/resolvers/UserFavoriteResolver.ts +1 -1
  126. package/src/rest/OAuthCallbackHandler.ts +1 -1
  127. package/src/rest/SignatureWebhookHandler.ts +1 -1
  128. package/src/services/DurableEntityActionTaskSubmitter.ts +78 -0
  129. package/src/services/ScheduledJobsService.ts +1 -1
  130. package/src/services/StartTaskGraphDispatcher.ts +17 -0
  131. package/src/services/TaskGraphActionRunner.ts +99 -0
  132. package/src/services/TaskGraphAgentRunner.ts +35 -1
  133. package/src/services/TaskGraphPromptRunner.ts +144 -0
  134. package/src/telephony/RingCentralTelephonyService.ts +1 -1
  135. package/src/telephony/TwilioTelephonyRouter.ts +1 -1
  136. package/src/telephony/VonageTelephonyRouter.ts +1 -1
@@ -1050,10 +1050,6 @@ export class MJActionExecutionLog_ {
1050
1050
  @MaxLength(100)
1051
1051
  User: string;
1052
1052
 
1053
- @Field({nullable: true})
1054
- @MaxLength(425)
1055
- EntityAction?: string;
1056
-
1057
1053
  @Field({nullable: true})
1058
1054
  @MaxLength(255)
1059
1055
  EntityActionInvocationType?: string;
@@ -2207,6 +2203,9 @@ export class MJAction_ {
2207
2203
  @Field(() => [MJAISkillAction_])
2208
2204
  MJAISkillActions_ActionIDArray: MJAISkillAction_[]; // Link to MJAISkillActions
2209
2205
 
2206
+ @Field(() => [MJTask_])
2207
+ MJTasks_ActionIDArray: MJTask_[]; // Link to MJTasks
2208
+
2210
2209
  }
2211
2210
 
2212
2211
  //****************************************************************************
@@ -2568,6 +2567,16 @@ export class MJActionResolver extends ResolverBase {
2568
2567
  return result;
2569
2568
  }
2570
2569
 
2570
+ @FieldResolver(() => [MJTask_])
2571
+ async MJTasks_ActionIDArray(@Root() mjaction_: MJAction_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
2572
+ this.CheckUserReadPermissions('MJ: Tasks', userPayload);
2573
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
2574
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwTasks')} WHERE ${provider.QuoteIdentifier('ActionID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Tasks', userPayload, EntityPermissionType.Read, 'AND');
2575
+ const rows = await provider.ExecuteSQL(sSQL, [mjaction_.ID], undefined, this.GetUserFromPayload(userPayload));
2576
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Tasks', rows, this.GetUserFromPayload(userPayload));
2577
+ return result;
2578
+ }
2579
+
2571
2580
  @Mutation(() => MJAction_)
2572
2581
  async CreateMJAction(
2573
2582
  @Arg('input', () => CreateMJActionInput) input: CreateMJActionInput,
@@ -7525,6 +7534,10 @@ export class MJAIAgentRequest_ {
7525
7534
  @MaxLength(20)
7526
7535
  ResponseSource?: string;
7527
7536
 
7537
+ @Field({nullable: true, description: `The workflow step (MJ: Tasks row) waiting on this request, when it came from a task graph rather than from a running agent. Distinct from OriginatingAgentRunStepID: a task graph outlives the agent run that submitted it, so the task — not the run — is what resumes when a person answers. NULL for requests raised by a run directly.`})
7538
+ @MaxLength(36)
7539
+ OriginatingTaskID?: string;
7540
+
7528
7541
  @Field({nullable: true})
7529
7542
  @MaxLength(255)
7530
7543
  Agent?: string;
@@ -7553,6 +7566,10 @@ export class MJAIAgentRequest_ {
7553
7566
  @MaxLength(255)
7554
7567
  ResumingAgentRun?: string;
7555
7568
 
7569
+ @Field({nullable: true})
7570
+ @MaxLength(255)
7571
+ OriginatingTask?: string;
7572
+
7556
7573
  }
7557
7574
 
7558
7575
  //****************************************************************************
@@ -7617,6 +7634,9 @@ export class CreateMJAIAgentRequestInput {
7617
7634
  @Field({ nullable: true })
7618
7635
  ResponseSource: string | null;
7619
7636
 
7637
+ @Field({ nullable: true })
7638
+ OriginatingTaskID: string | null;
7639
+
7620
7640
  @Field(() => RestoreContextInput, { nullable: true })
7621
7641
  RestoreContext___?: RestoreContextInput;
7622
7642
  }
@@ -7684,6 +7704,9 @@ export class UpdateMJAIAgentRequestInput {
7684
7704
  @Field({ nullable: true })
7685
7705
  ResponseSource?: string | null;
7686
7706
 
7707
+ @Field({ nullable: true })
7708
+ OriginatingTaskID?: string | null;
7709
+
7687
7710
  @Field(() => [KeyValuePairInput], { nullable: true })
7688
7711
  OldValues___?: KeyValuePairInput[];
7689
7712
 
@@ -16507,6 +16530,9 @@ export class MJAIModelType_ {
16507
16530
  @Field({nullable: true, description: `Default fallback instruction text used when PrefillFallbackMode is SystemInstruction and the provider does not support native prefill. Use {{prefill}} as a placeholder for the actual prefill text. Example: "IMPORTANT: You must begin your response with exactly: {{prefill}}". Individual AI Model Vendor records can override this. If null, a generic fallback is used.`})
16508
16531
  PrefillFallbackText?: string;
16509
16532
 
16533
+ @Field({nullable: true, description: `Type-wide default of the per-modality model-configuration bag (JSON, IAIModelConfiguration shape: LLM / Realtime / Vision / Audio sections). Base layer of the ModelConfiguration cascade — AIModel and AIModelVendor rows inherit from it per key and may override. NULL = contributes nothing.`})
16534
+ ModelConfiguration?: string;
16535
+
16510
16536
  @Field()
16511
16537
  @MaxLength(50)
16512
16538
  DefaultInputModality: string;
@@ -16549,6 +16575,9 @@ export class CreateMJAIModelTypeInput {
16549
16575
  @Field({ nullable: true })
16550
16576
  PrefillFallbackText: string | null;
16551
16577
 
16578
+ @Field({ nullable: true })
16579
+ ModelConfiguration: string | null;
16580
+
16552
16581
  @Field(() => RestoreContextInput, { nullable: true })
16553
16582
  RestoreContext___?: RestoreContextInput;
16554
16583
  }
@@ -16580,6 +16609,9 @@ export class UpdateMJAIModelTypeInput {
16580
16609
  @Field({ nullable: true })
16581
16610
  PrefillFallbackText?: string | null;
16582
16611
 
16612
+ @Field({ nullable: true })
16613
+ ModelConfiguration?: string | null;
16614
+
16583
16615
  @Field(() => [KeyValuePairInput], { nullable: true })
16584
16616
  OldValues___?: KeyValuePairInput[];
16585
16617
 
@@ -16771,6 +16803,9 @@ export class MJAIModelVendor_ {
16771
16803
  @Field({nullable: true, description: `Model-specific fallback instruction text used when PrefillFallbackMode is SystemInstruction and the provider does not support native prefill. Overrides the AI Model Type default. Use {{prefill}} as a placeholder. Allows tuning the fallback instruction per model since different models respond better to different phrasing.`})
16772
16804
  PrefillFallbackText?: string;
16773
16805
 
16806
+ @Field({nullable: true, description: `Most-specific layer of the per-modality model-configuration bag (JSON, IAIModelConfiguration shape) — configuration for THIS model on THIS provider. Deep-merges per key over the model and type layers. NULL = inherit the merged model/type configuration unchanged.`})
16807
+ ModelConfiguration?: string;
16808
+
16774
16809
  @Field()
16775
16810
  @MaxLength(50)
16776
16811
  Model: string;
@@ -16841,6 +16876,9 @@ export class CreateMJAIModelVendorInput {
16841
16876
  @Field({ nullable: true })
16842
16877
  PrefillFallbackText: string | null;
16843
16878
 
16879
+ @Field({ nullable: true })
16880
+ ModelConfiguration: string | null;
16881
+
16844
16882
  @Field(() => RestoreContextInput, { nullable: true })
16845
16883
  RestoreContext___?: RestoreContextInput;
16846
16884
  }
@@ -16899,6 +16937,9 @@ export class UpdateMJAIModelVendorInput {
16899
16937
  @Field({ nullable: true })
16900
16938
  PrefillFallbackText?: string | null;
16901
16939
 
16940
+ @Field({ nullable: true })
16941
+ ModelConfiguration?: string | null;
16942
+
16902
16943
  @Field(() => [KeyValuePairInput], { nullable: true })
16903
16944
  OldValues___?: KeyValuePairInput[];
16904
16945
 
@@ -17056,6 +17097,9 @@ export class MJAIModel_ {
17056
17097
  @Field({nullable: true, description: `Model-level fallback instruction text used when PrefillFallbackMode is SystemInstruction and the provider does not support native prefill. Overrides the AI Model Type default, can be further overridden per-vendor in AI Model Vendor. Use {{prefill}} as a placeholder.`})
17057
17098
  PrefillFallbackText?: string;
17058
17099
 
17100
+ @Field({nullable: true, description: `Per-model layer of the per-modality model-configuration bag (JSON, IAIModelConfiguration shape). Deep-merges per key over the AIModelType default; AIModelVendor rows may override per key on top. NULL = inherit the type default unchanged.`})
17101
+ ModelConfiguration?: string;
17102
+
17059
17103
  @Field()
17060
17104
  @MaxLength(50)
17061
17105
  AIModelType: string;
@@ -17213,6 +17257,9 @@ export class CreateMJAIModelInput {
17213
17257
  @Field({ nullable: true })
17214
17258
  PrefillFallbackText: string | null;
17215
17259
 
17260
+ @Field({ nullable: true })
17261
+ ModelConfiguration: string | null;
17262
+
17216
17263
  @Field({ nullable: true })
17217
17264
  Vendor: string | null;
17218
17265
 
@@ -17283,6 +17330,9 @@ export class UpdateMJAIModelInput {
17283
17330
  @Field({ nullable: true })
17284
17331
  PrefillFallbackText?: string | null;
17285
17332
 
17333
+ @Field({ nullable: true })
17334
+ ModelConfiguration?: string | null;
17335
+
17286
17336
  @Field({ nullable: true })
17287
17337
  Vendor?: string | null;
17288
17338
 
@@ -19961,6 +20011,9 @@ export class MJAIPrompt_ {
19961
20011
  @Field(() => [MJAIAgentType_])
19962
20012
  MJAIAgentTypes_ConversationSummaryPromptIDArray: MJAIAgentType_[]; // Link to MJAIAgentTypes
19963
20013
 
20014
+ @Field(() => [MJTask_])
20015
+ MJTasks_PromptIDArray: MJTask_[]; // Link to MJTasks
20016
+
19964
20017
  }
19965
20018
 
19966
20019
  //****************************************************************************
@@ -20564,6 +20617,16 @@ export class MJAIPromptResolver extends ResolverBase {
20564
20617
  return result;
20565
20618
  }
20566
20619
 
20620
+ @FieldResolver(() => [MJTask_])
20621
+ async MJTasks_PromptIDArray(@Root() mjaiprompt_: MJAIPrompt_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
20622
+ this.CheckUserReadPermissions('MJ: Tasks', userPayload);
20623
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
20624
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwTasks')} WHERE ${provider.QuoteIdentifier('PromptID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Tasks', userPayload, EntityPermissionType.Read, 'AND');
20625
+ const rows = await provider.ExecuteSQL(sSQL, [mjaiprompt_.ID], undefined, this.GetUserFromPayload(userPayload));
20626
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Tasks', rows, this.GetUserFromPayload(userPayload));
20627
+ return result;
20628
+ }
20629
+
20567
20630
  @Mutation(() => MJAIPrompt_)
20568
20631
  async CreateMJAIPrompt(
20569
20632
  @Arg('input', () => CreateMJAIPromptInput) input: CreateMJAIPromptInput,
@@ -48584,10 +48647,6 @@ export class MJEntityActionFilter_ {
48584
48647
  @Field()
48585
48648
  _mj__UpdatedAt: Date;
48586
48649
 
48587
- @Field()
48588
- @MaxLength(425)
48589
- EntityAction: string;
48590
-
48591
48650
  @Field()
48592
48651
  ActionFilter: string;
48593
48652
 
@@ -48947,10 +49006,6 @@ export class MJEntityActionInvocation_ {
48947
49006
  @MaxLength(255)
48948
49007
  RuntimeUXDriverClass?: string;
48949
49008
 
48950
- @Field()
48951
- @MaxLength(425)
48952
- EntityAction: string;
48953
-
48954
49009
  @Field()
48955
49010
  @MaxLength(255)
48956
49011
  InvocationType: string;
@@ -49131,10 +49186,6 @@ export class MJEntityActionParam_ {
49131
49186
  @Field(() => Boolean, {nullable: true, description: `Optional per-binding override of ActionParam.LogValue. NULL (the default) inherits the parameter definition. Set to 0 when this particular binding passes something sensitive through a parameter that is ordinarily safe to log - a message body through a generic Text parameter, for instance. Cannot re-enable logging for a value type the hard rule suppresses.`})
49132
49187
  LogValue?: boolean;
49133
49188
 
49134
- @Field()
49135
- @MaxLength(425)
49136
- EntityAction: string;
49137
-
49138
49189
  @Field()
49139
49190
  @MaxLength(255)
49140
49191
  ActionParam: string;
@@ -49333,6 +49384,10 @@ export class MJEntityAction_ {
49333
49384
  @MaxLength(20)
49334
49385
  LoggingMode: string;
49335
49386
 
49387
+ @Field({description: `How an After* dispatch of this binding executes. Inline (the default) runs it fire-and-forget in the saving process, which is fast but lost if that process dies. Durable submits a single-node task graph instead, so the work survives a restart and is reclaimed by the dispatcher — at the cost of a Task row, a dispatcher hop of latency, and the action's parameters being persisted (redacted) at rest. Ignored for Validate and Before* invocations, which run inside the save and cannot be deferred without changing whether the save succeeds.`})
49388
+ @MaxLength(20)
49389
+ RunMode: string;
49390
+
49336
49391
  @Field()
49337
49392
  @MaxLength(255)
49338
49393
  Entity: string;
@@ -49388,6 +49443,9 @@ export class CreateMJEntityActionInput {
49388
49443
  @Field({ nullable: true })
49389
49444
  LoggingMode?: string;
49390
49445
 
49446
+ @Field({ nullable: true })
49447
+ RunMode?: string;
49448
+
49391
49449
  @Field(() => RestoreContextInput, { nullable: true })
49392
49450
  RestoreContext___?: RestoreContextInput;
49393
49451
  }
@@ -49422,6 +49480,9 @@ export class UpdateMJEntityActionInput {
49422
49480
  @Field({ nullable: true })
49423
49481
  LoggingMode?: string;
49424
49482
 
49483
+ @Field({ nullable: true })
49484
+ RunMode?: string;
49485
+
49425
49486
  @Field(() => [KeyValuePairInput], { nullable: true })
49426
49487
  OldValues___?: KeyValuePairInput[];
49427
49488
 
@@ -84425,6 +84486,16 @@ export class MJTaskDependency_ {
84425
84486
  @Field({nullable: true, description: `Optional boolean expression gating this dependency edge. NULL (the default, and the value every pre-existing row carries) means the edge is unconditional, so adding this column changes the meaning of no existing graph. When present it is evaluated by the shared GraphTraversalEngine against the same context a design-time AIAgentStepPath.Condition sees, which is what lets a runtime task graph and a flow-editor graph be the same graph. A condition that evaluates false skips the edge; one that fails to evaluate ALSO skips it, but is reported distinctly so a graph stalled by a malformed expression cannot be mistaken for one that finished normally.`})
84426
84487
  Condition?: string;
84427
84488
 
84489
+ @Field(() => Int, {description: `Ordering within an exclusive group — higher wins. Mirrors AIAgentStepPath.Priority so a compiled workflow chooses the same branch the flow editor shows. Ignored for edges that are not part of an ExclusiveGroup.`})
84490
+ Priority: number;
84491
+
84492
+ @Field(() => Int, {description: `Deterministic tiebreak when two edges in an ExclusiveGroup share a Priority, applied ascending. Load-bearing rather than cosmetic: compiled dependencies get fresh UUIDs and Priority defaults to 0, so without a stored ordinal a tie would resolve by row order and the same workflow could take a different branch on a different machine.`})
84493
+ Sequence: number;
84494
+
84495
+ @Field({nullable: true, description: `XOR group key: sibling edges leaving the same origin that share a non-null ExclusiveGroup are an exclusive fan-out. The highest-Priority satisfied edge wins, ties broken by ascending Sequence; the rest are Skipped. NULL (the default) means an ordinary dependency, so existing graphs are unaffected. An unevaluable condition anywhere in the group holds the whole group rather than firing every branch.`})
84496
+ @MaxLength(255)
84497
+ ExclusiveGroup?: string;
84498
+
84428
84499
  @Field()
84429
84500
  @MaxLength(255)
84430
84501
  Task: string;
@@ -84455,6 +84526,15 @@ export class CreateMJTaskDependencyInput {
84455
84526
  @Field({ nullable: true })
84456
84527
  Condition: string | null;
84457
84528
 
84529
+ @Field(() => Int, { nullable: true })
84530
+ Priority?: number;
84531
+
84532
+ @Field(() => Int, { nullable: true })
84533
+ Sequence?: number;
84534
+
84535
+ @Field({ nullable: true })
84536
+ ExclusiveGroup: string | null;
84537
+
84458
84538
  @Field(() => RestoreContextInput, { nullable: true })
84459
84539
  RestoreContext___?: RestoreContextInput;
84460
84540
  }
@@ -84480,6 +84560,15 @@ export class UpdateMJTaskDependencyInput {
84480
84560
  @Field({ nullable: true })
84481
84561
  Condition?: string | null;
84482
84562
 
84563
+ @Field(() => Int, { nullable: true })
84564
+ Priority?: number;
84565
+
84566
+ @Field(() => Int, { nullable: true })
84567
+ Sequence?: number;
84568
+
84569
+ @Field({ nullable: true })
84570
+ ExclusiveGroup?: string | null;
84571
+
84483
84572
  @Field(() => [KeyValuePairInput], { nullable: true })
84484
84573
  OldValues___?: KeyValuePairInput[];
84485
84574
 
@@ -84780,7 +84869,7 @@ export class MJTask_ {
84780
84869
  @MaxLength(36)
84781
84870
  AgentID?: string;
84782
84871
 
84783
- @Field({description: `Current status of the task (Pending, In Progress, Complete, Cancelled, Failed, Blocked, Deferred)`})
84872
+ @Field({description: `Lifecycle state. Pending awaits prerequisites; In Progress is claimed and running; Complete succeeded; Failed did not; Blocked can never run because a prerequisite is unsatisfiable; Cancelled was stopped deliberately; Deferred is waiting on a schedule. Skipped is a branch that was NOT TAKEN at an exclusive fan-out — a normal outcome, not a failure: it satisfies dependents (so a join downstream of a fork still runs) and is invisible to failure precedence when the parent rolls up.`})
84784
84873
  @MaxLength(50)
84785
84874
  Status: string;
84786
84875
 
@@ -84822,6 +84911,21 @@ export class MJTask_ {
84822
84911
  @Field({nullable: true, description: `When the current dispatcher claim lapses. Long-running tasks extend it by heartbeat; reconciliation treats an expired claim as an orphaned task and returns it to Pending.`})
84823
84912
  ClaimExpiresAt?: Date;
84824
84913
 
84914
+ @Field({nullable: true, description: `The Action this task executes, when the node is action-assigned rather than agent-assigned or awaiting a person. Mutually exclusive with UserID and AgentID (CK_Task_Assignment). Set by durable entity-action dispatch, where a single-node graph carries one action to run with restart recovery.`})
84915
+ @MaxLength(36)
84916
+ ActionID?: string;
84917
+
84918
+ @Field({nullable: true, description: `Which kind of workflow step this task represents. NULL for a task that is not part of a workflow, such as a hand-authored to-do. Determines which of AgentID/ActionID/PromptID/UserID is meaningful and how Configuration is read. This is the executable vocabulary and is deliberately not the same value list as AIAgentStep.StepType, which describes a step at design time.`})
84919
+ @MaxLength(20)
84920
+ StepType?: string;
84921
+
84922
+ @Field({nullable: true})
84923
+ @MaxLength(36)
84924
+ PromptID?: string;
84925
+
84926
+ @Field({nullable: true, description: `Everything about this step that has no column of its own, as JSON: the loop definition for a ForEach or While step, an agent step's message and template parameters, the mappings that move data between this step and the workflow payload, and the execution policy (timeout, retries, what to do on failure). Typed by ITaskStepConfiguration.`})
84927
+ Configuration?: string;
84928
+
84825
84929
  @Field({nullable: true})
84826
84930
  @MaxLength(255)
84827
84931
  Parent?: string;
@@ -84854,6 +84958,14 @@ export class MJTask_ {
84854
84958
  @MaxLength(255)
84855
84959
  AgentRun?: string;
84856
84960
 
84961
+ @Field({nullable: true})
84962
+ @MaxLength(425)
84963
+ Action?: string;
84964
+
84965
+ @Field({nullable: true})
84966
+ @MaxLength(255)
84967
+ Prompt?: string;
84968
+
84857
84969
  @Field({nullable: true})
84858
84970
  @MaxLength(36)
84859
84971
  RootParentID?: string;
@@ -84864,6 +84976,9 @@ export class MJTask_ {
84864
84976
  @Field(() => [MJTaskDependency_])
84865
84977
  MJTaskDependencies_DependsOnTaskIDArray: MJTaskDependency_[]; // Link to MJTaskDependencies
84866
84978
 
84979
+ @Field(() => [MJAIAgentRequest_])
84980
+ MJAIAgentRequests_OriginatingTaskIDArray: MJAIAgentRequest_[]; // Link to MJAIAgentRequests
84981
+
84867
84982
  @Field(() => [MJTask_])
84868
84983
  MJTasks_ParentIDArray: MJTask_[]; // Link to MJTasks
84869
84984
 
@@ -84937,6 +85052,18 @@ export class CreateMJTaskInput {
84937
85052
  @Field({ nullable: true })
84938
85053
  ClaimExpiresAt: Date | null;
84939
85054
 
85055
+ @Field({ nullable: true })
85056
+ ActionID: string | null;
85057
+
85058
+ @Field({ nullable: true })
85059
+ StepType: string | null;
85060
+
85061
+ @Field({ nullable: true })
85062
+ PromptID: string | null;
85063
+
85064
+ @Field({ nullable: true })
85065
+ Configuration: string | null;
85066
+
84940
85067
  @Field(() => RestoreContextInput, { nullable: true })
84941
85068
  RestoreContext___?: RestoreContextInput;
84942
85069
  }
@@ -85010,6 +85137,18 @@ export class UpdateMJTaskInput {
85010
85137
  @Field({ nullable: true })
85011
85138
  ClaimExpiresAt?: Date | null;
85012
85139
 
85140
+ @Field({ nullable: true })
85141
+ ActionID?: string | null;
85142
+
85143
+ @Field({ nullable: true })
85144
+ StepType?: string | null;
85145
+
85146
+ @Field({ nullable: true })
85147
+ PromptID?: string | null;
85148
+
85149
+ @Field({ nullable: true })
85150
+ Configuration?: string | null;
85151
+
85013
85152
  @Field(() => [KeyValuePairInput], { nullable: true })
85014
85153
  OldValues___?: KeyValuePairInput[];
85015
85154
 
@@ -85094,6 +85233,16 @@ export class MJTaskResolver extends ResolverBase {
85094
85233
  return result;
85095
85234
  }
85096
85235
 
85236
+ @FieldResolver(() => [MJAIAgentRequest_])
85237
+ async MJAIAgentRequests_OriginatingTaskIDArray(@Root() mjtask_: MJTask_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
85238
+ this.CheckUserReadPermissions('MJ: AI Agent Requests', userPayload);
85239
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
85240
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwAIAgentRequests')} WHERE ${provider.QuoteIdentifier('OriginatingTaskID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: AI Agent Requests', userPayload, EntityPermissionType.Read, 'AND');
85241
+ const rows = await provider.ExecuteSQL(sSQL, [mjtask_.ID], undefined, this.GetUserFromPayload(userPayload));
85242
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: AI Agent Requests', rows, this.GetUserFromPayload(userPayload));
85243
+ return result;
85244
+ }
85245
+
85097
85246
  @FieldResolver(() => [MJTask_])
85098
85247
  async MJTasks_ParentIDArray(@Root() mjtask_: MJTask_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
85099
85248
  this.CheckUserReadPermissions('MJ: Tasks', userPayload);
@@ -20,7 +20,8 @@ import {
20
20
  UserInfo,
21
21
  } from '@memberjunction/core';
22
22
  import { MJAuditLogEntity, MJErrorLogEntity, MJUserViewEntityExtended } from '@memberjunction/core-entities';
23
- import { SQLServerDataProvider, UserCache } from '@memberjunction/sqlserver-dataprovider';
23
+ import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
24
+ import { UserCache } from '@memberjunction/generic-database-provider';
24
25
  import { PubSubEngine, AuthorizationError } from 'type-graphql';
25
26
  import { GraphQLError } from 'graphql';
26
27
  import { GetAPIKeyEngine } from '@memberjunction/api-keys';
@@ -62,16 +63,28 @@ export class ResolverBase {
62
63
  * - AllowDecryptInAPI=false + SendEncryptedValue=true: Keep encrypted ciphertext
63
64
  * - AllowDecryptInAPI=false + SendEncryptedValue=false: Replace with sentinel
64
65
  *
66
+ * Returns a COPY — `dataObject` is never written to. Callers routinely pass rows straight
67
+ * from `findBy`/`RunView`, which are the server cache's own objects held by reference, and
68
+ * `LocalCacheManager` deep-freezes them. Renaming in place therefore threw
69
+ * `Cannot add property _mj__CreatedAt, object is not extensible` on every `UserByEmail` /
70
+ * `UserByID` / `UserByEmployeeID` call and on every generated single-record resolver whose
71
+ * entity has caching enabled. (Before the freeze it did something worse but quieter: it
72
+ * rewrote the cached row's keys, so later readers were served transport-shaped rows that
73
+ * `BaseEntity.SetMany` rejects.) Copying here fixes every call site at once and makes the
74
+ * hazard unreachable for future ones.
75
+ *
65
76
  * @param entityName - The entity name
66
- * @param dataObject - The data object with field values
77
+ * @param dataObject - The data object with field values. Not modified.
67
78
  * @param contextUser - Optional user context for decryption (required for encrypted fields)
68
- * @returns The processed data object
79
+ * @returns A new object in transport shape, or null when there is nothing to map
69
80
  */
70
81
  protected async MapFieldNamesToCodeNames(entityName: string, dataObject: any, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<any> {
71
82
  // Return null for empty objects (e.g. when no rows found due to RLS filtering)
72
83
  if (!dataObject || Object.keys(dataObject).length === 0) {
73
84
  return null;
74
85
  }
86
+ // Shallow copy up front so every write below lands on our object, never the caller's.
87
+ dataObject = { ...dataObject };
75
88
 
76
89
  // for the given entity name provided, check to see if there are any fields
77
90
  // where the code name is different from the field name, and for just those
@@ -184,14 +197,21 @@ export class ResolverBase {
184
197
  return true;
185
198
  }
186
199
 
187
- protected async ArrayMapFieldNamesToCodeNames(entityName: string, dataObjectArray: any[], contextUser?: UserInfo): Promise<any[]> {
188
- // iterate through the array and call MapFieldNamesToCodeNames for each element
189
- if (dataObjectArray && dataObjectArray.length > 0) {
190
- for (const element of dataObjectArray) {
191
- await this.MapFieldNamesToCodeNames(entityName, element, contextUser);
192
- }
200
+ /**
201
+ * Array form of {@link MapFieldNamesToCodeNames}. Returns a NEW array of NEW objects; neither
202
+ * the input array nor its rows are modified. Both matter: the cache freezes the array as well
203
+ * as the rows it contains, so collecting the mapped copies (rather than mapping in place and
204
+ * returning the original) is what makes this safe on cache-served input.
205
+ */
206
+ protected async ArrayMapFieldNamesToCodeNames(entityName: string, dataObjectArray: any[], contextUser?: UserInfo, provider?: IMetadataProvider): Promise<any[]> {
207
+ if (!dataObjectArray || dataObjectArray.length === 0) {
208
+ return dataObjectArray;
193
209
  }
194
- return dataObjectArray;
210
+ const mapped: any[] = [];
211
+ for (const element of dataObjectArray) {
212
+ mapped.push(await this.MapFieldNamesToCodeNames(entityName, element, contextUser, provider));
213
+ }
214
+ return mapped;
195
215
  }
196
216
 
197
217
  /**
@@ -826,12 +846,27 @@ export class ResolverBase {
826
846
  LogStatus(`[ResolverBase] RunView result aggregate info: entityName=${viewInfo.Entity}, hasAggregateResults=${!!result?.AggregateResults}, aggregateResultCount=${result?.AggregateResults?.length || 0}, aggregateExecutionTime=${result?.AggregateExecutionTime}, aggregateResults=${JSON.stringify(result?.AggregateResults)}`);
827
847
  }
828
848
 
829
- // Process results for GraphQL transport
849
+ // Process results for GraphQL transport.
850
+ //
851
+ // Map onto COPIES, never in place. `FieldMapper.MapFields` renames keys by
852
+ // mutating (`obj[mapped] = obj[k]; delete obj[k]`), and these rows are the
853
+ // provider's own result objects — which the server cache holds BY REFERENCE
854
+ // under a reference-sharing storage provider. Mapping them in place rewrote
855
+ // `__mj_CreatedAt` to the transport alias `_mj__CreatedAt` inside the live
856
+ // cache, and because that cache is process-wide, one GraphQL response made
857
+ // every later read hand back transport-shaped rows that `BaseEntity.SetMany`
858
+ // rejects. `ArrayFilterEncryptedFieldsForAPI` mutates too, so it must also
859
+ // see the copies. (FileResolver already maps a spread copy.)
860
+ //
861
+ // These copies are LOAD-BEARING, not merely defensive: LocalCacheManager now
862
+ // deep-freezes cached rows (see ILocalStorageProvider.SharesReferences), so
863
+ // mapping in place would throw rather than silently corrupt. `{ ...r }` is a
864
+ // SHALLOW copy, which is sufficient here because the only post-map mutators
865
+ // rename top-level keys and redact scalar fields — and the cache's freeze is
866
+ // deep, so a nested value can no longer be reached through the copy either.
830
867
  const mapper = new FieldMapper();
831
868
  if (result?.Success && result.Results?.length) {
832
- for (const r of result.Results) {
833
- mapper.MapFields(r);
834
- }
869
+ result.Results = result.Results.map(r => mapper.MapFields({ ...r }));
835
870
  // Filter encrypted fields before sending to API client
836
871
  await this.ArrayFilterEncryptedFieldsForAPI(
837
872
  viewInfo.Entity,
@@ -948,9 +983,9 @@ export class ResolverBase {
948
983
  for (let i = 0; i < runViewResults.length; i++) {
949
984
  const runViewResult = runViewResults[i];
950
985
  if (runViewResult?.Success && runViewResult.Results?.length) {
951
- for (const result of runViewResult.Results) {
952
- mapper.MapFields(result);
953
- }
986
+ // Copy-then-map, same reason as the single-view path above: these rows
987
+ // are cache-held references and MapFields renames keys in place.
988
+ runViewResult.Results = runViewResult.Results.map(r => mapper.MapFields({ ...r }));
954
989
  // Filter encrypted fields before sending to API client
955
990
  // Use the corresponding param's entity name
956
991
  const entityName = params[i]?.viewInfo?.Entity;