@memberjunction/server 2.94.0 → 2.96.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 (97) hide show
  1. package/dist/config.d.ts +5 -1
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +2 -1
  4. package/dist/config.js.map +1 -1
  5. package/dist/context.d.ts.map +1 -1
  6. package/dist/context.js +4 -0
  7. package/dist/context.js.map +1 -1
  8. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  9. package/dist/resolvers/ActionResolver.js +5 -4
  10. package/dist/resolvers/ActionResolver.js.map +1 -1
  11. package/dist/resolvers/AskSkipResolver.d.ts +8 -3
  12. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  13. package/dist/resolvers/AskSkipResolver.js +241 -25
  14. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  15. package/dist/resolvers/CreateQueryResolver.d.ts +2 -2
  16. package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
  17. package/dist/resolvers/CreateQueryResolver.js +25 -17
  18. package/dist/resolvers/CreateQueryResolver.js.map +1 -1
  19. package/dist/resolvers/DatasetResolver.d.ts +2 -2
  20. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  21. package/dist/resolvers/DatasetResolver.js +6 -5
  22. package/dist/resolvers/DatasetResolver.js.map +1 -1
  23. package/dist/resolvers/EntityRecordNameResolver.d.ts +4 -4
  24. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  25. package/dist/resolvers/EntityRecordNameResolver.js +6 -5
  26. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  27. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  28. package/dist/resolvers/FileCategoryResolver.js +10 -11
  29. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  30. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  31. package/dist/resolvers/FileResolver.js +5 -6
  32. package/dist/resolvers/FileResolver.js.map +1 -1
  33. package/dist/resolvers/GetDataContextDataResolver.js +2 -2
  34. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  35. package/dist/resolvers/GetDataResolver.js +3 -3
  36. package/dist/resolvers/GetDataResolver.js.map +1 -1
  37. package/dist/resolvers/MergeRecordsResolver.d.ts +3 -3
  38. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  39. package/dist/resolvers/MergeRecordsResolver.js +8 -7
  40. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  41. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +1 -1
  42. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +1 -1
  43. package/dist/resolvers/PotentialDuplicateRecordResolver.js +4 -3
  44. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
  45. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  46. package/dist/resolvers/QueryResolver.js +19 -14
  47. package/dist/resolvers/QueryResolver.js.map +1 -1
  48. package/dist/resolvers/ReportResolver.d.ts +2 -2
  49. package/dist/resolvers/ReportResolver.d.ts.map +1 -1
  50. package/dist/resolvers/ReportResolver.js +8 -6
  51. package/dist/resolvers/ReportResolver.js.map +1 -1
  52. package/dist/resolvers/RunAIAgentResolver.d.ts +3 -7
  53. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  54. package/dist/resolvers/RunAIAgentResolver.js +8 -5
  55. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  56. package/dist/resolvers/RunAIPromptResolver.d.ts +3 -7
  57. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  58. package/dist/resolvers/RunAIPromptResolver.js +10 -8
  59. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  60. package/dist/resolvers/RunTemplateResolver.d.ts +2 -4
  61. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  62. package/dist/resolvers/RunTemplateResolver.js +5 -4
  63. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  64. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  65. package/dist/resolvers/SqlLoggingConfigResolver.js +7 -7
  66. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  67. package/dist/resolvers/UserFavoriteResolver.d.ts +3 -4
  68. package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
  69. package/dist/resolvers/UserFavoriteResolver.js +10 -68
  70. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  71. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  72. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  73. package/dist/resolvers/UserViewResolver.js +3 -4
  74. package/dist/resolvers/UserViewResolver.js.map +1 -1
  75. package/package.json +39 -39
  76. package/src/config.ts +2 -0
  77. package/src/context.ts +5 -0
  78. package/src/resolvers/ActionResolver.ts +5 -4
  79. package/src/resolvers/AskSkipResolver.ts +300 -29
  80. package/src/resolvers/CreateQueryResolver.ts +28 -17
  81. package/src/resolvers/DatasetResolver.ts +5 -4
  82. package/src/resolvers/EntityRecordNameResolver.ts +8 -6
  83. package/src/resolvers/FileCategoryResolver.ts +9 -10
  84. package/src/resolvers/FileResolver.ts +6 -7
  85. package/src/resolvers/GetDataContextDataResolver.ts +2 -2
  86. package/src/resolvers/GetDataResolver.ts +2 -2
  87. package/src/resolvers/InfoResolver.ts +1 -1
  88. package/src/resolvers/MergeRecordsResolver.ts +7 -6
  89. package/src/resolvers/PotentialDuplicateRecordResolver.ts +3 -2
  90. package/src/resolvers/QueryResolver.ts +22 -15
  91. package/src/resolvers/ReportResolver.ts +9 -6
  92. package/src/resolvers/RunAIAgentResolver.ts +12 -4
  93. package/src/resolvers/RunAIPromptResolver.ts +12 -8
  94. package/src/resolvers/RunTemplateResolver.ts +5 -5
  95. package/src/resolvers/SqlLoggingConfigResolver.ts +7 -6
  96. package/src/resolvers/UserFavoriteResolver.ts +8 -67
  97. package/src/resolvers/UserViewResolver.ts +3 -3
