@memberjunction/server 5.24.0 → 5.25.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 (52) hide show
  1. package/dist/agents/skip-sdk.d.ts +12 -0
  2. package/dist/agents/skip-sdk.d.ts.map +1 -1
  3. package/dist/agents/skip-sdk.js +70 -1
  4. package/dist/agents/skip-sdk.js.map +1 -1
  5. package/dist/generated/generated.d.ts +492 -0
  6. package/dist/generated/generated.d.ts.map +1 -1
  7. package/dist/generated/generated.js +2731 -0
  8. package/dist/generated/generated.js.map +1 -1
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/resolvers/ArtifactFileResolver.d.ts +15 -0
  14. package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -0
  15. package/dist/resolvers/ArtifactFileResolver.js +74 -0
  16. package/dist/resolvers/ArtifactFileResolver.js.map +1 -0
  17. package/dist/resolvers/AutotagPipelineResolver.d.ts +13 -0
  18. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  19. package/dist/resolvers/AutotagPipelineResolver.js +103 -3
  20. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  21. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  22. package/dist/resolvers/FileResolver.js +12 -32
  23. package/dist/resolvers/FileResolver.js.map +1 -1
  24. package/dist/resolvers/GeoResolver.d.ts +58 -0
  25. package/dist/resolvers/GeoResolver.d.ts.map +1 -0
  26. package/dist/resolvers/GeoResolver.js +302 -0
  27. package/dist/resolvers/GeoResolver.js.map +1 -0
  28. package/dist/resolvers/RunAIAgentResolver.d.ts +13 -1
  29. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  30. package/dist/resolvers/RunAIAgentResolver.js +115 -20
  31. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  32. package/dist/resolvers/SearchKnowledgeResolver.d.ts +21 -80
  33. package/dist/resolvers/SearchKnowledgeResolver.d.ts.map +1 -1
  34. package/dist/resolvers/SearchKnowledgeResolver.js +129 -604
  35. package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
  36. package/dist/resolvers/SearchKnowledgeSystemUserResolver.d.ts +19 -0
  37. package/dist/resolvers/SearchKnowledgeSystemUserResolver.d.ts.map +1 -0
  38. package/dist/resolvers/SearchKnowledgeSystemUserResolver.js +149 -0
  39. package/dist/resolvers/SearchKnowledgeSystemUserResolver.js.map +1 -0
  40. package/package.json +63 -63
  41. package/src/__tests__/search-knowledge-tags.test.ts +177 -337
  42. package/src/__tests__/skip-sdk-organic-keys.test.ts +274 -0
  43. package/src/agents/skip-sdk.ts +83 -2
  44. package/src/generated/generated.ts +1884 -1
  45. package/src/index.ts +2 -0
  46. package/src/resolvers/ArtifactFileResolver.ts +71 -0
  47. package/src/resolvers/AutotagPipelineResolver.ts +118 -4
  48. package/src/resolvers/FileResolver.ts +12 -41
  49. package/src/resolvers/GeoResolver.ts +258 -0
  50. package/src/resolvers/RunAIAgentResolver.ts +137 -23
  51. package/src/resolvers/SearchKnowledgeResolver.ts +114 -715
  52. package/src/resolvers/SearchKnowledgeSystemUserResolver.ts +138 -0
@@ -1,17 +1,17 @@
1
1
  import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, Subscription, Root, ResolverFilterData, ID, Int } from 'type-graphql';
2
2
  import { AppContext, UserPayload } from '../types.js';
3
- import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo } from '@memberjunction/core';
4
- import { MJConversationDetailEntity, MJConversationDetailAttachmentEntity, MJAIAgentRequestEntity } from '@memberjunction/core-entities';
3
+ import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo, IMetadataProvider } from '@memberjunction/core';
4
+ import { MJConversationDetailEntity, MJConversationDetailAttachmentEntity, MJConversationDetailArtifactEntity, MJArtifactVersionEntity, MJAIAgentRequestEntity } from '@memberjunction/core-entities';
5
5
  import { AgentRunner } from '@memberjunction/ai-agents';
6
6
  import { MJAIAgentEntityExtended, MJAIAgentRunEntityExtended, ExecuteAgentResult, ConversationUtility, AttachmentData } from '@memberjunction/ai-core-plus';
7
7
  import { AIEngine } from '@memberjunction/aiengine';
8
- import { ChatMessage } from '@memberjunction/ai';
8
+ import { ChatMessage, ChatMessageContent } from '@memberjunction/ai';
9
9
  import { ResolverBase } from '../generic/ResolverBase.js';
