@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.
package/dist/embed.cjs CHANGED
@@ -34,7 +34,7 @@ __export(embed_exports, {
34
34
  module.exports = __toCommonJS(embed_exports);
35
35
 
36
36
  // src/embed/mount.tsx
37
- var import_client5 = require("react-dom/client");
37
+ var import_client7 = require("react-dom/client");
38
38
 
39
39
  // src/react/panel-controller.ts
40
40
  var controllers = /* @__PURE__ */ new Map();
@@ -64,7 +64,7 @@ function submitAgentPanel(customerId, message, options) {
64
64
  }
65
65
 
66
66
  // src/react/components/AgentWidget/AgentWidget.tsx
67
- var import_react16 = require("react");
67
+ var import_react18 = require("react");
68
68
 
69
69
  // src/react/page-shift.ts
70
70
  var import_react = require("react");
@@ -146,8 +146,168 @@ function usePageShift(input) {
146
146
  }, [active, railSlotRef]);
147
147
  }
148
148
 
149
- // src/react/hooks/useAgentChat.ts
149
+ // src/react/hooks/useAgentHandoff.ts
150
+ var import_client = require("eve/client");
150
151
  var import_react2 = require("react");
152
+ function useAgentHandoff(options) {
153
+ const [page, setPage] = (0, import_react2.useState)(null);
154
+ const [busy, setBusy] = (0, import_react2.useState)(false);
155
+ const [rejected, setRejected] = (0, import_react2.useState)(false);
156
+ const [error, setError] = (0, import_react2.useState)(null);
157
+ const callbacks = (0, import_react2.useRef)(options);
158
+ callbacks.current = options;
159
+ const pending = (0, import_react2.useRef)(null);
160
+ const inFlight = (0, import_react2.useRef)(null);
161
+ const pageRef = (0, import_react2.useRef)(null);
162
+ const refreshRef = (0, import_react2.useRef)(null);
163
+ const generation = (0, import_react2.useRef)(0);
164
+ (0, import_react2.useEffect)(() => {
165
+ const token = ++generation.current;
166
+ const controller = new AbortController();
167
+ let timer;
168
+ let reading = null;
169
+ let cursor = 0;
170
+ pageRef.current = null;
171
+ pending.current = null;
172
+ inFlight.current?.abort();
173
+ inFlight.current = null;
174
+ setRejected(false);
175
+ setPage(null);
176
+ setError(null);
177
+ setBusy(false);
178
+ if (!options.enabled || !options.sessionId) return;
179
+ const active = () => !controller.signal.aborted && generation.current === token;
180
+ const refresh = () => {
181
+ reading ??= (async () => {
182
+ do {
183
+ const next = await options.client.handoff.read({
184
+ after: cursor,
185
+ signal: AbortSignal.any([
186
+ controller.signal,
187
+ AbortSignal.timeout(15e3)
188
+ ])
189
+ });
190
+ if (!active()) return;
191
+ callbacks.current.onEvents(next.events);
192
+ if (next.status !== "ai") callbacks.current.onOwnership();
193
+ cursor = next.cursor;
194
+ pageRef.current = next;
195
+ setPage(next);
196
+ setRejected(false);
197
+ if (!pending.current) setError(null);
198
+ if (!next.hasMore) break;
199
+ } while (active());
200
+ })().finally(() => {
201
+ reading = null;
202
+ });
203
+ return reading;
204
+ };
205
+ refreshRef.current = refresh;
206
+ const poll = async () => {
207
+ try {
208
+ await refresh();
209
+ } catch (cause) {
210
+ if (active()) {
211
+ const denied = cause instanceof import_client.ClientError && cause.status === 403;
212
+ setRejected(denied);
213
+ setError(
214
+ denied ? "This preview has changed. Restart the preview chat to test the current version." : "Reconnecting to your conversation\u2026"
215
+ );
216
+ }
217
+ } finally {
218
+ if (active()) timer = setTimeout(() => void poll(), 1500);
219
+ }
220
+ };
221
+ void poll();
222
+ return () => {
223
+ controller.abort();
224
+ inFlight.current?.abort();
225
+ clearTimeout(timer);
226
+ refreshRef.current = null;
227
+ };
228
+ }, [options.client, options.enabled, options.sessionId]);
229
+ async function execute(operation) {
230
+ if (inFlight.current) return;
231
+ const controller = new AbortController();
232
+ inFlight.current = controller;
233
+ const signal = AbortSignal.any([
234
+ controller.signal,
235
+ AbortSignal.timeout(15e3)
236
+ ]);
237
+ const token = generation.current;
238
+ pending.current = operation;
239
+ setBusy(true);
240
+ setError(null);
241
+ try {
242
+ if (operation.type === "start")
243
+ await options.client.handoff.start(operation.operationId, signal);
244
+ else if (operation.type === "message") {
245
+ const { type: _, ...command } = operation;
246
+ await options.client.handoff.send(command, signal);
247
+ } else {
248
+ const { type: _, ...command } = operation;
249
+ await options.client.handoff.returnToAI(command, signal);
250
+ }
251
+ if (token !== generation.current) return;
252
+ pending.current = null;
253
+ await refreshRef.current?.();
254
+ } catch {
255
+ if (token === generation.current)
256
+ setError(
257
+ "Could not confirm delivery. Retry to check the same request."
258
+ );
259
+ } finally {
260
+ if (inFlight.current === controller) inFlight.current = null;
261
+ if (token === generation.current) setBusy(false);
262
+ }
263
+ }
264
+ const binding = () => {
265
+ const current = pageRef.current;
266
+ if (!current?.handoffId) throw new Error("No active human conversation.");
267
+ return { handoffId: current.handoffId, epoch: current.epoch };
268
+ };
269
+ return {
270
+ page,
271
+ busy,
272
+ error,
273
+ hasPending: Boolean(pending.current),
274
+ canReset: !busy && !pending.current && (rejected || page?.status === "ai" || page?.status === "failed" || !options.sessionId),
275
+ blocksAI: Boolean(options.enabled && options.sessionId && !page) || busy || Boolean(pending.current) || Boolean(page && page.status !== "ai"),
276
+ start: () => execute(
277
+ pending.current ?? { type: "start", operationId: crypto.randomUUID() }
278
+ ),
279
+ send: (message) => {
280
+ if (pending.current)
281
+ throw new Error(
282
+ "Retry the pending request before sending another message."
283
+ );
284
+ return execute({
285
+ type: "message",
286
+ operationId: crypto.randomUUID(),
287
+ ...binding(),
288
+ message
289
+ });
290
+ },
291
+ returnToAI: () => execute(
292
+ pending.current ?? {
293
+ type: "return",
294
+ operationId: crypto.randomUUID(),
295
+ ...binding()
296
+ }
297
+ ),
298
+ retry: async () => {
299
+ if (pending.current) return execute(pending.current);
300
+ try {
301
+ await refreshRef.current?.();
302
+ } catch {
303
+ setError("Reconnecting to your conversation\u2026");
304
+ }
305
+ }
306
+ };
307
+ }
308
+
309
+ // src/react/hooks/useAgentChat.ts
310
+ var import_react3 = require("react");
151
311
 
152
312
  // src/runtime/tool-ui.ts
153
313
  var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
@@ -289,16 +449,16 @@ function parseStep(value) {
289
449
  if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
290
450
  return null;
291
451
  }
292
- const id = boundedString(value.id, 80);
452
+ const id2 = boundedString(value.id, 80);
293
453
  const label = boundedString(value.label, 160);
294
454
  const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
295
- 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(
455
+ 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(
296
456
  (path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
297
457
  )) {
298
458
  return null;
299
459
  }
300
460
  return {
301
- id,
461
+ id: id2,
302
462
  label,
303
463
  fieldPaths: value.fieldPaths,
304
464
  ...description ? { description } : {}
@@ -335,14 +495,14 @@ function parseAgentToolUiSurface(value) {
335
495
  ])) {
336
496
  return null;
337
497
  }
338
- const id = boundedString(value.id, 200);
498
+ const id2 = boundedString(value.id, 200);
339
499
  const title = boundedString(value.title, 200);
340
500
  const toolSlug = boundedString(value.toolSlug, 200);
341
501
  const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
342
502
  const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
343
503
  const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
344
504
  const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
345
- if (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
505
+ if (!id2 || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
346
506
  return null;
347
507
  }
348
508
  const fields = value.fields.map(parseField);
@@ -362,7 +522,7 @@ function parseAgentToolUiSurface(value) {
362
522
  }
363
523
  return {
364
524
  schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
365
- id,
525
+ id: id2,
366
526
  title,
367
527
  toolSlug,
368
528
  fields,
@@ -473,10 +633,205 @@ function completeConnectedToolWork(item, result) {
473
633
  }
474
634
 
475
635
  // src/runtime/client.ts
476
- var import_client2 = require("eve/client");
636
+ var import_client4 = require("eve/client");
637
+
638
+ // src/runtime/handoff.ts
639
+ var import_client3 = require("eve/client");
640
+
641
+ // src/runtime/generated/handoff-contract.ts
642
+ var import_zod = require("zod");
643
+ var id = import_zod.z.string().min(1).max(200);
644
+ var sequence = import_zod.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
645
+ var text = import_zod.z.string().trim().min(1).max(16e3);
646
+ var agentRuntimeHandoffVersion = "webless.ai/agent-runtime-handoff/v1";
647
+ var agentRuntimeHandoffStatusSchema = import_zod.z.enum([
648
+ "ai",
649
+ "requesting",
650
+ "queued",
651
+ "human",
652
+ "resolved",
653
+ "failed"
654
+ ]);
655
+ var agentRuntimeHandoffActorSchema = import_zod.z.strictObject({
656
+ id,
657
+ name: import_zod.z.string().trim().min(1).max(200)
658
+ });
659
+ var eventBase = {
660
+ id,
661
+ sequence: sequence.refine((value) => value > 0),
662
+ handoffId: id,
663
+ epoch: sequence.refine((value) => value > 0),
664
+ createdAt: import_zod.z.iso.datetime()
665
+ };
666
+ var agentRuntimeHandoffEventSchema = import_zod.z.discriminatedUnion("type", [
667
+ import_zod.z.strictObject({
668
+ ...eventBase,
669
+ type: import_zod.z.literal("status"),
670
+ status: agentRuntimeHandoffStatusSchema,
671
+ actor: agentRuntimeHandoffActorSchema.nullable()
672
+ }),
673
+ import_zod.z.strictObject({
674
+ ...eventBase,
675
+ type: import_zod.z.literal("visitor.message"),
676
+ message: text,
677
+ operationId: id
678
+ }),
679
+ import_zod.z.strictObject({
680
+ ...eventBase,
681
+ type: import_zod.z.literal("human.message"),
682
+ message: text,
683
+ actor: agentRuntimeHandoffActorSchema
684
+ })
685
+ ]);
686
+ var agentRuntimeHandoffReadRequestSchema = import_zod.z.strictObject({
687
+ after: sequence.default(0)
688
+ });
689
+ var agentRuntimeHandoffPageSchema = import_zod.z.strictObject({
690
+ apiVersion: import_zod.z.literal(agentRuntimeHandoffVersion),
691
+ sessionId: id,
692
+ enabled: import_zod.z.boolean(),
693
+ status: agentRuntimeHandoffStatusSchema,
694
+ handoffId: id.nullable(),
695
+ epoch: sequence,
696
+ after: sequence,
697
+ cursor: sequence,
698
+ hasMore: import_zod.z.boolean(),
699
+ events: import_zod.z.array(agentRuntimeHandoffEventSchema).max(100)
700
+ }).superRefine((page, ctx) => {
701
+ let cursor = page.after;
702
+ let previousEpoch = 0;
703
+ const statuses = /* @__PURE__ */ new Map();
704
+ const ids = /* @__PURE__ */ new Set();
705
+ const handoffs = /* @__PURE__ */ new Map();
706
+ const terminalEpochs = /* @__PURE__ */ new Set();
707
+ let currentStatus;
708
+ for (const event of page.events) {
709
+ if (event.epoch < previousEpoch)
710
+ ctx.addIssue({
711
+ code: "custom",
712
+ message: "Handoff epochs cannot decrease."
713
+ });
714
+ previousEpoch = event.epoch;
715
+ if (ids.has(event.id))
716
+ ctx.addIssue({
717
+ code: "custom",
718
+ message: "Duplicate handoff event ID."
719
+ });
720
+ ids.add(event.id);
721
+ const handoffId = handoffs.get(event.epoch);
722
+ if (handoffId !== void 0 && handoffId !== event.handoffId || event.epoch === page.epoch && event.handoffId !== page.handoffId)
723
+ ctx.addIssue({
724
+ code: "custom",
725
+ message: "Inconsistent handoff epoch identity."
726
+ });
727
+ handoffs.set(event.epoch, event.handoffId);
728
+ if (terminalEpochs.has(event.epoch) && (event.type !== "status" || event.status !== "ai"))
729
+ ctx.addIssue({
730
+ code: "custom",
731
+ message: "Handoff event follows resolution."
732
+ });
733
+ if (event.type === "status") {
734
+ if (event.status === "human" && event.actor === null)
735
+ ctx.addIssue({
736
+ code: "custom",
737
+ message: "Human ownership requires a representative."
738
+ });
739
+ const previous = statuses.get(event.epoch);
740
+ const transitions = {
741
+ ai: [],
742
+ requesting: ["queued", "resolved", "failed"],
743
+ queued: ["human", "resolved", "failed"],
744
+ human: ["resolved", "failed"],
745
+ resolved: ["ai"],
746
+ failed: []
747
+ };
748
+ if (previous !== void 0 && !transitions[previous].includes(event.status))
749
+ ctx.addIssue({
750
+ code: "custom",
751
+ message: "Invalid handoff ownership transition."
752
+ });
753
+ statuses.set(event.epoch, event.status);
754
+ if (["resolved", "failed", "ai"].includes(event.status))
755
+ terminalEpochs.add(event.epoch);
756
+ if (event.epoch === page.epoch) currentStatus = event.status;
757
+ }
758
+ if (event.sequence !== cursor + 1) {
759
+ ctx.addIssue({
760
+ code: "custom",
761
+ message: "Handoff event gap or replay."
762
+ });
763
+ }
764
+ cursor = event.sequence;
765
+ if (event.epoch > page.epoch) {
766
+ ctx.addIssue({
767
+ code: "custom",
768
+ message: "Event exceeds current epoch."
769
+ });
770
+ }
771
+ }
772
+ if (!page.hasMore && currentStatus !== void 0 && currentStatus !== page.status)
773
+ ctx.addIssue({
774
+ code: "custom",
775
+ message: "Handoff ownership contradicts its final status event."
776
+ });
777
+ if (page.cursor !== cursor || page.hasMore && page.events.length === 0) {
778
+ ctx.addIssue({ code: "custom", message: "Invalid handoff page cursor." });
779
+ }
780
+ if (page.epoch === 0 !== (page.handoffId === null)) {
781
+ ctx.addIssue({ code: "custom", message: "Invalid handoff identity." });
782
+ }
783
+ if (page.handoffId === null && page.status !== "ai") {
784
+ ctx.addIssue({
785
+ code: "custom",
786
+ message: "Handoff identity is required."
787
+ });
788
+ }
789
+ });
790
+ var agentRuntimeHandoffStartSchema = import_zod.z.strictObject({
791
+ operationId: id
792
+ });
793
+ var agentRuntimeHandoffCommandSchema = import_zod.z.strictObject({
794
+ operationId: id,
795
+ handoffId: id,
796
+ epoch: sequence.refine((value) => value > 0)
797
+ });
798
+ var agentRuntimeHandoffMessageSchema = agentRuntimeHandoffCommandSchema.extend({ message: text });
799
+ var agentRuntimeHandoffBindingSchema = import_zod.z.strictObject({
800
+ tenantId: id,
801
+ indexId: id,
802
+ visitorSubject: id,
803
+ sessionId: id,
804
+ handoffId: id,
805
+ epoch: sequence.refine((value) => value > 0)
806
+ });
807
+ var providerEventBase = {
808
+ apiVersion: import_zod.z.literal(agentRuntimeHandoffVersion),
809
+ binding: agentRuntimeHandoffBindingSchema,
810
+ eventId: id,
811
+ sequence: sequence.refine((value) => value > 0)
812
+ };
813
+ var agentRuntimeHandoffProviderEventSchema = import_zod.z.discriminatedUnion(
814
+ "type",
815
+ [
816
+ import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("queued") }),
817
+ import_zod.z.strictObject({
818
+ ...providerEventBase,
819
+ type: import_zod.z.literal("assigned"),
820
+ actor: agentRuntimeHandoffActorSchema
821
+ }),
822
+ import_zod.z.strictObject({
823
+ ...providerEventBase,
824
+ type: import_zod.z.literal("human.message"),
825
+ actor: agentRuntimeHandoffActorSchema,
826
+ message: text
827
+ }),
828
+ import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("resolved") }),
829
+ import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("failed") })
830
+ ]
831
+ );
477
832
 
478
833
  // src/runtime/capability.ts
479
- var import_client = require("eve/client");
834
+ var import_client2 = require("eve/client");
480
835
  var MAX_REFRESH_SKEW_MS = 3e4;
481
836
  var LOCAL_LOOPBACK_ORIGINS = [
482
837
  "http://127.0.0.1:3010",
@@ -544,18 +899,26 @@ function createAgentRuntimeCapability(options) {
544
899
  );
545
900
  }
546
901
  }
547
- const bootstrapBody = JSON.stringify({
902
+ const bootstrapBody = {
548
903
  clientSessionId: options.visitorSessionId,
549
904
  indexId: options.indexId,
550
905
  ...previewBuildId ? { previewBuildId } : {},
551
906
  ...previewGrant ? { previewGrant } : {},
552
907
  version: options.version
553
- });
554
- const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
555
- body: bootstrapBody,
556
- headers: { "content-type": "application/json" },
557
- method: "POST"
558
- });
908
+ };
909
+ const postBootstrap = async (origin) => {
910
+ const send = (advertiseLocation) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
911
+ body: JSON.stringify({ ...bootstrapBody, ...advertiseLocation ? { locationConsent: "v1" } : {} }),
912
+ headers: { "content-type": "application/json" },
913
+ method: "POST"
914
+ });
915
+ const response2 = await send(Boolean(options.locationConsent));
916
+ if (options.locationConsent && response2.status === 400) {
917
+ const error = await response2.clone().json().catch(() => null);
918
+ if (isRecord3(error) && error.code === "invalid_request") return send(false);
919
+ }
920
+ return response2;
921
+ };
559
922
  let response;
