@memberjunction/server 2.111.0 → 2.112.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.
Files changed (250) hide show
  1. package/dist/agents/skip-agent.d.ts +4 -4
  2. package/dist/agents/skip-agent.d.ts.map +1 -1
  3. package/dist/agents/skip-agent.js +808 -951
  4. package/dist/agents/skip-agent.js.map +1 -1
  5. package/dist/agents/skip-sdk.d.ts +1 -1
  6. package/dist/agents/skip-sdk.d.ts.map +1 -1
  7. package/dist/agents/skip-sdk.js +53 -43
  8. package/dist/agents/skip-sdk.js.map +1 -1
  9. package/dist/apolloServer/index.js +1 -1
  10. package/dist/auth/AuthProviderFactory.d.ts +1 -1
  11. package/dist/auth/AuthProviderFactory.d.ts.map +1 -1
  12. package/dist/auth/AuthProviderFactory.js +1 -3
  13. package/dist/auth/AuthProviderFactory.js.map +1 -1
  14. package/dist/auth/BaseAuthProvider.d.ts +1 -1
  15. package/dist/auth/BaseAuthProvider.d.ts.map +1 -1
  16. package/dist/auth/BaseAuthProvider.js +3 -2
  17. package/dist/auth/BaseAuthProvider.js.map +1 -1
  18. package/dist/auth/IAuthProvider.d.ts +1 -1
  19. package/dist/auth/IAuthProvider.d.ts.map +1 -1
  20. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  21. package/dist/auth/exampleNewUserSubClass.js +1 -1
  22. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  23. package/dist/auth/index.d.ts +1 -1
  24. package/dist/auth/index.d.ts.map +1 -1
  25. package/dist/auth/index.js +6 -6
  26. package/dist/auth/index.js.map +1 -1
  27. package/dist/auth/initializeProviders.js +1 -1
  28. package/dist/auth/initializeProviders.js.map +1 -1
  29. package/dist/auth/newUsers.d.ts +1 -1
  30. package/dist/auth/newUsers.d.ts.map +1 -1
  31. package/dist/auth/newUsers.js +7 -7
  32. package/dist/auth/newUsers.js.map +1 -1
  33. package/dist/auth/providers/Auth0Provider.d.ts +1 -1
  34. package/dist/auth/providers/Auth0Provider.d.ts.map +1 -1
  35. package/dist/auth/providers/Auth0Provider.js +1 -1
  36. package/dist/auth/providers/Auth0Provider.js.map +1 -1
  37. package/dist/auth/providers/CognitoProvider.d.ts +1 -1
  38. package/dist/auth/providers/CognitoProvider.d.ts.map +1 -1
  39. package/dist/auth/providers/CognitoProvider.js +3 -6
  40. package/dist/auth/providers/CognitoProvider.js.map +1 -1
  41. package/dist/auth/providers/GoogleProvider.d.ts +1 -1
  42. package/dist/auth/providers/GoogleProvider.d.ts.map +1 -1
  43. package/dist/auth/providers/GoogleProvider.js +1 -1
  44. package/dist/auth/providers/GoogleProvider.js.map +1 -1
  45. package/dist/auth/providers/MSALProvider.d.ts +1 -1
  46. package/dist/auth/providers/MSALProvider.d.ts.map +1 -1
  47. package/dist/auth/providers/MSALProvider.js +1 -1
  48. package/dist/auth/providers/MSALProvider.js.map +1 -1
  49. package/dist/auth/providers/OktaProvider.d.ts +1 -1
  50. package/dist/auth/providers/OktaProvider.d.ts.map +1 -1
  51. package/dist/auth/providers/OktaProvider.js +1 -1
  52. package/dist/auth/providers/OktaProvider.js.map +1 -1
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +22 -10
  55. package/dist/config.js.map +1 -1
  56. package/dist/context.d.ts +1 -1
  57. package/dist/context.d.ts.map +1 -1
  58. package/dist/context.js +9 -7
  59. package/dist/context.js.map +1 -1
  60. package/dist/entitySubclasses/entityPermissions.server.d.ts +1 -1
  61. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
  62. package/dist/entitySubclasses/entityPermissions.server.js +1 -1
  63. package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
  64. package/dist/generated/generated.d.ts +648 -648
  65. package/dist/generated/generated.d.ts.map +1 -1
  66. package/dist/generated/generated.js +2986 -1133
  67. package/dist/generated/generated.js.map +1 -1
  68. package/dist/generic/KeyInputOutputTypes.d.ts +1 -1
  69. package/dist/generic/KeyInputOutputTypes.d.ts.map +1 -1
  70. package/dist/generic/KeyInputOutputTypes.js +1 -1
  71. package/dist/generic/KeyInputOutputTypes.js.map +1 -1
  72. package/dist/generic/ResolverBase.d.ts +1 -1
  73. package/dist/generic/ResolverBase.d.ts.map +1 -1
  74. package/dist/generic/ResolverBase.js +15 -10
  75. package/dist/generic/ResolverBase.js.map +1 -1
  76. package/dist/generic/RunViewResolver.d.ts +1 -1
  77. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  78. package/dist/generic/RunViewResolver.js +15 -15
  79. package/dist/generic/RunViewResolver.js.map +1 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +18 -9
  82. package/dist/index.js.map +1 -1
  83. package/dist/resolvers/ActionResolver.d.ts +2 -2
  84. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  85. package/dist/resolvers/ActionResolver.js +28 -30
  86. package/dist/resolvers/ActionResolver.js.map +1 -1
  87. package/dist/resolvers/AskSkipResolver.d.ts +2 -2
  88. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  89. package/dist/resolvers/AskSkipResolver.js +60 -50
  90. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  91. package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
  92. package/dist/resolvers/ComponentRegistryResolver.js +36 -38
  93. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  94. package/dist/resolvers/CreateQueryResolver.d.ts +1 -1
  95. package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
  96. package/dist/resolvers/CreateQueryResolver.js +43 -40
  97. package/dist/resolvers/CreateQueryResolver.js.map +1 -1
  98. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  99. package/dist/resolvers/DatasetResolver.js +1 -1
  100. package/dist/resolvers/DatasetResolver.js.map +1 -1
  101. package/dist/resolvers/EntityRecordNameResolver.d.ts +1 -1
  102. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  103. package/dist/resolvers/EntityRecordNameResolver.js +1 -1
  104. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  105. package/dist/resolvers/EntityResolver.d.ts.map +1 -1
  106. package/dist/resolvers/EntityResolver.js +1 -1
  107. package/dist/resolvers/EntityResolver.js.map +1 -1
  108. package/dist/resolvers/FileCategoryResolver.js +1 -1
  109. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  110. package/dist/resolvers/FileResolver.js +1 -1
  111. package/dist/resolvers/FileResolver.js.map +1 -1
  112. package/dist/resolvers/GetDataContextDataResolver.d.ts +1 -1
  113. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  114. package/dist/resolvers/GetDataContextDataResolver.js +5 -5
  115. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  116. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  117. package/dist/resolvers/GetDataResolver.js +8 -6
  118. package/dist/resolvers/GetDataResolver.js.map +1 -1
  119. package/dist/resolvers/MergeRecordsResolver.d.ts +3 -3
  120. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  121. package/dist/resolvers/MergeRecordsResolver.js +3 -3
  122. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  123. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +1 -1
  124. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +1 -1
  125. package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
  126. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
  127. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  128. package/dist/resolvers/QueryResolver.js +11 -11
  129. package/dist/resolvers/QueryResolver.js.map +1 -1
  130. package/dist/resolvers/ReportResolver.js +1 -1
  131. package/dist/resolvers/ReportResolver.js.map +1 -1
  132. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  133. package/dist/resolvers/RunAIAgentResolver.js +27 -28
  134. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  135. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  136. package/dist/resolvers/RunAIPromptResolver.js +31 -31
  137. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  138. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  139. package/dist/resolvers/RunTemplateResolver.js +9 -9
  140. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  141. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  142. package/dist/resolvers/SqlLoggingConfigResolver.js +10 -10
  143. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  144. package/dist/resolvers/SyncDataResolver.d.ts +1 -1
  145. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  146. package/dist/resolvers/SyncDataResolver.js +15 -14
  147. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  148. package/dist/resolvers/SyncRolesUsersResolver.d.ts +1 -1
  149. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  150. package/dist/resolvers/SyncRolesUsersResolver.js +48 -44
  151. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  152. package/dist/resolvers/TaskResolver.d.ts.map +1 -1
  153. package/dist/resolvers/TaskResolver.js +7 -7
  154. package/dist/resolvers/TaskResolver.js.map +1 -1
  155. package/dist/resolvers/TransactionGroupResolver.d.ts +1 -1
  156. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  157. package/dist/resolvers/TransactionGroupResolver.js +12 -12
  158. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  159. package/dist/resolvers/UserFavoriteResolver.d.ts +1 -1
  160. package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
  161. package/dist/resolvers/UserFavoriteResolver.js +1 -1
  162. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  163. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  164. package/dist/resolvers/UserViewResolver.js.map +1 -1
  165. package/dist/rest/EntityCRUDHandler.d.ts +1 -1
  166. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  167. package/dist/rest/EntityCRUDHandler.js +14 -16
  168. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  169. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  170. package/dist/rest/RESTEndpointHandler.js +23 -25
  171. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  172. package/dist/rest/ViewOperationsHandler.d.ts +1 -1
  173. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  174. package/dist/rest/ViewOperationsHandler.js +17 -21
  175. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  176. package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
  177. package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
  178. package/dist/services/ScheduledJobsService.d.ts.map +1 -1
  179. package/dist/services/ScheduledJobsService.js +4 -6
  180. package/dist/services/ScheduledJobsService.js.map +1 -1
  181. package/dist/services/TaskOrchestrator.d.ts +1 -1
  182. package/dist/services/TaskOrchestrator.d.ts.map +1 -1
  183. package/dist/services/TaskOrchestrator.js +30 -30
  184. package/dist/services/TaskOrchestrator.js.map +1 -1
  185. package/dist/types.d.ts +3 -3
  186. package/dist/types.d.ts.map +1 -1
  187. package/dist/types.js +0 -1
  188. package/dist/types.js.map +1 -1
  189. package/dist/util.d.ts +1 -1
  190. package/dist/util.d.ts.map +1 -1
  191. package/dist/util.js +2 -2
  192. package/dist/util.js.map +1 -1
  193. package/package.json +36 -37
  194. package/src/agents/skip-agent.ts +1067 -1200
  195. package/src/agents/skip-sdk.ts +877 -851
  196. package/src/apolloServer/index.ts +2 -2
  197. package/src/auth/AuthProviderFactory.ts +8 -14
  198. package/src/auth/BaseAuthProvider.ts +5 -4
  199. package/src/auth/IAuthProvider.ts +2 -2
  200. package/src/auth/exampleNewUserSubClass.ts +9 -2
  201. package/src/auth/index.ts +31 -26
  202. package/src/auth/initializeProviders.ts +3 -3
  203. package/src/auth/newUsers.ts +166 -134
  204. package/src/auth/providers/Auth0Provider.ts +5 -5
  205. package/src/auth/providers/CognitoProvider.ts +7 -10
  206. package/src/auth/providers/GoogleProvider.ts +4 -5
  207. package/src/auth/providers/MSALProvider.ts +5 -5
  208. package/src/auth/providers/OktaProvider.ts +6 -7
  209. package/src/config.ts +63 -54
  210. package/src/context.ts +42 -30
  211. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  212. package/src/generated/generated.ts +48130 -39930
  213. package/src/generic/KeyInputOutputTypes.ts +3 -6
  214. package/src/generic/ResolverBase.ts +119 -78
  215. package/src/generic/RunViewResolver.ts +27 -23
  216. package/src/index.ts +66 -42
  217. package/src/resolvers/ActionResolver.ts +46 -57
  218. package/src/resolvers/AskSkipResolver.ts +607 -533
  219. package/src/resolvers/ComponentRegistryResolver.ts +547 -562
  220. package/src/resolvers/CreateQueryResolver.ts +683 -655
  221. package/src/resolvers/DatasetResolver.ts +5 -6
  222. package/src/resolvers/EntityCommunicationsResolver.ts +1 -1
  223. package/src/resolvers/EntityRecordNameResolver.ts +9 -5
  224. package/src/resolvers/EntityResolver.ts +9 -7
  225. package/src/resolvers/FileCategoryResolver.ts +2 -2
  226. package/src/resolvers/FileResolver.ts +4 -4
  227. package/src/resolvers/GetDataContextDataResolver.ts +106 -118
  228. package/src/resolvers/GetDataResolver.ts +194 -205
  229. package/src/resolvers/MergeRecordsResolver.ts +5 -5
  230. package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
  231. package/src/resolvers/QueryResolver.ts +95 -78
  232. package/src/resolvers/ReportResolver.ts +2 -2
  233. package/src/resolvers/RunAIAgentResolver.ts +818 -828
  234. package/src/resolvers/RunAIPromptResolver.ts +693 -709
  235. package/src/resolvers/RunTemplateResolver.ts +105 -103
  236. package/src/resolvers/SqlLoggingConfigResolver.ts +69 -72
  237. package/src/resolvers/SyncDataResolver.ts +386 -352
  238. package/src/resolvers/SyncRolesUsersResolver.ts +387 -350
  239. package/src/resolvers/TaskResolver.ts +110 -115
  240. package/src/resolvers/TransactionGroupResolver.ts +143 -138
  241. package/src/resolvers/UserFavoriteResolver.ts +17 -8
  242. package/src/resolvers/UserViewResolver.ts +17 -12
  243. package/src/rest/EntityCRUDHandler.ts +291 -268
  244. package/src/rest/RESTEndpointHandler.ts +782 -776
  245. package/src/rest/ViewOperationsHandler.ts +191 -195
  246. package/src/scheduler/LearningCycleScheduler.ts +8 -52
  247. package/src/services/ScheduledJobsService.ts +129 -132
  248. package/src/services/TaskOrchestrator.ts +792 -776
  249. package/src/types.ts +15 -9
  250. package/src/util.ts +112 -109