@@ -22,15 +22,118 @@ import { LoadDataContextItemsServer } from '@memberjunction/data-context-server'
22
22
  import { LearningCycleScheduler } from '../scheduler/LearningCycleScheduler.js';
23
23
  LoadDataContextItemsServer();
24
24
  import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
25
- import { apiKey, baseUrl, configInfo, graphqlPort } from '../config.js';
25
+ import { apiKey, baseUrl, publicUrl, configInfo, graphqlPort, graphqlRootPath } from '../config.js';
26
26
  import mssql from 'mssql';
27
27
  import { registerEnumType } from 'type-graphql';
28
28
  import { MJGlobal, CopyScalarsAndArrays } from '@memberjunction/global';
29
- import { sendPostRequest } from '../util.js';
29
+ import { GetReadWriteProvider, sendPostRequest } from '../util.js';
30
30
  import { GetAIAPIKey } from '@memberjunction/ai';
31
31
  import { CompositeKeyInputType } from '../generic/KeyInputOutputTypes.js';
32
32
  import { AIEngine } from '@memberjunction/aiengine';
33
33
  import { deleteAccessToken, registerAccessToken, tokenExists } from './GetDataResolver.js';
34
+ class ActiveConversationStreams {
35
+ static instance;
36
+ streams = new Map();
37
+ constructor() { }
38
+ static getInstance() {
39
+ if (!ActiveConversationStreams.instance) {
40
+ ActiveConversationStreams.instance = new ActiveConversationStreams();
41
+ }
42
+ return ActiveConversationStreams.instance;
43
+ }
44
+ updateStatus(conversationId, status, sessionId) {
45
+ const existing = this.streams.get(conversationId);
46
+ if (existing) {
47
+ existing.lastStatus = status;
48
+ existing.lastUpdate = new Date();
49
+ if (sessionId) {
50
+ existing.sessionIds.add(sessionId);
51
+ }
52
+ }
53
+ else {
54
+ const now = new Date();
55
+ this.streams.set(conversationId, {
56
+ lastStatus: status,
57
+ lastUpdate: now,
58
+ startTime: now,
59
+ sessionIds: sessionId ? new Set([sessionId]) : new Set()
60
+ });
61
+ }
62
+ }
63
+ getStatus(conversationId) {
64
+ const stream = this.streams.get(conversationId);
65
+ return stream ? stream.lastStatus : null;
66
+ }
67
+ getStartTime(conversationId) {
68
+ const stream = this.streams.get(conversationId);
69
+ return stream ? stream.startTime : null;
70
+ }
71
+ addSession(conversationId, sessionId) {
72
+ const stream = this.streams.get(conversationId);
73
+ if (stream) {
74
+ stream.sessionIds.add(sessionId);
75
+ }
76
+ else {
77
+ const now = new Date();
78
+ this.streams.set(conversationId, {
79
+ lastStatus: 'Processing...',
80
+ lastUpdate: now,
81
+ startTime: now,
82
+ sessionIds: new Set([sessionId])
83
+ });
84
+ }
85
+ }
86
+ removeConversation(conversationId) {
87
+ this.streams.delete(conversationId);
88
+ }
89
+ isActive(conversationId) {
90
+ const stream = this.streams.get(conversationId);
91
+ if (!stream)
92
+ return false;
93
+ const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
94
+ return stream.lastUpdate > fiveMinutesAgo;
95
+ }
96
+ getSessionIds(conversationId) {
97
+ const stream = this.streams.get(conversationId);
98
+ return stream ? Array.from(stream.sessionIds) : [];
99
+ }
100
+ cleanupStaleStreams() {
101
+ const now = new Date();
102
+ const staleThreshold = new Date(now.getTime() - 30 * 60 * 1000);
103
+ const staleConversations = [];
104
+ this.streams.forEach((stream, conversationId) => {
105
+ if (stream.lastUpdate < staleThreshold) {
106
+ staleConversations.push(conversationId);
107
+ }
108
+ });
109
+ staleConversations.forEach(conversationId => {
110
+ this.streams.delete(conversationId);
111
+ LogStatus(`Cleaned up stale stream for conversation ${conversationId}`);
112
+ });
113
+ if (staleConversations.length > 0) {
114
+ LogStatus(`Cleaned up ${staleConversations.length} stale conversation streams`);
115
+ }
116
+ }
117
+ }
118
+ const activeStreams = ActiveConversationStreams.getInstance();
119
+ setInterval(() => {
120
+ activeStreams.cleanupStaleStreams();
121
+ }, 10 * 60 * 1000);
122
+ let ReattachConversationResponse = class ReattachConversationResponse {
123
+ lastStatusMessage;
124
+ startTime;
125
+ };
126
+ __decorate([
127
+ Field(() => String, { nullable: true }),
128
+ __metadata("design:type", String)
129
+ ], ReattachConversationResponse.prototype, "lastStatusMessage", void 0);
130
+ __decorate([
131
+ Field(() => Date, { nullable: true }),
132
+ __metadata("design:type", Date)
133
+ ], ReattachConversationResponse.prototype, "startTime", void 0);
134
+ ReattachConversationResponse = __decorate([
135
+ ObjectType()
136
+ ], ReattachConversationResponse);
34
137
  var SkipResponsePhase;
