@memberjunction/server 2.112.0 → 2.113.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 +951 -808
  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 +43 -53
  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 +3 -1
  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 +2 -3
  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 +6 -3
  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 +10 -22
  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 +7 -9
  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 +788 -658
  65. package/dist/generated/generated.d.ts.map +1 -1
  66. package/dist/generated/generated.js +2050 -3054
  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 +10 -15
  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 +13 -18
  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 +30 -28
  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 +50 -60
  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 +38 -36
  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 +40 -43
  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 +6 -8
  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 +28 -27
  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 +14 -15
  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 +44 -48
  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 +16 -14
  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 +25 -23
  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 +21 -17
  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 +6 -4
  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 +1 -0
  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 +39 -36
  194. package/src/agents/skip-agent.ts +1200 -1067
  195. package/src/agents/skip-sdk.ts +851 -877
  196. package/src/apolloServer/index.ts +2 -2
  197. package/src/auth/AuthProviderFactory.ts +14 -8
  198. package/src/auth/BaseAuthProvider.ts +4 -5
  199. package/src/auth/IAuthProvider.ts +2 -2
  200. package/src/auth/exampleNewUserSubClass.ts +2 -9
  201. package/src/auth/index.ts +26 -31
  202. package/src/auth/initializeProviders.ts +3 -3
  203. package/src/auth/newUsers.ts +134 -166
  204. package/src/auth/providers/Auth0Provider.ts +5 -5
  205. package/src/auth/providers/CognitoProvider.ts +10 -7
  206. package/src/auth/providers/GoogleProvider.ts +5 -4
  207. package/src/auth/providers/MSALProvider.ts +5 -5
  208. package/src/auth/providers/OktaProvider.ts +7 -6
  209. package/src/config.ts +54 -63
  210. package/src/context.ts +30 -42
  211. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  212. package/src/generated/generated.ts +40442 -48106
  213. package/src/generic/KeyInputOutputTypes.ts +6 -3
  214. package/src/generic/ResolverBase.ts +78 -119
  215. package/src/generic/RunViewResolver.ts +23 -27
  216. package/src/index.ts +48 -66
  217. package/src/resolvers/ActionResolver.ts +57 -46
  218. package/src/resolvers/AskSkipResolver.ts +533 -607
  219. package/src/resolvers/ComponentRegistryResolver.ts +562 -547
  220. package/src/resolvers/CreateQueryResolver.ts +655 -683
  221. package/src/resolvers/DatasetResolver.ts +6 -5
  222. package/src/resolvers/EntityCommunicationsResolver.ts +1 -1
  223. package/src/resolvers/EntityRecordNameResolver.ts +5 -9
  224. package/src/resolvers/EntityResolver.ts +7 -9
  225. package/src/resolvers/FileCategoryResolver.ts +2 -2
  226. package/src/resolvers/FileResolver.ts +4 -4
  227. package/src/resolvers/GetDataContextDataResolver.ts +118 -106
  228. package/src/resolvers/GetDataResolver.ts +205 -194
  229. package/src/resolvers/MergeRecordsResolver.ts +5 -5
  230. package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
  231. package/src/resolvers/QueryResolver.ts +78 -95
  232. package/src/resolvers/ReportResolver.ts +2 -2
  233. package/src/resolvers/RunAIAgentResolver.ts +828 -818
  234. package/src/resolvers/RunAIPromptResolver.ts +709 -693
  235. package/src/resolvers/RunTemplateResolver.ts +103 -105
  236. package/src/resolvers/SqlLoggingConfigResolver.ts +72 -69
  237. package/src/resolvers/SyncDataResolver.ts +352 -386
  238. package/src/resolvers/SyncRolesUsersResolver.ts +350 -387
  239. package/src/resolvers/TaskResolver.ts +115 -110
  240. package/src/resolvers/TransactionGroupResolver.ts +138 -143
  241. package/src/resolvers/UserFavoriteResolver.ts +8 -17
  242. package/src/resolvers/UserViewResolver.ts +12 -17
  243. package/src/rest/EntityCRUDHandler.ts +268 -291
  244. package/src/rest/RESTEndpointHandler.ts +776 -782
  245. package/src/rest/ViewOperationsHandler.ts +195 -191
  246. package/src/scheduler/LearningCycleScheduler.ts +52 -8
  247. package/src/services/ScheduledJobsService.ts +132 -129
  248. package/src/services/TaskOrchestrator.ts +776 -792
  249. package/src/types.ts +9 -15
  250. package/src/util.ts +109 -112
