catalist-support-agent 1.0.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/admin-portal.d.ts +43 -0
- package/dist/admin-portal.d.ts.map +1 -0
- package/dist/admin-portal.js +166 -0
- package/dist/admin-portal.js.map +1 -0
- package/dist/analysis/entities.d.ts +73 -0
- package/dist/analysis/entities.d.ts.map +1 -0
- package/dist/analysis/entities.js +378 -0
- package/dist/analysis/entities.js.map +1 -0
- package/dist/analysis/index.d.ts +44 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +243 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/analysis/intent.d.ts +49 -0
- package/dist/analysis/intent.d.ts.map +1 -0
- package/dist/analysis/intent.js +320 -0
- package/dist/analysis/intent.js.map +1 -0
- package/dist/analysis/sentiment.d.ts +57 -0
- package/dist/analysis/sentiment.d.ts.map +1 -0
- package/dist/analysis/sentiment.js +351 -0
- package/dist/analysis/sentiment.js.map +1 -0
- package/dist/brand/compliance.d.ts +122 -0
- package/dist/brand/compliance.d.ts.map +1 -0
- package/dist/brand/compliance.js +378 -0
- package/dist/brand/compliance.js.map +1 -0
- package/dist/brand/forbidden-terms.d.ts +99 -0
- package/dist/brand/forbidden-terms.d.ts.map +1 -0
- package/dist/brand/forbidden-terms.js +265 -0
- package/dist/brand/forbidden-terms.js.map +1 -0
- package/dist/brand/index.d.ts +10 -0
- package/dist/brand/index.d.ts.map +1 -0
- package/dist/brand/index.js +12 -0
- package/dist/brand/index.js.map +1 -0
- package/dist/config.d.ts +325 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +492 -0
- package/dist/config.js.map +1 -0
- package/dist/delivery/index.d.ts +84 -0
- package/dist/delivery/index.d.ts.map +1 -0
- package/dist/delivery/index.js +435 -0
- package/dist/delivery/index.js.map +1 -0
- package/dist/embeddings/cache.d.ts +96 -0
- package/dist/embeddings/cache.d.ts.map +1 -0
- package/dist/embeddings/cache.js +193 -0
- package/dist/embeddings/cache.js.map +1 -0
- package/dist/embeddings/index.d.ts +152 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +337 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/openai-client.d.ts +67 -0
- package/dist/embeddings/openai-client.d.ts.map +1 -0
- package/dist/embeddings/openai-client.js +190 -0
- package/dist/embeddings/openai-client.js.map +1 -0
- package/dist/errors.d.ts +302 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +508 -0
- package/dist/errors.js.map +1 -0
- package/dist/escalation/index.d.ts +93 -0
- package/dist/escalation/index.d.ts.map +1 -0
- package/dist/escalation/index.js +436 -0
- package/dist/escalation/index.js.map +1 -0
- package/dist/extraction/deduplication.d.ts +97 -0
- package/dist/extraction/deduplication.d.ts.map +1 -0
- package/dist/extraction/deduplication.js +271 -0
- package/dist/extraction/deduplication.js.map +1 -0
- package/dist/extraction/gmail-extractor.d.ts +160 -0
- package/dist/extraction/gmail-extractor.d.ts.map +1 -0
- package/dist/extraction/gmail-extractor.js +396 -0
- package/dist/extraction/gmail-extractor.js.map +1 -0
- package/dist/extraction/gmail-token-manager.d.ts +36 -0
- package/dist/extraction/gmail-token-manager.d.ts.map +1 -0
- package/dist/extraction/gmail-token-manager.js +146 -0
- package/dist/extraction/gmail-token-manager.js.map +1 -0
- package/dist/extraction/index.d.ts +13 -0
- package/dist/extraction/index.d.ts.map +1 -0
- package/dist/extraction/index.js +20 -0
- package/dist/extraction/index.js.map +1 -0
- package/dist/extraction/pii-handler.d.ts +100 -0
- package/dist/extraction/pii-handler.d.ts.map +1 -0
- package/dist/extraction/pii-handler.js +295 -0
- package/dist/extraction/pii-handler.js.map +1 -0
- package/dist/extraction/pipeline.d.ts +94 -0
- package/dist/extraction/pipeline.d.ts.map +1 -0
- package/dist/extraction/pipeline.js +380 -0
- package/dist/extraction/pipeline.js.map +1 -0
- package/dist/extraction/quality-filter.d.ts +99 -0
- package/dist/extraction/quality-filter.d.ts.map +1 -0
- package/dist/extraction/quality-filter.js +370 -0
- package/dist/extraction/quality-filter.js.map +1 -0
- package/dist/extraction/rate-limiter.d.ts +90 -0
- package/dist/extraction/rate-limiter.d.ts.map +1 -0
- package/dist/extraction/rate-limiter.js +242 -0
- package/dist/extraction/rate-limiter.js.map +1 -0
- package/dist/extraction/state-manager.d.ts +126 -0
- package/dist/extraction/state-manager.d.ts.map +1 -0
- package/dist/extraction/state-manager.js +344 -0
- package/dist/extraction/state-manager.js.map +1 -0
- package/dist/generation/index.d.ts +75 -0
- package/dist/generation/index.d.ts.map +1 -0
- package/dist/generation/index.js +641 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +233 -0
- package/dist/index.js.map +1 -0
- package/dist/intake/index.d.ts +15 -0
- package/dist/intake/index.d.ts.map +1 -0
- package/dist/intake/index.js +19 -0
- package/dist/intake/index.js.map +1 -0
- package/dist/intake/normalizer.d.ts +163 -0
- package/dist/intake/normalizer.d.ts.map +1 -0
- package/dist/intake/normalizer.js +309 -0
- package/dist/intake/normalizer.js.map +1 -0
- package/dist/intake/postmark.d.ts +72 -0
- package/dist/intake/postmark.d.ts.map +1 -0
- package/dist/intake/postmark.js +276 -0
- package/dist/intake/postmark.js.map +1 -0
- package/dist/intake/slack.d.ts +106 -0
- package/dist/intake/slack.d.ts.map +1 -0
- package/dist/intake/slack.js +378 -0
- package/dist/intake/slack.js.map +1 -0
- package/dist/intake/twilio.d.ts +86 -0
- package/dist/intake/twilio.d.ts.map +1 -0
- package/dist/intake/twilio.js +283 -0
- package/dist/intake/twilio.js.map +1 -0
- package/dist/knowledge/index.d.ts +100 -0
- package/dist/knowledge/index.d.ts.map +1 -0
- package/dist/knowledge/index.js +516 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/knowledge/invoice-resolver.d.ts +62 -0
- package/dist/knowledge/invoice-resolver.d.ts.map +1 -0
- package/dist/knowledge/invoice-resolver.js +267 -0
- package/dist/knowledge/invoice-resolver.js.map +1 -0
- package/dist/types.d.ts +535 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +48 -0
- package/dist/types.js.map +1 -0
- package/ga-service-account.json +13 -0
- package/gmail-knowledge-migration.sql +149 -0
- package/nul +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escalation System Module
|
|
3
|
+
*
|
|
4
|
+
* Routes conversations requiring human attention:
|
|
5
|
+
* - Forbidden terms detection
|
|
6
|
+
* - Low confidence responses
|
|
7
|
+
* - Customer complaints
|
|
8
|
+
* - Explicit human requests
|
|
9
|
+
* - N-failure tracking
|
|
10
|
+
*/
|
|
11
|
+
import { createClient } from '@supabase/supabase-js';
|
|
12
|
+
import { config, getEscalationChannels, getSlaTarget } from '../config.js';
|
|
13
|
+
import { EscalationError, AllEscalationChannelsFailedError, } from '../errors.js';
|
|
14
|
+
import { createEscalationId } from '../types.js';
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Escalation Triggers
|
|
17
|
+
// =============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Determine if escalation is required based on analysis
|
|
20
|
+
*/
|
|
21
|
+
export function shouldEscalate(analysis) {
|
|
22
|
+
const reasons = [];
|
|
23
|
+
let trigger;
|
|
24
|
+
let severity = 'medium';
|
|
25
|
+
// CRITICAL: Forbidden terms
|
|
26
|
+
if (analysis.brandCompliance.status === 'blocked') {
|
|
27
|
+
trigger = 'forbidden_terms';
|
|
28
|
+
severity = 'high';
|
|
29
|
+
reasons.push('Forbidden terms detected in message');
|
|
30
|
+
}
|
|
31
|
+
// Complaint intent
|
|
32
|
+
if (analysis.intent.primary === 'complaint') {
|
|
33
|
+
trigger = trigger || 'complaint';
|
|
34
|
+
severity = 'high';
|
|
35
|
+
reasons.push('Customer complaint detected');
|
|
36
|
+
}
|
|
37
|
+
// Low confidence
|
|
38
|
+
if (analysis.overallConfidence < config.agent.confidenceThresholds.escalationTrigger) {
|
|
39
|
+
trigger = trigger || 'low_confidence';
|
|
40
|
+
reasons.push(`Low analysis confidence: ${Math.round(analysis.overallConfidence * 100)}%`);
|
|
41
|
+
}
|
|
42
|
+
// Critical urgency
|
|
43
|
+
if (analysis.sentiment.urgency === 'critical') {
|
|
44
|
+
trigger = trigger || 'high_urgency';
|
|
45
|
+
severity = 'critical';
|
|
46
|
+
reasons.push('Critical urgency level');
|
|
47
|
+
}
|
|
48
|
+
// Negative sentiment
|
|
49
|
+
if (analysis.sentiment.category === 'negative' && analysis.sentiment.score < -0.6) {
|
|
50
|
+
trigger = trigger || 'negative_sentiment';
|
|
51
|
+
reasons.push('Strong negative sentiment');
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
required: trigger !== undefined,
|
|
55
|
+
trigger,
|
|
56
|
+
severity,
|
|
57
|
+
reasons,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check for explicit human assistance request
|
|
62
|
+
*/
|
|
63
|
+
export function containsHumanRequest(text) {
|
|
64
|
+
const patterns = [
|
|
65
|
+
/speak\s+(to|with)\s+(a\s+)?(human|person|agent|representative|someone)/i,
|
|
66
|
+
/talk\s+(to|with)\s+(a\s+)?(human|person|agent|representative|someone)/i,
|
|
67
|
+
/real\s+(human|person)/i,
|
|
68
|
+
/not\s+(a\s+)?bot/i,
|
|
69
|
+
/transfer\s+me/i,
|
|
70
|
+
/escalate/i,
|
|
71
|
+
/manager/i,
|
|
72
|
+
/supervisor/i,
|
|
73
|
+
];
|
|
74
|
+
return patterns.some((p) => p.test(text));
|
|
75
|
+
}
|
|
76
|
+
// =============================================================================
|
|
77
|
+
// Escalation Service
|
|
78
|
+
// =============================================================================
|
|
79
|
+
export class EscalationService {
|
|
80
|
+
supabase;
|
|
81
|
+
constructor() {
|
|
82
|
+
this.supabase = createClient(config.supabase.url, config.supabase.serviceRoleKey);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create and route an escalation
|
|
86
|
+
*/
|
|
87
|
+
async escalate(context) {
|
|
88
|
+
// 1. Determine severity-based channels
|
|
89
|
+
const channels = getEscalationChannels(context.severity);
|
|
90
|
+
// 2. Calculate SLA target
|
|
91
|
+
const slaTargetMs = getSlaTarget(context.severity);
|
|
92
|
+
const slaTargetAt = new Date(Date.now() + slaTargetMs);
|
|
93
|
+
// 3. Create escalation record
|
|
94
|
+
const { data: escalation, error: insertError } = await this.supabase
|
|
95
|
+
.from('support_escalations')
|
|
96
|
+
.insert({
|
|
97
|
+
conversation_id: context.conversationId,
|
|
98
|
+
message_id: context.messageId,
|
|
99
|
+
customer_id: context.customerId,
|
|
100
|
+
trigger: context.trigger,
|
|
101
|
+
severity: context.severity,
|
|
102
|
+
reason: context.reason,
|
|
103
|
+
context_data: this.buildContextData(context),
|
|
104
|
+
sla_target_at: slaTargetAt.toISOString(),
|
|
105
|
+
})
|
|
106
|
+
.select('id')
|
|
107
|
+
.single();
|
|
108
|
+
if (insertError || !escalation) {
|
|
109
|
+
throw new EscalationError('Failed to create escalation record', {
|
|
110
|
+
cause: new Error(insertError?.message),
|
|
111
|
+
conversationId: context.conversationId,
|
|
112
|
+
messageId: context.messageId,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const escalationId = createEscalationId(escalation.id);
|
|
116
|
+
// 4. Send notifications to all channels
|
|
117
|
+
const deliveries = [];
|
|
118
|
+
const channelErrors = [];
|
|
119
|
+
for (const channel of channels) {
|
|
120
|
+
try {
|
|
121
|
+
const delivery = await this.notifyChannel(channel, escalationId, context);
|
|
122
|
+
deliveries.push(delivery);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
126
|
+
channelErrors.push({ channel, error: errorMessage });
|
|
127
|
+
deliveries.push({
|
|
128
|
+
channel,
|
|
129
|
+
success: false,
|
|
130
|
+
error: errorMessage,
|
|
131
|
+
timestamp: new Date().toISOString(),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// 5. Update escalation record with notification results
|
|
136
|
+
await this.supabase
|
|
137
|
+
.from('support_escalations')
|
|
138
|
+
.update({
|
|
139
|
+
notification_channels: deliveries.map((d) => ({
|
|
140
|
+
channel: d.channel,
|
|
141
|
+
sent_at: d.timestamp,
|
|
142
|
+
message_id: d.messageId,
|
|
143
|
+
success: d.success,
|
|
144
|
+
error: d.error,
|
|
145
|
+
})),
|
|
146
|
+
})
|
|
147
|
+
.eq('id', escalationId);
|
|
148
|
+
// 6. Update conversation status
|
|
149
|
+
await this.supabase
|
|
150
|
+
.from('support_conversations')
|
|
151
|
+
.update({
|
|
152
|
+
status: 'escalated',
|
|
153
|
+
is_handled_by_ai: false,
|
|
154
|
+
state_data: {
|
|
155
|
+
status: 'escalated',
|
|
156
|
+
escalatedAt: new Date().toISOString(),
|
|
157
|
+
escalationId,
|
|
158
|
+
reason: context.reason,
|
|
159
|
+
},
|
|
160
|
+
})
|
|
161
|
+
.eq('id', context.conversationId);
|
|
162
|
+
// 7. Check if all channels failed
|
|
163
|
+
const allSucceeded = deliveries.every((d) => d.success);
|
|
164
|
+
const anySucceeded = deliveries.some((d) => d.success);
|
|
165
|
+
if (!anySucceeded) {
|
|
166
|
+
// Use database fallback
|
|
167
|
+
await this.createDatabaseFallback(escalationId, context);
|
|
168
|
+
throw new AllEscalationChannelsFailedError(channelErrors, {
|
|
169
|
+
escalationId,
|
|
170
|
+
conversationId: context.conversationId,
|
|
171
|
+
messageId: context.messageId,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
escalationId,
|
|
176
|
+
conversationId: context.conversationId,
|
|
177
|
+
deliveries,
|
|
178
|
+
allSucceeded,
|
|
179
|
+
adminUrl: `${config.escalation.adminBaseUrl}/support/escalations/${escalationId}`,
|
|
180
|
+
createdAt: new Date().toISOString(),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Build context data for escalation record
|
|
185
|
+
*/
|
|
186
|
+
buildContextData(context) {
|
|
187
|
+
return {
|
|
188
|
+
trigger: context.trigger,
|
|
189
|
+
severity: context.severity,
|
|
190
|
+
reason: context.reason,
|
|
191
|
+
analysisResult: context.analysisResult
|
|
192
|
+
? {
|
|
193
|
+
intent: context.analysisResult.intent,
|
|
194
|
+
sentiment: context.analysisResult.sentiment,
|
|
195
|
+
brandCompliance: context.analysisResult.brandCompliance,
|
|
196
|
+
overallConfidence: context.analysisResult.overallConfidence,
|
|
197
|
+
escalationReasons: context.analysisResult.escalationReasons,
|
|
198
|
+
}
|
|
199
|
+
: undefined,
|
|
200
|
+
attemptedResponses: context.attemptedResponses?.length ?? 0,
|
|
201
|
+
knowledgeContext: context.knowledgeContext
|
|
202
|
+
? {
|
|
203
|
+
hasCustomerData: !!context.knowledgeContext.customer,
|
|
204
|
+
hasOrderData: !!(context.knowledgeContext.orders?.length ?? 0 > 0),
|
|
205
|
+
hasProductData: !!(context.knowledgeContext.products?.length ?? 0 > 0),
|
|
206
|
+
}
|
|
207
|
+
: undefined,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Send notification via a specific channel
|
|
212
|
+
*/
|
|
213
|
+
async notifyChannel(channel, escalationId, context) {
|
|
214
|
+
const timestamp = new Date().toISOString();
|
|
215
|
+
// Check if channel is configured
|
|
216
|
+
if (!this.isChannelConfigured(channel)) {
|
|
217
|
+
return {
|
|
218
|
+
channel,
|
|
219
|
+
success: false,
|
|
220
|
+
error: `Channel ${channel} not configured`,
|
|
221
|
+
timestamp,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// Build notification content
|
|
225
|
+
const content = this.buildNotificationContent(escalationId, context);
|
|
226
|
+
switch (channel) {
|
|
227
|
+
case 'email':
|
|
228
|
+
return this.sendEmailNotification(escalationId, context, content);
|
|
229
|
+
case 'sms':
|
|
230
|
+
return this.sendSmsNotification(escalationId, context, content);
|
|
231
|
+
case 'slack':
|
|
232
|
+
return this.sendSlackNotification(escalationId, context, content);
|
|
233
|
+
default:
|
|
234
|
+
return {
|
|
235
|
+
channel,
|
|
236
|
+
success: false,
|
|
237
|
+
error: `Unknown channel: ${channel}`,
|
|
238
|
+
timestamp,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Check if a channel is properly configured
|
|
244
|
+
*/
|
|
245
|
+
isChannelConfigured(channel) {
|
|
246
|
+
switch (channel) {
|
|
247
|
+
case 'email':
|
|
248
|
+
return !!config.postmark.serverToken;
|
|
249
|
+
case 'sms':
|
|
250
|
+
return !!config.twilio.accountSid && !!config.twilio.authToken;
|
|
251
|
+
case 'slack':
|
|
252
|
+
return !!config.slack.botToken;
|
|
253
|
+
default:
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Build notification content
|
|
259
|
+
*/
|
|
260
|
+
buildNotificationContent(escalationId, context) {
|
|
261
|
+
const severityEmoji = {
|
|
262
|
+
critical: '🚨',
|
|
263
|
+
high: '⚠️',
|
|
264
|
+
medium: '📋',
|
|
265
|
+
low: 'ℹ️',
|
|
266
|
+
}[context.severity] || '📋';
|
|
267
|
+
const subject = `${severityEmoji} [${context.severity.toUpperCase()}] Support Escalation - ${context.trigger}`;
|
|
268
|
+
const body = `
|
|
269
|
+
Support Escalation #${escalationId}
|
|
270
|
+
${'='.repeat(40)}
|
|
271
|
+
|
|
272
|
+
Severity: ${context.severity.toUpperCase()}
|
|
273
|
+
Trigger: ${context.trigger}
|
|
274
|
+
Reason: ${context.reason}
|
|
275
|
+
|
|
276
|
+
Customer: ${context.knowledgeContext?.customer?.companyName || 'Unknown'}
|
|
277
|
+
Conversation ID: ${context.conversationId}
|
|
278
|
+
|
|
279
|
+
View in Admin Portal:
|
|
280
|
+
${config.escalation.adminBaseUrl}/support/escalations/${escalationId}
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
This escalation requires human attention.
|
|
284
|
+
`.trim();
|
|
285
|
+
const shortBody = `${severityEmoji} Escalation: ${context.trigger}. ${context.reason}. View: ${config.escalation.adminBaseUrl}/support/escalations/${escalationId}`;
|
|
286
|
+
return { subject, body, shortBody };
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Send email notification (placeholder - would use Postmark)
|
|
290
|
+
*/
|
|
291
|
+
async sendEmailNotification(escalationId, context, content) {
|
|
292
|
+
// This would integrate with Postmark
|
|
293
|
+
// For now, return success (actual implementation in delivery module)
|
|
294
|
+
return {
|
|
295
|
+
channel: 'email',
|
|
296
|
+
success: true,
|
|
297
|
+
messageId: `email-${escalationId}`,
|
|
298
|
+
timestamp: new Date().toISOString(),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Send SMS notification (placeholder - would use Twilio)
|
|
303
|
+
*/
|
|
304
|
+
async sendSmsNotification(escalationId, context, content) {
|
|
305
|
+
// This would integrate with Twilio
|
|
306
|
+
return {
|
|
307
|
+
channel: 'sms',
|
|
308
|
+
success: true,
|
|
309
|
+
messageId: `sms-${escalationId}`,
|
|
310
|
+
timestamp: new Date().toISOString(),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Send Slack notification (placeholder - would use Slack API)
|
|
315
|
+
*/
|
|
316
|
+
async sendSlackNotification(escalationId, context, content) {
|
|
317
|
+
// This would integrate with Slack API
|
|
318
|
+
return {
|
|
319
|
+
channel: 'slack',
|
|
320
|
+
success: true,
|
|
321
|
+
messageId: `slack-${escalationId}`,
|
|
322
|
+
timestamp: new Date().toISOString(),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Create database fallback when all notification channels fail
|
|
327
|
+
*/
|
|
328
|
+
async createDatabaseFallback(escalationId, context) {
|
|
329
|
+
// Mark escalation as requiring attention via database
|
|
330
|
+
await this.supabase
|
|
331
|
+
.from('support_escalations')
|
|
332
|
+
.update({
|
|
333
|
+
context_data: {
|
|
334
|
+
...this.buildContextData(context),
|
|
335
|
+
allChannelsFailed: true,
|
|
336
|
+
requiresDatabaseFallback: true,
|
|
337
|
+
fallbackCreatedAt: new Date().toISOString(),
|
|
338
|
+
},
|
|
339
|
+
})
|
|
340
|
+
.eq('id', escalationId);
|
|
341
|
+
// Log for monitoring
|
|
342
|
+
console.error(`CRITICAL: All escalation channels failed for escalation ${escalationId}. Database fallback created.`);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Track response attempt failures
|
|
346
|
+
*/
|
|
347
|
+
async trackResponseAttempt(conversationId, success, error) {
|
|
348
|
+
// Get current attempt count from conversation
|
|
349
|
+
const { data: conversation } = await this.supabase
|
|
350
|
+
.from('support_conversations')
|
|
351
|
+
.select('metadata')
|
|
352
|
+
.eq('id', conversationId)
|
|
353
|
+
.single();
|
|
354
|
+
const currentAttempts = conversation?.metadata?.responseAttempts ?? 0;
|
|
355
|
+
const newAttemptCount = currentAttempts + 1;
|
|
356
|
+
// Update attempt count
|
|
357
|
+
await this.supabase
|
|
358
|
+
.from('support_conversations')
|
|
359
|
+
.update({
|
|
360
|
+
metadata: {
|
|
361
|
+
...conversation?.metadata,
|
|
362
|
+
responseAttempts: newAttemptCount,
|
|
363
|
+
lastAttemptSuccess: success,
|
|
364
|
+
lastAttemptError: error,
|
|
365
|
+
lastAttemptAt: new Date().toISOString(),
|
|
366
|
+
},
|
|
367
|
+
})
|
|
368
|
+
.eq('id', conversationId);
|
|
369
|
+
return {
|
|
370
|
+
shouldEscalate: !success && newAttemptCount >= config.agent.maxResponseAttempts,
|
|
371
|
+
attemptCount: newAttemptCount,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Detect human takeover (human responded in escalated conversation)
|
|
376
|
+
*/
|
|
377
|
+
async detectHumanTakeover(conversationId) {
|
|
378
|
+
// Check if there are human-sent messages after escalation
|
|
379
|
+
const { data } = await this.supabase
|
|
380
|
+
.from('support_messages')
|
|
381
|
+
.select('id')
|
|
382
|
+
.eq('conversation_id', conversationId)
|
|
383
|
+
.eq('direction', 'outbound')
|
|
384
|
+
.order('created_at', { ascending: false })
|
|
385
|
+
.limit(1);
|
|
386
|
+
// If there's a recent outbound message from a human, mark as taken over
|
|
387
|
+
return (data?.length ?? 0) > 0;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Resolve an escalation
|
|
391
|
+
*/
|
|
392
|
+
async resolveEscalation(escalationId, resolution) {
|
|
393
|
+
await this.supabase
|
|
394
|
+
.from('support_escalations')
|
|
395
|
+
.update({
|
|
396
|
+
response_received_at: new Date().toISOString(),
|
|
397
|
+
response_action: resolution.action,
|
|
398
|
+
resolution_notes: resolution.notes,
|
|
399
|
+
assigned_to: resolution.responderId,
|
|
400
|
+
})
|
|
401
|
+
.eq('id', escalationId);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// =============================================================================
|
|
405
|
+
// Singleton and Utility Functions
|
|
406
|
+
// =============================================================================
|
|
407
|
+
let escalationService = null;
|
|
408
|
+
export function getEscalationService() {
|
|
409
|
+
if (!escalationService) {
|
|
410
|
+
escalationService = new EscalationService();
|
|
411
|
+
}
|
|
412
|
+
return escalationService;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Create and route an escalation
|
|
416
|
+
*/
|
|
417
|
+
export async function escalateConversation(context) {
|
|
418
|
+
return getEscalationService().escalate(context);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Build escalation context from analysis
|
|
422
|
+
*/
|
|
423
|
+
export function buildEscalationContext(message, analysis, knowledge, trigger, severity, reason, attemptedResponses) {
|
|
424
|
+
return {
|
|
425
|
+
conversationId: analysis.conversationId,
|
|
426
|
+
messageId: message.id,
|
|
427
|
+
customerId: knowledge.customer?.id,
|
|
428
|
+
trigger,
|
|
429
|
+
severity,
|
|
430
|
+
reason,
|
|
431
|
+
analysisResult: analysis,
|
|
432
|
+
attemptedResponses,
|
|
433
|
+
knowledgeContext: knowledge,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/escalation/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deduplication Module
|
|
3
|
+
*
|
|
4
|
+
* Detects duplicate Q&A pairs using content hashing and optional embedding similarity.
|
|
5
|
+
* Prevents storage of near-identical entries in the knowledge base.
|
|
6
|
+
*/
|
|
7
|
+
export interface DeduplicationResult {
|
|
8
|
+
isDuplicate: boolean;
|
|
9
|
+
duplicateId?: string;
|
|
10
|
+
similarity?: number;
|
|
11
|
+
method: 'hash' | 'embedding' | 'none';
|
|
12
|
+
contentHash: string;
|
|
13
|
+
}
|
|
14
|
+
export interface DeduplicationConfig {
|
|
15
|
+
similarityThreshold: number;
|
|
16
|
+
useEmbeddingSimilarity: boolean;
|
|
17
|
+
supabaseUrl: string;
|
|
18
|
+
supabaseServiceRoleKey: string;
|
|
19
|
+
}
|
|
20
|
+
export interface QAContent {
|
|
21
|
+
questionSubject?: string;
|
|
22
|
+
questionText: string;
|
|
23
|
+
responseText: string;
|
|
24
|
+
}
|
|
25
|
+
export declare class DeduplicationService {
|
|
26
|
+
private config;
|
|
27
|
+
private supabase;
|
|
28
|
+
private hashCache;
|
|
29
|
+
constructor(config: DeduplicationConfig);
|
|
30
|
+
/**
|
|
31
|
+
* Generate content hash for a Q&A pair
|
|
32
|
+
*
|
|
33
|
+
* Hash is based on normalized question + response text.
|
|
34
|
+
* Subject is not included as it varies more between duplicates.
|
|
35
|
+
*/
|
|
36
|
+
generateContentHash(content: QAContent): string;
|
|
37
|
+
/**
|
|
38
|
+
* Check if content is a duplicate
|
|
39
|
+
*/
|
|
40
|
+
checkDuplicate(content: QAContent): Promise<DeduplicationResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Check for hash match in database
|
|
43
|
+
*/
|
|
44
|
+
private checkHashInDatabase;
|
|
45
|
+
/**
|
|
46
|
+
* Check for similar entries using embedding similarity
|
|
47
|
+
* Note: Requires the entry to have an embedding
|
|
48
|
+
*/
|
|
49
|
+
private checkEmbeddingSimilarity;
|
|
50
|
+
/**
|
|
51
|
+
* Add hash to local cache (for batch operations)
|
|
52
|
+
*/
|
|
53
|
+
addToCache(contentHash: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Clear local hash cache
|
|
56
|
+
*/
|
|
57
|
+
clearCache(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Load existing hashes from database into cache
|
|
60
|
+
*/
|
|
61
|
+
loadHashCache(limit?: number): Promise<number>;
|
|
62
|
+
/**
|
|
63
|
+
* Batch check for duplicates
|
|
64
|
+
*/
|
|
65
|
+
checkBatchDuplicates(contents: QAContent[]): Promise<Map<string, DeduplicationResult>>;
|
|
66
|
+
/**
|
|
67
|
+
* Normalize text for consistent hashing
|
|
68
|
+
*/
|
|
69
|
+
private normalizeText;
|
|
70
|
+
/**
|
|
71
|
+
* Get cache statistics
|
|
72
|
+
*/
|
|
73
|
+
getCacheStats(): {
|
|
74
|
+
size: number;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Update configuration
|
|
78
|
+
*/
|
|
79
|
+
updateConfig(config: Partial<DeduplicationConfig>): void;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get singleton deduplication service instance
|
|
83
|
+
*/
|
|
84
|
+
export declare function getDeduplicationService(): DeduplicationService;
|
|
85
|
+
/**
|
|
86
|
+
* Reset the singleton (for testing)
|
|
87
|
+
*/
|
|
88
|
+
export declare function resetDeduplicationService(): void;
|
|
89
|
+
/**
|
|
90
|
+
* Quick content hash generation
|
|
91
|
+
*/
|
|
92
|
+
export declare function hashContent(content: QAContent): string;
|
|
93
|
+
/**
|
|
94
|
+
* Quick duplicate check
|
|
95
|
+
*/
|
|
96
|
+
export declare function isDuplicate(content: QAContent): Promise<boolean>;
|
|
97
|
+
//# sourceMappingURL=deduplication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deduplication.d.ts","sourceRoot":"","sources":["../../src/extraction/deduplication.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAElC,mBAAmB,EAAE,MAAM,CAAC;IAG5B,sBAAsB,EAAE,OAAO,CAAC;IAGhC,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAeD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,QAAQ,CAAiB;IAGjC,OAAO,CAAC,SAAS,CAA0B;gBAE/B,MAAM,EAAE,mBAAmB;IAKvC;;;;;OAKG;IACH,mBAAmB,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM;IAY/C;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA6CtE;;OAEG;YACW,mBAAmB;IAiBjC;;;OAGG;YACW,wBAAwB;IAUtC;;OAEG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACG,aAAa,CAAC,KAAK,GAAE,MAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB3D;;OAEG;IACG,oBAAoB,CACxB,QAAQ,EAAE,SAAS,EAAE,GACpB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAyE5C;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,aAAa,IAAI;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAIjC;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI;CAGzD;AAQD;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,CAkB9D;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAMD;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAGtE"}
|