560
923
  let lastError;
561
924
  for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
@@ -613,7 +976,7 @@ async function withCapabilityRefresh(capability, request) {
613
976
  try {
614
977
  return await request();
615
978
  } catch (error) {
616
- if (!(error instanceof import_client.ClientError) || error.status !== 401) {
979
+ if (!(error instanceof import_client2.ClientError) || error.status !== 401) {
617
980
  throw error;
618
981
  }
619
982
  capability.invalidate();
@@ -621,6 +984,75 @@ async function withCapabilityRefresh(capability, request) {
621
984
  }
622
985
  }
623
986
 
987
+ // src/runtime/handoff.ts
988
+ function createHandoffClient(options) {
989
+ const target = () => {
990
+ const sessionId = options.getSessionId();
991
+ if (!sessionId)
992
+ throw new Error(
993
+ "Start a conversation before requesting a representative."
994
+ );
995
+ return {
996
+ sessionId,
997
+ path: `/webless/v1/session/${encodeURIComponent(sessionId)}/handoff`
998
+ };
999
+ };
1000
+ const request = (path, init) => withCapabilityRefresh(options.capability, async () => {
1001
+ const response = await options.getClient().fetch(path, {
1002
+ ...init,
1003
+ cache: "no-store",
1004
+ redirect: "error"
1005
+ });
1006
+ if (!response.ok) {
1007
+ throw new import_client3.ClientError(
1008
+ response.status,
1009
+ await response.text(),
1010
+ response.headers
1011
+ );
1012
+ }
1013
+ return response;
1014
+ });
1015
+ const command = async (suffix, body, signal) => {
1016
+ const { path } = target();
1017
+ await request(path + suffix, {
1018
+ method: "POST",
1019
+ headers: { "content-type": "application/json" },
1020
+ body: JSON.stringify(body),
1021
+ signal
1022
+ });
1023
+ };
1024
+ return {
1025
+ async read(input = {}) {
1026
+ const { after } = agentRuntimeHandoffReadRequestSchema.parse({
1027
+ after: input.after
1028
+ });
1029
+ const { sessionId, path } = target();
1030
+ const response = await request(`${path}?after=${after}`, {
1031
+ signal: input.signal
1032
+ });
1033
+ const value = await response.json();
1034
+ const page = agentRuntimeHandoffPageSchema.parse(value);
1035
+ if (page.sessionId !== sessionId || page.after !== after) {
1036
+ throw new Error(
1037
+ "The handoff response does not belong to this conversation or cursor."
1038
+ );
1039
+ }
1040
+ return page;
1041
+ },
1042
+ start: (operationId, signal) => command(
1043
+ "",
1044
+ agentRuntimeHandoffStartSchema.parse({ operationId }),
1045
+ signal
1046
+ ),
1047
+ send: (input, signal) => command(
1048
+ "/messages",
1049
+ agentRuntimeHandoffMessageSchema.parse(input),
1050
+ signal
1051
+ ),
1052
+ returnToAI: (input, signal) => command("/return", agentRuntimeHandoffCommandSchema.parse(input), signal)
1053
+ };
1054
+ }
1055
+
624
1056
  // src/runtime/config.ts
625
1057
  var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
626
1058
  var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
@@ -1090,6 +1522,7 @@ function specialistNameFromInput(input) {
1090
1522
  return match?.[1]?.trim() || void 0;
1091
1523
  }
1092
1524
  function requestedWorkItem(action) {
1525
+ if (action.kind === "tool-call" && action.toolName === "request_location") return null;
1093
1526
  if (action.kind === "tool-call" && action.toolName === "search_discovery") {
1094
1527
  return {
1095
1528
  id: action.callId,
@@ -1195,13 +1628,14 @@ function applyWorkEvent(event, handlers, workItems) {
1195
1628
  );
1196
1629
  }
1197
1630
  var AgentSession = class {
1198
- constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, previewBuildId, getUnpublishedPreviewGrant) {
1631
+ constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, previewBuildId, getUnpublishedPreviewGrant, locationConsent) {
1199
1632
  this.indexId = indexId;
1200
1633
  this.version = version;
1201
1634
  this.runtimeOrigin = runtimeOrigin;
1202
1635
  this.visitorSessionId = visitorSessionId;
1203
1636
  this.storeOptions = storeOptions;
1204
1637
  this.capability = createAgentRuntimeCapability({
1638
+ locationConsent,
1205
1639
  getUnpublishedPreviewGrant,
1206
1640
  indexId,
1207
1641
  previewBuildId,
@@ -1209,6 +1643,11 @@ var AgentSession = class {
1209
1643
  version,
1210
1644
  visitorSessionId
1211
1645
  });
1646
+ this.handoff = createHandoffClient({
1647
+ getClient: () => this.ensureClient(),
1648
+ getSessionId: () => this.getActiveSessionId(),
1649
+ capability: this.capability
1650
+ });
1212
1651
  }
1213
1652
  indexId;
1214
1653
  version;
@@ -1221,6 +1660,7 @@ var AgentSession = class {
1221
1660
  activeResponse;
1222
1661
  childStreams;
1223
1662
  capability;
1663
+ handoff;
1224
1664
  getActiveSessionId() {
1225
1665
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
1226
1666
  }
@@ -1295,7 +1735,7 @@ var AgentSession = class {
1295
1735
  }
1296
1736
  this.activeResponse = void 0;
1297
1737
  this.session = void 0;
1298
- this.client = new import_client2.Client({
1738
+ this.client = new import_client4.Client({
1299
1739
  auth: { bearer: () => this.capability.getAccessToken() },
1300
1740
  host: config.host,
1301
1741
  redirect: "error"
@@ -1331,7 +1771,7 @@ var AgentSession = class {
1331
1771
  () => activeSession.send(message, { signal })
1332
1772
  );
1333
1773
  } catch (error) {
1334
- if (error instanceof import_client2.ClientError && error.status === 409 && error.code === "session_not_active") {
1774
+ if (error instanceof import_client4.ClientError && error.status === 409 && error.code === "session_not_active") {
1335
1775
  clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
1336
1776
  this.session = void 0;
1337
1777
  session = void 0;
@@ -1505,10 +1945,10 @@ var AgentSession = class {
1505
1945
  }
1506
1946
  this.session = session;
1507
1947
  const inputResponses = responses.map(
1508
- ({ requestId, optionId, text: text2 }) => ({
1948
+ ({ requestId, optionId, text: text3 }) => ({
1509
1949
  requestId,
1510
1950
  ...optionId ? { optionId } : {},
1511
- ...text2 ? { text: text2 } : {}
1951
+ ...text3 ? { text: text3 } : {}
1512
1952
  })
1513
1953
  );
1514
1954
  const response = await withCapabilityRefresh(
@@ -1593,10 +2033,12 @@ function createAgentClient(options) {
1593
2033
  visitorSessionId,
1594
2034
  storeOptions,
1595
2035
  options.previewBuildId,
1596
- options.getUnpublishedPreviewGrant
2036
+ options.getUnpublishedPreviewGrant,
2037
+ options.locationConsent
1597
2038
  );
1598
2039
  return {
1599
2040
  indexId,
2041
+ handoff: session.handoff,
1600
2042
  version,
1601
2043
  runtimeOrigin,
1602
2044
  visitorSessionId,
@@ -1628,7 +2070,7 @@ function createAgentClient(options) {
1628
2070
  }
1629
2071
 
1630
2072
  // src/runtime/errors.ts
1631
- var import_client3 = require("eve/client");
2073
+ var import_client5 = require("eve/client");
1632
2074
  var TRANSIENT_AGENT_ERROR_MESSAGE = "The agent run stopped before the action finished. Please try again.";
1633
2075
  var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
1634
2076
  function isTransientRuntimeMessage(message) {
@@ -1640,7 +2082,7 @@ function isPreviewAuthorizationMessage(message) {
1640
2082
  return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
1641
2083
  }
1642
2084
  function formatAgentError(error) {
1643
- if (error instanceof import_client3.ClientError) {
2085
+ if (error instanceof import_client5.ClientError) {
1644
2086
  if (error.status === 401 && error.code === "index_required") {
1645
2087
  return "Missing indexId \u2014 pass a published index id to createAgentClient().";
1646
2088
  }
@@ -1674,14 +2116,14 @@ function formatAgentError(error) {
1674
2116
  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.";
1675
2117
  function visitorMessageDisplayText(message) {
1676
2118
  if (message.role !== "visitor") return message.text;
1677
- const text2 = message.text.trim();
1678
- if (!text2) return message.text;
1679
- if (text2.startsWith(COMPOSER_FORM_SKIP_DISMISSAL)) {
1680
- const afterDismissal = text2.slice(COMPOSER_FORM_SKIP_DISMISSAL.length).trim().replace(/^\n+/, "").trim();
2119
+ const text3 = message.text.trim();
2120
+ if (!text3) return message.text;
2121
+ if (text3.startsWith(COMPOSER_FORM_SKIP_DISMISSAL)) {
2122
+ const afterDismissal = text3.slice(COMPOSER_FORM_SKIP_DISMISSAL.length).trim().replace(/^\n+/, "").trim();
1681
2123
  if (afterDismissal) return afterDismissal;
1682
2124
  }
1683
2125
  const runtime = message.runtimeText?.trim();
1684
- if (runtime && text2 === runtime && runtime.includes(COMPOSER_FORM_SKIP_DISMISSAL)) {
2126
+ if (runtime && text3 === runtime && runtime.includes(COMPOSER_FORM_SKIP_DISMISSAL)) {
1685
2127
  const afterDismissal = runtime.slice(
1686
2128
  runtime.indexOf(COMPOSER_FORM_SKIP_DISMISSAL) + COMPOSER_FORM_SKIP_DISMISSAL.length
1687
2129
  ).trim().replace(/^\n+/, "").trim();
@@ -1755,13 +2197,13 @@ function parseVisitorFormFields(value) {
1755
2197
  const seen = /* @__PURE__ */ new Set();
1756
2198
  for (const item of value) {
1757
2199
  const record2 = asRecord(item);
1758
- const id = asString(record2?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
2200
+ const id2 = asString(record2?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
1759
2201
  const kind = asString(record2?.kind);
1760
- if (!record2 || !id || seen.has(id) || !isFieldKind2(kind)) continue;
1761
- seen.add(id);
1762
- const label = asString(record2.label) || id;
2202
+ if (!record2 || !id2 || seen.has(id2) || !isFieldKind2(kind)) continue;
2203
+ seen.add(id2);
2204
+ const label = asString(record2.label) || id2;
1763
2205
  fields.push({
1764
- id,
2206
+ id: id2,
1765
2207
  kind,
1766
2208
  label,
1767
2209
  placeholder: asString(record2.placeholder) || label,
@@ -1797,52 +2239,52 @@ function formatComposerFormMessage(form, values) {
1797
2239
  return value ? `${field.label}: ${value}` : "";
1798
2240
  }).filter(Boolean).join("\n");
1799
2241
  }
1800
- function looksLikeFieldCollection(text2) {
2242
+ function looksLikeFieldCollection(text3) {
1801
2243
  return /\b(please|need|send|share|enter|provide|add|collect|what|which|use)\b/i.test(
1802
- text2
1803
- ) || /:\s*$/m.test(text2) || /^[-*•]\s+/m.test(text2);
2244
+ text3
2245
+ ) || /:\s*$/m.test(text3) || /^[-*•]\s+/m.test(text3);
1804
2246
  }
1805
- function looksLikeBookingCopy(text2) {
2247
+ function looksLikeBookingCopy(text3) {
1806
2248
  return /book(ing)? (card|a demo|this time)|pick a (date|time)|available (times|slots)|calendly/i.test(
1807
- text2
2249
+ text3
1808
2250
  );
1809
2251
  }
1810
- function echoedLabeledFieldIds(text2) {
2252
+ function echoedLabeledFieldIds(text3) {
1811
2253
  const ids = /* @__PURE__ */ new Set();
1812
- if (/\bname\s*:\s+\S+/i.test(text2)) ids.add("name");
1813
- if (/\be-?mail\s*:\s+\S+/i.test(text2)) ids.add("email");
1814
- if (/\bphone\s*:\s+\S+/i.test(text2)) ids.add("phone");
1815
- if (/\bcompany\s*:\s+\S+/i.test(text2)) ids.add("company");
2254
+ if (/\bname\s*:\s+\S+/i.test(text3)) ids.add("name");
2255
+ if (/\be-?mail\s*:\s+\S+/i.test(text3)) ids.add("email");
2256
+ if (/\bphone\s*:\s+\S+/i.test(text3)) ids.add("phone");
2257
+ if (/\bcompany\s*:\s+\S+/i.test(text3)) ids.add("company");
1816
2258
  return ids;
1817
2259
  }
1818
- function matchLibraryFields(text2) {
2260
+ function matchLibraryFields(text3) {
1819
2261
  return FIELD_LIBRARY.flatMap((field) => {
1820
- if (field.exclude?.test(text2)) {
1821
- const leftover = text2.replace(field.exclude, " ");
2262
+ if (field.exclude?.test(text3)) {
2263
+ const leftover = text3.replace(field.exclude, " ");
1822
2264
  if (!field.patterns.some((pattern) => pattern.test(leftover))) return [];
1823
- } else if (!field.patterns.some((pattern) => pattern.test(text2))) {
2265
+ } else if (!field.patterns.some((pattern) => pattern.test(text3))) {
1824
2266
  return [];
1825
2267
  }
1826
2268
  const { patterns: _patterns, exclude: _exclude, ...next } = field;
1827
2269
  return [next];
1828
2270
  }).slice(0, MAX_FORM_FIELDS);
1829
2271
  }
1830
- function looksLikeCompletedActionRecap(text2) {
1831
- const confirmingCreate = /\bshould i create this\b/i.test(text2);
1832
- const echoingFilledFields = /\b(?:name|email|phone|company)\s*:\s+\S+/i.test(text2) && /[^\s@]+@[^\s@]+\.[^\s@]+/i.test(text2);
2272
+ function looksLikeCompletedActionRecap(text3) {
2273
+ const confirmingCreate = /\bshould i create this\b/i.test(text3);
2274
+ const echoingFilledFields = /\b(?:name|email|phone|company)\s*:\s+\S+/i.test(text3) && /[^\s@]+@[^\s@]+\.[^\s@]+/i.test(text3);
1833
2275
  if (echoingFilledFields) {
1834
- if (looksLikeFieldCollection(text2)) {
1835
- const echoed = echoedLabeledFieldIds(text2);
1836
- if (matchLibraryFields(text2).some((field) => !echoed.has(field.id))) {
2276
+ if (looksLikeFieldCollection(text3)) {
2277
+ const echoed = echoedLabeledFieldIds(text3);
2278
+ if (matchLibraryFields(text3).some((field) => !echoed.has(field.id))) {
1837
2279
  return false;
1838
2280
  }
1839
2281
  }
1840
2282
  return true;
1841
2283
  }
1842
- return confirmingCreate && !looksLikeFieldCollection(text2);
2284
+ return confirmingCreate && !looksLikeFieldCollection(text3);
1843
2285
  }
1844
- function inferComposerForm(text2) {
1845
- const cleaned = text2.trim();
2286
+ function inferComposerForm(text3) {
2287
+ const cleaned = text3.trim();
1846
2288
  if (!cleaned || looksLikeBookingCopy(cleaned) || looksLikeCompletedActionRecap(cleaned) || !looksLikeFieldCollection(cleaned)) {
1847
2289
  return null;
1848
2290
  }
@@ -1857,8 +2299,8 @@ function resolveComposerForm(input) {
1857
2299
  if (input.enabled === false || input.hasBookingOffer || input.hasPendingConfirmation) {
1858
2300
  return null;
1859
2301
  }
1860
- const text2 = input.agentText.trim();
1861
- if (!text2) return null;
2302
+ const text3 = input.agentText.trim();
2303
+ if (!text3) return null;
1862
2304
  const card = input.cards?.find((item) => item.type === "visitor_form");
1863
2305
  if (card?.fields && card.fields.length >= MIN_FORM_FIELDS) {
1864
2306
  return {
@@ -1866,7 +2308,7 @@ function resolveComposerForm(input) {
1866
2308
  fields: card.fields.slice(0, MAX_FORM_FIELDS)
1867
2309
  };
1868
2310
  }
1869
- return inferComposerForm(text2);
2311
+ return inferComposerForm(text3);
1870
2312
  }
1871
2313
 
1872
2314
  // src/react/lib/tool-card.ts
@@ -1880,9 +2322,9 @@ function preferBookingOffer(current, next) {
1880
2322
  }
1881
2323
  return next;
1882
2324
  }
1883
- function looksLikeBookingReady(text2) {
2325
+ function looksLikeBookingReady(text3) {
1884
2326
  return /demo option|options are ready|pick a (date|time)|available (times|slots)|book(ing)? (card|the demo)|\bschedule\b/i.test(
1885
- text2
2327
+ text3
1886
2328
  );
1887
2329
  }
1888
2330
  function bookingOfferIdentityKey(offer) {
@@ -1992,29 +2434,29 @@ function bookingCardFromActionOutput(output) {
1992
2434
  const data = asRecord2(record2?.output) ?? asRecord2(record2?.data) ?? record2;
1993
2435
  return parseToolCard(data);
1994
2436
  }
1995
- function ensureBookingOfferText(text2, offer) {
1996
- if (!offer) return text2;
1997
- if (extractToolCards(text2).some((card) => card.type === "booking_offer")) {
1998
- return text2;
2437
+ function ensureBookingOfferText(text3, offer) {
2438
+ if (!offer) return text3;
2439
+ if (extractToolCards(text3).some((card) => card.type === "booking_offer")) {
2440
+ return text3;
1999
2441
  }
2000
- const visible = stripToolCards(text2).trim() || text2.trim();
2442
+ const visible = stripToolCards(text3).trim() || text3.trim();
2001
2443
  return `${visible}
2002
2444
 
2003
2445
  ${formatBookingOfferFence(offer)}`;
2004
2446
  }
2005
- function hideToolCardFences(text2) {
2006
- 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();
2447
+ function hideToolCardFences(text3) {
2448
+ 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();
2007
2449
  }
2008
2450
  var BOOKING_CARD_FALLBACK = "Pick a date and time that works for you.";
2009
- function looksLikeBookingAvailabilityDump(text2) {
2010
- const cleaned = text2.trim();
2451
+ function looksLikeBookingAvailabilityDump(text3) {
2452
+ const cleaned = text3.trim();
2011
2453
  if (!cleaned) return false;
2012
2454
  const isoCount = (cleaned.match(/\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/g) ?? []).length;
2013
2455
  const utcCount = (cleaned.match(/\bUTC\b/g) ?? []).length;
2014
2456
  return isoCount >= 2 || utcCount >= 2 || /api\.calendly\.com|calendly\.com\//i.test(cleaned) || /webless-tool-card/i.test(cleaned);
2015
2457
  }
2016
- function sanitizeBookingOfferCopy(text2) {
2017
- const cleaned = hideToolCardFences(text2);
2458
+ function sanitizeBookingOfferCopy(text3) {
2459
+ const cleaned = hideToolCardFences(text3);
2018
2460
  if (!cleaned || looksLikeBookingAvailabilityDump(cleaned)) {
2019
2461
  return BOOKING_CARD_FALLBACK;
2020
2462
  }
@@ -2027,9 +2469,9 @@ function visitorTimeZone() {
2027
2469
  return "UTC";
2028
2470
  }
2029
2471
  }
2030
- function extractToolCards(text2) {
2472
+ function extractToolCards(text3) {
2031
2473
  const cards = [];
2032
- for (const match of text2.matchAll(FENCE_PATTERN)) {
2474
+ for (const match of text3.matchAll(FENCE_PATTERN)) {
2033
2475
  try {
2034
2476
  const card = parseToolCard(JSON.parse(match[1] ?? ""));
2035
2477
  if (card) cards.push(card);
@@ -2038,8 +2480,8 @@ function extractToolCards(text2) {
2038
2480
  }
2039
2481
  return cards;
2040
2482
  }
2041
- function stripToolCards(text2) {
2042
- return text2.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
2483
+ function stripToolCards(text3) {
2484
+ return text3.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
2043
2485
  }
2044
2486
  function localDateKey(date) {
2045
2487
  if (Number.isNaN(date.getTime())) return "";
@@ -2152,7 +2594,7 @@ function visitorBookingPrefix(booking) {
2152
2594
  function record(value) {
2153
2595
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
2154
2596
  }
2155
- function text(value, max = 500) {
2597
+ function text2(value, max = 500) {
2156
2598
  return typeof value === "string" && value.trim().length > 0 && value.trim().length <= max;
2157
2599
  }
2158
2600
  function safeSearchUrl(value) {
@@ -2173,7 +2615,7 @@ function parseAgentSearchReferences(value) {
2173
2615
  const source = record(value2);
2174
2616
  if (!source || Object.keys(source).some(
2175
2617
  (key) => !["id", "title", "url"].includes(key)
2176
- ) || !text(source.id, Infinity) || !text(source.title) || source.url !== void 0 && typeof source.url !== "string")
2618
+ ) || !text2(source.id, Infinity) || !text2(source.title) || source.url !== void 0 && typeof source.url !== "string")
2177
2619
  return null;
2178
2620
  const url = safeSearchUrl(source.url);
2179
2621
  sources.push({
@@ -2185,7 +2627,7 @@ function parseAgentSearchReferences(value) {
2185
2627
  let cta;
2186
2628
  if (data.cta !== void 0) {
2187
2629
  const action = record(data.cta);
2188
- if (!action || Object.keys(action).some((key) => !["label", "url"].includes(key)) || !text(action.label) || action.url !== void 0 && typeof action.url !== "string")
2630
+ if (!action || Object.keys(action).some((key) => !["label", "url"].includes(key)) || !text2(action.label) || action.url !== void 0 && typeof action.url !== "string")
2189
2631
  return null;
2190
2632
  const url = safeSearchUrl(action.url);
2191
2633
  cta = { label: action.label.trim(), ...url ? { url } : {} };
@@ -2204,7 +2646,7 @@ function parseAgentSearchDiscoveryOutput(value) {
2204
2646
  const data = record(decoded);
2205
2647
  if (!data || Object.keys(data).some(
2206
2648
  (key) => !["answer", "sources", "cta", "suggestions"].includes(key)
2207
- ) || !text(data.answer, 5e4) || !Array.isArray(data.suggestions) || data.suggestions.length > 8 || !data.suggestions.every((item) => text(item)))
2649
+ ) || !text2(data.answer, 5e4) || !Array.isArray(data.suggestions) || data.suggestions.length > 8 || !data.suggestions.every((item) => text2(item)))
2208
2650
  return null;
2209
2651
  const references = parseAgentSearchReferences(data);
2210
2652
  return references ? {
@@ -2222,9 +2664,21 @@ function conversationKey(storageKeyPrefix, visitorSessionId) {
2222
2664
  function parseMessage(value) {
2223
2665
  if (typeof value !== "object" || value === null) return null;
2224
2666
  const record2 = value;
2225
- if (typeof record2.id !== "string" || record2.role !== "agent" && record2.role !== "visitor" || typeof record2.text !== "string" || typeof record2.createdAt !== "number" || !Number.isFinite(record2.createdAt)) {
2667
+ 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)) {
2226
2668
  return null;
2227
2669
  }
2670
+ if (record2.role === "human") {
2671
+ const person = record2.representative;
2672
+ if (typeof person !== "object" || person === null || !("id" in person) || !("name" in person) || typeof person.id !== "string" || typeof person.name !== "string")
2673
+ return null;
2674
+ return {
2675
+ id: record2.id,
2676
+ role: "human",
2677
+ text: record2.text,
2678
+ createdAt: record2.createdAt,
2679
+ representative: { id: person.id, name: person.name }
2680
+ };
2681
+ }
2228
2682
  if (record2.role === "visitor") {
2229
2683
  return {
2230
2684
  id: record2.id,
@@ -2519,10 +2973,10 @@ function safeText(value) {
2519
2973
  return value.trim().slice(0, MAX_TEXT_LENGTH);
2520
2974
  }
2521
2975
  function safeHref(value) {
2522
- const text2 = safeText(value);
2523
- if (!text2) return "";
2976
+ const text3 = safeText(value);
2977
+ if (!text3) return "";
2524
2978
  try {
2525
- const url = new URL(text2);
2979
+ const url = new URL(text3);
2526
2980
  return url.protocol === "https:" ? url.toString() : "";
2527
2981
  } catch {
2528
2982
  return "";
@@ -2742,6 +3196,9 @@ function finalizeSummaryPresentation(result, proposed) {
2742
3196
  };
2743
3197
  }
2744
3198
  function presentVisitorToolResult(result, registry = []) {
3199
+ if (result.toolName === "request_location") {
3200
+ return { id: result.callId, toolName: result.toolName, status: result.status, kind: "hidden" };
3201
+ }
2745
3202
  if (result.status === "completed" && toolResultFailed(result)) {
2746
3203
  result = { ...result, status: "failed" };
2747
3204
  }
@@ -2882,15 +3339,15 @@ function appendChatCollectiblePrompts(messages, requests) {
2882
3339
  }
2883
3340
  return next;
2884
3341
  }
2885
- function chatInputResponseForText(requests, text2) {
2886
- const trimmed = text2.trim();
3342
+ function chatInputResponseForText(requests, text3) {
3343
+ const trimmed = text3.trim();
2887
3344
  if (!trimmed) return null;
2888
3345
  const pending = requests.find(isChatCollectibleInputRequest);
2889
3346
  if (!pending) return null;
2890
3347
  return { requestId: pending.requestId, text: trimmed };
2891
3348
  }
2892
- function normalizeAssistantDedupeKey(text2) {
2893
- return text2.trim().replace(/\s+/g, " ").toLowerCase();
3349
+ function normalizeAssistantDedupeKey(text3) {
3350
+ return text3.trim().replace(/\s+/g, " ").toLowerCase();
2894
3351
  }
2895
3352
  function isNearDuplicateAssistantText(left, right) {
2896
3353
  const a = normalizeAssistantDedupeKey(left);
@@ -3003,6 +3460,7 @@ function isJsonRecord(value) {
3003
3460
  return value !== null && typeof value === "object" && !Array.isArray(value);
3004
3461
  }
3005
3462
  function useAgentChat({
3463
+ handoffEnabled = false,
3006
3464
  customerId,
3007
3465
  getUnpublishedPreviewGrant,
3008
3466
  indexId,
@@ -3014,13 +3472,13 @@ function useAgentChat({
3014
3472
  greeting,
3015
3473
  toolResultRegistry
3016
3474
  }) {
3017
- const initialState = (0, import_react2.useMemo)(
3475
+ const initialState = (0, import_react3.useMemo)(
3018
3476
  () => createInitialState(greeting?.trim() || DEFAULT_GREETING),
3019
3477
  [greeting]
3020
3478
  );
3021
- const previewGrantProviderRef = (0, import_react2.useRef)(getUnpublishedPreviewGrant);
3479
+ const previewGrantProviderRef = (0, import_react3.useRef)(getUnpublishedPreviewGrant);
3022
3480
  previewGrantProviderRef.current = getUnpublishedPreviewGrant;
3023
- const toolResultRegistryRef = (0, import_react2.useRef)(toolResultRegistry);
3481
+ const toolResultRegistryRef = (0, import_react3.useRef)(toolResultRegistry);
3024
3482
  toolResultRegistryRef.current = toolResultRegistry;
3025
3483
  const resolveUnpublishedPreviewGrant = () => {
3026
3484
  const provider = previewGrantProviderRef.current;
@@ -3033,7 +3491,7 @@ function useAgentChat({
3033
3491
  }
3034
3492
  return provider();
3035
3493
  };
3036
- const resolvedStorageKeyPrefix = (0, import_react2.useMemo)(
3494
+ const resolvedStorageKeyPrefix = (0, import_react3.useMemo)(
3037
3495
  () => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({
3038
3496
  customerId,
3039
3497
  indexId,
@@ -3042,24 +3500,25 @@ function useAgentChat({
3042
3500
  }),
3043
3501
  [customerId, indexId, runtimeOrigin, storageKeyPrefix, version]
3044
3502
  );
3045
- const visitorId = (0, import_react2.useMemo)(
3503
+ const visitorId = (0, import_react3.useMemo)(
3046
3504
  () => visitorSessionId?.trim() || getOrCreateVisitorSessionId({
3047
3505
  storageKeyPrefix: resolvedStorageKeyPrefix
3048
3506
  }),
3049
3507
  [resolvedStorageKeyPrefix, visitorSessionId]
3050
3508
  );
3051
- const [state, setState] = (0, import_react2.useState)(
3509
+ const [state, setState] = (0, import_react3.useState)(
3052
3510
  () => stateFromConversation(
3053
3511
  loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
3054
3512
  initialState
3055
3513
  )
3056
3514
  );
3057
- const pendingBookingRef = (0, import_react2.useRef)(
3515
+ const pendingBookingRef = (0, import_react3.useRef)(
3058
3516
  loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
3059
3517
  );
3060
- const runRef = (0, import_react2.useRef)(null);
3061
- const clientRef = (0, import_react2.useRef)(
3518
+ const runRef = (0, import_react3.useRef)(null);
3519
+ const clientRef = (0, import_react3.useRef)(
3062
3520
  createAgentClient({
3521
+ locationConsent: true,
3063
3522
  customerId,
3064
3523
  getUnpublishedPreviewGrant: resolveUnpublishedPreviewGrant,
3065
3524
  indexId,
@@ -3071,8 +3530,8 @@ function useAgentChat({
3071
3530
  })
3072
3531
  );
3073
3532
  const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${previewBuildId ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
3074
- const identityRef = (0, import_react2.useRef)(identityKey);
3075
- (0, import_react2.useEffect)(() => {
3533
+ const identityRef = (0, import_react3.useRef)(identityKey);
3534
+ (0, import_react3.useEffect)(() => {
3076
3535
  if (identityRef.current === identityKey) {
3077
3536
  return;
3078
3537
  }
@@ -3080,6 +3539,7 @@ function useAgentChat({
3080
3539
  runRef.current?.abort();
3081
3540
  runRef.current = null;
3082
3541
  clientRef.current = createAgentClient({
3542
+ locationConsent: true,
3083
3543
  customerId,
3084
3544
  getUnpublishedPreviewGrant: resolveUnpublishedPreviewGrant,
3085
3545
  indexId,
@@ -3110,7 +3570,7 @@ function useAgentChat({
3110
3570
  version,
3111
3571
  visitorId
3112
3572
  ]);
3113
- (0, import_react2.useEffect)(() => {
3573
+ (0, import_react3.useEffect)(() => {
3114
3574
  if (!hasVisitorMessages(state.messages)) return;
3115
3575
  savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
3116
3576
  messages: state.messages,
@@ -3130,16 +3590,77 @@ function useAgentChat({
3130
3590
  state.toolResults,
3131
3591
  visitorId
3132
3592
  ]);
3133
- const reset = (0, import_react2.useCallback)(() => {
3593
+ const handoff = useAgentHandoff({
3594
+ enabled: handoffEnabled,
3595
+ client: clientRef.current,
3596
+ sessionId: clientRef.current.getActiveSessionId(),
3597
+ onOwnership: () => {
3598
+ runRef.current?.abort();
3599
+ runRef.current = null;
3600
+ setState(
3601
+ (prev) => prev.phase === "complete" && !prev.streamingText && !prev.pendingInputs?.length && !prev.toolSteps.length ? prev : {
3602
+ ...prev,
3603
+ phase: "complete",
3604
+ streamingText: "",
3605
+ pendingInputs: [],
3606
+ toolSteps: [],
3607
+ toolResults: [],
3608
+ pendingOffer: null,
3609
+ error: null
3610
+ }
3611
+ );
3612
+ },
3613
+ onEvents: (events) => {
3614
+ setState((prev) => {
3615
+ const seen = new Set(prev.messages.map((message) => message.id));
3616
+ const messages = [];
3617
+ for (const event of events) {
3618
+ if (event.type === "status" || seen.has(`handoff-${event.id}`))
3619
+ continue;
3620
+ messages.push(
3621
+ event.type === "human.message" ? {
3622
+ id: `handoff-${event.id}`,
3623
+ role: "human",
3624
+ text: event.message,
3625
+ createdAt: Date.parse(event.createdAt),
3626
+ representative: event.actor
3627
+ } : {
3628
+ id: `handoff-${event.id}`,
3629
+ role: "visitor",
3630
+ text: event.message,
3631
+ createdAt: Date.parse(event.createdAt)
3632
+ }
3633
+ );
3634
+ }
3635
+ return messages.length ? {
3636
+ ...prev,
3637
+ messages: [...prev.messages, ...messages].sort(
3638
+ (a, b) => a.createdAt - b.createdAt
3639
+ )
3640
+ } : prev;
3641
+ });
3642
+ }
3643
+ });
3644
+ const handoffBlocksAI = (0, import_react3.useRef)(handoff.blocksAI);
3645
+ handoffBlocksAI.current = handoff.blocksAI;
3646
+ const reset = (0, import_react3.useCallback)(() => {
3647
+ if (handoff.blocksAI && !handoff.canReset) return;
3134
3648
  runRef.current?.abort();
3135
3649
  runRef.current = null;
3136
3650
  clientRef.current.reset();
3137
3651
  pendingBookingRef.current = null;
3138
3652
  clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
3139
3653
  setState(initialState);
3140
- }, [initialState, resolvedStorageKeyPrefix, visitorId]);
3141
- const runTurn = (0, import_react2.useCallback)(
3654
+ }, [
3655
+ handoff.blocksAI,
3656
+ handoff.canReset,
3657
+ initialState,
3658
+ resolvedStorageKeyPrefix,
3659
+ visitorId
3660
+ ]);
3661
+ const runTurn = (0, import_react3.useCallback)(
3142
3662
  async (input) => {
3663
+ if (handoffBlocksAI.current) return null;
3143
3664
  const {
3144
3665
  controller,
3145
3666
  initialText = "",
@@ -3263,6 +3784,7 @@ function useAgentChat({
3263
3784
  signal
3264
3785
  });
3265
3786
  if (resume && finalText === null) {
3787
+ if (!isActiveRun() || handoffBlocksAI.current) return null;
3266
3788
  finalText = await clientRef.current.sendTurn(visitorText, {
3267
3789
  handlers,
3268
3790
  signal
@@ -3295,8 +3817,12 @@ function useAgentChat({
3295
3817
  messages: appendAgentTurnMessage(
3296
3818
  prev.messages,
3297
3819
  displayText,
3298
- (prev.toolResults ?? []).filter((result) => result.kind === "search"),
3299
- (prev.toolResults ?? []).filter((result) => result.kind !== "search" && result.kind !== "input")
3820
+ (prev.toolResults ?? []).filter(
3821
+ (result) => result.kind === "search"
3822
+ ),
3823
+ (prev.toolResults ?? []).filter(
3824
+ (result) => result.kind !== "search" && result.kind !== "input"
3825
+ )
3300
3826
  ),
3301
3827
  toolResults: (prev.toolResults ?? []).filter(
3302
3828
  (result) => result.kind === "input"
@@ -3337,7 +3863,7 @@ function useAgentChat({
3337
3863
  },
3338
3864
  [resolvedStorageKeyPrefix, visitorId]
3339
3865
  );
3340
- const rememberBooking = (0, import_react2.useCallback)(
3866
+ const rememberBooking = (0, import_react3.useCallback)(
3341
3867
  (booking) => {
3342
3868
  const current = pendingBookingRef.current;
3343
3869
  if (current?.eventUri === booking.eventUri && current.inviteeEmail === booking.inviteeEmail && current.inviteeUri === booking.inviteeUri) {
@@ -3348,7 +3874,7 @@ function useAgentChat({
3348
3874
  },
3349
3875
  [resolvedStorageKeyPrefix, visitorId]
3350
3876
  );
3351
- const forgetBooking = (0, import_react2.useCallback)(
3877
+ const forgetBooking = (0, import_react3.useCallback)(
3352
3878
  (eventUri) => {
3353
3879
  const current = pendingBookingRef.current;
3354
3880
  if (!current) return;
@@ -3358,12 +3884,21 @@ function useAgentChat({
3358
3884
  },
3359
3885
  [resolvedStorageKeyPrefix, visitorId]
3360
3886
  );
3361
- const submit = (0, import_react2.useCallback)(
3887
+ const submit = (0, import_react3.useCallback)(
3362
3888
  async (visitorText, options) => {
3363
3889
  const trimmed = visitorText.trim();
3364
3890
  const outgoing = options?.runtimeText ?? visitorText;
3365
3891
  if (!outgoing.trim()) return null;
3366
3892
  const declinedComposerFormId = options?.declinedComposerFormId?.trim();
3893
+ if (handoff.blocksAI) {
3894
+ if (!["requesting", "queued", "human"].includes(
3895
+ handoff.page?.status ?? ""
3896
+ ) || handoff.busy)
3897
+ return null;
3898
+ if (!trimmed) return null;
3899
+ await handoff.send(trimmed);
3900
+ return null;
3901
+ }
3367
3902
  const chatResponse = chatInputResponseForText(
3368
3903
  state.pendingInputs ?? [],
3369
3904
  outgoing.trim()
@@ -3465,9 +4000,10 @@ ${outgoing}` : outgoing;
3465
4000
  visitorText: runtimeText
3466
4001
  });
3467
4002
  },
3468
- [runTurn, state.pendingInputs]
4003
+ [handoff, runTurn, state.pendingInputs]
3469
4004
  );
3470
- const retry = (0, import_react2.useCallback)(async () => {
4005
+ const retry = (0, import_react3.useCallback)(async () => {
4006
+ if (handoffBlocksAI.current) return;
3471
4007
  const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
3472
4008
  if (!visitorMessage) return;
3473
4009
  if (runRef.current) {
@@ -3500,7 +4036,8 @@ ${outgoing}` : outgoing;
3500
4036
  visitorText: visitorTurnText(visitorMessage)
3501
4037
  });
3502
4038
  }, [runTurn, state.messages]);
3503
- const regenerate = (0, import_react2.useCallback)(async () => {
4039
+ const regenerate = (0, import_react3.useCallback)(async () => {
4040
+ if (handoffBlocksAI.current) return null;
3504
4041
  let lastVisitorIndex = -1;
3505
4042
  for (let index = state.messages.length - 1; index >= 0; index -= 1) {
3506
4043
  if (state.messages[index]?.role === "visitor") {
@@ -3543,7 +4080,7 @@ ${outgoing}` : outgoing;
3543
4080
  visitorText: visitorTurnText(visitorMessage)
3544
4081
  });
3545
4082
  }, [runTurn, state.messages]);
3546
- const respondToToolInput = (0, import_react2.useCallback)(
4083
+ const respondToToolInput = (0, import_react3.useCallback)(
3547
4084
  async (surface, values) => {
3548
4085
  await submit(`${surface.title} submitted`, {
3549
4086
  runtimeText: [
@@ -3556,9 +4093,9 @@ ${outgoing}` : outgoing;
3556
4093
  },
3557
4094
  [submit]
3558
4095
  );
3559
- const respondToInput = (0, import_react2.useCallback)(
4096
+ const respondToInput = (0, import_react3.useCallback)(
3560
4097
  async (response) => {
3561
- if (runRef.current) return;
4098
+ if (handoffBlocksAI.current || runRef.current) return;
3562
4099
  const pending = state.pendingInputs?.find(
3563
4100
  (request) => request.requestId === response.requestId
3564
4101
  );
@@ -3586,7 +4123,8 @@ ${outgoing}` : outgoing;
3586
4123
  },
3587
4124
  [respondToToolInput, runTurn, state.pendingInputs]
3588
4125
  );
3589
- (0, import_react2.useEffect)(() => {
4126
+ (0, import_react3.useEffect)(() => {
4127
+ if (handoff.blocksAI) return;
3590
4128
  const conversation = loadPersistedAgentConversation(
3591
4129
  resolvedStorageKeyPrefix,
3592
4130
  visitorId
@@ -3608,14 +4146,21 @@ ${outgoing}` : outgoing;
3608
4146
  }
3609
4147
  controller.abort();
3610
4148
  };
3611
- }, [identityKey, resolvedStorageKeyPrefix, runTurn, visitorId]);
3612
- (0, import_react2.useEffect)(() => {
4149
+ }, [
4150
+ handoff.blocksAI,
4151
+ identityKey,
4152
+ resolvedStorageKeyPrefix,
4153
+ runTurn,
4154
+ visitorId
4155
+ ]);
4156
+ (0, import_react3.useEffect)(() => {
3613
4157
  return () => {
3614
4158
  runRef.current?.abort();
3615
4159
  runRef.current = null;
3616
4160
  };
3617
4161
  }, []);
3618
4162
  return {
4163
+ handoff,
3619
4164
  state,
3620
4165
  reset,
3621
4166
  retry,
@@ -3637,12 +4182,12 @@ function isAgentBusy(phase) {
3637
4182
  }
3638
4183
 
3639
4184
  // src/react/hooks/useIsMobile.ts
3640
- var import_react3 = require("react");
4185
+ var import_react4 = require("react");
3641
4186
  function useIsMobile(breakpoint = 767) {
3642
- const [isMobile, setIsMobile] = (0, import_react3.useState)(
4187
+ const [isMobile, setIsMobile] = (0, import_react4.useState)(
3643
4188
  () => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
3644
4189
  );
3645
- (0, import_react3.useEffect)(() => {
4190
+ (0, import_react4.useEffect)(() => {
3646
4191
  const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
3647
4192
  const onChange = () => setIsMobile(media.matches);
3648
4193
  onChange();
@@ -3670,7 +4215,7 @@ function normalizeAgentPlacement(placement) {
3670
4215
  }
3671
4216
 
3672
4217
  // src/react/components/AgentRail/AgentRail.tsx
3673
- var import_react14 = require("react");
4218
+ var import_react16 = require("react");
3674
4219
 
3675
4220
  // src/react/types/conversation.ts
3676
4221
  var defaultAgentRailTheme = {
@@ -3753,7 +4298,7 @@ function agentThemeStyle(theme, resolvedColorScheme) {
3753
4298
  }
3754
4299
 
3755
4300
  // src/react/hooks/useAgentColorScheme.ts
3756
- var import_react4 = require("react");
4301
+ var import_react5 = require("react");
3757
4302
  var DARK_MODE_QUERY = "(prefers-color-scheme: dark)";
3758
4303
  function subscribeToDarkMode(onChange) {
3759
4304
  if (typeof window === "undefined" || !window.matchMedia) {
@@ -3771,7 +4316,7 @@ function getPrefersDarkMode() {
3771
4316
  return typeof window !== "undefined" && Boolean(window.matchMedia?.(DARK_MODE_QUERY).matches);
3772
4317
  }
3773
4318
  function useAgentColorScheme(colorScheme = "auto") {
3774
- const prefersDarkMode = (0, import_react4.useSyncExternalStore)(
4319
+ const prefersDarkMode = (0, import_react5.useSyncExternalStore)(
3775
4320
  subscribeToDarkMode,
3776
4321
  getPrefersDarkMode,
3777
4322
  () => false
@@ -3783,7 +4328,7 @@ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
3783
4328
  }
3784
4329
 
3785
4330
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
3786
- var import_react5 = require("react");
4331
+ var import_react6 = require("react");
3787
4332
  var import_jsx_runtime = require("react/jsx-runtime");
3788
4333
  function joinLabels(labels) {
3789
4334
  if (labels.length <= 1) return labels[0] ?? "";
@@ -3902,7 +4447,7 @@ function AgentActivityBubble({
3902
4447
  const statusText = workSummary(steps, failed, brandLabel);
3903
4448
  const softReview = statusText === AGENT_SOFT_REVIEW_STATUS;
3904
4449
  const receiptId = steps.map((step) => step.id).join(":");
3905
- const [expandedReceiptId, setExpandedReceiptId] = (0, import_react5.useState)(
4450
+ const [expandedReceiptId, setExpandedReceiptId] = (0, import_react6.useState)(
3906
4451
  null
3907
4452
  );
3908
4453
  const detailsOpen = !active && expandedReceiptId === receiptId;
@@ -3953,17 +4498,17 @@ function AgentActivityBubble({
3953
4498
  }
3954
4499
 
3955
4500
  // src/react/components/AnswerReceiptDialog/AnswerReceiptDialog.tsx
3956
- var import_react7 = require("react");
4501
+ var import_react8 = require("react");
3957
4502
  var import_react_dom = require("react-dom");
3958
4503
 
3959
4504
  // src/react/components/AgentRail/AgentRailOverlayContext.tsx
3960
- var import_react6 = require("react");
3961
- var AgentRailOverlayContext = (0, import_react6.createContext)(null);
4505
+ var import_react7 = require("react");
4506
+ var AgentRailOverlayContext = (0, import_react7.createContext)(null);
3962
4507
  function useAgentRailPortalRoots() {
3963
- return (0, import_react6.useContext)(AgentRailOverlayContext);
4508
+ return (0, import_react7.useContext)(AgentRailOverlayContext);
3964
4509
  }
3965
4510
  function useAgentRailMenuPortalRoot() {
3966
- return (0, import_react6.useContext)(AgentRailOverlayContext)?.railRef ?? null;
4511
+ return (0, import_react7.useContext)(AgentRailOverlayContext)?.railRef ?? null;
3967
4512
  }
3968
4513
 
3969
4514
  // src/react/components/AnswerReceiptDialog/AnswerReceiptDialog.tsx
@@ -3987,10 +4532,10 @@ function AnswerReceiptDialog({
3987
4532
  }) {
3988
4533
  const portalRoots = useAgentRailPortalRoots();
3989
4534
  const overlayRoot = portalRoots?.overlayRef ?? null;
3990
- const cardRef = (0, import_react7.useRef)(null);
3991
- const closeButtonRef = (0, import_react7.useRef)(null);
3992
- const previouslyFocusedRef = (0, import_react7.useRef)(null);
3993
- (0, import_react7.useEffect)(() => {
4535
+ const cardRef = (0, import_react8.useRef)(null);
4536
+ const closeButtonRef = (0, import_react8.useRef)(null);
4537
+ const previouslyFocusedRef = (0, import_react8.useRef)(null);
4538
+ (0, import_react8.useEffect)(() => {
3994
4539
  previouslyFocusedRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
3995
4540
  closeButtonRef.current?.focus({ preventScroll: true });
3996
4541
  const handleKeyDown = (event) => {
@@ -4075,7 +4620,7 @@ function AnswerReceiptDialog({
4075
4620
  }
4076
4621
 
4077
4622
  // src/react/components/Composer/Composer.tsx
4078
- var import_react8 = require("react");
4623
+ var import_react9 = require("react");
4079
4624
  var import_jsx_runtime3 = require("react/jsx-runtime");
4080
4625
  var FORM_SUBMITTED_MESSAGE = "Shared my details";
4081
4626
  function SendIcon() {
@@ -4137,17 +4682,17 @@ function Composer({
4137
4682
  allowFormResume = true,
4138
4683
  onSubmit
4139
4684
  }) {
4140
- const [value, setValue] = (0, import_react8.useState)("");
4141
- const [draft, setDraft] = (0, import_react8.useState)(() => createDraft(form));
4142
- const inputRef = (0, import_react8.useRef)(null);
4143
- const firstFieldRef = (0, import_react8.useRef)(null);
4144
- const formRef = (0, import_react8.useRef)(null);
4145
- const formId = (0, import_react8.useId)();
4685
+ const [value, setValue] = (0, import_react9.useState)("");
4686
+ const [draft, setDraft] = (0, import_react9.useState)(() => createDraft(form));
4687
+ const inputRef = (0, import_react9.useRef)(null);
4688
+ const firstFieldRef = (0, import_react9.useRef)(null);
4689
+ const formRef = (0, import_react9.useRef)(null);
4690
+ const formId = (0, import_react9.useId)();
4146
4691
  if (form && form.id !== draft.form?.id) setDraft(createDraft(form));
4147
4692
  const savedForm = allowFormResume && !draft.submitted ? draft.form : null;
4148
4693
  const activeForm = !disabled && draft.expanded ? savedForm : null;
4149
4694
  const canSend = !disabled && Boolean(value.trim() || activeForm);
4150
- (0, import_react8.useEffect)(() => {
4695
+ (0, import_react9.useEffect)(() => {
4151
4696
  if (activeForm) firstFieldRef.current?.focus();
4152
4697
  else if ((savedForm || draft.submitted) && !disabled)
4153
4698
  inputRef.current?.focus();
@@ -4408,7 +4953,7 @@ function FollowUpChips({
4408
4953
  }
4409
4954
 
4410
4955
  // src/react/components/MessageBubble/MessageBubble.tsx
4411
- var import_react10 = require("react");
4956
+ var import_react11 = require("react");
4412
4957
 
4413
4958
  // src/react/components/SearchReferences/SearchReferences.tsx
4414
4959
  var import_jsx_runtime5 = require("react/jsx-runtime");
@@ -4499,7 +5044,7 @@ function SearchReferences({
4499
5044
  }
4500
5045
 
4501
5046
  // src/react/components/BookingCard/BookingCard.tsx
4502
- var import_react9 = require("react");
5047
+ var import_react10 = require("react");
4503
5048
  var import_jsx_runtime6 = require("react/jsx-runtime");
4504
5049
  var BOOKING_STEPS = [
4505
5050
  { id: "date", label: "Date" },
@@ -4554,27 +5099,27 @@ function InteractiveBookingCard({
4554
5099
  offer,
4555
5100
  onBook
4556
5101
  }) {
4557
- const fieldId = (0, import_react9.useId)();
5102
+ const fieldId = (0, import_react10.useId)();
4558
5103
  const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
4559
- const [step, setStep] = (0, import_react9.useState)("date");
4560
- const [eventTypeUri, setEventTypeUri] = (0, import_react9.useState)(defaultType);
4561
- const [selectedDate, setSelectedDate] = (0, import_react9.useState)("");
4562
- const [startTime, setStartTime] = (0, import_react9.useState)("");
4563
- const [name, setName] = (0, import_react9.useState)("");
4564
- const [email, setEmail] = (0, import_react9.useState)("");
4565
- const activeStepRef = (0, import_react9.useRef)(null);
4566
- const previousStepRef = (0, import_react9.useRef)(step);
5104
+ const [step, setStep] = (0, import_react10.useState)("date");
5105
+ const [eventTypeUri, setEventTypeUri] = (0, import_react10.useState)(defaultType);
5106
+ const [selectedDate, setSelectedDate] = (0, import_react10.useState)("");
5107
+ const [startTime, setStartTime] = (0, import_react10.useState)("");
5108
+ const [name, setName] = (0, import_react10.useState)("");
5109
+ const [email, setEmail] = (0, import_react10.useState)("");
5110
+ const activeStepRef = (0, import_react10.useRef)(null);
5111
+ const previousStepRef = (0, import_react10.useRef)(step);
4567
5112
  const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
4568
- (0, import_react9.useEffect)(() => {
5113
+ (0, import_react10.useEffect)(() => {
4569
5114
  if (previousStepRef.current === step) return;
4570
5115
  previousStepRef.current = step;
4571
5116
  activeStepRef.current?.scrollIntoView({ block: "nearest" });
4572
5117
  }, [step]);
4573
- const slots = (0, import_react9.useMemo)(
5118
+ const slots = (0, import_react10.useMemo)(
4574
5119
  () => bookingSlotsForEventType(offer.slots, eventTypeUri),
4575
5120
  [eventTypeUri, offer.slots]
4576
5121
  );
4577
- const availableByDate = (0, import_react9.useMemo)(() => {
5122
+ const availableByDate = (0, import_react10.useMemo)(() => {
4578
5123
  const next = /* @__PURE__ */ new Map();
4579
5124
  for (const slot of slots) {
4580
5125
  const key = slotDateKey(slot.startTime);
@@ -4582,7 +5127,7 @@ function InteractiveBookingCard({
4582
5127
  }
4583
5128
  return next;
4584
5129
  }, [slots]);
4585
- const [visibleMonth, setVisibleMonth] = (0, import_react9.useState)(
5130
+ const [visibleMonth, setVisibleMonth] = (0, import_react10.useState)(
4586
5131
  () => firstAvailableBookingMonth(slots)
4587
5132
  );
4588
5133
  function selectEventType(nextType) {
@@ -4595,7 +5140,7 @@ function InteractiveBookingCard({
4595
5140
  )
4596
5141
  );
4597
5142
  }
4598
- const daySlots = (0, import_react9.useMemo)(
5143
+ const daySlots = (0, import_react10.useMemo)(
4599
5144
  () => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
4600
5145
  [selectedDate, slots]
4601
5146
  );
@@ -4604,7 +5149,7 @@ function InteractiveBookingCard({
4604
5149
  );
4605
5150
  const selectedSample = availableByDate.get(selectedDate) ?? startTime;
4606
5151
  const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
4607
- const weekdays = (0, import_react9.useMemo)(() => weekdayLabels(), []);
5152
+ const weekdays = (0, import_react10.useMemo)(() => weekdayLabels(), []);
4608
5153
  const cells = calendarCells(visibleMonth.year, visibleMonth.month);
4609
5154
  const canPrevMonth = [...availableByDate.keys()].some((key) => {
4610
5155
  const month = monthFromKey(key);
@@ -4890,8 +5435,8 @@ function resolveMessageUrl(safeUrl, baseUrl) {
4890
5435
  // src/react/components/MessageBubble/MessageBubble.tsx
4891
5436
  var import_styles = require("streamdown/styles.css");
4892
5437
  var import_jsx_runtime7 = require("react/jsx-runtime");
4893
- function normalizeDedupeText(text2) {
4894
- return text2.trim().replace(/\s+/g, " ").toLowerCase();
5438
+ function normalizeDedupeText(text3) {
5439
+ return text3.trim().replace(/\s+/g, " ").toLowerCase();
4895
5440
  }
4896
5441
  function paragraphsAreNearDuplicates(first, second) {
4897
5442
  const left = normalizeDedupeText(first);
@@ -4907,8 +5452,8 @@ function paragraphsShareOpening(first, second) {
4907
5452
  if (!opening || opening.length < 20) return false;
4908
5453
  return second.trim().startsWith(opening);
4909
5454
  }
4910
- function collapseRepeatedText(text2) {
4911
- const trimmed = text2.trim();
5455
+ function collapseRepeatedText(text3) {
5456
+ const trimmed = text3.trim();
4912
5457
  if (trimmed.length < 40) return trimmed;
4913
5458
  const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
4914
5459
  if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
@@ -4929,13 +5474,14 @@ function MessageBubble({
4929
5474
  message,
4930
5475
  brandLogoUrl,
4931
5476
  bookingDisabled = false,
5477
+ showProvenance = false,
4932
5478
  bookingReadOnly = false,
4933
5479
  linkBaseUrl,
4934
5480
  offer,
4935
5481
  onBook
4936
5482
  }) {
4937
5483
  const resolvedLogoUrl = brandLogoUrl?.trim();
4938
- const [failedLogoUrl, setFailedLogoUrl] = (0, import_react10.useState)(null);
5484
+ const [failedLogoUrl, setFailedLogoUrl] = (0, import_react11.useState)(null);
4939
5485
  const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
4940
5486
  const cards = message.role === "agent" ? extractToolCards(message.text) : [];
4941
5487
  const extractedOffers = cards.filter(
@@ -4950,7 +5496,34 @@ function MessageBubble({
4950
5496
  if (message.role === "visitor") {
4951
5497
  const visitorText = visitorMessageDisplayText(message).trim();
4952
5498
  if (!visitorText) return null;
4953
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "message-bubble__text", children: visitorText }) });
5499
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
5500
+ "article",
5501
+ {
5502
+ className: "message-bubble message-bubble--visitor",
5503
+ "aria-label": "Message from visitor",
5504
+ children: [
5505
+ showProvenance ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "message-bubble__speaker", children: "You \xB7 Visitor" }) : null,
5506
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "message-bubble__text", children: visitorText })
5507
+ ]
5508
+ }
5509
+ );
5510
+ }
5511
+ if (message.role === "human") {
5512
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
5513
+ "article",
5514
+ {
5515
+ className: "message-bubble message-bubble--human",
5516
+ "aria-label": `Message from ${message.representative.name}`,
5517
+ children: [
5518
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("strong", { className: "message-bubble__representative", children: [
5519
+ message.representative.name,
5520
+ " ",
5521
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Human representative" })
5522
+ ] }),
5523
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "message-bubble__text", children: message.text })
5524
+ ]
5525
+ }
5526
+ );
4954
5527
  }
4955
5528
  const citations = message.citations ?? [];
4956
5529
  const agentText = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "message-bubble__text", children: [
@@ -4987,40 +5560,48 @@ function MessageBubble({
4987
5560
  ] });
4988
5561
  if (!displayText && !message.searchResults?.length && offers.length === 0)
4989
5562
  return null;
4990
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
4991
- displayText || message.searchResults?.length ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "message-bubble__agent-row", children: [
4992
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4993
- "img",
4994
- {
4995
- src: resolvedLogoUrl,
4996
- alt: "",
4997
- onError: () => {
4998
- setFailedLogoUrl(resolvedLogoUrl ?? null);
4999
- }
5000
- }
5001
- ) }),
5002
- agentText
5003
- ] }) : agentText : null,
5004
- offers.map((nextOffer, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5005
- BookingCard,
5006
- {
5007
- disabled: bookingDisabled,
5008
- readOnly: bookingReadOnly,
5009
- offer: nextOffer,
5010
- onBook
5011
- },
5012
- `${bookingOfferIdentityKey(nextOffer)}-${index}`
5013
- ))
5014
- ] });
5563
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
5564
+ "article",
5565
+ {
5566
+ className: "message-bubble message-bubble--agent",
5567
+ "aria-label": "Message from AI assistant",
5568
+ children: [
5569
+ showProvenance ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "message-bubble__speaker", children: "AI assistant" }) : null,
5570
+ displayText || message.searchResults?.length ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "message-bubble__agent-row", children: [
5571
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5572
+ "img",
5573
+ {
5574
+ src: resolvedLogoUrl,
5575
+ alt: "",
5576
+ onError: () => {
5577
+ setFailedLogoUrl(resolvedLogoUrl ?? null);
5578
+ }
5579
+ }
5580
+ ) }),
5581
+ agentText
5582
+ ] }) : agentText : null,
5583
+ offers.map((nextOffer, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5584
+ BookingCard,
5585
+ {
5586
+ disabled: bookingDisabled,
5587
+ readOnly: bookingReadOnly,
5588
+ offer: nextOffer,
5589
+ onBook
5590
+ },
5591
+ `${bookingOfferIdentityKey(nextOffer)}-${index}`
5592
+ ))
5593
+ ]
5594
+ }
5595
+ );
5015
5596
  }
5016
5597
 
5017
5598
  // src/react/components/MessageActions/MessageActions.tsx
5018
- var import_react11 = require("react");
5599
+ var import_react12 = require("react");
5019
5600
  var import_react_dom2 = require("react-dom");
5020
5601
 
5021
5602
  // src/react/lib/speech.ts
5022
- function toSpeechText(text2) {
5023
- let out = hideToolCardFences(text2);
5603
+ function toSpeechText(text3) {
5604
+ let out = hideToolCardFences(text3);
5024
5605
  out = out.replace(/```[\s\S]*?```/g, " ");
5025
5606
  out = out.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
5026
5607
  out = out.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
@@ -5355,12 +5936,12 @@ function StopIcon() {
5355
5936
  }
5356
5937
  ) });
5357
5938
  }
5358
- async function writeToClipboard(text2) {
5939
+ async function writeToClipboard(text3) {
5359
5940
  try {
5360
- await navigator.clipboard.writeText(text2);
5941
+ await navigator.clipboard.writeText(text3);
5361
5942
  } catch {
5362
5943
  const textarea = document.createElement("textarea");
5363
- textarea.value = text2;
5944
+ textarea.value = text3;
5364
5945
  textarea.style.position = "fixed";
5365
5946
  textarea.style.opacity = "0";
5366
5947
  document.body.appendChild(textarea);
@@ -5411,26 +5992,26 @@ function MessageActions({
5411
5992
  speechText
5412
5993
  }) {
5413
5994
  const menuPortalRoot = useAgentRailMenuPortalRoot();
5414
- const [copied, setCopied] = (0, import_react11.useState)(false);
5415
- const [rating, setRating] = (0, import_react11.useState)(null);
5416
- const [menuOpen, setMenuOpen] = (0, import_react11.useState)(false);
5417
- const [menuPosition, setMenuPosition] = (0, import_react11.useState)(null);
5418
- const [speaking, setSpeaking] = (0, import_react11.useState)(false);
5419
- const [speechSupported, setSpeechSupported] = (0, import_react11.useState)(null);
5420
- const copyTimerRef = (0, import_react11.useRef)(null);
5421
- const menuRef = (0, import_react11.useRef)(null);
5422
- const portaledMenuRef = (0, import_react11.useRef)(null);
5423
- const moreButtonRef = (0, import_react11.useRef)(null);
5995
+ const [copied, setCopied] = (0, import_react12.useState)(false);
5996
+ const [rating, setRating] = (0, import_react12.useState)(null);
5997
+ const [menuOpen, setMenuOpen] = (0, import_react12.useState)(false);
5998
+ const [menuPosition, setMenuPosition] = (0, import_react12.useState)(null);
5999
+ const [speaking, setSpeaking] = (0, import_react12.useState)(false);
6000
+ const [speechSupported, setSpeechSupported] = (0, import_react12.useState)(null);
6001
+ const copyTimerRef = (0, import_react12.useRef)(null);
6002
+ const menuRef = (0, import_react12.useRef)(null);
6003
+ const portaledMenuRef = (0, import_react12.useRef)(null);
6004
+ const moreButtonRef = (0, import_react12.useRef)(null);
5424
6005
  const answeredLabel = formatAnsweredAt(answeredAt ?? 0);
5425
6006
  const resolvedSpeechText = speechText ?? toSpeechText(copyText);
5426
6007
  const canOpenReceipt = Boolean(receiptSteps) && Boolean(onOpenReceipt);
5427
6008
  const readAloudEligible = Boolean(readAloud && resolvedSpeechText);
5428
6009
  const canReadAloud = readAloudEligible && speechSupported === true;
5429
6010
  const showMenu = canOpenReceipt || (speechSupported === null ? readAloudEligible : canReadAloud);
5430
- (0, import_react11.useLayoutEffect)(() => {
6011
+ (0, import_react12.useLayoutEffect)(() => {
5431
6012
  setSpeechSupported(isSpeechSupported());
5432
6013
  }, []);
5433
- (0, import_react11.useLayoutEffect)(() => {
6014
+ (0, import_react12.useLayoutEffect)(() => {
5434
6015
  if (!menuOpen || !moreButtonRef.current || !portaledMenuRef.current) {
5435
6016
  setMenuPosition(null);
5436
6017
  return;
@@ -5447,7 +6028,7 @@ function MessageActions({
5447
6028
  })
5448
6029
  );
5449
6030
  }, [menuOpen, menuPortalRoot]);
5450
- (0, import_react11.useEffect)(() => {
6031
+ (0, import_react12.useEffect)(() => {
5451
6032
  const unsubscribe = subscribeSpeechInterrupts(() => setSpeaking(false));
5452
6033
  return () => {
5453
6034
  unsubscribe();
@@ -5457,7 +6038,7 @@ function MessageActions({
5457
6038
  stopSpeech();
5458
6039
  };
5459
6040
  }, []);
5460
- (0, import_react11.useEffect)(() => {
6041
+ (0, import_react12.useEffect)(() => {
5461
6042
  if (!menuOpen) return;
5462
6043
  const handlePointerDown = (event) => {
5463
6044
  const target = event.target;
@@ -5626,7 +6207,7 @@ function MessageActions({
5626
6207
  }
5627
6208
 
5628
6209
  // src/react/components/HumanInputCard/HumanInputCard.tsx
5629
- var import_react13 = require("react");
6210
+ var import_react14 = require("react");
5630
6211
 
5631
6212
  // src/react/components/ConfirmationCard/ConfirmationCard.tsx
5632
6213
  var import_jsx_runtime9 = require("react/jsx-runtime");
@@ -5668,7 +6249,7 @@ function ConfirmationCard({
5668
6249
  }
5669
6250
 
5670
6251
  // src/react/components/ToolInputCard/ToolInputCard.tsx
5671
- var import_react12 = require("react");
6252
+ var import_react13 = require("react");
5672
6253
  var import_jsx_runtime10 = require("react/jsx-runtime");
5673
6254
  function isRecord5(value) {
5674
6255
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -5791,13 +6372,13 @@ function valuesMatch(left, right) {
5791
6372
  }
5792
6373
  function FieldDescription({
5793
6374
  field,
5794
- id
6375
+ id: id2
5795
6376
  }) {
5796
- return field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id, className: "tool-input-card__description", children: field.description }) : null;
6377
+ return field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: id2, className: "tool-input-card__description", children: field.description }) : null;
5797
6378
  }
5798
6379
  function ChoiceField({
5799
6380
  field,
5800
- id,
6381
+ id: id2,
5801
6382
  value,
5802
6383
  disabled,
5803
6384
  describedBy,
@@ -5815,7 +6396,7 @@ function ChoiceField({
5815
6396
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
5816
6397
  "select",
5817
6398
  {
5818
- id,
6399
+ id: id2,
5819
6400
  value: selectedIndex >= 0 ? String(selectedIndex) : "",
5820
6401
  disabled,
5821
6402
  required: field.required,
@@ -5838,7 +6419,7 @@ function ChoiceField({
5838
6419
  {
5839
6420
  className: "tool-input-card__choices",
5840
6421
  role: multiple ? "group" : "radiogroup",
5841
- "aria-labelledby": `${id}-label`,
6422
+ "aria-labelledby": `${id2}-label`,
5842
6423
  "aria-describedby": describedBy,
5843
6424
  "aria-invalid": Boolean(error),
5844
6425
  "aria-required": field.required,
@@ -5849,7 +6430,7 @@ function ChoiceField({
5849
6430
  "input",
5850
6431
  {
5851
6432
  type: multiple ? "checkbox" : "radio",
5852
- name: id,
6433
+ name: id2,
5853
6434
  value: String(index),
5854
6435
  checked,
5855
6436
  disabled,
@@ -5877,22 +6458,22 @@ function ToolField({
5877
6458
  disabled,
5878
6459
  error,
5879
6460
  field,
5880
- id,
6461
+ id: id2,
5881
6462
  value,
5882
6463
  onBlur,
5883
6464
  onChange
5884
6465
  }) {
5885
6466
  const describedBy = [
5886
- field.description ? `${id}-description` : "",
5887
- error ? `${id}-error` : ""
6467
+ field.description ? `${id2}-description` : "",
6468
+ error ? `${id2}-error` : ""
5888
6469
  ].filter(Boolean).join(" ");
5889
6470
  if (field.kind === "checkbox" || field.kind === "confirmation") {
5890
6471
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-input-card__field", children: [
5891
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "tool-input-card__check", htmlFor: id, children: [
6472
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "tool-input-card__check", htmlFor: id2, children: [
5892
6473
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5893
6474
  "input",
5894
6475
  {
5895
- id,
6476
+ id: id2,
5896
6477
  type: "checkbox",
5897
6478
  checked: value === true,
5898
6479
  disabled,
@@ -5904,18 +6485,18 @@ function ToolField({
5904
6485
  ),
5905
6486
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
5906
6487
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: field.label }),
5907
- field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id}-description`, children: field.description }) : null
6488
+ field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-description`, children: field.description }) : null
5908
6489
  ] })
5909
6490
  ] }),
5910
- error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id}-error`, className: "tool-input-card__error", children: error }) : null
6491
+ error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
5911
6492
  ] });
5912
6493
  }
5913
- const label = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { id: `${id}-label`, htmlFor: id, children: [
6494
+ const label = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { id: `${id2}-label`, htmlFor: id2, children: [
5914
6495
  field.label,
5915
6496
  field.required ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { "aria-hidden": "true", children: " *" }) : null
5916
6497
  ] });
5917
6498
  const common = {
5918
- id,
6499
+ id: id2,
5919
6500
  disabled,
5920
6501
  required: field.required,
5921
6502
  "aria-describedby": describedBy || void 0,
@@ -5928,7 +6509,7 @@ function ToolField({
5928
6509
  ChoiceField,
5929
6510
  {
5930
6511
  field,
5931
- id,
6512
+ id: id2,
5932
6513
  value,
5933
6514
  disabled,
5934
6515
  describedBy: describedBy || void 0,
@@ -5965,7 +6546,7 @@ function ToolField({
5965
6546
  onChange: (event) => onChange(Number(event.target.value))
5966
6547
  }
5967
6548
  ),
5968
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("output", { htmlFor: id, children: numericValue })
6549
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("output", { htmlFor: id2, children: numericValue })
5969
6550
  ] });
5970
6551
  } else {
5971
6552
  const type = field.kind === "date-time" ? "datetime-local" : field.kind === "calendar" ? "date" : field.kind;
@@ -5989,9 +6570,9 @@ function ToolField({
5989
6570
  }
5990
6571
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-input-card__field", children: [
5991
6572
  label,
5992
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldDescription, { field, id: `${id}-description` }),
6573
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldDescription, { field, id: `${id2}-description` }),
5993
6574
  control,
5994
- error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id}-error`, className: "tool-input-card__error", children: error }) : null
6575
+ error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
5995
6576
  ] });
5996
6577
  }
5997
6578
  function ToolInputCard({
@@ -5999,11 +6580,11 @@ function ToolInputCard({
5999
6580
  surface,
6000
6581
  onSubmit
6001
6582
  }) {
6002
- const [values, setValues] = (0, import_react12.useState)(
6583
+ const [values, setValues] = (0, import_react13.useState)(
6003
6584
  () => initialValues(surface)
6004
6585
  );
6005
- const [touched, setTouched] = (0, import_react12.useState)(() => /* @__PURE__ */ new Set());
6006
- const [submitted, setSubmitted] = (0, import_react12.useState)(false);
6586
+ const [touched, setTouched] = (0, import_react13.useState)(() => /* @__PURE__ */ new Set());
6587
+ const [submitted, setSubmitted] = (0, import_react13.useState)(false);
6007
6588
  const errors = Object.fromEntries(
6008
6589
  surface.fields.map((field) => [
6009
6590
  field.path,
@@ -6096,7 +6677,7 @@ function HumanInputCard({
6096
6677
  request,
6097
6678
  onRespond
6098
6679
  }) {
6099
- const [text2, setText] = (0, import_react13.useState)("");
6680
+ const [text3, setText] = (0, import_react14.useState)("");
6100
6681
  const options = request.options ?? [];
6101
6682
  const showText = request.display === "text" || request.allowFreeform && options.length === 0;
6102
6683
  if (request.ui) {
@@ -6121,7 +6702,7 @@ function HumanInputCard({
6121
6702
  }
6122
6703
  function submitText(event) {
6123
6704
  event.preventDefault();
6124
- const value = text2.trim();
6705
+ const value = text3.trim();
6125
6706
  if (!value || disabled) return;
6126
6707
  onRespond?.({ requestId: request.requestId, text: value });
6127
6708
  }
@@ -6139,12 +6720,12 @@ function HumanInputCard({
6139
6720
  "input",
6140
6721
  {
6141
6722
  id: `human-input-text-${request.requestId}`,
6142
- value: text2,
6723
+ value: text3,
6143
6724
  disabled,
6144
6725
  onChange: (event) => setText(event.target.value)
6145
6726
  }
6146
6727
  ),
6147
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", disabled: disabled || !text2.trim(), children: "Send" })
6728
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", disabled: disabled || !text3.trim(), children: "Send" })
6148
6729
  ] })
6149
6730
  ] }) : null,
6150
6731
  !showText && options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "human-input-card__unavailable", role: "status", children: "This request can\u2019t be answered here." }) : null
@@ -6153,31 +6734,173 @@ function HumanInputCard({
6153
6734
  );
6154
6735
  }
6155
6736
 
6156
- // src/react/components/CollectionResultCard/CollectionResultCard.tsx
6737
+ // src/react/components/LocationConsent/LocationConsent.tsx
6738
+ var import_react15 = require("react");
6739
+
6740
+ // src/runtime/location-consent.ts
6741
+ function isLocationConsentRequest(request) {
6742
+ return request.kind === "tool-approval" && request.action.toolName === "request_location";
6743
+ }
6744
+ function preciseLocationResponse(requestId, position) {
6745
+ const { latitude, longitude, accuracy } = position.coords;
6746
+ if (!Number.isFinite(latitude) || latitude < -90 || latitude > 90 || !Number.isFinite(longitude) || longitude < -180 || longitude > 180 || !Number.isFinite(accuracy) || accuracy < 0 || !Number.isFinite(position.timestamp)) {
6747
+ throw new Error("Your device returned an invalid location.");
6748
+ }
6749
+ return {
6750
+ requestId,
6751
+ optionId: "approve",
6752
+ text: JSON.stringify({
6753
+ status: "shared",
6754
+ location: {
6755
+ source: "device",
6756
+ latitude,
6757
+ longitude,
6758
+ accuracyMeters: accuracy,
6759
+ capturedAt: new Date(position.timestamp).toISOString(),
6760
+ consentedAt: (/* @__PURE__ */ new Date()).toISOString(),
6761
+ scope: "conversation"
6762
+ }
6763
+ })
6764
+ };
6765
+ }
6766
+ function getPreciseLocation(geolocation) {
6767
+ return new Promise(
6768
+ (resolve, reject) => geolocation.getCurrentPosition(
6769
+ resolve,
6770
+ (error) => reject(
6771
+ new Error(
6772
+ 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."
6773
+ )
6774
+ ),
6775
+ { enableHighAccuracy: true, maximumAge: 0, timeout: 1e4 }
6776
+ )
6777
+ );
6778
+ }
6779
+
6780
+ // src/react/components/LocationConsent/LocationConsent.tsx
6157
6781
  var import_jsx_runtime12 = require("react/jsx-runtime");
6782
+ function LocationConsent({
6783
+ request,
6784
+ onRespond
6785
+ }) {
6786
+ const [preference, setPreference] = (0, import_react15.useState)("unset");
6787
+ const [locating, setLocating] = (0, import_react15.useState)(false);
6788
+ const [error, setError] = (0, import_react15.useState)(null);
6789
+ const respond = (0, import_react15.useRef)(onRespond);
6790
+ const answered = (0, import_react15.useRef)(null);
6791
+ const requestId = request?.requestId;
6792
+ (0, import_react15.useEffect)(() => {
6793
+ respond.current = onRespond;
6794
+ }, [onRespond]);
6795
+ (0, import_react15.useEffect)(() => {
6796
+ if (!requestId || preference === "unset" || answered.current === requestId || !respond.current)
6797
+ return;
6798
+ if (preference === "off") {
6799
+ setLocating(false);
6800
+ answered.current = requestId;
6801
+ respond.current({
6802
+ requestId,
6803
+ optionId: "approve",
6804
+ text: JSON.stringify({ status: "declined" })
6805
+ });
6806
+ return;
6807
+ }
6808
+ let cancelled = false;
6809
+ const pendingId = requestId;
6810
+ setLocating(true);
6811
+ setError(null);
6812
+ async function locate() {
6813
+ try {
6814
+ if (!Reflect.has(navigator, "geolocation"))
6815
+ throw new Error("Location sharing isn\u2019t available in this browser.");
6816
+ const position = await getPreciseLocation(navigator.geolocation);
6817
+ if (cancelled) return;
6818
+ const response = preciseLocationResponse(pendingId, position);
6819
+ answered.current = pendingId;
6820
+ respond.current?.(response);
6821
+ } catch (failure) {
6822
+ if (cancelled) return;
6823
+ setPreference("unset");
6824
+ setError(
6825
+ failure instanceof Error ? failure.message : "Location sharing failed. Try again or continue without sharing."
6826
+ );
6827
+ } finally {
6828
+ if (!cancelled) setLocating(false);
6829
+ }
6830
+ }
6831
+ void locate();
6832
+ return () => {
6833
+ cancelled = true;
6834
+ };
6835
+ }, [requestId, preference]);
6836
+ const isLocating = Boolean(requestId) && preference === "on" && locating;
6837
+ if (!request && preference === "unset") return null;
6838
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "location-consent", "aria-label": "Location sharing", children: [
6839
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "location-consent__copy", children: [
6840
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: isLocating ? "Getting your location\u2026" : request ? "Get better results" : "Precise location" }),
6841
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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" })
6842
+ ] }),
6843
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "location-consent__actions", children: [
6844
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6845
+ "button",
6846
+ {
6847
+ type: "button",
6848
+ role: "switch",
6849
+ className: "location-consent__toggle",
6850
+ "aria-label": "Share precise location",
6851
+ "aria-checked": preference === "on",
6852
+ "aria-busy": isLocating,
6853
+ disabled: !onRespond,
6854
+ onClick: () => {
6855
+ setError(null);
6856
+ setPreference(preference === "on" ? "off" : "on");
6857
+ },
6858
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", {})
6859
+ }
6860
+ ),
6861
+ request && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6862
+ "button",
6863
+ {
6864
+ type: "button",
6865
+ className: "location-consent__decline",
6866
+ disabled: !onRespond,
6867
+ onClick: () => {
6868
+ setError(null);
6869
+ setPreference("off");
6870
+ },
6871
+ children: "No thanks"
6872
+ }
6873
+ )
6874
+ ] }),
6875
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { role: "alert", className: "location-consent__error", children: error })
6876
+ ] });
6877
+ }
6878
+
6879
+ // src/react/components/CollectionResultCard/CollectionResultCard.tsx
6880
+ var import_jsx_runtime13 = require("react/jsx-runtime");
6158
6881
  function CollectionResultCard({
6159
6882
  result
6160
6883
  }) {
6161
6884
  const empty = result.items.length === 0;
6162
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6885
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
6163
6886
  "section",
6164
6887
  {
6165
6888
  className: `collection-result-card tool-result-card tool-result-card--${result.status}`,
6166
6889
  "aria-label": result.title,
6167
6890
  role: result.status === "completed" ? "status" : "alert",
6168
6891
  children: [
6169
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "tool-result-card__heading", children: [
6170
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6171
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: result.title })
6892
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "tool-result-card__heading", children: [
6893
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6894
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: result.title })
6172
6895
  ] }),
6173
- empty ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "collection-result-card__empty", children: "No matching record for the email you shared." }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: "collection-result-card__list", children: result.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("li", { children: [
6174
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "collection-result-card__item-title", children: item.title }),
6175
- item.description ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { children: item.description }) : null,
6176
- item.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("dl", { children: item.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
6177
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("dt", { children: detail.label }),
6178
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("dd", { children: detail.value })
6896
+ empty ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "collection-result-card__empty", children: "No matching record for the email you shared." }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("ul", { className: "collection-result-card__list", children: result.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("li", { children: [
6897
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "collection-result-card__item-title", children: item.title }),
6898
+ item.description ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: item.description }) : null,
6899
+ item.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dl", { children: item.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
6900
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dt", { children: detail.label }),
6901
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dd", { children: detail.value })
6179
6902
  ] }, `${detail.label}:${detail.value}`)) }) : null,
6180
- item.href ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
6903
+ item.href ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
6181
6904
  ] }, item.title)) })
6182
6905
  ]
6183
6906
  }
@@ -6185,23 +6908,23 @@ function CollectionResultCard({
6185
6908
  }
6186
6909
 
6187
6910
  // src/react/components/EntityResultCard/EntityResultCard.tsx
6188
- var import_jsx_runtime13 = require("react/jsx-runtime");
6911
+ var import_jsx_runtime14 = require("react/jsx-runtime");
6189
6912
  function EntityResultCard({
6190
6913
  result
6191
6914
  }) {
6192
6915
  const compact = !result.description && !result.details?.length && !result.links?.length;
6193
6916
  const collapsible = result.status === "completed" && Boolean(result.details?.length) && !result.links?.length;
6194
- const heading = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "tool-result-card__heading", children: [
6195
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6196
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: result.title })
6917
+ const heading = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "tool-result-card__heading", children: [
6918
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6919
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: result.title })
6197
6920
  ] });
6198
- const content = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
6199
- result.description ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: result.description }) : null,
6200
- result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
6201
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dt", { children: detail.label }),
6202
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dd", { children: detail.value })
6921
+ const content = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
6922
+ result.description ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { children: result.description }) : null,
6923
+ result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
6924
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("dt", { children: detail.label }),
6925
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("dd", { children: detail.value })
6203
6926
  ] }, `${detail.label}:${detail.value}`)) }) : null,
6204
- result.links?.length ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
6927
+ result.links?.length ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6205
6928
  "a",
6206
6929
  {
6207
6930
  href: link.href,
@@ -6213,12 +6936,12 @@ function EntityResultCard({
6213
6936
  )) }) : null
6214
6937
  ] });
6215
6938
  if (collapsible) {
6216
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("details", { className: "entity-result-card tool-result-card entity-result-card--disclosure", children: [
6217
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("summary", { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { role: "status", children: heading }) }),
6939
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("details", { className: "entity-result-card tool-result-card entity-result-card--disclosure", children: [
6940
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("summary", { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { role: "status", children: heading }) }),
6218
6941
  content
6219
6942
  ] });
6220
6943
  }
6221
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
6944
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
6222
6945
  "section",
6223
6946
  {
6224
6947
  className: `entity-result-card tool-result-card tool-result-card--${result.status}${compact ? " entity-result-card--compact" : ""}`,
@@ -6233,24 +6956,24 @@ function EntityResultCard({
6233
6956
  }
6234
6957
 
6235
6958
  // src/react/components/SignatureResultCard/SignatureResultCard.tsx
6236
- var import_jsx_runtime14 = require("react/jsx-runtime");
6959
+ var import_jsx_runtime15 = require("react/jsx-runtime");
6237
6960
  function SignatureResultCard({
6238
6961
  result
6239
6962
  }) {
6240
6963
  const primaryLink = result.links?.[0];
6241
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
6964
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
6242
6965
  "section",
6243
6966
  {
6244
6967
  className: `signature-result-card tool-result-card tool-result-card--${result.status}`,
6245
6968
  "aria-label": result.title,
6246
6969
  children: [
6247
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "tool-result-card__heading", children: [
6248
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6249
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: result.title })
6970
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-card__heading", children: [
6971
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6972
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { children: result.title })
6250
6973
  ] }),
6251
- result.statusLabel ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
6252
- result.description ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { children: result.description }) : null,
6253
- primaryLink ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6974
+ result.statusLabel ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
6975
+ result.description ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { children: result.description }) : null,
6976
+ primaryLink ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6254
6977
  "a",
6255
6978
  {
6256
6979
  className: "signature-result-card__cta",
@@ -6266,27 +6989,27 @@ function SignatureResultCard({
6266
6989
  }
6267
6990
 
6268
6991
  // src/react/components/ToolResultCard/ToolResultCard.tsx
6269
- var import_jsx_runtime15 = require("react/jsx-runtime");
6992
+ var import_jsx_runtime16 = require("react/jsx-runtime");
6270
6993
  function ToolResultCard({
6271
6994
  result
6272
6995
  }) {
6273
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
6996
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
6274
6997
  "section",
6275
6998
  {
6276
6999
  className: `tool-result-card tool-result-card--${result.status}`,
6277
7000
  "aria-label": result.title,
6278
7001
  role: result.status === "completed" ? "status" : "alert",
6279
7002
  children: [
6280
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-card__heading", children: [
6281
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
6282
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { children: result.title })
7003
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "tool-result-card__heading", children: [
7004
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
7005
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("strong", { children: result.title })
6283
7006
  ] }),
6284
- result.description ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { children: result.description }) : null,
6285
- result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
6286
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("dt", { children: detail.label }),
6287
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("dd", { children: detail.value })
7007
+ result.description ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { children: result.description }) : null,
7008
+ result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
7009
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: detail.label }),
7010
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: detail.value })
6288
7011
  ] }, `${detail.label}:${detail.value}`)) }) : null,
6289
- result.links?.length ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7012
+ result.links?.length ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6290
7013
  "a",
6291
7014
  {
6292
7015
  href: link.href,
@@ -6302,14 +7025,14 @@ function ToolResultCard({
6302
7025
  }
6303
7026
 
6304
7027
  // src/react/components/VisitorToolResultView/VisitorToolResultView.tsx
6305
- var import_jsx_runtime16 = require("react/jsx-runtime");
7028
+ var import_jsx_runtime17 = require("react/jsx-runtime");
6306
7029
  function VisitorToolResultView({
6307
7030
  disabled = false,
6308
7031
  onToolInput,
6309
7032
  result
6310
7033
  }) {
6311
7034
  if (result.kind === "input") {
6312
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7035
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6313
7036
  ToolInputCard,
6314
7037
  {
6315
7038
  disabled,
@@ -6319,16 +7042,16 @@ function VisitorToolResultView({
6319
7042
  );
6320
7043
  }
6321
7044
  if (result.kind === "entity") {
6322
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(EntityResultCard, { result });
7045
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(EntityResultCard, { result });
6323
7046
  }
6324
7047
  if (result.kind === "collection") {
6325
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(CollectionResultCard, { result });
7048
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CollectionResultCard, { result });
6326
7049
  }
6327
7050
  if (result.kind === "signature") {
6328
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SignatureResultCard, { result });
7051
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SignatureResultCard, { result });
6329
7052
  }
6330
7053
  if (result.kind === "summary") {
6331
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToolResultCard, { result });
7054
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ToolResultCard, { result });
6332
7055
  }
6333
7056
  return null;
6334
7057
  }
@@ -6337,9 +7060,9 @@ function isRenderableVisitorToolResult(result) {
6337
7060
  }
6338
7061
 
6339
7062
  // src/react/components/AgentRail/AgentRail.tsx
6340
- var import_jsx_runtime17 = require("react/jsx-runtime");
7063
+ var import_jsx_runtime18 = require("react/jsx-runtime");
6341
7064
  function MinimizeIcon() {
6342
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7065
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6343
7066
  "path",
6344
7067
  {
6345
7068
  d: "M3.5 8h9",
@@ -6350,7 +7073,7 @@ function MinimizeIcon() {
6350
7073
  ) });
6351
7074
  }
6352
7075
  function CloseIcon2() {
6353
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7076
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6354
7077
  "path",
6355
7078
  {
6356
7079
  d: "M4 4l8 8M12 4l-8 8",
@@ -6361,7 +7084,7 @@ function CloseIcon2() {
6361
7084
  ) });
6362
7085
  }
6363
7086
  function NewChatIcon() {
6364
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7087
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6365
7088
  "path",
6366
7089
  {
6367
7090
  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",
@@ -6373,7 +7096,7 @@ function NewChatIcon() {
6373
7096
  ) });
6374
7097
  }
6375
7098
  function ExpandIcon() {
6376
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7099
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6377
7100
  "path",
6378
7101
  {
6379
7102
  d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
@@ -6385,7 +7108,7 @@ function ExpandIcon() {
6385
7108
  ) });
6386
7109
  }
6387
7110
  function RestoreIcon() {
6388
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7111
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6389
7112
  "path",
6390
7113
  {
6391
7114
  d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
@@ -6397,7 +7120,7 @@ function RestoreIcon() {
6397
7120
  ) });
6398
7121
  }
6399
7122
  function ChevronDownIcon() {
6400
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7123
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6401
7124
  "path",
6402
7125
  {
6403
7126
  d: "M4 6.5l4 4 4-4",
@@ -6411,6 +7134,7 @@ function ChevronDownIcon() {
6411
7134
  var DEFAULT_DISCLAIMER_LABEL = "Agent can make mistakes. Check important info.";
6412
7135
  function AgentRail({
6413
7136
  state,
7137
+ handoff,
6414
7138
  theme,
6415
7139
  colorScheme = "auto",
6416
7140
  brandLabel = "",
@@ -6436,43 +7160,51 @@ function AgentRail({
6436
7160
  onInputResponse,
6437
7161
  onToolInput
6438
7162
  }) {
6439
- const railRef = (0, import_react14.useRef)(null);
6440
- const overlayRef = (0, import_react14.useRef)(null);
6441
- const transcriptRef = (0, import_react14.useRef)(null);
6442
- const responseRef = (0, import_react14.useRef)(null);
6443
- const threadRef = (0, import_react14.useRef)(null);
6444
- const lastScrolledVisitorIdRef = (0, import_react14.useRef)(void 0);
6445
- const pinnedToBottomRef = (0, import_react14.useRef)(true);
6446
- const smoothScrollToLatestRef = (0, import_react14.useRef)(false);
6447
- const lockedTranscriptScrollTopRef = (0, import_react14.useRef)(null);
6448
- const [showJumpToLatest, setShowJumpToLatest] = (0, import_react14.useState)(false);
6449
- const [receiptOpen, setReceiptOpen] = (0, import_react14.useState)(false);
7163
+ const railRef = (0, import_react16.useRef)(null);
7164
+ const overlayRef = (0, import_react16.useRef)(null);
7165
+ const transcriptRef = (0, import_react16.useRef)(null);
7166
+ const responseRef = (0, import_react16.useRef)(null);
7167
+ const threadRef = (0, import_react16.useRef)(null);
7168
+ const lastScrolledVisitorIdRef = (0, import_react16.useRef)(void 0);
7169
+ const pinnedToBottomRef = (0, import_react16.useRef)(true);
7170
+ const smoothScrollToLatestRef = (0, import_react16.useRef)(false);
7171
+ const lockedTranscriptScrollTopRef = (0, import_react16.useRef)(null);
7172
+ const [showJumpToLatest, setShowJumpToLatest] = (0, import_react16.useState)(false);
7173
+ const [receiptOpen, setReceiptOpen] = (0, import_react16.useState)(false);
6450
7174
  const resolvedBrandLabel = brandLabel.trim();
6451
7175
  const resolvedBrandLogoUrl = brandLogoUrl?.trim();
6452
7176
  const resolvedDisclaimerLabel = disclaimerLabel === void 0 ? DEFAULT_DISCLAIMER_LABEL : disclaimerLabel;
6453
- const [failedLogoUrl, setFailedLogoUrl] = (0, import_react14.useState)(null);
7177
+ const [failedLogoUrl, setFailedLogoUrl] = (0, import_react16.useState)(null);
6454
7178
  const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
6455
7179
  const resolvedColorScheme = useAgentColorScheme(colorScheme);
6456
7180
  const railStyle = agentThemeStyle(theme, resolvedColorScheme);
7181
+ const handoffActive = Boolean(handoff?.blocksAI);
7182
+ const handoffStatus = handoff?.page?.status;
7183
+ const locationRequest = handoffActive ? void 0 : state.pendingInputs?.find(isLocationConsentRequest);
6457
7184
  const pendingInputRequests = (state.pendingInputs ?? []).filter(
6458
- (request) => shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
7185
+ (request) => !handoffActive && !isLocationConsentRequest(request) && shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
6459
7186
  );
6460
- const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && pendingInputRequests.length > 0;
6461
- const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
7187
+ const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && (pendingInputRequests.length > 0 || Boolean(locationRequest));
7188
+ const semanticSurfaceDisabled = handoffActive || isBusy && state.phase !== "waiting-input";
6462
7189
  const visitorToolResults = (state.toolResults ?? []).filter(
6463
7190
  isRenderableVisitorToolResult
6464
7191
  );
6465
7192
  const activeVisitorToolInput = [...visitorToolResults].reverse().find((result) => result.kind === "input");
6466
7193
  const visibleVisitorToolResults = activeVisitorToolInput ? [activeVisitorToolInput] : visitorToolResults;
6467
- const activityActive = state.toolSteps.some((step) => step.state === "active");
7194
+ const activityActive = state.toolSteps.some(
7195
+ (step) => step.state === "active"
7196
+ );
6468
7197
  const showActivity = state.toolSteps.length > 0 && pendingInputRequests.length === 0 && !state.pendingOffer && (activityActive || isBusy && !state.streamingText);
6469
- const activitySteps = activityActive || !isBusy || state.streamingText ? state.toolSteps : [...state.toolSteps, {
6470
- id: "preparing-answer",
6471
- kind: "tool",
6472
- label: "Preparing your answer",
6473
- detail: "Preparing your answer",
6474
- state: "active"
6475
- }];
7198
+ const activitySteps = activityActive || !isBusy || state.streamingText ? state.toolSteps : [
7199
+ ...state.toolSteps,
7200
+ {
7201
+ id: "preparing-answer",
7202
+ kind: "tool",
7203
+ label: "Preparing your answer",
7204
+ detail: "Preparing your answer",
7205
+ state: "active"
7206
+ }
7207
+ ];
6476
7208
  const hasVisitorMessages2 = state.messages.some(
6477
7209
  (message) => message.role === "visitor"
6478
7210
  );
@@ -6501,7 +7233,7 @@ function AgentRail({
6501
7233
  }
6502
7234
  }
6503
7235
  const lastIsAgent = lastMessage?.role === "agent";
6504
- const showMessageActions = state.phase === "complete" && lastIsAgent && hasVisitorMessages2 && Boolean(hideToolCardFences(lastMessage.text).trim());
7236
+ const showMessageActions = !handoffActive && state.phase === "complete" && lastIsAgent && hasVisitorMessages2 && Boolean(hideToolCardFences(lastMessage.text).trim());
6505
7237
  const streamingMessage = state.streamingText && !lastIsAgent ? {
6506
7238
  createdAt: 0,
6507
7239
  id: "streaming-response",
@@ -6543,7 +7275,7 @@ function AgentRail({
6543
7275
  ...visibleVisitorToolResults
6544
7276
  ].reverse().find((result) => result.kind !== "input")?.id;
6545
7277
  const receiptSteps = answerReceipt && state.toolSteps.length > 0 ? state.toolSteps : void 0;
6546
- (0, import_react14.useEffect)(() => {
7278
+ (0, import_react16.useEffect)(() => {
6547
7279
  if (state.phase !== "complete") {
6548
7280
  setReceiptOpen(false);
6549
7281
  }
@@ -6565,7 +7297,7 @@ function AgentRail({
6565
7297
  onFollowUpSelect?.(label);
6566
7298
  }
6567
7299
  const latestVisitorId = visibleMessages[lastVisitorIndex]?.id;
6568
- (0, import_react14.useEffect)(() => {
7300
+ (0, import_react16.useEffect)(() => {
6569
7301
  const node = transcriptRef.current;
6570
7302
  if (!node) return;
6571
7303
  if (latestVisitorId !== lastScrolledVisitorIdRef.current) {
@@ -6595,7 +7327,7 @@ function AgentRail({
6595
7327
  state.followUps,
6596
7328
  state.journey
6597
7329
  ]);
6598
- (0, import_react14.useEffect)(() => {
7330
+ (0, import_react16.useEffect)(() => {
6599
7331
  const node = transcriptRef.current;
6600
7332
  if (!node) return;
6601
7333
  const handleScroll = () => {
@@ -6609,7 +7341,7 @@ function AgentRail({
6609
7341
  handleScroll();
6610
7342
  return () => node.removeEventListener("scroll", handleScroll);
6611
7343
  }, []);
6612
- (0, import_react14.useEffect)(() => {
7344
+ (0, import_react16.useEffect)(() => {
6613
7345
  if (!receiptOpen) {
6614
7346
  lockedTranscriptScrollTopRef.current = null;
6615
7347
  return;
@@ -6647,7 +7379,7 @@ function AgentRail({
6647
7379
  window.setTimeout(settle, 900);
6648
7380
  node.scrollTo({ top: node.scrollHeight, behavior: "smooth" });
6649
7381
  }
6650
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7382
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6651
7383
  "aside",
6652
7384
  {
6653
7385
  ref: railRef,
@@ -6661,244 +7393,305 @@ function AgentRail({
6661
7393
  autoFocus: mobileFullscreen || expanded,
6662
7394
  role: mobileFullscreen || expanded ? "dialog" : void 0,
6663
7395
  tabIndex: mobileFullscreen || expanded ? -1 : void 0,
6664
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6665
- AgentRailOverlayContext.Provider,
6666
- {
6667
- value: { railRef, overlayRef },
6668
- children: [
6669
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "agent-rail__surface", inert: receiptOpen || void 0, children: [
6670
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "agent-rail__brand-row", children: [
6671
- onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6672
- "button",
6673
- {
6674
- type: "button",
6675
- className: "agent-rail__collapse",
6676
- "aria-label": "Collapse assist",
6677
- onClick: onCollapse,
6678
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(MinimizeIcon, {})
6679
- }
6680
- ) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6681
- "button",
6682
- {
6683
- type: "button",
6684
- className: "agent-rail__close",
6685
- "aria-label": "Close agent",
6686
- onClick: onClose,
6687
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CloseIcon2, {})
7396
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(AgentRailOverlayContext.Provider, { value: { railRef, overlayRef }, children: [
7397
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "agent-rail__surface", inert: receiptOpen || void 0, children: [
7398
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "agent-rail__brand-row", children: [
7399
+ onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7400
+ "button",
7401
+ {
7402
+ type: "button",
7403
+ className: "agent-rail__collapse",
7404
+ "aria-label": "Collapse assist",
7405
+ onClick: onCollapse,
7406
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MinimizeIcon, {})
7407
+ }
7408
+ ) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7409
+ "button",
7410
+ {
7411
+ type: "button",
7412
+ className: "agent-rail__close",
7413
+ "aria-label": "Close agent",
7414
+ onClick: onClose,
7415
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CloseIcon2, {})
7416
+ }
7417
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
7418
+ resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "agent-rail__identity", children: [
7419
+ showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7420
+ "img",
7421
+ {
7422
+ className: "agent-rail__brand-logo",
7423
+ src: resolvedBrandLogoUrl,
7424
+ alt: "",
7425
+ onError: () => {
7426
+ setFailedLogoUrl(resolvedBrandLogoUrl ?? null);
6688
7427
  }
6689
- ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
6690
- resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "agent-rail__identity", children: [
6691
- showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6692
- "img",
6693
- {
6694
- className: "agent-rail__brand-logo",
6695
- src: resolvedBrandLogoUrl,
6696
- alt: "",
6697
- onError: () => {
6698
- setFailedLogoUrl(resolvedBrandLogoUrl ?? null);
6699
- }
6700
- }
6701
- ) }) : null,
6702
- resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
6703
- ] }) : null,
6704
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "agent-rail__actions", children: [
6705
- onReset ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6706
- "button",
6707
- {
6708
- type: "button",
6709
- className: "agent-rail__new-chat",
6710
- "aria-label": "Start a new conversation",
6711
- disabled: !hasVisitorMessages2,
6712
- onClick: handleReset,
6713
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(NewChatIcon, {})
6714
- }
6715
- ) : null,
6716
- onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6717
- "button",
7428
+ }
7429
+ ) }) : null,
7430
+ resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
7431
+ ] }) : null,
7432
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "agent-rail__actions", children: [
7433
+ onReset ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7434
+ "button",
7435
+ {
7436
+ type: "button",
7437
+ className: "agent-rail__new-chat",
7438
+ "aria-label": "Start a new conversation",
7439
+ disabled: !hasVisitorMessages2 || handoffActive && !handoff?.canReset,
7440
+ onClick: handleReset,
7441
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NewChatIcon, {})
7442
+ }
7443
+ ) : null,
7444
+ onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7445
+ "button",
7446
+ {
7447
+ type: "button",
7448
+ className: "agent-rail__expand",
7449
+ "aria-label": expanded ? "Exit full screen" : "Open full screen",
7450
+ onClick: onExpandToggle,
7451
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ExpandIcon, {})
7452
+ }
7453
+ ) : null
7454
+ ] })
7455
+ ] }) }),
7456
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { ref: threadRef, className: "agent-rail__thread", children: [
7457
+ !hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
7458
+ greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7459
+ MessageBubble,
7460
+ {
7461
+ showProvenance: Boolean(
7462
+ handoff?.page?.enabled || handoffActive
7463
+ ),
7464
+ message: greeting,
7465
+ bookingDisabled: isBusy || handoffActive,
7466
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
7467
+ onBook
7468
+ }
7469
+ ) : null,
7470
+ showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7471
+ FollowUpChips,
7472
+ {
7473
+ suggestions: state.followUps,
7474
+ disabled: isBusy || handoffActive,
7475
+ label: "Start here",
7476
+ onSelect: (suggestion) => handleFollowUpSelect(suggestion.label)
7477
+ }
7478
+ ) }) : null,
7479
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7480
+ AgentActivityBubble,
7481
+ {
7482
+ brandLabel: resolvedBrandLabel,
7483
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
7484
+ failed: state.phase === "error",
7485
+ steps: activitySteps
7486
+ }
7487
+ ) : null,
7488
+ visibleVisitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7489
+ VisitorToolResultView,
7490
+ {
7491
+ result,
7492
+ disabled: semanticSurfaceDisabled,
7493
+ onToolInput
7494
+ },
7495
+ result.id
7496
+ )),
7497
+ pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7498
+ HumanInputCard,
7499
+ {
7500
+ request,
7501
+ onRespond: onInputResponse
7502
+ },
7503
+ request.requestId
7504
+ ))
7505
+ ] }) : null,
7506
+ transcriptMessages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
7507
+ "div",
7508
+ {
7509
+ ref: index === lastVisitorIndex + 1 ? responseRef : void 0,
7510
+ className: "agent-rail__turn-block",
7511
+ children: [
7512
+ message.role === "agent" ? message.toolResults?.map((result) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7513
+ VisitorToolResultView,
6718
7514
  {
6719
- type: "button",
6720
- className: "agent-rail__expand",
6721
- "aria-label": expanded ? "Exit full screen" : "Open full screen",
6722
- onClick: onExpandToggle,
6723
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ExpandIcon, {})
6724
- }
6725
- ) : null
6726
- ] })
6727
- ] }) }),
6728
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: threadRef, className: "agent-rail__thread", children: [
6729
- !hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
6730
- greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7515
+ result
7516
+ },
7517
+ result.id
7518
+ )) : null,
7519
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6731
7520
  MessageBubble,
6732
7521
  {
6733
- message: greeting,
6734
- bookingDisabled: isBusy,
7522
+ showProvenance: Boolean(
7523
+ handoff?.page?.enabled || handoffActive
7524
+ ),
7525
+ message,
7526
+ bookingDisabled: isBusy || handoffActive,
6735
7527
  brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
7528
+ offer: index === transcriptMessages.length - 1 ? state.pendingOffer : void 0,
6736
7529
  onBook
6737
7530
  }
6738
- ) : null,
6739
- showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6740
- FollowUpChips,
6741
- {
6742
- suggestions: state.followUps,
6743
- disabled: isBusy,
6744
- label: "Start here",
6745
- onSelect: (suggestion) => handleFollowUpSelect(suggestion.label)
6746
- }
6747
- ) }) : null,
6748
- showActivity ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6749
- AgentActivityBubble,
7531
+ ),
7532
+ index === lastAgentIndex && showMessageActions && message.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7533
+ MessageActions,
6750
7534
  {
6751
- brandLabel: resolvedBrandLabel,
6752
- brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
6753
- failed: state.phase === "error",
6754
- steps: activitySteps
7535
+ answeredAt: message.createdAt,
7536
+ copyText: hideToolCardFences(message.text).trim() || message.text,
7537
+ readAloud,
7538
+ receiptSteps,
7539
+ onOpenReceipt: receiptSteps ? openReceipt : void 0,
7540
+ onRegenerate: onRegenerate ? handleRegenerate : void 0,
7541
+ onFeedback: onFeedback ? (rating) => onFeedback(rating, message) : void 0
6755
7542
  }
6756
7543
  ) : null,
6757
- visibleVisitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6758
- VisitorToolResultView,
6759
- {
6760
- result,
6761
- disabled: semanticSurfaceDisabled,
6762
- onToolInput
6763
- },
6764
- result.id
6765
- )),
6766
- pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6767
- HumanInputCard,
6768
- {
6769
- request,
6770
- onRespond: onInputResponse
6771
- },
6772
- request.requestId
6773
- ))
6774
- ] }) : null,
6775
- transcriptMessages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6776
- "div",
6777
- {
6778
- ref: index === lastVisitorIndex + 1 ? responseRef : void 0,
6779
- className: "agent-rail__turn-block",
6780
- children: [
6781
- message.role === "agent" ? message.toolResults?.map((result) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VisitorToolResultView, { result }, result.id)) : null,
6782
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6783
- MessageBubble,
6784
- {
6785
- message,
6786
- bookingDisabled: isBusy,
6787
- brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
6788
- offer: index === transcriptMessages.length - 1 ? state.pendingOffer : void 0,
6789
- onBook
6790
- }
6791
- ),
6792
- index === lastAgentIndex && showMessageActions && message.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6793
- MessageActions,
6794
- {
6795
- answeredAt: message.createdAt,
6796
- copyText: hideToolCardFences(message.text).trim() || message.text,
6797
- readAloud,
6798
- receiptSteps,
6799
- onOpenReceipt: receiptSteps ? openReceipt : void 0,
6800
- onRegenerate: onRegenerate ? handleRegenerate : void 0,
6801
- onFeedback: onFeedback ? (rating) => onFeedback(rating, message) : void 0
6802
- }
6803
- ) : null,
6804
- index === lastVisitorIndex ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6805
- showActivity ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6806
- AgentActivityBubble,
6807
- {
6808
- brandLabel: resolvedBrandLabel,
6809
- brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
6810
- failed: state.phase === "error",
6811
- steps: activitySteps
6812
- }
6813
- ) : null,
6814
- visibleVisitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6815
- VisitorToolResultView,
6816
- {
6817
- result,
6818
- disabled: semanticSurfaceDisabled,
6819
- onToolInput
6820
- },
6821
- result.id
6822
- )),
6823
- pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6824
- HumanInputCard,
6825
- {
6826
- request,
6827
- onRespond: onInputResponse
6828
- },
6829
- request.requestId
6830
- ))
6831
- ] }) : null
6832
- ]
6833
- },
6834
- message.role === "agent" && transcriptMessages[index - 1]?.role === "visitor" ? `answer-${transcriptMessages[index - 1]?.id}` : message.id
6835
- )),
6836
- waitingForBooking ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(BookingCardLoader, {}) : null,
6837
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
6838
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
6839
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "Something went wrong" }),
6840
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { children: state.error })
6841
- ] }),
6842
- onRetry ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
6843
- ] }) : null
6844
- ] }) }),
6845
- showDisclaimerLabel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { children: disclaimerLink ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6846
- "a",
7544
+ index === lastVisitorIndex ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
7545
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7546
+ AgentActivityBubble,
7547
+ {
7548
+ brandLabel: resolvedBrandLabel,
7549
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
7550
+ failed: state.phase === "error",
7551
+ steps: activitySteps
7552
+ }
7553
+ ) : null,
7554
+ visibleVisitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7555
+ VisitorToolResultView,
7556
+ {
7557
+ result,
7558
+ disabled: semanticSurfaceDisabled,
7559
+ onToolInput
7560
+ },
7561
+ result.id
7562
+ )),
7563
+ pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7564
+ HumanInputCard,
7565
+ {
7566
+ request,
7567
+ onRespond: onInputResponse
7568
+ },
7569
+ request.requestId
7570
+ ))
7571
+ ] }) : null
7572
+ ]
7573
+ },
7574
+ message.role === "agent" && transcriptMessages[index - 1]?.role === "visitor" ? `answer-${transcriptMessages[index - 1]?.id}` : message.id
7575
+ )),
7576
+ waitingForBooking ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(BookingCardLoader, {}) : null,
7577
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
7578
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
7579
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("strong", { children: "Something went wrong" }),
7580
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { children: state.error })
7581
+ ] }),
7582
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
7583
+ ] }) : null
7584
+ ] }) }),
7585
+ showDisclaimerLabel ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { children: disclaimerLink ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7586
+ "a",
7587
+ {
7588
+ href: disclaimerLink,
7589
+ rel: "noopener noreferrer",
7590
+ target: "_blank",
7591
+ children: resolvedDisclaimerLabel
7592
+ }
7593
+ ) : resolvedDisclaimerLabel }) }) : null,
7594
+ handoff && (handoff.page?.enabled || handoffActive) ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "agent-rail__handoff", children: [
7595
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("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?" }),
7596
+ handoffStatus === "ai" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7597
+ "button",
7598
+ {
7599
+ type: "button",
7600
+ disabled: handoff.busy || handoff.hasPending || isBusy,
7601
+ onClick: () => void handoff.start(),
7602
+ children: "Talk to a person"
7603
+ }
7604
+ ) : null,
7605
+ handoffStatus === "resolved" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7606
+ "button",
7607
+ {
7608
+ type: "button",
7609
+ disabled: handoff.busy || handoff.hasPending,
7610
+ onClick: () => void handoff.returnToAI(),
7611
+ children: "Return to AI"
7612
+ }
7613
+ ) : null,
7614
+ handoffStatus === "failed" && onReset ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7615
+ "button",
7616
+ {
7617
+ type: "button",
7618
+ disabled: !handoff.canReset,
7619
+ onClick: handleReset,
7620
+ children: "Start a new conversation"
7621
+ }
7622
+ ) : null,
7623
+ handoff.error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { role: "alert", children: [
7624
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: handoff.error }),
7625
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7626
+ "button",
6847
7627
  {
6848
- href: disclaimerLink,
6849
- rel: "noopener noreferrer",
6850
- target: "_blank",
6851
- children: resolvedDisclaimerLabel
7628
+ type: "button",
7629
+ disabled: handoff.busy,
7630
+ onClick: () => void handoff.retry(),
7631
+ children: "Retry"
6852
7632
  }
6853
- ) : resolvedDisclaimerLabel }) }) : null,
6854
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
6855
- showJumpToLatest ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6856
- "button",
6857
- {
6858
- type: "button",
6859
- className: "agent-rail__jump-to-latest",
6860
- "aria-label": "Jump to the latest message",
6861
- title: "Jump to the latest message",
6862
- onClick: scrollToLatest,
6863
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ChevronDownIcon, {})
6864
- }
6865
- ) : null,
6866
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6867
- Composer,
6868
- {
6869
- variant: expanded || mobileFullscreen ? "dock" : "default",
6870
- disabled: isBusy,
6871
- form: composerForm && lastMessage ? { ...composerForm, id: `${lastMessage.id}:${composerForm.id}` } : null,
6872
- allowFormResume: !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
6873
- placeholder: composerPlaceholder,
6874
- onSubmit: handleSubmit
6875
- },
6876
- `${state.messages.find((message) => message.role === "visitor")?.id ?? "empty"}:${latestResultId ?? ""}`
6877
- ),
6878
- poweredByLabel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: poweredByLabel }) }) }) : null
6879
- ] })
6880
- ] }),
6881
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: overlayRef, className: "agent-rail__overlay" }),
6882
- receiptOpen && receiptSteps ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6883
- AnswerReceiptDialog,
7633
+ )
7634
+ ] }) : null
7635
+ ] }) : null,
7636
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
7637
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7638
+ LocationConsent,
7639
+ {
7640
+ request: locationRequest,
7641
+ onRespond: onInputResponse
7642
+ },
7643
+ state.messages.find((message) => message.role === "visitor")?.id ?? "empty"
7644
+ ),
7645
+ showJumpToLatest ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7646
+ "button",
6884
7647
  {
6885
- brandLabel: resolvedBrandLabel,
6886
- onClose: () => setReceiptOpen(false),
6887
- steps: receiptSteps
7648
+ type: "button",
7649
+ className: "agent-rail__jump-to-latest",
7650
+ "aria-label": "Jump to the latest message",
7651
+ title: "Jump to the latest message",
7652
+ onClick: scrollToLatest,
7653
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronDownIcon, {})
6888
7654
  }