@@ -1,29 +1,7 @@
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';
1
+ import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, Subscription, Root, ResolverFilterData, ID } from 'type-graphql';
16
2
  import { AppContext, UserPayload } from '../types.js';
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';
3
+ import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo } from '@memberjunction/core';
4
+ import { AIAgentEntityExtended, ArtifactEntity, ArtifactVersionEntity, ConversationDetailArtifactEntity, ConversationDetailEntity, UserNotificationEntity, AIAgentRunEntityExtended } from '@memberjunction/core-entities';
27
5
  import { AgentRunner } from '@memberjunction/ai-agents';
28
6
  import { ExecuteAgentResult } from '@memberjunction/ai-core-plus';
29
7
  import { AIEngine } from '@memberjunction/aiengine';
@@ -35,878 +13,910 @@ import { SafeJSONParse } from '@memberjunction/global';
35
13
 
36
14
  @ObjectType()
37
15
  export class AIAgentRunResult {
38
- @Field()
39
- success: boolean;
16
+ @Field()
17
+ success: boolean;
40
18
 
41
- @Field({ nullable: true })
42
- errorMessage?: string;
19
+ @Field({ nullable: true })
20
+ errorMessage?: string;
43
21
 
44
- @Field({ nullable: true })
45
- executionTimeMs?: number;
22
+ @Field({ nullable: true })
23
+ executionTimeMs?: number;
46
24
 
47
- @Field()
48
- result: string; // JSON serialized ExecuteAgentResult with scalars only
25
+ @Field()
26
+ result: string; // JSON serialized ExecuteAgentResult with scalars only
49
27
  }
50
28
 
51
29
  @ObjectType()
52
30
  export class AgentExecutionProgress {
53
- @Field()
54
- currentStep: string;
31
+ @Field()
32
+ currentStep: string;
55
33
 
56
- @Field()
57
- percentage: number;
34
+ @Field()
35
+ percentage: number;
58
36
 
59
- @Field()
60
- message: string;
37
+ @Field()
38
+ message: string;
61
39
 
62
- @Field({ nullable: true })
63
- agentName?: string;
40
+ @Field({ nullable: true })
41
+ agentName?: string;
64
42
 
65
- @Field({ nullable: true })
66
- agentType?: string;
43
+ @Field({ nullable: true })
44
+ agentType?: string;
67
45
  }
68
46
 
69
47
  @ObjectType()
70
48
  export class AgentStreamingContent {
71
- @Field()
72
- content: string;
49
+ @Field()
50
+ content: string;
73
51
 
74
- @Field()
75
- isPartial: boolean;
52
+ @Field()
53
+ isPartial: boolean;
76
54
 
77
- @Field({ nullable: true })
78
- stepName?: string;
55
+ @Field({ nullable: true })
56
+ stepName?: string;
79
57
 
80
- @Field({ nullable: true })
81
- agentName?: string;
58
+ @Field({ nullable: true })
59
+ agentName?: string;
82
60
  }
83
61
 
84
62
  @ObjectType()
85
63
  export class AgentExecutionStepSummary {
86
- @Field()
87
- stepId: string;
64
+ @Field()
65
+ stepId: string;
88
66
 
89
- @Field()
90
- stepName: string;
67
+ @Field()
68
+ stepName: string;
91
69
 
92
- @Field({ nullable: true })
93
- agentName?: string;
70
+ @Field({ nullable: true })
71
+ agentName?: string;
94
72
 
95
- @Field({ nullable: true })
96
- agentType?: string;
73
+ @Field({ nullable: true })
74
+ agentType?: string;
97
75
 
98
- @Field()
99
- startTime: Date;
76
+ @Field()
77
+ startTime: Date;
100
78
 
101
- @Field({ nullable: true })
102
- endTime?: Date;
79
+ @Field({ nullable: true })
80
+ endTime?: Date;
103
81
 
104
- @Field()
105
- status: string;
82
+ @Field()
83
+ status: string;
106
84
 
107
- @Field({ nullable: true })
108
- result?: string;
85
+ @Field({ nullable: true })
86
+ result?: string;
109
87
  }
110
88
 
111
89
  @ObjectType()
112
90
  export class AgentPartialResult {
113
- @Field()
114
- currentStep: string;
91
+ @Field()
92
+ currentStep: string;
115
93
 
116
- @Field({ nullable: true })
117
- partialOutput?: string;
94
+ @Field({ nullable: true })
95
+ partialOutput?: string;
118
96
  }
