apps/web/src/lib/aiDisabledWorkflow.ts
1import {
2 confirmedHandoff,
3 emailFromMessage,
4 isHandoffCancellation,
5} from '@local/ai/components/workflows'
6import {
7 type AiCapabilities,
8 aiToolEnabled,
9} from '@local/config/ai-capabilities'
10import {
11 acceptedAgendaEmail,
12 acceptedSalesOffer,
13 needsAgendaInterests,
14 requestsTicketBooking,
15} from './aiFlows'
16import { supportEmail } from './aiResponsePolicy'
17
18type Turn = { role: 'assistant' | 'user'; text: string }
19
20/** Resolve a short confirmation from the latest relevant topic, not a generic offer alone. */
21function contactContext(message: string, history: Turn[]): string {
22 return (
23 [
24 message,
25 ...history
26 .slice()
27 .reverse()
28 .map((turn) => turn.text),
29 ].find((text) =>
30 /\b(?:sponsor(?:ship|ing)?|exhibit(?:or|ing)?|tickets?|passes|badges?|booking|agenda)\b/i.test(
31 text,
32 ),
33 ) || message
34 )
35}
36
37/** Disabled handoffs are ordinary chat replies, without collection, consent or a model call. */
38export function disabledWorkflowReply(
39 config: AiCapabilities,
40 message: string,
41 history: Turn[],
42): string | undefined {
43 // Cancellation is owned by the existing cancellation guard, regardless of selector mode.
44 if (isHandoffCancellation(history, message)) return undefined
45 // Reject entry into disabled workflows, rather than collecting details for
46 // an action the assistant cannot perform. Factual questions remain available.
47 if (!aiToolEnabled(config, 'submitLead')) {
48 if (requestsTicketBooking(message, history))
49 return 'For help buying tickets, please email [support@unleash.ai](mailto:support@unleash.ai). I can answer questions about the passes here, but I can’t submit a ticket enquiry through the chat.'
50 if (
51 /\b(?:(?:i|we)(?: would|['’]d)? (?:want|need|like) to|help (?:me|us)|(?:can|could) (?:i|we)) (?:sponsor|exhibit)\b|\b(?:put|connect) (?:me|us) (?:in touch|with).{0,30}(?:sponsor|exhibit)|\b(?:sponsorship|exhibitor) (?:enquiry|inquiry|request)\b/i.test(
52 message,
53 )
54 )
55 return 'For sponsorship or exhibiting, please email [customersuccess@unleash.ai](mailto:customersuccess@unleash.ai). I can answer questions about the opportunities here, but I can’t submit an enquiry through the chat.'
56 }
57 if (
58 !aiToolEnabled(config, 'buildAgenda') &&
59 (needsAgendaInterests(history, message, []) ||
60 /\b(?:build|create|make|plan|personalize|personalise)\b.{0,35}\b(?:agenda|schedule)\b/i.test(
61 message,
62 ))
63 )
64 return 'I can’t build a personal agenda in this chat right now. You can browse the event’s agenda to choose your sessions.'
65 const previous =
66 history.findLast((turn) => turn.role === 'assistant')?.text || ''
67 const context = contactContext(message, history)
68 const chatOnly =
69 /\b(?:here|in (?:the )?chat|on screen)\b|\b(?:not|no|without)\s+(?:by\s+|an?\s+)?email\b/i.test(
70 message,
71 )
72 const agendaContext =
73 /email[^\n]*agenda|agenda[^\n]*email|email the selected sessions/i.test(
74 previous,
75 ) ||
76 (/\b(?:name|email address)\b/i.test(previous) && /\bagenda\b/i.test(context))
77 const emailRequest =
78 !chatOnly &&
79 (acceptedAgendaEmail(history, message) ||
80 (agendaContext &&
81 (confirmedHandoff(history, message) ||
82 Boolean(emailFromMessage(message)))) ||
83 /\bemail\b.{0,35}\bagenda\b|\bagenda\b.{0,35}\bemail\b/i.test(message))
84 if (!aiToolEnabled(config, 'sendAgendaByEmail') && emailRequest) {
85 return 'Emailing agendas is currently unavailable. Nothing has been sent. You can keep or copy your selected agenda from this chat.'
86 }
87 if (
88 !aiToolEnabled(config, 'submitLead') &&
89 !agendaContext &&
90 (acceptedSalesOffer(history, message) ||
91 confirmedHandoff(history, message) ||
92 (/who should.*contact|(?:name|work email).*enquiry|name and work email/i.test(
93 previous,
94 ) &&
95 Boolean(emailFromMessage(message))))
96 ) {
97 return `Contact requests cannot currently be submitted through this chat. Nothing has been sent. Please contact ${supportEmail(context)} directly.`
98 }
99 return undefined
100}
101