6889
- ) : null
6890
- ]
6891
- }
6892
- )
7655
+ ) : null,
7656
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7657
+ Composer,
7658
+ {
7659
+ variant: expanded || mobileFullscreen ? "dock" : "default",
7660
+ disabled: isBusy || Boolean(handoff?.busy || handoff?.hasPending) || handoffActive && !["requesting", "queued", "human"].includes(
7661
+ handoffStatus ?? ""
7662
+ ),
7663
+ form: !handoffActive && composerForm && lastMessage ? {
7664
+ ...composerForm,
7665
+ id: `${lastMessage.id}:${composerForm.id}`
7666
+ } : null,
7667
+ allowFormResume: !handoffActive && !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
7668
+ placeholder: handoffActive ? "Message support\u2026" : composerPlaceholder,
7669
+ onSubmit: handleSubmit
7670
+ },
7671
+ `${state.messages.find((message) => message.role === "visitor")?.id ?? "empty"}:${latestResultId ?? ""}`
7672
+ ),
7673
+ poweredByLabel ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: poweredByLabel }) }) }) : null
7674
+ ] })
7675
+ ] }),
7676
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: overlayRef, className: "agent-rail__overlay" }),
7677
+ receiptOpen && receiptSteps ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7678
+ AnswerReceiptDialog,
7679
+ {
7680
+ brandLabel: resolvedBrandLabel,
7681
+ onClose: () => setReceiptOpen(false),
7682
+ steps: receiptSteps
7683
+ }
7684
+ ) : null
7685
+ ] })
6893
7686
  }