119
97
 
120
98
  @ObjectType()
121
99
  export class AgentExecutionStreamMessage {
122
- @Field(() => ID)
123
- sessionId: string;
100
+ @Field(() => ID)
101
+ sessionId: string;
124
102
 
125
- @Field(() => ID)
126
- agentRunId: string;
103
+ @Field(() => ID)
104
+ agentRunId: string;
127
105
 
128
- @Field()
129
- type: 'progress' | 'streaming' | 'partial_result' | 'complete';
106
+ @Field()
107
+ type: 'progress' | 'streaming' | 'partial_result' | 'complete';
130
108
 
131
- @Field({ nullable: true })
132
- progress?: AgentExecutionProgress;
109
+ @Field({ nullable: true })
110
+ progress?: AgentExecutionProgress;
133
111
 
134
- @Field({ nullable: true })
135
- streaming?: AgentStreamingContent;
112
+ @Field({ nullable: true })
113
+ streaming?: AgentStreamingContent;
136
114
 
137
- @Field({ nullable: true })
138
- partialResult?: AgentPartialResult;
115
+ @Field({ nullable: true })
116
+ partialResult?: AgentPartialResult;
139
117
 
140
- @Field()
141
- timestamp: Date;
142
-
143
- // Not a GraphQL field - used internally for streaming
144
- agentRun?: any;
118
+ @Field()
119
+ timestamp: Date;
120
+
121
+ // Not a GraphQL field - used internally for streaming
122
+ agentRun?: any;
145
123
  }
146
124
 
125
+
126
+
127
+
128
+
147
129
  @Resolver()
148
130
  export class RunAIAgentResolver extends ResolverBase {
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);
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
+ }
171
171
  }
172
172
 
173
- return sanitized;
174
- }
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
+ }
175
186
 
176
- /**
177
- * Parse and validate JSON input
178
- */
179
- private parseJsonInput(jsonString: string | undefined, fieldName: string): any {
180
- if (!jsonString) return {};
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
+ }
181
191
 
182
- try {
183
- return JSON.parse(jsonString);
184
- } catch (parseError) {
185
- throw new Error(`Invalid JSON in ${fieldName}: ${(parseError as Error).message}`);
192
+ return agentEntity;
186
193
  }
187
- }
188
194
 
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);
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
+ }
195
252
 
196
- // Find agent in cached collection
197
- const agentEntity = AIEngine.Instance.Agents.find((a: AIAgentEntityExtended) => a.ID === agentId);
198
253
 
199
- if (!agentEntity) {
200
- throw new Error(`AI Agent with ID ${agentId} not found`);
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
+ });
201
264
  }
202
265
 
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})`);
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
+ };
206
302
  }
207
303
 
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
- }
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
+ };
430
453
  }
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
- };
467
- }
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);
497
454
  }
498
455
 
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;
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);
627
493
  }
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;
494
+
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
+ );
674
537
  }
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 {};
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
+ );
692
583
  }
693
584
 
694
- if (!conversationDetailId) {
695
- LogStatus('Skipping artifact creation - no conversationDetailId provided');
696
- return {};
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
+ }
697
614
  }
698
615
 
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;
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];
703
641
 
704
- if (creationMode === 'Never') {
705
- LogStatus(`Skipping artifact creation - agent "${agent?.Name}" has ArtifactCreationMode='Never'`);
706
- return {};
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
+ }
707
661
  }
708
662
 
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})`);
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 {};
771
678
  }
772
- }
773
679
 
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;
680
+ if (!conversationDetailId) {
681
+ LogStatus('Skipping artifact creation - no conversationDetailId provided');
682
+ return {};
683
+ }
780
684
 
781
- if (!(await version.Save())) {
782
- throw new Error('Failed to save artifact version');
783
- }
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;
784
689
 
785
- LogStatus(`Created artifact version ${newVersionNumber} (${version.ID})`);
690
+ if (creationMode === 'Never') {
691
+ LogStatus(`Skipping artifact creation - agent "${agent?.Name}" has ArtifactCreationMode='Never'`);
692
+ return {};
693
+ }
786
694
 
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
- );
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
+ }
792
766
 
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}`);
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;
776
+
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
+ }
808
810
  }
809
- }
811
+
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';
820
+
821
+ if (!(await junction.Save())) {
822
+ throw new Error('Failed to create artifact-message association');
823
+ }
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 {};
810
835
  }
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 {};
833
836
  }
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
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
+ }
910
920
  }
911
- }
912
- }
921
+
922
+ }