@memberjunction/server 5.44.0 → 5.45.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 (254) hide show
  1. package/dist/agentSessions/ReturningVisitorRecap.d.ts +39 -0
  2. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -0
  3. package/dist/agentSessions/ReturningVisitorRecap.js +198 -0
  4. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -0
  5. package/dist/agentSessions/SessionJanitor.d.ts +13 -0
  6. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  7. package/dist/agentSessions/SessionJanitor.js +65 -7
  8. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  9. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  10. package/dist/agentSessions/SessionManager.js +37 -0
  11. package/dist/agentSessions/SessionManager.js.map +1 -1
  12. package/dist/auth/magicLink/MagicLinkRouter.d.ts +10 -0
  13. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  14. package/dist/auth/magicLink/MagicLinkRouter.js +16 -0
  15. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  16. package/dist/auth/magicLink/index.d.ts +1 -1
  17. package/dist/auth/magicLink/index.d.ts.map +1 -1
  18. package/dist/auth/magicLink/index.js +1 -1
  19. package/dist/auth/magicLink/index.js.map +1 -1
  20. package/dist/auth/magicLink/types.d.ts +26 -0
  21. package/dist/auth/magicLink/types.d.ts.map +1 -1
  22. package/dist/config.d.ts +1883 -144
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +160 -36
  25. package/dist/config.js.map +1 -1
  26. package/dist/context.d.ts.map +1 -1
  27. package/dist/context.js +45 -2
  28. package/dist/context.js.map +1 -1
  29. package/dist/generated/generated.d.ts +522 -0
  30. package/dist/generated/generated.d.ts.map +1 -1
  31. package/dist/generated/generated.js +2860 -0
  32. package/dist/generated/generated.js.map +1 -1
  33. package/dist/generic/ResolverBase.d.ts +13 -0
  34. package/dist/generic/ResolverBase.d.ts.map +1 -1
  35. package/dist/generic/ResolverBase.js +22 -0
  36. package/dist/generic/ResolverBase.js.map +1 -1
  37. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  38. package/dist/generic/RunViewResolver.js +4 -0
  39. package/dist/generic/RunViewResolver.js.map +1 -1
  40. package/dist/index.d.ts +0 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +101 -3
  43. package/dist/index.js.map +1 -1
  44. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  45. package/dist/integration/CustomColumnPromoter.js +6 -0
  46. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  47. package/dist/realtimeWidget/WidgetRouter.d.ts +26 -0
  48. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -0
  49. package/dist/realtimeWidget/WidgetRouter.js +182 -0
  50. package/dist/realtimeWidget/WidgetRouter.js.map +1 -0
  51. package/dist/realtimeWidget/WidgetSessionService.d.ts +265 -0
  52. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -0
  53. package/dist/realtimeWidget/WidgetSessionService.js +477 -0
  54. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -0
  55. package/dist/realtimeWidget/host-identity.d.ts +40 -0
  56. package/dist/realtimeWidget/host-identity.d.ts.map +1 -0
  57. package/dist/realtimeWidget/host-identity.js +58 -0
  58. package/dist/realtimeWidget/host-identity.js.map +1 -0
  59. package/dist/realtimeWidget/index.d.ts +10 -0
  60. package/dist/realtimeWidget/index.d.ts.map +1 -0
  61. package/dist/realtimeWidget/index.js +9 -0
  62. package/dist/realtimeWidget/index.js.map +1 -0
  63. package/dist/realtimeWidget/visitorIdentity.d.ts +76 -0
  64. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -0
  65. package/dist/realtimeWidget/visitorIdentity.js +202 -0
  66. package/dist/realtimeWidget/visitorIdentity.js.map +1 -0
  67. package/dist/realtimeWidget/widgetCore.d.ts +106 -0
  68. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -0
  69. package/dist/realtimeWidget/widgetCore.js +173 -0
  70. package/dist/realtimeWidget/widgetCore.js.map +1 -0
  71. package/dist/realtimeWidget/widgetGuestElevation.d.ts +51 -0
  72. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -0
  73. package/dist/realtimeWidget/widgetGuestElevation.js +79 -0
  74. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -0
  75. package/dist/resolvers/ComponentRegistryResolver.d.ts +7 -0
  76. package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
  77. package/dist/resolvers/ComponentRegistryResolver.js +31 -8
  78. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  79. package/dist/resolvers/EntityPermissionResolver.d.ts.map +1 -1
  80. package/dist/resolvers/EntityPermissionResolver.js +1 -2
  81. package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
  82. package/dist/resolvers/QuerySystemUserResolver.d.ts +9 -6
  83. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  84. package/dist/resolvers/QuerySystemUserResolver.js +15 -13
  85. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  86. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -0
  87. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  88. package/dist/resolvers/RealtimeClientSessionResolver.js +47 -3
  89. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  90. package/dist/resolvers/RingCentralTelephonyResolver.d.ts +27 -0
  91. package/dist/resolvers/RingCentralTelephonyResolver.d.ts.map +1 -0
  92. package/dist/resolvers/RingCentralTelephonyResolver.js +87 -0
  93. package/dist/resolvers/RingCentralTelephonyResolver.js.map +1 -0
  94. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  95. package/dist/resolvers/RunAIAgentResolver.js +14 -3
  96. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  97. package/dist/resolvers/SearchKnowledgeResolver.d.ts.map +1 -1
  98. package/dist/resolvers/SearchKnowledgeResolver.js +2 -0
  99. package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
  100. package/dist/resolvers/TeamsMeetingsResolver.d.ts +28 -0
  101. package/dist/resolvers/TeamsMeetingsResolver.d.ts.map +1 -0
  102. package/dist/resolvers/TeamsMeetingsResolver.js +91 -0
  103. package/dist/resolvers/TeamsMeetingsResolver.js.map +1 -0
  104. package/dist/resolvers/TelephonyResolver.d.ts +26 -0
  105. package/dist/resolvers/TelephonyResolver.d.ts.map +1 -0
  106. package/dist/resolvers/TelephonyResolver.js +86 -0
  107. package/dist/resolvers/TelephonyResolver.js.map +1 -0
  108. package/dist/resolvers/TestQuerySQLResolver.d.ts +1 -1
  109. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  110. package/dist/resolvers/TestQuerySQLResolver.js +2 -3
  111. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  112. package/dist/resolvers/VonageTelephonyResolver.d.ts +26 -0
  113. package/dist/resolvers/VonageTelephonyResolver.d.ts.map +1 -0
  114. package/dist/resolvers/VonageTelephonyResolver.js +86 -0
  115. package/dist/resolvers/VonageTelephonyResolver.js.map +1 -0
  116. package/dist/rest/MediaStreamHandler.js +4 -1
  117. package/dist/rest/MediaStreamHandler.js.map +1 -1
  118. package/dist/telephony/RingCentralTelephonyService.d.ts +115 -0
  119. package/dist/telephony/RingCentralTelephonyService.d.ts.map +1 -0
  120. package/dist/telephony/RingCentralTelephonyService.js +262 -0
  121. package/dist/telephony/RingCentralTelephonyService.js.map +1 -0
  122. package/dist/telephony/TeamsMeetingsRouter.d.ts +40 -0
  123. package/dist/telephony/TeamsMeetingsRouter.d.ts.map +1 -0
  124. package/dist/telephony/TeamsMeetingsRouter.js +96 -0
  125. package/dist/telephony/TeamsMeetingsRouter.js.map +1 -0
  126. package/dist/telephony/TeamsMeetingsService.d.ts +113 -0
  127. package/dist/telephony/TeamsMeetingsService.d.ts.map +1 -0
  128. package/dist/telephony/TeamsMeetingsService.js +196 -0
  129. package/dist/telephony/TeamsMeetingsService.js.map +1 -0
  130. package/dist/telephony/TwilioTelephonyRouter.d.ts +36 -0
  131. package/dist/telephony/TwilioTelephonyRouter.d.ts.map +1 -0
  132. package/dist/telephony/TwilioTelephonyRouter.js +143 -0
  133. package/dist/telephony/TwilioTelephonyRouter.js.map +1 -0
  134. package/dist/telephony/TwilioTelephonyService.d.ts +95 -0
  135. package/dist/telephony/TwilioTelephonyService.d.ts.map +1 -0
  136. package/dist/telephony/TwilioTelephonyService.js +193 -0
  137. package/dist/telephony/TwilioTelephonyService.js.map +1 -0
  138. package/dist/telephony/VonageTelephonyRouter.d.ts +41 -0
  139. package/dist/telephony/VonageTelephonyRouter.d.ts.map +1 -0
  140. package/dist/telephony/VonageTelephonyRouter.js +201 -0
  141. package/dist/telephony/VonageTelephonyRouter.js.map +1 -0
  142. package/dist/telephony/VonageTelephonyService.d.ts +90 -0
  143. package/dist/telephony/VonageTelephonyService.d.ts.map +1 -0
  144. package/dist/telephony/VonageTelephonyService.js +191 -0
  145. package/dist/telephony/VonageTelephonyService.js.map +1 -0
  146. package/dist/telephony/calendar-scheduler.d.ts +67 -0
  147. package/dist/telephony/calendar-scheduler.d.ts.map +1 -0
  148. package/dist/telephony/calendar-scheduler.js +133 -0
  149. package/dist/telephony/calendar-scheduler.js.map +1 -0
  150. package/dist/telephony/index.d.ts +29 -0
  151. package/dist/telephony/index.d.ts.map +1 -0
  152. package/dist/telephony/index.js +38 -0
  153. package/dist/telephony/index.js.map +1 -0
  154. package/dist/telephony/media-upgrade-router.d.ts +33 -0
  155. package/dist/telephony/media-upgrade-router.d.ts.map +1 -0
  156. package/dist/telephony/media-upgrade-router.js +56 -0
  157. package/dist/telephony/media-upgrade-router.js.map +1 -0
  158. package/dist/telephony/ringcentral-runtime.d.ts +14 -0
  159. package/dist/telephony/ringcentral-runtime.d.ts.map +1 -0
  160. package/dist/telephony/ringcentral-runtime.js +18 -0
  161. package/dist/telephony/ringcentral-runtime.js.map +1 -0
  162. package/dist/telephony/teams-meetings-runtime.d.ts +16 -0
  163. package/dist/telephony/teams-meetings-runtime.d.ts.map +1 -0
  164. package/dist/telephony/teams-meetings-runtime.js +20 -0
  165. package/dist/telephony/teams-meetings-runtime.js.map +1 -0
  166. package/dist/telephony/teamsAcsMediaRegistry.d.ts +69 -0
  167. package/dist/telephony/teamsAcsMediaRegistry.d.ts.map +1 -0
  168. package/dist/telephony/teamsAcsMediaRegistry.js +118 -0
  169. package/dist/telephony/teamsAcsMediaRegistry.js.map +1 -0
  170. package/dist/telephony/telephony-runtime.d.ts +14 -0
  171. package/dist/telephony/telephony-runtime.d.ts.map +1 -0
  172. package/dist/telephony/telephony-runtime.js +18 -0
  173. package/dist/telephony/telephony-runtime.js.map +1 -0
  174. package/dist/telephony/twilioMediaRegistry.d.ts +60 -0
  175. package/dist/telephony/twilioMediaRegistry.d.ts.map +1 -0
  176. package/dist/telephony/twilioMediaRegistry.js +106 -0
  177. package/dist/telephony/twilioMediaRegistry.js.map +1 -0
  178. package/dist/telephony/vonage-runtime.d.ts +14 -0
  179. package/dist/telephony/vonage-runtime.d.ts.map +1 -0
  180. package/dist/telephony/vonage-runtime.js +18 -0
  181. package/dist/telephony/vonage-runtime.js.map +1 -0
  182. package/dist/telephony/vonageMediaRegistry.d.ts +78 -0
  183. package/dist/telephony/vonageMediaRegistry.d.ts.map +1 -0
  184. package/dist/telephony/vonageMediaRegistry.js +158 -0
  185. package/dist/telephony/vonageMediaRegistry.js.map +1 -0
  186. package/package.json +88 -83
  187. package/src/__tests__/search-knowledge-tags.test.ts +9 -9
  188. package/src/__tests__/teams-meetings-service.test.ts +65 -0
  189. package/src/__tests__/teamsAcsMediaRegistry.test.ts +82 -0
  190. package/src/__tests__/twilio-telephony-service.test.ts +23 -0
  191. package/src/__tests__/twilioMediaRegistry.test.ts +103 -0
  192. package/src/__tests__/vonageMediaRegistry.test.ts +180 -0
  193. package/src/__tests__/widget.test.ts +204 -0
  194. package/src/__tests__/widgetGuestElevation.test.ts +57 -0
  195. package/src/__tests__/widgetHostIdentity.test.ts +117 -0
  196. package/src/agentSessions/ReturningVisitorRecap.ts +242 -0
  197. package/src/agentSessions/SessionJanitor.ts +66 -6
  198. package/src/agentSessions/SessionManager.ts +38 -0
  199. package/src/auth/magicLink/MagicLinkRouter.ts +17 -0
  200. package/src/auth/magicLink/index.ts +1 -0
  201. package/src/auth/magicLink/types.ts +26 -0
  202. package/src/config.ts +172 -38
  203. package/src/context.ts +50 -3
  204. package/src/generated/generated.ts +1991 -1
  205. package/src/generic/ResolverBase.ts +28 -0
  206. package/src/generic/RunViewResolver.ts +4 -0
  207. package/src/index.ts +109 -3
  208. package/src/integration/CustomColumnPromoter.ts +6 -0
  209. package/src/realtimeWidget/WidgetRouter.ts +204 -0
  210. package/src/realtimeWidget/WidgetSessionService.ts +714 -0
  211. package/src/realtimeWidget/host-identity.ts +84 -0
  212. package/src/realtimeWidget/index.ts +15 -0
  213. package/src/realtimeWidget/visitorIdentity.ts +258 -0
  214. package/src/realtimeWidget/widgetCore.ts +231 -0
  215. package/src/realtimeWidget/widgetGuestElevation.ts +115 -0
  216. package/src/resolvers/ComponentRegistryResolver.ts +36 -10
  217. package/src/resolvers/EntityPermissionResolver.ts +1 -2
  218. package/src/resolvers/QuerySystemUserResolver.ts +14 -10
  219. package/src/resolvers/RealtimeClientSessionResolver.ts +59 -2
  220. package/src/resolvers/RingCentralTelephonyResolver.ts +64 -0
  221. package/src/resolvers/RunAIAgentResolver.ts +16 -4
  222. package/src/resolvers/SearchKnowledgeResolver.ts +2 -0
  223. package/src/resolvers/TeamsMeetingsResolver.ts +68 -0
  224. package/src/resolvers/TelephonyResolver.ts +63 -0
  225. package/src/resolvers/TestQuerySQLResolver.ts +2 -3
  226. package/src/resolvers/VonageTelephonyResolver.ts +63 -0
  227. package/src/rest/MediaStreamHandler.ts +4 -1
  228. package/src/telephony/RingCentralTelephonyService.ts +342 -0
  229. package/src/telephony/TeamsMeetingsRouter.ts +124 -0
  230. package/src/telephony/TeamsMeetingsService.ts +268 -0
  231. package/src/telephony/TwilioTelephonyRouter.ts +184 -0
  232. package/src/telephony/TwilioTelephonyService.ts +261 -0
  233. package/src/telephony/VonageTelephonyRouter.ts +239 -0
  234. package/src/telephony/VonageTelephonyService.ts +259 -0
  235. package/src/telephony/calendar-scheduler.ts +176 -0
  236. package/src/telephony/index.ts +43 -0
  237. package/src/telephony/media-upgrade-router.ts +62 -0
  238. package/src/telephony/ringcentral-runtime.ts +22 -0
  239. package/src/telephony/teams-meetings-runtime.ts +24 -0
  240. package/src/telephony/teamsAcsMediaRegistry.ts +152 -0
  241. package/src/telephony/telephony-runtime.ts +22 -0
  242. package/src/telephony/twilioMediaRegistry.ts +137 -0
  243. package/src/telephony/vonage-runtime.ts +22 -0
  244. package/src/telephony/vonageMediaRegistry.ts +200 -0
  245. package/dist/agents/skip-agent.d.ts +0 -111
  246. package/dist/agents/skip-agent.d.ts.map +0 -1
  247. package/dist/agents/skip-agent.js +0 -1487
  248. package/dist/agents/skip-agent.js.map +0 -1
  249. package/dist/agents/skip-sdk.d.ts +0 -261
  250. package/dist/agents/skip-sdk.d.ts.map +0 -1
  251. package/dist/agents/skip-sdk.js +0 -909
  252. package/dist/agents/skip-sdk.js.map +0 -1
  253. package/src/agents/skip-agent.ts +0 -1594
  254. package/src/agents/skip-sdk.ts +0 -1210