6894
7687
  );
6895
7688
  }
6896
7689
 
6897
7690
  // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
6898
- var import_react15 = require("react");
6899
- var import_jsx_runtime18 = require("react/jsx-runtime");
7691
+ var import_react17 = require("react");
7692
+ var import_jsx_runtime19 = require("react/jsx-runtime");
6900
7693
  function ChatSparkIcon() {
6901
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
7694
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
6902
7695
  "svg",
6903
7696
  {
6904
7697
  className: "assist-edge-tab__chat-spark",
@@ -6906,7 +7699,7 @@ function ChatSparkIcon() {
6906
7699
  fill: "none",
6907
7700
  "aria-hidden": "true",
6908
7701
  children: [
6909
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7702
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6910
7703
  "path",
6911
7704
  {
6912
7705
  className: "assist-edge-tab__spark assist-edge-tab__spark--a",
@@ -6914,7 +7707,7 @@ function ChatSparkIcon() {
6914
7707
  fill: "currentColor"
6915
7708
  }
6916
7709
  ),
6917
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7710
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6918
7711
  "path",
6919
7712
  {
6920
7713
  className: "assist-edge-tab__spark assist-edge-tab__spark--b",
@@ -6922,8 +7715,8 @@ function ChatSparkIcon() {
6922
7715
  fill: "currentColor"
6923
7716
  }
6924
7717
  ),
6925
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("g", { className: "assist-edge-tab__bot", children: [
6926
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7718
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("g", { className: "assist-edge-tab__bot", children: [
7719
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6927
7720
  "path",
6928
7721
  {
6929
7722
  d: "M11.15 5.2V3.05",
@@ -6932,8 +7725,8 @@ function ChatSparkIcon() {
6932
7725
  strokeLinecap: "round"
6933
7726
  }
6934
7727
  ),
6935
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: "11.15", cy: "2.45", r: "0.85", fill: "currentColor" }),
6936
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7728
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("circle", { cx: "11.15", cy: "2.45", r: "0.85", fill: "currentColor" }),
7729
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6937
7730
  "rect",
6938
7731
  {
6939
7732
  x: "3.55",
@@ -6945,7 +7738,7 @@ function ChatSparkIcon() {
6945
7738
  strokeWidth: "1.9"
6946
7739
  }
6947
7740
  ),
6948
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7741
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6949
7742
  "circle",
6950
7743
  {
6951
7744
  className: "assist-edge-tab__eye",
@@ -6955,7 +7748,7 @@ function ChatSparkIcon() {
6955
7748
  fill: "currentColor"
6956
7749
  }
6957
7750
  ),
6958
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7751
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6959
7752
  "circle",
6960
7753
  {
6961
7754
  className: "assist-edge-tab__eye assist-edge-tab__eye--r",
@@ -6976,10 +7769,10 @@ function TabMarkIcon({
6976
7769
  }) {
6977
7770
  const custom = customIconUrl?.trim();
6978
7771
  const logo = logoUrl?.trim();
6979
- const [customFailed, setCustomFailed] = (0, import_react15.useState)(false);
6980
- const [logoFailed, setLogoFailed] = (0, import_react15.useState)(false);
7772
+ const [customFailed, setCustomFailed] = (0, import_react17.useState)(false);
7773
+ const [logoFailed, setLogoFailed] = (0, import_react17.useState)(false);
6981
7774
  if (custom && !customFailed) {
6982
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7775
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6983
7776
  "img",
6984
7777
  {
6985
7778
  alt: "",
@@ -6991,7 +7784,7 @@ function TabMarkIcon({
6991
7784
  );
6992
7785
  }
6993
7786
  if (logo && !logoFailed) {
6994
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7787
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6995
7788
  "img",
6996
7789
  {
6997
7790
  alt: "",
@@ -7002,10 +7795,10 @@ function TabMarkIcon({
7002
7795
  }
7003
7796
  );
7004
7797
  }
7005
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatSparkIcon, {});
7798
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatSparkIcon, {});
7006
7799
  }
7007
7800
  function ChevronLeftIcon() {
7008
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7801
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7009
7802
  "path",
7010
7803
  {
7011
7804
  d: "M10 4L6 8l4 4",
@@ -7017,7 +7810,7 @@ function ChevronLeftIcon() {
7017
7810
  ) });
7018
7811
  }
7019
7812
  function ChevronDownIcon2() {
7020
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7813
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7021
7814
  "path",
7022
7815
  {
7023
7816
  d: "M4 6l4 4 4-4",
@@ -7029,7 +7822,7 @@ function ChevronDownIcon2() {
7029
7822
  ) });
7030
7823
  }
7031
7824
  function DragDots() {
7032
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("i", {}, index)) });
7825
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("i", {}, index)) });
7033
7826
  }
7034
7827
  var VARIANT_COPY = {
7035
7828
  outline: { label: "Ask anything", aria: "Ask anything" },
@@ -7084,7 +7877,7 @@ function AssistEdgeTab({
7084
7877
  ...!pill && resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
7085
7878
  colorScheme: chromeScheme
7086
7879
  };
7087
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
7880
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
7088
7881
  "button",
7089
7882
  {
7090
7883
  type: "button",
@@ -7096,13 +7889,13 @@ function AssistEdgeTab({
7096
7889
  tabIndex: visible ? 0 : -1,
7097
7890
  onClick: onOpen,
7098
7891
  children: [
7099
- pill ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
7100
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7892
+ pill ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
7893
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7101
7894
  "span",
7102
7895
  {
7103
7896
  className: `assist-edge-tab__mark assist-edge-tab__mark--chip${mobile ? " assist-edge-tab__mark--mobile" : ""}`,
7104
7897
  "aria-hidden": "true",
7105
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7898
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7106
7899
  TabMarkIcon,
7107
7900
  {
7108
7901
  customIconUrl,
@@ -7112,12 +7905,12 @@ function AssistEdgeTab({
7112
7905
  )
7113
7906
  }
7114
7907
  ),
7115
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "assist-edge-tab__text", children: [
7116
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7117
- visibleSubLabel ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__sublabel", children: visibleSubLabel }) : null
7908
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "assist-edge-tab__text", children: [
7909
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7910
+ visibleSubLabel ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "assist-edge-tab__sublabel", children: visibleSubLabel }) : null
7118
7911
  ] })
7119
- ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
7120
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7912
+ ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
7913
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7121
7914
  TabMarkIcon,
7122
7915
  {
7123
7916
  customIconUrl,
@@ -7125,16 +7918,16 @@ function AssistEdgeTab({
7125
7918
  },
7126
7919
  markSourceKey
7127
7920
  ) }),
7128
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7129
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronDownIcon2, {})
7921
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7922
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChevronDownIcon2, {})
7130
7923
  ] }) : null,