35
138
  (function (SkipResponsePhase) {
36
139
  SkipResponsePhase["ClarifyingQuestion"] = "clarifying_question";
@@ -247,7 +350,7 @@ let AskSkipResolver = class AskSkipResolver {
247
350
  static { AskSkipResolver_1 = this; }
248
351
  static _defaultNewChatName = 'New Chat';
249
352
  static _maxHistoricalMessages = 30;
250
- async ExecuteAskSkipRecordChat(UserQuestion, ConversationId, EntityName, compositeKey, { dataSource, userPayload }, pubSub) {
353
+ async ExecuteAskSkipRecordChat(UserQuestion, ConversationId, EntityName, compositeKey, { dataSource, userPayload, providers }, pubSub) {
251
354
  const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
252
355
  if (!user)
253
356
  throw new Error(`User ${userPayload.email} not found in UserCache`);
@@ -255,7 +358,7 @@ let AskSkipResolver = class AskSkipResolver {
255
358
  if (ConversationId && ConversationId.length > 0) {
256
359
  messages = await this.LoadConversationDetailsIntoSkipMessages(dataSource, ConversationId, AskSkipResolver_1._maxHistoricalMessages);
257
360
  }
258
- const md = new Metadata();
361
+ const md = GetReadWriteProvider(providers);
259
362
  const { convoEntity, dataContextEntity, convoDetailEntity, dataContext } = await this.HandleSkipChatInitialObjectLoading(dataSource, ConversationId, UserQuestion, user, userPayload, md, null);
260
363
  if (!ConversationId || ConversationId.length === 0) {
261
364
  const dci = await md.GetEntityObject('Data Context Items', user);
@@ -287,7 +390,7 @@ let AskSkipResolver = class AskSkipResolver {
287
390
  });
288
391
  return this.handleSimpleSkipChatPostRequest(input, convoEntity, convoDetailEntity, true, user, userPayload);
289
392
  }
290
- async ExecuteAskSkipLearningCycle({ dataSource, userPayload }, ForceEntityRefresh) {
393
+ async ExecuteAskSkipLearningCycle({ dataSource, userPayload, providers }, ForceEntityRefresh) {
291
394
  const skipConfigInfo = configInfo.askSkip;
292
395
  if (!skipConfigInfo.learningCycleEnabled) {
293
396
  return {
@@ -328,7 +431,7 @@ let AskSkipResolver = class AskSkipResolver {
328
431
  requestChanges: []
329
432
  };
330
433
  }
331
- const md = new Metadata();
434
+ const md = GetReadWriteProvider(providers);
332
435
  const skipAgent = AIEngine.Instance.GetAgentByName('Skip');
333
436
  if (!skipAgent) {
334
437
  throw new Error("Skip agent not found in AIEngine");
@@ -587,7 +690,7 @@ cycle.`);
587
690
  organizationID: skipConfigInfo.orgID,
588
691
  organizationInfo: configInfo?.askSkip?.organizationInfo,
589
692
  apiKeys: this.buildSkipAPIKeys(),
590
- callingServerURL: accessToken ? `${baseUrl}:${graphqlPort}` : undefined,
693
+ callingServerURL: accessToken ? (publicUrl || `${baseUrl}:${graphqlPort}${graphqlRootPath}`) : undefined,
591
694
  callingServerAPIKey: accessToken ? apiKey : undefined,
592
695
  callingServerAccessToken: accessToken ? accessToken.Token : undefined
593
696
  };
@@ -812,14 +915,88 @@ cycle.`);
812
915
  },
813
916
  ];
814
917
  }
815
- async ExecuteAskSkipAnalysisQuery(UserQuestion, ConversationId, { dataSource, userPayload }, pubSub, DataContextId, ForceEntityRefresh, StartTime) {
816
- const md = new Metadata();
918
+ async ReattachToProcessingConversation(ConversationId, { userPayload, providers }, pubSub) {
919
+ try {
920
+ const md = GetReadWriteProvider(providers);
921
+ const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
922
+ if (!user) {
923
+ LogError(`User ${userPayload.email} not found in UserCache`);
924
+ return null;
925
+ }
926
+ const convoEntity = await md.GetEntityObject('Conversations', user);
927
+ const loadResult = await convoEntity.Load(ConversationId);
928
+ if (!loadResult) {
929
+ LogError(`Could not load conversation ${ConversationId} for re-attachment`);
930
+ return null;
931
+ }
932
+ if (convoEntity.UserID !== user.ID) {
933
+ LogError(`Conversation ${ConversationId} does not belong to user ${user.Email}`);
934
+ return null;
935
+ }
936
+ if (convoEntity.Status === 'Processing') {
937
+ activeStreams.addSession(ConversationId, userPayload.sessionId);
938
+ const lastStatusMessage = activeStreams.getStatus(ConversationId) || 'Processing...';
939
+ const startTime = activeStreams.getStartTime(ConversationId);
940
+ const isStreamActive = activeStreams.isActive(ConversationId);
941
+ if (isStreamActive) {
942
+ const statusMessage = {
943
+ type: 'AskSkip',
944
+ status: 'OK',
945
+ ResponsePhase: 'Processing',
946
+ conversationID: convoEntity.ID,
947
+ message: lastStatusMessage,
948
+ };
949
+ pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
950
+ pushStatusUpdates: {
951
+ message: JSON.stringify(statusMessage),
952
+ sessionId: userPayload.sessionId
953
+ }
954
+ });
955
+ LogStatus(`Re-attached session ${userPayload.sessionId} to active stream for conversation ${ConversationId}, last status: ${lastStatusMessage}`);
956
+ return {
957
+ lastStatusMessage,
958
+ startTime: startTime || convoEntity.__mj_UpdatedAt
959
+ };
960
+ }
961
+ else {
962
+ const statusMessage = {
963
+ type: 'AskSkip',
964
+ status: 'OK',
965
+ ResponsePhase: 'Processing',
966
+ conversationID: convoEntity.ID,
967
+ message: 'Processing...',
968
+ };
969
+ pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
970
+ pushStatusUpdates: {
971
+ message: JSON.stringify(statusMessage),
972
+ sessionId: userPayload.sessionId
973
+ }
974
+ });
975
+ LogStatus(`Re-attached session ${userPayload.sessionId} to conversation ${ConversationId}, but stream is inactive`);
976
+ return {
977
+ lastStatusMessage: 'Processing...',
978
+ startTime: convoEntity.__mj_UpdatedAt
979
+ };
980
+ }
981
+ }
982
+ else {
983
+ LogStatus(`Conversation ${ConversationId} is not processing (Status: ${convoEntity.Status})`);
984
+ return null;
985
+ }
986
+ }
987
+ catch (error) {
988
+ LogError(`Error re-attaching to conversation: ${error}`);
989
+ return null;
990
+ }
991
+ }
992
+ async ExecuteAskSkipAnalysisQuery(UserQuestion, ConversationId, { dataSource, userPayload, providers }, pubSub, DataContextId, ForceEntityRefresh, StartTime) {
993
+ const md = GetReadWriteProvider(providers);
817
994
  const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
818
995
  if (!user)
819
996
  throw new Error(`User ${userPayload.email} not found in UserCache`);
820
997
  const requestStartTime = StartTime || new Date();
821
998
  const { convoEntity, dataContextEntity, convoDetailEntity, dataContext } = await this.HandleSkipChatInitialObjectLoading(dataSource, ConversationId, UserQuestion, user, userPayload, md, DataContextId);
822
- await this.setConversationStatus(convoEntity, 'Processing', userPayload);
999
+ await this.setConversationStatus(convoEntity, 'Processing', userPayload, pubSub);
823
1000
  const messages = await this.LoadConversationDetailsIntoSkipMessages(dataSource, convoEntity.ID, AskSkipResolver_1._maxHistoricalMessages);
824
1001
  const conversationDetailCount = 1;
825
1002
  const input = await this.buildSkipChatAPIRequest(messages, ConversationId, dataContext, 'initial_request', true, true, true, false, user, dataSource, ForceEntityRefresh === undefined ? false : ForceEntityRefresh, true);
@@ -1375,7 +1552,7 @@ cycle.`);
1375
1552
  const skipConfigInfo = configInfo.askSkip;
1376
1553
  LogStatus(` >>> HandleSkipRequest: Sending request to Skip API: ${skipConfigInfo.chatURL}`);
1377
1554
  if (conversationDetailCount > 10) {
1378
- await this.setConversationStatus(convoEntity, 'Available', userPayload);
1555
+ await this.setConversationStatus(convoEntity, 'Available', userPayload, pubSub);
1379
1556
  pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
1380
1557
  message: JSON.stringify({
1381
1558
  type: 'AskSkip',
@@ -1400,21 +1577,26 @@ cycle.`);
1400
1577
  response = await sendPostRequest(skipConfigInfo.chatURL, input, true, null, (message) => {
1401
1578
  LogStatus(JSON.stringify(message, null, 4));
1402
1579
  if (message.type === 'status_update') {
1403
- pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
1404
- message: JSON.stringify({
1405
- type: 'AskSkip',
1406
- status: 'OK',
1407
- conversationID: ConversationId,
1408
- ResponsePhase: message.value.responsePhase,
1409
- message: message.value.messages[0].content,
1410
- }),
1411
- sessionId: userPayload.sessionId,
1412
- });
1580
+ const statusContent = message.value.messages[0].content;
1581
+ activeStreams.updateStatus(ConversationId, statusContent, userPayload.sessionId);
1582
+ const sessionIds = activeStreams.getSessionIds(ConversationId);
1583
+ for (const sessionId of sessionIds) {
1584
+ pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
1585
+ message: JSON.stringify({
1586
+ type: 'AskSkip',
1587
+ status: 'OK',
1588
+ conversationID: ConversationId,
1589
+ ResponsePhase: message.value.responsePhase,
1590
+ message: statusContent,
1591
+ }),
1592
+ sessionId: sessionId,
1593
+ });
1594
+ }
1413
1595
  }