@@ -1,909 +0,0 @@
1
- /**
2
- * Skip TypeScript SDK
3
- *
4
- * A general-purpose SDK for calling the Skip SaaS API.
5
- * This module provides a clean, reusable interface for integrating with Skip
6
- * that can eventually be extracted into a standalone npm package.
7
- */
8
- import { SkipResponsePhase } from '@memberjunction/skip-types';
9
- import { LogStatus, LogError, Metadata, RunQuery, RunView, EntityInfo, EntityFieldValueInfo } from '@memberjunction/core';
10
- import { QueryEngine } from '@memberjunction/core-entities';
11
- import { request as httpRequest } from 'http';
12
- import { request as httpsRequest } from 'https';
13
- import { gzip as gzipCompress, createGunzip } from 'zlib';
14
- import { configInfo, baseUrl, publicUrl, graphqlPort, graphqlRootPath, apiKey as callbackAPIKey } from '../config.js';
15
- import { getDbType } from '../index.js';
16
- import { GetAIAPIKey } from '@memberjunction/ai';
17
- import { AIEngine } from '@memberjunction/aiengine';
18
- import { CopyScalarsAndArrays, UUIDsEqual } from '@memberjunction/global';
19
- import { registerAccessToken } from '../resolvers/GetDataResolver.js';
20
- import { BehaviorSubject } from 'rxjs';
21
- import { take } from 'rxjs/operators';
22
- /**
23
- * Skip TypeScript SDK
24
- * Provides a clean interface for calling the Skip SaaS API
25
- */
26
- export class SkipSDK {
27
- /**
28
- * The metadata provider this SDK instance is bound to. Set via the constructor
29
- * config or the `Provider` setter. Falls back to the global `Metadata.Provider`
30
- * when not set — multi-tenant servers should always supply an explicit provider
31
- * so each request reaches its own database connection.
32
- */
33
- get Provider() {
34
- return this.config.provider ?? new Metadata();
35
- }
36
- set Provider(value) {
37
- this.config.provider = value ?? undefined;
38
- }
39
- // Static cache for Skip entities (shared across all instances)
40
- static { this.__skipEntitiesCache$ = new BehaviorSubject(null); }
41
- static { this.__lastRefreshTime = 0; }
42
- constructor(config) {
43
- // Use provided config or fall back to MJ server config
44
- this.config = {
45
- apiUrl: config?.apiUrl || configInfo.askSkip?.chatURL,
46
- apiKey: config?.apiKey || configInfo.askSkip?.apiKey,
47
- organizationId: config?.organizationId || configInfo.askSkip?.orgID,
48
- organizationInfo: config?.organizationInfo || configInfo.askSkip?.organizationInfo
49
- };
50
- }
51
- /**
52
- * Call the Skip chat API
53
- */
54
- async chat(options) {
55
- LogStatus(`[SkipSDK] Sending request to Skip API: ${this.config.apiUrl}`);
56
- try {
57
- // Build the Skip API request
58
- const skipRequest = await this.buildSkipRequest(options);
59
- // Call Skip API with SSE streaming support
60
- const responses = await this.sendSSERequest(this.config.apiUrl, skipRequest, this.buildHeaders(), (streamMessage) => {
61
- // Handle streaming status updates
62
- // Queue messages come as flat objects: {responsePhase: 'queued'|'error', message: '...', error: '...'}
63
- // Skip API messages come wrapped: {type: 'status_update', value: {responsePhase: '...', messages: [...]}}
64
- if (streamMessage.type === 'status_update' && options.onStatusUpdate) {
65
- const statusContent = streamMessage.value?.messages?.[0]?.content;
66
- const responsePhase = streamMessage.value?.responsePhase;
67
- if (statusContent) {
68
- options.onStatusUpdate(statusContent, responsePhase);
69
- }
70
- }
71
- else if (streamMessage.responsePhase === SkipResponsePhase.queued && options.onStatusUpdate) {
72
- // Handle queue progress messages
73
- const statusContent = streamMessage.message;
74
- const responsePhase = streamMessage.responsePhase;
75
- if (statusContent) {
76
- options.onStatusUpdate(statusContent, responsePhase);
77
- }
78
- }
79
- else if (streamMessage.responsePhase === 'error') {
80
- // Queue error messages - log but don't throw (final response will handle error)
81
- // Note: 'error' is not in SkipResponsePhase enum - it's a queue-specific error state
82
- LogError(`[SkipSDK] Queue error: ${streamMessage.error || 'Unknown error'}`);
83
- if (options.onStatusUpdate) {
84
- options.onStatusUpdate(`Error: ${streamMessage.error || 'Request failed'}`, 'error');
85
- }
86
- }
87
- });
88
- // The last response is the final one
89
- if (responses && responses.length > 0) {
90
- const finalResponse = responses[responses.length - 1].value;
91
- // Check if Skip itself reported an error (success: false in the response body)
92
- if (finalResponse.success === false) {
93
- const skipError = finalResponse.error || 'Skip API returned an error response';
94
- LogError(`[SkipSDK] Skip API error: ${skipError}`);
95
- return {
96
- success: false,
97
- response: finalResponse,
98
- responsePhase: finalResponse.responsePhase,
99
- error: skipError,
100
- allResponses: responses
101
- };
102
- }
103
- return {
104
- success: true,
105
- response: finalResponse,
106
- responsePhase: finalResponse.responsePhase,
107
- allResponses: responses
108
- };
109
- }
110
- else {
111
- return {
112
- success: false,
113
- error: 'No response received from Skip API'
114
- };
115
- }
116
- }
117
- catch (error) {
118
- LogError(`[SkipSDK] Error calling Skip API: ${error}`);
119
- // Provide user-friendly error messages for common failures
120
- const rawError = error instanceof Error ? error.message : String(error);
121
- let userFriendlyError = rawError;
122
- const errorStr = rawError.toLowerCase();
123
- if (errorStr.includes('stream error') || errorStr.includes('aborted') || errorStr.includes('econnreset')) {
124
- userFriendlyError = 'The Skip analysis service became unavailable during processing. Please try again.';
125
- }
126
- else if (errorStr.includes('econnrefused') || errorStr.includes('enotfound')) {
127
- userFriendlyError = 'Unable to connect to the Skip analysis service. The service may be temporarily unavailable.';
128
- }
129
- else if (errorStr.includes('timeout')) {
130
- userFriendlyError = 'The Skip analysis service took too long to respond. Please try again.';
131
- }
132
- return {
133
- success: false,
134
- error: userFriendlyError
135
- };
136
- }
137
- }
138
- /**
139
- * Build the Skip API request object
140
- */
141
- async buildSkipRequest(options) {
142
- const { messages, conversationId, dataContext, requestPhase = 'initial_request', contextUser, dataSource, includeEntities = true, includeQueries = true, includeNotes = true, includeRequests = false, forceEntityRefresh = false, includeCallbackAuth = true, payload, externalReferenceID } = options;
143
- // Build base request with metadata
144
- const baseRequest = await this.buildBaseRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, includeRequests, forceEntityRefresh, includeCallbackAuth, { conversationId, requestPhase });
145
- // Build artifacts for this conversation
146
- const artifacts = await this.buildArtifacts(contextUser, dataSource, conversationId);
147
- // Process messages: filter delegation messages and enrich with metadata
148
- const processedMessages = this.processMessages(messages);
149
- // Construct the full Skip API request
150
- const request = {
151
- messages: processedMessages,
152
- conversationID: conversationId,
153
- dataContext: dataContext ? CopyScalarsAndArrays(dataContext) : undefined,
154
- requestPhase,
155
- artifacts,
156
- payload, // Pass through payload for incremental artifact building (e.g., PRD in progress)
157
- entities: baseRequest.entities || [],
158
- queries: baseRequest.queries || [],
159
- queryCatalog: baseRequest.queryCatalog,
160
- notes: baseRequest.notes,
161
- noteTypes: baseRequest.noteTypes,
162
- userEmail: baseRequest.userEmail,
163
- organizationID: baseRequest.organizationID,
164
- organizationInfo: baseRequest.organizationInfo,
165
- apiKeys: baseRequest.apiKeys,
166
- callingServerURL: baseRequest.callingServerURL,
167
- callingServerAPIKey: baseRequest.callingServerAPIKey,
168
- callingServerAccessToken: baseRequest.callingServerAccessToken,
169
- externalReferenceID,
170
- databasePlatform: getDbType()
171
- };
172
- return request;
173
- }
174
- /**
175
- * Build base request with metadata, API keys, and callback auth
176
- */
177
- async buildBaseRequest(contextUser, dataSource, includeEntities, includeQueries, includeNotes, includeRequests, forceEntityRefresh, includeCallbackAuth, additionalTokenInfo = {}) {
178
- const entities = includeEntities ? await this.buildEntities(forceEntityRefresh) : [];
179
- const queries = includeQueries ? this.buildQueries() : [];
180
- // Always build the lightweight query catalog for collision detection,
181
- // regardless of whether full queries are included
182
- const queryCatalog = this.buildQueryCatalog();
183
- const { notes, noteTypes } = includeNotes ? await this.buildAgentNotes(contextUser) : { notes: [], noteTypes: [] };
184
- // Note: requests would be built here if includeRequests is true
185
- // Setup access token for Skip callbacks if needed
186
- let accessToken;
187
- if (includeCallbackAuth) {
188
- const tokenInfo = {
189
- type: 'skip_api_request',
190
- userEmail: contextUser.Email,
191
- userName: contextUser.Name,
192
- userID: contextUser.ID,
193
- ...additionalTokenInfo
194
- };
195
- accessToken = registerAccessToken(undefined, 1000 * 60 * 10, // 10 minutes
196
- tokenInfo);
197
- }
198
- return {
199
- entities,
200
- queries,
201
- queryCatalog,
202
- notes,
203
- noteTypes,
204
- userEmail: contextUser.Email,
205
- organizationID: this.config.organizationId,
206
- organizationInfo: this.config.organizationInfo,
207
- apiKeys: this.buildAPIKeys(),
208
- callingServerURL: accessToken ? (publicUrl || `${baseUrl}:${graphqlPort}${graphqlRootPath}`) : undefined,
209
- callingServerAPIKey: accessToken ? callbackAPIKey : undefined,
210
- callingServerAccessToken: accessToken ? accessToken.Token : undefined
211
- };
212
- }
213
- /**
214
- * Build entity metadata for Skip
215
- * Copied from AskSkipResolver.BuildSkipEntities - uses cached metadata with refresh logic
216
- */
217
- async buildEntities(forceRefresh, refreshIntervalMinutes = 15) {
218
- try {
219
- const now = Date.now();
220
- const cacheExpired = (now - SkipSDK.__lastRefreshTime) > (refreshIntervalMinutes * 60 * 1000);
221
- // If force refresh is requested OR cache expired OR cache is empty, refresh
222
- if (forceRefresh || cacheExpired || SkipSDK.__skipEntitiesCache$.value === null) {
223
- LogStatus(`[SkipSDK] Refreshing Skip entities cache (force: ${forceRefresh}, expired: ${cacheExpired})`);
224
- const newData = this.refreshSkipEntities();
225
- SkipSDK.__skipEntitiesCache$.next(newData);
226
- }
227
- return SkipSDK.__skipEntitiesCache$.pipe(take(1)).toPromise();
228
- }
229
- catch (e) {
230
- LogError(`[SkipSDK] buildEntities error: ${e}`);
231
- return [];
232
- }
233
- }
234
- /**
235
- * Build saved queries for Skip
236
- */
237
- buildQueries(status = 'Approved') {
238
- const qe = QueryEngine.Instance;
239
- const approvedQueries = qe.Queries.filter((q) => q.Status === status);
240
- return approvedQueries.map((q) => ({
241
- ID: q.ID,
242
- Name: q.Name,
243
- Description: q.Description,
244
- Category: q.Category,
245
- CategoryPath: this.buildQueryCategoryPath(qe, q.CategoryID),
246
- CategoryID: q.CategoryID,
247
- SQL: q.SQL,
248
- Status: q.Status,
249
- QualityRank: q.QualityRank,
250
- Reusable: q.Reusable,
251
- EmbeddingVector: q.EmbeddingVector,
252
- EmbeddingModelID: q.EmbeddingModelID,
253
- EmbeddingModelName: q.EmbeddingModel,
254
- TechnicalDescription: q.TechnicalDescription,
255
- Fields: qe.GetQueryFields(q.ID).map((f) => ({
256
- ID: f.ID,
257
- QueryID: f.QueryID,
258
- Name: f.Name,
259
- Description: f.Description,
260
- Sequence: f.Sequence,
261
- SQLBaseType: f.SQLBaseType,
262
- SQLFullType: f.SQLFullType,
263
- SourceEntityID: f.SourceEntityID,
264
- SourceEntity: f.SourceEntity,
265
- SourceFieldName: f.SourceFieldName,
266
- IsComputed: f.IsComputed,
267
- ComputationDescription: f.ComputationDescription,
268
- IsSummary: f.IsSummary,
269
- SummaryDescription: f.SummaryDescription
270
- })),
271
- Parameters: qe.GetQueryParameters(q.ID).map((p) => ({
272
- ID: p.ID,
273
- QueryID: p.QueryID,
274
- Name: p.Name,
275
- Description: p.Description,
276
- Type: p.Type,
277
- IsRequired: p.IsRequired,
278
- DefaultValue: p.DefaultValue,
279
- SampleValue: p.SampleValue,
280
- ValidationFilters: p.ValidationFilters
281
- })),
282
- Entities: qe.QueryEntities.filter(e => UUIDsEqual(e.QueryID, q.ID)).map((e) => ({
283
- ID: e.ID,
284
- QueryID: e.QueryID,
285
- EntityID: e.EntityID,
286
- Entity: e.Entity
287
- })),
288
- SQLDialectID: q.SQLDialectID,
289
- UsesTemplate: q.UsesTemplate,
290
- IsApproved: q.IsApproved,
291
- CacheEnabled: q.CacheEnabled,
292
- CacheMaxSize: q.CacheMaxSize,
293
- CacheTTLMinutes: q.CacheTTLMinutes,
294
- CacheValidationSQL: q.CacheValidationSQL
295
- }));
296
- }
297
- /**
298
- * Recursively build category path for a query
299
- */
300
- buildQueryCategoryPath(qe, categoryID) {
301
- const cat = qe.Categories.find((c) => UUIDsEqual(c.ID, categoryID));
302
- if (!cat)
303
- return '';
304
- if (!cat.ParentID)
305
- return cat.Name;
306
- const parentPath = this.buildQueryCategoryPath(qe, cat.ParentID);
307
- return parentPath ? `${parentPath}/${cat.Name}` : cat.Name;
308
- }
309
- /**
310
- * Build a lightweight catalog of ALL query names and category paths (regardless of status).
311
- * Always called regardless of includeQueries, so collision detection
312
- * has accurate data even when full query metadata is not transmitted.
313
- * Includes all statuses because the database enforces name+category uniqueness
314
- * across all queries, not just approved ones.
315
- */
316
- buildQueryCatalog() {
317
- const qe = QueryEngine.Instance;
318
- return qe.Queries.map((q) => ({
319
- Name: q.Name,
320
- CategoryPath: this.buildQueryCategoryPath(qe, q.CategoryID)
321
- }));
322
- }
323
- /**
324
- * Build agent notes for Skip
325
- */
326
- async buildAgentNotes(contextUser) {
327
- // TODO: Implement agent notes building
328
- // This would query AIAgentNote entities and convert to SkipAPIAgentNote format
329
- // For now, returning empty arrays
330
- return { notes: [], noteTypes: [] };
331
- }
332
- /**
333
- * Build artifacts for a conversation using optimized query
334
- * Uses GetConversationArtifactsForAgent query which joins through ConversationDetailArtifact
335
- * to get artifacts that were outputs from Skip agent's conversation details
336
- */
337
- async buildArtifacts(contextUser, dataSource, conversationId) {
338
- try {
339
- const rq = new RunQuery();
340
- // Ensure AIEngine is configured and get Skip agent ID
341
- await AIEngine.Instance.Config(false, contextUser);
342
- const skipAgent = AIEngine.Instance.GetAgentByName('Skip');
343
- const skipAgentId = skipAgent?.ID;
344
- if (!skipAgentId) {
345
- LogError('[SkipSDK] Skip agent not found in AIEngine');
346
- }
347
- // Use optimized query that replaces 4 RunView calls with 1 query
348
- // This query includes Configuration field needed for component spec extraction
349
- // Filter by Skip agent ID to only get artifacts created by Skip (not delegation agents)
350
- const result = await rq.RunQuery({
351
- QueryName: 'GetConversationArtifactsForAgent',
352
- CategoryPath: 'MJ/Conversations',
353
- Parameters: {
354
- ConversationID: conversationId,
355
- AgentID: skipAgentId // Filter to only artifacts created by Skip agent
356
- }
357
- }, contextUser);
358
- if (!result.Success || !result.Results || result.Results.length === 0) {
359
- return [];
360
- }
361
- // Query returns flat result set: one row per artifact version
362
- // Group by ArtifactID to build SkipAPIArtifact objects with their versions
363
- const artifactMap = new Map();
364
- // Process each row (represents one version)
365
- for (const row of result.Results) {
366
- const artifactId = row.ArtifactID;
367
- // Initialize artifact entry if not exists
368
- if (!artifactMap.has(artifactId)) {
369
- // Map database sharingScope values to SkipAPIArtifact expected values
370
- let sharingScope = 'None';
371
- const dbSharingScope = (row.SharingScope || '').toLowerCase();
372
- if (dbSharingScope === 'always' || dbSharingScope === 'everyone') {
373
- sharingScope = 'Everyone';
374
- }
375
- else if (dbSharingScope === 'public') {
376
- sharingScope = 'Public';
377
- }
378
- else if (dbSharingScope === 'specific users' || dbSharingScope === 'specificusers') {
379
- sharingScope = 'SpecificUsers';
380
- }
381
- artifactMap.set(artifactId, {
382
- artifact: {
383
- id: artifactId,
384
- conversationId: conversationId,
385
- name: row.ArtifactName,
386
- description: row.ArtifactDescription || '',
387
- sharingScope: sharingScope,
388
- comments: row.ArtifactComments || '',
389
- createdAt: new Date(row.ArtifactCreatedAt),
390
- updatedAt: new Date(row.ArtifactUpdatedAt)
391
- },
392
- artifactType: {
393
- id: row.ArtifactTypeID,
394
- name: row.ArtifactTypeName,
395
- description: row.ArtifactTypeDescription,
396
- contentType: row.ArtifactTypeContentType,
397
- enabled: true,
398
- createdAt: new Date(row.ArtifactTypeCreatedAt),
399
- updatedAt: new Date(row.ArtifactTypeUpdatedAt)
400
- },
401
- versions: []
402
- });
403
- }
404
- // Add this version to the artifact
405
- const entry = artifactMap.get(artifactId);
406
- entry.versions.push({
407
- id: row.VersionID,
408
- artifactId: artifactId,
409
- conversationDetailID: row.ConversationDetailID, // Direct from join table!
410
- version: row.Version,
411
- configuration: row.Configuration || '',
412
- content: row.Content || '',
413
- comments: row.VersionComments || '',
414
- createdAt: new Date(row.VersionCreatedAt),
415
- updatedAt: new Date(row.VersionUpdatedAt)
416
- });
417
- }
418
- // Convert map to SkipAPIArtifact array
419
- const artifacts = Array.from(artifactMap.values()).map(entry => ({
420
- ...entry.artifact,
421
- artifactType: entry.artifactType,
422
- versions: entry.versions
423
- }));
424
- // Also include INPUT artifacts (e.g., user-captured Data Snapshots
425
- // attached to messages via the Analyze button or
426
- // client:capture-data-snapshot actionable command). The query above
427
- // only returns Direction='Output' artifacts produced BY Skip — but
428
- // Skip also needs to see artifacts the user gave it as input.
429
- const inputArtifacts = await this.buildInputArtifacts(contextUser, conversationId, artifactMap);
430
- if (inputArtifacts.length > 0) {
431
- artifacts.push(...inputArtifacts);
432
- }
433
- return artifacts;
434
- }
435
- catch (error) {
436
- LogError(`Failed to build artifacts for conversation ${conversationId}: ${error}`);
437
- return [];
438
- }
439
- }
440
- /**
441
- * Fetch INPUT artifacts attached to user messages in this conversation
442
- * (Direction='Input' on ConversationDetailArtifact). Skip should see these
443
- * so it can use captured Data Snapshots, user-uploaded files, etc., in
444
- * its analysis.
445
- *
446
- * Deduplicates against `alreadyLoaded` (the Output artifacts) so the same
447
- * artifact ID doesn't appear twice if it was both produced and re-attached.
448
- */
449
- async buildInputArtifacts(contextUser, conversationId, alreadyLoaded) {
450
- try {
451
- const rv = new RunView();
452
- // Pull conversation detail IDs in this conversation
453
- const detailsResult = await rv.RunView({
454
- EntityName: 'MJ: Conversation Details',
455
- ExtraFilter: `ConversationID='${conversationId}'`,
456
- Fields: ['ID'],
457
- ResultType: 'simple',
458
- }, contextUser);
459
- const detailIds = detailsResult.Success && detailsResult.Results
460
- ? detailsResult.Results.map(r => r.ID)
461
- : [];
462
- if (detailIds.length === 0)
463
- return [];
464
- // Junction rows where Direction='Input' for those details
465
- const junctionResult = await rv.RunView({
466
- EntityName: 'MJ: Conversation Detail Artifacts',
467
- ExtraFilter: `ConversationDetailID IN ('${detailIds.join("','")}') AND Direction='Input'`,
468
- Fields: ['ArtifactVersionID', 'ConversationDetailID'],
469
- ResultType: 'simple',
470
- }, contextUser);
471
- const junctions = junctionResult.Success && junctionResult.Results
472
- ? junctionResult.Results
473
- : [];
474
- if (junctions.length === 0)
475
- return [];
476
- // Load each ArtifactVersion + its parent Artifact + ArtifactType
477
- const versionIds = [...new Set(junctions.map(j => j.ArtifactVersionID))];
478
- const versionsResult = await rv.RunView({
479
- EntityName: 'MJ: Artifact Versions',
480
- ExtraFilter: `ID IN ('${versionIds.join("','")}')`,
481
- ResultType: 'simple',
482
- }, contextUser);
483
- const versions = versionsResult.Success && versionsResult.Results
484
- ? versionsResult.Results
485
- : [];
486
- if (versions.length === 0)
487
- return [];
488
- const artifactIds = [...new Set(versions.map(v => v.ArtifactID))];
489
- const artifactsResult = await rv.RunView({
490
- EntityName: 'MJ: Artifacts',
491
- ExtraFilter: `ID IN ('${artifactIds.join("','")}')`,
492
- ResultType: 'simple',
493
- }, contextUser);
494
- const artifactRows = artifactsResult.Success && artifactsResult.Results
495
- ? artifactsResult.Results
496
- : [];
497
- const typeIds = [...new Set(artifactRows.map(a => a.TypeID))];
498
- const typesResult = await rv.RunView({
499
- EntityName: 'MJ: Artifact Types',
500
- ExtraFilter: `ID IN ('${typeIds.join("','")}')`,
501
- ResultType: 'simple',
502
- }, contextUser);
503
- const typeRows = typesResult.Success && typesResult.Results
504
- ? typesResult.Results
505
- : [];
506
- const typeMap = new Map(typeRows.map(t => [t.ID, t]));
507
- // Build a junction lookup: artifactVersionId -> conversationDetailId
508
- const versionToDetail = new Map(junctions.map(j => [j.ArtifactVersionID, j.ConversationDetailID]));
509
- // Build SkipAPIArtifact entries
510
- const inputArtifactMap = new Map();
511
- for (const v of versions) {
512
- const aRow = artifactRows.find(a => UUIDsEqual(a.ID, v.ArtifactID));
513
- if (!aRow)
514
- continue;
515
- if (alreadyLoaded.has(aRow.ID))
516
- continue; // dedup
517
- const typeRow = typeMap.get(aRow.TypeID);
518
- if (!typeRow)
519
- continue;
520
- let entry = inputArtifactMap.get(aRow.ID);
521
- if (!entry) {
522
- entry = {
523
- artifact: {
524
- id: aRow.ID,
525
- conversationId,
526
- name: aRow.Name,
527
- description: aRow.Description || '',
528
- sharingScope: 'None',
529
- comments: aRow.Comments || '',
530
- createdAt: new Date(aRow.__mj_CreatedAt),
531
- updatedAt: new Date(aRow.__mj_UpdatedAt),
532
- },
533
- artifactType: {
534
- id: typeRow.ID,
535
- name: typeRow.Name,
536
- description: typeRow.Description,
537
- contentType: typeRow.ContentType,
538
- enabled: true,
539
- createdAt: new Date(typeRow.__mj_CreatedAt),
540
- updatedAt: new Date(typeRow.__mj_UpdatedAt),
541
- },
542
- versions: [],
543
- };
544
- inputArtifactMap.set(aRow.ID, entry);
545
- }
546
- entry.versions.push({
547
- id: v.ID,
548
- artifactId: v.ArtifactID,
549
- conversationDetailID: versionToDetail.get(v.ID) ?? '',
550
- version: v.VersionNumber,
551
- configuration: v.Configuration || '',
552
- content: v.Content || '',
553
- comments: v.Comments || '',
554
- createdAt: new Date(v.__mj_CreatedAt),
555
- updatedAt: new Date(v.__mj_UpdatedAt),
556
- });
557
- }
558
- return Array.from(inputArtifactMap.values()).map(entry => ({
559
- ...entry.artifact,
560
- artifactType: entry.artifactType,
561
- versions: entry.versions,
562
- }));
563
- }
564
- catch (error) {
565
- LogError(`Failed to load input artifacts for conversation ${conversationId}: ${error}`);
566
- return [];
567
- }
568
- }
569
- /**
570
- * Build API keys for AI services
571
- */
572
- buildAPIKeys() {
573
- return [
574
- {
575
- vendorDriverName: 'OpenAILLM',
576
- apiKey: GetAIAPIKey('OpenAILLM')
577
- },
578
- {
579
- vendorDriverName: 'AnthropicLLM',
580
- apiKey: GetAIAPIKey('AnthropicLLM')
581
- },
582
- {
583
- vendorDriverName: 'GeminiLLM',
584
- apiKey: GetAIAPIKey('GeminiLLM')
585
- },
586
- {
587
- vendorDriverName: 'GroqLLM',
588
- apiKey: GetAIAPIKey('GroqLLM')
589
- },
590
- {
591
- vendorDriverName: 'MistralLLM',
592
- apiKey: GetAIAPIKey('MistralLLM')
593
- },
594
- {
595
- vendorDriverName: 'CerebrasLLM',
596
- apiKey: GetAIAPIKey('CerebrasLLM')
597
- }
598
- ];
599
- }
600
- /**
601
- * Build HTTP headers for Skip API requests
602
- */
603
- buildHeaders() {
604
- return {
605
- 'x-api-key': this.config.apiKey || '',
606
- 'Content-Type': 'application/json'
607
- };
608
- }
609
- /**
610
- * Send an SSE-aware POST request to the Skip API.
611
- * Replaces sendPostRequest for SSE format: parses `data: {json}\n\n` events
612
- * instead of NDJSON `{json}\n` lines. This is required because Azure Container
613
- * Apps' Envoy proxy buffers NDJSON responses but streams SSE responses.
614
- */
615
- async sendSSERequest(url, payload, headers, streamCallback) {
616
- // Gzip the request body
617
- const compressed = await new Promise((resolve, reject) => {
618
- gzipCompress(JSON.stringify(payload), (err, result) => {
619
- if (err)
620
- reject(err);
621
- else
622
- resolve(result);
623
- });
624
- });
625
- return new Promise((resolve, reject) => {
626
- try {
627
- const parsedUrl = new URL(url);
628
- const options = {
629
- hostname: parsedUrl.hostname,
630
- port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
631
- path: parsedUrl.pathname,
632
- method: 'POST',
633
- headers: {
634
- 'Content-Type': 'application/json',
635
- 'Content-Encoding': 'gzip',
636
- ...headers
637
- }
638
- };
639
- const requestFn = parsedUrl.protocol === 'https:' ? httpsRequest : httpRequest;
640
- const events = [];
641
- let buffer = '';
642
- let streamEnded = false;
643
- const parseSSELine = (line) => {
644
- if (line.trim() === '')
645
- return; // Skip empty lines (SSE event delimiters)
646
- if (!line.startsWith('data: '))
647
- return; // Skip non-data SSE fields
648
- const jsonStr = line.slice(6);
649
- if (!jsonStr.trim())
650
- return;
651
- try {
652
- const event = JSON.parse(jsonStr);
653
- events.push(event);
654
- streamCallback?.(event);
655
- }
656
- catch (e) {
657
- LogError(`[SkipSDK] SSE parse error: ${e}`);
658
- }
659
- };
660
- const handleStreamEnd = () => {
661
- if (streamEnded)
662
- return;
663
- streamEnded = true;
664
- // Try to parse any remaining data in buffer
665
- if (buffer.trim()) {
666
- parseSSELine(buffer);
667
- }
668
- resolve(events);
669
- };
670
- const req = requestFn(options, (res) => {
671
- // Check for non-2xx HTTP status codes before attempting SSE parsing.
672
- // The Skip API returns JSON error bodies for auth/validation failures (401, 403, etc.)
673
- // which won't contain SSE `data:` lines, resulting in an empty events array
674
- // and the misleading "No response received from Skip API" error.
675
- if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
676
- let errorBody = '';
677
- res.on('data', (chunk) => { errorBody += chunk.toString(); });
678
- res.on('end', () => {
679
- let errorMessage = `Skip API returned HTTP ${res.statusCode}`;
680
- try {
681
- const parsed = JSON.parse(errorBody);
682
- if (parsed.message) {
683
- errorMessage = parsed.message;
684
- }
685
- else if (parsed.error) {
686
- errorMessage = parsed.error;
687
- }
688
- }
689
- catch {
690
- // Non-JSON body — use raw text if available
691
- if (errorBody.trim()) {
692
- errorMessage += `: ${errorBody.trim().substring(0, 200)}`;
693
- }
694
- }
695
- LogError(`[SkipSDK] HTTP ${res.statusCode} from ${url}: ${errorMessage}`);
696
- reject(new Error(errorMessage));
697
- });
698
- return;
699
- }
700
- const gunzip = createGunzip();
701
- const stream = res.headers['content-encoding'] === 'gzip' ? res.pipe(gunzip) : res;
702
- stream.on('data', (chunk) => {
703
- buffer += chunk.toString();
704
- let boundary;
705
- while ((boundary = buffer.indexOf('\n')) !== -1) {
706
- const line = buffer.substring(0, boundary);
707
- buffer = buffer.substring(boundary + 1);
708
- parseSSELine(line);
709
- }
710
- });
711
- stream.on('end', handleStreamEnd);
712
- stream.on('close', () => {
713
- if (!streamEnded) {
714
- LogError(`[SkipSDK] SSE stream closed prematurely for ${url}`);
715
- handleStreamEnd();
716
- }
717
- });
718
- stream.on('error', (e) => {
719
- if (!streamEnded) {
720
- LogError(`[SkipSDK] SSE stream error for ${url}: ${e.message}`);
721
- reject(new Error(`SSE stream error: ${e.message}`));
722
- }
723
- });
724
- });
725
- req.on('error', (e) => {
726
- LogError(`[SkipSDK] SSE request error for ${url}: ${e.message}`);
727
- reject(new Error(`HTTP request failed to ${url}: ${e.message}`));
728
- });
729
- req.write(compressed);
730
- req.end();
731
- }
732
- catch (e) {
733
- LogError(`[SkipSDK] sendSSERequest error: ${e}`);
734
- reject(e);
735
- }
736
- });
737
- }
738
- /**
739
- * Refreshes the Skip entities cache
740
- * Rebuilds the entity information that is provided to Skip
741
- * Refreshes the entity metadata cache. Filters entities by schema/scope config,
742
- * filters fields by AI scope, and enriches field values from the database.
743
- * Returns EntityInfo objects directly — no intermediate Skip-specific types needed.
744
- */
745
- async refreshSkipEntities() {
746
- try {
747
- const md = this.Provider;
748
- // Diagnostic logging
749
- LogStatus(`[SkipSDK.refreshSkipEntities] Total entities in metadata: ${md.Entities.length}`);
750
- LogStatus(`[SkipSDK.refreshSkipEntities] Config excludeSchemas: ${JSON.stringify(configInfo.askSkip?.entitiesToSend?.excludeSchemas)}`);
751
- LogStatus(`[SkipSDK.refreshSkipEntities] Config includeEntitiesFromExcludedSchemas: ${JSON.stringify(configInfo.askSkip?.entitiesToSend?.includeEntitiesFromExcludedSchemas)}`);
752
- const skipSpecialIncludeEntities = (configInfo.askSkip?.entitiesToSend?.includeEntitiesFromExcludedSchemas ?? [])
753
- .map((e) => e.trim().toLowerCase());
754
- // Get the list of entities
755
- const entities = md.Entities.filter((e) => {
756
- if (!(configInfo.askSkip?.entitiesToSend?.excludeSchemas ?? []).includes(e.SchemaName) ||
757
- skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase())) {
758
- const sd = e.ScopeDefault?.trim();
759
- if (sd && sd.length > 0) {
760
- const scopes = sd.split(',').map((s) => s.trim().toLowerCase()) ?? ['all'];
761
- return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai') || skipSpecialIncludeEntities.includes(e.Name.trim().toLowerCase());
762
- }
763
- else {
764
- return true; // no scope, so include it
765
- }
766
- }
767
- return false;
768
- });
769
- LogStatus(`[SkipSDK.refreshSkipEntities] Filtered entities count: ${entities.length}`);
770
- if (entities.length === 0) {
771
- LogError(`[SkipSDK.refreshSkipEntities] WARNING: No entities passed filtering! This will result in empty Skip entities list.`);
772
- }
773
- // Build enriched EntityInfo objects with filtered fields and packed values
774
- const result = await Promise.all(entities.map((e) => this.buildEntityForSkip(e)));
775
- LogStatus(`[SkipSDK.refreshSkipEntities] Successfully packed ${result.length} entities for Skip`);
776
- SkipSDK.__lastRefreshTime = Date.now();
777
- return result;
778
- }
779
- catch (e) {
780
- LogError(`[SkipSDK] refreshSkipEntities error: ${e}`);
781
- return [];
782
- }
783
- }
784
- /**
785
- * Builds an EntityInfo object for Skip, filtering fields by AI scope and
786
- * enriching field values from the database. Returns a new EntityInfo
787
- * constructed from a plain object so it serializes cleanly via toJSON().
788
- */
789
- async buildEntityForSkip(e) {
790
- try {
791
- // Filter fields by scope (only include fields visible to AI)
792
- const filteredFields = e.Fields.filter(f => {
793
- const scopes = f.ScopeDefault?.split(',').map((s) => s.trim().toLowerCase());
794
- return !scopes || scopes.length === 0 || scopes.includes('all') || scopes.includes('ai');
795
- });
796
- // Enrich each field with packed possible values
797
- const enrichedFields = await Promise.all(filteredFields.map(f => this.enrichFieldValues(f)));
798
- // Clone the entity via toJSON, then swap in filtered+enriched fields and Skip-specific
799
- // Active-only organic keys. Any future EntityInfo properties flow through automatically.
800
- return new EntityInfo({
801
- ...e.toJSON(),
802
- Fields: enrichedFields,
803
- OrganicKeys: e.OrganicKeys.filter(ok => ok.Status === 'Active'),
804
- });
805
- }
806
- catch (err) {
807
- LogError(`[SkipSDK] buildEntityForSkip error: ${err}`);
808
- return null;
809
- }
810
- }
811
- /**
812
- * Enriches a field's EntityFieldValues with possible values from the database.
813
- * Returns a plain object that can be used to construct an EntityFieldInfo.
814
- */
815
- async enrichFieldValues(f) {
816
- return {
817
- ...f.toJSON(),
818
- EntityFieldValues: await this.packFieldValues(f),
819
- };
820
- }
821
- /**
822
- * Packs possible values for an entity field based on the ValuesToPackWithSchema setting.
823
- * Returns EntityFieldValueInfo-compatible objects.
824
- */
825
- async packFieldValues(f) {
826
- try {
827
- if (f.ValuesToPackWithSchema === 'None') {
828
- return [];
829
- }
830
- else if (f.ValuesToPackWithSchema === 'All') {
831
- return await this.getFieldDistinctValues(f);
832
- }
833
- else if (f.ValuesToPackWithSchema === 'Auto') {
834
- if (f.ValueListTypeEnum === 'List') {
835
- return f.EntityFieldValues.map((v) => new EntityFieldValueInfo({ Value: v.Value, Code: v.Value }));
836
- }
837
- else if (f.ValueListTypeEnum === 'ListOrUserEntry') {
838
- const values = await this.getFieldDistinctValues(f);
839
- if (!values || values.length === 0) {
840
- return f.EntityFieldValues.map((v) => new EntityFieldValueInfo({ Value: v.Value, Code: v.Value }));
841
- }
842
- else {
843
- const fromEntityFieldValues = f.EntityFieldValues.map((v) => new EntityFieldValueInfo({ Value: v.Value, Code: v.Value }));
844
- return [...new Set([...fromEntityFieldValues, ...values])];
845
- }
846
- }
847
- }
848
- return [];
849
- }
850
- catch (e) {
851
- LogError(`[SkipSDK] packFieldValues error: ${e}`);
852
- return [];
853
- }
854
- }
855
- /**
856
- * Gets distinct values for a field from the database.
857
- * Returns EntityFieldValueInfo objects.
858
- */
859
- async getFieldDistinctValues(f) {
860
- try {
861
- // Use this SDK instance's bound provider so multi-tenant servers route the SQL
862
- // through the right connection. ExecuteSQL works on both SQL Server and PostgreSQL.
863
- const provider = this.Provider;
864
- const sql = `SELECT DISTINCT ${f.Name} FROM ${f.SchemaName}.${f.BaseView}`;
865
- const rows = await provider.ExecuteSQL(sql);
866
- if (!rows || rows.length === 0) {
867
- return [];
868
- }
869
- return rows.map((r) => new EntityFieldValueInfo({ Value: r[f.Name], Code: r[f.Name] }));
870
- }
871
- catch (e) {
872
- LogError(`[SkipSDK] getFieldDistinctValues error: ${e}`);
873
- return [];
874
- }
875
- }
876
- /**
877
- * Process messages: filter delegation messages and add metadata fields
878
- * Messages coming in should already have conversationDetailID if they exist in the database
879
- */
880
- processMessages(messages) {
881
- // Filter out delegation messages (administrative messages that shouldn't go to Skip)
882
- const filteredMessages = messages.filter(msg => !this.isDelegationMessage(msg.content));
883
- // Enrich messages with default metadata if not already present
884
- return filteredMessages.map(msg => ({
885
- ...msg,
886
- // Add default metadata fields if not already present
887
- // Messages from DB already have conversationDetailID, temp messages get temp-X
888
- hiddenToUser: msg.hiddenToUser ?? false,
889
- userRating: msg.userRating ?? null,
890
- userFeedback: msg.userFeedback ?? null,
891
- reflectionInsights: msg.reflectionInsights ?? null,
892
- summaryOfEarlierConveration: msg.summaryOfEarlierConveration ?? null
893
- }));
894
- }
895
- /**
896
- * Check if a message is a delegation message that should be filtered out
897
- * Uses flexible pattern matching to detect variations of delegation messages
898
- */
899
- isDelegationMessage(content) {
900
- if (!content)
901
- return false;
902
- const lowerContent = content.toLowerCase();
903
- // Check for both "delegating" or "delegate" AND "skip" in any order
904
- const hasDelegatingOrDelegate = lowerContent.includes('delegating') || lowerContent.includes('delegate');
905
- const hasSkip = lowerContent.includes('skip');
906
- return hasDelegatingOrDelegate && hasSkip;
907
- }
908
- }
909
- //# sourceMappingURL=skip-sdk.js.map