7131
- !pill && variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
7132
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronLeftIcon, {}),
7133
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7134
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DragDots, {})
7924
+ !pill && variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
7925
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChevronLeftIcon, {}),
7926
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7927
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DragDots, {})
7135
7928
  ] }) : null,
7136
- !pill && variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
7137
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7929
+ !pill && variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
7930
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7138
7931
  TabMarkIcon,
7139
7932
  {
7140
7933
  customIconUrl,
@@ -7142,8 +7935,8 @@ function AssistEdgeTab({
7142
7935
  },
7143
7936
  markSourceKey
7144
7937
  ) }),
7145
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7146
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronLeftIcon, {})
7938
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7939
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChevronLeftIcon, {})
7147
7940
  ] }) : null
7148
7941
  ]
7149
7942
  }
@@ -7165,8 +7958,8 @@ function findPrecedingVisitorText(messages, agentMessageId) {
7165
7958
  }
7166
7959
  return void 0;
7167
7960
  }
7168
- function normalizeAgentFeedbackAnswerText(text2) {
7169
- const normalized = text2.replace(/\s+/g, " ").trim();
7961
+ function normalizeAgentFeedbackAnswerText(text3) {
7962
+ const normalized = text3.replace(/\s+/g, " ").trim();
7170
7963
  if (!normalized) return void 0;
7171
7964
  return normalized.length > 4e3 ? `${normalized.slice(0, 3997)}...` : normalized;
7172
7965
  }
@@ -7207,7 +8000,7 @@ function sendAgentAnswerFeedback(eventUrl, event) {
7207
8000
  }
7208
8001
 
7209
8002
  // src/react/components/AgentWidget/AgentWidget.tsx
7210
- var import_jsx_runtime19 = require("react/jsx-runtime");
8003
+ var import_jsx_runtime20 = require("react/jsx-runtime");
7211
8004
  function AgentWidget({
7212
8005
  indexId,
7213
8006
  customerId,
@@ -7226,9 +8019,9 @@ function AgentWidget({
7226
8019
  }) {
7227
8020
  const isMobile = useIsMobile();
7228
8021
  const placement = normalizeAgentPlacement(placementInput);
7229
- const railSlotRef = (0, import_react16.useRef)(null);
7230
- const [railCollapsed, setRailCollapsed] = (0, import_react16.useState)(defaultCollapsed);
7231
- const [railExpanded, setRailExpanded] = (0, import_react16.useState)(false);
8022
+ const railSlotRef = (0, import_react18.useRef)(null);
8023
+ const [railCollapsed, setRailCollapsed] = (0, import_react18.useState)(defaultCollapsed);
8024
+ const [railExpanded, setRailExpanded] = (0, import_react18.useState)(false);
7232
8025
  const pageShiftActive = shouldApplyPageShift({
7233
8026
  pageShift,
7234
8027
  isMobile,
@@ -7281,7 +8074,7 @@ function AgentWidget({
7281
8074
  } : {},
7282
8075
  ...branding?.colors?.border ? { border: branding.colors.border } : {}
7283
8076
  };
7284
- (0, import_react16.useEffect)(() => {
8077
+ (0, import_react18.useEffect)(() => {
7285
8078
  if (!registerPanelController) return;
7286
8079
  registerAgentPanelController(customerId, {
7287
8080
  open: () => setRailCollapsed(false),
@@ -7316,7 +8109,7 @@ function AgentWidget({
7316
8109
  })
7317
8110
  );
7318
8111
  }
7319
- (0, import_react16.useEffect)(() => {
8112
+ (0, import_react18.useEffect)(() => {
7320
8113
  if (railCollapsed) return;
7321
8114
  const handleKeyDown = (event) => {
7322
8115
  if (event.key === "Tab" && (isMobile || railExpanded)) {
@@ -7350,19 +8143,19 @@ function AgentWidget({
7350
8143
  window.addEventListener("keydown", handleKeyDown);
7351
8144
  return () => window.removeEventListener("keydown", handleKeyDown);
7352
8145
  }, [isMobile, railCollapsed, railExpanded]);
7353
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "webless-agent-root", children: [
7354
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8146
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "webless-agent-root", children: [
8147
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
7355
8148
  "div",
7356
8149
  {
7357
8150
  className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
7358
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8151
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
7359
8152
  "div",
7360
8153
  {
7361
8154
  ref: railSlotRef,
7362
8155
  className: "webless-agent-root__rail-slot",
7363
8156
  inert: railCollapsed || void 0,
7364
8157
  "aria-hidden": railCollapsed,
7365
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8158
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
7366
8159
  AgentRail,
7367
8160
  {
7368
8161
  theme,
@@ -7399,7 +8192,7 @@ function AgentWidget({
7399
8192
  )
7400
8193
  }
7401
8194
  ),
7402
- railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8195
+ railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
7403
8196
  AssistEdgeTab,
7404
8197
  {
7405
8198
  variant: placement.variant,
@@ -7439,12 +8232,12 @@ function readUnpublishedPreviewBuildId(href) {
7439
8232
  }
7440
8233
 
7441
8234
  // src/embed/AgentWidget.tsx
7442
- var import_jsx_runtime20 = require("react/jsx-runtime");
8235
+ var import_jsx_runtime21 = require("react/jsx-runtime");
7443
8236
  function AgentWidget2({
7444
8237
  manifest
7445
8238
  }) {
7446
8239
  const defaultCollapsed = manifest.version !== "unpublished";
7447
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
8240
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7448
8241
  AgentWidget,
7449
8242
  {
7450
8243
  indexId: manifest.indexId,
@@ -7569,7 +8362,7 @@ function normalizeAgentBranding(branding) {
7569
8362
  }
7570
8363
 
7571
8364
  // src/embed/mount.tsx
7572
- var import_jsx_runtime21 = require("react/jsx-runtime");
8365
+ var import_jsx_runtime22 = require("react/jsx-runtime");
7573
8366
  var mountedHandles = /* @__PURE__ */ new Map();
7574
8367
  var latestCustomerId = null;
7575
8368
  function resolveMountHost(manifest, script) {
@@ -7598,8 +8391,8 @@ function mountAgent(input) {
7598
8391
  const mountTarget = resolveMountHost(manifest, input.script ?? null);
7599
8392
  const host = createHost(manifest.customerId);
7600
8393
  mountTarget.append(host);
7601
- const root = (0, import_client5.createRoot)(host);
7602
- root.render(/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AgentWidget2, { manifest }));
8394
+ const root = (0, import_client7.createRoot)(host);
8395
+ root.render(/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AgentWidget2, { manifest }));
7603
8396
  const handle = {
7604
8397
  customerId: manifest.customerId,
7605
8398
  manifest,
@@ -7625,9 +8418,9 @@ function mountAgent(input) {
7625
8418
  return handle;
7626
8419
  }
7627
8420
  function unmountAgent(customerId) {
7628
- const id = customerId ?? latestCustomerId;
7629
- if (!id) return false;
7630
- const handle = mountedHandles.get(id);
8421
+ const id2 = customerId ?? latestCustomerId;
8422
+ if (!id2) return false;
8423
+ const handle = mountedHandles.get(id2);
7631
8424
  if (!handle) return false;
7632
8425
  handle.unmount();
7633
8426
  return true;