@webless/agent 0.11.0 → 0.12.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/{chunk-NW37MHU7.js → chunk-EVWZF34F.js} +1103 -477
- package/dist/chunk-EVWZF34F.js.map +1 -0
- package/dist/embed.cjs +1132 -506
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +64 -0
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +3 -2
- package/dist/embed.d.ts +3 -2
- package/dist/embed.js +1 -1
- package/dist/index.cjs +283 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +277 -6
- package/dist/index.js.map +1 -1
- package/dist/{panel-controller-rxR75tRa.d.cts → panel-controller-CYoivbYB.d.cts} +173 -1
- package/dist/{panel-controller-rxR75tRa.d.ts → panel-controller-CYoivbYB.d.ts} +173 -1
- package/dist/presentation.d.cts +2 -1
- package/dist/presentation.d.ts +2 -1
- package/dist/react.cjs +1131 -505
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +64 -0
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +126 -4
- package/dist/react.d.ts +126 -4
- package/dist/react.js +1 -1
- package/dist/{types-B2j6hbI5.d.cts → types-4lTJW5mp.d.cts} +133 -1
- package/dist/{types-B2j6hbI5.d.ts → types-4lTJW5mp.d.ts} +133 -1
- package/package.json +3 -2
- package/scripts/sync-handoff-contract.mjs +18 -0
- package/dist/chunk-NW37MHU7.js.map +0 -1
|
@@ -138,16 +138,16 @@ function parseStep(value) {
|
|
|
138
138
|
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
139
139
|
return null;
|
|
140
140
|
}
|
|
141
|
-
const
|
|
141
|
+
const id2 = boundedString(value.id, 80);
|
|
142
142
|
const label = boundedString(value.label, 160);
|
|
143
143
|
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
144
|
-
if (!
|
|
144
|
+
if (!id2 || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(id2) || !label || value.description !== void 0 && !description || !Array.isArray(value.fieldPaths) || value.fieldPaths.length < 1 || value.fieldPaths.length > 32 || value.fieldPaths.some(
|
|
145
145
|
(path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
|
|
146
146
|
)) {
|
|
147
147
|
return null;
|
|
148
148
|
}
|
|
149
149
|
return {
|
|
150
|
-
id,
|
|
150
|
+
id: id2,
|
|
151
151
|
label,
|
|
152
152
|
fieldPaths: value.fieldPaths,
|
|
153
153
|
...description ? { description } : {}
|
|
@@ -184,14 +184,14 @@ function parseAgentToolUiSurface(value) {
|
|
|
184
184
|
])) {
|
|
185
185
|
return null;
|
|
186
186
|
}
|
|
187
|
-
const
|
|
187
|
+
const id2 = boundedString(value.id, 200);
|
|
188
188
|
const title = boundedString(value.title, 200);
|
|
189
189
|
const toolSlug = boundedString(value.toolSlug, 200);
|
|
190
190
|
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
191
191
|
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
192
192
|
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
193
193
|
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
194
|
-
if (!
|
|
194
|
+
if (!id2 || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
195
195
|
return null;
|
|
196
196
|
}
|
|
197
197
|
const fields = value.fields.map(parseField);
|
|
@@ -211,7 +211,7 @@ function parseAgentToolUiSurface(value) {
|
|
|
211
211
|
}
|
|
212
212
|
return {
|
|
213
213
|
schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
214
|
-
id,
|
|
214
|
+
id: id2,
|
|
215
215
|
title,
|
|
216
216
|
toolSlug,
|
|
217
217
|
fields,
|
|
@@ -391,14 +391,14 @@ function parseAgentSearchDiscoveryOutput(value) {
|
|
|
391
391
|
var COMPOSER_FORM_SKIP_DISMISSAL = "I'd like to keep chatting without sharing the requested details for now. Please don't ask for them again unless I choose to proceed with something that needs them.";
|
|
392
392
|
function visitorMessageDisplayText(message) {
|
|
393
393
|
if (message.role !== "visitor") return message.text;
|
|
394
|
-
const
|
|
395
|
-
if (!
|
|
396
|
-
if (
|
|
397
|
-
const afterDismissal =
|
|
394
|
+
const text3 = message.text.trim();
|
|
395
|
+
if (!text3) return message.text;
|
|
396
|
+
if (text3.startsWith(COMPOSER_FORM_SKIP_DISMISSAL)) {
|
|
397
|
+
const afterDismissal = text3.slice(COMPOSER_FORM_SKIP_DISMISSAL.length).trim().replace(/^\n+/, "").trim();
|
|
398
398
|
if (afterDismissal) return afterDismissal;
|
|
399
399
|
}
|
|
400
400
|
const runtime = message.runtimeText?.trim();
|
|
401
|
-
if (runtime &&
|
|
401
|
+
if (runtime && text3 === runtime && runtime.includes(COMPOSER_FORM_SKIP_DISMISSAL)) {
|
|
402
402
|
const afterDismissal = runtime.slice(
|
|
403
403
|
runtime.indexOf(COMPOSER_FORM_SKIP_DISMISSAL) + COMPOSER_FORM_SKIP_DISMISSAL.length
|
|
404
404
|
).trim().replace(/^\n+/, "").trim();
|
|
@@ -472,13 +472,13 @@ function parseVisitorFormFields(value) {
|
|
|
472
472
|
const seen = /* @__PURE__ */ new Set();
|
|
473
473
|
for (const item of value) {
|
|
474
474
|
const record2 = asRecord(item);
|
|
475
|
-
const
|
|
475
|
+
const id2 = asString(record2?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
476
476
|
const kind = asString(record2?.kind);
|
|
477
|
-
if (!record2 || !
|
|
478
|
-
seen.add(
|
|
479
|
-
const label = asString(record2.label) ||
|
|
477
|
+
if (!record2 || !id2 || seen.has(id2) || !isFieldKind2(kind)) continue;
|
|
478
|
+
seen.add(id2);
|
|
479
|
+
const label = asString(record2.label) || id2;
|
|
480
480
|
fields.push({
|
|
481
|
-
id,
|
|
481
|
+
id: id2,
|
|
482
482
|
kind,
|
|
483
483
|
label,
|
|
484
484
|
placeholder: asString(record2.placeholder) || label,
|
|
@@ -514,52 +514,52 @@ function formatComposerFormMessage(form, values) {
|
|
|
514
514
|
return value ? `${field.label}: ${value}` : "";
|
|
515
515
|
}).filter(Boolean).join("\n");
|
|
516
516
|
}
|
|
517
|
-
function looksLikeFieldCollection(
|
|
517
|
+
function looksLikeFieldCollection(text3) {
|
|
518
518
|
return /\b(please|need|send|share|enter|provide|add|collect|what|which|use)\b/i.test(
|
|
519
|
-
|
|
520
|
-
) || /:\s*$/m.test(
|
|
519
|
+
text3
|
|
520
|
+
) || /:\s*$/m.test(text3) || /^[-*•]\s+/m.test(text3);
|
|
521
521
|
}
|
|
522
|
-
function looksLikeBookingCopy(
|
|
522
|
+
function looksLikeBookingCopy(text3) {
|
|
523
523
|
return /book(ing)? (card|a demo|this time)|pick a (date|time)|available (times|slots)|calendly/i.test(
|
|
524
|
-
|
|
524
|
+
text3
|
|
525
525
|
);
|
|
526
526
|
}
|
|
527
|
-
function echoedLabeledFieldIds(
|
|
527
|
+
function echoedLabeledFieldIds(text3) {
|
|
528
528
|
const ids = /* @__PURE__ */ new Set();
|
|
529
|
-
if (/\bname\s*:\s+\S+/i.test(
|
|
530
|
-
if (/\be-?mail\s*:\s+\S+/i.test(
|
|
531
|
-
if (/\bphone\s*:\s+\S+/i.test(
|
|
532
|
-
if (/\bcompany\s*:\s+\S+/i.test(
|
|
529
|
+
if (/\bname\s*:\s+\S+/i.test(text3)) ids.add("name");
|
|
530
|
+
if (/\be-?mail\s*:\s+\S+/i.test(text3)) ids.add("email");
|
|
531
|
+
if (/\bphone\s*:\s+\S+/i.test(text3)) ids.add("phone");
|
|
532
|
+
if (/\bcompany\s*:\s+\S+/i.test(text3)) ids.add("company");
|
|
533
533
|
return ids;
|
|
534
534
|
}
|
|
535
|
-
function matchLibraryFields(
|
|
535
|
+
function matchLibraryFields(text3) {
|
|
536
536
|
return FIELD_LIBRARY.flatMap((field) => {
|
|
537
|
-
if (field.exclude?.test(
|
|
538
|
-
const leftover =
|
|
537
|
+
if (field.exclude?.test(text3)) {
|
|
538
|
+
const leftover = text3.replace(field.exclude, " ");
|
|
539
539
|
if (!field.patterns.some((pattern) => pattern.test(leftover))) return [];
|
|
540
|
-
} else if (!field.patterns.some((pattern) => pattern.test(
|
|
540
|
+
} else if (!field.patterns.some((pattern) => pattern.test(text3))) {
|
|
541
541
|
return [];
|
|
542
542
|
}
|
|
543
543
|
const { patterns: _patterns, exclude: _exclude, ...next } = field;
|
|
544
544
|
return [next];
|
|
545
545
|
}).slice(0, MAX_FORM_FIELDS);
|
|
546
546
|
}
|
|
547
|
-
function looksLikeCompletedActionRecap(
|
|
548
|
-
const confirmingCreate = /\bshould i create this\b/i.test(
|
|
549
|
-
const echoingFilledFields = /\b(?:name|email|phone|company)\s*:\s+\S+/i.test(
|
|
547
|
+
function looksLikeCompletedActionRecap(text3) {
|
|
548
|
+
const confirmingCreate = /\bshould i create this\b/i.test(text3);
|
|
549
|
+
const echoingFilledFields = /\b(?:name|email|phone|company)\s*:\s+\S+/i.test(text3) && /[^\s@]+@[^\s@]+\.[^\s@]+/i.test(text3);
|
|
550
550
|
if (echoingFilledFields) {
|
|
551
|
-
if (looksLikeFieldCollection(
|
|
552
|
-
const echoed = echoedLabeledFieldIds(
|
|
553
|
-
if (matchLibraryFields(
|
|
551
|
+
if (looksLikeFieldCollection(text3)) {
|
|
552
|
+
const echoed = echoedLabeledFieldIds(text3);
|
|
553
|
+
if (matchLibraryFields(text3).some((field) => !echoed.has(field.id))) {
|
|
554
554
|
return false;
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
557
|
return true;
|
|
558
558
|
}
|
|
559
|
-
return confirmingCreate && !looksLikeFieldCollection(
|
|
559
|
+
return confirmingCreate && !looksLikeFieldCollection(text3);
|
|
560
560
|
}
|
|
561
|
-
function inferComposerForm(
|
|
562
|
-
const cleaned =
|
|
561
|
+
function inferComposerForm(text3) {
|
|
562
|
+
const cleaned = text3.trim();
|
|
563
563
|
if (!cleaned || looksLikeBookingCopy(cleaned) || looksLikeCompletedActionRecap(cleaned) || !looksLikeFieldCollection(cleaned)) {
|
|
564
564
|
return null;
|
|
565
565
|
}
|
|
@@ -574,8 +574,8 @@ function resolveComposerForm(input) {
|
|
|
574
574
|
if (input.enabled === false || input.hasBookingOffer || input.hasPendingConfirmation) {
|
|
575
575
|
return null;
|
|
576
576
|
}
|
|
577
|
-
const
|
|
578
|
-
if (!
|
|
577
|
+
const text3 = input.agentText.trim();
|
|
578
|
+
if (!text3) return null;
|
|
579
579
|
const card = input.cards?.find((item) => item.type === "visitor_form");
|
|
580
580
|
if (card?.fields && card.fields.length >= MIN_FORM_FIELDS) {
|
|
581
581
|
return {
|
|
@@ -583,7 +583,7 @@ function resolveComposerForm(input) {
|
|
|
583
583
|
fields: card.fields.slice(0, MAX_FORM_FIELDS)
|
|
584
584
|
};
|
|
585
585
|
}
|
|
586
|
-
return inferComposerForm(
|
|
586
|
+
return inferComposerForm(text3);
|
|
587
587
|
}
|
|
588
588
|
|
|
589
589
|
// src/react/lib/tool-card.ts
|
|
@@ -597,9 +597,9 @@ function preferBookingOffer(current, next) {
|
|
|
597
597
|
}
|
|
598
598
|
return next;
|
|
599
599
|
}
|
|
600
|
-
function looksLikeBookingReady(
|
|
600
|
+
function looksLikeBookingReady(text3) {
|
|
601
601
|
return /demo option|options are ready|pick a (date|time)|available (times|slots)|book(ing)? (card|the demo)|\bschedule\b/i.test(
|
|
602
|
-
|
|
602
|
+
text3
|
|
603
603
|
);
|
|
604
604
|
}
|
|
605
605
|
function bookingOfferIdentityKey(offer) {
|
|
@@ -709,29 +709,29 @@ function bookingCardFromActionOutput(output) {
|
|
|
709
709
|
const data = asRecord2(record2?.output) ?? asRecord2(record2?.data) ?? record2;
|
|
710
710
|
return parseToolCard(data);
|
|
711
711
|
}
|
|
712
|
-
function ensureBookingOfferText(
|
|
713
|
-
if (!offer) return
|
|
714
|
-
if (extractToolCards(
|
|
715
|
-
return
|
|
712
|
+
function ensureBookingOfferText(text3, offer) {
|
|
713
|
+
if (!offer) return text3;
|
|
714
|
+
if (extractToolCards(text3).some((card) => card.type === "booking_offer")) {
|
|
715
|
+
return text3;
|
|
716
716
|
}
|
|
717
|
-
const visible = stripToolCards(
|
|
717
|
+
const visible = stripToolCards(text3).trim() || text3.trim();
|
|
718
718
|
return `${visible}
|
|
719
719
|
|
|
720
720
|
${formatBookingOfferFence(offer)}`;
|
|
721
721
|
}
|
|
722
|
-
function hideToolCardFences(
|
|
723
|
-
return
|
|
722
|
+
function hideToolCardFences(text3) {
|
|
723
|
+
return text3.replace(/```(?:webless-tool-card|json)\s*[\s\S]*?```/gi, "").replace(/```(?:webless-tool-card|json)[\s\S]*$/i, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
724
724
|
}
|
|
725
725
|
var BOOKING_CARD_FALLBACK = "Pick a date and time that works for you.";
|
|
726
|
-
function looksLikeBookingAvailabilityDump(
|
|
727
|
-
const cleaned =
|
|
726
|
+
function looksLikeBookingAvailabilityDump(text3) {
|
|
727
|
+
const cleaned = text3.trim();
|
|
728
728
|
if (!cleaned) return false;
|
|
729
729
|
const isoCount = (cleaned.match(/\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/g) ?? []).length;
|
|
730
730
|
const utcCount = (cleaned.match(/\bUTC\b/g) ?? []).length;
|
|
731
731
|
return isoCount >= 2 || utcCount >= 2 || /api\.calendly\.com|calendly\.com\//i.test(cleaned) || /webless-tool-card/i.test(cleaned);
|
|
732
732
|
}
|
|
733
|
-
function sanitizeBookingOfferCopy(
|
|
734
|
-
const cleaned = hideToolCardFences(
|
|
733
|
+
function sanitizeBookingOfferCopy(text3) {
|
|
734
|
+
const cleaned = hideToolCardFences(text3);
|
|
735
735
|
if (!cleaned || looksLikeBookingAvailabilityDump(cleaned)) {
|
|
736
736
|
return BOOKING_CARD_FALLBACK;
|
|
737
737
|
}
|
|
@@ -744,9 +744,9 @@ function visitorTimeZone() {
|
|
|
744
744
|
return "UTC";
|
|
745
745
|
}
|
|
746
746
|
}
|
|
747
|
-
function extractToolCards(
|
|
747
|
+
function extractToolCards(text3) {
|
|
748
748
|
const cards = [];
|
|
749
|
-
for (const match of
|
|
749
|
+
for (const match of text3.matchAll(FENCE_PATTERN)) {
|
|
750
750
|
try {
|
|
751
751
|
const card = parseToolCard(JSON.parse(match[1] ?? ""));
|
|
752
752
|
if (card) cards.push(card);
|
|
@@ -755,8 +755,8 @@ function extractToolCards(text2) {
|
|
|
755
755
|
}
|
|
756
756
|
return cards;
|
|
757
757
|
}
|
|
758
|
-
function stripToolCards(
|
|
759
|
-
return
|
|
758
|
+
function stripToolCards(text3) {
|
|
759
|
+
return text3.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
760
760
|
}
|
|
761
761
|
function localDateKey(date) {
|
|
762
762
|
if (Number.isNaN(date.getTime())) return "";
|
|
@@ -876,10 +876,10 @@ function safeText(value) {
|
|
|
876
876
|
return value.trim().slice(0, MAX_TEXT_LENGTH);
|
|
877
877
|
}
|
|
878
878
|
function safeHref(value) {
|
|
879
|
-
const
|
|
880
|
-
if (!
|
|
879
|
+
const text3 = safeText(value);
|
|
880
|
+
if (!text3) return "";
|
|
881
881
|
try {
|
|
882
|
-
const url = new URL(
|
|
882
|
+
const url = new URL(text3);
|
|
883
883
|
return url.protocol === "https:" ? url.toString() : "";
|
|
884
884
|
} catch {
|
|
885
885
|
return "";
|
|
@@ -1212,17 +1212,372 @@ function legacyBookingEnvelope(output) {
|
|
|
1212
1212
|
} : null;
|
|
1213
1213
|
}
|
|
1214
1214
|
|
|
1215
|
+
// src/react/hooks/useAgentHandoff.ts
|
|
1216
|
+
import { ClientError } from "eve/client";
|
|
1217
|
+
import { useEffect, useRef, useState } from "react";
|
|
1218
|
+
function useAgentHandoff(options) {
|
|
1219
|
+
const [page, setPage] = useState(null);
|
|
1220
|
+
const [busy, setBusy] = useState(false);
|
|
1221
|
+
const [rejected, setRejected] = useState(false);
|
|
1222
|
+
const [error, setError] = useState(null);
|
|
1223
|
+
const callbacks = useRef(options);
|
|
1224
|
+
callbacks.current = options;
|
|
1225
|
+
const pending = useRef(null);
|
|
1226
|
+
const inFlight = useRef(null);
|
|
1227
|
+
const pageRef = useRef(null);
|
|
1228
|
+
const refreshRef = useRef(null);
|
|
1229
|
+
const generation = useRef(0);
|
|
1230
|
+
useEffect(() => {
|
|
1231
|
+
const token = ++generation.current;
|
|
1232
|
+
const controller = new AbortController();
|
|
1233
|
+
let timer;
|
|
1234
|
+
let reading = null;
|
|
1235
|
+
let cursor = 0;
|
|
1236
|
+
pageRef.current = null;
|
|
1237
|
+
pending.current = null;
|
|
1238
|
+
inFlight.current?.abort();
|
|
1239
|
+
inFlight.current = null;
|
|
1240
|
+
setRejected(false);
|
|
1241
|
+
setPage(null);
|
|
1242
|
+
setError(null);
|
|
1243
|
+
setBusy(false);
|
|
1244
|
+
if (!options.enabled || !options.sessionId) return;
|
|
1245
|
+
const active = () => !controller.signal.aborted && generation.current === token;
|
|
1246
|
+
const refresh = () => {
|
|
1247
|
+
reading ??= (async () => {
|
|
1248
|
+
do {
|
|
1249
|
+
const next = await options.client.handoff.read({
|
|
1250
|
+
after: cursor,
|
|
1251
|
+
signal: AbortSignal.any([
|
|
1252
|
+
controller.signal,
|
|
1253
|
+
AbortSignal.timeout(15e3)
|
|
1254
|
+
])
|
|
1255
|
+
});
|
|
1256
|
+
if (!active()) return;
|
|
1257
|
+
callbacks.current.onEvents(next.events);
|
|
1258
|
+
if (next.status !== "ai") callbacks.current.onOwnership();
|
|
1259
|
+
cursor = next.cursor;
|
|
1260
|
+
pageRef.current = next;
|
|
1261
|
+
setPage(next);
|
|
1262
|
+
setRejected(false);
|
|
1263
|
+
if (!pending.current) setError(null);
|
|
1264
|
+
if (!next.hasMore) break;
|
|
1265
|
+
} while (active());
|
|
1266
|
+
})().finally(() => {
|
|
1267
|
+
reading = null;
|
|
1268
|
+
});
|
|
1269
|
+
return reading;
|
|
1270
|
+
};
|
|
1271
|
+
refreshRef.current = refresh;
|
|
1272
|
+
const poll = async () => {
|
|
1273
|
+
try {
|
|
1274
|
+
await refresh();
|
|
1275
|
+
} catch (cause) {
|
|
1276
|
+
if (active()) {
|
|
1277
|
+
const denied = cause instanceof ClientError && cause.status === 403;
|
|
1278
|
+
setRejected(denied);
|
|
1279
|
+
setError(
|
|
1280
|
+
denied ? "This preview has changed. Restart the preview chat to test the current version." : "Reconnecting to your conversation\u2026"
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
} finally {
|
|
1284
|
+
if (active()) timer = setTimeout(() => void poll(), 1500);
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
void poll();
|
|
1288
|
+
return () => {
|
|
1289
|
+
controller.abort();
|
|
1290
|
+
inFlight.current?.abort();
|
|
1291
|
+
clearTimeout(timer);
|
|
1292
|
+
refreshRef.current = null;
|
|
1293
|
+
};
|
|
1294
|
+
}, [options.client, options.enabled, options.sessionId]);
|
|
1295
|
+
async function execute(operation) {
|
|
1296
|
+
if (inFlight.current) return;
|
|
1297
|
+
const controller = new AbortController();
|
|
1298
|
+
inFlight.current = controller;
|
|
1299
|
+
const signal = AbortSignal.any([
|
|
1300
|
+
controller.signal,
|
|
1301
|
+
AbortSignal.timeout(15e3)
|
|
1302
|
+
]);
|
|
1303
|
+
const token = generation.current;
|
|
1304
|
+
pending.current = operation;
|
|
1305
|
+
setBusy(true);
|
|
1306
|
+
setError(null);
|
|
1307
|
+
try {
|
|
1308
|
+
if (operation.type === "start")
|
|
1309
|
+
await options.client.handoff.start(operation.operationId, signal);
|
|
1310
|
+
else if (operation.type === "message") {
|
|
1311
|
+
const { type: _, ...command } = operation;
|
|
1312
|
+
await options.client.handoff.send(command, signal);
|
|
1313
|
+
} else {
|
|
1314
|
+
const { type: _, ...command } = operation;
|
|
1315
|
+
await options.client.handoff.returnToAI(command, signal);
|
|
1316
|
+
}
|
|
1317
|
+
if (token !== generation.current) return;
|
|
1318
|
+
pending.current = null;
|
|
1319
|
+
await refreshRef.current?.();
|
|
1320
|
+
} catch {
|
|
1321
|
+
if (token === generation.current)
|
|
1322
|
+
setError(
|
|
1323
|
+
"Could not confirm delivery. Retry to check the same request."
|
|
1324
|
+
);
|
|
1325
|
+
} finally {
|
|
1326
|
+
if (inFlight.current === controller) inFlight.current = null;
|
|
1327
|
+
if (token === generation.current) setBusy(false);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
const binding = () => {
|
|
1331
|
+
const current = pageRef.current;
|
|
1332
|
+
if (!current?.handoffId) throw new Error("No active human conversation.");
|
|
1333
|
+
return { handoffId: current.handoffId, epoch: current.epoch };
|
|
1334
|
+
};
|
|
1335
|
+
return {
|
|
1336
|
+
page,
|
|
1337
|
+
busy,
|
|
1338
|
+
error,
|
|
1339
|
+
hasPending: Boolean(pending.current),
|
|
1340
|
+
canReset: !busy && !pending.current && (rejected || page?.status === "ai" || page?.status === "failed" || !options.sessionId),
|
|
1341
|
+
blocksAI: Boolean(options.enabled && options.sessionId && !page) || busy || Boolean(pending.current) || Boolean(page && page.status !== "ai"),
|
|
1342
|
+
start: () => execute(
|
|
1343
|
+
pending.current ?? { type: "start", operationId: crypto.randomUUID() }
|
|
1344
|
+
),
|
|
1345
|
+
send: (message) => {
|
|
1346
|
+
if (pending.current)
|
|
1347
|
+
throw new Error(
|
|
1348
|
+
"Retry the pending request before sending another message."
|
|
1349
|
+
);
|
|
1350
|
+
return execute({
|
|
1351
|
+
type: "message",
|
|
1352
|
+
operationId: crypto.randomUUID(),
|
|
1353
|
+
...binding(),
|
|
1354
|
+
message
|
|
1355
|
+
});
|
|
1356
|
+
},
|
|
1357
|
+
returnToAI: () => execute(
|
|
1358
|
+
pending.current ?? {
|
|
1359
|
+
type: "return",
|
|
1360
|
+
operationId: crypto.randomUUID(),
|
|
1361
|
+
...binding()
|
|
1362
|
+
}
|
|
1363
|
+
),
|
|
1364
|
+
retry: async () => {
|
|
1365
|
+
if (pending.current) return execute(pending.current);
|
|
1366
|
+
try {
|
|
1367
|
+
await refreshRef.current?.();
|
|
1368
|
+
} catch {
|
|
1369
|
+
setError("Reconnecting to your conversation\u2026");
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1215
1375
|
// src/react/hooks/useAgentChat.ts
|
|
1216
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
1376
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
|
|
1217
1377
|
|
|
1218
1378
|
// src/runtime/client.ts
|
|
1219
1379
|
import {
|
|
1220
1380
|
Client,
|
|
1221
|
-
ClientError as
|
|
1381
|
+
ClientError as ClientError4
|
|
1222
1382
|
} from "eve/client";
|
|
1223
1383
|
|
|
1384
|
+
// src/runtime/handoff.ts
|
|
1385
|
+
import { ClientError as ClientError3 } from "eve/client";
|
|
1386
|
+
|
|
1387
|
+
// src/runtime/generated/handoff-contract.ts
|
|
1388
|
+
import { z } from "zod";
|
|
1389
|
+
var id = z.string().min(1).max(200);
|
|
1390
|
+
var sequence = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
1391
|
+
var text2 = z.string().trim().min(1).max(16e3);
|
|
1392
|
+
var agentRuntimeHandoffVersion = "webless.ai/agent-runtime-handoff/v1";
|
|
1393
|
+
var agentRuntimeHandoffStatusSchema = z.enum([
|
|
1394
|
+
"ai",
|
|
1395
|
+
"requesting",
|
|
1396
|
+
"queued",
|
|
1397
|
+
"human",
|
|
1398
|
+
"resolved",
|
|
1399
|
+
"failed"
|
|
1400
|
+
]);
|
|
1401
|
+
var agentRuntimeHandoffActorSchema = z.strictObject({
|
|
1402
|
+
id,
|
|
1403
|
+
name: z.string().trim().min(1).max(200)
|
|
1404
|
+
});
|
|
1405
|
+
var eventBase = {
|
|
1406
|
+
id,
|
|
1407
|
+
sequence: sequence.refine((value) => value > 0),
|
|
1408
|
+
handoffId: id,
|
|
1409
|
+
epoch: sequence.refine((value) => value > 0),
|
|
1410
|
+
createdAt: z.iso.datetime()
|
|
1411
|
+
};
|
|
1412
|
+
var agentRuntimeHandoffEventSchema = z.discriminatedUnion("type", [
|
|
1413
|
+
z.strictObject({
|
|
1414
|
+
...eventBase,
|
|
1415
|
+
type: z.literal("status"),
|
|
1416
|
+
status: agentRuntimeHandoffStatusSchema,
|
|
1417
|
+
actor: agentRuntimeHandoffActorSchema.nullable()
|
|
1418
|
+
}),
|
|
1419
|
+
z.strictObject({
|
|
1420
|
+
...eventBase,
|
|
1421
|
+
type: z.literal("visitor.message"),
|
|
1422
|
+
message: text2,
|
|
1423
|
+
operationId: id
|
|
1424
|
+
}),
|
|
1425
|
+
z.strictObject({
|
|
1426
|
+
...eventBase,
|
|
1427
|
+
type: z.literal("human.message"),
|
|
1428
|
+
message: text2,
|
|
1429
|
+
actor: agentRuntimeHandoffActorSchema
|
|
1430
|
+
})
|
|
1431
|
+
]);
|
|
1432
|
+
var agentRuntimeHandoffReadRequestSchema = z.strictObject({
|
|
1433
|
+
after: sequence.default(0)
|
|
1434
|
+
});
|
|
1435
|
+
var agentRuntimeHandoffPageSchema = z.strictObject({
|
|
1436
|
+
apiVersion: z.literal(agentRuntimeHandoffVersion),
|
|
1437
|
+
sessionId: id,
|
|
1438
|
+
enabled: z.boolean(),
|
|
1439
|
+
status: agentRuntimeHandoffStatusSchema,
|
|
1440
|
+
handoffId: id.nullable(),
|
|
1441
|
+
epoch: sequence,
|
|
1442
|
+
after: sequence,
|
|
1443
|
+
cursor: sequence,
|
|
1444
|
+
hasMore: z.boolean(),
|
|
1445
|
+
events: z.array(agentRuntimeHandoffEventSchema).max(100)
|
|
1446
|
+
}).superRefine((page, ctx) => {
|
|
1447
|
+
let cursor = page.after;
|
|
1448
|
+
let previousEpoch = 0;
|
|
1449
|
+
const statuses = /* @__PURE__ */ new Map();
|
|
1450
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1451
|
+
const handoffs = /* @__PURE__ */ new Map();
|
|
1452
|
+
const terminalEpochs = /* @__PURE__ */ new Set();
|
|
1453
|
+
let currentStatus;
|
|
1454
|
+
for (const event of page.events) {
|
|
1455
|
+
if (event.epoch < previousEpoch)
|
|
1456
|
+
ctx.addIssue({
|
|
1457
|
+
code: "custom",
|
|
1458
|
+
message: "Handoff epochs cannot decrease."
|
|
1459
|
+
});
|
|
1460
|
+
previousEpoch = event.epoch;
|
|
1461
|
+
if (ids.has(event.id))
|
|
1462
|
+
ctx.addIssue({
|
|
1463
|
+
code: "custom",
|
|
1464
|
+
message: "Duplicate handoff event ID."
|
|
1465
|
+
});
|
|
1466
|
+
ids.add(event.id);
|
|
1467
|
+
const handoffId = handoffs.get(event.epoch);
|
|
1468
|
+
if (handoffId !== void 0 && handoffId !== event.handoffId || event.epoch === page.epoch && event.handoffId !== page.handoffId)
|
|
1469
|
+
ctx.addIssue({
|
|
1470
|
+
code: "custom",
|
|
1471
|
+
message: "Inconsistent handoff epoch identity."
|
|
1472
|
+
});
|
|
1473
|
+
handoffs.set(event.epoch, event.handoffId);
|
|
1474
|
+
if (terminalEpochs.has(event.epoch) && (event.type !== "status" || event.status !== "ai"))
|
|
1475
|
+
ctx.addIssue({
|
|
1476
|
+
code: "custom",
|
|
1477
|
+
message: "Handoff event follows resolution."
|
|
1478
|
+
});
|
|
1479
|
+
if (event.type === "status") {
|
|
1480
|
+
if (event.status === "human" && event.actor === null)
|
|
1481
|
+
ctx.addIssue({
|
|
1482
|
+
code: "custom",
|
|
1483
|
+
message: "Human ownership requires a representative."
|
|
1484
|
+
});
|
|
1485
|
+
const previous = statuses.get(event.epoch);
|
|
1486
|
+
const transitions = {
|
|
1487
|
+
ai: [],
|
|
1488
|
+
requesting: ["queued", "resolved", "failed"],
|
|
1489
|
+
queued: ["human", "resolved", "failed"],
|
|
1490
|
+
human: ["resolved", "failed"],
|
|
1491
|
+
resolved: ["ai"],
|
|
1492
|
+
failed: []
|
|
1493
|
+
};
|
|
1494
|
+
if (previous !== void 0 && !transitions[previous].includes(event.status))
|
|
1495
|
+
ctx.addIssue({
|
|
1496
|
+
code: "custom",
|
|
1497
|
+
message: "Invalid handoff ownership transition."
|
|
1498
|
+
});
|
|
1499
|
+
statuses.set(event.epoch, event.status);
|
|
1500
|
+
if (["resolved", "failed", "ai"].includes(event.status))
|
|
1501
|
+
terminalEpochs.add(event.epoch);
|
|
1502
|
+
if (event.epoch === page.epoch) currentStatus = event.status;
|
|
1503
|
+
}
|
|
1504
|
+
if (event.sequence !== cursor + 1) {
|
|
1505
|
+
ctx.addIssue({
|
|
1506
|
+
code: "custom",
|
|
1507
|
+
message: "Handoff event gap or replay."
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
cursor = event.sequence;
|
|
1511
|
+
if (event.epoch > page.epoch) {
|
|
1512
|
+
ctx.addIssue({
|
|
1513
|
+
code: "custom",
|
|
1514
|
+
message: "Event exceeds current epoch."
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
if (!page.hasMore && currentStatus !== void 0 && currentStatus !== page.status)
|
|
1519
|
+
ctx.addIssue({
|
|
1520
|
+
code: "custom",
|
|
1521
|
+
message: "Handoff ownership contradicts its final status event."
|
|
1522
|
+
});
|
|
1523
|
+
if (page.cursor !== cursor || page.hasMore && page.events.length === 0) {
|
|
1524
|
+
ctx.addIssue({ code: "custom", message: "Invalid handoff page cursor." });
|
|
1525
|
+
}
|
|
1526
|
+
if (page.epoch === 0 !== (page.handoffId === null)) {
|
|
1527
|
+
ctx.addIssue({ code: "custom", message: "Invalid handoff identity." });
|
|
1528
|
+
}
|
|
1529
|
+
if (page.handoffId === null && page.status !== "ai") {
|
|
1530
|
+
ctx.addIssue({
|
|
1531
|
+
code: "custom",
|
|
1532
|
+
message: "Handoff identity is required."
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
var agentRuntimeHandoffStartSchema = z.strictObject({
|
|
1537
|
+
operationId: id
|
|
1538
|
+
});
|
|
1539
|
+
var agentRuntimeHandoffCommandSchema = z.strictObject({
|
|
1540
|
+
operationId: id,
|
|
1541
|
+
handoffId: id,
|
|
1542
|
+
epoch: sequence.refine((value) => value > 0)
|
|
1543
|
+
});
|
|
1544
|
+
var agentRuntimeHandoffMessageSchema = agentRuntimeHandoffCommandSchema.extend({ message: text2 });
|
|
1545
|
+
var agentRuntimeHandoffBindingSchema = z.strictObject({
|
|
1546
|
+
tenantId: id,
|
|
1547
|
+
indexId: id,
|
|
1548
|
+
visitorSubject: id,
|
|
1549
|
+
sessionId: id,
|
|
1550
|
+
handoffId: id,
|
|
1551
|
+
epoch: sequence.refine((value) => value > 0)
|
|
1552
|
+
});
|
|
1553
|
+
var providerEventBase = {
|
|
1554
|
+
apiVersion: z.literal(agentRuntimeHandoffVersion),
|
|
1555
|
+
binding: agentRuntimeHandoffBindingSchema,
|
|
1556
|
+
eventId: id,
|
|
1557
|
+
sequence: sequence.refine((value) => value > 0)
|
|
1558
|
+
};
|
|
1559
|
+
var agentRuntimeHandoffProviderEventSchema = z.discriminatedUnion(
|
|
1560
|
+
"type",
|
|
1561
|
+
[
|
|
1562
|
+
z.strictObject({ ...providerEventBase, type: z.literal("queued") }),
|
|
1563
|
+
z.strictObject({
|
|
1564
|
+
...providerEventBase,
|
|
1565
|
+
type: z.literal("assigned"),
|
|
1566
|
+
actor: agentRuntimeHandoffActorSchema
|
|
1567
|
+
}),
|
|
1568
|
+
z.strictObject({
|
|
1569
|
+
...providerEventBase,
|
|
1570
|
+
type: z.literal("human.message"),
|
|
1571
|
+
actor: agentRuntimeHandoffActorSchema,
|
|
1572
|
+
message: text2
|
|
1573
|
+
}),
|
|
1574
|
+
z.strictObject({ ...providerEventBase, type: z.literal("resolved") }),
|
|
1575
|
+
z.strictObject({ ...providerEventBase, type: z.literal("failed") })
|
|
1576
|
+
]
|
|
1577
|
+
);
|
|
1578
|
+
|
|
1224
1579
|
// src/runtime/capability.ts
|
|
1225
|
-
import { ClientError } from "eve/client";
|
|
1580
|
+
import { ClientError as ClientError2 } from "eve/client";
|
|
1226
1581
|
var MAX_REFRESH_SKEW_MS = 3e4;
|
|
1227
1582
|
var LOCAL_LOOPBACK_ORIGINS = [
|
|
1228
1583
|
"http://127.0.0.1:3010",
|
|
@@ -1367,7 +1722,7 @@ async function withCapabilityRefresh(capability, request) {
|
|
|
1367
1722
|
try {
|
|
1368
1723
|
return await request();
|
|
1369
1724
|
} catch (error) {
|
|
1370
|
-
if (!(error instanceof
|
|
1725
|
+
if (!(error instanceof ClientError2) || error.status !== 401) {
|
|
1371
1726
|
throw error;
|
|
1372
1727
|
}
|
|
1373
1728
|
capability.invalidate();
|
|
@@ -1375,6 +1730,75 @@ async function withCapabilityRefresh(capability, request) {
|
|
|
1375
1730
|
}
|
|
1376
1731
|
}
|
|
1377
1732
|
|
|
1733
|
+
// src/runtime/handoff.ts
|
|
1734
|
+
function createHandoffClient(options) {
|
|
1735
|
+
const target = () => {
|
|
1736
|
+
const sessionId = options.getSessionId();
|
|
1737
|
+
if (!sessionId)
|
|
1738
|
+
throw new Error(
|
|
1739
|
+
"Start a conversation before requesting a representative."
|
|
1740
|
+
);
|
|
1741
|
+
return {
|
|
1742
|
+
sessionId,
|
|
1743
|
+
path: `/webless/v1/session/${encodeURIComponent(sessionId)}/handoff`
|
|
1744
|
+
};
|
|
1745
|
+
};
|
|
1746
|
+
const request = (path, init) => withCapabilityRefresh(options.capability, async () => {
|
|
1747
|
+
const response = await options.getClient().fetch(path, {
|
|
1748
|
+
...init,
|
|
1749
|
+
cache: "no-store",
|
|
1750
|
+
redirect: "error"
|
|
1751
|
+
});
|
|
1752
|
+
if (!response.ok) {
|
|
1753
|
+
throw new ClientError3(
|
|
1754
|
+
response.status,
|
|
1755
|
+
await response.text(),
|
|
1756
|
+
response.headers
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
return response;
|
|
1760
|
+
});
|
|
1761
|
+
const command = async (suffix, body, signal) => {
|
|
1762
|
+
const { path } = target();
|
|
1763
|
+
await request(path + suffix, {
|
|
1764
|
+
method: "POST",
|
|
1765
|
+
headers: { "content-type": "application/json" },
|
|
1766
|
+
body: JSON.stringify(body),
|
|
1767
|
+
signal
|
|
1768
|
+
});
|
|
1769
|
+
};
|
|
1770
|
+
return {
|
|
1771
|
+
async read(input = {}) {
|
|
1772
|
+
const { after } = agentRuntimeHandoffReadRequestSchema.parse({
|
|
1773
|
+
after: input.after
|
|
1774
|
+
});
|
|
1775
|
+
const { sessionId, path } = target();
|
|
1776
|
+
const response = await request(`${path}?after=${after}`, {
|
|
1777
|
+
signal: input.signal
|
|
1778
|
+
});
|
|
1779
|
+
const value = await response.json();
|
|
1780
|
+
const page = agentRuntimeHandoffPageSchema.parse(value);
|
|
1781
|
+
if (page.sessionId !== sessionId || page.after !== after) {
|
|
1782
|
+
throw new Error(
|
|
1783
|
+
"The handoff response does not belong to this conversation or cursor."
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
return page;
|
|
1787
|
+
},
|
|
1788
|
+
start: (operationId, signal) => command(
|
|
1789
|
+
"",
|
|
1790
|
+
agentRuntimeHandoffStartSchema.parse({ operationId }),
|
|
1791
|
+
signal
|
|
1792
|
+
),
|
|
1793
|
+
send: (input, signal) => command(
|
|
1794
|
+
"/messages",
|
|
1795
|
+
agentRuntimeHandoffMessageSchema.parse(input),
|
|
1796
|
+
signal
|
|
1797
|
+
),
|
|
1798
|
+
returnToAI: (input, signal) => command("/return", agentRuntimeHandoffCommandSchema.parse(input), signal)
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1378
1802
|
// src/runtime/config.ts
|
|
1379
1803
|
var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
|
|
1380
1804
|
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
@@ -1965,6 +2389,11 @@ var AgentSession = class {
|
|
|
1965
2389
|
version,
|
|
1966
2390
|
visitorSessionId
|
|
1967
2391
|
});
|
|
2392
|
+
this.handoff = createHandoffClient({
|
|
2393
|
+
getClient: () => this.ensureClient(),
|
|
2394
|
+
getSessionId: () => this.getActiveSessionId(),
|
|
2395
|
+
capability: this.capability
|
|
2396
|
+
});
|
|
1968
2397
|
}
|
|
1969
2398
|
indexId;
|
|
1970
2399
|
version;
|
|
@@ -1977,6 +2406,7 @@ var AgentSession = class {
|
|
|
1977
2406
|
activeResponse;
|
|
1978
2407
|
childStreams;
|
|
1979
2408
|
capability;
|
|
2409
|
+
handoff;
|
|
1980
2410
|
getActiveSessionId() {
|
|
1981
2411
|
return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
|
|
1982
2412
|
}
|
|
@@ -2087,7 +2517,7 @@ var AgentSession = class {
|
|
|
2087
2517
|
() => activeSession.send(message, { signal })
|
|
2088
2518
|
);
|
|
2089
2519
|
} catch (error) {
|
|
2090
|
-
if (error instanceof
|
|
2520
|
+
if (error instanceof ClientError4 && error.status === 409 && error.code === "session_not_active") {
|
|
2091
2521
|
clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
|
|
2092
2522
|
this.session = void 0;
|
|
2093
2523
|
session = void 0;
|
|
@@ -2261,10 +2691,10 @@ var AgentSession = class {
|
|
|
2261
2691
|
}
|
|
2262
2692
|
this.session = session;
|
|
2263
2693
|
const inputResponses = responses.map(
|
|
2264
|
-
({ requestId, optionId, text:
|
|
2694
|
+
({ requestId, optionId, text: text3 }) => ({
|
|
2265
2695
|
requestId,
|
|
2266
2696
|
...optionId ? { optionId } : {},
|
|
2267
|
-
...
|
|
2697
|
+
...text3 ? { text: text3 } : {}
|
|
2268
2698
|
})
|
|
2269
2699
|
);
|
|
2270
2700
|
const response = await withCapabilityRefresh(
|
|
@@ -2354,6 +2784,7 @@ function createAgentClient(options) {
|
|
|
2354
2784
|
);
|
|
2355
2785
|
return {
|
|
2356
2786
|
indexId,
|
|
2787
|
+
handoff: session.handoff,
|
|
2357
2788
|
version,
|
|
2358
2789
|
runtimeOrigin,
|
|
2359
2790
|
visitorSessionId,
|
|
@@ -2385,7 +2816,7 @@ function createAgentClient(options) {
|
|
|
2385
2816
|
}
|
|
2386
2817
|
|
|
2387
2818
|
// src/runtime/errors.ts
|
|
2388
|
-
import { ClientError as
|
|
2819
|
+
import { ClientError as ClientError5 } from "eve/client";
|
|
2389
2820
|
var TRANSIENT_AGENT_ERROR_MESSAGE = "The agent run stopped before the action finished. Please try again.";
|
|
2390
2821
|
var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
|
|
2391
2822
|
function isTransientRuntimeMessage(message) {
|
|
@@ -2397,7 +2828,7 @@ function isPreviewAuthorizationMessage(message) {
|
|
|
2397
2828
|
return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
|
|
2398
2829
|
}
|
|
2399
2830
|
function formatAgentError(error) {
|
|
2400
|
-
if (error instanceof
|
|
2831
|
+
if (error instanceof ClientError5) {
|
|
2401
2832
|
if (error.status === 401 && error.code === "index_required") {
|
|
2402
2833
|
return "Missing indexId \u2014 pass a published index id to createAgentClient().";
|
|
2403
2834
|
}
|
|
@@ -2435,9 +2866,21 @@ function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
|
2435
2866
|
function parseMessage(value) {
|
|
2436
2867
|
if (typeof value !== "object" || value === null) return null;
|
|
2437
2868
|
const record2 = value;
|
|
2438
|
-
if (typeof record2.id !== "string" || record2.role !== "agent" && record2.role !== "visitor" || typeof record2.text !== "string" || typeof record2.createdAt !== "number" || !Number.isFinite(record2.createdAt)) {
|
|
2869
|
+
if (typeof record2.id !== "string" || record2.role !== "agent" && record2.role !== "visitor" && record2.role !== "human" || typeof record2.text !== "string" || typeof record2.createdAt !== "number" || !Number.isFinite(record2.createdAt)) {
|
|
2439
2870
|
return null;
|
|
2440
2871
|
}
|
|
2872
|
+
if (record2.role === "human") {
|
|
2873
|
+
const person = record2.representative;
|
|
2874
|
+
if (typeof person !== "object" || person === null || !("id" in person) || !("name" in person) || typeof person.id !== "string" || typeof person.name !== "string")
|
|
2875
|
+
return null;
|
|
2876
|
+
return {
|
|
2877
|
+
id: record2.id,
|
|
2878
|
+
role: "human",
|
|
2879
|
+
text: record2.text,
|
|
2880
|
+
createdAt: record2.createdAt,
|
|
2881
|
+
representative: { id: person.id, name: person.name }
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2441
2884
|
if (record2.role === "visitor") {
|
|
2442
2885
|
return {
|
|
2443
2886
|
id: record2.id,
|
|
@@ -2751,15 +3194,15 @@ function appendChatCollectiblePrompts(messages, requests) {
|
|
|
2751
3194
|
}
|
|
2752
3195
|
return next;
|
|
2753
3196
|
}
|
|
2754
|
-
function chatInputResponseForText(requests,
|
|
2755
|
-
const trimmed =
|
|
3197
|
+
function chatInputResponseForText(requests, text3) {
|
|
3198
|
+
const trimmed = text3.trim();
|
|
2756
3199
|
if (!trimmed) return null;
|
|
2757
3200
|
const pending = requests.find(isChatCollectibleInputRequest);
|
|
2758
3201
|
if (!pending) return null;
|
|
2759
3202
|
return { requestId: pending.requestId, text: trimmed };
|
|
2760
3203
|
}
|
|
2761
|
-
function normalizeAssistantDedupeKey(
|
|
2762
|
-
return
|
|
3204
|
+
function normalizeAssistantDedupeKey(text3) {
|
|
3205
|
+
return text3.trim().replace(/\s+/g, " ").toLowerCase();
|
|
2763
3206
|
}
|
|
2764
3207
|
function isNearDuplicateAssistantText(left, right) {
|
|
2765
3208
|
const a = normalizeAssistantDedupeKey(left);
|
|
@@ -2872,6 +3315,7 @@ function isJsonRecord(value) {
|
|
|
2872
3315
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2873
3316
|
}
|
|
2874
3317
|
function useAgentChat({
|
|
3318
|
+
handoffEnabled = false,
|
|
2875
3319
|
customerId,
|
|
2876
3320
|
getUnpublishedPreviewGrant,
|
|
2877
3321
|
indexId,
|
|
@@ -2887,9 +3331,9 @@ function useAgentChat({
|
|
|
2887
3331
|
() => createInitialState(greeting?.trim() || DEFAULT_GREETING),
|
|
2888
3332
|
[greeting]
|
|
2889
3333
|
);
|
|
2890
|
-
const previewGrantProviderRef =
|
|
3334
|
+
const previewGrantProviderRef = useRef2(getUnpublishedPreviewGrant);
|
|
2891
3335
|
previewGrantProviderRef.current = getUnpublishedPreviewGrant;
|
|
2892
|
-
const toolResultRegistryRef =
|
|
3336
|
+
const toolResultRegistryRef = useRef2(toolResultRegistry);
|
|
2893
3337
|
toolResultRegistryRef.current = toolResultRegistry;
|
|
2894
3338
|
const resolveUnpublishedPreviewGrant = () => {
|
|
2895
3339
|
const provider = previewGrantProviderRef.current;
|
|
@@ -2917,17 +3361,17 @@ function useAgentChat({
|
|
|
2917
3361
|
}),
|
|
2918
3362
|
[resolvedStorageKeyPrefix, visitorSessionId]
|
|
2919
3363
|
);
|
|
2920
|
-
const [state, setState] =
|
|
3364
|
+
const [state, setState] = useState2(
|
|
2921
3365
|
() => stateFromConversation(
|
|
2922
3366
|
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
|
|
2923
3367
|
initialState
|
|
2924
3368
|
)
|
|
2925
3369
|
);
|
|
2926
|
-
const pendingBookingRef =
|
|
3370
|
+
const pendingBookingRef = useRef2(
|
|
2927
3371
|
loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
|
|
2928
3372
|
);
|
|
2929
|
-
const runRef =
|
|
2930
|
-
const clientRef =
|
|
3373
|
+
const runRef = useRef2(null);
|
|
3374
|
+
const clientRef = useRef2(
|
|
2931
3375
|
createAgentClient({
|
|
2932
3376
|
locationConsent: true,
|
|
2933
3377
|
customerId,
|
|
@@ -2941,8 +3385,8 @@ function useAgentChat({
|
|
|
2941
3385
|
})
|
|
2942
3386
|
);
|
|
2943
3387
|
const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${previewBuildId ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
|
|
2944
|
-
const identityRef =
|
|
2945
|
-
|
|
3388
|
+
const identityRef = useRef2(identityKey);
|
|
3389
|
+
useEffect2(() => {
|
|
2946
3390
|
if (identityRef.current === identityKey) {
|
|
2947
3391
|
return;
|
|
2948
3392
|
}
|
|
@@ -2981,7 +3425,7 @@ function useAgentChat({
|
|
|
2981
3425
|
version,
|
|
2982
3426
|
visitorId
|
|
2983
3427
|
]);
|
|
2984
|
-
|
|
3428
|
+
useEffect2(() => {
|
|
2985
3429
|
if (!hasVisitorMessages(state.messages)) return;
|
|
2986
3430
|
savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
|
|
2987
3431
|
messages: state.messages,
|
|
@@ -3001,16 +3445,77 @@ function useAgentChat({
|
|
|
3001
3445
|
state.toolResults,
|
|
3002
3446
|
visitorId
|
|
3003
3447
|
]);
|
|
3448
|
+
const handoff = useAgentHandoff({
|
|
3449
|
+
enabled: handoffEnabled,
|
|
3450
|
+
client: clientRef.current,
|
|
3451
|
+
sessionId: clientRef.current.getActiveSessionId(),
|
|
3452
|
+
onOwnership: () => {
|
|
3453
|
+
runRef.current?.abort();
|
|
3454
|
+
runRef.current = null;
|
|
3455
|
+
setState(
|
|
3456
|
+
(prev) => prev.phase === "complete" && !prev.streamingText && !prev.pendingInputs?.length && !prev.toolSteps.length ? prev : {
|
|
3457
|
+
...prev,
|
|
3458
|
+
phase: "complete",
|
|
3459
|
+
streamingText: "",
|
|
3460
|
+
pendingInputs: [],
|
|
3461
|
+
toolSteps: [],
|
|
3462
|
+
toolResults: [],
|
|
3463
|
+
pendingOffer: null,
|
|
3464
|
+
error: null
|
|
3465
|
+
}
|
|
3466
|
+
);
|
|
3467
|
+
},
|
|
3468
|
+
onEvents: (events) => {
|
|
3469
|
+
setState((prev) => {
|
|
3470
|
+
const seen = new Set(prev.messages.map((message) => message.id));
|
|
3471
|
+
const messages = [];
|
|
3472
|
+
for (const event of events) {
|
|
3473
|
+
if (event.type === "status" || seen.has(`handoff-${event.id}`))
|
|
3474
|
+
continue;
|
|
3475
|
+
messages.push(
|
|
3476
|
+
event.type === "human.message" ? {
|
|
3477
|
+
id: `handoff-${event.id}`,
|
|
3478
|
+
role: "human",
|
|
3479
|
+
text: event.message,
|
|
3480
|
+
createdAt: Date.parse(event.createdAt),
|
|
3481
|
+
representative: event.actor
|
|
3482
|
+
} : {
|
|
3483
|
+
id: `handoff-${event.id}`,
|
|
3484
|
+
role: "visitor",
|
|
3485
|
+
text: event.message,
|
|
3486
|
+
createdAt: Date.parse(event.createdAt)
|
|
3487
|
+
}
|
|
3488
|
+
);
|
|
3489
|
+
}
|
|
3490
|
+
return messages.length ? {
|
|
3491
|
+
...prev,
|
|
3492
|
+
messages: [...prev.messages, ...messages].sort(
|
|
3493
|
+
(a, b) => a.createdAt - b.createdAt
|
|
3494
|
+
)
|
|
3495
|
+
} : prev;
|
|
3496
|
+
});
|
|
3497
|
+
}
|
|
3498
|
+
});
|
|
3499
|
+
const handoffBlocksAI = useRef2(handoff.blocksAI);
|
|
3500
|
+
handoffBlocksAI.current = handoff.blocksAI;
|
|
3004
3501
|
const reset = useCallback(() => {
|
|
3502
|
+
if (handoff.blocksAI && !handoff.canReset) return;
|
|
3005
3503
|
runRef.current?.abort();
|
|
3006
3504
|
runRef.current = null;
|
|
3007
3505
|
clientRef.current.reset();
|
|
3008
3506
|
pendingBookingRef.current = null;
|
|
3009
3507
|
clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
|
|
3010
3508
|
setState(initialState);
|
|
3011
|
-
}, [
|
|
3509
|
+
}, [
|
|
3510
|
+
handoff.blocksAI,
|
|
3511
|
+
handoff.canReset,
|
|
3512
|
+
initialState,
|
|
3513
|
+
resolvedStorageKeyPrefix,
|
|
3514
|
+
visitorId
|
|
3515
|
+
]);
|
|
3012
3516
|
const runTurn = useCallback(
|
|
3013
3517
|
async (input) => {
|
|
3518
|
+
if (handoffBlocksAI.current) return null;
|
|
3014
3519
|
const {
|
|
3015
3520
|
controller,
|
|
3016
3521
|
initialText = "",
|
|
@@ -3134,6 +3639,7 @@ function useAgentChat({
|
|
|
3134
3639
|
signal
|
|
3135
3640
|
});
|
|
3136
3641
|
if (resume && finalText === null) {
|
|
3642
|
+
if (!isActiveRun() || handoffBlocksAI.current) return null;
|
|
3137
3643
|
finalText = await clientRef.current.sendTurn(visitorText, {
|
|
3138
3644
|
handlers,
|
|
3139
3645
|
signal
|
|
@@ -3166,8 +3672,12 @@ function useAgentChat({
|
|
|
3166
3672
|
messages: appendAgentTurnMessage(
|
|
3167
3673
|
prev.messages,
|
|
3168
3674
|
displayText,
|
|
3169
|
-
(prev.toolResults ?? []).filter(
|
|
3170
|
-
|
|
3675
|
+
(prev.toolResults ?? []).filter(
|
|
3676
|
+
(result) => result.kind === "search"
|
|
3677
|
+
),
|
|
3678
|
+
(prev.toolResults ?? []).filter(
|
|
3679
|
+
(result) => result.kind !== "search" && result.kind !== "input"
|
|
3680
|
+
)
|
|
3171
3681
|
),
|
|
3172
3682
|
toolResults: (prev.toolResults ?? []).filter(
|
|
3173
3683
|
(result) => result.kind === "input"
|
|
@@ -3235,6 +3745,15 @@ function useAgentChat({
|
|
|
3235
3745
|
const outgoing = options?.runtimeText ?? visitorText;
|
|
3236
3746
|
if (!outgoing.trim()) return null;
|
|
3237
3747
|
const declinedComposerFormId = options?.declinedComposerFormId?.trim();
|
|
3748
|
+
if (handoff.blocksAI) {
|
|
3749
|
+
if (!["requesting", "queued", "human"].includes(
|
|
3750
|
+
handoff.page?.status ?? ""
|
|
3751
|
+
) || handoff.busy)
|
|
3752
|
+
return null;
|
|
3753
|
+
if (!trimmed) return null;
|
|
3754
|
+
await handoff.send(trimmed);
|
|
3755
|
+
return null;
|
|
3756
|
+
}
|
|
3238
3757
|
const chatResponse = chatInputResponseForText(
|
|
3239
3758
|
state.pendingInputs ?? [],
|
|
3240
3759
|
outgoing.trim()
|
|
@@ -3336,9 +3855,10 @@ ${outgoing}` : outgoing;
|
|
|
3336
3855
|
visitorText: runtimeText
|
|
3337
3856
|
});
|
|
3338
3857
|
},
|
|
3339
|
-
[runTurn, state.pendingInputs]
|
|
3858
|
+
[handoff, runTurn, state.pendingInputs]
|
|
3340
3859
|
);
|
|
3341
3860
|
const retry = useCallback(async () => {
|
|
3861
|
+
if (handoffBlocksAI.current) return;
|
|
3342
3862
|
const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
|
|
3343
3863
|
if (!visitorMessage) return;
|
|
3344
3864
|
if (runRef.current) {
|
|
@@ -3372,6 +3892,7 @@ ${outgoing}` : outgoing;
|
|
|
3372
3892
|
});
|
|
3373
3893
|
}, [runTurn, state.messages]);
|
|
3374
3894
|
const regenerate = useCallback(async () => {
|
|
3895
|
+
if (handoffBlocksAI.current) return null;
|
|
3375
3896
|
let lastVisitorIndex = -1;
|
|
3376
3897
|
for (let index = state.messages.length - 1; index >= 0; index -= 1) {
|
|
3377
3898
|
if (state.messages[index]?.role === "visitor") {
|
|
@@ -3429,7 +3950,7 @@ ${outgoing}` : outgoing;
|
|
|
3429
3950
|
);
|
|
3430
3951
|
const respondToInput = useCallback(
|
|
3431
3952
|
async (response) => {
|
|
3432
|
-
if (runRef.current) return;
|
|
3953
|
+
if (handoffBlocksAI.current || runRef.current) return;
|
|
3433
3954
|
const pending = state.pendingInputs?.find(
|
|
3434
3955
|
(request) => request.requestId === response.requestId
|
|
3435
3956
|
);
|
|
@@ -3457,7 +3978,8 @@ ${outgoing}` : outgoing;
|
|
|
3457
3978
|
},
|
|
3458
3979
|
[respondToToolInput, runTurn, state.pendingInputs]
|
|
3459
3980
|
);
|
|
3460
|
-
|
|
3981
|
+
useEffect2(() => {
|
|
3982
|
+
if (handoff.blocksAI) return;
|
|
3461
3983
|
const conversation = loadPersistedAgentConversation(
|
|
3462
3984
|
resolvedStorageKeyPrefix,
|
|
3463
3985
|
visitorId
|
|
@@ -3479,14 +4001,21 @@ ${outgoing}` : outgoing;
|
|
|
3479
4001
|
}
|
|
3480
4002
|
controller.abort();
|
|
3481
4003
|
};
|
|
3482
|
-
}, [
|
|
3483
|
-
|
|
4004
|
+
}, [
|
|
4005
|
+
handoff.blocksAI,
|
|
4006
|
+
identityKey,
|
|
4007
|
+
resolvedStorageKeyPrefix,
|
|
4008
|
+
runTurn,
|
|
4009
|
+
visitorId
|
|
4010
|
+
]);
|
|
4011
|
+
useEffect2(() => {
|
|
3484
4012
|
return () => {
|
|
3485
4013
|
runRef.current?.abort();
|
|
3486
4014
|
runRef.current = null;
|
|
3487
4015
|
};
|
|
3488
4016
|
}, []);
|
|
3489
4017
|
return {
|
|
4018
|
+
handoff,
|
|
3490
4019
|
state,
|
|
3491
4020
|
reset,
|
|
3492
4021
|
retry,
|
|
@@ -3597,12 +4126,12 @@ var defaultDarkAgentRailTheme = {
|
|
|
3597
4126
|
};
|
|
3598
4127
|
|
|
3599
4128
|
// src/react/components/MessageActions/MessageActions.tsx
|
|
3600
|
-
import { useEffect as
|
|
4129
|
+
import { useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useState as useState3 } from "react";
|
|
3601
4130
|
import { createPortal } from "react-dom";
|
|
3602
4131
|
|
|
3603
4132
|
// src/react/lib/speech.ts
|
|
3604
|
-
function toSpeechText(
|
|
3605
|
-
let out = hideToolCardFences(
|
|
4133
|
+
function toSpeechText(text3) {
|
|
4134
|
+
let out = hideToolCardFences(text3);
|
|
3606
4135
|
out = out.replace(/```[\s\S]*?```/g, " ");
|
|
3607
4136
|
out = out.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
3608
4137
|
out = out.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
|
|
@@ -3947,12 +4476,12 @@ function StopIcon() {
|
|
|
3947
4476
|
}
|
|
3948
4477
|
) });
|
|
3949
4478
|
}
|
|
3950
|
-
async function writeToClipboard(
|
|
4479
|
+
async function writeToClipboard(text3) {
|
|
3951
4480
|
try {
|
|
3952
|
-
await navigator.clipboard.writeText(
|
|
4481
|
+
await navigator.clipboard.writeText(text3);
|
|
3953
4482
|
} catch {
|
|
3954
4483
|
const textarea = document.createElement("textarea");
|
|
3955
|
-
textarea.value =
|
|
4484
|
+
textarea.value = text3;
|
|
3956
4485
|
textarea.style.position = "fixed";
|
|
3957
4486
|
textarea.style.opacity = "0";
|
|
3958
4487
|
document.body.appendChild(textarea);
|
|
@@ -4003,16 +4532,16 @@ function MessageActions({
|
|
|
4003
4532
|
speechText
|
|
4004
4533
|
}) {
|
|
4005
4534
|
const menuPortalRoot = useAgentRailMenuPortalRoot();
|
|
4006
|
-
const [copied, setCopied] =
|
|
4007
|
-
const [rating, setRating] =
|
|
4008
|
-
const [menuOpen, setMenuOpen] =
|
|
4009
|
-
const [menuPosition, setMenuPosition] =
|
|
4010
|
-
const [speaking, setSpeaking] =
|
|
4011
|
-
const [speechSupported, setSpeechSupported] =
|
|
4012
|
-
const copyTimerRef =
|
|
4013
|
-
const menuRef =
|
|
4014
|
-
const portaledMenuRef =
|
|
4015
|
-
const moreButtonRef =
|
|
4535
|
+
const [copied, setCopied] = useState3(false);
|
|
4536
|
+
const [rating, setRating] = useState3(null);
|
|
4537
|
+
const [menuOpen, setMenuOpen] = useState3(false);
|
|
4538
|
+
const [menuPosition, setMenuPosition] = useState3(null);
|
|
4539
|
+
const [speaking, setSpeaking] = useState3(false);
|
|
4540
|
+
const [speechSupported, setSpeechSupported] = useState3(null);
|
|
4541
|
+
const copyTimerRef = useRef3(null);
|
|
4542
|
+
const menuRef = useRef3(null);
|
|
4543
|
+
const portaledMenuRef = useRef3(null);
|
|
4544
|
+
const moreButtonRef = useRef3(null);
|
|
4016
4545
|
const answeredLabel = formatAnsweredAt(answeredAt ?? 0);
|
|
4017
4546
|
const resolvedSpeechText = speechText ?? toSpeechText(copyText);
|
|
4018
4547
|
const canOpenReceipt = Boolean(receiptSteps) && Boolean(onOpenReceipt);
|
|
@@ -4039,7 +4568,7 @@ function MessageActions({
|
|
|
4039
4568
|
})
|
|
4040
4569
|
);
|
|
4041
4570
|
}, [menuOpen, menuPortalRoot]);
|
|
4042
|
-
|
|
4571
|
+
useEffect3(() => {
|
|
4043
4572
|
const unsubscribe = subscribeSpeechInterrupts(() => setSpeaking(false));
|
|
4044
4573
|
return () => {
|
|
4045
4574
|
unsubscribe();
|
|
@@ -4049,7 +4578,7 @@ function MessageActions({
|
|
|
4049
4578
|
stopSpeech();
|
|
4050
4579
|
};
|
|
4051
4580
|
}, []);
|
|
4052
|
-
|
|
4581
|
+
useEffect3(() => {
|
|
4053
4582
|
if (!menuOpen) return;
|
|
4054
4583
|
const handlePointerDown = (event) => {
|
|
4055
4584
|
const target = event.target;
|
|
@@ -4218,7 +4747,7 @@ function MessageActions({
|
|
|
4218
4747
|
}
|
|
4219
4748
|
|
|
4220
4749
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
4221
|
-
import { useEffect as
|
|
4750
|
+
import { useEffect as useEffect8, useRef as useRef8, useState as useState11 } from "react";
|
|
4222
4751
|
|
|
4223
4752
|
// src/react/lib/agent-theme.ts
|
|
4224
4753
|
function agentThemeStyle(theme, resolvedColorScheme) {
|
|
@@ -4293,7 +4822,7 @@ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
|
|
|
4293
4822
|
}
|
|
4294
4823
|
|
|
4295
4824
|
// src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
|
|
4296
|
-
import { useState as
|
|
4825
|
+
import { useState as useState4 } from "react";
|
|
4297
4826
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4298
4827
|
function joinLabels(labels) {
|
|
4299
4828
|
if (labels.length <= 1) return labels[0] ?? "";
|
|
@@ -4412,7 +4941,7 @@ function AgentActivityBubble({
|
|
|
4412
4941
|
const statusText = workSummary(steps, failed, brandLabel);
|
|
4413
4942
|
const softReview = statusText === AGENT_SOFT_REVIEW_STATUS;
|
|
4414
4943
|
const receiptId = steps.map((step) => step.id).join(":");
|
|
4415
|
-
const [expandedReceiptId, setExpandedReceiptId] =
|
|
4944
|
+
const [expandedReceiptId, setExpandedReceiptId] = useState4(
|
|
4416
4945
|
null
|
|
4417
4946
|
);
|
|
4418
4947
|
const detailsOpen = !active && expandedReceiptId === receiptId;
|
|
@@ -4463,7 +4992,7 @@ function AgentActivityBubble({
|
|
|
4463
4992
|
}
|
|
4464
4993
|
|
|
4465
4994
|
// src/react/components/AnswerReceiptDialog/AnswerReceiptDialog.tsx
|
|
4466
|
-
import { useEffect as
|
|
4995
|
+
import { useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
4467
4996
|
import { createPortal as createPortal2 } from "react-dom";
|
|
4468
4997
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4469
4998
|
var FOCUSABLE_SELECTOR = 'button:not(:disabled), a[href], textarea:not(:disabled), input:not(:disabled), [tabindex]:not([tabindex="-1"])';
|
|
@@ -4485,10 +5014,10 @@ function AnswerReceiptDialog({
|
|
|
4485
5014
|
}) {
|
|
4486
5015
|
const portalRoots = useAgentRailPortalRoots();
|
|
4487
5016
|
const overlayRoot = portalRoots?.overlayRef ?? null;
|
|
4488
|
-
const cardRef =
|
|
4489
|
-
const closeButtonRef =
|
|
4490
|
-
const previouslyFocusedRef =
|
|
4491
|
-
|
|
5017
|
+
const cardRef = useRef4(null);
|
|
5018
|
+
const closeButtonRef = useRef4(null);
|
|
5019
|
+
const previouslyFocusedRef = useRef4(null);
|
|
5020
|
+
useEffect4(() => {
|
|
4492
5021
|
previouslyFocusedRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
4493
5022
|
closeButtonRef.current?.focus({ preventScroll: true });
|
|
4494
5023
|
const handleKeyDown = (event) => {
|
|
@@ -4574,10 +5103,10 @@ function AnswerReceiptDialog({
|
|
|
4574
5103
|
|
|
4575
5104
|
// src/react/components/Composer/Composer.tsx
|
|
4576
5105
|
import {
|
|
4577
|
-
useEffect as
|
|
5106
|
+
useEffect as useEffect5,
|
|
4578
5107
|
useId,
|
|
4579
|
-
useRef as
|
|
4580
|
-
useState as
|
|
5108
|
+
useRef as useRef5,
|
|
5109
|
+
useState as useState5
|
|
4581
5110
|
} from "react";
|
|
4582
5111
|
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4583
5112
|
var FORM_SUBMITTED_MESSAGE = "Shared my details";
|
|
@@ -4640,17 +5169,17 @@ function Composer({
|
|
|
4640
5169
|
allowFormResume = true,
|
|
4641
5170
|
onSubmit
|
|
4642
5171
|
}) {
|
|
4643
|
-
const [value, setValue] =
|
|
4644
|
-
const [draft, setDraft] =
|
|
4645
|
-
const inputRef =
|
|
4646
|
-
const firstFieldRef =
|
|
4647
|
-
const formRef =
|
|
5172
|
+
const [value, setValue] = useState5("");
|
|
5173
|
+
const [draft, setDraft] = useState5(() => createDraft(form));
|
|
5174
|
+
const inputRef = useRef5(null);
|
|
5175
|
+
const firstFieldRef = useRef5(null);
|
|
5176
|
+
const formRef = useRef5(null);
|
|
4648
5177
|
const formId = useId();
|
|
4649
5178
|
if (form && form.id !== draft.form?.id) setDraft(createDraft(form));
|
|
4650
5179
|
const savedForm = allowFormResume && !draft.submitted ? draft.form : null;
|
|
4651
5180
|
const activeForm = !disabled && draft.expanded ? savedForm : null;
|
|
4652
5181
|
const canSend = !disabled && Boolean(value.trim() || activeForm);
|
|
4653
|
-
|
|
5182
|
+
useEffect5(() => {
|
|
4654
5183
|
if (activeForm) firstFieldRef.current?.focus();
|
|
4655
5184
|
else if ((savedForm || draft.submitted) && !disabled)
|
|
4656
5185
|
inputRef.current?.focus();
|
|
@@ -4911,7 +5440,7 @@ function FollowUpChips({
|
|
|
4911
5440
|
}
|
|
4912
5441
|
|
|
4913
5442
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
4914
|
-
import { useState as
|
|
5443
|
+
import { useState as useState7 } from "react";
|
|
4915
5444
|
|
|
4916
5445
|
// src/react/components/SearchReferences/SearchReferences.tsx
|
|
4917
5446
|
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
@@ -5003,11 +5532,11 @@ function SearchReferences({
|
|
|
5003
5532
|
|
|
5004
5533
|
// src/react/components/BookingCard/BookingCard.tsx
|
|
5005
5534
|
import {
|
|
5006
|
-
useEffect as
|
|
5535
|
+
useEffect as useEffect6,
|
|
5007
5536
|
useId as useId2,
|
|
5008
5537
|
useMemo as useMemo2,
|
|
5009
|
-
useRef as
|
|
5010
|
-
useState as
|
|
5538
|
+
useRef as useRef6,
|
|
5539
|
+
useState as useState6
|
|
5011
5540
|
} from "react";
|
|
5012
5541
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
5013
5542
|
var BOOKING_STEPS = [
|
|
@@ -5065,16 +5594,16 @@ function InteractiveBookingCard({
|
|
|
5065
5594
|
}) {
|
|
5066
5595
|
const fieldId = useId2();
|
|
5067
5596
|
const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
|
|
5068
|
-
const [step, setStep] =
|
|
5069
|
-
const [eventTypeUri, setEventTypeUri] =
|
|
5070
|
-
const [selectedDate, setSelectedDate] =
|
|
5071
|
-
const [startTime, setStartTime] =
|
|
5072
|
-
const [name, setName] =
|
|
5073
|
-
const [email, setEmail] =
|
|
5074
|
-
const activeStepRef =
|
|
5075
|
-
const previousStepRef =
|
|
5597
|
+
const [step, setStep] = useState6("date");
|
|
5598
|
+
const [eventTypeUri, setEventTypeUri] = useState6(defaultType);
|
|
5599
|
+
const [selectedDate, setSelectedDate] = useState6("");
|
|
5600
|
+
const [startTime, setStartTime] = useState6("");
|
|
5601
|
+
const [name, setName] = useState6("");
|
|
5602
|
+
const [email, setEmail] = useState6("");
|
|
5603
|
+
const activeStepRef = useRef6(null);
|
|
5604
|
+
const previousStepRef = useRef6(step);
|
|
5076
5605
|
const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
|
|
5077
|
-
|
|
5606
|
+
useEffect6(() => {
|
|
5078
5607
|
if (previousStepRef.current === step) return;
|
|
5079
5608
|
previousStepRef.current = step;
|
|
5080
5609
|
activeStepRef.current?.scrollIntoView({ block: "nearest" });
|
|
@@ -5091,7 +5620,7 @@ function InteractiveBookingCard({
|
|
|
5091
5620
|
}
|
|
5092
5621
|
return next;
|
|
5093
5622
|
}, [slots]);
|
|
5094
|
-
const [visibleMonth, setVisibleMonth] =
|
|
5623
|
+
const [visibleMonth, setVisibleMonth] = useState6(
|
|
5095
5624
|
() => firstAvailableBookingMonth(slots)
|
|
5096
5625
|
);
|
|
5097
5626
|
function selectEventType(nextType) {
|
|
@@ -5399,8 +5928,8 @@ function resolveMessageUrl(safeUrl, baseUrl) {
|
|
|
5399
5928
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
5400
5929
|
import "streamdown/styles.css";
|
|
5401
5930
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
5402
|
-
function normalizeDedupeText(
|
|
5403
|
-
return
|
|
5931
|
+
function normalizeDedupeText(text3) {
|
|
5932
|
+
return text3.trim().replace(/\s+/g, " ").toLowerCase();
|
|
5404
5933
|
}
|
|
5405
5934
|
function paragraphsAreNearDuplicates(first, second) {
|
|
5406
5935
|
const left = normalizeDedupeText(first);
|
|
@@ -5416,8 +5945,8 @@ function paragraphsShareOpening(first, second) {
|
|
|
5416
5945
|
if (!opening || opening.length < 20) return false;
|
|
5417
5946
|
return second.trim().startsWith(opening);
|
|
5418
5947
|
}
|
|
5419
|
-
function collapseRepeatedText(
|
|
5420
|
-
const trimmed =
|
|
5948
|
+
function collapseRepeatedText(text3) {
|
|
5949
|
+
const trimmed = text3.trim();
|
|
5421
5950
|
if (trimmed.length < 40) return trimmed;
|
|
5422
5951
|
const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
|
|
5423
5952
|
if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
|
|
@@ -5438,13 +5967,14 @@ function MessageBubble({
|
|
|
5438
5967
|
message,
|
|
5439
5968
|
brandLogoUrl,
|
|
5440
5969
|
bookingDisabled = false,
|
|
5970
|
+
showProvenance = false,
|
|
5441
5971
|
bookingReadOnly = false,
|
|
5442
5972
|
linkBaseUrl,
|
|
5443
5973
|
offer,
|
|
5444
5974
|
onBook
|
|
5445
5975
|
}) {
|
|
5446
5976
|
const resolvedLogoUrl = brandLogoUrl?.trim();
|
|
5447
|
-
const [failedLogoUrl, setFailedLogoUrl] =
|
|
5977
|
+
const [failedLogoUrl, setFailedLogoUrl] = useState7(null);
|
|
5448
5978
|
const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
|
|
5449
5979
|
const cards = message.role === "agent" ? extractToolCards(message.text) : [];
|
|
5450
5980
|
const extractedOffers = cards.filter(
|
|
@@ -5459,7 +5989,34 @@ function MessageBubble({
|
|
|
5459
5989
|
if (message.role === "visitor") {
|
|
5460
5990
|
const visitorText = visitorMessageDisplayText(message).trim();
|
|
5461
5991
|
if (!visitorText) return null;
|
|
5462
|
-
return /* @__PURE__ */
|
|
5992
|
+
return /* @__PURE__ */ jsxs8(
|
|
5993
|
+
"article",
|
|
5994
|
+
{
|
|
5995
|
+
className: "message-bubble message-bubble--visitor",
|
|
5996
|
+
"aria-label": "Message from visitor",
|
|
5997
|
+
children: [
|
|
5998
|
+
showProvenance ? /* @__PURE__ */ jsx8("span", { className: "message-bubble__speaker", children: "You \xB7 Visitor" }) : null,
|
|
5999
|
+
/* @__PURE__ */ jsx8("p", { className: "message-bubble__text", children: visitorText })
|
|
6000
|
+
]
|
|
6001
|
+
}
|
|
6002
|
+
);
|
|
6003
|
+
}
|
|
6004
|
+
if (message.role === "human") {
|
|
6005
|
+
return /* @__PURE__ */ jsxs8(
|
|
6006
|
+
"article",
|
|
6007
|
+
{
|
|
6008
|
+
className: "message-bubble message-bubble--human",
|
|
6009
|
+
"aria-label": `Message from ${message.representative.name}`,
|
|
6010
|
+
children: [
|
|
6011
|
+
/* @__PURE__ */ jsxs8("strong", { className: "message-bubble__representative", children: [
|
|
6012
|
+
message.representative.name,
|
|
6013
|
+
" ",
|
|
6014
|
+
/* @__PURE__ */ jsx8("span", { children: "Human representative" })
|
|
6015
|
+
] }),
|
|
6016
|
+
/* @__PURE__ */ jsx8("p", { className: "message-bubble__text", children: message.text })
|
|
6017
|
+
]
|
|
6018
|
+
}
|
|
6019
|
+
);
|
|
5463
6020
|
}
|
|
5464
6021
|
const citations = message.citations ?? [];
|
|
5465
6022
|
const agentText = /* @__PURE__ */ jsxs8("div", { className: "message-bubble__text", children: [
|
|
@@ -5496,35 +6053,43 @@ function MessageBubble({
|
|
|
5496
6053
|
] });
|
|
5497
6054
|
if (!displayText && !message.searchResults?.length && offers.length === 0)
|
|
5498
6055
|
return null;
|
|
5499
|
-
return /* @__PURE__ */ jsxs8(
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
6056
|
+
return /* @__PURE__ */ jsxs8(
|
|
6057
|
+
"article",
|
|
6058
|
+
{
|
|
6059
|
+
className: "message-bubble message-bubble--agent",
|
|
6060
|
+
"aria-label": "Message from AI assistant",
|
|
6061
|
+
children: [
|
|
6062
|
+
showProvenance ? /* @__PURE__ */ jsx8("span", { className: "message-bubble__speaker", children: "AI assistant" }) : null,
|
|
6063
|
+
displayText || message.searchResults?.length ? showBrandLogo ? /* @__PURE__ */ jsxs8("div", { className: "message-bubble__agent-row", children: [
|
|
6064
|
+
/* @__PURE__ */ jsx8("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ jsx8(
|
|
6065
|
+
"img",
|
|
6066
|
+
{
|
|
6067
|
+
src: resolvedLogoUrl,
|
|
6068
|
+
alt: "",
|
|
6069
|
+
onError: () => {
|
|
6070
|
+
setFailedLogoUrl(resolvedLogoUrl ?? null);
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
) }),
|
|
6074
|
+
agentText
|
|
6075
|
+
] }) : agentText : null,
|
|
6076
|
+
offers.map((nextOffer, index) => /* @__PURE__ */ jsx8(
|
|
6077
|
+
BookingCard,
|
|
6078
|
+
{
|
|
6079
|
+
disabled: bookingDisabled,
|
|
6080
|
+
readOnly: bookingReadOnly,
|
|
6081
|
+
offer: nextOffer,
|
|
6082
|
+
onBook
|
|
6083
|
+
},
|
|
6084
|
+
`${bookingOfferIdentityKey(nextOffer)}-${index}`
|
|
6085
|
+
))
|
|
6086
|
+
]
|
|
6087
|
+
}
|
|
6088
|
+
);
|
|
5524
6089
|
}
|
|
5525
6090
|
|
|
5526
6091
|
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
5527
|
-
import { useState as
|
|
6092
|
+
import { useState as useState9 } from "react";
|
|
5528
6093
|
|
|
5529
6094
|
// src/react/components/ConfirmationCard/ConfirmationCard.tsx
|
|
5530
6095
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
@@ -5566,7 +6131,7 @@ function ConfirmationCard({
|
|
|
5566
6131
|
}
|
|
5567
6132
|
|
|
5568
6133
|
// src/react/components/ToolInputCard/ToolInputCard.tsx
|
|
5569
|
-
import { useState as
|
|
6134
|
+
import { useState as useState8 } from "react";
|
|
5570
6135
|
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5571
6136
|
function isRecord5(value) {
|
|
5572
6137
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -5689,13 +6254,13 @@ function valuesMatch(left, right) {
|
|
|
5689
6254
|
}
|
|
5690
6255
|
function FieldDescription({
|
|
5691
6256
|
field,
|
|
5692
|
-
id
|
|
6257
|
+
id: id2
|
|
5693
6258
|
}) {
|
|
5694
|
-
return field.description ? /* @__PURE__ */ jsx10("span", { id, className: "tool-input-card__description", children: field.description }) : null;
|
|
6259
|
+
return field.description ? /* @__PURE__ */ jsx10("span", { id: id2, className: "tool-input-card__description", children: field.description }) : null;
|
|
5695
6260
|
}
|
|
5696
6261
|
function ChoiceField({
|
|
5697
6262
|
field,
|
|
5698
|
-
id,
|
|
6263
|
+
id: id2,
|
|
5699
6264
|
value,
|
|
5700
6265
|
disabled,
|
|
5701
6266
|
describedBy,
|
|
@@ -5713,7 +6278,7 @@ function ChoiceField({
|
|
|
5713
6278
|
return /* @__PURE__ */ jsxs10(
|
|
5714
6279
|
"select",
|
|
5715
6280
|
{
|
|
5716
|
-
id,
|
|
6281
|
+
id: id2,
|
|
5717
6282
|
value: selectedIndex >= 0 ? String(selectedIndex) : "",
|
|
5718
6283
|
disabled,
|
|
5719
6284
|
required: field.required,
|
|
@@ -5736,7 +6301,7 @@ function ChoiceField({
|
|
|
5736
6301
|
{
|
|
5737
6302
|
className: "tool-input-card__choices",
|
|
5738
6303
|
role: multiple ? "group" : "radiogroup",
|
|
5739
|
-
"aria-labelledby": `${
|
|
6304
|
+
"aria-labelledby": `${id2}-label`,
|
|
5740
6305
|
"aria-describedby": describedBy,
|
|
5741
6306
|
"aria-invalid": Boolean(error),
|
|
5742
6307
|
"aria-required": field.required,
|
|
@@ -5747,7 +6312,7 @@ function ChoiceField({
|
|
|
5747
6312
|
"input",
|
|
5748
6313
|
{
|
|
5749
6314
|
type: multiple ? "checkbox" : "radio",
|
|
5750
|
-
name:
|
|
6315
|
+
name: id2,
|
|
5751
6316
|
value: String(index),
|
|
5752
6317
|
checked,
|
|
5753
6318
|
disabled,
|
|
@@ -5775,22 +6340,22 @@ function ToolField({
|
|
|
5775
6340
|
disabled,
|
|
5776
6341
|
error,
|
|
5777
6342
|
field,
|
|
5778
|
-
id,
|
|
6343
|
+
id: id2,
|
|
5779
6344
|
value,
|
|
5780
6345
|
onBlur,
|
|
5781
6346
|
onChange
|
|
5782
6347
|
}) {
|
|
5783
6348
|
const describedBy = [
|
|
5784
|
-
field.description ? `${
|
|
5785
|
-
error ? `${
|
|
6349
|
+
field.description ? `${id2}-description` : "",
|
|
6350
|
+
error ? `${id2}-error` : ""
|
|
5786
6351
|
].filter(Boolean).join(" ");
|
|
5787
6352
|
if (field.kind === "checkbox" || field.kind === "confirmation") {
|
|
5788
6353
|
return /* @__PURE__ */ jsxs10("div", { className: "tool-input-card__field", children: [
|
|
5789
|
-
/* @__PURE__ */ jsxs10("label", { className: "tool-input-card__check", htmlFor:
|
|
6354
|
+
/* @__PURE__ */ jsxs10("label", { className: "tool-input-card__check", htmlFor: id2, children: [
|
|
5790
6355
|
/* @__PURE__ */ jsx10(
|
|
5791
6356
|
"input",
|
|
5792
6357
|
{
|
|
5793
|
-
id,
|
|
6358
|
+
id: id2,
|
|
5794
6359
|
type: "checkbox",
|
|
5795
6360
|
checked: value === true,
|
|
5796
6361
|
disabled,
|
|
@@ -5802,18 +6367,18 @@ function ToolField({
|
|
|
5802
6367
|
),
|
|
5803
6368
|
/* @__PURE__ */ jsxs10("span", { children: [
|
|
5804
6369
|
/* @__PURE__ */ jsx10("strong", { children: field.label }),
|
|
5805
|
-
field.description ? /* @__PURE__ */ jsx10("span", { id: `${
|
|
6370
|
+
field.description ? /* @__PURE__ */ jsx10("span", { id: `${id2}-description`, children: field.description }) : null
|
|
5806
6371
|
] })
|
|
5807
6372
|
] }),
|
|
5808
|
-
error ? /* @__PURE__ */ jsx10("span", { id: `${
|
|
6373
|
+
error ? /* @__PURE__ */ jsx10("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
|
|
5809
6374
|
] });
|
|
5810
6375
|
}
|
|
5811
|
-
const label = /* @__PURE__ */ jsxs10("label", { id: `${
|
|
6376
|
+
const label = /* @__PURE__ */ jsxs10("label", { id: `${id2}-label`, htmlFor: id2, children: [
|
|
5812
6377
|
field.label,
|
|
5813
6378
|
field.required ? /* @__PURE__ */ jsx10("span", { "aria-hidden": "true", children: " *" }) : null
|
|
5814
6379
|
] });
|
|
5815
6380
|
const common = {
|
|
5816
|
-
id,
|
|
6381
|
+
id: id2,
|
|
5817
6382
|
disabled,
|
|
5818
6383
|
required: field.required,
|
|
5819
6384
|
"aria-describedby": describedBy || void 0,
|
|
@@ -5826,7 +6391,7 @@ function ToolField({
|
|
|
5826
6391
|
ChoiceField,
|
|
5827
6392
|
{
|
|
5828
6393
|
field,
|
|
5829
|
-
id,
|
|
6394
|
+
id: id2,
|
|
5830
6395
|
value,
|
|
5831
6396
|
disabled,
|
|
5832
6397
|
describedBy: describedBy || void 0,
|
|
@@ -5863,7 +6428,7 @@ function ToolField({
|
|
|
5863
6428
|
onChange: (event) => onChange(Number(event.target.value))
|
|
5864
6429
|
}
|
|
5865
6430
|
),
|
|
5866
|
-
/* @__PURE__ */ jsx10("output", { htmlFor:
|
|
6431
|
+
/* @__PURE__ */ jsx10("output", { htmlFor: id2, children: numericValue })
|
|
5867
6432
|
] });
|
|
5868
6433
|
} else {
|
|
5869
6434
|
const type = field.kind === "date-time" ? "datetime-local" : field.kind === "calendar" ? "date" : field.kind;
|
|
@@ -5887,9 +6452,9 @@ function ToolField({
|
|
|
5887
6452
|
}
|
|
5888
6453
|
return /* @__PURE__ */ jsxs10("div", { className: "tool-input-card__field", children: [
|
|
5889
6454
|
label,
|
|
5890
|
-
/* @__PURE__ */ jsx10(FieldDescription, { field, id: `${
|
|
6455
|
+
/* @__PURE__ */ jsx10(FieldDescription, { field, id: `${id2}-description` }),
|
|
5891
6456
|
control,
|
|
5892
|
-
error ? /* @__PURE__ */ jsx10("span", { id: `${
|
|
6457
|
+
error ? /* @__PURE__ */ jsx10("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
|
|
5893
6458
|
] });
|
|
5894
6459
|
}
|
|
5895
6460
|
function ToolInputCard({
|
|
@@ -5897,11 +6462,11 @@ function ToolInputCard({
|
|
|
5897
6462
|
surface,
|
|
5898
6463
|
onSubmit
|
|
5899
6464
|
}) {
|
|
5900
|
-
const [values, setValues] =
|
|
6465
|
+
const [values, setValues] = useState8(
|
|
5901
6466
|
() => initialValues(surface)
|
|
5902
6467
|
);
|
|
5903
|
-
const [touched, setTouched] =
|
|
5904
|
-
const [submitted, setSubmitted] =
|
|
6468
|
+
const [touched, setTouched] = useState8(() => /* @__PURE__ */ new Set());
|
|
6469
|
+
const [submitted, setSubmitted] = useState8(false);
|
|
5905
6470
|
const errors = Object.fromEntries(
|
|
5906
6471
|
surface.fields.map((field) => [
|
|
5907
6472
|
field.path,
|
|
@@ -5994,7 +6559,7 @@ function HumanInputCard({
|
|
|
5994
6559
|
request,
|
|
5995
6560
|
onRespond
|
|
5996
6561
|
}) {
|
|
5997
|
-
const [
|
|
6562
|
+
const [text3, setText] = useState9("");
|
|
5998
6563
|
const options = request.options ?? [];
|
|
5999
6564
|
const showText = request.display === "text" || request.allowFreeform && options.length === 0;
|
|
6000
6565
|
if (request.ui) {
|
|
@@ -6019,7 +6584,7 @@ function HumanInputCard({
|
|
|
6019
6584
|
}
|
|
6020
6585
|
function submitText(event) {
|
|
6021
6586
|
event.preventDefault();
|
|
6022
|
-
const value =
|
|
6587
|
+
const value = text3.trim();
|
|
6023
6588
|
if (!value || disabled) return;
|
|
6024
6589
|
onRespond?.({ requestId: request.requestId, text: value });
|
|
6025
6590
|
}
|
|
@@ -6037,12 +6602,12 @@ function HumanInputCard({
|
|
|
6037
6602
|
"input",
|
|
6038
6603
|
{
|
|
6039
6604
|
id: `human-input-text-${request.requestId}`,
|
|
6040
|
-
value:
|
|
6605
|
+
value: text3,
|
|
6041
6606
|
disabled,
|
|
6042
6607
|
onChange: (event) => setText(event.target.value)
|
|
6043
6608
|
}
|
|
6044
6609
|
),
|
|
6045
|
-
/* @__PURE__ */ jsx11("button", { type: "submit", disabled: disabled || !
|
|
6610
|
+
/* @__PURE__ */ jsx11("button", { type: "submit", disabled: disabled || !text3.trim(), children: "Send" })
|
|
6046
6611
|
] })
|
|
6047
6612
|
] }) : null,
|
|
6048
6613
|
!showText && options.length === 0 ? /* @__PURE__ */ jsx11("p", { className: "human-input-card__unavailable", role: "status", children: "This request can\u2019t be answered here." }) : null
|
|
@@ -6052,7 +6617,7 @@ function HumanInputCard({
|
|
|
6052
6617
|
}
|
|
6053
6618
|
|
|
6054
6619
|
// src/react/components/LocationConsent/LocationConsent.tsx
|
|
6055
|
-
import { useEffect as
|
|
6620
|
+
import { useEffect as useEffect7, useRef as useRef7, useState as useState10 } from "react";
|
|
6056
6621
|
|
|
6057
6622
|
// src/runtime/location-consent.ts
|
|
6058
6623
|
function isLocationConsentRequest(request) {
|
|
@@ -6100,16 +6665,16 @@ function LocationConsent({
|
|
|
6100
6665
|
request,
|
|
6101
6666
|
onRespond
|
|
6102
6667
|
}) {
|
|
6103
|
-
const [preference, setPreference] =
|
|
6104
|
-
const [locating, setLocating] =
|
|
6105
|
-
const [error, setError] =
|
|
6106
|
-
const respond =
|
|
6107
|
-
const answered =
|
|
6668
|
+
const [preference, setPreference] = useState10("unset");
|
|
6669
|
+
const [locating, setLocating] = useState10(false);
|
|
6670
|
+
const [error, setError] = useState10(null);
|
|
6671
|
+
const respond = useRef7(onRespond);
|
|
6672
|
+
const answered = useRef7(null);
|
|
6108
6673
|
const requestId = request?.requestId;
|
|
6109
|
-
|
|
6674
|
+
useEffect7(() => {
|
|
6110
6675
|
respond.current = onRespond;
|
|
6111
6676
|
}, [onRespond]);
|
|
6112
|
-
|
|
6677
|
+
useEffect7(() => {
|
|
6113
6678
|
if (!requestId || preference === "unset" || answered.current === requestId || !respond.current)
|
|
6114
6679
|
return;
|
|
6115
6680
|
if (preference === "off") {
|
|
@@ -6451,6 +7016,7 @@ function ChevronDownIcon() {
|
|
|
6451
7016
|
var DEFAULT_DISCLAIMER_LABEL = "Agent can make mistakes. Check important info.";
|
|
6452
7017
|
function AgentRail({
|
|
6453
7018
|
state,
|
|
7019
|
+
handoff,
|
|
6454
7020
|
theme,
|
|
6455
7021
|
colorScheme = "auto",
|
|
6456
7022
|
brandLabel = "",
|
|
@@ -6476,44 +7042,51 @@ function AgentRail({
|
|
|
6476
7042
|
onInputResponse,
|
|
6477
7043
|
onToolInput
|
|
6478
7044
|
}) {
|
|
6479
|
-
const railRef =
|
|
6480
|
-
const overlayRef =
|
|
6481
|
-
const transcriptRef =
|
|
6482
|
-
const responseRef =
|
|
6483
|
-
const threadRef =
|
|
6484
|
-
const lastScrolledVisitorIdRef =
|
|
6485
|
-
const pinnedToBottomRef =
|
|
6486
|
-
const smoothScrollToLatestRef =
|
|
6487
|
-
const lockedTranscriptScrollTopRef =
|
|
6488
|
-
const [showJumpToLatest, setShowJumpToLatest] =
|
|
6489
|
-
const [receiptOpen, setReceiptOpen] =
|
|
7045
|
+
const railRef = useRef8(null);
|
|
7046
|
+
const overlayRef = useRef8(null);
|
|
7047
|
+
const transcriptRef = useRef8(null);
|
|
7048
|
+
const responseRef = useRef8(null);
|
|
7049
|
+
const threadRef = useRef8(null);
|
|
7050
|
+
const lastScrolledVisitorIdRef = useRef8(void 0);
|
|
7051
|
+
const pinnedToBottomRef = useRef8(true);
|
|
7052
|
+
const smoothScrollToLatestRef = useRef8(false);
|
|
7053
|
+
const lockedTranscriptScrollTopRef = useRef8(null);
|
|
7054
|
+
const [showJumpToLatest, setShowJumpToLatest] = useState11(false);
|
|
7055
|
+
const [receiptOpen, setReceiptOpen] = useState11(false);
|
|
6490
7056
|
const resolvedBrandLabel = brandLabel.trim();
|
|
6491
7057
|
const resolvedBrandLogoUrl = brandLogoUrl?.trim();
|
|
6492
7058
|
const resolvedDisclaimerLabel = disclaimerLabel === void 0 ? DEFAULT_DISCLAIMER_LABEL : disclaimerLabel;
|
|
6493
|
-
const [failedLogoUrl, setFailedLogoUrl] =
|
|
7059
|
+
const [failedLogoUrl, setFailedLogoUrl] = useState11(null);
|
|
6494
7060
|
const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
|
|
6495
7061
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
6496
7062
|
const railStyle = agentThemeStyle(theme, resolvedColorScheme);
|
|
6497
|
-
const
|
|
7063
|
+
const handoffActive = Boolean(handoff?.blocksAI);
|
|
7064
|
+
const handoffStatus = handoff?.page?.status;
|
|
7065
|
+
const locationRequest = handoffActive ? void 0 : state.pendingInputs?.find(isLocationConsentRequest);
|
|
6498
7066
|
const pendingInputRequests = (state.pendingInputs ?? []).filter(
|
|
6499
|
-
(request) => !isLocationConsentRequest(request) && shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
7067
|
+
(request) => !handoffActive && !isLocationConsentRequest(request) && shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
6500
7068
|
);
|
|
6501
7069
|
const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && (pendingInputRequests.length > 0 || Boolean(locationRequest));
|
|
6502
|
-
const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
|
|
7070
|
+
const semanticSurfaceDisabled = handoffActive || isBusy && state.phase !== "waiting-input";
|
|
6503
7071
|
const visitorToolResults = (state.toolResults ?? []).filter(
|
|
6504
7072
|
isRenderableVisitorToolResult
|
|
6505
7073
|
);
|
|
6506
7074
|
const activeVisitorToolInput = [...visitorToolResults].reverse().find((result) => result.kind === "input");
|
|
6507
7075
|
const visibleVisitorToolResults = activeVisitorToolInput ? [activeVisitorToolInput] : visitorToolResults;
|
|
6508
|
-
const activityActive = state.toolSteps.some(
|
|
7076
|
+
const activityActive = state.toolSteps.some(
|
|
7077
|
+
(step) => step.state === "active"
|
|
7078
|
+
);
|
|
6509
7079
|
const showActivity = state.toolSteps.length > 0 && pendingInputRequests.length === 0 && !state.pendingOffer && (activityActive || isBusy && !state.streamingText);
|
|
6510
|
-
const activitySteps = activityActive || !isBusy || state.streamingText ? state.toolSteps : [
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
7080
|
+
const activitySteps = activityActive || !isBusy || state.streamingText ? state.toolSteps : [
|
|
7081
|
+
...state.toolSteps,
|
|
7082
|
+
{
|
|
7083
|
+
id: "preparing-answer",
|
|
7084
|
+
kind: "tool",
|
|
7085
|
+
label: "Preparing your answer",
|
|
7086
|
+
detail: "Preparing your answer",
|
|
7087
|
+
state: "active"
|
|
7088
|
+
}
|
|
7089
|
+
];
|
|
6517
7090
|
const hasVisitorMessages2 = state.messages.some(
|
|
6518
7091
|
(message) => message.role === "visitor"
|
|
6519
7092
|
);
|
|
@@ -6542,7 +7115,7 @@ function AgentRail({
|
|
|
6542
7115
|
}
|
|
6543
7116
|
}
|
|
6544
7117
|
const lastIsAgent = lastMessage?.role === "agent";
|
|
6545
|
-
const showMessageActions = state.phase === "complete" && lastIsAgent && hasVisitorMessages2 && Boolean(hideToolCardFences(lastMessage.text).trim());
|
|
7118
|
+
const showMessageActions = !handoffActive && state.phase === "complete" && lastIsAgent && hasVisitorMessages2 && Boolean(hideToolCardFences(lastMessage.text).trim());
|
|
6546
7119
|
const streamingMessage = state.streamingText && !lastIsAgent ? {
|
|
6547
7120
|
createdAt: 0,
|
|
6548
7121
|
id: "streaming-response",
|
|
@@ -6584,7 +7157,7 @@ function AgentRail({
|
|
|
6584
7157
|
...visibleVisitorToolResults
|
|
6585
7158
|
].reverse().find((result) => result.kind !== "input")?.id;
|
|
6586
7159
|
const receiptSteps = answerReceipt && state.toolSteps.length > 0 ? state.toolSteps : void 0;
|
|
6587
|
-
|
|
7160
|
+
useEffect8(() => {
|
|
6588
7161
|
if (state.phase !== "complete") {
|
|
6589
7162
|
setReceiptOpen(false);
|
|
6590
7163
|
}
|
|
@@ -6606,7 +7179,7 @@ function AgentRail({
|
|
|
6606
7179
|
onFollowUpSelect?.(label);
|
|
6607
7180
|
}
|
|
6608
7181
|
const latestVisitorId = visibleMessages[lastVisitorIndex]?.id;
|
|
6609
|
-
|
|
7182
|
+
useEffect8(() => {
|
|
6610
7183
|
const node = transcriptRef.current;
|
|
6611
7184
|
if (!node) return;
|
|
6612
7185
|
if (latestVisitorId !== lastScrolledVisitorIdRef.current) {
|
|
@@ -6636,7 +7209,7 @@ function AgentRail({
|
|
|
6636
7209
|
state.followUps,
|
|
6637
7210
|
state.journey
|
|
6638
7211
|
]);
|
|
6639
|
-
|
|
7212
|
+
useEffect8(() => {
|
|
6640
7213
|
const node = transcriptRef.current;
|
|
6641
7214
|
if (!node) return;
|
|
6642
7215
|
const handleScroll = () => {
|
|
@@ -6650,7 +7223,7 @@ function AgentRail({
|
|
|
6650
7223
|
handleScroll();
|
|
6651
7224
|
return () => node.removeEventListener("scroll", handleScroll);
|
|
6652
7225
|
}, []);
|
|
6653
|
-
|
|
7226
|
+
useEffect8(() => {
|
|
6654
7227
|
if (!receiptOpen) {
|
|
6655
7228
|
lockedTranscriptScrollTopRef.current = null;
|
|
6656
7229
|
return;
|
|
@@ -6702,249 +7275,302 @@ function AgentRail({
|
|
|
6702
7275
|
autoFocus: mobileFullscreen || expanded,
|
|
6703
7276
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
6704
7277
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
6705
|
-
children: /* @__PURE__ */ jsxs17(
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
7278
|
+
children: /* @__PURE__ */ jsxs17(AgentRailOverlayContext.Provider, { value: { railRef, overlayRef }, children: [
|
|
7279
|
+
/* @__PURE__ */ jsxs17("div", { className: "agent-rail__surface", inert: receiptOpen || void 0, children: [
|
|
7280
|
+
/* @__PURE__ */ jsx18("header", { className: "agent-rail__header", children: /* @__PURE__ */ jsxs17("div", { className: "agent-rail__brand-row", children: [
|
|
7281
|
+
onCollapse ? /* @__PURE__ */ jsx18(
|
|
7282
|
+
"button",
|
|
7283
|
+
{
|
|
7284
|
+
type: "button",
|
|
7285
|
+
className: "agent-rail__collapse",
|
|
7286
|
+
"aria-label": "Collapse assist",
|
|
7287
|
+
onClick: onCollapse,
|
|
7288
|
+
children: /* @__PURE__ */ jsx18(MinimizeIcon, {})
|
|
7289
|
+
}
|
|
7290
|
+
) : onClose ? /* @__PURE__ */ jsx18(
|
|
7291
|
+
"button",
|
|
7292
|
+
{
|
|
7293
|
+
type: "button",
|
|
7294
|
+
className: "agent-rail__close",
|
|
7295
|
+
"aria-label": "Close agent",
|
|
7296
|
+
onClick: onClose,
|
|
7297
|
+
children: /* @__PURE__ */ jsx18(CloseIcon2, {})
|
|
7298
|
+
}
|
|
7299
|
+
) : /* @__PURE__ */ jsx18("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
|
|
7300
|
+
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ jsxs17("span", { className: "agent-rail__identity", children: [
|
|
7301
|
+
showBrandLogo ? /* @__PURE__ */ jsx18("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
|
|
7302
|
+
"img",
|
|
7303
|
+
{
|
|
7304
|
+
className: "agent-rail__brand-logo",
|
|
7305
|
+
src: resolvedBrandLogoUrl,
|
|
7306
|
+
alt: "",
|
|
7307
|
+
onError: () => {
|
|
7308
|
+
setFailedLogoUrl(resolvedBrandLogoUrl ?? null);
|
|
6729
7309
|
}
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
7310
|
+
}
|
|
7311
|
+
) }) : null,
|
|
7312
|
+
resolvedBrandLabel ? /* @__PURE__ */ jsx18("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
|
|
7313
|
+
] }) : null,
|
|
7314
|
+
/* @__PURE__ */ jsxs17("span", { className: "agent-rail__actions", children: [
|
|
7315
|
+
onReset ? /* @__PURE__ */ jsx18(
|
|
7316
|
+
"button",
|
|
7317
|
+
{
|
|
7318
|
+
type: "button",
|
|
7319
|
+
className: "agent-rail__new-chat",
|
|
7320
|
+
"aria-label": "Start a new conversation",
|
|
7321
|
+
disabled: !hasVisitorMessages2 || handoffActive && !handoff?.canReset,
|
|
7322
|
+
onClick: handleReset,
|
|
7323
|
+
children: /* @__PURE__ */ jsx18(NewChatIcon, {})
|
|
7324
|
+
}
|
|
7325
|
+
) : null,
|
|
7326
|
+
onExpandToggle ? /* @__PURE__ */ jsx18(
|
|
7327
|
+
"button",
|
|
7328
|
+
{
|
|
7329
|
+
type: "button",
|
|
7330
|
+
className: "agent-rail__expand",
|
|
7331
|
+
"aria-label": expanded ? "Exit full screen" : "Open full screen",
|
|
7332
|
+
onClick: onExpandToggle,
|
|
7333
|
+
children: expanded ? /* @__PURE__ */ jsx18(RestoreIcon, {}) : /* @__PURE__ */ jsx18(ExpandIcon, {})
|
|
7334
|
+
}
|
|
7335
|
+
) : null
|
|
7336
|
+
] })
|
|
7337
|
+
] }) }),
|
|
7338
|
+
/* @__PURE__ */ jsx18("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ jsxs17("div", { ref: threadRef, className: "agent-rail__thread", children: [
|
|
7339
|
+
!hasVisitorMessages2 ? /* @__PURE__ */ jsxs17("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
7340
|
+
greeting?.role === "agent" ? /* @__PURE__ */ jsx18(
|
|
7341
|
+
MessageBubble,
|
|
7342
|
+
{
|
|
7343
|
+
showProvenance: Boolean(
|
|
7344
|
+
handoff?.page?.enabled || handoffActive
|
|
7345
|
+
),
|
|
7346
|
+
message: greeting,
|
|
7347
|
+
bookingDisabled: isBusy || handoffActive,
|
|
7348
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7349
|
+
onBook
|
|
7350
|
+
}
|
|
7351
|
+
) : null,
|
|
7352
|
+
showIdleFollowUps ? /* @__PURE__ */ jsx18("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ jsx18(
|
|
7353
|
+
FollowUpChips,
|
|
7354
|
+
{
|
|
7355
|
+
suggestions: state.followUps,
|
|
7356
|
+
disabled: isBusy || handoffActive,
|
|
7357
|
+
label: "Start here",
|
|
7358
|
+
onSelect: (suggestion) => handleFollowUpSelect(suggestion.label)
|
|
7359
|
+
}
|
|
7360
|
+
) }) : null,
|
|
7361
|
+
showActivity ? /* @__PURE__ */ jsx18(
|
|
7362
|
+
AgentActivityBubble,
|
|
7363
|
+
{
|
|
7364
|
+
brandLabel: resolvedBrandLabel,
|
|
7365
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7366
|
+
failed: state.phase === "error",
|
|
7367
|
+
steps: activitySteps
|
|
7368
|
+
}
|
|
7369
|
+
) : null,
|
|
7370
|
+
visibleVisitorToolResults.map((result) => /* @__PURE__ */ jsx18(
|
|
7371
|
+
VisitorToolResultView,
|
|
7372
|
+
{
|
|
7373
|
+
result,
|
|
7374
|
+
disabled: semanticSurfaceDisabled,
|
|
7375
|
+
onToolInput
|
|
7376
|
+
},
|
|
7377
|
+
result.id
|
|
7378
|
+
)),
|
|
7379
|
+
pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ jsx18(
|
|
7380
|
+
HumanInputCard,
|
|
7381
|
+
{
|
|
7382
|
+
request,
|
|
7383
|
+
onRespond: onInputResponse
|
|
7384
|
+
},
|
|
7385
|
+
request.requestId
|
|
7386
|
+
))
|
|
7387
|
+
] }) : null,
|
|
7388
|
+
transcriptMessages.map((message, index) => /* @__PURE__ */ jsxs17(
|
|
7389
|
+
"div",
|
|
7390
|
+
{
|
|
7391
|
+
ref: index === lastVisitorIndex + 1 ? responseRef : void 0,
|
|
7392
|
+
className: "agent-rail__turn-block",
|
|
7393
|
+
children: [
|
|
7394
|
+
message.role === "agent" ? message.toolResults?.map((result) => /* @__PURE__ */ jsx18(
|
|
7395
|
+
VisitorToolResultView,
|
|
6759
7396
|
{
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
}
|
|
6766
|
-
) : null
|
|
6767
|
-
] })
|
|
6768
|
-
] }) }),
|
|
6769
|
-
/* @__PURE__ */ jsx18("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ jsxs17("div", { ref: threadRef, className: "agent-rail__thread", children: [
|
|
6770
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ jsxs17("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
6771
|
-
greeting?.role === "agent" ? /* @__PURE__ */ jsx18(
|
|
7397
|
+
result
|
|
7398
|
+
},
|
|
7399
|
+
result.id
|
|
7400
|
+
)) : null,
|
|
7401
|
+
/* @__PURE__ */ jsx18(
|
|
6772
7402
|
MessageBubble,
|
|
6773
7403
|
{
|
|
6774
|
-
|
|
6775
|
-
|
|
7404
|
+
showProvenance: Boolean(
|
|
7405
|
+
handoff?.page?.enabled || handoffActive
|
|
7406
|
+
),
|
|
7407
|
+
message,
|
|
7408
|
+
bookingDisabled: isBusy || handoffActive,
|
|
6776
7409
|
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7410
|
+
offer: index === transcriptMessages.length - 1 ? state.pendingOffer : void 0,
|
|
6777
7411
|
onBook
|
|
6778
7412
|
}
|
|
6779
|
-
)
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
{
|
|
6783
|
-
suggestions: state.followUps,
|
|
6784
|
-
disabled: isBusy,
|
|
6785
|
-
label: "Start here",
|
|
6786
|
-
onSelect: (suggestion) => handleFollowUpSelect(suggestion.label)
|
|
6787
|
-
}
|
|
6788
|
-
) }) : null,
|
|
6789
|
-
showActivity ? /* @__PURE__ */ jsx18(
|
|
6790
|
-
AgentActivityBubble,
|
|
7413
|
+
),
|
|
7414
|
+
index === lastAgentIndex && showMessageActions && message.role === "agent" ? /* @__PURE__ */ jsx18(
|
|
7415
|
+
MessageActions,
|
|
6791
7416
|
{
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
7417
|
+
answeredAt: message.createdAt,
|
|
7418
|
+
copyText: hideToolCardFences(message.text).trim() || message.text,
|
|
7419
|
+
readAloud,
|
|
7420
|
+
receiptSteps,
|
|
7421
|
+
onOpenReceipt: receiptSteps ? openReceipt : void 0,
|
|
7422
|
+
onRegenerate: onRegenerate ? handleRegenerate : void 0,
|
|
7423
|
+
onFeedback: onFeedback ? (rating) => onFeedback(rating, message) : void 0
|
|
6796
7424
|
}
|
|
6797
7425
|
) : null,
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
/* @__PURE__ */ jsx18("p", { children: state.error })
|
|
6882
|
-
] }),
|
|
6883
|
-
onRetry ? /* @__PURE__ */ jsx18("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
6884
|
-
] }) : null
|
|
6885
|
-
] }) }),
|
|
6886
|
-
showDisclaimerLabel ? /* @__PURE__ */ jsx18("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ jsx18("p", { children: disclaimerLink ? /* @__PURE__ */ jsx18(
|
|
6887
|
-
"a",
|
|
7426
|
+
index === lastVisitorIndex ? /* @__PURE__ */ jsxs17(Fragment4, { children: [
|
|
7427
|
+
showActivity ? /* @__PURE__ */ jsx18(
|
|
7428
|
+
AgentActivityBubble,
|
|
7429
|
+
{
|
|
7430
|
+
brandLabel: resolvedBrandLabel,
|
|
7431
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7432
|
+
failed: state.phase === "error",
|
|
7433
|
+
steps: activitySteps
|
|
7434
|
+
}
|
|
7435
|
+
) : null,
|
|
7436
|
+
visibleVisitorToolResults.map((result) => /* @__PURE__ */ jsx18(
|
|
7437
|
+
VisitorToolResultView,
|
|
7438
|
+
{
|
|
7439
|
+
result,
|
|
7440
|
+
disabled: semanticSurfaceDisabled,
|
|
7441
|
+
onToolInput
|
|
7442
|
+
},
|
|
7443
|
+
result.id
|
|
7444
|
+
)),
|
|
7445
|
+
pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ jsx18(
|
|
7446
|
+
HumanInputCard,
|
|
7447
|
+
{
|
|
7448
|
+
request,
|
|
7449
|
+
onRespond: onInputResponse
|
|
7450
|
+
},
|
|
7451
|
+
request.requestId
|
|
7452
|
+
))
|
|
7453
|
+
] }) : null
|
|
7454
|
+
]
|
|
7455
|
+
},
|
|
7456
|
+
message.role === "agent" && transcriptMessages[index - 1]?.role === "visitor" ? `answer-${transcriptMessages[index - 1]?.id}` : message.id
|
|
7457
|
+
)),
|
|
7458
|
+
waitingForBooking ? /* @__PURE__ */ jsx18(BookingCardLoader, {}) : null,
|
|
7459
|
+
state.error ? /* @__PURE__ */ jsxs17("section", { className: "agent-rail__error", role: "alert", children: [
|
|
7460
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
7461
|
+
/* @__PURE__ */ jsx18("strong", { children: "Something went wrong" }),
|
|
7462
|
+
/* @__PURE__ */ jsx18("p", { children: state.error })
|
|
7463
|
+
] }),
|
|
7464
|
+
onRetry ? /* @__PURE__ */ jsx18("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
7465
|
+
] }) : null
|
|
7466
|
+
] }) }),
|
|
7467
|
+
showDisclaimerLabel ? /* @__PURE__ */ jsx18("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ jsx18("p", { children: disclaimerLink ? /* @__PURE__ */ jsx18(
|
|
7468
|
+
"a",
|
|
7469
|
+
{
|
|
7470
|
+
href: disclaimerLink,
|
|
7471
|
+
rel: "noopener noreferrer",
|
|
7472
|
+
target: "_blank",
|
|
7473
|
+
children: resolvedDisclaimerLabel
|
|
7474
|
+
}
|
|
7475
|
+
) : resolvedDisclaimerLabel }) }) : null,
|
|
7476
|
+
handoff && (handoff.page?.enabled || handoffActive) ? /* @__PURE__ */ jsxs17("div", { className: "agent-rail__handoff", children: [
|
|
7477
|
+
/* @__PURE__ */ jsx18("p", { role: "status", "aria-live": "polite", children: !handoff.page ? "Checking conversation\u2026" : handoffStatus === "human" ? "Connected with support" : handoffStatus === "queued" ? "Waiting for a representative" : handoffStatus === "requesting" ? "Connecting you to support\u2026" : handoffStatus === "resolved" ? "Your conversation with support has ended" : handoffStatus === "failed" ? "We couldn\u2019t connect you to support" : "Need a person?" }),
|
|
7478
|
+
handoffStatus === "ai" ? /* @__PURE__ */ jsx18(
|
|
7479
|
+
"button",
|
|
7480
|
+
{
|
|
7481
|
+
type: "button",
|
|
7482
|
+
disabled: handoff.busy || handoff.hasPending || isBusy,
|
|
7483
|
+
onClick: () => void handoff.start(),
|
|
7484
|
+
children: "Talk to a person"
|
|
7485
|
+
}
|
|
7486
|
+
) : null,
|
|
7487
|
+
handoffStatus === "resolved" ? /* @__PURE__ */ jsx18(
|
|
7488
|
+
"button",
|
|
7489
|
+
{
|
|
7490
|
+
type: "button",
|
|
7491
|
+
disabled: handoff.busy || handoff.hasPending,
|
|
7492
|
+
onClick: () => void handoff.returnToAI(),
|
|
7493
|
+
children: "Return to AI"
|
|
7494
|
+
}
|
|
7495
|
+
) : null,
|
|
7496
|
+
handoffStatus === "failed" && onReset ? /* @__PURE__ */ jsx18(
|
|
7497
|
+
"button",
|
|
7498
|
+
{
|
|
7499
|
+
type: "button",
|
|
7500
|
+
disabled: !handoff.canReset,
|
|
7501
|
+
onClick: handleReset,
|
|
7502
|
+
children: "Start a new conversation"
|
|
7503
|
+
}
|
|
7504
|
+
) : null,
|
|
7505
|
+
handoff.error ? /* @__PURE__ */ jsxs17("div", { role: "alert", children: [
|
|
7506
|
+
/* @__PURE__ */ jsx18("span", { children: handoff.error }),
|
|
7507
|
+
/* @__PURE__ */ jsx18(
|
|
7508
|
+
"button",
|
|
6888
7509
|
{
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
children:
|
|
7510
|
+
type: "button",
|
|
7511
|
+
disabled: handoff.busy,
|
|
7512
|
+
onClick: () => void handoff.retry(),
|
|
7513
|
+
children: "Retry"
|
|
6893
7514
|
}
|
|
6894
|
-
)
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
className: "agent-rail__jump-to-latest",
|
|
6909
|
-
"aria-label": "Jump to the latest message",
|
|
6910
|
-
title: "Jump to the latest message",
|
|
6911
|
-
onClick: scrollToLatest,
|
|
6912
|
-
children: /* @__PURE__ */ jsx18(ChevronDownIcon, {})
|
|
6913
|
-
}
|
|
6914
|
-
) : null,
|
|
6915
|
-
/* @__PURE__ */ jsx18(
|
|
6916
|
-
Composer,
|
|
6917
|
-
{
|
|
6918
|
-
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
6919
|
-
disabled: isBusy,
|
|
6920
|
-
form: composerForm && lastMessage ? { ...composerForm, id: `${lastMessage.id}:${composerForm.id}` } : null,
|
|
6921
|
-
allowFormResume: !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
|
|
6922
|
-
placeholder: composerPlaceholder,
|
|
6923
|
-
onSubmit: handleSubmit
|
|
6924
|
-
},
|
|
6925
|
-
`${state.messages.find((message) => message.role === "visitor")?.id ?? "empty"}:${latestResultId ?? ""}`
|
|
6926
|
-
),
|
|
6927
|
-
poweredByLabel ? /* @__PURE__ */ jsx18("div", { className: "agent-rail__footer", children: /* @__PURE__ */ jsx18("p", { children: /* @__PURE__ */ jsx18("span", { children: poweredByLabel }) }) }) : null
|
|
6928
|
-
] })
|
|
6929
|
-
] }),
|
|
6930
|
-
/* @__PURE__ */ jsx18("div", { ref: overlayRef, className: "agent-rail__overlay" }),
|
|
6931
|
-
receiptOpen && receiptSteps ? /* @__PURE__ */ jsx18(
|
|
6932
|
-
AnswerReceiptDialog,
|
|
7515
|
+
)
|
|
7516
|
+
] }) : null
|
|
7517
|
+
] }) : null,
|
|
7518
|
+
/* @__PURE__ */ jsxs17("div", { className: "agent-rail__composer-wrap", children: [
|
|
7519
|
+
/* @__PURE__ */ jsx18(
|
|
7520
|
+
LocationConsent,
|
|
7521
|
+
{
|
|
7522
|
+
request: locationRequest,
|
|
7523
|
+
onRespond: onInputResponse
|
|
7524
|
+
},
|
|
7525
|
+
state.messages.find((message) => message.role === "visitor")?.id ?? "empty"
|
|
7526
|
+
),
|
|
7527
|
+
showJumpToLatest ? /* @__PURE__ */ jsx18(
|
|
7528
|
+
"button",
|
|
6933
7529
|
{
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
7530
|
+
type: "button",
|
|
7531
|
+
className: "agent-rail__jump-to-latest",
|
|
7532
|
+
"aria-label": "Jump to the latest message",
|
|
7533
|
+
title: "Jump to the latest message",
|
|
7534
|
+
onClick: scrollToLatest,
|
|
7535
|
+
children: /* @__PURE__ */ jsx18(ChevronDownIcon, {})
|
|
6937
7536
|
}
|
|
6938
|
-
) : null
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
7537
|
+
) : null,
|
|
7538
|
+
/* @__PURE__ */ jsx18(
|
|
7539
|
+
Composer,
|
|
7540
|
+
{
|
|
7541
|
+
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
7542
|
+
disabled: isBusy || Boolean(handoff?.busy || handoff?.hasPending) || handoffActive && !["requesting", "queued", "human"].includes(
|
|
7543
|
+
handoffStatus ?? ""
|
|
7544
|
+
),
|
|
7545
|
+
form: !handoffActive && composerForm && lastMessage ? {
|
|
7546
|
+
...composerForm,
|
|
7547
|
+
id: `${lastMessage.id}:${composerForm.id}`
|
|
7548
|
+
} : null,
|
|
7549
|
+
allowFormResume: !handoffActive && !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
|
|
7550
|
+
placeholder: handoffActive ? "Message support\u2026" : composerPlaceholder,
|
|
7551
|
+
onSubmit: handleSubmit
|
|
7552
|
+
},
|
|
7553
|
+
`${state.messages.find((message) => message.role === "visitor")?.id ?? "empty"}:${latestResultId ?? ""}`
|
|
7554
|
+
),
|
|
7555
|
+
poweredByLabel ? /* @__PURE__ */ jsx18("div", { className: "agent-rail__footer", children: /* @__PURE__ */ jsx18("p", { children: /* @__PURE__ */ jsx18("span", { children: poweredByLabel }) }) }) : null
|
|
7556
|
+
] })
|
|
7557
|
+
] }),
|
|
7558
|
+
/* @__PURE__ */ jsx18("div", { ref: overlayRef, className: "agent-rail__overlay" }),
|
|
7559
|
+
receiptOpen && receiptSteps ? /* @__PURE__ */ jsx18(
|
|
7560
|
+
AnswerReceiptDialog,
|
|
7561
|
+
{
|
|
7562
|
+
brandLabel: resolvedBrandLabel,
|
|
7563
|
+
onClose: () => setReceiptOpen(false),
|
|
7564
|
+
steps: receiptSteps
|
|
7565
|
+
}
|
|
7566
|
+
) : null
|
|
7567
|
+
] })
|
|
6942
7568
|
}
|
|
6943
7569
|
);
|
|
6944
7570
|
}
|
|
6945
7571
|
|
|
6946
7572
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
6947
|
-
import { useState as
|
|
7573
|
+
import { useState as useState12 } from "react";
|
|
6948
7574
|
import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
6949
7575
|
function ChatSparkIcon() {
|
|
6950
7576
|
return /* @__PURE__ */ jsxs18(
|
|
@@ -7025,8 +7651,8 @@ function TabMarkIcon({
|
|
|
7025
7651
|
}) {
|
|
7026
7652
|
const custom = customIconUrl?.trim();
|
|
7027
7653
|
const logo = logoUrl?.trim();
|
|
7028
|
-
const [customFailed, setCustomFailed] =
|
|
7029
|
-
const [logoFailed, setLogoFailed] =
|
|
7654
|
+
const [customFailed, setCustomFailed] = useState12(false);
|
|
7655
|
+
const [logoFailed, setLogoFailed] = useState12(false);
|
|
7030
7656
|
if (custom && !customFailed) {
|
|
7031
7657
|
return /* @__PURE__ */ jsx19(
|
|
7032
7658
|
"img",
|
|
@@ -7214,8 +7840,8 @@ function findPrecedingVisitorText(messages, agentMessageId) {
|
|
|
7214
7840
|
}
|
|
7215
7841
|
return void 0;
|
|
7216
7842
|
}
|
|
7217
|
-
function normalizeAgentFeedbackAnswerText(
|
|
7218
|
-
const normalized =
|
|
7843
|
+
function normalizeAgentFeedbackAnswerText(text3) {
|
|
7844
|
+
const normalized = text3.replace(/\s+/g, " ").trim();
|
|
7219
7845
|
if (!normalized) return void 0;
|
|
7220
7846
|
return normalized.length > 4e3 ? `${normalized.slice(0, 3997)}...` : normalized;
|
|
7221
7847
|
}
|
|
@@ -7256,10 +7882,10 @@ function sendAgentAnswerFeedback(eventUrl, event) {
|
|
|
7256
7882
|
}
|
|
7257
7883
|
|
|
7258
7884
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
7259
|
-
import { useEffect as
|
|
7885
|
+
import { useEffect as useEffect11, useRef as useRef9, useState as useState14 } from "react";
|
|
7260
7886
|
|
|
7261
7887
|
// src/react/page-shift.ts
|
|
7262
|
-
import { useEffect as
|
|
7888
|
+
import { useEffect as useEffect9 } from "react";
|
|
7263
7889
|
var PAGE_SHIFT_CLASS = "webless-agent-page-shift";
|
|
7264
7890
|
var DEFAULT_RAIL_WIDTH_PX = 450;
|
|
7265
7891
|
function shouldApplyPageShift(input) {
|
|
@@ -7311,7 +7937,7 @@ function clearPageMargin() {
|
|
|
7311
7937
|
}
|
|
7312
7938
|
function usePageShift(input) {
|
|
7313
7939
|
const { active, railSlotRef } = input;
|
|
7314
|
-
|
|
7940
|
+
useEffect9(() => {
|
|
7315
7941
|
if (typeof document === "undefined") {
|
|
7316
7942
|
return;
|
|
7317
7943
|
}
|
|
@@ -7339,12 +7965,12 @@ function usePageShift(input) {
|
|
|
7339
7965
|
}
|
|
7340
7966
|
|
|
7341
7967
|
// src/react/hooks/useIsMobile.ts
|
|
7342
|
-
import { useEffect as
|
|
7968
|
+
import { useEffect as useEffect10, useState as useState13 } from "react";
|
|
7343
7969
|
function useIsMobile(breakpoint = 767) {
|
|
7344
|
-
const [isMobile, setIsMobile] =
|
|
7970
|
+
const [isMobile, setIsMobile] = useState13(
|
|
7345
7971
|
() => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
|
|
7346
7972
|
);
|
|
7347
|
-
|
|
7973
|
+
useEffect10(() => {
|
|
7348
7974
|
const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
|
|
7349
7975
|
const onChange = () => setIsMobile(media.matches);
|
|
7350
7976
|
onChange();
|
|
@@ -7374,9 +8000,9 @@ function AgentWidget({
|
|
|
7374
8000
|
}) {
|
|
7375
8001
|
const isMobile = useIsMobile();
|
|
7376
8002
|
const placement = normalizeAgentPlacement(placementInput);
|
|
7377
|
-
const railSlotRef =
|
|
7378
|
-
const [railCollapsed, setRailCollapsed] =
|
|
7379
|
-
const [railExpanded, setRailExpanded] =
|
|
8003
|
+
const railSlotRef = useRef9(null);
|
|
8004
|
+
const [railCollapsed, setRailCollapsed] = useState14(defaultCollapsed);
|
|
8005
|
+
const [railExpanded, setRailExpanded] = useState14(false);
|
|
7380
8006
|
const pageShiftActive = shouldApplyPageShift({
|
|
7381
8007
|
pageShift,
|
|
7382
8008
|
isMobile,
|
|
@@ -7429,7 +8055,7 @@ function AgentWidget({
|
|
|
7429
8055
|
} : {},
|
|
7430
8056
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
7431
8057
|
};
|
|
7432
|
-
|
|
8058
|
+
useEffect11(() => {
|
|
7433
8059
|
if (!registerPanelController) return;
|
|
7434
8060
|
registerAgentPanelController(customerId, {
|
|
7435
8061
|
open: () => setRailCollapsed(false),
|
|
@@ -7464,7 +8090,7 @@ function AgentWidget({
|
|
|
7464
8090
|
})
|
|
7465
8091
|
);
|
|
7466
8092
|
}
|
|
7467
|
-
|
|
8093
|
+
useEffect11(() => {
|
|
7468
8094
|
if (railCollapsed) return;
|
|
7469
8095
|
const handleKeyDown = (event) => {
|
|
7470
8096
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -7608,4 +8234,4 @@ export {
|
|
|
7608
8234
|
sendAgentAnswerFeedback,
|
|
7609
8235
|
AgentWidget
|
|
7610
8236
|
};
|
|
7611
|
-
//# sourceMappingURL=chunk-
|
|
8237
|
+
//# sourceMappingURL=chunk-EVWZF34F.js.map
|