10
10
  import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
11
11
  import { RequireSystemUser } from '../directives/RequireSystemUser.js';
12
12
  import { GetReadWriteProvider } from '../util.js';
13
13
  import { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
14
- import { getAttachmentService } from '@memberjunction/aiengine';
14
+ import { GetAttachmentService } from '@memberjunction/aiengine';
15
15
  import { NotificationEngine } from '@memberjunction/notifications';
16
16
 
17
17
  @ObjectType()
@@ -453,7 +453,7 @@ export class RunAIAgentResolver extends ResolverBase {
453
453
 
454
454
  if (lastRunId && result.agentRun?.ID) {
455
455
  postExecutionOps.push(
456
- this.syncFeedbackRequestFromConversation(lastRunId, result.agentRun.ID, userMessage, currentUser)
456
+ this.syncFeedbackRequestFromConversation(lastRunId, result.agentRun.ID, userMessage, currentUser, p)
457
457
  );
458
458
  }
459
459
 
@@ -797,10 +797,11 @@ export class RunAIAgentResolver extends ResolverBase {
797
797
  lastRunId: string,
798
798
  newRunId: string,
799
799
  userMessage: string | undefined,
800
- contextUser: UserInfo
800
+ contextUser: UserInfo,
801
+ provider: IMetadataProvider
801
802
  ): Promise<void> {
802
803
  try {
803
- const rv = new RunView();
804
+ const rv = RunView.FromMetadataProvider(provider);
804
805
  const result = await rv.RunView<MJAIAgentRequestEntity>({
805
806
  EntityName: 'MJ: AI Agent Requests',
806
807
  ExtraFilter: `OriginatingAgentRunID='${lastRunId}' AND Status='Requested'`,
@@ -939,8 +940,7 @@ export class RunAIAgentResolver extends ResolverBase {
939
940
  // times: once in loadConversationHistoryWithAttachments (just to get conversationId),
940
941
  // once in AgentRunner (same reason), and once in createCompletionNotification. Now we
941
942
  // load it a single time and thread conversationId through the call chain.
942
- const md = new Metadata();
943
- const currentDetail = await md.GetEntityObject<MJConversationDetailEntity>(
943
+ const currentDetail = await p.GetEntityObject<MJConversationDetailEntity>(
944
944
  'MJ: Conversation Details',
945
945
  currentUser
946
946
  );
@@ -953,7 +953,8 @@ export class RunAIAgentResolver extends ResolverBase {
953
953
  const messages = await this.loadConversationHistoryWithAttachments(
954
954
  conversationId,
955
955
  currentUser,
956
- maxHistoryMessages || 20
956
+ maxHistoryMessages || 20,
957
+ p
957
958
  );
958
959
 
959
960
  // Convert to JSON string for the existing executeAIAgent method
@@ -1031,8 +1032,8 @@ export class RunAIAgentResolver extends ResolverBase {
1031
1032
  throw new Error('Unable to determine current user');
1032
1033
  }
1033
1034
 
1034
- const md = new Metadata();
1035
- const request = await md.GetEntityObject<MJAIAgentRequestEntity>(
1035
+ const p = GetReadWriteProvider(providers);
1036
+ const request = await p.GetEntityObject<MJAIAgentRequestEntity>(
1036
1037
  'MJ: AI Agent Requests',
1037
1038
  currentUser
1038
1039
  );
@@ -1096,7 +1097,7 @@ export class RunAIAgentResolver extends ResolverBase {
1096
1097
  async ReassignAgentRequest(
1097
1098
  @Arg('requestId') requestId: string,
1098
1099
  @Arg('newUserID') newUserID: string,
1099
- @Ctx() { userPayload }: AppContext,
1100
+ @Ctx() { userPayload, providers }: AppContext,
1100
1101
  @Arg('note', { nullable: true }) note?: string
1101
1102
  ): Promise<AIAgentRunResult> {
1102
1103
  const startTime = Date.now();
@@ -1106,8 +1107,8 @@ export class RunAIAgentResolver extends ResolverBase {
1106
1107
  throw new Error('Unable to determine current user');
1107
1108
  }
1108
1109
 
1109
- const md = new Metadata();
1110
- const request = await md.GetEntityObject<MJAIAgentRequestEntity>(
1110
+ const p = GetReadWriteProvider(providers);
1111
+ const request = await p.GetEntityObject<MJAIAgentRequestEntity>(
1111
1112
  'MJ: AI Agent Requests',
1112
1113
  currentUser
1113
1114
  );
@@ -1262,10 +1263,11 @@ export class RunAIAgentResolver extends ResolverBase {
1262
1263
  private async loadConversationHistoryWithAttachments(
1263
1264
  conversationId: string,
1264
1265
  contextUser: UserInfo,
1265
- maxMessages: number
1266
+ maxMessages: number,
1267
+ provider: IMetadataProvider
1266
1268
  ): Promise<ChatMessage[]> {
1267
- const rv = new RunView();
1268
- const attachmentService = getAttachmentService();
1269
+ const rv = RunView.FromMetadataProvider(provider);
1270
+ const attachmentService = GetAttachmentService();
1269
1271
 
1270
1272
  // Load recent conversation details (messages) for this conversation.
1271
1273
  // Only fetch the three fields we actually use — ID for attachment lookups,
@@ -1290,9 +1292,12 @@ export class RunAIAgentResolver extends ResolverBase {
1290
1292
  const messageIds = details.map(d => d.ID);
1291
1293
 
1292
1294
  // Batch load all attachments for these messages
1293
- const attachmentsByDetailId = await attachmentService.getAttachmentsBatch(messageIds, contextUser);
1295
+ const attachmentsByDetailId = await attachmentService.GetAttachmentsBatch(messageIds, contextUser, provider);
1296
+
1297
+ // Batch load input artifacts for these messages
1298
+ const inputArtifactsByDetailId = await this.loadInputArtifactsBatch(messageIds, contextUser, provider);
1294
1299
 
1295
- // Build ChatMessage array with attachments
1300
+ // Build ChatMessage array with attachments and input artifacts
1296
1301
  const messages: ChatMessage[] = [];
1297
1302
 
1298
1303
  for (const detail of details) {
@@ -1301,7 +1306,7 @@ export class RunAIAgentResolver extends ResolverBase {
1301
1306
 
1302
1307
  // Get attachment data with content URLs (handles both inline and FileID storage)
1303
1308
  const attachmentDataPromises = attachments.map(att =>
1304
- attachmentService.getAttachmentData(att, contextUser)
1309
+ attachmentService.GetAttachmentData(att, contextUser, provider)
1305
1310
  );
1306
1311
  const attachmentDataResults = await Promise.all(attachmentDataPromises);
1307
1312
 
@@ -1319,12 +1324,38 @@ export class RunAIAgentResolver extends ResolverBase {
1319
1324
  content: result.contentUrl
1320
1325
  }));
1321
1326
 
1327
+ // Get input artifacts for this message and convert to AttachmentData
1328
+ const inputArtifacts = inputArtifactsByDetailId.get(detail.ID) || [];
1329
+ for (const artifactVersion of inputArtifacts) {
1330
+ if (artifactVersion.ContentMode === 'File' && artifactVersion.FileID) {
1331
+ // File-backed artifact — download content and treat like a document attachment
1332
+ const fileContent = await this.downloadArtifactFileContent(artifactVersion, contextUser, provider);
1333
+ if (fileContent) {
1334
+ validAttachments.push({
1335
+ type: ConversationUtility.GetAttachmentTypeFromMime(artifactVersion.MimeType || ''),
1336
+ mimeType: artifactVersion.MimeType || 'application/octet-stream',
1337
+ fileName: artifactVersion.FileName || artifactVersion.Name || undefined,
1338
+ sizeBytes: artifactVersion.ContentSizeBytes || undefined,
1339
+ content: fileContent
1340
+ });
1341
+ }
1342
+ } else if (artifactVersion.Content) {
1343
+ // Text artifact — include content directly as a text attachment
1344
+ validAttachments.push({
1345
+ type: 'Document' as AttachmentData['type'],
1346
+ mimeType: 'text/plain',
1347
+ fileName: artifactVersion.Name || 'artifact.txt',
1348
+ content: `[Artifact: ${artifactVersion.Name || 'Untitled'}]\n\n${artifactVersion.Content}`
1349
+ });
1350
+ }
1351
+ }
1352
+
1322
1353
  // Build message content (with or without attachments)
1323
- let content: string | ReturnType<typeof ConversationUtility.BuildChatMessageContent>;
1354
+ let content: ChatMessageContent;
1324
1355
 
1325
1356
  if (validAttachments.length > 0) {
1326
1357
  // Use ConversationUtility to build multimodal content blocks
1327
- content = ConversationUtility.BuildChatMessageContent(
1358
+ content = await ConversationUtility.BuildChatMessageContent(
1328
1359
  detail.Message || '',
1329
1360
  validAttachments
1330
1361
  );
@@ -1352,4 +1383,87 @@ export class RunAIAgentResolver extends ResolverBase {
1352
1383
  return 'user'; // Default to user
1353
1384
  }
1354
1385
 
1386
+ /**
1387
+ * Batch load input artifact versions for conversation details.
1388
+ * Returns a map of ConversationDetailID -> ArtifactVersion[]
1389
+ */
1390
+ private async loadInputArtifactsBatch(
1391
+ conversationDetailIds: string[],
1392
+ contextUser: UserInfo,
1393
+ provider: IMetadataProvider
1394
+ ): Promise<Map<string, MJArtifactVersionEntity[]>> {
1395
+ const map = new Map<string, MJArtifactVersionEntity[]>();
1396
+ if (conversationDetailIds.length === 0) return map;
1397
+
1398
+ const rv = RunView.FromMetadataProvider(provider);
1399
+ const idList = conversationDetailIds.map(id => `'${id}'`).join(',');
1400
+
1401
+ // Load ConversationDetailArtifact links with Direction='Input'
1402
+ const linksResult = await rv.RunView<MJConversationDetailArtifactEntity>({
1403
+ EntityName: 'MJ: Conversation Detail Artifacts',
1404
+ ExtraFilter: `ConversationDetailID IN (${idList}) AND Direction = 'Input'`,
1405
+ ResultType: 'entity_object'
1406
+ }, contextUser);
1407
+
1408
+ if (!linksResult.Success || !linksResult.Results || linksResult.Results.length === 0) {
1409
+ return map;
1410
+ }
1411
+
1412
+ // Load the referenced artifact versions
1413
+ const versionIds = linksResult.Results.map(l => `'${l.ArtifactVersionID}'`).join(',');
1414
+ const versionsResult = await rv.RunView<MJArtifactVersionEntity>({
1415
+ EntityName: 'MJ: Artifact Versions',
1416
+ ExtraFilter: `ID IN (${versionIds})`,
1417
+ ResultType: 'entity_object'
1418
+ }, contextUser);
1419
+
1420
+ if (!versionsResult.Success || !versionsResult.Results) return map;
1421
+
1422
+ // Build a lookup of version ID -> version entity
1423
+ const versionMap = new Map<string, MJArtifactVersionEntity>();
1424
+ for (const v of versionsResult.Results) {
1425
+ versionMap.set(v.ID, v);
1426
+ }
1427
+
1428
+ // Group by conversation detail ID
1429
+ for (const link of linksResult.Results) {
1430
+ const version = versionMap.get(link.ArtifactVersionID);
1431
+ if (version) {
1432
+ const existing = map.get(link.ConversationDetailID) || [];
1433
+ existing.push(version);
1434
+ map.set(link.ConversationDetailID, existing);
1435
+ }
1436
+ }
1437
+
1438
+ return map;
1439
+ }
1440
+
1441
+ /**
1442
+ * Download file content from an artifact version's FileID.
1443
+ * Returns base64 data URL for extraction pipeline compatibility.
1444
+ * Uses the same downloadFileContent path as ConversationAttachmentService
1445
+ * to avoid Box driver path resolution issues.
1446
+ */
1447
+ private async downloadArtifactFileContent(
1448
+ artifactVersion: MJArtifactVersionEntity,
1449
+ contextUser: UserInfo,
1450
+ provider: IMetadataProvider
1451
+ ): Promise<string | null> {
1452
+ if (!artifactVersion.FileID) return null;
1453
+
1454
+ try {
1455
+ // Use the attachment service's downloadFileContent which uses GetObject directly
1456
+ const attachmentService = GetAttachmentService();
1457
+ const buffer = await attachmentService.DownloadFileContent(artifactVersion.FileID, contextUser, provider);
1458
+ if (!buffer) return null;
1459
+
1460
+ const base64 = buffer.toString('base64');
1461
+ const mimeType = artifactVersion.MimeType || 'application/octet-stream';
1462
+ return `data:${mimeType};base64,${base64}`;
1463
+ } catch (err) {
1464
+ LogError(`Failed to download artifact file ${artifactVersion.FileID}: ${err}`);
1465
+ return null;
1466
+ }
1467
+ }
1468
+
1355
1469
  }