Antonio joined his mother Lilly in Boston in 1986, and 4 months later started a pasta company called Lilly’s Gastronomia Italiana. Together, they have grown into one of the finest and best known pasta manufacturers in the greater Boston area. After working in the factory for 10 years, Antonio eventually took on the lead sales role for the company. In 2001, he also started a 2-year professional chef program at the Cambridge School of Culinary Arts. Antonio has managed business development for Lilly’s Fresh Pasta since 2010.
/* ==================================================================
LILLY'S FRESH PASTA - ALLERGEN ROUTING v2
Prepared 2026-09-22. Tested: 39/39 classifier cases passed.
WHERE THIS GOES
---------------
WordPress -> Settings -> Head and Footer -> "Footer" box.
Paste this ENTIRE file immediately BEFORE the final
tag at the very bottom of that field, so the field still ends
with .
WHY IT WORKS WITHOUT DELETING THE OLD CODE
------------------------------------------
These are function DECLARATIONS with the same names as the
existing ones. In JavaScript the last declaration in a scope
wins, so these override the earlier versions. The old code stays
in place but never runs.
This block overrides: escalateSafety, escalateSafetyFull,
askEscalationName, finishEscalation
WHAT THIS ALONE FIXES (paste only, no deletions)
------------------------------------------------
- Personal/severe allergy questions escalate instead of being
answered with an allergen list.
- Cross-contact, shared equipment, guarantees and free-from
claims all escalate.
- Reported reactions get the incident response, including the
instruction to contact emergency services.
- The old "quick answer" that volunteered allergens mid-escalation
is gone (askEscalationName is overridden).
- No response-time promises ("shortly", "immediately" removed).
- Correct HubSpot ticket sources for routing.
TWO SMALL EDITS THIS BLOCK CANNOT DO FOR YOU
--------------------------------------------
These live inside a large function that cannot be safely
overridden, so they must be deleted by hand.
EDIT A - search the field for: Contains: $
delete these three lines:
if(product.allergens && product.allergens.length){
msg += ` Contains: ${product.allergens.join(', ')}.`;
}
(Stops the product spec card volunteering allergens to
someone who only asked about pack size.)
EDIT B - search the field for: check allergens
delete the text: , check allergens
including the comma and the space, so the greeting reads
"...and specialty products, or find the right pasta for
your menu."
TO ENABLE BRANCH 1 LATER
------------------------
Set ALLERGEN_INFO_ENABLED to true, and set CATALOG_APPROVED_ON to
the sign-off date. Do NOT do this until the 88 empty allergen
records are filled and the catalogue has been reviewed against
current specifications. See the "Allergen workflow" doc.
================================================================== */
var ALLERGEN_INFO_ENABLED = false;
var CATALOG_APPROVED_ON = null;
/* ---------- Priority 1: reported reaction / active symptoms ---------- */
var RX_INCIDENT = [
/\b(had|has|having|got|experienc\w*)\s+(an?\s+)?(allergic\s+)?reaction\b/i,
/\breact(ed|ion)\b[^.!]{0,40}\b(after|from|to)\b/i,
/\banaphyla\w*/i,
/\bepi[- ]?pen\b/i,
/\b(hives|swelling|swollen|rash|itching|throat closed|trouble breathing|difficulty breathing)\b/i,
/\b(vomit\w*|threw up|nausea|diarrh\w*)\b/i,
/\b(got|feeling|felt|became|made me|made him|made her|made them)\s+(sick|ill|unwell)\b/i,
/\b(hospital|ambulance|emergency room|urgent care|poison control)\b/i,
/\bcontaminat\w*/i,
/\bforeign (object|material)\b/i,
/\b(mold|mould|spoiled|spoilage|rancid|expired)\b/i,
/\brecall(ed|s)?\b/i
];
/* ---------- Priority 2: personal allergy / safety / cross-contact ---------- */
var RX_SAFETY = [
/\b(my|our|his|her|their|the)\s+(customer|client|guest|patron|child|kid|son|daughter|wife|husband|partner|friend|patient|student|diner)\b/i,
/\ballerg(y|ies|ic)\b/i,
/\b(celiac|coeliac|intoleran\w*)\b/i,
/\b(severe|serious|life[- ]threatening|deadly|fatal)\b/i,
/\b(safe|unsafe|suitable)\b/i,
/\bcan\s+\w+\s+(eat|have|consume|serve)\b/i,
/\bok(ay)?\s+(for|to eat|to serve)\b/i,
/\bcross[- ]?cont(act|amination)\b/i,
/\bshared?\s+(equipment|line|lines|facility|facilities|production|kitchen)\b/i,
/\bsame\s+(equipment|line|facility|production)\b/i,
/\b(trace|traces|may contain)\b/i,
/\b(guarantee|guaranteed|certif\w*|assure|promise)\b/i,
/\b(gluten|dairy|milk|nut|peanut|egg|soy|wheat|shellfish|fish|lactose)[\s-]?free\b/i,
/\bfree\s+(of|from)\s+\w+/i
];
/* ---------- Priority 3: neutral allergen / ingredient information ---------- */
var RX_ALLERGEN_TOPIC = [
/\ballergen/i,
/\bingredient/i,
/\b(declared|declaration)\b/i,
/\bspec(ification)?\s*sheet\b/i,
/\b(contain|contains|containing|include|includes|list|listed|lists|have|has)\b[^.!]{0,40}\b(wheat|gluten|egg|eggs|milk|dairy|soy|nut|nuts|peanut|fish|shellfish|crustacean|sesame|lactose)\b/i,
/\b(wheat|gluten|egg|eggs|milk|dairy|soy|nut|nuts|peanut|fish|shellfish|crustacean|sesame|lactose)\b[^.!]{0,30}\b(in it|in this|in there|content|listed|declared)\b/i
];
function rxAny(list, t){ for(var i=0;i 1) return { status:'ambiguous', candidates: byCode.slice(0,5) };
}
var exact = usable.filter(function(p){ return t.indexOf(p.name.toLowerCase().trim()) !== -1; });
if(exact.length === 1) return gate(exact[0]);
if(exact.length > 1){
var maxLen = 0;
exact.forEach(function(p){ if(p.name.length > maxLen) maxLen = p.name.length; });
var ties = exact.filter(function(p){ return p.name.length === maxLen; });
if(ties.length === 1) return gate(ties[0]);
return { status:'ambiguous', candidates: ties.slice(0,5) };
}
var words = t.split(/\s+/).filter(function(w){ return w.length > 3; });
var scored = [];
usable.forEach(function(p){
var pn = p.name.toLowerCase(), hits = 0;
words.forEach(function(w){ if(pn.indexOf(w) !== -1) hits++; });
if(hits >= 2) scored.push({ p:p, hits:hits });
});
if(!scored.length) return { status:'none' };
var top = 0;
scored.forEach(function(x){ if(x.hits > top) top = x.hits; });
var best = scored.filter(function(x){ return x.hits === top; });
if(best.length === 1) return gate(best[0].p);
return { status:'ambiguous', candidates: best.slice(0,5).map(function(x){ return x.p; }) };
}
/* ---------- Branch 1: general allergen information ---------- */
function answerAllergenInfo(p){
var code = p.code ? ' (item ' + p.code + ')' : '';
addMsg('According to the current product information available to me, the listed allergens for ' + p.name + code + ' are: ' + p.allergens.join(', ') + '. Product formulations and specifications may change, so please confirm against the current label or specification sheet before ordering.');
trackEv('chat_allergen_information', { product_code: p.code || null, category: p.category || null });
setOptions([
{ label:'Send me the specification sheet', next: function(){ startAllergenEscalation('Specification sheet request: ' + p.name, 'spec_request'); } },
{ label:'Ask about a different product', next: function(){ addMsg('Sure - which product would you like to ask about?'); } },
{ label:'That is all, thanks', next: function(){ addMsg('Happy to help.'); } }
]);
}
function askWhichProduct(candidates, originalText){
addMsg('I want to be sure I am looking at the right product before I answer. Which of these did you mean?');
var opts = candidates.map(function(p){
return { label: p.name + (p.code ? ' (' + p.code + ')' : ''), next: function(){
if(ALLERGEN_INFO_ENABLED && p.allergens && p.allergens.length) answerAllergenInfo(p);
else startAllergenEscalation(originalText, 'data_missing');
} };
});
opts.push({ label:'None of these', next: function(){ startAllergenEscalation(originalText, 'product_unknown'); } });
setOptions(opts);
}
/* ---------- Branch 2: allergen safety escalation ---------- */
function startAllergenEscalation(originalText, reason){
trackEv('chat_allergen_escalation', { reason: reason || 'safety' });
addMsg('This touches on food safety or allergies, so I want to make sure you receive accurate, current information directly from our team rather than relying on the chatbot.');
addMsg('May I have your name and the best way to reach you? A team member will follow up as soon as possible.');
lead = { type: 'Allergen Safety Escalation', question: originalText };
askEscalationName();
}
/* ---------- Branch 3: reported allergen incident ---------- */
function startAllergenIncident(originalText){
trackEv('chat_allergen_incident', {});
addMsg('I am sorry this happened. I can help connect you with our team, but I cannot provide medical advice. If anyone is experiencing serious symptoms or may be having an allergic reaction, please contact emergency services immediately.');
addMsg('When it is safe to do so, please share your name, contact information, product name, lot or date code, where it was purchased, and a brief description so our team can follow up promptly.');
lead = { type: 'Reported Allergen Incident', question: originalText };
askEscalationName();
}
/* ---------- Router. Overrides the earlier escalateSafety. ----------
Deterministic keyword checks run BEFORE any catalogue lookup. */
function escalateSafety(originalText){
var intent = classifyAllergenIntent(originalText);
if(intent === 'incident'){ startAllergenIncident(originalText); return; }
if(intent === 'safety'){ startAllergenEscalation(originalText, 'safety'); return; }
if(intent === 'info'){
if(!ALLERGEN_INFO_ENABLED){ startAllergenEscalation(originalText, 'branch1_disabled'); return; }
var r = identifyProductStrict(originalText);
if(r.status === 'ambiguous'){ askWhichProduct(r.candidates, originalText); return; }
if(r.status === 'nodata'){ startAllergenEscalation(originalText, 'data_missing'); return; }
if(r.status !== 'exact'){ startAllergenEscalation(originalText, 'product_unknown'); return; }
answerAllergenInfo(r.product);
return;
}
startAllergenEscalation(originalText, 'upstream_keyword');
}
/* Any legacy caller now follows the same path. */
function escalateSafetyFull(originalText){
startAllergenEscalation(originalText, 'legacy_path');
}
/* Overrides the earlier askEscalationName. Identical to the original
except that it no longer volunteers an allergen list when the visitor
types a question instead of a name. */
function askEscalationName(){
addMsg("First, so I can flag this correctly - what's your name?");
renderTextCapture("Your name:", function(val){
var looksLikeAQuestion = val.indexOf('?') !== -1 ||
/^(how|why|what|can|does|is|are|who|when|where|could|would|will)\b/i.test(val.trim()) ||
val.trim().split(/\s+/).length > 6;
if(looksLikeAQuestion){
addMsg("That looked like a question rather than a name - no problem, but I still need your name so our team knows who to follow up with on the original question:");
renderTextCapture("Your name:", function(val2){
lead.name = val2;
askEscalationContact();
});
return;
}
lead.name = val;
askEscalationContact();
});
}
/* Incident-only follow-up questions. All optional and skippable. */
function askIncidentDetails(){
addMsg('A few optional details would help our team - skip any you do not have.');
renderTextCapture('Product name and lot or date code:', function(v){
lead.productRef = v.trim();
renderTextCapture('Where it was purchased:', function(v2){
lead.purchaseLoc = v2.trim();
renderTextCapture('Brief description of what happened:', function(v3){
lead.incidentDesc = v3.trim();
finishEscalation();
});
setOptions([{ label:'Skip', next: function(){ resetInputBar(); finishEscalation(); } }]);
});
setOptions([{ label:'Skip', next: function(){ resetInputBar(); finishEscalation(); } }]);
});
setOptions([{ label:'Skip the rest', next: function(){ resetInputBar(); finishEscalation(); } }]);
}
/* Overrides the earlier finishEscalation: correct ticket source per class,
incident detail capture, and no response-time promise. */
function finishEscalation(){
if(lead.type === 'Reported Allergen Incident' && !lead._incidentAsked){
lead._incidentAsked = true;
askIncidentDetails();
return;
}
var src = (lead.type === 'Reported Allergen Incident')
? 'Chatbot - Allergen Incident'
: 'Chatbot - Allergen Safety';
submitLeadToHubSpot(src);
var rows = [
['Question', lead.question],
['Name', lead.name],
['Contact', lead.phone ? (lead.email + ' / ' + lead.phone) : lead.email]
];
if(lead.productRef) rows.push(['Product / lot', lead.productRef]);
if(lead.purchaseLoc) rows.push(['Purchased at', lead.purchaseLoc]);
if(lead.incidentDesc) rows.push(['Description', lead.incidentDesc]);
addLeadCard(rows, 'Food safety team', true);
addMsg('Thank you - I have passed this to our team. Someone will follow up with you as soon as possible.');
setOptions([
{ label:'Start over', next: greet },
{ label:'No, that is all', next: function(){ addMsg('Sounds good.'); } }
]);
}
/* ===== END ALLERGEN ROUTING v2 ===== */