@webless/agent 0.10.4 → 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.
@@ -138,16 +138,16 @@ function parseStep(value) {
138
138
  if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
139
139
  return null;
140
140
  }
141
- const id = boundedString(value.id, 80);
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 (!id || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(id) || !label || value.description !== void 0 && !description || !Array.isArray(value.fieldPaths) || value.fieldPaths.length < 1 || value.fieldPaths.length > 32 || value.fieldPaths.some(
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 id = boundedString(value.id, 200);
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 (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
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 text2 = message.text.trim();
395
- if (!text2) return message.text;
396
- if (text2.startsWith(COMPOSER_FORM_SKIP_DISMISSAL)) {
397
- const afterDismissal = text2.slice(COMPOSER_FORM_SKIP_DISMISSAL.length).trim().replace(/^\n+/, "").trim();
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 && text2 === runtime && runtime.includes(COMPOSER_FORM_SKIP_DISMISSAL)) {
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 id = asString(record2?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
475
+ const id2 = asString(record2?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
476
476
  const kind = asString(record2?.kind);
477
- if (!record2 || !id || seen.has(id) || !isFieldKind2(kind)) continue;
478
- seen.add(id);
479
- const label = asString(record2.label) || id;
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(text2) {
517
+ function looksLikeFieldCollection(text3) {
518
518
  return /\b(please|need|send|share|enter|provide|add|collect|what|which|use)\b/i.test(
519
- text2
520
- ) || /:\s*$/m.test(text2) || /^[-*•]\s+/m.test(text2);
519
+ text3
520
+ ) || /:\s*$/m.test(text3) || /^[-*•]\s+/m.test(text3);
521
521
  }
522
- function looksLikeBookingCopy(text2) {
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
- text2
524
+ text3
525
525
  );
526
526
  }
527
- function echoedLabeledFieldIds(text2) {
527
+ function echoedLabeledFieldIds(text3) {
528
528
  const ids = /* @__PURE__ */ new Set();
529
- if (/\bname\s*:\s+\S+/i.test(text2)) ids.add("name");
530
- if (/\be-?mail\s*:\s+\S+/i.test(text2)) ids.add("email");
531
- if (/\bphone\s*:\s+\S+/i.test(text2)) ids.add("phone");
532
- if (/\bcompany\s*:\s+\S+/i.test(text2)) ids.add("company");
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(text2) {
535
+ function matchLibraryFields(text3) {
536
536
  return FIELD_LIBRARY.flatMap((field) => {
537
- if (field.exclude?.test(text2)) {
538
- const leftover = text2.replace(field.exclude, " ");
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(text2))) {
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(text2) {
548
- const confirmingCreate = /\bshould i create this\b/i.test(text2);
549
- const echoingFilledFields = /\b(?:name|email|phone|company)\s*:\s+\S+/i.test(text2) && /[^\s@]+@[^\s@]+\.[^\s@]+/i.test(text2);
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(text2)) {
552
- const echoed = echoedLabeledFieldIds(text2);
553
- if (matchLibraryFields(text2).some((field) => !echoed.has(field.id))) {
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(text2);
559
+ return confirmingCreate && !looksLikeFieldCollection(text3);
560
560
  }
561
- function inferComposerForm(text2) {
562
- const cleaned = text2.trim();
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 text2 = input.agentText.trim();
578
- if (!text2) return null;
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(text2);
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(text2) {
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
- text2
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(text2, offer) {
713
- if (!offer) return text2;
714
- if (extractToolCards(text2).some((card) => card.type === "booking_offer")) {
715
- return text2;
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(text2).trim() || text2.trim();
717
+ const visible = stripToolCards(text3).trim() || text3.trim();
718
718
  return `${visible}
719
719
 
720
720
  ${formatBookingOfferFence(offer)}`;
721
721
  }
722
- function hideToolCardFences(text2) {
723
- return text2.replace(/```(?:webless-tool-card|json)\s*[\s\S]*?```/gi, "").replace(/```(?:webless-tool-card|json)[\s\S]*$/i, "").replace(/\n{3,}/g, "\n\n").trim();
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(text2) {
727
- const cleaned = text2.trim();
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(text2) {
734
- const cleaned = hideToolCardFences(text2);
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(text2) {
747
+ function extractToolCards(text3) {
748
748
  const cards = [];
749
- for (const match of text2.matchAll(FENCE_PATTERN)) {
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(text2) {
759
- return text2.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
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 text2 = safeText(value);
880
- if (!text2) return "";
879
+ const text3 = safeText(value);
880
+ if (!text3) return "";
881
881
  try {
882
- const url = new URL(text2);
882
+ const url = new URL(text3);
883
883
  return url.protocol === "https:" ? url.toString() : "";
884
884
  } catch {
885
885
  return "";
@@ -1099,6 +1099,9 @@ function finalizeSummaryPresentation(result, proposed) {
1099
1099
  };
1100
1100
  }
1101
1101
  function presentVisitorToolResult(result, registry = []) {
1102
+ if (result.toolName === "request_location") {
1103
+ return { id: result.callId, toolName: result.toolName, status: result.status, kind: "hidden" };
1104
+ }
1102
1105
  if (result.status === "completed" && toolResultFailed(result)) {
1103
1106
  result = { ...result, status: "failed" };
1104
1107
  }
@@ -1209,17 +1212,372 @@ function legacyBookingEnvelope(output) {
1209
1212
  } : null;
1210
1213
  }
1211
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
+
1212
1375
  // src/react/hooks/useAgentChat.ts
1213
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
1376
+ import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
1214
1377
 
1215
1378
  // src/runtime/client.ts
1216
1379
  import {
1217
1380
  Client,
1218
- ClientError as ClientError2
1381
+ ClientError as ClientError4
1219
1382
  } from "eve/client";
1220
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
+
1221
1579
  // src/runtime/capability.ts
1222
- import { ClientError } from "eve/client";
1580
+ import { ClientError as ClientError2 } from "eve/client";
1223
1581
  var MAX_REFRESH_SKEW_MS = 3e4;
1224
1582
  var LOCAL_LOOPBACK_ORIGINS = [
1225
1583
  "http://127.0.0.1:3010",
@@ -1287,18 +1645,26 @@ function createAgentRuntimeCapability(options) {
1287
1645
  );
1288
1646
  }
1289
1647
  }
1290
- const bootstrapBody = JSON.stringify({
1648
+ const bootstrapBody = {
1291
1649
  clientSessionId: options.visitorSessionId,
1292
1650
  indexId: options.indexId,
1293
1651
  ...previewBuildId ? { previewBuildId } : {},
1294
1652
  ...previewGrant ? { previewGrant } : {},
1295
1653
  version: options.version
1296
- });
1297
- const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
1298
- body: bootstrapBody,
1299
- headers: { "content-type": "application/json" },
1300
- method: "POST"
1301
- });
1654
+ };
1655
+ const postBootstrap = async (origin) => {
1656
+ const send = (advertiseLocation) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
1657
+ body: JSON.stringify({ ...bootstrapBody, ...advertiseLocation ? { locationConsent: "v1" } : {} }),
1658
+ headers: { "content-type": "application/json" },
1659
+ method: "POST"
1660
+ });
1661
+ const response2 = await send(Boolean(options.locationConsent));
1662
+ if (options.locationConsent && response2.status === 400) {
1663
+ const error = await response2.clone().json().catch(() => null);
1664
+ if (isRecord3(error) && error.code === "invalid_request") return send(false);
1665
+ }
1666
+ return response2;
1667
+ };
1302
1668
  let response;
1303
1669
  let lastError;
1304
1670
  for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
@@ -1356,7 +1722,7 @@ async function withCapabilityRefresh(capability, request) {
1356
1722
  try {
1357
1723
  return await request();
1358
1724
  } catch (error) {
1359
- if (!(error instanceof ClientError) || error.status !== 401) {
1725
+ if (!(error instanceof ClientError2) || error.status !== 401) {
1360
1726
  throw error;
1361
1727
  }
1362
1728
  capability.invalidate();
@@ -1364,6 +1730,75 @@ async function withCapabilityRefresh(capability, request) {
1364
1730
  }
1365
1731
  }
1366
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
+
1367
1802
  // src/runtime/config.ts
1368
1803
  var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
1369
1804
  var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
@@ -1833,6 +2268,7 @@ function specialistNameFromInput(input) {
1833
2268
  return match?.[1]?.trim() || void 0;
1834
2269
  }
1835
2270
  function requestedWorkItem(action) {
2271
+ if (action.kind === "tool-call" && action.toolName === "request_location") return null;
1836
2272
  if (action.kind === "tool-call" && action.toolName === "search_discovery") {
1837
2273
  return {
1838
2274
  id: action.callId,
@@ -1938,13 +2374,14 @@ function applyWorkEvent(event, handlers, workItems) {
1938
2374
  );
1939
2375
  }
1940
2376
  var AgentSession = class {
1941
- constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, previewBuildId, getUnpublishedPreviewGrant) {
2377
+ constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, previewBuildId, getUnpublishedPreviewGrant, locationConsent) {
1942
2378
  this.indexId = indexId;
1943
2379
  this.version = version;
1944
2380
  this.runtimeOrigin = runtimeOrigin;
1945
2381
  this.visitorSessionId = visitorSessionId;
1946
2382
  this.storeOptions = storeOptions;
1947
2383
  this.capability = createAgentRuntimeCapability({
2384
+ locationConsent,
1948
2385
  getUnpublishedPreviewGrant,
1949
2386
  indexId,
1950
2387
  previewBuildId,
@@ -1952,6 +2389,11 @@ var AgentSession = class {
1952
2389
  version,
1953
2390
  visitorSessionId
1954
2391
  });
2392
+ this.handoff = createHandoffClient({
2393
+ getClient: () => this.ensureClient(),
2394
+ getSessionId: () => this.getActiveSessionId(),
2395
+ capability: this.capability
2396
+ });
1955
2397
  }
1956
2398
  indexId;
1957
2399
  version;
@@ -1964,6 +2406,7 @@ var AgentSession = class {
1964
2406
  activeResponse;
1965
2407
  childStreams;
1966
2408
  capability;
2409
+ handoff;
1967
2410
  getActiveSessionId() {
1968
2411
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
1969
2412
  }
@@ -2074,7 +2517,7 @@ var AgentSession = class {
2074
2517
  () => activeSession.send(message, { signal })
2075
2518
  );
2076
2519
  } catch (error) {
2077
- if (error instanceof ClientError2 && error.status === 409 && error.code === "session_not_active") {
2520
+ if (error instanceof ClientError4 && error.status === 409 && error.code === "session_not_active") {
2078
2521
  clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
2079
2522
  this.session = void 0;
2080
2523
  session = void 0;
@@ -2248,10 +2691,10 @@ var AgentSession = class {
2248
2691
  }
2249
2692
  this.session = session;
2250
2693
  const inputResponses = responses.map(
2251
- ({ requestId, optionId, text: text2 }) => ({
2694
+ ({ requestId, optionId, text: text3 }) => ({
2252
2695
  requestId,
2253
2696
  ...optionId ? { optionId } : {},
2254
- ...text2 ? { text: text2 } : {}
2697
+ ...text3 ? { text: text3 } : {}
2255
2698
  })
2256
2699
  );
2257
2700
  const response = await withCapabilityRefresh(
@@ -2336,10 +2779,12 @@ function createAgentClient(options) {
2336
2779
  visitorSessionId,
2337
2780
  storeOptions,
2338
2781
  options.previewBuildId,
2339
- options.getUnpublishedPreviewGrant
2782
+ options.getUnpublishedPreviewGrant,
2783
+ options.locationConsent
2340
2784
  );
2341
2785
  return {
2342
2786
  indexId,
2787
+ handoff: session.handoff,
2343
2788
  version,
2344
2789
  runtimeOrigin,
2345
2790
  visitorSessionId,
@@ -2371,7 +2816,7 @@ function createAgentClient(options) {
2371
2816
  }
2372
2817
 
2373
2818
  // src/runtime/errors.ts
2374
- import { ClientError as ClientError3 } from "eve/client";
2819
+ import { ClientError as ClientError5 } from "eve/client";
2375
2820
  var TRANSIENT_AGENT_ERROR_MESSAGE = "The agent run stopped before the action finished. Please try again.";
2376
2821
  var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
2377
2822
  function isTransientRuntimeMessage(message) {
@@ -2383,7 +2828,7 @@ function isPreviewAuthorizationMessage(message) {
2383
2828
  return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
2384
2829
  }
2385
2830
  function formatAgentError(error) {
2386
- if (error instanceof ClientError3) {
2831
+ if (error instanceof ClientError5) {
2387
2832
  if (error.status === 401 && error.code === "index_required") {
2388
2833
  return "Missing indexId \u2014 pass a published index id to createAgentClient().";
2389
2834
  }
@@ -2421,9 +2866,21 @@ function conversationKey(storageKeyPrefix, visitorSessionId) {
2421
2866
  function parseMessage(value) {
2422
2867
  if (typeof value !== "object" || value === null) return null;
2423
2868
  const record2 = value;
2424
- 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)) {
2425
2870
  return null;
2426
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
+ }
2427
2884
  if (record2.role === "visitor") {
2428
2885
  return {
2429
2886
  id: record2.id,
@@ -2737,15 +3194,15 @@ function appendChatCollectiblePrompts(messages, requests) {
2737
3194
  }
2738
3195
  return next;
2739
3196
  }
2740
- function chatInputResponseForText(requests, text2) {
2741
- const trimmed = text2.trim();
3197
+ function chatInputResponseForText(requests, text3) {
3198
+ const trimmed = text3.trim();
2742
3199
  if (!trimmed) return null;
2743
3200
  const pending = requests.find(isChatCollectibleInputRequest);
2744
3201
  if (!pending) return null;
2745
3202
  return { requestId: pending.requestId, text: trimmed };
2746
3203
  }
2747
- function normalizeAssistantDedupeKey(text2) {
2748
- return text2.trim().replace(/\s+/g, " ").toLowerCase();
3204
+ function normalizeAssistantDedupeKey(text3) {
3205
+ return text3.trim().replace(/\s+/g, " ").toLowerCase();
2749
3206
  }
2750
3207
  function isNearDuplicateAssistantText(left, right) {
2751
3208
  const a = normalizeAssistantDedupeKey(left);
@@ -2858,6 +3315,7 @@ function isJsonRecord(value) {
2858
3315
  return value !== null && typeof value === "object" && !Array.isArray(value);
2859
3316
  }
2860
3317
  function useAgentChat({
3318
+ handoffEnabled = false,
2861
3319
  customerId,
2862
3320
  getUnpublishedPreviewGrant,
2863
3321
  indexId,
@@ -2873,9 +3331,9 @@ function useAgentChat({
2873
3331
  () => createInitialState(greeting?.trim() || DEFAULT_GREETING),
2874
3332
  [greeting]
2875
3333
  );
2876
- const previewGrantProviderRef = useRef(getUnpublishedPreviewGrant);
3334
+ const previewGrantProviderRef = useRef2(getUnpublishedPreviewGrant);
2877
3335
  previewGrantProviderRef.current = getUnpublishedPreviewGrant;
2878
- const toolResultRegistryRef = useRef(toolResultRegistry);
3336
+ const toolResultRegistryRef = useRef2(toolResultRegistry);
2879
3337
  toolResultRegistryRef.current = toolResultRegistry;
2880
3338
  const resolveUnpublishedPreviewGrant = () => {
2881
3339
  const provider = previewGrantProviderRef.current;
@@ -2903,18 +3361,19 @@ function useAgentChat({
2903
3361
  }),
2904
3362
  [resolvedStorageKeyPrefix, visitorSessionId]
2905
3363
  );
2906
- const [state, setState] = useState(
3364
+ const [state, setState] = useState2(
2907
3365
  () => stateFromConversation(
2908
3366
  loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
2909
3367
  initialState
2910
3368
  )
2911
3369
  );
2912
- const pendingBookingRef = useRef(
3370
+ const pendingBookingRef = useRef2(
2913
3371
  loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
2914
3372
  );
2915
- const runRef = useRef(null);
2916
- const clientRef = useRef(
3373
+ const runRef = useRef2(null);
3374
+ const clientRef = useRef2(
2917
3375
  createAgentClient({
3376
+ locationConsent: true,
2918
3377
  customerId,
2919
3378
  getUnpublishedPreviewGrant: resolveUnpublishedPreviewGrant,
2920
3379
  indexId,
@@ -2926,8 +3385,8 @@ function useAgentChat({
2926
3385
  })
2927
3386
  );
2928
3387
  const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${previewBuildId ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
2929
- const identityRef = useRef(identityKey);
2930
- useEffect(() => {
3388
+ const identityRef = useRef2(identityKey);
3389
+ useEffect2(() => {
2931
3390
  if (identityRef.current === identityKey) {
2932
3391
  return;
2933
3392
  }
@@ -2935,6 +3394,7 @@ function useAgentChat({
2935
3394
  runRef.current?.abort();
2936
3395
  runRef.current = null;
2937
3396
  clientRef.current = createAgentClient({
3397
+ locationConsent: true,
2938
3398
  customerId,
2939
3399
  getUnpublishedPreviewGrant: resolveUnpublishedPreviewGrant,
2940
3400
  indexId,
@@ -2965,7 +3425,7 @@ function useAgentChat({
2965
3425
  version,
2966
3426
  visitorId
2967
3427
  ]);
2968
- useEffect(() => {
3428
+ useEffect2(() => {
2969
3429
  if (!hasVisitorMessages(state.messages)) return;
2970
3430
  savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
2971
3431
  messages: state.messages,
@@ -2985,16 +3445,77 @@ function useAgentChat({
2985
3445
  state.toolResults,
2986
3446
  visitorId
2987
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;
2988
3501
  const reset = useCallback(() => {
3502
+ if (handoff.blocksAI && !handoff.canReset) return;
2989
3503
  runRef.current?.abort();
2990
3504
  runRef.current = null;
2991
3505
  clientRef.current.reset();
2992
3506
  pendingBookingRef.current = null;
2993
3507
  clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
2994
3508
  setState(initialState);
2995
- }, [initialState, resolvedStorageKeyPrefix, visitorId]);
3509
+ }, [
3510
+ handoff.blocksAI,
3511
+ handoff.canReset,
3512
+ initialState,
3513
+ resolvedStorageKeyPrefix,
3514
+ visitorId
3515
+ ]);
2996
3516
  const runTurn = useCallback(
2997
3517
  async (input) => {
3518
+ if (handoffBlocksAI.current) return null;
2998
3519
  const {
2999
3520
  controller,
3000
3521
  initialText = "",
@@ -3118,6 +3639,7 @@ function useAgentChat({
3118
3639
  signal
3119
3640
  });
3120
3641
  if (resume && finalText === null) {
3642
+ if (!isActiveRun() || handoffBlocksAI.current) return null;
3121
3643
  finalText = await clientRef.current.sendTurn(visitorText, {
3122
3644
  handlers,
3123
3645
  signal
@@ -3150,8 +3672,12 @@ function useAgentChat({
3150
3672
  messages: appendAgentTurnMessage(
3151
3673
  prev.messages,
3152
3674
  displayText,
3153
- (prev.toolResults ?? []).filter((result) => result.kind === "search"),
3154
- (prev.toolResults ?? []).filter((result) => result.kind !== "search" && result.kind !== "input")
3675
+ (prev.toolResults ?? []).filter(
3676
+ (result) => result.kind === "search"
3677
+ ),
3678
+ (prev.toolResults ?? []).filter(
3679
+ (result) => result.kind !== "search" && result.kind !== "input"
3680
+ )
3155
3681
  ),
3156
3682
  toolResults: (prev.toolResults ?? []).filter(
3157
3683
  (result) => result.kind === "input"
@@ -3219,6 +3745,15 @@ function useAgentChat({
3219
3745
  const outgoing = options?.runtimeText ?? visitorText;
3220
3746
  if (!outgoing.trim()) return null;
3221
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
+ }
3222
3757
  const chatResponse = chatInputResponseForText(
3223
3758
  state.pendingInputs ?? [],
3224
3759
  outgoing.trim()
@@ -3320,9 +3855,10 @@ ${outgoing}` : outgoing;
3320
3855
  visitorText: runtimeText
3321
3856
  });
3322
3857
  },
3323
- [runTurn, state.pendingInputs]
3858
+ [handoff, runTurn, state.pendingInputs]
3324
3859
  );
3325
3860
  const retry = useCallback(async () => {
3861
+ if (handoffBlocksAI.current) return;
3326
3862
  const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
3327
3863
  if (!visitorMessage) return;
3328
3864
  if (runRef.current) {
@@ -3356,6 +3892,7 @@ ${outgoing}` : outgoing;
3356
3892
  });
3357
3893
  }, [runTurn, state.messages]);
3358
3894
  const regenerate = useCallback(async () => {
3895
+ if (handoffBlocksAI.current) return null;
3359
3896
  let lastVisitorIndex = -1;
3360
3897
  for (let index = state.messages.length - 1; index >= 0; index -= 1) {
3361
3898
  if (state.messages[index]?.role === "visitor") {
@@ -3413,7 +3950,7 @@ ${outgoing}` : outgoing;
3413
3950
  );
3414
3951
  const respondToInput = useCallback(
3415
3952
  async (response) => {
3416
- if (runRef.current) return;
3953
+ if (handoffBlocksAI.current || runRef.current) return;
3417
3954
  const pending = state.pendingInputs?.find(
3418
3955
  (request) => request.requestId === response.requestId
3419
3956
  );
@@ -3441,7 +3978,8 @@ ${outgoing}` : outgoing;
3441
3978
  },
3442
3979
  [respondToToolInput, runTurn, state.pendingInputs]
3443
3980
  );
3444
- useEffect(() => {
3981
+ useEffect2(() => {
3982
+ if (handoff.blocksAI) return;
3445
3983
  const conversation = loadPersistedAgentConversation(
3446
3984
  resolvedStorageKeyPrefix,
3447
3985
  visitorId
@@ -3463,14 +4001,21 @@ ${outgoing}` : outgoing;
3463
4001
  }
3464
4002
  controller.abort();
3465
4003
  };
3466
- }, [identityKey, resolvedStorageKeyPrefix, runTurn, visitorId]);
3467
- useEffect(() => {
4004
+ }, [
4005
+ handoff.blocksAI,
4006
+ identityKey,
4007
+ resolvedStorageKeyPrefix,
4008
+ runTurn,
4009
+ visitorId
4010
+ ]);
4011
+ useEffect2(() => {
3468
4012
  return () => {
3469
4013
  runRef.current?.abort();
3470
4014
  runRef.current = null;
3471
4015
  };
3472
4016
  }, []);
3473
4017
  return {
4018
+ handoff,
3474
4019
  state,
3475
4020
  reset,
3476
4021
  retry,
@@ -3581,12 +4126,12 @@ var defaultDarkAgentRailTheme = {
3581
4126
  };
3582
4127
 
3583
4128
  // src/react/components/MessageActions/MessageActions.tsx
3584
- import { useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState2 } from "react";
4129
+ import { useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useState as useState3 } from "react";
3585
4130
  import { createPortal } from "react-dom";
3586
4131
 
3587
4132
  // src/react/lib/speech.ts
3588
- function toSpeechText(text2) {
3589
- let out = hideToolCardFences(text2);
4133
+ function toSpeechText(text3) {
4134
+ let out = hideToolCardFences(text3);
3590
4135
  out = out.replace(/```[\s\S]*?```/g, " ");
3591
4136
  out = out.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
3592
4137
  out = out.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
@@ -3931,12 +4476,12 @@ function StopIcon() {
3931
4476
  }
3932
4477
  ) });
3933
4478
  }
3934
- async function writeToClipboard(text2) {
4479
+ async function writeToClipboard(text3) {
3935
4480
  try {
3936
- await navigator.clipboard.writeText(text2);
4481
+ await navigator.clipboard.writeText(text3);
3937
4482
  } catch {
3938
4483
  const textarea = document.createElement("textarea");
3939
- textarea.value = text2;
4484
+ textarea.value = text3;
3940
4485
  textarea.style.position = "fixed";
3941
4486
  textarea.style.opacity = "0";
3942
4487
  document.body.appendChild(textarea);
@@ -3987,16 +4532,16 @@ function MessageActions({
3987
4532
  speechText
3988
4533
  }) {
3989
4534
  const menuPortalRoot = useAgentRailMenuPortalRoot();
3990
- const [copied, setCopied] = useState2(false);
3991
- const [rating, setRating] = useState2(null);
3992
- const [menuOpen, setMenuOpen] = useState2(false);
3993
- const [menuPosition, setMenuPosition] = useState2(null);
3994
- const [speaking, setSpeaking] = useState2(false);
3995
- const [speechSupported, setSpeechSupported] = useState2(null);
3996
- const copyTimerRef = useRef2(null);
3997
- const menuRef = useRef2(null);
3998
- const portaledMenuRef = useRef2(null);
3999
- const moreButtonRef = useRef2(null);
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);
4000
4545
  const answeredLabel = formatAnsweredAt(answeredAt ?? 0);
4001
4546
  const resolvedSpeechText = speechText ?? toSpeechText(copyText);
4002
4547
  const canOpenReceipt = Boolean(receiptSteps) && Boolean(onOpenReceipt);
@@ -4023,7 +4568,7 @@ function MessageActions({
4023
4568
  })
4024
4569
  );
4025
4570
  }, [menuOpen, menuPortalRoot]);
4026
- useEffect2(() => {
4571
+ useEffect3(() => {
4027
4572
  const unsubscribe = subscribeSpeechInterrupts(() => setSpeaking(false));
4028
4573
  return () => {
4029
4574
  unsubscribe();
@@ -4033,7 +4578,7 @@ function MessageActions({
4033
4578
  stopSpeech();
4034
4579
  };
4035
4580
  }, []);
4036
- useEffect2(() => {
4581
+ useEffect3(() => {
4037
4582
  if (!menuOpen) return;
4038
4583
  const handlePointerDown = (event) => {
4039
4584
  const target = event.target;
@@ -4202,7 +4747,7 @@ function MessageActions({
4202
4747
  }
4203
4748
 
4204
4749
  // src/react/components/AgentRail/AgentRail.tsx
4205
- import { useEffect as useEffect6, useRef as useRef6, useState as useState9 } from "react";
4750
+ import { useEffect as useEffect8, useRef as useRef8, useState as useState11 } from "react";
4206
4751
 
4207
4752
  // src/react/lib/agent-theme.ts
4208
4753
  function agentThemeStyle(theme, resolvedColorScheme) {
@@ -4277,7 +4822,7 @@ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
4277
4822
  }
4278
4823
 
4279
4824
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
4280
- import { useState as useState3 } from "react";
4825
+ import { useState as useState4 } from "react";
4281
4826
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
4282
4827
  function joinLabels(labels) {
4283
4828
  if (labels.length <= 1) return labels[0] ?? "";
@@ -4396,7 +4941,7 @@ function AgentActivityBubble({
4396
4941
  const statusText = workSummary(steps, failed, brandLabel);
4397
4942
  const softReview = statusText === AGENT_SOFT_REVIEW_STATUS;
4398
4943
  const receiptId = steps.map((step) => step.id).join(":");
4399
- const [expandedReceiptId, setExpandedReceiptId] = useState3(
4944
+ const [expandedReceiptId, setExpandedReceiptId] = useState4(
4400
4945
  null
4401
4946
  );
4402
4947
  const detailsOpen = !active && expandedReceiptId === receiptId;
@@ -4447,7 +4992,7 @@ function AgentActivityBubble({
4447
4992
  }
4448
4993
 
4449
4994
  // src/react/components/AnswerReceiptDialog/AnswerReceiptDialog.tsx
4450
- import { useEffect as useEffect3, useRef as useRef3 } from "react";
4995
+ import { useEffect as useEffect4, useRef as useRef4 } from "react";
4451
4996
  import { createPortal as createPortal2 } from "react-dom";
4452
4997
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
4453
4998
  var FOCUSABLE_SELECTOR = 'button:not(:disabled), a[href], textarea:not(:disabled), input:not(:disabled), [tabindex]:not([tabindex="-1"])';
@@ -4469,10 +5014,10 @@ function AnswerReceiptDialog({
4469
5014
  }) {
4470
5015
  const portalRoots = useAgentRailPortalRoots();
4471
5016
  const overlayRoot = portalRoots?.overlayRef ?? null;
4472
- const cardRef = useRef3(null);
4473
- const closeButtonRef = useRef3(null);
4474
- const previouslyFocusedRef = useRef3(null);
4475
- useEffect3(() => {
5017
+ const cardRef = useRef4(null);
5018
+ const closeButtonRef = useRef4(null);
5019
+ const previouslyFocusedRef = useRef4(null);
5020
+ useEffect4(() => {
4476
5021
  previouslyFocusedRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
4477
5022
  closeButtonRef.current?.focus({ preventScroll: true });
4478
5023
  const handleKeyDown = (event) => {
@@ -4558,10 +5103,10 @@ function AnswerReceiptDialog({
4558
5103
 
4559
5104
  // src/react/components/Composer/Composer.tsx
4560
5105
  import {
4561
- useEffect as useEffect4,
5106
+ useEffect as useEffect5,
4562
5107
  useId,
4563
- useRef as useRef4,
4564
- useState as useState4
5108
+ useRef as useRef5,
5109
+ useState as useState5
4565
5110
  } from "react";
4566
5111
  import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
4567
5112
  var FORM_SUBMITTED_MESSAGE = "Shared my details";
@@ -4624,17 +5169,17 @@ function Composer({
4624
5169
  allowFormResume = true,
4625
5170
  onSubmit
4626
5171
  }) {
4627
- const [value, setValue] = useState4("");
4628
- const [draft, setDraft] = useState4(() => createDraft(form));
4629
- const inputRef = useRef4(null);
4630
- const firstFieldRef = useRef4(null);
4631
- const formRef = useRef4(null);
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);
4632
5177
  const formId = useId();
4633
5178
  if (form && form.id !== draft.form?.id) setDraft(createDraft(form));
4634
5179
  const savedForm = allowFormResume && !draft.submitted ? draft.form : null;
4635
5180
  const activeForm = !disabled && draft.expanded ? savedForm : null;
4636
5181
  const canSend = !disabled && Boolean(value.trim() || activeForm);
4637
- useEffect4(() => {
5182
+ useEffect5(() => {
4638
5183
  if (activeForm) firstFieldRef.current?.focus();
4639
5184
  else if ((savedForm || draft.submitted) && !disabled)
4640
5185
  inputRef.current?.focus();
@@ -4895,7 +5440,7 @@ function FollowUpChips({
4895
5440
  }
4896
5441
 
4897
5442
  // src/react/components/MessageBubble/MessageBubble.tsx
4898
- import { useState as useState6 } from "react";
5443
+ import { useState as useState7 } from "react";
4899
5444
 
4900
5445
  // src/react/components/SearchReferences/SearchReferences.tsx
4901
5446
  import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
@@ -4987,11 +5532,11 @@ function SearchReferences({
4987
5532
 
4988
5533
  // src/react/components/BookingCard/BookingCard.tsx
4989
5534
  import {
4990
- useEffect as useEffect5,
5535
+ useEffect as useEffect6,
4991
5536
  useId as useId2,
4992
5537
  useMemo as useMemo2,
4993
- useRef as useRef5,
4994
- useState as useState5
5538
+ useRef as useRef6,
5539
+ useState as useState6
4995
5540
  } from "react";
4996
5541
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
4997
5542
  var BOOKING_STEPS = [
@@ -5049,16 +5594,16 @@ function InteractiveBookingCard({
5049
5594
  }) {
5050
5595
  const fieldId = useId2();
5051
5596
  const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
5052
- const [step, setStep] = useState5("date");
5053
- const [eventTypeUri, setEventTypeUri] = useState5(defaultType);
5054
- const [selectedDate, setSelectedDate] = useState5("");
5055
- const [startTime, setStartTime] = useState5("");
5056
- const [name, setName] = useState5("");
5057
- const [email, setEmail] = useState5("");
5058
- const activeStepRef = useRef5(null);
5059
- const previousStepRef = useRef5(step);
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);
5060
5605
  const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
5061
- useEffect5(() => {
5606
+ useEffect6(() => {
5062
5607
  if (previousStepRef.current === step) return;
5063
5608
  previousStepRef.current = step;
5064
5609
  activeStepRef.current?.scrollIntoView({ block: "nearest" });
@@ -5075,7 +5620,7 @@ function InteractiveBookingCard({
5075
5620
  }
5076
5621
  return next;
5077
5622
  }, [slots]);
5078
- const [visibleMonth, setVisibleMonth] = useState5(
5623
+ const [visibleMonth, setVisibleMonth] = useState6(
5079
5624
  () => firstAvailableBookingMonth(slots)
5080
5625
  );
5081
5626
  function selectEventType(nextType) {
@@ -5383,8 +5928,8 @@ function resolveMessageUrl(safeUrl, baseUrl) {
5383
5928
  // src/react/components/MessageBubble/MessageBubble.tsx
5384
5929
  import "streamdown/styles.css";
5385
5930
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
5386
- function normalizeDedupeText(text2) {
5387
- return text2.trim().replace(/\s+/g, " ").toLowerCase();
5931
+ function normalizeDedupeText(text3) {
5932
+ return text3.trim().replace(/\s+/g, " ").toLowerCase();
5388
5933
  }
5389
5934
  function paragraphsAreNearDuplicates(first, second) {
5390
5935
  const left = normalizeDedupeText(first);
@@ -5400,8 +5945,8 @@ function paragraphsShareOpening(first, second) {
5400
5945
  if (!opening || opening.length < 20) return false;
5401
5946
  return second.trim().startsWith(opening);
5402
5947
  }
5403
- function collapseRepeatedText(text2) {
5404
- const trimmed = text2.trim();
5948
+ function collapseRepeatedText(text3) {
5949
+ const trimmed = text3.trim();
5405
5950
  if (trimmed.length < 40) return trimmed;
5406
5951
  const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
5407
5952
  if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
@@ -5422,13 +5967,14 @@ function MessageBubble({
5422
5967
  message,
5423
5968
  brandLogoUrl,
5424
5969
  bookingDisabled = false,
5970
+ showProvenance = false,
5425
5971
  bookingReadOnly = false,
5426
5972
  linkBaseUrl,
5427
5973
  offer,
5428
5974
  onBook
5429
5975
  }) {
5430
5976
  const resolvedLogoUrl = brandLogoUrl?.trim();
5431
- const [failedLogoUrl, setFailedLogoUrl] = useState6(null);
5977
+ const [failedLogoUrl, setFailedLogoUrl] = useState7(null);
5432
5978
  const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
5433
5979
  const cards = message.role === "agent" ? extractToolCards(message.text) : [];
5434
5980
  const extractedOffers = cards.filter(
@@ -5443,7 +5989,34 @@ function MessageBubble({
5443
5989
  if (message.role === "visitor") {
5444
5990
  const visitorText = visitorMessageDisplayText(message).trim();
5445
5991
  if (!visitorText) return null;
5446
- return /* @__PURE__ */ jsx8("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ jsx8("p", { className: "message-bubble__text", children: visitorText }) });
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
+ );
5447
6020
  }
5448
6021
  const citations = message.citations ?? [];
5449
6022
  const agentText = /* @__PURE__ */ jsxs8("div", { className: "message-bubble__text", children: [
@@ -5480,35 +6053,43 @@ function MessageBubble({
5480
6053
  ] });
5481
6054
  if (!displayText && !message.searchResults?.length && offers.length === 0)
5482
6055
  return null;
5483
- return /* @__PURE__ */ jsxs8("article", { className: "message-bubble message-bubble--agent", children: [
5484
- displayText || message.searchResults?.length ? showBrandLogo ? /* @__PURE__ */ jsxs8("div", { className: "message-bubble__agent-row", children: [
5485
- /* @__PURE__ */ jsx8("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ jsx8(
5486
- "img",
5487
- {
5488
- src: resolvedLogoUrl,
5489
- alt: "",
5490
- onError: () => {
5491
- setFailedLogoUrl(resolvedLogoUrl ?? null);
5492
- }
5493
- }
5494
- ) }),
5495
- agentText
5496
- ] }) : agentText : null,
5497
- offers.map((nextOffer, index) => /* @__PURE__ */ jsx8(
5498
- BookingCard,
5499
- {
5500
- disabled: bookingDisabled,
5501
- readOnly: bookingReadOnly,
5502
- offer: nextOffer,
5503
- onBook
5504
- },
5505
- `${bookingOfferIdentityKey(nextOffer)}-${index}`
5506
- ))
5507
- ] });
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
+ );
5508
6089
  }
5509
6090
 
5510
6091
  // src/react/components/HumanInputCard/HumanInputCard.tsx
5511
- import { useState as useState8 } from "react";
6092
+ import { useState as useState9 } from "react";
5512
6093
 
5513
6094
  // src/react/components/ConfirmationCard/ConfirmationCard.tsx
5514
6095
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -5550,7 +6131,7 @@ function ConfirmationCard({
5550
6131
  }
5551
6132
 
5552
6133
  // src/react/components/ToolInputCard/ToolInputCard.tsx
5553
- import { useState as useState7 } from "react";
6134
+ import { useState as useState8 } from "react";
5554
6135
  import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
5555
6136
  function isRecord5(value) {
5556
6137
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -5673,13 +6254,13 @@ function valuesMatch(left, right) {
5673
6254
  }
5674
6255
  function FieldDescription({
5675
6256
  field,
5676
- id
6257
+ id: id2
5677
6258
  }) {
5678
- 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;
5679
6260
  }
5680
6261
  function ChoiceField({
5681
6262
  field,
5682
- id,
6263
+ id: id2,
5683
6264
  value,
5684
6265
  disabled,
5685
6266
  describedBy,
@@ -5697,7 +6278,7 @@ function ChoiceField({
5697
6278
  return /* @__PURE__ */ jsxs10(
5698
6279
  "select",
5699
6280
  {
5700
- id,
6281
+ id: id2,
5701
6282
  value: selectedIndex >= 0 ? String(selectedIndex) : "",
5702
6283
  disabled,
5703
6284
  required: field.required,
@@ -5720,7 +6301,7 @@ function ChoiceField({
5720
6301
  {
5721
6302
  className: "tool-input-card__choices",
5722
6303
  role: multiple ? "group" : "radiogroup",
5723
- "aria-labelledby": `${id}-label`,
6304
+ "aria-labelledby": `${id2}-label`,
5724
6305
  "aria-describedby": describedBy,
5725
6306
  "aria-invalid": Boolean(error),
5726
6307
  "aria-required": field.required,
@@ -5731,7 +6312,7 @@ function ChoiceField({
5731
6312
  "input",
5732
6313
  {
5733
6314
  type: multiple ? "checkbox" : "radio",
5734
- name: id,
6315
+ name: id2,
5735
6316
  value: String(index),
5736
6317
  checked,
5737
6318
  disabled,
@@ -5759,22 +6340,22 @@ function ToolField({
5759
6340
  disabled,
5760
6341
  error,
5761
6342
  field,
5762
- id,
6343
+ id: id2,
5763
6344
  value,
5764
6345
  onBlur,
5765
6346
  onChange
5766
6347
  }) {
5767
6348
  const describedBy = [
5768
- field.description ? `${id}-description` : "",
5769
- error ? `${id}-error` : ""
6349
+ field.description ? `${id2}-description` : "",
6350
+ error ? `${id2}-error` : ""
5770
6351
  ].filter(Boolean).join(" ");
5771
6352
  if (field.kind === "checkbox" || field.kind === "confirmation") {
5772
6353
  return /* @__PURE__ */ jsxs10("div", { className: "tool-input-card__field", children: [
5773
- /* @__PURE__ */ jsxs10("label", { className: "tool-input-card__check", htmlFor: id, children: [
6354
+ /* @__PURE__ */ jsxs10("label", { className: "tool-input-card__check", htmlFor: id2, children: [
5774
6355
  /* @__PURE__ */ jsx10(
5775
6356
  "input",
5776
6357
  {
5777
- id,
6358
+ id: id2,
5778
6359
  type: "checkbox",
5779
6360
  checked: value === true,
5780
6361
  disabled,
@@ -5786,18 +6367,18 @@ function ToolField({
5786
6367
  ),
5787
6368
  /* @__PURE__ */ jsxs10("span", { children: [
5788
6369
  /* @__PURE__ */ jsx10("strong", { children: field.label }),
5789
- field.description ? /* @__PURE__ */ jsx10("span", { id: `${id}-description`, children: field.description }) : null
6370
+ field.description ? /* @__PURE__ */ jsx10("span", { id: `${id2}-description`, children: field.description }) : null
5790
6371
  ] })
5791
6372
  ] }),
5792
- error ? /* @__PURE__ */ jsx10("span", { id: `${id}-error`, className: "tool-input-card__error", children: error }) : null
6373
+ error ? /* @__PURE__ */ jsx10("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
5793
6374
  ] });
5794
6375
  }
5795
- const label = /* @__PURE__ */ jsxs10("label", { id: `${id}-label`, htmlFor: id, children: [
6376
+ const label = /* @__PURE__ */ jsxs10("label", { id: `${id2}-label`, htmlFor: id2, children: [
5796
6377
  field.label,
5797
6378
  field.required ? /* @__PURE__ */ jsx10("span", { "aria-hidden": "true", children: " *" }) : null
5798
6379
  ] });
5799
6380
  const common = {
5800
- id,
6381
+ id: id2,
5801
6382
  disabled,
5802
6383
  required: field.required,
5803
6384
  "aria-describedby": describedBy || void 0,
@@ -5810,7 +6391,7 @@ function ToolField({
5810
6391
  ChoiceField,
5811
6392
  {
5812
6393
  field,
5813
- id,
6394
+ id: id2,
5814
6395
  value,
5815
6396
  disabled,
5816
6397
  describedBy: describedBy || void 0,
@@ -5847,7 +6428,7 @@ function ToolField({
5847
6428
  onChange: (event) => onChange(Number(event.target.value))
5848
6429
  }
5849
6430
  ),
5850
- /* @__PURE__ */ jsx10("output", { htmlFor: id, children: numericValue })
6431
+ /* @__PURE__ */ jsx10("output", { htmlFor: id2, children: numericValue })
5851
6432
  ] });
5852
6433
  } else {
5853
6434
  const type = field.kind === "date-time" ? "datetime-local" : field.kind === "calendar" ? "date" : field.kind;
@@ -5871,9 +6452,9 @@ function ToolField({
5871
6452
  }
5872
6453
  return /* @__PURE__ */ jsxs10("div", { className: "tool-input-card__field", children: [
5873
6454
  label,
5874
- /* @__PURE__ */ jsx10(FieldDescription, { field, id: `${id}-description` }),
6455
+ /* @__PURE__ */ jsx10(FieldDescription, { field, id: `${id2}-description` }),
5875
6456
  control,
5876
- error ? /* @__PURE__ */ jsx10("span", { id: `${id}-error`, className: "tool-input-card__error", children: error }) : null
6457
+ error ? /* @__PURE__ */ jsx10("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
5877
6458
  ] });
5878
6459
  }
5879
6460
  function ToolInputCard({
@@ -5881,11 +6462,11 @@ function ToolInputCard({
5881
6462
  surface,
5882
6463
  onSubmit
5883
6464
  }) {
5884
- const [values, setValues] = useState7(
6465
+ const [values, setValues] = useState8(
5885
6466
  () => initialValues(surface)
5886
6467
  );
5887
- const [touched, setTouched] = useState7(() => /* @__PURE__ */ new Set());
5888
- const [submitted, setSubmitted] = useState7(false);
6468
+ const [touched, setTouched] = useState8(() => /* @__PURE__ */ new Set());
6469
+ const [submitted, setSubmitted] = useState8(false);
5889
6470
  const errors = Object.fromEntries(
5890
6471
  surface.fields.map((field) => [
5891
6472
  field.path,
@@ -5978,7 +6559,7 @@ function HumanInputCard({
5978
6559
  request,
5979
6560
  onRespond
5980
6561
  }) {
5981
- const [text2, setText] = useState8("");
6562
+ const [text3, setText] = useState9("");
5982
6563
  const options = request.options ?? [];
5983
6564
  const showText = request.display === "text" || request.allowFreeform && options.length === 0;
5984
6565
  if (request.ui) {
@@ -6003,7 +6584,7 @@ function HumanInputCard({
6003
6584
  }
6004
6585
  function submitText(event) {
6005
6586
  event.preventDefault();
6006
- const value = text2.trim();
6587
+ const value = text3.trim();
6007
6588
  if (!value || disabled) return;
6008
6589
  onRespond?.({ requestId: request.requestId, text: value });
6009
6590
  }
@@ -6021,12 +6602,12 @@ function HumanInputCard({
6021
6602
  "input",
6022
6603
  {
6023
6604
  id: `human-input-text-${request.requestId}`,
6024
- value: text2,
6605
+ value: text3,
6025
6606
  disabled,
6026
6607
  onChange: (event) => setText(event.target.value)
6027
6608
  }
6028
6609
  ),
6029
- /* @__PURE__ */ jsx11("button", { type: "submit", disabled: disabled || !text2.trim(), children: "Send" })
6610
+ /* @__PURE__ */ jsx11("button", { type: "submit", disabled: disabled || !text3.trim(), children: "Send" })
6030
6611
  ] })
6031
6612
  ] }) : null,
6032
6613
  !showText && options.length === 0 ? /* @__PURE__ */ jsx11("p", { className: "human-input-card__unavailable", role: "status", children: "This request can\u2019t be answered here." }) : null
@@ -6035,31 +6616,173 @@ function HumanInputCard({
6035
6616
  );
6036
6617
  }
6037
6618
 
6038
- // src/react/components/CollectionResultCard/CollectionResultCard.tsx
6619
+ // src/react/components/LocationConsent/LocationConsent.tsx
6620
+ import { useEffect as useEffect7, useRef as useRef7, useState as useState10 } from "react";
6621
+
6622
+ // src/runtime/location-consent.ts
6623
+ function isLocationConsentRequest(request) {
6624
+ return request.kind === "tool-approval" && request.action.toolName === "request_location";
6625
+ }
6626
+ function preciseLocationResponse(requestId, position) {
6627
+ const { latitude, longitude, accuracy } = position.coords;
6628
+ if (!Number.isFinite(latitude) || latitude < -90 || latitude > 90 || !Number.isFinite(longitude) || longitude < -180 || longitude > 180 || !Number.isFinite(accuracy) || accuracy < 0 || !Number.isFinite(position.timestamp)) {
6629
+ throw new Error("Your device returned an invalid location.");
6630
+ }
6631
+ return {
6632
+ requestId,
6633
+ optionId: "approve",
6634
+ text: JSON.stringify({
6635
+ status: "shared",
6636
+ location: {
6637
+ source: "device",
6638
+ latitude,
6639
+ longitude,
6640
+ accuracyMeters: accuracy,
6641
+ capturedAt: new Date(position.timestamp).toISOString(),
6642
+ consentedAt: (/* @__PURE__ */ new Date()).toISOString(),
6643
+ scope: "conversation"
6644
+ }
6645
+ })
6646
+ };
6647
+ }
6648
+ function getPreciseLocation(geolocation) {
6649
+ return new Promise(
6650
+ (resolve, reject) => geolocation.getCurrentPosition(
6651
+ resolve,
6652
+ (error) => reject(
6653
+ new Error(
6654
+ error.code === 1 ? "Location access is blocked. Allow it in your browser, or continue without sharing." : "Your location couldn\u2019t be found. Try again or continue without sharing."
6655
+ )
6656
+ ),
6657
+ { enableHighAccuracy: true, maximumAge: 0, timeout: 1e4 }
6658
+ )
6659
+ );
6660
+ }
6661
+
6662
+ // src/react/components/LocationConsent/LocationConsent.tsx
6039
6663
  import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
6664
+ function LocationConsent({
6665
+ request,
6666
+ onRespond
6667
+ }) {
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);
6673
+ const requestId = request?.requestId;
6674
+ useEffect7(() => {
6675
+ respond.current = onRespond;
6676
+ }, [onRespond]);
6677
+ useEffect7(() => {
6678
+ if (!requestId || preference === "unset" || answered.current === requestId || !respond.current)
6679
+ return;
6680
+ if (preference === "off") {
6681
+ setLocating(false);
6682
+ answered.current = requestId;
6683
+ respond.current({
6684
+ requestId,
6685
+ optionId: "approve",
6686
+ text: JSON.stringify({ status: "declined" })
6687
+ });
6688
+ return;
6689
+ }
6690
+ let cancelled = false;
6691
+ const pendingId = requestId;
6692
+ setLocating(true);
6693
+ setError(null);
6694
+ async function locate() {
6695
+ try {
6696
+ if (!Reflect.has(navigator, "geolocation"))
6697
+ throw new Error("Location sharing isn\u2019t available in this browser.");
6698
+ const position = await getPreciseLocation(navigator.geolocation);
6699
+ if (cancelled) return;
6700
+ const response = preciseLocationResponse(pendingId, position);
6701
+ answered.current = pendingId;
6702
+ respond.current?.(response);
6703
+ } catch (failure) {
6704
+ if (cancelled) return;
6705
+ setPreference("unset");
6706
+ setError(
6707
+ failure instanceof Error ? failure.message : "Location sharing failed. Try again or continue without sharing."
6708
+ );
6709
+ } finally {
6710
+ if (!cancelled) setLocating(false);
6711
+ }
6712
+ }
6713
+ void locate();
6714
+ return () => {
6715
+ cancelled = true;
6716
+ };
6717
+ }, [requestId, preference]);
6718
+ const isLocating = Boolean(requestId) && preference === "on" && locating;
6719
+ if (!request && preference === "unset") return null;
6720
+ return /* @__PURE__ */ jsxs12("section", { className: "location-consent", "aria-label": "Location sharing", children: [
6721
+ /* @__PURE__ */ jsxs12("div", { className: "location-consent__copy", children: [
6722
+ /* @__PURE__ */ jsx12("strong", { children: isLocating ? "Getting your location\u2026" : request ? "Get better results" : "Precise location" }),
6723
+ /* @__PURE__ */ jsx12("span", { children: isLocating ? "Allow location access in your browser." : request ? "Share your precise location" : preference === "on" ? "On for location-aware answers" : "Off for this chat" })
6724
+ ] }),
6725
+ /* @__PURE__ */ jsxs12("div", { className: "location-consent__actions", children: [
6726
+ /* @__PURE__ */ jsx12(
6727
+ "button",
6728
+ {
6729
+ type: "button",
6730
+ role: "switch",
6731
+ className: "location-consent__toggle",
6732
+ "aria-label": "Share precise location",
6733
+ "aria-checked": preference === "on",
6734
+ "aria-busy": isLocating,
6735
+ disabled: !onRespond,
6736
+ onClick: () => {
6737
+ setError(null);
6738
+ setPreference(preference === "on" ? "off" : "on");
6739
+ },
6740
+ children: /* @__PURE__ */ jsx12("span", {})
6741
+ }
6742
+ ),
6743
+ request && /* @__PURE__ */ jsx12(
6744
+ "button",
6745
+ {
6746
+ type: "button",
6747
+ className: "location-consent__decline",
6748
+ disabled: !onRespond,
6749
+ onClick: () => {
6750
+ setError(null);
6751
+ setPreference("off");
6752
+ },
6753
+ children: "No thanks"
6754
+ }
6755
+ )
6756
+ ] }),
6757
+ error && /* @__PURE__ */ jsx12("p", { role: "alert", className: "location-consent__error", children: error })
6758
+ ] });
6759
+ }
6760
+
6761
+ // src/react/components/CollectionResultCard/CollectionResultCard.tsx
6762
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
6040
6763
  function CollectionResultCard({
6041
6764
  result
6042
6765
  }) {
6043
6766
  const empty = result.items.length === 0;
6044
- return /* @__PURE__ */ jsxs12(
6767
+ return /* @__PURE__ */ jsxs13(
6045
6768
  "section",
6046
6769
  {
6047
6770
  className: `collection-result-card tool-result-card tool-result-card--${result.status}`,
6048
6771
  "aria-label": result.title,
6049
6772
  role: result.status === "completed" ? "status" : "alert",
6050
6773
  children: [
6051
- /* @__PURE__ */ jsxs12("div", { className: "tool-result-card__heading", children: [
6052
- /* @__PURE__ */ jsx12("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6053
- /* @__PURE__ */ jsx12("strong", { children: result.title })
6774
+ /* @__PURE__ */ jsxs13("div", { className: "tool-result-card__heading", children: [
6775
+ /* @__PURE__ */ jsx13("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6776
+ /* @__PURE__ */ jsx13("strong", { children: result.title })
6054
6777
  ] }),
6055
- empty ? /* @__PURE__ */ jsx12("p", { className: "collection-result-card__empty", children: "No matching record for the email you shared." }) : /* @__PURE__ */ jsx12("ul", { className: "collection-result-card__list", children: result.items.map((item) => /* @__PURE__ */ jsxs12("li", { children: [
6056
- /* @__PURE__ */ jsx12("div", { className: "collection-result-card__item-title", children: item.title }),
6057
- item.description ? /* @__PURE__ */ jsx12("p", { children: item.description }) : null,
6058
- item.details?.length ? /* @__PURE__ */ jsx12("dl", { children: item.details.map((detail) => /* @__PURE__ */ jsxs12("div", { children: [
6059
- /* @__PURE__ */ jsx12("dt", { children: detail.label }),
6060
- /* @__PURE__ */ jsx12("dd", { children: detail.value })
6778
+ empty ? /* @__PURE__ */ jsx13("p", { className: "collection-result-card__empty", children: "No matching record for the email you shared." }) : /* @__PURE__ */ jsx13("ul", { className: "collection-result-card__list", children: result.items.map((item) => /* @__PURE__ */ jsxs13("li", { children: [
6779
+ /* @__PURE__ */ jsx13("div", { className: "collection-result-card__item-title", children: item.title }),
6780
+ item.description ? /* @__PURE__ */ jsx13("p", { children: item.description }) : null,
6781
+ item.details?.length ? /* @__PURE__ */ jsx13("dl", { children: item.details.map((detail) => /* @__PURE__ */ jsxs13("div", { children: [
6782
+ /* @__PURE__ */ jsx13("dt", { children: detail.label }),
6783
+ /* @__PURE__ */ jsx13("dd", { children: detail.value })
6061
6784
  ] }, `${detail.label}:${detail.value}`)) }) : null,
6062
- item.href ? /* @__PURE__ */ jsx12("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
6785
+ item.href ? /* @__PURE__ */ jsx13("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
6063
6786
  ] }, item.title)) })
6064
6787
  ]
6065
6788
  }
@@ -6067,23 +6790,23 @@ function CollectionResultCard({
6067
6790
  }
6068
6791
 
6069
6792
  // src/react/components/EntityResultCard/EntityResultCard.tsx
6070
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
6793
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
6071
6794
  function EntityResultCard({
6072
6795
  result
6073
6796
  }) {
6074
6797
  const compact = !result.description && !result.details?.length && !result.links?.length;
6075
6798
  const collapsible = result.status === "completed" && Boolean(result.details?.length) && !result.links?.length;
6076
- const heading = /* @__PURE__ */ jsxs13("span", { className: "tool-result-card__heading", children: [
6077
- /* @__PURE__ */ jsx13("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6078
- /* @__PURE__ */ jsx13("strong", { children: result.title })
6799
+ const heading = /* @__PURE__ */ jsxs14("span", { className: "tool-result-card__heading", children: [
6800
+ /* @__PURE__ */ jsx14("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6801
+ /* @__PURE__ */ jsx14("strong", { children: result.title })
6079
6802
  ] });
6080
- const content = /* @__PURE__ */ jsxs13(Fragment3, { children: [
6081
- result.description ? /* @__PURE__ */ jsx13("p", { children: result.description }) : null,
6082
- result.details?.length ? /* @__PURE__ */ jsx13("dl", { children: result.details.map((detail) => /* @__PURE__ */ jsxs13("div", { children: [
6083
- /* @__PURE__ */ jsx13("dt", { children: detail.label }),
6084
- /* @__PURE__ */ jsx13("dd", { children: detail.value })
6803
+ const content = /* @__PURE__ */ jsxs14(Fragment3, { children: [
6804
+ result.description ? /* @__PURE__ */ jsx14("p", { children: result.description }) : null,
6805
+ result.details?.length ? /* @__PURE__ */ jsx14("dl", { children: result.details.map((detail) => /* @__PURE__ */ jsxs14("div", { children: [
6806
+ /* @__PURE__ */ jsx14("dt", { children: detail.label }),
6807
+ /* @__PURE__ */ jsx14("dd", { children: detail.value })
6085
6808
  ] }, `${detail.label}:${detail.value}`)) }) : null,
6086
- result.links?.length ? /* @__PURE__ */ jsx13("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ jsx13(
6809
+ result.links?.length ? /* @__PURE__ */ jsx14("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ jsx14(
6087
6810
  "a",
6088
6811
  {
6089
6812
  href: link.href,
@@ -6095,12 +6818,12 @@ function EntityResultCard({
6095
6818
  )) }) : null
6096
6819
  ] });
6097
6820
  if (collapsible) {
6098
- return /* @__PURE__ */ jsxs13("details", { className: "entity-result-card tool-result-card entity-result-card--disclosure", children: [
6099
- /* @__PURE__ */ jsx13("summary", { children: /* @__PURE__ */ jsx13("span", { role: "status", children: heading }) }),
6821
+ return /* @__PURE__ */ jsxs14("details", { className: "entity-result-card tool-result-card entity-result-card--disclosure", children: [
6822
+ /* @__PURE__ */ jsx14("summary", { children: /* @__PURE__ */ jsx14("span", { role: "status", children: heading }) }),
6100
6823
  content
6101
6824
  ] });
6102
6825
  }
6103
- return /* @__PURE__ */ jsxs13(
6826
+ return /* @__PURE__ */ jsxs14(
6104
6827
  "section",
6105
6828
  {
6106
6829
  className: `entity-result-card tool-result-card tool-result-card--${result.status}${compact ? " entity-result-card--compact" : ""}`,
@@ -6115,24 +6838,24 @@ function EntityResultCard({
6115
6838
  }
6116
6839
 
6117
6840
  // src/react/components/SignatureResultCard/SignatureResultCard.tsx
6118
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
6841
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
6119
6842
  function SignatureResultCard({
6120
6843
  result
6121
6844
  }) {
6122
6845
  const primaryLink = result.links?.[0];
6123
- return /* @__PURE__ */ jsxs14(
6846
+ return /* @__PURE__ */ jsxs15(
6124
6847
  "section",
6125
6848
  {
6126
6849
  className: `signature-result-card tool-result-card tool-result-card--${result.status}`,
6127
6850
  "aria-label": result.title,
6128
6851
  children: [
6129
- /* @__PURE__ */ jsxs14("div", { className: "tool-result-card__heading", children: [
6130
- /* @__PURE__ */ jsx14("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6131
- /* @__PURE__ */ jsx14("strong", { children: result.title })
6852
+ /* @__PURE__ */ jsxs15("div", { className: "tool-result-card__heading", children: [
6853
+ /* @__PURE__ */ jsx15("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6854
+ /* @__PURE__ */ jsx15("strong", { children: result.title })
6132
6855
  ] }),
6133
- result.statusLabel ? /* @__PURE__ */ jsx14("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
6134
- result.description ? /* @__PURE__ */ jsx14("p", { children: result.description }) : null,
6135
- primaryLink ? /* @__PURE__ */ jsx14(
6856
+ result.statusLabel ? /* @__PURE__ */ jsx15("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
6857
+ result.description ? /* @__PURE__ */ jsx15("p", { children: result.description }) : null,
6858
+ primaryLink ? /* @__PURE__ */ jsx15(
6136
6859
  "a",
6137
6860
  {
6138
6861
  className: "signature-result-card__cta",
@@ -6148,27 +6871,27 @@ function SignatureResultCard({
6148
6871
  }
6149
6872
 
6150
6873
  // src/react/components/ToolResultCard/ToolResultCard.tsx
6151
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
6874
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
6152
6875
  function ToolResultCard({
6153
6876
  result
6154
6877
  }) {
6155
- return /* @__PURE__ */ jsxs15(
6878
+ return /* @__PURE__ */ jsxs16(
6156
6879
  "section",
6157
6880
  {
6158
6881
  className: `tool-result-card tool-result-card--${result.status}`,
6159
6882
  "aria-label": result.title,
6160
6883
  role: result.status === "completed" ? "status" : "alert",
6161
6884
  children: [
6162
- /* @__PURE__ */ jsxs15("div", { className: "tool-result-card__heading", children: [
6163
- /* @__PURE__ */ jsx15("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6164
- /* @__PURE__ */ jsx15("strong", { children: result.title })
6885
+ /* @__PURE__ */ jsxs16("div", { className: "tool-result-card__heading", children: [
6886
+ /* @__PURE__ */ jsx16("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6887
+ /* @__PURE__ */ jsx16("strong", { children: result.title })
6165
6888
  ] }),
6166
- result.description ? /* @__PURE__ */ jsx15("p", { children: result.description }) : null,
6167
- result.details?.length ? /* @__PURE__ */ jsx15("dl", { children: result.details.map((detail) => /* @__PURE__ */ jsxs15("div", { children: [
6168
- /* @__PURE__ */ jsx15("dt", { children: detail.label }),
6169
- /* @__PURE__ */ jsx15("dd", { children: detail.value })
6889
+ result.description ? /* @__PURE__ */ jsx16("p", { children: result.description }) : null,
6890
+ result.details?.length ? /* @__PURE__ */ jsx16("dl", { children: result.details.map((detail) => /* @__PURE__ */ jsxs16("div", { children: [
6891
+ /* @__PURE__ */ jsx16("dt", { children: detail.label }),
6892
+ /* @__PURE__ */ jsx16("dd", { children: detail.value })
6170
6893
  ] }, `${detail.label}:${detail.value}`)) }) : null,
6171
- result.links?.length ? /* @__PURE__ */ jsx15("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ jsx15(
6894
+ result.links?.length ? /* @__PURE__ */ jsx16("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ jsx16(
6172
6895
  "a",
6173
6896
  {
6174
6897
  href: link.href,
@@ -6184,14 +6907,14 @@ function ToolResultCard({
6184
6907
  }
6185
6908
 
6186
6909
  // src/react/components/VisitorToolResultView/VisitorToolResultView.tsx
6187
- import { jsx as jsx16 } from "react/jsx-runtime";
6910
+ import { jsx as jsx17 } from "react/jsx-runtime";
6188
6911
  function VisitorToolResultView({
6189
6912
  disabled = false,
6190
6913
  onToolInput,
6191
6914
  result
6192
6915
  }) {
6193
6916
  if (result.kind === "input") {
6194
- return /* @__PURE__ */ jsx16(
6917
+ return /* @__PURE__ */ jsx17(
6195
6918
  ToolInputCard,
6196
6919
  {
6197
6920
  disabled,
@@ -6201,16 +6924,16 @@ function VisitorToolResultView({
6201
6924
  );
6202
6925
  }
6203
6926
  if (result.kind === "entity") {
6204
- return /* @__PURE__ */ jsx16(EntityResultCard, { result });
6927
+ return /* @__PURE__ */ jsx17(EntityResultCard, { result });
6205
6928
  }
6206
6929
  if (result.kind === "collection") {
6207
- return /* @__PURE__ */ jsx16(CollectionResultCard, { result });
6930
+ return /* @__PURE__ */ jsx17(CollectionResultCard, { result });
6208
6931
  }
6209
6932
  if (result.kind === "signature") {
6210
- return /* @__PURE__ */ jsx16(SignatureResultCard, { result });
6933
+ return /* @__PURE__ */ jsx17(SignatureResultCard, { result });
6211
6934
  }
6212
6935
  if (result.kind === "summary") {
6213
- return /* @__PURE__ */ jsx16(ToolResultCard, { result });
6936
+ return /* @__PURE__ */ jsx17(ToolResultCard, { result });
6214
6937
  }
6215
6938
  return null;
6216
6939
  }
@@ -6219,9 +6942,9 @@ function isRenderableVisitorToolResult(result) {
6219
6942
  }
6220
6943
 
6221
6944
  // src/react/components/AgentRail/AgentRail.tsx
6222
- import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6945
+ import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
6223
6946
  function MinimizeIcon() {
6224
- return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(
6947
+ return /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
6225
6948
  "path",
6226
6949
  {
6227
6950
  d: "M3.5 8h9",
@@ -6232,7 +6955,7 @@ function MinimizeIcon() {
6232
6955
  ) });
6233
6956
  }
6234
6957
  function CloseIcon2() {
6235
- return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(
6958
+ return /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
6236
6959
  "path",
6237
6960
  {
6238
6961
  d: "M4 4l8 8M12 4l-8 8",
@@ -6243,7 +6966,7 @@ function CloseIcon2() {
6243
6966
  ) });
6244
6967
  }
6245
6968
  function NewChatIcon() {
6246
- return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(
6969
+ return /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
6247
6970
  "path",
6248
6971
  {
6249
6972
  d: "M9.5 3.5h3v3M12.25 3.75 8 8M7 4H4.5A1.5 1.5 0 0 0 3 5.5v6A1.5 1.5 0 0 0 4.5 13h6a1.5 1.5 0 0 0 1.5-1.5V9",
@@ -6255,7 +6978,7 @@ function NewChatIcon() {
6255
6978
  ) });
6256
6979
  }
6257
6980
  function ExpandIcon() {
6258
- return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(
6981
+ return /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
6259
6982
  "path",
6260
6983
  {
6261
6984
  d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
@@ -6267,7 +6990,7 @@ function ExpandIcon() {
6267
6990
  ) });
6268
6991
  }
6269
6992
  function RestoreIcon() {
6270
- return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(
6993
+ return /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
6271
6994
  "path",
6272
6995
  {
6273
6996
  d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
@@ -6279,7 +7002,7 @@ function RestoreIcon() {
6279
7002
  ) });
6280
7003
  }
6281
7004
  function ChevronDownIcon() {
6282
- return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(
7005
+ return /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
6283
7006
  "path",
6284
7007
  {
6285
7008
  d: "M4 6.5l4 4 4-4",
@@ -6293,6 +7016,7 @@ function ChevronDownIcon() {
6293
7016
  var DEFAULT_DISCLAIMER_LABEL = "Agent can make mistakes. Check important info.";
6294
7017
  function AgentRail({
6295
7018
  state,
7019
+ handoff,
6296
7020
  theme,
6297
7021
  colorScheme = "auto",
6298
7022
  brandLabel = "",
@@ -6318,43 +7042,51 @@ function AgentRail({
6318
7042
  onInputResponse,
6319
7043
  onToolInput
6320
7044
  }) {
6321
- const railRef = useRef6(null);
6322
- const overlayRef = useRef6(null);
6323
- const transcriptRef = useRef6(null);
6324
- const responseRef = useRef6(null);
6325
- const threadRef = useRef6(null);
6326
- const lastScrolledVisitorIdRef = useRef6(void 0);
6327
- const pinnedToBottomRef = useRef6(true);
6328
- const smoothScrollToLatestRef = useRef6(false);
6329
- const lockedTranscriptScrollTopRef = useRef6(null);
6330
- const [showJumpToLatest, setShowJumpToLatest] = useState9(false);
6331
- const [receiptOpen, setReceiptOpen] = useState9(false);
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);
6332
7056
  const resolvedBrandLabel = brandLabel.trim();
6333
7057
  const resolvedBrandLogoUrl = brandLogoUrl?.trim();
6334
7058
  const resolvedDisclaimerLabel = disclaimerLabel === void 0 ? DEFAULT_DISCLAIMER_LABEL : disclaimerLabel;
6335
- const [failedLogoUrl, setFailedLogoUrl] = useState9(null);
7059
+ const [failedLogoUrl, setFailedLogoUrl] = useState11(null);
6336
7060
  const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
6337
7061
  const resolvedColorScheme = useAgentColorScheme(colorScheme);
6338
7062
  const railStyle = agentThemeStyle(theme, resolvedColorScheme);
7063
+ const handoffActive = Boolean(handoff?.blocksAI);
7064
+ const handoffStatus = handoff?.page?.status;
7065
+ const locationRequest = handoffActive ? void 0 : state.pendingInputs?.find(isLocationConsentRequest);
6339
7066
  const pendingInputRequests = (state.pendingInputs ?? []).filter(
6340
- (request) => shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
7067
+ (request) => !handoffActive && !isLocationConsentRequest(request) && shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
6341
7068
  );
6342
- const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && pendingInputRequests.length > 0;
6343
- const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
7069
+ const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && (pendingInputRequests.length > 0 || Boolean(locationRequest));
7070
+ const semanticSurfaceDisabled = handoffActive || isBusy && state.phase !== "waiting-input";
6344
7071
  const visitorToolResults = (state.toolResults ?? []).filter(
6345
7072
  isRenderableVisitorToolResult
6346
7073
  );
6347
7074
  const activeVisitorToolInput = [...visitorToolResults].reverse().find((result) => result.kind === "input");
6348
7075
  const visibleVisitorToolResults = activeVisitorToolInput ? [activeVisitorToolInput] : visitorToolResults;
6349
- const activityActive = state.toolSteps.some((step) => step.state === "active");
7076
+ const activityActive = state.toolSteps.some(
7077
+ (step) => step.state === "active"
7078
+ );
6350
7079
  const showActivity = state.toolSteps.length > 0 && pendingInputRequests.length === 0 && !state.pendingOffer && (activityActive || isBusy && !state.streamingText);
6351
- const activitySteps = activityActive || !isBusy || state.streamingText ? state.toolSteps : [...state.toolSteps, {
6352
- id: "preparing-answer",
6353
- kind: "tool",
6354
- label: "Preparing your answer",
6355
- detail: "Preparing your answer",
6356
- state: "active"
6357
- }];
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
+ ];
6358
7090
  const hasVisitorMessages2 = state.messages.some(
6359
7091
  (message) => message.role === "visitor"
6360
7092
  );
@@ -6383,7 +7115,7 @@ function AgentRail({
6383
7115
  }
6384
7116
  }
6385
7117
  const lastIsAgent = lastMessage?.role === "agent";
6386
- 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());
6387
7119
  const streamingMessage = state.streamingText && !lastIsAgent ? {
6388
7120
  createdAt: 0,
6389
7121
  id: "streaming-response",
@@ -6425,7 +7157,7 @@ function AgentRail({
6425
7157
  ...visibleVisitorToolResults
6426
7158
  ].reverse().find((result) => result.kind !== "input")?.id;
6427
7159
  const receiptSteps = answerReceipt && state.toolSteps.length > 0 ? state.toolSteps : void 0;
6428
- useEffect6(() => {
7160
+ useEffect8(() => {
6429
7161
  if (state.phase !== "complete") {
6430
7162
  setReceiptOpen(false);
6431
7163
  }
@@ -6447,7 +7179,7 @@ function AgentRail({
6447
7179
  onFollowUpSelect?.(label);
6448
7180
  }
6449
7181
  const latestVisitorId = visibleMessages[lastVisitorIndex]?.id;
6450
- useEffect6(() => {
7182
+ useEffect8(() => {
6451
7183
  const node = transcriptRef.current;
6452
7184
  if (!node) return;
6453
7185
  if (latestVisitorId !== lastScrolledVisitorIdRef.current) {
@@ -6477,7 +7209,7 @@ function AgentRail({
6477
7209
  state.followUps,
6478
7210
  state.journey
6479
7211
  ]);
6480
- useEffect6(() => {
7212
+ useEffect8(() => {
6481
7213
  const node = transcriptRef.current;
6482
7214
  if (!node) return;
6483
7215
  const handleScroll = () => {
@@ -6491,7 +7223,7 @@ function AgentRail({
6491
7223
  handleScroll();
6492
7224
  return () => node.removeEventListener("scroll", handleScroll);
6493
7225
  }, []);
6494
- useEffect6(() => {
7226
+ useEffect8(() => {
6495
7227
  if (!receiptOpen) {
6496
7228
  lockedTranscriptScrollTopRef.current = null;
6497
7229
  return;
@@ -6529,7 +7261,7 @@ function AgentRail({
6529
7261
  window.setTimeout(settle, 900);
6530
7262
  node.scrollTo({ top: node.scrollHeight, behavior: "smooth" });
6531
7263
  }
6532
- return /* @__PURE__ */ jsx17(
7264
+ return /* @__PURE__ */ jsx18(
6533
7265
  "aside",
6534
7266
  {
6535
7267
  ref: railRef,
@@ -6543,244 +7275,305 @@ function AgentRail({
6543
7275
  autoFocus: mobileFullscreen || expanded,
6544
7276
  role: mobileFullscreen || expanded ? "dialog" : void 0,
6545
7277
  tabIndex: mobileFullscreen || expanded ? -1 : void 0,
6546
- children: /* @__PURE__ */ jsxs16(
6547
- AgentRailOverlayContext.Provider,
6548
- {
6549
- value: { railRef, overlayRef },
6550
- children: [
6551
- /* @__PURE__ */ jsxs16("div", { className: "agent-rail__surface", inert: receiptOpen || void 0, children: [
6552
- /* @__PURE__ */ jsx17("header", { className: "agent-rail__header", children: /* @__PURE__ */ jsxs16("div", { className: "agent-rail__brand-row", children: [
6553
- onCollapse ? /* @__PURE__ */ jsx17(
6554
- "button",
6555
- {
6556
- type: "button",
6557
- className: "agent-rail__collapse",
6558
- "aria-label": "Collapse assist",
6559
- onClick: onCollapse,
6560
- children: /* @__PURE__ */ jsx17(MinimizeIcon, {})
6561
- }
6562
- ) : onClose ? /* @__PURE__ */ jsx17(
6563
- "button",
6564
- {
6565
- type: "button",
6566
- className: "agent-rail__close",
6567
- "aria-label": "Close agent",
6568
- onClick: onClose,
6569
- children: /* @__PURE__ */ jsx17(CloseIcon2, {})
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);
6570
7309
  }
6571
- ) : /* @__PURE__ */ jsx17("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
6572
- resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ jsxs16("span", { className: "agent-rail__identity", children: [
6573
- showBrandLogo ? /* @__PURE__ */ jsx17("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(
6574
- "img",
6575
- {
6576
- className: "agent-rail__brand-logo",
6577
- src: resolvedBrandLogoUrl,
6578
- alt: "",
6579
- onError: () => {
6580
- setFailedLogoUrl(resolvedBrandLogoUrl ?? null);
6581
- }
6582
- }
6583
- ) }) : null,
6584
- resolvedBrandLabel ? /* @__PURE__ */ jsx17("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
6585
- ] }) : null,
6586
- /* @__PURE__ */ jsxs16("span", { className: "agent-rail__actions", children: [
6587
- onReset ? /* @__PURE__ */ jsx17(
6588
- "button",
6589
- {
6590
- type: "button",
6591
- className: "agent-rail__new-chat",
6592
- "aria-label": "Start a new conversation",
6593
- disabled: !hasVisitorMessages2,
6594
- onClick: handleReset,
6595
- children: /* @__PURE__ */ jsx17(NewChatIcon, {})
6596
- }
6597
- ) : null,
6598
- onExpandToggle ? /* @__PURE__ */ jsx17(
6599
- "button",
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,
6600
7396
  {
6601
- type: "button",
6602
- className: "agent-rail__expand",
6603
- "aria-label": expanded ? "Exit full screen" : "Open full screen",
6604
- onClick: onExpandToggle,
6605
- children: expanded ? /* @__PURE__ */ jsx17(RestoreIcon, {}) : /* @__PURE__ */ jsx17(ExpandIcon, {})
6606
- }
6607
- ) : null
6608
- ] })
6609
- ] }) }),
6610
- /* @__PURE__ */ jsx17("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ jsxs16("div", { ref: threadRef, className: "agent-rail__thread", children: [
6611
- !hasVisitorMessages2 ? /* @__PURE__ */ jsxs16("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
6612
- greeting?.role === "agent" ? /* @__PURE__ */ jsx17(
7397
+ result
7398
+ },
7399
+ result.id
7400
+ )) : null,
7401
+ /* @__PURE__ */ jsx18(
6613
7402
  MessageBubble,
6614
7403
  {
6615
- message: greeting,
6616
- bookingDisabled: isBusy,
7404
+ showProvenance: Boolean(
7405
+ handoff?.page?.enabled || handoffActive
7406
+ ),
7407
+ message,
7408
+ bookingDisabled: isBusy || handoffActive,
6617
7409
  brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
7410
+ offer: index === transcriptMessages.length - 1 ? state.pendingOffer : void 0,
6618
7411
  onBook
6619
7412
  }
6620
- ) : null,
6621
- showIdleFollowUps ? /* @__PURE__ */ jsx17("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ jsx17(
6622
- FollowUpChips,
7413
+ ),
7414
+ index === lastAgentIndex && showMessageActions && message.role === "agent" ? /* @__PURE__ */ jsx18(
7415
+ MessageActions,
6623
7416
  {
6624
- suggestions: state.followUps,
6625
- disabled: isBusy,
6626
- label: "Start here",
6627
- onSelect: (suggestion) => handleFollowUpSelect(suggestion.label)
6628
- }
6629
- ) }) : null,
6630
- showActivity ? /* @__PURE__ */ jsx17(
6631
- AgentActivityBubble,
6632
- {
6633
- brandLabel: resolvedBrandLabel,
6634
- brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
6635
- failed: state.phase === "error",
6636
- steps: activitySteps
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
6637
7424
  }
6638
7425
  ) : null,
6639
- visibleVisitorToolResults.map((result) => /* @__PURE__ */ jsx17(
6640
- VisitorToolResultView,
6641
- {
6642
- result,
6643
- disabled: semanticSurfaceDisabled,
6644
- onToolInput
6645
- },
6646
- result.id
6647
- )),
6648
- pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ jsx17(
6649
- HumanInputCard,
6650
- {
6651
- request,
6652
- onRespond: onInputResponse
6653
- },
6654
- request.requestId
6655
- ))
6656
- ] }) : null,
6657
- transcriptMessages.map((message, index) => /* @__PURE__ */ jsxs16(
6658
- "div",
6659
- {
6660
- ref: index === lastVisitorIndex + 1 ? responseRef : void 0,
6661
- className: "agent-rail__turn-block",
6662
- children: [
6663
- message.role === "agent" ? message.toolResults?.map((result) => /* @__PURE__ */ jsx17(VisitorToolResultView, { result }, result.id)) : null,
6664
- /* @__PURE__ */ jsx17(
6665
- MessageBubble,
6666
- {
6667
- message,
6668
- bookingDisabled: isBusy,
6669
- brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
6670
- offer: index === transcriptMessages.length - 1 ? state.pendingOffer : void 0,
6671
- onBook
6672
- }
6673
- ),
6674
- index === lastAgentIndex && showMessageActions && message.role === "agent" ? /* @__PURE__ */ jsx17(
6675
- MessageActions,
6676
- {
6677
- answeredAt: message.createdAt,
6678
- copyText: hideToolCardFences(message.text).trim() || message.text,
6679
- readAloud,
6680
- receiptSteps,
6681
- onOpenReceipt: receiptSteps ? openReceipt : void 0,
6682
- onRegenerate: onRegenerate ? handleRegenerate : void 0,
6683
- onFeedback: onFeedback ? (rating) => onFeedback(rating, message) : void 0
6684
- }
6685
- ) : null,
6686
- index === lastVisitorIndex ? /* @__PURE__ */ jsxs16(Fragment4, { children: [
6687
- showActivity ? /* @__PURE__ */ jsx17(
6688
- AgentActivityBubble,
6689
- {
6690
- brandLabel: resolvedBrandLabel,
6691
- brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
6692
- failed: state.phase === "error",
6693
- steps: activitySteps
6694
- }
6695
- ) : null,
6696
- visibleVisitorToolResults.map((result) => /* @__PURE__ */ jsx17(
6697
- VisitorToolResultView,
6698
- {
6699
- result,
6700
- disabled: semanticSurfaceDisabled,
6701
- onToolInput
6702
- },
6703
- result.id
6704
- )),
6705
- pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ jsx17(
6706
- HumanInputCard,
6707
- {
6708
- request,
6709
- onRespond: onInputResponse
6710
- },
6711
- request.requestId
6712
- ))
6713
- ] }) : null
6714
- ]
6715
- },
6716
- message.role === "agent" && transcriptMessages[index - 1]?.role === "visitor" ? `answer-${transcriptMessages[index - 1]?.id}` : message.id
6717
- )),
6718
- waitingForBooking ? /* @__PURE__ */ jsx17(BookingCardLoader, {}) : null,
6719
- state.error ? /* @__PURE__ */ jsxs16("section", { className: "agent-rail__error", role: "alert", children: [
6720
- /* @__PURE__ */ jsxs16("div", { children: [
6721
- /* @__PURE__ */ jsx17("strong", { children: "Something went wrong" }),
6722
- /* @__PURE__ */ jsx17("p", { children: state.error })
6723
- ] }),
6724
- onRetry ? /* @__PURE__ */ jsx17("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
6725
- ] }) : null
6726
- ] }) }),
6727
- showDisclaimerLabel ? /* @__PURE__ */ jsx17("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ jsx17("p", { children: disclaimerLink ? /* @__PURE__ */ jsx17(
6728
- "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",
6729
7509
  {
6730
- href: disclaimerLink,
6731
- rel: "noopener noreferrer",
6732
- target: "_blank",
6733
- children: resolvedDisclaimerLabel
7510
+ type: "button",
7511
+ disabled: handoff.busy,
7512
+ onClick: () => void handoff.retry(),
7513
+ children: "Retry"
6734
7514
  }
6735
- ) : resolvedDisclaimerLabel }) }) : null,
6736
- /* @__PURE__ */ jsxs16("div", { className: "agent-rail__composer-wrap", children: [
6737
- showJumpToLatest ? /* @__PURE__ */ jsx17(
6738
- "button",
6739
- {
6740
- type: "button",
6741
- className: "agent-rail__jump-to-latest",
6742
- "aria-label": "Jump to the latest message",
6743
- title: "Jump to the latest message",
6744
- onClick: scrollToLatest,
6745
- children: /* @__PURE__ */ jsx17(ChevronDownIcon, {})
6746
- }
6747
- ) : null,
6748
- /* @__PURE__ */ jsx17(
6749
- Composer,
6750
- {
6751
- variant: expanded || mobileFullscreen ? "dock" : "default",
6752
- disabled: isBusy,
6753
- form: composerForm && lastMessage ? { ...composerForm, id: `${lastMessage.id}:${composerForm.id}` } : null,
6754
- allowFormResume: !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
6755
- placeholder: composerPlaceholder,
6756
- onSubmit: handleSubmit
6757
- },
6758
- `${state.messages.find((message) => message.role === "visitor")?.id ?? "empty"}:${latestResultId ?? ""}`
6759
- ),
6760
- poweredByLabel ? /* @__PURE__ */ jsx17("div", { className: "agent-rail__footer", children: /* @__PURE__ */ jsx17("p", { children: /* @__PURE__ */ jsx17("span", { children: poweredByLabel }) }) }) : null
6761
- ] })
6762
- ] }),
6763
- /* @__PURE__ */ jsx17("div", { ref: overlayRef, className: "agent-rail__overlay" }),
6764
- receiptOpen && receiptSteps ? /* @__PURE__ */ jsx17(
6765
- 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",
6766
7529
  {
6767
- brandLabel: resolvedBrandLabel,
6768
- onClose: () => setReceiptOpen(false),
6769
- steps: receiptSteps
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, {})
6770
7536
  }
6771
- ) : null
6772
- ]
6773
- }
6774
- )
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
+ ] })
6775
7568
  }
6776
7569
  );
6777
7570
  }
6778
7571
 
6779
7572
  // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
6780
- import { useState as useState10 } from "react";
6781
- import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
7573
+ import { useState as useState12 } from "react";
7574
+ import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
6782
7575
  function ChatSparkIcon() {
6783
- return /* @__PURE__ */ jsxs17(
7576
+ return /* @__PURE__ */ jsxs18(
6784
7577
  "svg",
6785
7578
  {
6786
7579
  className: "assist-edge-tab__chat-spark",
@@ -6788,7 +7581,7 @@ function ChatSparkIcon() {
6788
7581
  fill: "none",
6789
7582
  "aria-hidden": "true",
6790
7583
  children: [
6791
- /* @__PURE__ */ jsx18(
7584
+ /* @__PURE__ */ jsx19(
6792
7585
  "path",
6793
7586
  {
6794
7587
  className: "assist-edge-tab__spark assist-edge-tab__spark--a",
@@ -6796,7 +7589,7 @@ function ChatSparkIcon() {
6796
7589
  fill: "currentColor"
6797
7590
  }
6798
7591
  ),
6799
- /* @__PURE__ */ jsx18(
7592
+ /* @__PURE__ */ jsx19(
6800
7593
  "path",
6801
7594
  {
6802
7595
  className: "assist-edge-tab__spark assist-edge-tab__spark--b",
@@ -6804,8 +7597,8 @@ function ChatSparkIcon() {
6804
7597
  fill: "currentColor"
6805
7598
  }
6806
7599
  ),
6807
- /* @__PURE__ */ jsxs17("g", { className: "assist-edge-tab__bot", children: [
6808
- /* @__PURE__ */ jsx18(
7600
+ /* @__PURE__ */ jsxs18("g", { className: "assist-edge-tab__bot", children: [
7601
+ /* @__PURE__ */ jsx19(
6809
7602
  "path",
6810
7603
  {
6811
7604
  d: "M11.15 5.2V3.05",
@@ -6814,8 +7607,8 @@ function ChatSparkIcon() {
6814
7607
  strokeLinecap: "round"
6815
7608
  }
6816
7609
  ),
6817
- /* @__PURE__ */ jsx18("circle", { cx: "11.15", cy: "2.45", r: "0.85", fill: "currentColor" }),
6818
- /* @__PURE__ */ jsx18(
7610
+ /* @__PURE__ */ jsx19("circle", { cx: "11.15", cy: "2.45", r: "0.85", fill: "currentColor" }),
7611
+ /* @__PURE__ */ jsx19(
6819
7612
  "rect",
6820
7613
  {
6821
7614
  x: "3.55",
@@ -6827,7 +7620,7 @@ function ChatSparkIcon() {
6827
7620
  strokeWidth: "1.9"
6828
7621
  }
6829
7622
  ),
6830
- /* @__PURE__ */ jsx18(
7623
+ /* @__PURE__ */ jsx19(
6831
7624
  "circle",
6832
7625
  {
6833
7626
  className: "assist-edge-tab__eye",
@@ -6837,7 +7630,7 @@ function ChatSparkIcon() {
6837
7630
  fill: "currentColor"
6838
7631
  }
6839
7632
  ),
6840
- /* @__PURE__ */ jsx18(
7633
+ /* @__PURE__ */ jsx19(
6841
7634
  "circle",
6842
7635
  {
6843
7636
  className: "assist-edge-tab__eye assist-edge-tab__eye--r",
@@ -6858,10 +7651,10 @@ function TabMarkIcon({
6858
7651
  }) {
6859
7652
  const custom = customIconUrl?.trim();
6860
7653
  const logo = logoUrl?.trim();
6861
- const [customFailed, setCustomFailed] = useState10(false);
6862
- const [logoFailed, setLogoFailed] = useState10(false);
7654
+ const [customFailed, setCustomFailed] = useState12(false);
7655
+ const [logoFailed, setLogoFailed] = useState12(false);
6863
7656
  if (custom && !customFailed) {
6864
- return /* @__PURE__ */ jsx18(
7657
+ return /* @__PURE__ */ jsx19(
6865
7658
  "img",
6866
7659
  {
6867
7660
  alt: "",
@@ -6873,7 +7666,7 @@ function TabMarkIcon({
6873
7666
  );
6874
7667
  }
6875
7668
  if (logo && !logoFailed) {
6876
- return /* @__PURE__ */ jsx18(
7669
+ return /* @__PURE__ */ jsx19(
6877
7670
  "img",
6878
7671
  {
6879
7672
  alt: "",
@@ -6884,10 +7677,10 @@ function TabMarkIcon({
6884
7677
  }
6885
7678
  );
6886
7679
  }
6887
- return /* @__PURE__ */ jsx18(ChatSparkIcon, {});
7680
+ return /* @__PURE__ */ jsx19(ChatSparkIcon, {});
6888
7681
  }
6889
7682
  function ChevronLeftIcon() {
6890
- return /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
7683
+ return /* @__PURE__ */ jsx19("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(
6891
7684
  "path",
6892
7685
  {
6893
7686
  d: "M10 4L6 8l4 4",
@@ -6899,7 +7692,7 @@ function ChevronLeftIcon() {
6899
7692
  ) });
6900
7693
  }
6901
7694
  function ChevronDownIcon2() {
6902
- return /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
7695
+ return /* @__PURE__ */ jsx19("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(
6903
7696
  "path",
6904
7697
  {
6905
7698
  d: "M4 6l4 4 4-4",
@@ -6911,7 +7704,7 @@ function ChevronDownIcon2() {
6911
7704
  ) });
6912
7705
  }
6913
7706
  function DragDots() {
6914
- return /* @__PURE__ */ jsx18("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ jsx18("i", {}, index)) });
7707
+ return /* @__PURE__ */ jsx19("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ jsx19("i", {}, index)) });
6915
7708
  }
6916
7709
  var VARIANT_COPY = {
6917
7710
  outline: { label: "Ask anything", aria: "Ask anything" },
@@ -6966,7 +7759,7 @@ function AssistEdgeTab({
6966
7759
  ...!pill && resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
6967
7760
  colorScheme: chromeScheme
6968
7761
  };
6969
- return /* @__PURE__ */ jsxs17(
7762
+ return /* @__PURE__ */ jsxs18(
6970
7763
  "button",
6971
7764
  {
6972
7765
  type: "button",
@@ -6978,13 +7771,13 @@ function AssistEdgeTab({
6978
7771
  tabIndex: visible ? 0 : -1,
6979
7772
  onClick: onOpen,
6980
7773
  children: [
6981
- pill ? /* @__PURE__ */ jsxs17(Fragment5, { children: [
6982
- /* @__PURE__ */ jsx18(
7774
+ pill ? /* @__PURE__ */ jsxs18(Fragment5, { children: [
7775
+ /* @__PURE__ */ jsx19(
6983
7776
  "span",
6984
7777
  {
6985
7778
  className: `assist-edge-tab__mark assist-edge-tab__mark--chip${mobile ? " assist-edge-tab__mark--mobile" : ""}`,
6986
7779
  "aria-hidden": "true",
6987
- children: /* @__PURE__ */ jsx18(
7780
+ children: /* @__PURE__ */ jsx19(
6988
7781
  TabMarkIcon,
6989
7782
  {
6990
7783
  customIconUrl,
@@ -6994,12 +7787,12 @@ function AssistEdgeTab({
6994
7787
  )
6995
7788
  }
6996
7789
  ),
6997
- /* @__PURE__ */ jsxs17("span", { className: "assist-edge-tab__text", children: [
6998
- /* @__PURE__ */ jsx18("span", { className: "assist-edge-tab__label", children: visibleLabel }),
6999
- visibleSubLabel ? /* @__PURE__ */ jsx18("span", { className: "assist-edge-tab__sublabel", children: visibleSubLabel }) : null
7790
+ /* @__PURE__ */ jsxs18("span", { className: "assist-edge-tab__text", children: [
7791
+ /* @__PURE__ */ jsx19("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7792
+ visibleSubLabel ? /* @__PURE__ */ jsx19("span", { className: "assist-edge-tab__sublabel", children: visibleSubLabel }) : null
7000
7793
  ] })
7001
- ] }) : variant === "outline" ? /* @__PURE__ */ jsxs17(Fragment5, { children: [
7002
- /* @__PURE__ */ jsx18("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
7794
+ ] }) : variant === "outline" ? /* @__PURE__ */ jsxs18(Fragment5, { children: [
7795
+ /* @__PURE__ */ jsx19("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(
7003
7796
  TabMarkIcon,
7004
7797
  {
7005
7798
  customIconUrl,
@@ -7007,16 +7800,16 @@ function AssistEdgeTab({
7007
7800
  },
7008
7801
  markSourceKey
7009
7802
  ) }),
7010
- /* @__PURE__ */ jsx18("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7011
- /* @__PURE__ */ jsx18(ChevronDownIcon2, {})
7803
+ /* @__PURE__ */ jsx19("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7804
+ /* @__PURE__ */ jsx19(ChevronDownIcon2, {})
7012
7805
  ] }) : null,
7013
- !pill && variant === "ask" ? /* @__PURE__ */ jsxs17(Fragment5, { children: [
7014
- /* @__PURE__ */ jsx18(ChevronLeftIcon, {}),
7015
- /* @__PURE__ */ jsx18("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7016
- /* @__PURE__ */ jsx18(DragDots, {})
7806
+ !pill && variant === "ask" ? /* @__PURE__ */ jsxs18(Fragment5, { children: [
7807
+ /* @__PURE__ */ jsx19(ChevronLeftIcon, {}),
7808
+ /* @__PURE__ */ jsx19("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7809
+ /* @__PURE__ */ jsx19(DragDots, {})
7017
7810
  ] }) : null,
7018
- !pill && variant === "fill" ? /* @__PURE__ */ jsxs17(Fragment5, { children: [
7019
- /* @__PURE__ */ jsx18("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(
7811
+ !pill && variant === "fill" ? /* @__PURE__ */ jsxs18(Fragment5, { children: [
7812
+ /* @__PURE__ */ jsx19("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(
7020
7813
  TabMarkIcon,
7021
7814
  {
7022
7815
  customIconUrl,
@@ -7024,8 +7817,8 @@ function AssistEdgeTab({
7024
7817
  },
7025
7818
  markSourceKey
7026
7819
  ) }),
7027
- /* @__PURE__ */ jsx18("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7028
- /* @__PURE__ */ jsx18(ChevronLeftIcon, {})
7820
+ /* @__PURE__ */ jsx19("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7821
+ /* @__PURE__ */ jsx19(ChevronLeftIcon, {})
7029
7822
  ] }) : null
7030
7823
  ]
7031
7824
  }
@@ -7047,8 +7840,8 @@ function findPrecedingVisitorText(messages, agentMessageId) {
7047
7840
  }
7048
7841
  return void 0;
7049
7842
  }
7050
- function normalizeAgentFeedbackAnswerText(text2) {
7051
- const normalized = text2.replace(/\s+/g, " ").trim();
7843
+ function normalizeAgentFeedbackAnswerText(text3) {
7844
+ const normalized = text3.replace(/\s+/g, " ").trim();
7052
7845
  if (!normalized) return void 0;
7053
7846
  return normalized.length > 4e3 ? `${normalized.slice(0, 3997)}...` : normalized;
7054
7847
  }
@@ -7089,10 +7882,10 @@ function sendAgentAnswerFeedback(eventUrl, event) {
7089
7882
  }
7090
7883
 
7091
7884
  // src/react/components/AgentWidget/AgentWidget.tsx
7092
- import { useEffect as useEffect9, useRef as useRef7, useState as useState12 } from "react";
7885
+ import { useEffect as useEffect11, useRef as useRef9, useState as useState14 } from "react";
7093
7886
 
7094
7887
  // src/react/page-shift.ts
7095
- import { useEffect as useEffect7 } from "react";
7888
+ import { useEffect as useEffect9 } from "react";
7096
7889
  var PAGE_SHIFT_CLASS = "webless-agent-page-shift";
7097
7890
  var DEFAULT_RAIL_WIDTH_PX = 450;
7098
7891
  function shouldApplyPageShift(input) {
@@ -7144,7 +7937,7 @@ function clearPageMargin() {
7144
7937
  }
7145
7938
  function usePageShift(input) {
7146
7939
  const { active, railSlotRef } = input;
7147
- useEffect7(() => {
7940
+ useEffect9(() => {
7148
7941
  if (typeof document === "undefined") {
7149
7942
  return;
7150
7943
  }
@@ -7172,12 +7965,12 @@ function usePageShift(input) {
7172
7965
  }
7173
7966
 
7174
7967
  // src/react/hooks/useIsMobile.ts
7175
- import { useEffect as useEffect8, useState as useState11 } from "react";
7968
+ import { useEffect as useEffect10, useState as useState13 } from "react";
7176
7969
  function useIsMobile(breakpoint = 767) {
7177
- const [isMobile, setIsMobile] = useState11(
7970
+ const [isMobile, setIsMobile] = useState13(
7178
7971
  () => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
7179
7972
  );
7180
- useEffect8(() => {
7973
+ useEffect10(() => {
7181
7974
  const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
7182
7975
  const onChange = () => setIsMobile(media.matches);
7183
7976
  onChange();
@@ -7188,7 +7981,7 @@ function useIsMobile(breakpoint = 767) {
7188
7981
  }
7189
7982
 
7190
7983
  // src/react/components/AgentWidget/AgentWidget.tsx
7191
- import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
7984
+ import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
7192
7985
  function AgentWidget({
7193
7986
  indexId,
7194
7987
  customerId,
@@ -7207,9 +8000,9 @@ function AgentWidget({
7207
8000
  }) {
7208
8001
  const isMobile = useIsMobile();
7209
8002
  const placement = normalizeAgentPlacement(placementInput);
7210
- const railSlotRef = useRef7(null);
7211
- const [railCollapsed, setRailCollapsed] = useState12(defaultCollapsed);
7212
- const [railExpanded, setRailExpanded] = useState12(false);
8003
+ const railSlotRef = useRef9(null);
8004
+ const [railCollapsed, setRailCollapsed] = useState14(defaultCollapsed);
8005
+ const [railExpanded, setRailExpanded] = useState14(false);
7213
8006
  const pageShiftActive = shouldApplyPageShift({
7214
8007
  pageShift,
7215
8008
  isMobile,
@@ -7262,7 +8055,7 @@ function AgentWidget({
7262
8055
  } : {},
7263
8056
  ...branding?.colors?.border ? { border: branding.colors.border } : {}
7264
8057
  };
7265
- useEffect9(() => {
8058
+ useEffect11(() => {
7266
8059
  if (!registerPanelController) return;
7267
8060
  registerAgentPanelController(customerId, {
7268
8061
  open: () => setRailCollapsed(false),
@@ -7297,7 +8090,7 @@ function AgentWidget({
7297
8090
  })
7298
8091
  );
7299
8092
  }
7300
- useEffect9(() => {
8093
+ useEffect11(() => {
7301
8094
  if (railCollapsed) return;
7302
8095
  const handleKeyDown = (event) => {
7303
8096
  if (event.key === "Tab" && (isMobile || railExpanded)) {
@@ -7331,19 +8124,19 @@ function AgentWidget({
7331
8124
  window.addEventListener("keydown", handleKeyDown);
7332
8125
  return () => window.removeEventListener("keydown", handleKeyDown);
7333
8126
  }, [isMobile, railCollapsed, railExpanded]);
7334
- return /* @__PURE__ */ jsxs18("div", { className: "webless-agent-root", children: [
7335
- /* @__PURE__ */ jsx19(
8127
+ return /* @__PURE__ */ jsxs19("div", { className: "webless-agent-root", children: [
8128
+ /* @__PURE__ */ jsx20(
7336
8129
  "div",
7337
8130
  {
7338
8131
  className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
7339
- children: /* @__PURE__ */ jsx19(
8132
+ children: /* @__PURE__ */ jsx20(
7340
8133
  "div",
7341
8134
  {
7342
8135
  ref: railSlotRef,
7343
8136
  className: "webless-agent-root__rail-slot",
7344
8137
  inert: railCollapsed || void 0,
7345
8138
  "aria-hidden": railCollapsed,
7346
- children: /* @__PURE__ */ jsx19(
8139
+ children: /* @__PURE__ */ jsx20(
7347
8140
  AgentRail,
7348
8141
  {
7349
8142
  theme,
@@ -7380,7 +8173,7 @@ function AgentWidget({
7380
8173
  )
7381
8174
  }
7382
8175
  ),
7383
- railCollapsed ? /* @__PURE__ */ jsx19(
8176
+ railCollapsed ? /* @__PURE__ */ jsx20(
7384
8177
  AssistEdgeTab,
7385
8178
  {
7386
8179
  variant: placement.variant,
@@ -7441,4 +8234,4 @@ export {
7441
8234
  sendAgentAnswerFeedback,
7442
8235
  AgentWidget
7443
8236
  };
7444
- //# sourceMappingURL=chunk-74CSWSC5.js.map
8237
+ //# sourceMappingURL=chunk-EVWZF34F.js.map