@@ -1,7 +1,29 @@
1
- import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, Subscription, Root, ResolverFilterData, ID } from 'type-graphql';
1
+ import {
2
+ Resolver,
3
+ Mutation,
4
+ Query,
5
+ Arg,
6
+ Ctx,
7
+ ObjectType,
8
+ Field,
9
+ PubSub,
10
+ PubSubEngine,
11
+ Subscription,
12
+ Root,
13
+ ResolverFilterData,
14
+ ID,
15
+ } from 'type-graphql';
2
16
  import { AppContext, UserPayload } from '../types.js';
3
- import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo } from '@memberjunction/core';
4
- import { AIAgentEntityExtended, ArtifactEntity, ArtifactVersionEntity, ConversationDetailArtifactEntity, ConversationDetailEntity, UserNotificationEntity, AIAgentRunEntityExtended } from '@memberjunction/core-entities';
17
+ import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo } from '@memberjunction/global';
18
+ import {
19
+ AIAgentEntityExtended,
20
+ ArtifactEntity,
21
+ ArtifactVersionEntity,
22
+ ConversationDetailArtifactEntity,
23
+ ConversationDetailEntity,
24
+ UserNotificationEntity,
25
+ AIAgentRunEntityExtended,
26
+ } from '@memberjunction/core-entities';
5
27
  import { AgentRunner } from '@memberjunction/ai-agents';
6
28
  import { ExecuteAgentResult } from '@memberjunction/ai-core-plus';
7
29
  import { AIEngine } from '@memberjunction/aiengine';
@@ -13,910 +35,878 @@ import { SafeJSONParse } from '@memberjunction/global';
13
35
 
14
36
  @ObjectType()
15
37
  export class AIAgentRunResult {
16
- @Field()
17
- success: boolean;
38
+ @Field()
39
+ success: boolean;
18
40
 
19
- @Field({ nullable: true })
20
- errorMessage?: string;
41
+ @Field({ nullable: true })
42
+ errorMessage?: string;
21
43
 
22
- @Field({ nullable: true })
23
- executionTimeMs?: number;
44
+ @Field({ nullable: true })
45
+ executionTimeMs?: number;
24
46
 
25
- @Field()
26
- result: string; // JSON serialized ExecuteAgentResult with scalars only
47
+ @Field()
48
+ result: string; // JSON serialized ExecuteAgentResult with scalars only
27
49
  }
28
50
 
29
51
  @ObjectType()
30
52
  export class AgentExecutionProgress {
31
- @Field()
32
- currentStep: string;
53
+ @Field()
54
+ currentStep: string;
33
55
 
34
- @Field()
35
- percentage: number;
56
+ @Field()
57
+ percentage: number;
36
58
 
37
- @Field()
38
- message: string;
59
+ @Field()
60
+ message: string;
39
61
 
40
- @Field({ nullable: true })
41
- agentName?: string;
62
+ @Field({ nullable: true })
63
+ agentName?: string;
42
64
 
43
- @Field({ nullable: true })
44
- agentType?: string;
65
+ @Field({ nullable: true })
66
+ agentType?: string;
45
67
  }
46
68
 
47
69
  @ObjectType()
48
70
  export class AgentStreamingContent {
49
- @Field()
50
- content: string;
71
+ @Field()
72
+ content: string;
51
73
 
52
- @Field()
53
- isPartial: boolean;
74
+ @Field()
75
+ isPartial: boolean;
54
76
 
55
- @Field({ nullable: true })
56
- stepName?: string;
77
+ @Field({ nullable: true })
78
+ stepName?: string;
57
79
 
58
- @Field({ nullable: true })
59
- agentName?: string;
80
+ @Field({ nullable: true })
81
+ agentName?: string;
60
82
  }
61
83
 
62
84
  @ObjectType()
63
85
  export class AgentExecutionStepSummary {
64
- @Field()
65
- stepId: string;
86
+ @Field()
87
+ stepId: string;
66
88
 
67
- @Field()
68
- stepName: string;
89
+ @Field()
90
+ stepName: string;
69
91
 
70
- @Field({ nullable: true })
71
- agentName?: string;
92
+ @Field({ nullable: true })
93
+ agentName?: string;
72
94
 
73
- @Field({ nullable: true })
74
- agentType?: string;
95
+ @Field({ nullable: true })
96
+ agentType?: string;
75
97
 
76
- @Field()
77
- startTime: Date;
98
+ @Field()
99
+ startTime: Date;
78
100
 
79
- @Field({ nullable: true })
80
- endTime?: Date;
101
+ @Field({ nullable: true })
102
+ endTime?: Date;
81
103
 
82
- @Field()
83
- status: string;
104
+ @Field()
105
+ status: string;
84
106
 
85
- @Field({ nullable: true })
86
- result?: string;
107
+ @Field({ nullable: true })
108
+ result?: string;
87
109
  }
88
110
 
89
111
  @ObjectType()
90
112
  export class AgentPartialResult {
91
- @Field()
92
- currentStep: string;
113
+ @Field()
114
+ currentStep: string;
93
115
 
94
- @Field({ nullable: true })
95
- partialOutput?: string;
116
+ @Field({ nullable: true })
117
+ partialOutput?: string;
96
118
  }
97
119
 
98
120
  @ObjectType()
99
121
  export class AgentExecutionStreamMessage {
100
- @Field(() => ID)
101
- sessionId: string;
122
+ @Field(() => ID)
123
+ sessionId: string;
102
124
 
103
- @Field(() => ID)
104
- agentRunId: string;
125
+ @Field(() => ID)
126
+ agentRunId: string;
105
127
 
106
- @Field()
107
- type: 'progress' | 'streaming' | 'partial_result' | 'complete';
128
+ @Field()
129
+ type: 'progress' | 'streaming' | 'partial_result' | 'complete';
108
130
 
109
- @Field({ nullable: true })
110
- progress?: AgentExecutionProgress;
111
-
112
- @Field({ nullable: true })
113
- streaming?: AgentStreamingContent;
114
-
115
- @Field({ nullable: true })
116
- partialResult?: AgentPartialResult;
117
-
118
- @Field()
119
- timestamp: Date;
120
-
121
- // Not a GraphQL field - used internally for streaming
122
- agentRun?: any;
123
- }
131
+ @Field({ nullable: true })
132
+ progress?: AgentExecutionProgress;
124
133
 
134
+ @Field({ nullable: true })
135
+ streaming?: AgentStreamingContent;
125
136
 
137
+ @Field({ nullable: true })
138
+ partialResult?: AgentPartialResult;
126
139
 
140
+ @Field()
141
+ timestamp: Date;
127
142
 
143
+ // Not a GraphQL field - used internally for streaming
144
+ agentRun?: any;
145
+ }
128
146
 
129
147
  @Resolver()
