@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.
- package/dist/agentSessions/ReturningVisitorRecap.d.ts +39 -0
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js +198 -0
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -0
- package/dist/agentSessions/SessionJanitor.d.ts +13 -0
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +65 -7
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +37 -0
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +10 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +16 -0
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +1 -1
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +1 -1
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +26 -0
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/config.d.ts +1883 -144
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +160 -36
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +45 -2
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +522 -0
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +2860 -0
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +13 -0
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +22 -0
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +4 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +101 -3
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +6 -0
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts +26 -0
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -0
- package/dist/realtimeWidget/WidgetRouter.js +182 -0
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -0
- package/dist/realtimeWidget/WidgetSessionService.d.ts +265 -0
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -0
- package/dist/realtimeWidget/WidgetSessionService.js +477 -0
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -0
- package/dist/realtimeWidget/host-identity.d.ts +40 -0
- package/dist/realtimeWidget/host-identity.d.ts.map +1 -0
- package/dist/realtimeWidget/host-identity.js +58 -0
- package/dist/realtimeWidget/host-identity.js.map +1 -0
- package/dist/realtimeWidget/index.d.ts +10 -0
- package/dist/realtimeWidget/index.d.ts.map +1 -0
- package/dist/realtimeWidget/index.js +9 -0
- package/dist/realtimeWidget/index.js.map +1 -0
- package/dist/realtimeWidget/visitorIdentity.d.ts +76 -0
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -0
- package/dist/realtimeWidget/visitorIdentity.js +202 -0
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -0
- package/dist/realtimeWidget/widgetCore.d.ts +106 -0
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -0
- package/dist/realtimeWidget/widgetCore.js +173 -0
- package/dist/realtimeWidget/widgetCore.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +51 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.js +79 -0
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -0
- package/dist/resolvers/ComponentRegistryResolver.d.ts +7 -0
- package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js +31 -8
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.js +1 -2
- package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +9 -6
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +15 -13
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +47 -3
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RingCentralTelephonyResolver.d.ts +27 -0
- package/dist/resolvers/RingCentralTelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/RingCentralTelephonyResolver.js +87 -0
- package/dist/resolvers/RingCentralTelephonyResolver.js.map +1 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +14 -3
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/SearchKnowledgeResolver.d.ts.map +1 -1
- package/dist/resolvers/SearchKnowledgeResolver.js +2 -0
- package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
- package/dist/resolvers/TeamsMeetingsResolver.d.ts +28 -0
- package/dist/resolvers/TeamsMeetingsResolver.d.ts.map +1 -0
- package/dist/resolvers/TeamsMeetingsResolver.js +91 -0
- package/dist/resolvers/TeamsMeetingsResolver.js.map +1 -0
- package/dist/resolvers/TelephonyResolver.d.ts +26 -0
- package/dist/resolvers/TelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/TelephonyResolver.js +86 -0
- package/dist/resolvers/TelephonyResolver.js.map +1 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +2 -3
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/VonageTelephonyResolver.d.ts +26 -0
- package/dist/resolvers/VonageTelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/VonageTelephonyResolver.js +86 -0
- package/dist/resolvers/VonageTelephonyResolver.js.map +1 -0
- package/dist/rest/MediaStreamHandler.js +4 -1
- package/dist/rest/MediaStreamHandler.js.map +1 -1
- package/dist/telephony/RingCentralTelephonyService.d.ts +115 -0
- package/dist/telephony/RingCentralTelephonyService.d.ts.map +1 -0
- package/dist/telephony/RingCentralTelephonyService.js +262 -0
- package/dist/telephony/RingCentralTelephonyService.js.map +1 -0
- package/dist/telephony/TeamsMeetingsRouter.d.ts +40 -0
- package/dist/telephony/TeamsMeetingsRouter.d.ts.map +1 -0
- package/dist/telephony/TeamsMeetingsRouter.js +96 -0
- package/dist/telephony/TeamsMeetingsRouter.js.map +1 -0
- package/dist/telephony/TeamsMeetingsService.d.ts +113 -0
- package/dist/telephony/TeamsMeetingsService.d.ts.map +1 -0
- package/dist/telephony/TeamsMeetingsService.js +196 -0
- package/dist/telephony/TeamsMeetingsService.js.map +1 -0
- package/dist/telephony/TwilioTelephonyRouter.d.ts +36 -0
- package/dist/telephony/TwilioTelephonyRouter.d.ts.map +1 -0
- package/dist/telephony/TwilioTelephonyRouter.js +143 -0
- package/dist/telephony/TwilioTelephonyRouter.js.map +1 -0
- package/dist/telephony/TwilioTelephonyService.d.ts +95 -0
- package/dist/telephony/TwilioTelephonyService.d.ts.map +1 -0
- package/dist/telephony/TwilioTelephonyService.js +193 -0
- package/dist/telephony/TwilioTelephonyService.js.map +1 -0
- package/dist/telephony/VonageTelephonyRouter.d.ts +41 -0
- package/dist/telephony/VonageTelephonyRouter.d.ts.map +1 -0
- package/dist/telephony/VonageTelephonyRouter.js +201 -0
- package/dist/telephony/VonageTelephonyRouter.js.map +1 -0
- package/dist/telephony/VonageTelephonyService.d.ts +90 -0
- package/dist/telephony/VonageTelephonyService.d.ts.map +1 -0
- package/dist/telephony/VonageTelephonyService.js +191 -0
- package/dist/telephony/VonageTelephonyService.js.map +1 -0
- package/dist/telephony/calendar-scheduler.d.ts +67 -0
- package/dist/telephony/calendar-scheduler.d.ts.map +1 -0
- package/dist/telephony/calendar-scheduler.js +133 -0
- package/dist/telephony/calendar-scheduler.js.map +1 -0
- package/dist/telephony/index.d.ts +29 -0
- package/dist/telephony/index.d.ts.map +1 -0
- package/dist/telephony/index.js +38 -0
- package/dist/telephony/index.js.map +1 -0
- package/dist/telephony/media-upgrade-router.d.ts +33 -0
- package/dist/telephony/media-upgrade-router.d.ts.map +1 -0
- package/dist/telephony/media-upgrade-router.js +56 -0
- package/dist/telephony/media-upgrade-router.js.map +1 -0
- package/dist/telephony/ringcentral-runtime.d.ts +14 -0
- package/dist/telephony/ringcentral-runtime.d.ts.map +1 -0
- package/dist/telephony/ringcentral-runtime.js +18 -0
- package/dist/telephony/ringcentral-runtime.js.map +1 -0
- package/dist/telephony/teams-meetings-runtime.d.ts +16 -0
- package/dist/telephony/teams-meetings-runtime.d.ts.map +1 -0
- package/dist/telephony/teams-meetings-runtime.js +20 -0
- package/dist/telephony/teams-meetings-runtime.js.map +1 -0
- package/dist/telephony/teamsAcsMediaRegistry.d.ts +69 -0
- package/dist/telephony/teamsAcsMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/teamsAcsMediaRegistry.js +118 -0
- package/dist/telephony/teamsAcsMediaRegistry.js.map +1 -0
- package/dist/telephony/telephony-runtime.d.ts +14 -0
- package/dist/telephony/telephony-runtime.d.ts.map +1 -0
- package/dist/telephony/telephony-runtime.js +18 -0
- package/dist/telephony/telephony-runtime.js.map +1 -0
- package/dist/telephony/twilioMediaRegistry.d.ts +60 -0
- package/dist/telephony/twilioMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/twilioMediaRegistry.js +106 -0
- package/dist/telephony/twilioMediaRegistry.js.map +1 -0
- package/dist/telephony/vonage-runtime.d.ts +14 -0
- package/dist/telephony/vonage-runtime.d.ts.map +1 -0
- package/dist/telephony/vonage-runtime.js +18 -0
- package/dist/telephony/vonage-runtime.js.map +1 -0
- package/dist/telephony/vonageMediaRegistry.d.ts +78 -0
- package/dist/telephony/vonageMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/vonageMediaRegistry.js +158 -0
- package/dist/telephony/vonageMediaRegistry.js.map +1 -0
- package/package.json +88 -83
- package/src/__tests__/search-knowledge-tags.test.ts +9 -9
- package/src/__tests__/teams-meetings-service.test.ts +65 -0
- package/src/__tests__/teamsAcsMediaRegistry.test.ts +82 -0
- package/src/__tests__/twilio-telephony-service.test.ts +23 -0
- package/src/__tests__/twilioMediaRegistry.test.ts +103 -0
- package/src/__tests__/vonageMediaRegistry.test.ts +180 -0
- package/src/__tests__/widget.test.ts +204 -0
- package/src/__tests__/widgetGuestElevation.test.ts +57 -0
- package/src/__tests__/widgetHostIdentity.test.ts +117 -0
- package/src/agentSessions/ReturningVisitorRecap.ts +242 -0
- package/src/agentSessions/SessionJanitor.ts +66 -6
- package/src/agentSessions/SessionManager.ts +38 -0
- package/src/auth/magicLink/MagicLinkRouter.ts +17 -0
- package/src/auth/magicLink/index.ts +1 -0
- package/src/auth/magicLink/types.ts +26 -0
- package/src/config.ts +172 -38
- package/src/context.ts +50 -3
- package/src/generated/generated.ts +1991 -1
- package/src/generic/ResolverBase.ts +28 -0
- package/src/generic/RunViewResolver.ts +4 -0
- package/src/index.ts +109 -3
- package/src/integration/CustomColumnPromoter.ts +6 -0
- package/src/realtimeWidget/WidgetRouter.ts +204 -0
- package/src/realtimeWidget/WidgetSessionService.ts +714 -0
- package/src/realtimeWidget/host-identity.ts +84 -0
- package/src/realtimeWidget/index.ts +15 -0
- package/src/realtimeWidget/visitorIdentity.ts +258 -0
- package/src/realtimeWidget/widgetCore.ts +231 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +115 -0
- package/src/resolvers/ComponentRegistryResolver.ts +36 -10
- package/src/resolvers/EntityPermissionResolver.ts +1 -2
- package/src/resolvers/QuerySystemUserResolver.ts +14 -10
- package/src/resolvers/RealtimeClientSessionResolver.ts +59 -2
- package/src/resolvers/RingCentralTelephonyResolver.ts +64 -0
- package/src/resolvers/RunAIAgentResolver.ts +16 -4
- package/src/resolvers/SearchKnowledgeResolver.ts +2 -0
- package/src/resolvers/TeamsMeetingsResolver.ts +68 -0
- package/src/resolvers/TelephonyResolver.ts +63 -0
- package/src/resolvers/TestQuerySQLResolver.ts +2 -3
- package/src/resolvers/VonageTelephonyResolver.ts +63 -0
- package/src/rest/MediaStreamHandler.ts +4 -1
- package/src/telephony/RingCentralTelephonyService.ts +342 -0
- package/src/telephony/TeamsMeetingsRouter.ts +124 -0
- package/src/telephony/TeamsMeetingsService.ts +268 -0
- package/src/telephony/TwilioTelephonyRouter.ts +184 -0
- package/src/telephony/TwilioTelephonyService.ts +261 -0
- package/src/telephony/VonageTelephonyRouter.ts +239 -0
- package/src/telephony/VonageTelephonyService.ts +259 -0
- package/src/telephony/calendar-scheduler.ts +176 -0
- package/src/telephony/index.ts +43 -0
- package/src/telephony/media-upgrade-router.ts +62 -0
- package/src/telephony/ringcentral-runtime.ts +22 -0
- package/src/telephony/teams-meetings-runtime.ts +24 -0
- package/src/telephony/teamsAcsMediaRegistry.ts +152 -0
- package/src/telephony/telephony-runtime.ts +22 -0
- package/src/telephony/twilioMediaRegistry.ts +137 -0
- package/src/telephony/vonage-runtime.ts +22 -0
- package/src/telephony/vonageMediaRegistry.ts +200 -0
- package/dist/agents/skip-agent.d.ts +0 -111
- package/dist/agents/skip-agent.d.ts.map +0 -1
- package/dist/agents/skip-agent.js +0 -1487
- package/dist/agents/skip-agent.js.map +0 -1
- package/dist/agents/skip-sdk.d.ts +0 -261
- package/dist/agents/skip-sdk.d.ts.map +0 -1
- package/dist/agents/skip-sdk.js +0 -909
- package/dist/agents/skip-sdk.js.map +0 -1
- package/src/agents/skip-agent.ts +0 -1594
- package/src/agents/skip-sdk.ts +0 -1210
package/dist/agents/skip-sdk.js
DELETED
|
@@ -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
|