1414
1596
  });
1415
1597
  }
1416
1598
  catch (error) {
1417
- await this.setConversationStatus(convoEntity, 'Available', userPayload);
1599
+ await this.setConversationStatus(convoEntity, 'Available', userPayload, pubSub);
1418
1600
  LogError(`Error in HandleSkipChatRequest sendPostRequest: ${error}`);
1419
1601
  pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
1420
1602
  message: JSON.stringify({
@@ -1445,7 +1627,7 @@ cycle.`);
1445
1627
  }
1446
1628
  }
1447
1629
  else {
1448
- await this.setConversationStatus(convoEntity, 'Available', userPayload);
1630
+ await this.setConversationStatus(convoEntity, 'Available', userPayload, pubSub);
1449
1631
  pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
1450
1632
  message: JSON.stringify({
1451
1633
  type: 'AskSkip',
@@ -1515,7 +1697,7 @@ cycle.`);
1515
1697
  convoDetailEntityAI.Role = 'AI';
1516
1698
  convoDetailEntityAI.HiddenToUser = false;
1517
1699
  convoDetailEntityAI.CompletionTime = endTime.getTime() - startTime.getTime();
1518
- await this.setConversationStatus(convoEntity, 'Available', userPayload);
1700
+ await this.setConversationStatus(convoEntity, 'Available', userPayload, pubSub);
1519
1701
  if (await convoDetailEntityAI.Save()) {
1520
1702
  return {
1521
1703
  Success: true,
@@ -1782,13 +1964,38 @@ cycle.`);
1782
1964
  AIMessageConversationDetailID: convoDetailEntityAI.ID,
1783
1965
  };
1784
1966
  }
1785
- async setConversationStatus(convoEntity, status, userPayload) {
1967
+ async setConversationStatus(convoEntity, status, userPayload, pubSub) {
1786
1968
  if (convoEntity.Status !== status) {
1787
1969
  convoEntity.Status = status;
1788
1970
  const convoSaveResult = await convoEntity.Save();
1789
1971
  if (!convoSaveResult) {
1790
1972
  LogError(`Error updating conversation status to '${status}'`, undefined, convoEntity.LatestResult);
1791
1973
  }
1974
+ else {
1975
+ if (status === 'Available') {
1976
+ activeStreams.removeConversation(convoEntity.ID);
1977
+ LogStatus(`Removed conversation ${convoEntity.ID} from active streams (status changed to Available)`);
1978
+ }
1979
+ else if (status === 'Processing') {
1980
+ activeStreams.addSession(convoEntity.ID, userPayload.sessionId);
1981
+ LogStatus(`Added session ${userPayload.sessionId} to active streams for conversation ${convoEntity.ID}`);
1982
+ }
1983
+ if (pubSub) {
1984
+ const statusMessage = {
1985
+ type: 'ConversationStatusUpdate',
1986
+ conversationID: convoEntity.ID,
1987
+ status: status,
1988
+ timestamp: new Date().toISOString()
1989
+ };
1990
+ pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
1991
+ pushStatusUpdates: {
1992
+ message: JSON.stringify(statusMessage),
1993
+ sessionId: userPayload.sessionId
1994
+ }
1995
+ });
1996
+ LogStatus(`Published conversation status update for ${convoEntity.ID}: ${status}`);
1997
+ }
1998
+ }
1792
1999
  return convoSaveResult;
1793
2000
  }
1794
2001
  return true;
@@ -1943,6 +2150,15 @@ __decorate([
1943
2150
  __metadata("design:paramtypes", [Object, PubSubEngine, String, String]),
1944
2151
  __metadata("design:returntype", Promise)
1945
2152
  ], AskSkipResolver.prototype, "ExecuteAskSkipRunScript", null);
2153
+ __decorate([
2154
+ Query(() => ReattachConversationResponse),
2155
+ __param(0, Arg('ConversationId', () => String)),
2156
+ __param(1, Ctx()),
2157
+ __param(2, PubSub()),
2158
+ __metadata("design:type", Function),
2159
+ __metadata("design:paramtypes", [String, Object, PubSubEngine]),
2160
+ __metadata("design:returntype", Promise)
2161
+ ], AskSkipResolver.prototype, "ReattachToProcessingConversation", null);
1946
2162
  __decorate([
1947
2163
  Query(() => AskSkipResultType),
1948
2164
  __param(0, Arg('UserQuestion', () => String)),