130
148
  export class RunAIAgentResolver extends ResolverBase {
131
- /**
132
- * Sanitize ExecuteAgentResult for JSON serialization
133
- * Removes circular references and non-serializable objects
134
- */
135
- private sanitizeAgentResult(result: ExecuteAgentResult): any {
136
- const sanitized: any = {
137
- success: result.success,
138
- payload: result.payload,
139
- suggestedResponses: result.suggestedResponses,
140
- errorMessage: result.agentRun?.ErrorMessage,
141
- finalStep: result.agentRun?.FinalStep,
142
- cancelled: result.agentRun?.Status === 'Cancelled',
143
- cancellationReason: result.agentRun?.CancellationReason
144
- };
145
-
146
- // Safely extract agent run data using GetAll() for proper serialization
147
- if (result.agentRun && typeof result.agentRun.GetAll === 'function') {
148
- // Use GetAll() to get the full serialized object including extended properties
149
- sanitized.agentRun = result.agentRun.GetAll();
150
- }
151
- else {
152
- // shouldn't ever get here
153
- console.error('❌ Unexpected agent run structure:', result.agentRun);
154
- }
155
-
156
- return sanitized;
157
- }
158
-
159
-
160
- /**
161
- * Parse and validate JSON input
162
- */
163
- private parseJsonInput(jsonString: string | undefined, fieldName: string): any {
164
- if (!jsonString) return {};
165
-
166
- try {
167
- return JSON.parse(jsonString);
168
- } catch (parseError) {
169
- throw new Error(`Invalid JSON in ${fieldName}: ${(parseError as Error).message}`);
170
- }
149
+ /**
150
+ * Sanitize ExecuteAgentResult for JSON serialization
151
+ * Removes circular references and non-serializable objects
152
+ */
153
+ private sanitizeAgentResult(result: ExecuteAgentResult): any {
154
+ const sanitized: any = {
155
+ success: result.success,
156
+ payload: result.payload,
157
+ suggestedResponses: result.suggestedResponses,
158
+ errorMessage: result.agentRun?.ErrorMessage,
159
+ finalStep: result.agentRun?.FinalStep,
160
+ cancelled: result.agentRun?.Status === 'Cancelled',
161
+ cancellationReason: result.agentRun?.CancellationReason,
162
+ };
163
+
164
+ // Safely extract agent run data using GetAll() for proper serialization
165
+ if (result.agentRun && typeof result.agentRun.GetAll === 'function') {
166
+ // Use GetAll() to get the full serialized object including extended properties
167
+ sanitized.agentRun = result.agentRun.GetAll();
168
+ } else {
169
+ // shouldn't ever get here
170
+ console.error(' Unexpected agent run structure:', result.agentRun);
171
171
  }
172
172
 
173
- /**
174
- * Validate the agent entity
175
- */
176
- private async validateAgent(agentId: string, currentUser: any): Promise<AIAgentEntityExtended> {
177
- // Use AIEngine to get cached agent data
178
- await AIEngine.Instance.Config(false, currentUser);
179
-
180
- // Find agent in cached collection
181
- const agentEntity = AIEngine.Instance.Agents.find((a: AIAgentEntityExtended) => a.ID === agentId);
182
-
183
- if (!agentEntity) {
184
- throw new Error(`AI Agent with ID ${agentId} not found`);
185
- }
173
+ return sanitized;
174
+ }
186
175
 
187
- // Check if agent is active
188
- if (agentEntity.Status !== 'Active') {
189
- throw new Error(`AI Agent "${agentEntity.Name}" is not active (Status: ${agentEntity.Status})`);
190
- }
176
+ /**
177
+ * Parse and validate JSON input
178
+ */
179
+ private parseJsonInput(jsonString: string | undefined, fieldName: string): any {
180
+ if (!jsonString) return {};
191
181
 
192
- return agentEntity;
182
+ try {
183
+ return JSON.parse(jsonString);
184
+ } catch (parseError) {
185
+ throw new Error(`Invalid JSON in ${fieldName}: ${(parseError as Error).message}`);
193
186
  }
187
+ }
194
188
 
195
- /**
196
- * Create streaming progress callback
197
- */
198
- private createProgressCallback(pubSub: PubSubEngine, sessionId: string, userPayload: UserPayload, agentRunRef: { current: any }) {
199
- return (progress: any) => {
200
- // Only publish progress for significant steps (not initialization noise)
201
- const significantSteps = ['prompt_execution', 'action_execution', 'subagent_execution', 'decision_processing'];
202
- if (!significantSteps.includes(progress.step)) {
203
- console.log(`🔇 Skipping noise progress: ${progress.step}`);
204
- return;
205
- }
206
-
207
- // Get the agent run from the progress metadata or use the ref
208
- const agentRun = progress.metadata?.agentRun || agentRunRef.current;
209
- if (!agentRun) {
210
- console.error('❌ No agent run available for progress callback');
211
- return;
212
- }
213
-
214
- console.log('📡 Publishing progress update:', {
215
- step: progress.step,
216
- percentage: progress.percentage,
217
- message: progress.message,
218
- sessionId,
219
- agentRunId: agentRun.ID
220
- });
221
-
222
- // Publish progress updates with the full serialized agent run
223
- const progressMsg: AgentExecutionStreamMessage = {
224
- sessionId,
225
- agentRunId: agentRun.ID,
226
- type: 'progress',
227
- agentRun: agentRun.GetAll(), // Serialize the full agent run
228
- progress: {
229
- currentStep: progress.step,
230
- percentage: progress.percentage,
231
- message: progress.message,
232
- agentName: (progress.metadata as any)?.agentName || undefined,
233
- agentType: (progress.metadata as any)?.agentType || undefined
234
- },
235
- timestamp: new Date()
236
- };
237
- this.PublishProgressUpdate(pubSub, progressMsg, userPayload);
238
- };
239
- }
240
-
241
- private PublishProgressUpdate(pubSub: PubSubEngine, data: any, userPayload: UserPayload) {
242
- pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
243
- message: JSON.stringify({
244
- resolver: 'RunAIAgentResolver',
245
- type: 'ExecutionProgress',
246
- status: 'ok',
247
- data,
248
- }),
249
- sessionId: userPayload.sessionId,
250
- });
251
- }
189
+ /**
190
+ * Validate the agent entity
191
+ */
192
+ private async validateAgent(agentId: string, currentUser: any): Promise<AIAgentEntityExtended> {
193
+ // Use AIEngine to get cached agent data
194
+ await AIEngine.Instance.Config(false, currentUser);
252
195
 
196
+ // Find agent in cached collection
197
+ const agentEntity = AIEngine.Instance.Agents.find((a: AIAgentEntityExtended) => a.ID === agentId);
253
198
 
254
- private PublishStreamingUpdate(pubSub: PubSubEngine, data: any, userPayload: UserPayload) {
255
- pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
256
- message: JSON.stringify({
257
- resolver: 'RunAIAgentResolver',
258
- type: 'StreamingContent',
259
- status: 'ok',
260
- data,
261
- }),
262
- sessionId: userPayload.sessionId,
263
- });
199
+ if (!agentEntity) {
200
+ throw new Error(`AI Agent with ID ${agentId} not found`);
264
201
  }
265
202
 
266
- /**
267
- * Create streaming content callback
268
- */
269
- private createStreamingCallback(pubSub: PubSubEngine, sessionId: string, userPayload: UserPayload, agentRunRef: { current: any }) {
270
- return (chunk: any) => {
271
- // Use the agent run from the ref
272
- const agentRun = agentRunRef.current;
273
- if (!agentRun) {
274
- console.error('❌ No agent run available for streaming callback');
275
- return;
276
- }
277
-
278
- console.log('💬 Publishing streaming content:', {
279
- content: chunk.content.substring(0, 50) + '...',
280
- isComplete: chunk.isComplete,
281
- stepType: chunk.stepType,
282
- sessionId,
283
- agentRunId: agentRun.ID
284
- });
285
-
286
- // Publish streaming content with the full serialized agent run
287
- const streamMsg: AgentExecutionStreamMessage = {
288
- sessionId,
289
- agentRunId: agentRun.ID,
290
- type: 'streaming',
291
- agentRun: agentRun.GetAll(), // Include the full serialized agent run
292
- streaming: {
293
- content: chunk.content,
294
- isPartial: !chunk.isComplete,
295
- stepName: chunk.stepType,
296
- agentName: chunk.modelName
297
- },
298
- timestamp: new Date()
299
- };
300
- this.PublishStreamingUpdate(pubSub, streamMsg, userPayload);
301
- };
203
+ // Check if agent is active
204
+ if (agentEntity.Status !== 'Active') {
205
+ throw new Error(`AI Agent "${agentEntity.Name}" is not active (Status: ${agentEntity.Status})`);
302
206
  }
303
207
 
304
- /**
305
- * Internal method that handles the core AI agent execution logic.
306
- * This method is called by both the regular and system user resolvers.
307
- * @private
308
- */
309
- private async executeAIAgent(
310
- p: DatabaseProviderBase,
311
- agentId: string,
312
- userPayload: UserPayload,
313
- messagesJson: string,
314
- sessionId: string,
315
- pubSub: PubSubEngine,
316
- data?: string,
317
- payload?: string,
318
- templateData?: string,
319
- lastRunId?: string,
320
- autoPopulateLastRunPayload?: boolean,
321
- configurationId?: string,
322
- conversationDetailId?: string,
323
- createArtifacts: boolean = false,
324
- createNotification: boolean = false,
325
- sourceArtifactId?: string,
326
- sourceArtifactVersionId?: string
327
- ): Promise<AIAgentRunResult> {
328
- const startTime = Date.now();
329
-
330
- try {
331
- LogStatus(`=== RUNNING AI AGENT FOR ID: ${agentId} ===`);
332
-
333
- // Parse and validate messages
334
- const parsedMessages = this.parseJsonInput(messagesJson, 'messages');
335
- if (!Array.isArray(parsedMessages)) {
336
- throw new Error('Messages must be an array');
337
- }
338
-
339
- // Parse data contexts
340
- const parsedData = this.parseJsonInput(data, 'data');
341
- const parsedTemplateData = this.parseJsonInput(templateData, 'templateData');
342
-
343
- // Get and validate current user
344
- const currentUser = this.GetUserFromPayload(userPayload);
345
- if (!currentUser) {
346
- throw new Error('Unable to determine current user');
347
- }
348
-
349
- // Validate agent
350
- const agentEntity = await this.validateAgent(agentId, currentUser);
351
-
352
- // @jordanfanapour IMPORTANT TO-DO for various engine classes (via base engine class) and here for AI Agent Runner and for AI Prompt Runner, need to be able to pass in a IMetadataProvider for it to use
353
- // for multi-user server environments like this one
354
- // Create AI agent runner
355
- const agentRunner = new AgentRunner();
356
-
357
- // Track agent run for streaming (use ref to update later)
358
- const agentRunRef = { current: null as any };
359
-
360
- console.log(`🚀 Starting agent execution with sessionId: ${sessionId}`);
361
-
362
- // Execute the agent with streaming callbacks
363
- const result = await agentRunner.RunAgent({
364
- agent: agentEntity,
365
- conversationMessages: parsedMessages,
366
- payload: payload ? SafeJSONParse(payload) : undefined,
367
- contextUser: currentUser,
368
- onProgress: this.createProgressCallback(pubSub, sessionId, userPayload, agentRunRef),
369
- onStreaming: this.createStreamingCallback(pubSub, sessionId, userPayload, agentRunRef),
370
- lastRunId: lastRunId,
371
- autoPopulateLastRunPayload: autoPopulateLastRunPayload,
372
- configurationId: configurationId,
373
- data: parsedData,
374
- conversationDetailId: conversationDetailId,
375
- });
376
-
377
- // Update agent run ref once available
378
- if (result.agentRun) {
379
- agentRunRef.current = result.agentRun;
380
- }
381
-
382
- const executionTime = Date.now() - startTime;
383
-
384
- // Publish final events
385
- this.publishFinalEvents(pubSub, sessionId, userPayload, result);
386
-
387
- // Process completion for artifacts and notifications (if enabled)
388
- if (result.success && conversationDetailId && result.payload) {
389
- const currentUser = this.GetUserFromPayload(userPayload);
390
-
391
- if (createArtifacts) {
392
- const artifactInfo = await this.processAgentCompletionForArtifacts(
393
- result.agentRun,
394
- result.payload,
395
- currentUser,
396
- conversationDetailId,
397
- sourceArtifactId
398
- );
399
-
400
- // Create notification if enabled and artifact was created successfully
401
- if (createNotification && artifactInfo.artifactId && artifactInfo.versionId && artifactInfo.versionNumber) {
402
- await this.createCompletionNotification(
403
- result.agentRun,
404
- {
405
- artifactId: artifactInfo.artifactId,
406
- versionId: artifactInfo.versionId,
407
- versionNumber: artifactInfo.versionNumber
408
- },
409
- conversationDetailId,
410
- currentUser,
411
- pubSub,
412
- userPayload
413
- );
414
- }
415
- }
416
- }
417
-
418
- // Create sanitized payload for JSON serialization
419
- const sanitizedResult = this.sanitizeAgentResult(result);
420
- const returnResult = JSON.stringify(sanitizedResult);
421
-
422
- // Log completion
423
- if (result.success) {
424
- LogStatus(`=== AI AGENT RUN COMPLETED FOR: ${agentEntity.Name} (${executionTime}ms) ===`);
425
- } else {
426
- LogError(`AI Agent run failed for ${agentEntity.Name}: ${result.agentRun?.ErrorMessage}`);
427
- }
428
-
429
- return {
430
- success: result.success,
431
- errorMessage: result.agentRun?.ErrorMessage || undefined,
432
- executionTimeMs: executionTime,
433
- result: returnResult
434
- };
435
-
436
- } catch (error) {
437
- const executionTime = Date.now() - startTime;
438
- LogError(`AI Agent run failed:`, undefined, error);
439
-
440
- // Create error payload
441
- const errorResult = {
442
- success: false,
443
- errorMessage: (error as Error).message || 'Unknown error occurred',
444
- executionTimeMs: executionTime
445
- };
446
-
447
- return {
448
- success: false,
449
- errorMessage: errorResult.errorMessage,
450
- executionTimeMs: executionTime,
451
- result: JSON.stringify(errorResult)
452
- };
208
+ return agentEntity;
209
+ }
210
+
211
+ /**
212
+ * Create streaming progress callback
213
+ */
214
+ private createProgressCallback(pubSub: PubSubEngine, sessionId: string, userPayload: UserPayload, agentRunRef: { current: any }) {
215
+ return (progress: any) => {
216
+ // Only publish progress for significant steps (not initialization noise)
217
+ const significantSteps = ['prompt_execution', 'action_execution', 'subagent_execution', 'decision_processing'];
218
+ if (!significantSteps.includes(progress.step)) {
219
+ console.log(`🔇 Skipping noise progress: ${progress.step}`);
220
+ return;
221
+ }
222
+
223
+ // Get the agent run from the progress metadata or use the ref
224
+ const agentRun = progress.metadata?.agentRun || agentRunRef.current;
225
+ if (!agentRun) {
226
+ console.error('❌ No agent run available for progress callback');
227
+ return;
228
+ }
229
+
230
+ console.log('📡 Publishing progress update:', {
231
+ step: progress.step,
232
+ percentage: progress.percentage,
233
+ message: progress.message,
234
+ sessionId,
235
+ agentRunId: agentRun.ID,
236
+ });
237
+
238
+ // Publish progress updates with the full serialized agent run
239
+ const progressMsg: AgentExecutionStreamMessage = {
240
+ sessionId,
241
+ agentRunId: agentRun.ID,
242
+ type: 'progress',
243
+ agentRun: agentRun.GetAll(), // Serialize the full agent run
244
+ progress: {
245
+ currentStep: progress.step,
246
+ percentage: progress.percentage,
247
+ message: progress.message,
248
+ agentName: (progress.metadata as any)?.agentName || undefined,
249
+ agentType: (progress.metadata as any)?.agentType || undefined,
250
+ },
251
+ timestamp: new Date(),
252
+ };
253
+ this.PublishProgressUpdate(pubSub, progressMsg, userPayload);
254
+ };
255
+ }
256
+
257
+ private PublishProgressUpdate(pubSub: PubSubEngine, data: any, userPayload: UserPayload) {
258
+ pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
259
+ message: JSON.stringify({
260
+ resolver: 'RunAIAgentResolver',
261
+ type: 'ExecutionProgress',
262
+ status: 'ok',
263
+ data,
264
+ }),
265
+ sessionId: userPayload.sessionId,
266
+ });
267
+ }
268
+
269
+ private PublishStreamingUpdate(pubSub: PubSubEngine, data: any, userPayload: UserPayload) {
270
+ pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
271
+ message: JSON.stringify({
272
+ resolver: 'RunAIAgentResolver',
273
+ type: 'StreamingContent',
274
+ status: 'ok',
275
+ data,
276
+ }),
277
+ sessionId: userPayload.sessionId,
278
+ });
279
+ }
280
+
281
+ /**
282
+ * Create streaming content callback
283
+ */
284
+ private createStreamingCallback(pubSub: PubSubEngine, sessionId: string, userPayload: UserPayload, agentRunRef: { current: any }) {
285
+ return (chunk: any) => {
286
+ // Use the agent run from the ref
287
+ const agentRun = agentRunRef.current;
288
+ if (!agentRun) {
289
+ console.error('❌ No agent run available for streaming callback');
290
+ return;
291
+ }
292
+
293
+ console.log('💬 Publishing streaming content:', {
294
+ content: chunk.content.substring(0, 50) + '...',
295
+ isComplete: chunk.isComplete,
296
+ stepType: chunk.stepType,
297
+ sessionId,
298
+ agentRunId: agentRun.ID,
299
+ });
300
+
301
+ // Publish streaming content with the full serialized agent run
302
+ const streamMsg: AgentExecutionStreamMessage = {
303
+ sessionId,
304
+ agentRunId: agentRun.ID,
305
+ type: 'streaming',
306
+ agentRun: agentRun.GetAll(), // Include the full serialized agent run
307
+ streaming: {
308
+ content: chunk.content,
309
+ isPartial: !chunk.isComplete,
310
+ stepName: chunk.stepType,
311
+ agentName: chunk.modelName,
312
+ },
313
+ timestamp: new Date(),
314
+ };
315
+ this.PublishStreamingUpdate(pubSub, streamMsg, userPayload);
316
+ };
317
+ }
318
+
319
+ /**
320
+ * Internal method that handles the core AI agent execution logic.
321
+ * This method is called by both the regular and system user resolvers.
322
+ * @private
323
+ */
324
+ private async executeAIAgent(
325
+ p: DatabaseProviderBase,
326
+ agentId: string,
327
+ userPayload: UserPayload,
328
+ messagesJson: string,
329
+ sessionId: string,
330
+ pubSub: PubSubEngine,
331
+ data?: string,
332
+ payload?: string,
333
+ templateData?: string,
334
+ lastRunId?: string,
335
+ autoPopulateLastRunPayload?: boolean,
336
+ configurationId?: string,
337
+ conversationDetailId?: string,
338
+ createArtifacts: boolean = false,
339
+ createNotification: boolean = false,
340
+ sourceArtifactId?: string,
341
+ sourceArtifactVersionId?: string
342
+ ): Promise<AIAgentRunResult> {
343
+ const startTime = Date.now();
344
+
345
+ try {
346
+ LogStatus(`=== RUNNING AI AGENT FOR ID: ${agentId} ===`);
347
+
348
+ // Parse and validate messages
349
+ const parsedMessages = this.parseJsonInput(messagesJson, 'messages');
350
+ if (!Array.isArray(parsedMessages)) {
351
+ throw new Error('Messages must be an array');
352
+ }
353
+
354
+ // Parse data contexts
355
+ const parsedData = this.parseJsonInput(data, 'data');
356
+ const parsedTemplateData = this.parseJsonInput(templateData, 'templateData');
357
+
358
+ // Get and validate current user
359
+ const currentUser = this.GetUserFromPayload(userPayload);
360
+ if (!currentUser) {
361
+ throw new Error('Unable to determine current user');
362
+ }
363
+
364
+ // Validate agent
365
+ const agentEntity = await this.validateAgent(agentId, currentUser);
366
+
367
+ // @jordanfanapour IMPORTANT TO-DO for various engine classes (via base engine class) and here for AI Agent Runner and for AI Prompt Runner, need to be able to pass in a IMetadataProvider for it to use
368
+ // for multi-user server environments like this one
369
+ // Create AI agent runner
370
+ const agentRunner = new AgentRunner();
371
+
372
+ // Track agent run for streaming (use ref to update later)
373
+ const agentRunRef = { current: null as any };
374
+
375
+ console.log(`🚀 Starting agent execution with sessionId: ${sessionId}`);
376
+
377
+ // Execute the agent with streaming callbacks
378
+ const result = await agentRunner.RunAgent({
379
+ agent: agentEntity,
380
+ conversationMessages: parsedMessages,
381
+ payload: payload ? SafeJSONParse(payload) : undefined,
382
+ contextUser: currentUser,
383
+ onProgress: this.createProgressCallback(pubSub, sessionId, userPayload, agentRunRef),
384
+ onStreaming: this.createStreamingCallback(pubSub, sessionId, userPayload, agentRunRef),
385
+ lastRunId: lastRunId,
386
+ autoPopulateLastRunPayload: autoPopulateLastRunPayload,
387
+ configurationId: configurationId,
388
+ data: parsedData,
389
+ conversationDetailId: conversationDetailId,
390
+ });
391
+
392
+ // Update agent run ref once available
393
+ if (result.agentRun) {
394
+ agentRunRef.current = result.agentRun;
395
+ }
396
+
397
+ const executionTime = Date.now() - startTime;
398
+
399
+ // Publish final events
400
+ this.publishFinalEvents(pubSub, sessionId, userPayload, result);
401
+
402
+ // Process completion for artifacts and notifications (if enabled)
403
+ if (result.success && conversationDetailId && result.payload) {
404
+ const currentUser = this.GetUserFromPayload(userPayload);
405
+
406
+ if (createArtifacts) {
407
+ const artifactInfo = await this.processAgentCompletionForArtifacts(
408
+ result.agentRun,
409
+ result.payload,
410
+ currentUser,
411
+ conversationDetailId,
412
+ sourceArtifactId
413
+ );
414
+
415
+ // Create notification if enabled and artifact was created successfully
416
+ if (createNotification && artifactInfo.artifactId && artifactInfo.versionId && artifactInfo.versionNumber) {
417
+ await this.createCompletionNotification(
418
+ result.agentRun,
419
+ {
420
+ artifactId: artifactInfo.artifactId,
421
+ versionId: artifactInfo.versionId,
422
+ versionNumber: artifactInfo.versionNumber,
423
+ },
424
+ conversationDetailId,
425
+ currentUser,
426
+ pubSub,
427
+ userPayload
428
+ );
429
+ }
453
430
  }
431
+ }
432
+
433
+ // Create sanitized payload for JSON serialization
434
+ const sanitizedResult = this.sanitizeAgentResult(result);
435
+ const returnResult = JSON.stringify(sanitizedResult);
436
+
437
+ // Log completion
438
+ if (result.success) {
439
+ LogStatus(`=== AI AGENT RUN COMPLETED FOR: ${agentEntity.Name} (${executionTime}ms) ===`);
440
+ } else {
441
+ LogError(`AI Agent run failed for ${agentEntity.Name}: ${result.agentRun?.ErrorMessage}`);
442
+ }
443
+
444
+ return {
445
+ success: result.success,
446
+ errorMessage: result.agentRun?.ErrorMessage || undefined,
447
+ executionTimeMs: executionTime,
448
+ result: returnResult,
449
+ };
450
+ } catch (error) {
451
+ const executionTime = Date.now() - startTime;
452
+ LogError(`AI Agent run failed:`, undefined, error);
453
+
454
+ // Create error payload
455
+ const errorResult = {
456
+ success: false,
457
+ errorMessage: (error as Error).message || 'Unknown error occurred',
458
+ executionTimeMs: executionTime,
459
+ };
460
+
461
+ return {
462
+ success: false,
463
+ errorMessage: errorResult.errorMessage,
464
+ executionTimeMs: executionTime,
465
+ result: JSON.stringify(errorResult),
466
+ };
454
467
  }
455
-
456
- /**
457
- * Publish final streaming events (partial result and completion)
458
- */
459
- private publishFinalEvents(pubSub: PubSubEngine, sessionId: string, userPayload: UserPayload, result: ExecuteAgentResult) {
460
- if (result.agentRun) {
461
- // Get the last step from agent run
462
- let lastStep = 'Completed';
463
- if (result.agentRun?.Steps && result.agentRun.Steps.length > 0) {
464
- // Get the last step from the Steps array
465
- const lastStepEntity = result.agentRun.Steps[result.agentRun.Steps.length - 1];
466
- lastStep = lastStepEntity?.StepName || 'Completed';
467
- }
468
-
469
- // Publish partial result
470
- const partialResult: AgentPartialResult = {
471
- currentStep: lastStep,
472
- partialOutput: result.payload || undefined
473
- };
474
-
475
- const partialMsg: AgentExecutionStreamMessage = {
476
- sessionId,
477
- agentRunId: result.agentRun.ID,
478
- type: 'partial_result',
479
- partialResult,
480
- timestamp: new Date()
481
- };
482
- this.PublishStreamingUpdate(pubSub, partialMsg, userPayload);
483
- }
484
-
485
- // Publish completion
486
- const completeMsg: AgentExecutionStreamMessage = {
487
- sessionId,
488
- agentRunId: result.agentRun?.ID || 'unknown',
489
- type: 'complete',
490
- timestamp: new Date()
491
- };
492
- this.PublishStreamingUpdate(pubSub, completeMsg, userPayload);
468
+ }
469
+
470
+ /**
471
+ * Publish final streaming events (partial result and completion)
472
+ */
473
+ private publishFinalEvents(pubSub: PubSubEngine, sessionId: string, userPayload: UserPayload, result: ExecuteAgentResult) {
474
+ if (result.agentRun) {
475
+ // Get the last step from agent run
476
+ let lastStep = 'Completed';
477
+ if (result.agentRun?.Steps && result.agentRun.Steps.length > 0) {
478
+ // Get the last step from the Steps array
479
+ const lastStepEntity = result.agentRun.Steps[result.agentRun.Steps.length - 1];
480
+ lastStep = lastStepEntity?.StepName || 'Completed';
481
+ }
482
+
483
+ // Publish partial result
484
+ const partialResult: AgentPartialResult = {
485
+ currentStep: lastStep,
486
+ partialOutput: result.payload || undefined,
487
+ };
488
+
489
+ const partialMsg: AgentExecutionStreamMessage = {
490
+ sessionId,
491
+ agentRunId: result.agentRun.ID,
492
+ type: 'partial_result',
493
+ partialResult,
494
+ timestamp: new Date(),
495
+ };
496
+ this.PublishStreamingUpdate(pubSub, partialMsg, userPayload);
493
497
  }
494
498
 
495
- /**
496
- * Public mutation for regular users to run AI agents with authentication.
497
- */
498
- @Mutation(() => AIAgentRunResult)
499
- async RunAIAgent(
500
- @Arg('agentId') agentId: string,
501
- @Ctx() { userPayload, providers }: AppContext,
502
- @Arg('messages') messagesJson: string,
503
- @Arg('sessionId') sessionId: string,
504
- @PubSub() pubSub: PubSubEngine,
505
- @Arg('data', { nullable: true }) data?: string,
506
- @Arg('payload', { nullable: true }) payload?: string,
507
- @Arg('templateData', { nullable: true }) templateData?: string,
508
- @Arg('lastRunId', { nullable: true }) lastRunId?: string,
509
- @Arg('autoPopulateLastRunPayload', { nullable: true }) autoPopulateLastRunPayload?: boolean,
510
- @Arg('configurationId', { nullable: true }) configurationId?: string,
511
- @Arg('conversationDetailId', { nullable: true }) conversationDetailId?: string,
512
- @Arg('createArtifacts', { nullable: true }) createArtifacts?: boolean,
513
- @Arg('createNotification', { nullable: true }) createNotification?: boolean,
514
- @Arg('sourceArtifactId', { nullable: true }) sourceArtifactId?: string,
515
- @Arg('sourceArtifactVersionId', { nullable: true }) sourceArtifactVersionId?: string
516
- ): Promise<AIAgentRunResult> {
517
- const p = GetReadWriteProvider(providers);
518
- return this.executeAIAgent(
519
- p,
520
- agentId,
521
- userPayload,
522
- messagesJson,
523
- sessionId,
524
- pubSub,
525
- data,
526
- payload,
527
- templateData,
528
- lastRunId,
529
- autoPopulateLastRunPayload,
530
- configurationId,
531
- conversationDetailId,
532
- createArtifacts || false,
533
- createNotification || false,
534
- sourceArtifactId,
535
- sourceArtifactVersionId
536
- );
499
+ // Publish completion
500
+ const completeMsg: AgentExecutionStreamMessage = {
501
+ sessionId,
502
+ agentRunId: result.agentRun?.ID || 'unknown',
503
+ type: 'complete',
504
+ timestamp: new Date(),
505
+ };
506
+ this.PublishStreamingUpdate(pubSub, completeMsg, userPayload);
507
+ }
508
+
509
+ /**
510
+ * Public mutation for regular users to run AI agents with authentication.
511
+ */
512
+ @Mutation(() => AIAgentRunResult)
513
+ async RunAIAgent(
514
+ @Arg('agentId') agentId: string,
515
+ @Ctx() { userPayload, providers }: AppContext,
516
+ @Arg('messages') messagesJson: string,
517
+ @Arg('sessionId') sessionId: string,
518
+ @PubSub() pubSub: PubSubEngine,
519
+ @Arg('data', { nullable: true }) data?: string,
520
+ @Arg('payload', { nullable: true }) payload?: string,
521
+ @Arg('templateData', { nullable: true }) templateData?: string,
522
+ @Arg('lastRunId', { nullable: true }) lastRunId?: string,
523
+ @Arg('autoPopulateLastRunPayload', { nullable: true }) autoPopulateLastRunPayload?: boolean,
524
+ @Arg('configurationId', { nullable: true }) configurationId?: string,
525
+ @Arg('conversationDetailId', { nullable: true }) conversationDetailId?: string,
526
+ @Arg('createArtifacts', { nullable: true }) createArtifacts?: boolean,
527
+ @Arg('createNotification', { nullable: true }) createNotification?: boolean,
528
+ @Arg('sourceArtifactId', { nullable: true }) sourceArtifactId?: string,
529
+ @Arg('sourceArtifactVersionId', { nullable: true }) sourceArtifactVersionId?: string
530
+ ): Promise<AIAgentRunResult> {
531
+ const p = GetReadWriteProvider(providers);
532
+ return this.executeAIAgent(
533
+ p,
534
+ agentId,
535
+ userPayload,
536
+ messagesJson,
537
+ sessionId,
538
+ pubSub,
539
+ data,
540
+ payload,
541
+ templateData,
542
+ lastRunId,
543
+ autoPopulateLastRunPayload,
544
+ configurationId,
545
+ conversationDetailId,
546
+ createArtifacts || false,
547
+ createNotification || false,
548
+ sourceArtifactId,
549
+ sourceArtifactVersionId
550
+ );
551
+ }
552
+
553
+ /**
554
+ * System user query for running AI agents with elevated privileges.
555
+ * Requires the @RequireSystemUser decorator to ensure only system users can access.
556
+ */
557
+ @RequireSystemUser()
558
+ @Query(() => AIAgentRunResult)
559
+ async RunAIAgentSystemUser(
560
+ @Arg('agentId') agentId: string,
561
+ @Ctx() { userPayload, providers }: AppContext,
562
+ @Arg('messages') messagesJson: string,
563
+ @Arg('sessionId') sessionId: string,
564
+ @PubSub() pubSub: PubSubEngine,
565
+ @Arg('data', { nullable: true }) data?: string,
566
+ @Arg('payload', { nullable: true }) payload?: string,
567
+ @Arg('templateData', { nullable: true }) templateData?: string,
568
+ @Arg('lastRunId', { nullable: true }) lastRunId?: string,
569
+ @Arg('autoPopulateLastRunPayload', { nullable: true }) autoPopulateLastRunPayload?: boolean,
570
+ @Arg('configurationId', { nullable: true }) configurationId?: string,
571
+ @Arg('conversationDetailId', { nullable: true }) conversationDetailId?: string,
572
+ @Arg('createArtifacts', { nullable: true }) createArtifacts?: boolean,
573
+ @Arg('createNotification', { nullable: true }) createNotification?: boolean,
574
+ @Arg('sourceArtifactId', { nullable: true }) sourceArtifactId?: string,
575
+ @Arg('sourceArtifactVersionId', { nullable: true }) sourceArtifactVersionId?: string
576
+ ): Promise<AIAgentRunResult> {
577
+ const p = GetReadWriteProvider(providers);
578
+ return this.executeAIAgent(
579
+ p,
580
+ agentId,
581
+ userPayload,
582
+ messagesJson,
583
+ sessionId,
584
+ pubSub,
585
+ data,
586
+ payload,
587
+ templateData,
588
+ lastRunId,
589
+ autoPopulateLastRunPayload,
590
+ configurationId,
591
+ conversationDetailId,
592
+ createArtifacts || false,
593
+ createNotification || false,
594
+ sourceArtifactId,
595
+ sourceArtifactVersionId
596
+ );
597
+ }
598
+
599
+ /**
600
+ * Get the maximum version number for an artifact
601
+ * Used when creating new version of an explicitly specified artifact
602
+ */
603
+ private async getMaxVersionForArtifact(artifactId: string, contextUser: UserInfo): Promise<number> {
604
+ try {
605
+ const rv = new RunView();
606
+
607
+ // Query all versions for this artifact to find max version number
608
+ const result = await rv.RunView<ArtifactVersionEntity>(
609
+ {
610
+ EntityName: 'MJ: Artifact Versions',
611
+ ExtraFilter: `ArtifactID='${artifactId}'`,
612
+ OrderBy: 'VersionNumber DESC',
613
+ MaxRows: 1,
614
+ ResultType: 'entity_object',
615
+ },
616
+ contextUser
617
+ );
618
+
619
+ if (result.Success && result.Results && result.Results.length > 0) {
620
+ return result.Results[0].VersionNumber || 0;
621
+ }
622
+
623
+ return 0; // No versions found, will create version 1
624
+ } catch (error) {
625
+ LogError(`Error getting max version for artifact: ${(error as Error).message}`);
626
+ return 0;
537
627
  }
538
-
539
- /**
540
- * System user query for running AI agents with elevated privileges.
541
- * Requires the @RequireSystemUser decorator to ensure only system users can access.
542
- */
543
- @RequireSystemUser()
544
- @Query(() => AIAgentRunResult)
545
- async RunAIAgentSystemUser(
546
- @Arg('agentId') agentId: string,
547
- @Ctx() { userPayload, providers }: AppContext,
548
- @Arg('messages') messagesJson: string,
549
- @Arg('sessionId') sessionId: string,
550
- @PubSub() pubSub: PubSubEngine,
551
- @Arg('data', { nullable: true }) data?: string,
552
- @Arg('payload', { nullable: true }) payload?: string,
553
- @Arg('templateData', { nullable: true }) templateData?: string,
554
- @Arg('lastRunId', { nullable: true }) lastRunId?: string,
555
- @Arg('autoPopulateLastRunPayload', { nullable: true }) autoPopulateLastRunPayload?: boolean,
556
- @Arg('configurationId', { nullable: true }) configurationId?: string,
557
- @Arg('conversationDetailId', { nullable: true }) conversationDetailId?: string,
558
- @Arg('createArtifacts', { nullable: true }) createArtifacts?: boolean,
559
- @Arg('createNotification', { nullable: true }) createNotification?: boolean,
560
- @Arg('sourceArtifactId', { nullable: true }) sourceArtifactId?: string,
561
- @Arg('sourceArtifactVersionId', { nullable: true }) sourceArtifactVersionId?: string
562
- ): Promise<AIAgentRunResult> {
563
- const p = GetReadWriteProvider(providers);
564
- return this.executeAIAgent(
565
- p,
566
- agentId,
567
- userPayload,
568
- messagesJson,
569
- sessionId,
570
- pubSub,
571
- data,
572
- payload,
573
- templateData,
574
- lastRunId,
575
- autoPopulateLastRunPayload,
576
- configurationId,
577
- conversationDetailId,
578
- createArtifacts || false,
579
- createNotification || false,
580
- sourceArtifactId,
581
- sourceArtifactVersionId
582
- );
628
+ }
629
+
630
+ /**
631
+ * Find the most recent artifact for a conversation detail to determine versioning
632
+ * Returns artifact info if exists, null if this is first artifact
633
+ */
634
+ private async findPreviousArtifactForMessage(
635
+ conversationDetailId: string,
636
+ contextUser: UserInfo
637
+ ): Promise<{ artifactId: string; versionNumber: number } | null> {
638
+ try {
639
+ const rv = new RunView();
640
+
641
+ // Query junction table to find artifacts for this message
642
+ const result = await rv.RunView<ConversationDetailArtifactEntity>(
643
+ {
644
+ EntityName: 'MJ: Conversation Detail Artifacts',
645
+ ExtraFilter: `ConversationDetailID='${conversationDetailId}' AND Direction='Output'`,
646
+ OrderBy: '__mj_CreatedAt DESC',
647
+ MaxRows: 1,
648
+ ResultType: 'entity_object',
649
+ },
650
+ contextUser
651
+ );
652
+
653
+ if (!result.Success || !result.Results || result.Results.length === 0) {
654
+ return null;
655
+ }
656
+
657
+ const junction = result.Results[0];
658
+
659
+ // Load the artifact version to get version number and artifact ID
660
+ const md = new Metadata();
661
+ const version = await md.GetEntityObject<ArtifactVersionEntity>('MJ: Artifact Versions', contextUser);
662
+
663
+ if (!(await version.Load(junction.ArtifactVersionID))) {
664
+ return null;
665
+ }
666
+
667
+ return {
668
+ artifactId: version.ArtifactID,
669
+ versionNumber: version.VersionNumber,
670
+ };
671
+ } catch (error) {
672
+ LogError(`Error finding previous artifact: ${(error as Error).message}`);
673
+ return null;
583
674
  }
584
-
585
- /**
586
- * Get the maximum version number for an artifact
587
- * Used when creating new version of an explicitly specified artifact
588
- */
589
- private async getMaxVersionForArtifact(
590
- artifactId: string,
591
- contextUser: UserInfo
592
- ): Promise<number> {
593
- try {
594
- const rv = new RunView();
595
-
596
- // Query all versions for this artifact to find max version number
597
- const result = await rv.RunView<ArtifactVersionEntity>({
598
- EntityName: 'MJ: Artifact Versions',
599
- ExtraFilter: `ArtifactID='${artifactId}'`,
600
- OrderBy: 'VersionNumber DESC',
601
- MaxRows: 1,
602
- ResultType: 'entity_object'
603
- }, contextUser);
604
-
605
- if (result.Success && result.Results && result.Results.length > 0) {
606
- return result.Results[0].VersionNumber || 0;
607
- }
608
-
609
- return 0; // No versions found, will create version 1
610
- } catch (error) {
611
- LogError(`Error getting max version for artifact: ${(error as Error).message}`);
612
- return 0;
613
- }
675
+ }
676
+
677
+ /**
678
+ * Process agent completion to create artifacts from payload
679
+ * Called after agent run completes successfully
680
+ */
681
+ private async processAgentCompletionForArtifacts(
682
+ agentRun: AIAgentRunEntityExtended,
683
+ payload: any,
684
+ contextUser: UserInfo,
685
+ conversationDetailId?: string,
686
+ sourceArtifactId?: string
687
+ ): Promise<{ artifactId?: string; versionId?: string; versionNumber?: number }> {
688
+ // Validate inputs
689
+ if (!payload || Object.keys(payload).length === 0) {
690
+ LogStatus('No payload to create artifact from');
691
+ return {};
614
692
  }
615
693
 
616
- /**
617
- * Find the most recent artifact for a conversation detail to determine versioning
618
- * Returns artifact info if exists, null if this is first artifact
619
- */
620
- private async findPreviousArtifactForMessage(
621
- conversationDetailId: string,
622
- contextUser: UserInfo
623
- ): Promise<{ artifactId: string; versionNumber: number } | null> {
624
- try {
625
- const rv = new RunView();
626
-
627
- // Query junction table to find artifacts for this message
628
- const result = await rv.RunView<ConversationDetailArtifactEntity>({
629
- EntityName: 'MJ: Conversation Detail Artifacts',
630
- ExtraFilter: `ConversationDetailID='${conversationDetailId}' AND Direction='Output'`,
631
- OrderBy: '__mj_CreatedAt DESC',
632
- MaxRows: 1,
633
- ResultType: 'entity_object'
634
- }, contextUser);
635
-
636
- if (!result.Success || !result.Results || result.Results.length === 0) {
637
- return null;
638
- }
639
-
640
- const junction = result.Results[0];
641
-
642
- // Load the artifact version to get version number and artifact ID
643
- const md = new Metadata();
644
- const version = await md.GetEntityObject<ArtifactVersionEntity>(
645
- 'MJ: Artifact Versions',
646
- contextUser
647
- );
648
-
649
- if (!(await version.Load(junction.ArtifactVersionID))) {
650
- return null;
651
- }
652
-
653
- return {
654
- artifactId: version.ArtifactID,
655
- versionNumber: version.VersionNumber
656
- };
657
- } catch (error) {
658
- LogError(`Error finding previous artifact: ${(error as Error).message}`);
659
- return null;
660
- }
694
+ if (!conversationDetailId) {
695
+ LogStatus('Skipping artifact creation - no conversationDetailId provided');
696
+ return {};
661
697
  }
662
698
 
663
- /**
664
- * Process agent completion to create artifacts from payload
665
- * Called after agent run completes successfully
666
- */
667
- private async processAgentCompletionForArtifacts(
668
- agentRun: AIAgentRunEntityExtended,
669
- payload: any,
670
- contextUser: UserInfo,
671
- conversationDetailId?: string,
672
- sourceArtifactId?: string
673
- ): Promise<{ artifactId?: string; versionId?: string; versionNumber?: number }> {
674
- // Validate inputs
675
- if (!payload || Object.keys(payload).length === 0) {
676
- LogStatus('No payload to create artifact from');
677
- return {};
678
- }
679
-
680
- if (!conversationDetailId) {
681
- LogStatus('Skipping artifact creation - no conversationDetailId provided');
682
- return {};
683
- }
699
+ // Check agent's ArtifactCreationMode
700
+ await AIEngine.Instance.Config(false, contextUser);
701
+ const agent = AIEngine.Instance.Agents.find((a) => a.ID === agentRun.AgentID);
702
+ const creationMode = agent?.ArtifactCreationMode;
684
703
 
685
- // Check agent's ArtifactCreationMode
686
- await AIEngine.Instance.Config(false, contextUser);
687
- const agent = AIEngine.Instance.Agents.find(a => a.ID === agentRun.AgentID);
688
- const creationMode = agent?.ArtifactCreationMode;
704
+ if (creationMode === 'Never') {
705
+ LogStatus(`Skipping artifact creation - agent "${agent?.Name}" has ArtifactCreationMode='Never'`);
706
+ return {};
707
+ }
689
708
 
690
- if (creationMode === 'Never') {
691
- LogStatus(`Skipping artifact creation - agent "${agent?.Name}" has ArtifactCreationMode='Never'`);
692
- return {};
709
+ try {
710
+ const md = new Metadata();
711
+ const JSON_ARTIFACT_TYPE_ID = 'ae674c7e-ea0d-49ea-89e4-0649f5eb20d4';
712
+
713
+ // 1. Determine if creating new artifact or new version
714
+ let artifactId: string;
715
+ let newVersionNumber: number;
716
+ let isNewArtifact = false;
717
+
718
+ // Priority 1: Use explicit source artifact if provided (agent continuity/refinement)
719
+ if (sourceArtifactId) {
720
+ const maxVersion = await this.getMaxVersionForArtifact(sourceArtifactId, contextUser);
721
+ artifactId = sourceArtifactId;
722
+ newVersionNumber = maxVersion + 1;
723
+ LogStatus(`Creating version ${newVersionNumber} of source artifact ${artifactId} (explicit source)`);
724
+ }
725
+ // Priority 2: Try to find previous artifact for this message (fallback)
726
+ else {
727
+ const previousArtifact = await this.findPreviousArtifactForMessage(conversationDetailId, contextUser);
728
+
729
+ if (previousArtifact) {
730
+ // Create new version of existing artifact
731
+ artifactId = previousArtifact.artifactId;
732
+ newVersionNumber = previousArtifact.versionNumber + 1;
733
+ LogStatus(`Creating version ${newVersionNumber} of existing artifact ${artifactId}`);
734
+ } else {
735
+ // Create new artifact header
736
+ const artifact = await md.GetEntityObject<ArtifactEntity>('MJ: Artifacts', contextUser);
737
+
738
+ // Get agent info for naming and visibility control
739
+ await AIEngine.Instance.Config(false, contextUser);
740
+ const agent = AIEngine.Instance.Agents.find((a) => a.ID === agentRun.AgentID);
741
+ const agentName = agent?.Name || 'Agent';
742
+
743
+ artifact.Name = `${agentName} Payload - ${new Date().toLocaleString()}`;
744
+ artifact.Description = `Payload returned by ${agentName}`;
745
+
746
+ // Use agent's DefaultArtifactTypeID if available, otherwise JSON
747
+ const defaultArtifactTypeId = (agent as any)?.DefaultArtifactTypeID;
748
+ artifact.TypeID = defaultArtifactTypeId || JSON_ARTIFACT_TYPE_ID;
749
+
750
+ artifact.UserID = contextUser.ID;
751
+ artifact.EnvironmentID = (contextUser as any).EnvironmentID || 'F51358F3-9447-4176-B313-BF8025FD8D09';
752
+
753
+ // Set visibility based on agent's ArtifactCreationMode
754
+ // Will compile after CodeGen adds the new fields
755
+ const creationMode = agent.ArtifactCreationMode;
756
+ if (creationMode === 'System Only') {
757
+ artifact.Visibility = 'System Only';
758
+ LogStatus(`Artifact marked as "System Only" per agent configuration`);
759
+ } else {
760
+ artifact.Visibility = 'Always';
761
+ }
762
+
763
+ if (!(await artifact.Save())) {
764
+ throw new Error('Failed to save artifact');
765
+ }
766
+
767
+ artifactId = artifact.ID;
768
+ newVersionNumber = 1;
769
+ isNewArtifact = true;
770
+ LogStatus(`Created new artifact: ${artifact.Name} (${artifactId})`);
693
771
  }
772
+ }
694
773
 
695
- try {
696
- const md = new Metadata();
697
- const JSON_ARTIFACT_TYPE_ID = 'ae674c7e-ea0d-49ea-89e4-0649f5eb20d4';
698
-
699
- // 1. Determine if creating new artifact or new version
700
- let artifactId: string;
701
- let newVersionNumber: number;
702
- let isNewArtifact = false;
703
-
704
- // Priority 1: Use explicit source artifact if provided (agent continuity/refinement)
705
- if (sourceArtifactId) {
706
- const maxVersion = await this.getMaxVersionForArtifact(sourceArtifactId, contextUser);
707
- artifactId = sourceArtifactId;
708
- newVersionNumber = maxVersion + 1;
709
- LogStatus(`Creating version ${newVersionNumber} of source artifact ${artifactId} (explicit source)`);
710
- }
711
- // Priority 2: Try to find previous artifact for this message (fallback)
712
- else {
713
- const previousArtifact = await this.findPreviousArtifactForMessage(
714
- conversationDetailId,
715
- contextUser
716
- );
717
-
718
- if (previousArtifact) {
719
- // Create new version of existing artifact
720
- artifactId = previousArtifact.artifactId;
721
- newVersionNumber = previousArtifact.versionNumber + 1;
722
- LogStatus(`Creating version ${newVersionNumber} of existing artifact ${artifactId}`);
723
- } else {
724
- // Create new artifact header
725
- const artifact = await md.GetEntityObject<ArtifactEntity>(
726
- 'MJ: Artifacts',
727
- contextUser
728
- );
729
-
730
- // Get agent info for naming and visibility control
731
- await AIEngine.Instance.Config(false, contextUser);
732
- const agent = AIEngine.Instance.Agents.find(a => a.ID === agentRun.AgentID);
733
- const agentName = agent?.Name || 'Agent';
734
-
735
- artifact.Name = `${agentName} Payload - ${new Date().toLocaleString()}`;
736
- artifact.Description = `Payload returned by ${agentName}`;
737
-
738
- // Use agent's DefaultArtifactTypeID if available, otherwise JSON
739
- const defaultArtifactTypeId = (agent as any)?.DefaultArtifactTypeID;
740
- artifact.TypeID = defaultArtifactTypeId || JSON_ARTIFACT_TYPE_ID;
741
-
742
- artifact.UserID = contextUser.ID;
743
- artifact.EnvironmentID = (contextUser as any).EnvironmentID ||
744
- 'F51358F3-9447-4176-B313-BF8025FD8D09';
745
-
746
- // Set visibility based on agent's ArtifactCreationMode
747
- // Will compile after CodeGen adds the new fields
748
- const creationMode = agent.ArtifactCreationMode;
749
- if (creationMode === 'System Only') {
750
- artifact.Visibility = 'System Only';
751
- LogStatus(`Artifact marked as "System Only" per agent configuration`);
752
- } else {
753
- artifact.Visibility = 'Always';
754
- }
755
-
756
- if (!(await artifact.Save())) {
757
- throw new Error('Failed to save artifact');
758
- }
759
-
760
- artifactId = artifact.ID;
761
- newVersionNumber = 1;
762
- isNewArtifact = true;
763
- LogStatus(`Created new artifact: ${artifact.Name} (${artifactId})`);
764
- }
765
- }
774
+ // 2. Create artifact version with content
775
+ const version = await md.GetEntityObject<ArtifactVersionEntity>('MJ: Artifact Versions', contextUser);
776
+ version.ArtifactID = artifactId;
777
+ version.VersionNumber = newVersionNumber;
778
+ version.Content = JSON.stringify(payload, null, 2);
779
+ version.UserID = contextUser.ID;
766
780
 
767
- // 2. Create artifact version with content
768
- const version = await md.GetEntityObject<ArtifactVersionEntity>(
769
- 'MJ: Artifact Versions',
770
- contextUser
771
- );
772
- version.ArtifactID = artifactId;
773
- version.VersionNumber = newVersionNumber;
774
- version.Content = JSON.stringify(payload, null, 2);
775
- version.UserID = contextUser.ID;
781
+ if (!(await version.Save())) {
782
+ throw new Error('Failed to save artifact version');
783
+ }
776
784
 
777
- if (!(await version.Save())) {
778
- throw new Error('Failed to save artifact version');
779
- }
780
-
781
- LogStatus(`Created artifact version ${newVersionNumber} (${version.ID})`);
782
-
783
- // If this is the first version of a new artifact, check for extracted Name attribute and update artifact
784
- if (isNewArtifact && newVersionNumber === 1) {
785
- const nameAttr = (version as any).Attributes?.find((attr: any) =>
786
- attr.StandardProperty === 'name' || attr.Name?.toLowerCase() === 'name'
787
- );
788
-
789
- // Check for valid name value (not null, not empty, not string "null")
790
- let extractedName = nameAttr?.Value?.trim();
791
- if (extractedName && extractedName.toLowerCase() !== 'null') {
792
- // Strip surrounding quotes (double or single) from start and end
793
- extractedName = extractedName.replace(/^["']|["']$/g, '');
794
-
795
- // Load artifact to update with extracted name
796
- const artifact = await md.GetEntityObject<ArtifactEntity>(
797
- 'MJ: Artifacts',
798
- contextUser
799
- );
800
-
801
- if (!(await artifact.Load(artifactId))) {
802
- LogError('Failed to reload artifact for name update');
803
- } else {
804
- artifact.Name = extractedName;
805
- if (await artifact.Save()) {
806
- LogStatus(`✨ Updated artifact name to: ${artifact.Name}`);
807
- }
808
- }
809
- }
810
- }
785
+ LogStatus(`Created artifact version ${newVersionNumber} (${version.ID})`);
811
786
 
812
- // 3. Create junction record linking artifact to conversation detail
813
- const junction = await md.GetEntityObject<ConversationDetailArtifactEntity>(
814
- 'MJ: Conversation Detail Artifacts',
815
- contextUser
816
- );
817
- junction.ConversationDetailID = conversationDetailId;
818
- junction.ArtifactVersionID = version.ID;
819
- junction.Direction = 'Output';
787
+ // If this is the first version of a new artifact, check for extracted Name attribute and update artifact
788
+ if (isNewArtifact && newVersionNumber === 1) {
789
+ const nameAttr = (version as any).Attributes?.find(
790
+ (attr: any) => attr.StandardProperty === 'name' || attr.Name?.toLowerCase() === 'name'
791
+ );
820
792
 
821
- if (!(await junction.Save())) {
822
- throw new Error('Failed to create artifact-message association');
793
+ // Check for valid name value (not null, not empty, not string "null")
794
+ let extractedName = nameAttr?.Value?.trim();
795
+ if (extractedName && extractedName.toLowerCase() !== 'null') {
796
+ // Strip surrounding quotes (double or single) from start and end
797
+ extractedName = extractedName.replace(/^["']|["']$/g, '');
798
+
799
+ // Load artifact to update with extracted name
800
+ const artifact = await md.GetEntityObject<ArtifactEntity>('MJ: Artifacts', contextUser);
801
+
802
+ if (!(await artifact.Load(artifactId))) {
803
+ LogError('Failed to reload artifact for name update');
804
+ } else {
805
+ artifact.Name = extractedName;
806
+ if (await artifact.Save()) {
807
+ LogStatus(`✨ Updated artifact name to: ${artifact.Name}`);
823
808
  }
824
-
825
- LogStatus(`Linked artifact to conversation detail ${conversationDetailId}`);
826
-
827
- return {
828
- artifactId,
829
- versionId: version.ID,
830
- versionNumber: newVersionNumber
831
- };
832
- } catch (error) {
833
- LogError(`Failed to process agent completion for artifacts: ${(error as Error).message}`);
834
- return {};
809
+ }
835
810
  }
811
+ }
812
+
813
+ // 3. Create junction record linking artifact to conversation detail
814
+ const junction = await md.GetEntityObject<ConversationDetailArtifactEntity>('MJ: Conversation Detail Artifacts', contextUser);
815
+ junction.ConversationDetailID = conversationDetailId;
816
+ junction.ArtifactVersionID = version.ID;
817
+ junction.Direction = 'Output';
818
+
819
+ if (!(await junction.Save())) {
820
+ throw new Error('Failed to create artifact-message association');
821
+ }
822
+
823
+ LogStatus(`Linked artifact to conversation detail ${conversationDetailId}`);
824
+
825
+ return {
826
+ artifactId,
827
+ versionId: version.ID,
828
+ versionNumber: newVersionNumber,
829
+ };
830
+ } catch (error) {
831
+ LogError(`Failed to process agent completion for artifacts: ${(error as Error).message}`);
832
+ return {};
836
833
  }
837
-
838
- /**
839
- * Create a user notification for agent completion with artifact
840
- * Notification includes navigation link back to the conversation
841
- */
842
- private async createCompletionNotification(
843
- agentRun: AIAgentRunEntityExtended,
844
- artifactInfo: { artifactId: string; versionId: string; versionNumber: number },
845
- conversationDetailId: string,
846
- contextUser: UserInfo,
847
- pubSub: PubSubEngine,
848
- userPayload: UserPayload
849
- ): Promise<void> {
850
- try {
851
- const md = new Metadata();
852
-
853
- // Get agent info for notification message
854
- await AIEngine.Instance.Config(false, contextUser);
855
- const agent = AIEngine.Instance.Agents.find(a => a.ID === agentRun.AgentID);
856
- const agentName = agent?.Name || 'Agent';
857
-
858
- // Load conversation detail to get conversation info
859
- const detail = await md.GetEntityObject<ConversationDetailEntity>(
860
- 'Conversation Details',
861
- contextUser
862
- );
863
- if (!(await detail.Load(conversationDetailId))) {
864
- throw new Error(`Failed to load conversation detail ${conversationDetailId}`);
865
- }
866
-
867
- // Create notification entity
868
- const notification = await md.GetEntityObject<UserNotificationEntity>(
869
- 'User Notifications',
870
- contextUser
871
- );
872
-
873
- notification.UserID = contextUser.ID;
874
- notification.Title = `${agentName} completed your request`;
875
-
876
- // Craft message based on versioning
877
- if (artifactInfo.versionNumber > 1) {
878
- notification.Message = `${agentName} has finished processing and created version ${artifactInfo.versionNumber}`;
879
- } else {
880
- notification.Message = `${agentName} has finished processing and created a new artifact`;
881
- }
882
-
883
- // Store navigation configuration as JSON
884
- // Client will parse this to navigate to the conversation with artifact visible
885
- notification.ResourceConfiguration = JSON.stringify({
886
- type: 'conversation',
887
- conversationId: detail.ConversationID,
888
- messageId: conversationDetailId,
889
- artifactId: artifactInfo.artifactId,
890
- versionNumber: artifactInfo.versionNumber
891
- });
892
-
893
- notification.Unread = true; // Default unread
894
- // ResourceTypeID and ResourceRecordID left null - using custom navigation
895
-
896
- if (!(await notification.Save())) {
897
- throw new Error('Failed to save notification');
898
- }
899
-
900
- LogStatus(`📬 Created notification ${notification.ID} for user ${contextUser.ID}`);
901
-
902
- // Publish real-time notification event so client updates immediately
903
- pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
904
- userPayload: JSON.stringify(userPayload),
905
- message: JSON.stringify({
906
- type: 'notification',
907
- notificationId: notification.ID,
908
- action: 'create',
909
- title: notification.Title,
910
- message: notification.Message
911
- })
912
- });
913
-
914
- LogStatus(`📡 Published notification event to client`);
915
-
916
- } catch (error) {
917
- LogError(`Failed to create completion notification: ${(error as Error).message}`);
918
- // Don't throw - notification failure shouldn't fail the agent run
919
- }
834
+ }
835
+
836
+ /**
837
+ * Create a user notification for agent completion with artifact
838
+ * Notification includes navigation link back to the conversation
839
+ */
840
+ private async createCompletionNotification(
841
+ agentRun: AIAgentRunEntityExtended,
842
+ artifactInfo: { artifactId: string; versionId: string; versionNumber: number },
843
+ conversationDetailId: string,
844
+ contextUser: UserInfo,
845
+ pubSub: PubSubEngine,
846
+ userPayload: UserPayload
847
+ ): Promise<void> {
848
+ try {
849
+ const md = new Metadata();
850
+
851
+ // Get agent info for notification message
852
+ await AIEngine.Instance.Config(false, contextUser);
853
+ const agent = AIEngine.Instance.Agents.find((a) => a.ID === agentRun.AgentID);
854
+ const agentName = agent?.Name || 'Agent';
855
+
856
+ // Load conversation detail to get conversation info
857
+ const detail = await md.GetEntityObject<ConversationDetailEntity>('Conversation Details', contextUser);
858
+ if (!(await detail.Load(conversationDetailId))) {
859
+ throw new Error(`Failed to load conversation detail ${conversationDetailId}`);
860
+ }
861
+
862
+ // Create notification entity
863
+ const notification = await md.GetEntityObject<UserNotificationEntity>('User Notifications', contextUser);
864
+
865
+ notification.UserID = contextUser.ID;
866
+ notification.Title = `${agentName} completed your request`;
867
+
868
+ // Craft message based on versioning
869
+ if (artifactInfo.versionNumber > 1) {
870
+ notification.Message = `${agentName} has finished processing and created version ${artifactInfo.versionNumber}`;
871
+ } else {
872
+ notification.Message = `${agentName} has finished processing and created a new artifact`;
873
+ }
874
+
875
+ // Store navigation configuration as JSON
876
+ // Client will parse this to navigate to the conversation with artifact visible
877
+ notification.ResourceConfiguration = JSON.stringify({
878
+ type: 'conversation',
879
+ conversationId: detail.ConversationID,
880
+ messageId: conversationDetailId,
881
+ artifactId: artifactInfo.artifactId,
882
+ versionNumber: artifactInfo.versionNumber,
883
+ });
884
+
885
+ notification.Unread = true; // Default unread
886
+ // ResourceTypeID and ResourceRecordID left null - using custom navigation
887
+
888
+ if (!(await notification.Save())) {
889
+ throw new Error('Failed to save notification');
890
+ }
891
+
892
+ LogStatus(`📬 Created notification ${notification.ID} for user ${contextUser.ID}`);
893
+
894
+ // Publish real-time notification event so client updates immediately
895
+ pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
896
+ userPayload: JSON.stringify(userPayload),
897
+ message: JSON.stringify({
898
+ type: 'notification',
899
+ notificationId: notification.ID,
900
+ action: 'create',
901
+ title: notification.Title,
902
+ message: notification.Message,
903
+ }),
904
+ });
905
+
906
+ LogStatus(`📡 Published notification event to client`);
907
+ } catch (error) {
908
+ LogError(`Failed to create completion notification: ${(error as Error).message}`);
909
+ // Don't throw - notification failure shouldn't fail the agent run
920
910
  }
921
-
922
- }
911
+ }
912
+ }