@webless/agent 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-NW37MHU7.js → chunk-EVWZF34F.js} +1103 -477
- package/dist/chunk-EVWZF34F.js.map +1 -0
- package/dist/embed.cjs +1132 -506
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +64 -0
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +3 -2
- package/dist/embed.d.ts +3 -2
- package/dist/embed.js +1 -1
- package/dist/index.cjs +283 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +277 -6
- package/dist/index.js.map +1 -1
- package/dist/{panel-controller-rxR75tRa.d.cts → panel-controller-CYoivbYB.d.cts} +173 -1
- package/dist/{panel-controller-rxR75tRa.d.ts → panel-controller-CYoivbYB.d.ts} +173 -1
- package/dist/presentation.d.cts +2 -1
- package/dist/presentation.d.ts +2 -1
- package/dist/react.cjs +1131 -505
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +64 -0
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +126 -4
- package/dist/react.d.ts +126 -4
- package/dist/react.js +1 -1
- package/dist/{types-B2j6hbI5.d.cts → types-4lTJW5mp.d.cts} +133 -1
- package/dist/{types-B2j6hbI5.d.ts → types-4lTJW5mp.d.ts} +133 -1
- package/package.json +3 -2
- package/scripts/sync-handoff-contract.mjs +18 -0
- package/dist/chunk-NW37MHU7.js.map +0 -1
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
|
|
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
|
|
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/
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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",
|
|
@@ -621,7 +976,7 @@ async function withCapabilityRefresh(capability, request) {
|
|
|
621
976
|
try {
|
|
622
977
|
return await request();
|
|
623
978
|
} catch (error) {
|
|
624
|
-
if (!(error instanceof
|
|
979
|
+
if (!(error instanceof import_client2.ClientError) || error.status !== 401) {
|
|
625
980
|
throw error;
|
|
626
981
|
}
|
|
627
982
|
capability.invalidate();
|
|
@@ -629,6 +984,75 @@ async function withCapabilityRefresh(capability, request) {
|
|
|
629
984
|
}
|
|
630
985
|
}
|
|
631
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
|
+
|
|
632
1056
|
// src/runtime/config.ts
|
|
633
1057
|
var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
|
|
634
1058
|
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
@@ -1219,6 +1643,11 @@ var AgentSession = class {
|
|
|
1219
1643
|
version,
|
|
1220
1644
|
visitorSessionId
|
|
1221
1645
|
});
|
|
1646
|
+
this.handoff = createHandoffClient({
|
|
1647
|
+
getClient: () => this.ensureClient(),
|
|
1648
|
+
getSessionId: () => this.getActiveSessionId(),
|
|
1649
|
+
capability: this.capability
|
|
1650
|
+
});
|
|
1222
1651
|
}
|
|
1223
1652
|
indexId;
|
|
1224
1653
|
version;
|
|
@@ -1231,6 +1660,7 @@ var AgentSession = class {
|
|
|
1231
1660
|
activeResponse;
|
|
1232
1661
|
childStreams;
|
|
1233
1662
|
capability;
|
|
1663
|
+
handoff;
|
|
1234
1664
|
getActiveSessionId() {
|
|
1235
1665
|
return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
|
|
1236
1666
|
}
|
|
@@ -1305,7 +1735,7 @@ var AgentSession = class {
|
|
|
1305
1735
|
}
|
|
1306
1736
|
this.activeResponse = void 0;
|
|
1307
1737
|
this.session = void 0;
|
|
1308
|
-
this.client = new
|
|
1738
|
+
this.client = new import_client4.Client({
|
|
1309
1739
|
auth: { bearer: () => this.capability.getAccessToken() },
|
|
1310
1740
|
host: config.host,
|
|
1311
1741
|
redirect: "error"
|
|
@@ -1341,7 +1771,7 @@ var AgentSession = class {
|
|
|
1341
1771
|
() => activeSession.send(message, { signal })
|
|
1342
1772
|
);
|
|
1343
1773
|
} catch (error) {
|
|
1344
|
-
if (error instanceof
|
|
1774
|
+
if (error instanceof import_client4.ClientError && error.status === 409 && error.code === "session_not_active") {
|
|
1345
1775
|
clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
|
|
1346
1776
|
this.session = void 0;
|
|
1347
1777
|
session = void 0;
|
|
@@ -1515,10 +1945,10 @@ var AgentSession = class {
|
|
|
1515
1945
|
}
|
|
1516
1946
|
this.session = session;
|
|
1517
1947
|
const inputResponses = responses.map(
|
|
1518
|
-
({ requestId, optionId, text:
|
|
1948
|
+
({ requestId, optionId, text: text3 }) => ({
|
|
1519
1949
|
requestId,
|
|
1520
1950
|
...optionId ? { optionId } : {},
|
|
1521
|
-
...
|
|
1951
|
+
...text3 ? { text: text3 } : {}
|
|
1522
1952
|
})
|
|
1523
1953
|
);
|
|
1524
1954
|
const response = await withCapabilityRefresh(
|
|
@@ -1608,6 +2038,7 @@ function createAgentClient(options) {
|
|
|
1608
2038
|
);
|
|
1609
2039
|
return {
|
|
1610
2040
|
indexId,
|
|
2041
|
+
handoff: session.handoff,
|
|
1611
2042
|
version,
|
|
1612
2043
|
runtimeOrigin,
|
|
1613
2044
|
visitorSessionId,
|
|
@@ -1639,7 +2070,7 @@ function createAgentClient(options) {
|
|
|
1639
2070
|
}
|
|
1640
2071
|
|
|
1641
2072
|
// src/runtime/errors.ts
|
|
1642
|
-
var
|
|
2073
|
+
var import_client5 = require("eve/client");
|
|
1643
2074
|
var TRANSIENT_AGENT_ERROR_MESSAGE = "The agent run stopped before the action finished. Please try again.";
|
|
1644
2075
|
var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
|
|
1645
2076
|
function isTransientRuntimeMessage(message) {
|
|
@@ -1651,7 +2082,7 @@ function isPreviewAuthorizationMessage(message) {
|
|
|
1651
2082
|
return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
|
|
1652
2083
|
}
|
|
1653
2084
|
function formatAgentError(error) {
|
|
1654
|
-
if (error instanceof
|
|
2085
|
+
if (error instanceof import_client5.ClientError) {
|
|
1655
2086
|
if (error.status === 401 && error.code === "index_required") {
|
|
1656
2087
|
return "Missing indexId \u2014 pass a published index id to createAgentClient().";
|
|
1657
2088
|
}
|
|
@@ -1685,14 +2116,14 @@ function formatAgentError(error) {
|
|
|
1685
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.";
|
|
1686
2117
|
function visitorMessageDisplayText(message) {
|
|
1687
2118
|
if (message.role !== "visitor") return message.text;
|
|
1688
|
-
const
|
|
1689
|
-
if (!
|
|
1690
|
-
if (
|
|
1691
|
-
const afterDismissal =
|
|
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();
|
|
1692
2123
|
if (afterDismissal) return afterDismissal;
|
|
1693
2124
|
}
|
|
1694
2125
|
const runtime = message.runtimeText?.trim();
|
|
1695
|
-
if (runtime &&
|
|
2126
|
+
if (runtime && text3 === runtime && runtime.includes(COMPOSER_FORM_SKIP_DISMISSAL)) {
|
|
1696
2127
|
const afterDismissal = runtime.slice(
|
|
1697
2128
|
runtime.indexOf(COMPOSER_FORM_SKIP_DISMISSAL) + COMPOSER_FORM_SKIP_DISMISSAL.length
|
|
1698
2129
|
).trim().replace(/^\n+/, "").trim();
|
|
@@ -1766,13 +2197,13 @@ function parseVisitorFormFields(value) {
|
|
|
1766
2197
|
const seen = /* @__PURE__ */ new Set();
|
|
1767
2198
|
for (const item of value) {
|
|
1768
2199
|
const record2 = asRecord(item);
|
|
1769
|
-
const
|
|
2200
|
+
const id2 = asString(record2?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
1770
2201
|
const kind = asString(record2?.kind);
|
|
1771
|
-
if (!record2 || !
|
|
1772
|
-
seen.add(
|
|
1773
|
-
const label = asString(record2.label) ||
|
|
2202
|
+
if (!record2 || !id2 || seen.has(id2) || !isFieldKind2(kind)) continue;
|
|
2203
|
+
seen.add(id2);
|
|
2204
|
+
const label = asString(record2.label) || id2;
|
|
1774
2205
|
fields.push({
|
|
1775
|
-
id,
|
|
2206
|
+
id: id2,
|
|
1776
2207
|
kind,
|
|
1777
2208
|
label,
|
|
1778
2209
|
placeholder: asString(record2.placeholder) || label,
|
|
@@ -1808,52 +2239,52 @@ function formatComposerFormMessage(form, values) {
|
|
|
1808
2239
|
return value ? `${field.label}: ${value}` : "";
|
|
1809
2240
|
}).filter(Boolean).join("\n");
|
|
1810
2241
|
}
|
|
1811
|
-
function looksLikeFieldCollection(
|
|
2242
|
+
function looksLikeFieldCollection(text3) {
|
|
1812
2243
|
return /\b(please|need|send|share|enter|provide|add|collect|what|which|use)\b/i.test(
|
|
1813
|
-
|
|
1814
|
-
) || /:\s*$/m.test(
|
|
2244
|
+
text3
|
|
2245
|
+
) || /:\s*$/m.test(text3) || /^[-*•]\s+/m.test(text3);
|
|
1815
2246
|
}
|
|
1816
|
-
function looksLikeBookingCopy(
|
|
2247
|
+
function looksLikeBookingCopy(text3) {
|
|
1817
2248
|
return /book(ing)? (card|a demo|this time)|pick a (date|time)|available (times|slots)|calendly/i.test(
|
|
1818
|
-
|
|
2249
|
+
text3
|
|
1819
2250
|
);
|
|
1820
2251
|
}
|
|
1821
|
-
function echoedLabeledFieldIds(
|
|
2252
|
+
function echoedLabeledFieldIds(text3) {
|
|
1822
2253
|
const ids = /* @__PURE__ */ new Set();
|
|
1823
|
-
if (/\bname\s*:\s+\S+/i.test(
|
|
1824
|
-
if (/\be-?mail\s*:\s+\S+/i.test(
|
|
1825
|
-
if (/\bphone\s*:\s+\S+/i.test(
|
|
1826
|
-
if (/\bcompany\s*:\s+\S+/i.test(
|
|
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");
|
|
1827
2258
|
return ids;
|
|
1828
2259
|
}
|
|
1829
|
-
function matchLibraryFields(
|
|
2260
|
+
function matchLibraryFields(text3) {
|
|
1830
2261
|
return FIELD_LIBRARY.flatMap((field) => {
|
|
1831
|
-
if (field.exclude?.test(
|
|
1832
|
-
const leftover =
|
|
2262
|
+
if (field.exclude?.test(text3)) {
|
|
2263
|
+
const leftover = text3.replace(field.exclude, " ");
|
|
1833
2264
|
if (!field.patterns.some((pattern) => pattern.test(leftover))) return [];
|
|
1834
|
-
} else if (!field.patterns.some((pattern) => pattern.test(
|
|
2265
|
+
} else if (!field.patterns.some((pattern) => pattern.test(text3))) {
|
|
1835
2266
|
return [];
|
|
1836
2267
|
}
|
|
1837
2268
|
const { patterns: _patterns, exclude: _exclude, ...next } = field;
|
|
1838
2269
|
return [next];
|
|
1839
2270
|
}).slice(0, MAX_FORM_FIELDS);
|
|
1840
2271
|
}
|
|
1841
|
-
function looksLikeCompletedActionRecap(
|
|
1842
|
-
const confirmingCreate = /\bshould i create this\b/i.test(
|
|
1843
|
-
const echoingFilledFields = /\b(?:name|email|phone|company)\s*:\s+\S+/i.test(
|
|
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);
|
|
1844
2275
|
if (echoingFilledFields) {
|
|
1845
|
-
if (looksLikeFieldCollection(
|
|
1846
|
-
const echoed = echoedLabeledFieldIds(
|
|
1847
|
-
if (matchLibraryFields(
|
|
2276
|
+
if (looksLikeFieldCollection(text3)) {
|
|
2277
|
+
const echoed = echoedLabeledFieldIds(text3);
|
|
2278
|
+
if (matchLibraryFields(text3).some((field) => !echoed.has(field.id))) {
|
|
1848
2279
|
return false;
|
|
1849
2280
|
}
|
|
1850
2281
|
}
|
|
1851
2282
|
return true;
|
|
1852
2283
|
}
|
|
1853
|
-
return confirmingCreate && !looksLikeFieldCollection(
|
|
2284
|
+
return confirmingCreate && !looksLikeFieldCollection(text3);
|
|
1854
2285
|
}
|
|
1855
|
-
function inferComposerForm(
|
|
1856
|
-
const cleaned =
|
|
2286
|
+
function inferComposerForm(text3) {
|
|
2287
|
+
const cleaned = text3.trim();
|
|
1857
2288
|
if (!cleaned || looksLikeBookingCopy(cleaned) || looksLikeCompletedActionRecap(cleaned) || !looksLikeFieldCollection(cleaned)) {
|
|
1858
2289
|
return null;
|
|
1859
2290
|
}
|
|
@@ -1868,8 +2299,8 @@ function resolveComposerForm(input) {
|
|
|
1868
2299
|
if (input.enabled === false || input.hasBookingOffer || input.hasPendingConfirmation) {
|
|
1869
2300
|
return null;
|
|
1870
2301
|
}
|
|
1871
|
-
const
|
|
1872
|
-
if (!
|
|
2302
|
+
const text3 = input.agentText.trim();
|
|
2303
|
+
if (!text3) return null;
|
|
1873
2304
|
const card = input.cards?.find((item) => item.type === "visitor_form");
|
|
1874
2305
|
if (card?.fields && card.fields.length >= MIN_FORM_FIELDS) {
|
|
1875
2306
|
return {
|
|
@@ -1877,7 +2308,7 @@ function resolveComposerForm(input) {
|
|
|
1877
2308
|
fields: card.fields.slice(0, MAX_FORM_FIELDS)
|
|
1878
2309
|
};
|
|
1879
2310
|
}
|
|
1880
|
-
return inferComposerForm(
|
|
2311
|
+
return inferComposerForm(text3);
|
|
1881
2312
|
}
|
|
1882
2313
|
|
|
1883
2314
|
// src/react/lib/tool-card.ts
|
|
@@ -1891,9 +2322,9 @@ function preferBookingOffer(current, next) {
|
|
|
1891
2322
|
}
|
|
1892
2323
|
return next;
|
|
1893
2324
|
}
|
|
1894
|
-
function looksLikeBookingReady(
|
|
2325
|
+
function looksLikeBookingReady(text3) {
|
|
1895
2326
|
return /demo option|options are ready|pick a (date|time)|available (times|slots)|book(ing)? (card|the demo)|\bschedule\b/i.test(
|
|
1896
|
-
|
|
2327
|
+
text3
|
|
1897
2328
|
);
|
|
1898
2329
|
}
|
|
1899
2330
|
function bookingOfferIdentityKey(offer) {
|
|
@@ -2003,29 +2434,29 @@ function bookingCardFromActionOutput(output) {
|
|
|
2003
2434
|
const data = asRecord2(record2?.output) ?? asRecord2(record2?.data) ?? record2;
|
|
2004
2435
|
return parseToolCard(data);
|
|
2005
2436
|
}
|
|
2006
|
-
function ensureBookingOfferText(
|
|
2007
|
-
if (!offer) return
|
|
2008
|
-
if (extractToolCards(
|
|
2009
|
-
return
|
|
2437
|
+
function ensureBookingOfferText(text3, offer) {
|
|
2438
|
+
if (!offer) return text3;
|
|
2439
|
+
if (extractToolCards(text3).some((card) => card.type === "booking_offer")) {
|
|
2440
|
+
return text3;
|
|
2010
2441
|
}
|
|
2011
|
-
const visible = stripToolCards(
|
|
2442
|
+
const visible = stripToolCards(text3).trim() || text3.trim();
|
|
2012
2443
|
return `${visible}
|
|
2013
2444
|
|
|
2014
2445
|
${formatBookingOfferFence(offer)}`;
|
|
2015
2446
|
}
|
|
2016
|
-
function hideToolCardFences(
|
|
2017
|
-
return
|
|
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();
|
|
2018
2449
|
}
|
|
2019
2450
|
var BOOKING_CARD_FALLBACK = "Pick a date and time that works for you.";
|
|
2020
|
-
function looksLikeBookingAvailabilityDump(
|
|
2021
|
-
const cleaned =
|
|
2451
|
+
function looksLikeBookingAvailabilityDump(text3) {
|
|
2452
|
+
const cleaned = text3.trim();
|
|
2022
2453
|
if (!cleaned) return false;
|
|
2023
2454
|
const isoCount = (cleaned.match(/\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/g) ?? []).length;
|
|
2024
2455
|
const utcCount = (cleaned.match(/\bUTC\b/g) ?? []).length;
|
|
2025
2456
|
return isoCount >= 2 || utcCount >= 2 || /api\.calendly\.com|calendly\.com\//i.test(cleaned) || /webless-tool-card/i.test(cleaned);
|
|
2026
2457
|
}
|
|
2027
|
-
function sanitizeBookingOfferCopy(
|
|
2028
|
-
const cleaned = hideToolCardFences(
|
|
2458
|
+
function sanitizeBookingOfferCopy(text3) {
|
|
2459
|
+
const cleaned = hideToolCardFences(text3);
|
|
2029
2460
|
if (!cleaned || looksLikeBookingAvailabilityDump(cleaned)) {
|
|
2030
2461
|
return BOOKING_CARD_FALLBACK;
|
|
2031
2462
|
}
|
|
@@ -2038,9 +2469,9 @@ function visitorTimeZone() {
|
|
|
2038
2469
|
return "UTC";
|
|
2039
2470
|
}
|
|
2040
2471
|
}
|
|
2041
|
-
function extractToolCards(
|
|
2472
|
+
function extractToolCards(text3) {
|
|
2042
2473
|
const cards = [];
|
|
2043
|
-
for (const match of
|
|
2474
|
+
for (const match of text3.matchAll(FENCE_PATTERN)) {
|
|
2044
2475
|
try {
|
|
2045
2476
|
const card = parseToolCard(JSON.parse(match[1] ?? ""));
|
|
2046
2477
|
if (card) cards.push(card);
|
|
@@ -2049,8 +2480,8 @@ function extractToolCards(text2) {
|
|
|
2049
2480
|
}
|
|
2050
2481
|
return cards;
|
|
2051
2482
|
}
|
|
2052
|
-
function stripToolCards(
|
|
2053
|
-
return
|
|
2483
|
+
function stripToolCards(text3) {
|
|
2484
|
+
return text3.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
2054
2485
|
}
|
|
2055
2486
|
function localDateKey(date) {
|
|
2056
2487
|
if (Number.isNaN(date.getTime())) return "";
|
|
@@ -2163,7 +2594,7 @@ function visitorBookingPrefix(booking) {
|
|
|
2163
2594
|
function record(value) {
|
|
2164
2595
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2165
2596
|
}
|
|
2166
|
-
function
|
|
2597
|
+
function text2(value, max = 500) {
|
|
2167
2598
|
return typeof value === "string" && value.trim().length > 0 && value.trim().length <= max;
|
|
2168
2599
|
}
|
|
2169
2600
|
function safeSearchUrl(value) {
|
|
@@ -2184,7 +2615,7 @@ function parseAgentSearchReferences(value) {
|
|
|
2184
2615
|
const source = record(value2);
|
|
2185
2616
|
if (!source || Object.keys(source).some(
|
|
2186
2617
|
(key) => !["id", "title", "url"].includes(key)
|
|
2187
|
-
) || !
|
|
2618
|
+
) || !text2(source.id, Infinity) || !text2(source.title) || source.url !== void 0 && typeof source.url !== "string")
|
|
2188
2619
|
return null;
|
|
2189
2620
|
const url = safeSearchUrl(source.url);
|
|
2190
2621
|
sources.push({
|
|
@@ -2196,7 +2627,7 @@ function parseAgentSearchReferences(value) {
|
|
|
2196
2627
|
let cta;
|
|
2197
2628
|
if (data.cta !== void 0) {
|
|
2198
2629
|
const action = record(data.cta);
|
|
2199
|
-
if (!action || Object.keys(action).some((key) => !["label", "url"].includes(key)) || !
|
|
2630
|
+
if (!action || Object.keys(action).some((key) => !["label", "url"].includes(key)) || !text2(action.label) || action.url !== void 0 && typeof action.url !== "string")
|
|
2200
2631
|
return null;
|
|
2201
2632
|
const url = safeSearchUrl(action.url);
|
|
2202
2633
|
cta = { label: action.label.trim(), ...url ? { url } : {} };
|
|
@@ -2215,7 +2646,7 @@ function parseAgentSearchDiscoveryOutput(value) {
|
|
|
2215
2646
|
const data = record(decoded);
|
|
2216
2647
|
if (!data || Object.keys(data).some(
|
|
2217
2648
|
(key) => !["answer", "sources", "cta", "suggestions"].includes(key)
|
|
2218
|
-
) || !
|
|
2649
|
+
) || !text2(data.answer, 5e4) || !Array.isArray(data.suggestions) || data.suggestions.length > 8 || !data.suggestions.every((item) => text2(item)))
|
|
2219
2650
|
return null;
|
|
2220
2651
|
const references = parseAgentSearchReferences(data);
|
|
2221
2652
|
return references ? {
|
|
@@ -2233,9 +2664,21 @@ function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
|
2233
2664
|
function parseMessage(value) {
|
|
2234
2665
|
if (typeof value !== "object" || value === null) return null;
|
|
2235
2666
|
const record2 = value;
|
|
2236
|
-
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)) {
|
|
2237
2668
|
return null;
|
|
2238
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
|
+
}
|
|
2239
2682
|
if (record2.role === "visitor") {
|
|
2240
2683
|
return {
|
|
2241
2684
|
id: record2.id,
|
|
@@ -2530,10 +2973,10 @@ function safeText(value) {
|
|
|
2530
2973
|
return value.trim().slice(0, MAX_TEXT_LENGTH);
|
|
2531
2974
|
}
|
|
2532
2975
|
function safeHref(value) {
|
|
2533
|
-
const
|
|
2534
|
-
if (!
|
|
2976
|
+
const text3 = safeText(value);
|
|
2977
|
+
if (!text3) return "";
|
|
2535
2978
|
try {
|
|
2536
|
-
const url = new URL(
|
|
2979
|
+
const url = new URL(text3);
|
|
2537
2980
|
return url.protocol === "https:" ? url.toString() : "";
|
|
2538
2981
|
} catch {
|
|
2539
2982
|
return "";
|
|
@@ -2896,15 +3339,15 @@ function appendChatCollectiblePrompts(messages, requests) {
|
|
|
2896
3339
|
}
|
|
2897
3340
|
return next;
|
|
2898
3341
|
}
|
|
2899
|
-
function chatInputResponseForText(requests,
|
|
2900
|
-
const trimmed =
|
|
3342
|
+
function chatInputResponseForText(requests, text3) {
|
|
3343
|
+
const trimmed = text3.trim();
|
|
2901
3344
|
if (!trimmed) return null;
|
|
2902
3345
|
const pending = requests.find(isChatCollectibleInputRequest);
|
|
2903
3346
|
if (!pending) return null;
|
|
2904
3347
|
return { requestId: pending.requestId, text: trimmed };
|
|
2905
3348
|
}
|
|
2906
|
-
function normalizeAssistantDedupeKey(
|
|
2907
|
-
return
|
|
3349
|
+
function normalizeAssistantDedupeKey(text3) {
|
|
3350
|
+
return text3.trim().replace(/\s+/g, " ").toLowerCase();
|
|
2908
3351
|
}
|
|
2909
3352
|
function isNearDuplicateAssistantText(left, right) {
|
|
2910
3353
|
const a = normalizeAssistantDedupeKey(left);
|
|
@@ -3017,6 +3460,7 @@ function isJsonRecord(value) {
|
|
|
3017
3460
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3018
3461
|
}
|
|
3019
3462
|
function useAgentChat({
|
|
3463
|
+
handoffEnabled = false,
|
|
3020
3464
|
customerId,
|
|
3021
3465
|
getUnpublishedPreviewGrant,
|
|
3022
3466
|
indexId,
|
|
@@ -3028,13 +3472,13 @@ function useAgentChat({
|
|
|
3028
3472
|
greeting,
|
|
3029
3473
|
toolResultRegistry
|
|
3030
3474
|
}) {
|
|
3031
|
-
const initialState = (0,
|
|
3475
|
+
const initialState = (0, import_react3.useMemo)(
|
|
3032
3476
|
() => createInitialState(greeting?.trim() || DEFAULT_GREETING),
|
|
3033
3477
|
[greeting]
|
|
3034
3478
|
);
|
|
3035
|
-
const previewGrantProviderRef = (0,
|
|
3479
|
+
const previewGrantProviderRef = (0, import_react3.useRef)(getUnpublishedPreviewGrant);
|
|
3036
3480
|
previewGrantProviderRef.current = getUnpublishedPreviewGrant;
|
|
3037
|
-
const toolResultRegistryRef = (0,
|
|
3481
|
+
const toolResultRegistryRef = (0, import_react3.useRef)(toolResultRegistry);
|
|
3038
3482
|
toolResultRegistryRef.current = toolResultRegistry;
|
|
3039
3483
|
const resolveUnpublishedPreviewGrant = () => {
|
|
3040
3484
|
const provider = previewGrantProviderRef.current;
|
|
@@ -3047,7 +3491,7 @@ function useAgentChat({
|
|
|
3047
3491
|
}
|
|
3048
3492
|
return provider();
|
|
3049
3493
|
};
|
|
3050
|
-
const resolvedStorageKeyPrefix = (0,
|
|
3494
|
+
const resolvedStorageKeyPrefix = (0, import_react3.useMemo)(
|
|
3051
3495
|
() => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({
|
|
3052
3496
|
customerId,
|
|
3053
3497
|
indexId,
|
|
@@ -3056,23 +3500,23 @@ function useAgentChat({
|
|
|
3056
3500
|
}),
|
|
3057
3501
|
[customerId, indexId, runtimeOrigin, storageKeyPrefix, version]
|
|
3058
3502
|
);
|
|
3059
|
-
const visitorId = (0,
|
|
3503
|
+
const visitorId = (0, import_react3.useMemo)(
|
|
3060
3504
|
() => visitorSessionId?.trim() || getOrCreateVisitorSessionId({
|
|
3061
3505
|
storageKeyPrefix: resolvedStorageKeyPrefix
|
|
3062
3506
|
}),
|
|
3063
3507
|
[resolvedStorageKeyPrefix, visitorSessionId]
|
|
3064
3508
|
);
|
|
3065
|
-
const [state, setState] = (0,
|
|
3509
|
+
const [state, setState] = (0, import_react3.useState)(
|
|
3066
3510
|
() => stateFromConversation(
|
|
3067
3511
|
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
|
|
3068
3512
|
initialState
|
|
3069
3513
|
)
|
|
3070
3514
|
);
|
|
3071
|
-
const pendingBookingRef = (0,
|
|
3515
|
+
const pendingBookingRef = (0, import_react3.useRef)(
|
|
3072
3516
|
loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
|
|
3073
3517
|
);
|
|
3074
|
-
const runRef = (0,
|
|
3075
|
-
const clientRef = (0,
|
|
3518
|
+
const runRef = (0, import_react3.useRef)(null);
|
|
3519
|
+
const clientRef = (0, import_react3.useRef)(
|
|
3076
3520
|
createAgentClient({
|
|
3077
3521
|
locationConsent: true,
|
|
3078
3522
|
customerId,
|
|
@@ -3086,8 +3530,8 @@ function useAgentChat({
|
|
|
3086
3530
|
})
|
|
3087
3531
|
);
|
|
3088
3532
|
const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${previewBuildId ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
|
|
3089
|
-
const identityRef = (0,
|
|
3090
|
-
(0,
|
|
3533
|
+
const identityRef = (0, import_react3.useRef)(identityKey);
|
|
3534
|
+
(0, import_react3.useEffect)(() => {
|
|
3091
3535
|
if (identityRef.current === identityKey) {
|
|
3092
3536
|
return;
|
|
3093
3537
|
}
|
|
@@ -3126,7 +3570,7 @@ function useAgentChat({
|
|
|
3126
3570
|
version,
|
|
3127
3571
|
visitorId
|
|
3128
3572
|
]);
|
|
3129
|
-
(0,
|
|
3573
|
+
(0, import_react3.useEffect)(() => {
|
|
3130
3574
|
if (!hasVisitorMessages(state.messages)) return;
|
|
3131
3575
|
savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
|
|
3132
3576
|
messages: state.messages,
|
|
@@ -3146,16 +3590,77 @@ function useAgentChat({
|
|
|
3146
3590
|
state.toolResults,
|
|
3147
3591
|
visitorId
|
|
3148
3592
|
]);
|
|
3149
|
-
const
|
|
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;
|
|
3150
3648
|
runRef.current?.abort();
|
|
3151
3649
|
runRef.current = null;
|
|
3152
3650
|
clientRef.current.reset();
|
|
3153
3651
|
pendingBookingRef.current = null;
|
|
3154
3652
|
clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
|
|
3155
3653
|
setState(initialState);
|
|
3156
|
-
}, [
|
|
3157
|
-
|
|
3654
|
+
}, [
|
|
3655
|
+
handoff.blocksAI,
|
|
3656
|
+
handoff.canReset,
|
|
3657
|
+
initialState,
|
|
3658
|
+
resolvedStorageKeyPrefix,
|
|
3659
|
+
visitorId
|
|
3660
|
+
]);
|
|
3661
|
+
const runTurn = (0, import_react3.useCallback)(
|
|
3158
3662
|
async (input) => {
|
|
3663
|
+
if (handoffBlocksAI.current) return null;
|
|
3159
3664
|
const {
|
|
3160
3665
|
controller,
|
|
3161
3666
|
initialText = "",
|
|
@@ -3279,6 +3784,7 @@ function useAgentChat({
|
|
|
3279
3784
|
signal
|
|
3280
3785
|
});
|
|
3281
3786
|
if (resume && finalText === null) {
|
|
3787
|
+
if (!isActiveRun() || handoffBlocksAI.current) return null;
|
|
3282
3788
|
finalText = await clientRef.current.sendTurn(visitorText, {
|
|
3283
3789
|
handlers,
|
|
3284
3790
|
signal
|
|
@@ -3311,8 +3817,12 @@ function useAgentChat({
|
|
|
3311
3817
|
messages: appendAgentTurnMessage(
|
|
3312
3818
|
prev.messages,
|
|
3313
3819
|
displayText,
|
|
3314
|
-
(prev.toolResults ?? []).filter(
|
|
3315
|
-
|
|
3820
|
+
(prev.toolResults ?? []).filter(
|
|
3821
|
+
(result) => result.kind === "search"
|
|
3822
|
+
),
|
|
3823
|
+
(prev.toolResults ?? []).filter(
|
|
3824
|
+
(result) => result.kind !== "search" && result.kind !== "input"
|
|
3825
|
+
)
|
|
3316
3826
|
),
|
|
3317
3827
|
toolResults: (prev.toolResults ?? []).filter(
|
|
3318
3828
|
(result) => result.kind === "input"
|
|
@@ -3353,7 +3863,7 @@ function useAgentChat({
|
|
|
3353
3863
|
},
|
|
3354
3864
|
[resolvedStorageKeyPrefix, visitorId]
|
|
3355
3865
|
);
|
|
3356
|
-
const rememberBooking = (0,
|
|
3866
|
+
const rememberBooking = (0, import_react3.useCallback)(
|
|
3357
3867
|
(booking) => {
|
|
3358
3868
|
const current = pendingBookingRef.current;
|
|
3359
3869
|
if (current?.eventUri === booking.eventUri && current.inviteeEmail === booking.inviteeEmail && current.inviteeUri === booking.inviteeUri) {
|
|
@@ -3364,7 +3874,7 @@ function useAgentChat({
|
|
|
3364
3874
|
},
|
|
3365
3875
|
[resolvedStorageKeyPrefix, visitorId]
|
|
3366
3876
|
);
|
|
3367
|
-
const forgetBooking = (0,
|
|
3877
|
+
const forgetBooking = (0, import_react3.useCallback)(
|
|
3368
3878
|
(eventUri) => {
|
|
3369
3879
|
const current = pendingBookingRef.current;
|
|
3370
3880
|
if (!current) return;
|
|
@@ -3374,12 +3884,21 @@ function useAgentChat({
|
|
|
3374
3884
|
},
|
|
3375
3885
|
[resolvedStorageKeyPrefix, visitorId]
|
|
3376
3886
|
);
|
|
3377
|
-
const submit = (0,
|
|
3887
|
+
const submit = (0, import_react3.useCallback)(
|
|
3378
3888
|
async (visitorText, options) => {
|
|
3379
3889
|
const trimmed = visitorText.trim();
|
|
3380
3890
|
const outgoing = options?.runtimeText ?? visitorText;
|
|
3381
3891
|
if (!outgoing.trim()) return null;
|
|
3382
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
|
+
}
|
|
3383
3902
|
const chatResponse = chatInputResponseForText(
|
|
3384
3903
|
state.pendingInputs ?? [],
|
|
3385
3904
|
outgoing.trim()
|
|
@@ -3481,9 +4000,10 @@ ${outgoing}` : outgoing;
|
|
|
3481
4000
|
visitorText: runtimeText
|
|
3482
4001
|
});
|
|
3483
4002
|
},
|
|
3484
|
-
[runTurn, state.pendingInputs]
|
|
4003
|
+
[handoff, runTurn, state.pendingInputs]
|
|
3485
4004
|
);
|
|
3486
|
-
const retry = (0,
|
|
4005
|
+
const retry = (0, import_react3.useCallback)(async () => {
|
|
4006
|
+
if (handoffBlocksAI.current) return;
|
|
3487
4007
|
const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
|
|
3488
4008
|
if (!visitorMessage) return;
|
|
3489
4009
|
if (runRef.current) {
|
|
@@ -3516,7 +4036,8 @@ ${outgoing}` : outgoing;
|
|
|
3516
4036
|
visitorText: visitorTurnText(visitorMessage)
|
|
3517
4037
|
});
|
|
3518
4038
|
}, [runTurn, state.messages]);
|
|
3519
|
-
const regenerate = (0,
|
|
4039
|
+
const regenerate = (0, import_react3.useCallback)(async () => {
|
|
4040
|
+
if (handoffBlocksAI.current) return null;
|
|
3520
4041
|
let lastVisitorIndex = -1;
|
|
3521
4042
|
for (let index = state.messages.length - 1; index >= 0; index -= 1) {
|
|
3522
4043
|
if (state.messages[index]?.role === "visitor") {
|
|
@@ -3559,7 +4080,7 @@ ${outgoing}` : outgoing;
|
|
|
3559
4080
|
visitorText: visitorTurnText(visitorMessage)
|
|
3560
4081
|
});
|
|
3561
4082
|
}, [runTurn, state.messages]);
|
|
3562
|
-
const respondToToolInput = (0,
|
|
4083
|
+
const respondToToolInput = (0, import_react3.useCallback)(
|
|
3563
4084
|
async (surface, values) => {
|
|
3564
4085
|
await submit(`${surface.title} submitted`, {
|
|
3565
4086
|
runtimeText: [
|
|
@@ -3572,9 +4093,9 @@ ${outgoing}` : outgoing;
|
|
|
3572
4093
|
},
|
|
3573
4094
|
[submit]
|
|
3574
4095
|
);
|
|
3575
|
-
const respondToInput = (0,
|
|
4096
|
+
const respondToInput = (0, import_react3.useCallback)(
|
|
3576
4097
|
async (response) => {
|
|
3577
|
-
if (runRef.current) return;
|
|
4098
|
+
if (handoffBlocksAI.current || runRef.current) return;
|
|
3578
4099
|
const pending = state.pendingInputs?.find(
|
|
3579
4100
|
(request) => request.requestId === response.requestId
|
|
3580
4101
|
);
|
|
@@ -3602,7 +4123,8 @@ ${outgoing}` : outgoing;
|
|
|
3602
4123
|
},
|
|
3603
4124
|
[respondToToolInput, runTurn, state.pendingInputs]
|
|
3604
4125
|
);
|
|
3605
|
-
(0,
|
|
4126
|
+
(0, import_react3.useEffect)(() => {
|
|
4127
|
+
if (handoff.blocksAI) return;
|
|
3606
4128
|
const conversation = loadPersistedAgentConversation(
|
|
3607
4129
|
resolvedStorageKeyPrefix,
|
|
3608
4130
|
visitorId
|
|
@@ -3624,14 +4146,21 @@ ${outgoing}` : outgoing;
|
|
|
3624
4146
|
}
|
|
3625
4147
|
controller.abort();
|
|
3626
4148
|
};
|
|
3627
|
-
}, [
|
|
3628
|
-
|
|
4149
|
+
}, [
|
|
4150
|
+
handoff.blocksAI,
|
|
4151
|
+
identityKey,
|
|
4152
|
+
resolvedStorageKeyPrefix,
|
|
4153
|
+
runTurn,
|
|
4154
|
+
visitorId
|
|
4155
|
+
]);
|
|
4156
|
+
(0, import_react3.useEffect)(() => {
|
|
3629
4157
|
return () => {
|
|
3630
4158
|
runRef.current?.abort();
|
|
3631
4159
|
runRef.current = null;
|
|
3632
4160
|
};
|
|
3633
4161
|
}, []);
|
|
3634
4162
|
return {
|
|
4163
|
+
handoff,
|
|
3635
4164
|
state,
|
|
3636
4165
|
reset,
|
|
3637
4166
|
retry,
|
|
@@ -3653,12 +4182,12 @@ function isAgentBusy(phase) {
|
|
|
3653
4182
|
}
|
|
3654
4183
|
|
|
3655
4184
|
// src/react/hooks/useIsMobile.ts
|
|
3656
|
-
var
|
|
4185
|
+
var import_react4 = require("react");
|
|
3657
4186
|
function useIsMobile(breakpoint = 767) {
|
|
3658
|
-
const [isMobile, setIsMobile] = (0,
|
|
4187
|
+
const [isMobile, setIsMobile] = (0, import_react4.useState)(
|
|
3659
4188
|
() => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
|
|
3660
4189
|
);
|
|
3661
|
-
(0,
|
|
4190
|
+
(0, import_react4.useEffect)(() => {
|
|
3662
4191
|
const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
|
|
3663
4192
|
const onChange = () => setIsMobile(media.matches);
|
|
3664
4193
|
onChange();
|
|
@@ -3686,7 +4215,7 @@ function normalizeAgentPlacement(placement) {
|
|
|
3686
4215
|
}
|
|
3687
4216
|
|
|
3688
4217
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
3689
|
-
var
|
|
4218
|
+
var import_react16 = require("react");
|
|
3690
4219
|
|
|
3691
4220
|
// src/react/types/conversation.ts
|
|
3692
4221
|
var defaultAgentRailTheme = {
|
|
@@ -3769,7 +4298,7 @@ function agentThemeStyle(theme, resolvedColorScheme) {
|
|
|
3769
4298
|
}
|
|
3770
4299
|
|
|
3771
4300
|
// src/react/hooks/useAgentColorScheme.ts
|
|
3772
|
-
var
|
|
4301
|
+
var import_react5 = require("react");
|
|
3773
4302
|
var DARK_MODE_QUERY = "(prefers-color-scheme: dark)";
|
|
3774
4303
|
function subscribeToDarkMode(onChange) {
|
|
3775
4304
|
if (typeof window === "undefined" || !window.matchMedia) {
|
|
@@ -3787,7 +4316,7 @@ function getPrefersDarkMode() {
|
|
|
3787
4316
|
return typeof window !== "undefined" && Boolean(window.matchMedia?.(DARK_MODE_QUERY).matches);
|
|
3788
4317
|
}
|
|
3789
4318
|
function useAgentColorScheme(colorScheme = "auto") {
|
|
3790
|
-
const prefersDarkMode = (0,
|
|
4319
|
+
const prefersDarkMode = (0, import_react5.useSyncExternalStore)(
|
|
3791
4320
|
subscribeToDarkMode,
|
|
3792
4321
|
getPrefersDarkMode,
|
|
3793
4322
|
() => false
|
|
@@ -3799,7 +4328,7 @@ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
|
|
|
3799
4328
|
}
|
|
3800
4329
|
|
|
3801
4330
|
// src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
|
|
3802
|
-
var
|
|
4331
|
+
var import_react6 = require("react");
|
|
3803
4332
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
3804
4333
|
function joinLabels(labels) {
|
|
3805
4334
|
if (labels.length <= 1) return labels[0] ?? "";
|
|
@@ -3918,7 +4447,7 @@ function AgentActivityBubble({
|
|
|
3918
4447
|
const statusText = workSummary(steps, failed, brandLabel);
|
|
3919
4448
|
const softReview = statusText === AGENT_SOFT_REVIEW_STATUS;
|
|
3920
4449
|
const receiptId = steps.map((step) => step.id).join(":");
|
|
3921
|
-
const [expandedReceiptId, setExpandedReceiptId] = (0,
|
|
4450
|
+
const [expandedReceiptId, setExpandedReceiptId] = (0, import_react6.useState)(
|
|
3922
4451
|
null
|
|
3923
4452
|
);
|
|
3924
4453
|
const detailsOpen = !active && expandedReceiptId === receiptId;
|
|
@@ -3969,17 +4498,17 @@ function AgentActivityBubble({
|
|
|
3969
4498
|
}
|
|
3970
4499
|
|
|
3971
4500
|
// src/react/components/AnswerReceiptDialog/AnswerReceiptDialog.tsx
|
|
3972
|
-
var
|
|
4501
|
+
var import_react8 = require("react");
|
|
3973
4502
|
var import_react_dom = require("react-dom");
|
|
3974
4503
|
|
|
3975
4504
|
// src/react/components/AgentRail/AgentRailOverlayContext.tsx
|
|
3976
|
-
var
|
|
3977
|
-
var AgentRailOverlayContext = (0,
|
|
4505
|
+
var import_react7 = require("react");
|
|
4506
|
+
var AgentRailOverlayContext = (0, import_react7.createContext)(null);
|
|
3978
4507
|
function useAgentRailPortalRoots() {
|
|
3979
|
-
return (0,
|
|
4508
|
+
return (0, import_react7.useContext)(AgentRailOverlayContext);
|
|
3980
4509
|
}
|
|
3981
4510
|
function useAgentRailMenuPortalRoot() {
|
|
3982
|
-
return (0,
|
|
4511
|
+
return (0, import_react7.useContext)(AgentRailOverlayContext)?.railRef ?? null;
|
|
3983
4512
|
}
|
|
3984
4513
|
|
|
3985
4514
|
// src/react/components/AnswerReceiptDialog/AnswerReceiptDialog.tsx
|
|
@@ -4003,10 +4532,10 @@ function AnswerReceiptDialog({
|
|
|
4003
4532
|
}) {
|
|
4004
4533
|
const portalRoots = useAgentRailPortalRoots();
|
|
4005
4534
|
const overlayRoot = portalRoots?.overlayRef ?? null;
|
|
4006
|
-
const cardRef = (0,
|
|
4007
|
-
const closeButtonRef = (0,
|
|
4008
|
-
const previouslyFocusedRef = (0,
|
|
4009
|
-
(0,
|
|
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)(() => {
|
|
4010
4539
|
previouslyFocusedRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
4011
4540
|
closeButtonRef.current?.focus({ preventScroll: true });
|
|
4012
4541
|
const handleKeyDown = (event) => {
|
|
@@ -4091,7 +4620,7 @@ function AnswerReceiptDialog({
|
|
|
4091
4620
|
}
|
|
4092
4621
|
|
|
4093
4622
|
// src/react/components/Composer/Composer.tsx
|
|
4094
|
-
var
|
|
4623
|
+
var import_react9 = require("react");
|
|
4095
4624
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
4096
4625
|
var FORM_SUBMITTED_MESSAGE = "Shared my details";
|
|
4097
4626
|
function SendIcon() {
|
|
@@ -4153,17 +4682,17 @@ function Composer({
|
|
|
4153
4682
|
allowFormResume = true,
|
|
4154
4683
|
onSubmit
|
|
4155
4684
|
}) {
|
|
4156
|
-
const [value, setValue] = (0,
|
|
4157
|
-
const [draft, setDraft] = (0,
|
|
4158
|
-
const inputRef = (0,
|
|
4159
|
-
const firstFieldRef = (0,
|
|
4160
|
-
const formRef = (0,
|
|
4161
|
-
const formId = (0,
|
|
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)();
|
|
4162
4691
|
if (form && form.id !== draft.form?.id) setDraft(createDraft(form));
|
|
4163
4692
|
const savedForm = allowFormResume && !draft.submitted ? draft.form : null;
|
|
4164
4693
|
const activeForm = !disabled && draft.expanded ? savedForm : null;
|
|
4165
4694
|
const canSend = !disabled && Boolean(value.trim() || activeForm);
|
|
4166
|
-
(0,
|
|
4695
|
+
(0, import_react9.useEffect)(() => {
|
|
4167
4696
|
if (activeForm) firstFieldRef.current?.focus();
|
|
4168
4697
|
else if ((savedForm || draft.submitted) && !disabled)
|
|
4169
4698
|
inputRef.current?.focus();
|
|
@@ -4424,7 +4953,7 @@ function FollowUpChips({
|
|
|
4424
4953
|
}
|
|
4425
4954
|
|
|
4426
4955
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
4427
|
-
var
|
|
4956
|
+
var import_react11 = require("react");
|
|
4428
4957
|
|
|
4429
4958
|
// src/react/components/SearchReferences/SearchReferences.tsx
|
|
4430
4959
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
@@ -4515,7 +5044,7 @@ function SearchReferences({
|
|
|
4515
5044
|
}
|
|
4516
5045
|
|
|
4517
5046
|
// src/react/components/BookingCard/BookingCard.tsx
|
|
4518
|
-
var
|
|
5047
|
+
var import_react10 = require("react");
|
|
4519
5048
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
4520
5049
|
var BOOKING_STEPS = [
|
|
4521
5050
|
{ id: "date", label: "Date" },
|
|
@@ -4570,27 +5099,27 @@ function InteractiveBookingCard({
|
|
|
4570
5099
|
offer,
|
|
4571
5100
|
onBook
|
|
4572
5101
|
}) {
|
|
4573
|
-
const fieldId = (0,
|
|
5102
|
+
const fieldId = (0, import_react10.useId)();
|
|
4574
5103
|
const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
|
|
4575
|
-
const [step, setStep] = (0,
|
|
4576
|
-
const [eventTypeUri, setEventTypeUri] = (0,
|
|
4577
|
-
const [selectedDate, setSelectedDate] = (0,
|
|
4578
|
-
const [startTime, setStartTime] = (0,
|
|
4579
|
-
const [name, setName] = (0,
|
|
4580
|
-
const [email, setEmail] = (0,
|
|
4581
|
-
const activeStepRef = (0,
|
|
4582
|
-
const previousStepRef = (0,
|
|
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);
|
|
4583
5112
|
const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
|
|
4584
|
-
(0,
|
|
5113
|
+
(0, import_react10.useEffect)(() => {
|
|
4585
5114
|
if (previousStepRef.current === step) return;
|
|
4586
5115
|
previousStepRef.current = step;
|
|
4587
5116
|
activeStepRef.current?.scrollIntoView({ block: "nearest" });
|
|
4588
5117
|
}, [step]);
|
|
4589
|
-
const slots = (0,
|
|
5118
|
+
const slots = (0, import_react10.useMemo)(
|
|
4590
5119
|
() => bookingSlotsForEventType(offer.slots, eventTypeUri),
|
|
4591
5120
|
[eventTypeUri, offer.slots]
|
|
4592
5121
|
);
|
|
4593
|
-
const availableByDate = (0,
|
|
5122
|
+
const availableByDate = (0, import_react10.useMemo)(() => {
|
|
4594
5123
|
const next = /* @__PURE__ */ new Map();
|
|
4595
5124
|
for (const slot of slots) {
|
|
4596
5125
|
const key = slotDateKey(slot.startTime);
|
|
@@ -4598,7 +5127,7 @@ function InteractiveBookingCard({
|
|
|
4598
5127
|
}
|
|
4599
5128
|
return next;
|
|
4600
5129
|
}, [slots]);
|
|
4601
|
-
const [visibleMonth, setVisibleMonth] = (0,
|
|
5130
|
+
const [visibleMonth, setVisibleMonth] = (0, import_react10.useState)(
|
|
4602
5131
|
() => firstAvailableBookingMonth(slots)
|
|
4603
5132
|
);
|
|
4604
5133
|
function selectEventType(nextType) {
|
|
@@ -4611,7 +5140,7 @@ function InteractiveBookingCard({
|
|
|
4611
5140
|
)
|
|
4612
5141
|
);
|
|
4613
5142
|
}
|
|
4614
|
-
const daySlots = (0,
|
|
5143
|
+
const daySlots = (0, import_react10.useMemo)(
|
|
4615
5144
|
() => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
|
|
4616
5145
|
[selectedDate, slots]
|
|
4617
5146
|
);
|
|
@@ -4620,7 +5149,7 @@ function InteractiveBookingCard({
|
|
|
4620
5149
|
);
|
|
4621
5150
|
const selectedSample = availableByDate.get(selectedDate) ?? startTime;
|
|
4622
5151
|
const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
|
|
4623
|
-
const weekdays = (0,
|
|
5152
|
+
const weekdays = (0, import_react10.useMemo)(() => weekdayLabels(), []);
|
|
4624
5153
|
const cells = calendarCells(visibleMonth.year, visibleMonth.month);
|
|
4625
5154
|
const canPrevMonth = [...availableByDate.keys()].some((key) => {
|
|
4626
5155
|
const month = monthFromKey(key);
|
|
@@ -4906,8 +5435,8 @@ function resolveMessageUrl(safeUrl, baseUrl) {
|
|
|
4906
5435
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
4907
5436
|
var import_styles = require("streamdown/styles.css");
|
|
4908
5437
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
4909
|
-
function normalizeDedupeText(
|
|
4910
|
-
return
|
|
5438
|
+
function normalizeDedupeText(text3) {
|
|
5439
|
+
return text3.trim().replace(/\s+/g, " ").toLowerCase();
|
|
4911
5440
|
}
|
|
4912
5441
|
function paragraphsAreNearDuplicates(first, second) {
|
|
4913
5442
|
const left = normalizeDedupeText(first);
|
|
@@ -4923,8 +5452,8 @@ function paragraphsShareOpening(first, second) {
|
|
|
4923
5452
|
if (!opening || opening.length < 20) return false;
|
|
4924
5453
|
return second.trim().startsWith(opening);
|
|
4925
5454
|
}
|
|
4926
|
-
function collapseRepeatedText(
|
|
4927
|
-
const trimmed =
|
|
5455
|
+
function collapseRepeatedText(text3) {
|
|
5456
|
+
const trimmed = text3.trim();
|
|
4928
5457
|
if (trimmed.length < 40) return trimmed;
|
|
4929
5458
|
const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
|
|
4930
5459
|
if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
|
|
@@ -4945,13 +5474,14 @@ function MessageBubble({
|
|
|
4945
5474
|
message,
|
|
4946
5475
|
brandLogoUrl,
|
|
4947
5476
|
bookingDisabled = false,
|
|
5477
|
+
showProvenance = false,
|
|
4948
5478
|
bookingReadOnly = false,
|
|
4949
5479
|
linkBaseUrl,
|
|
4950
5480
|
offer,
|
|
4951
5481
|
onBook
|
|
4952
5482
|
}) {
|
|
4953
5483
|
const resolvedLogoUrl = brandLogoUrl?.trim();
|
|
4954
|
-
const [failedLogoUrl, setFailedLogoUrl] = (0,
|
|
5484
|
+
const [failedLogoUrl, setFailedLogoUrl] = (0, import_react11.useState)(null);
|
|
4955
5485
|
const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
|
|
4956
5486
|
const cards = message.role === "agent" ? extractToolCards(message.text) : [];
|
|
4957
5487
|
const extractedOffers = cards.filter(
|
|
@@ -4966,7 +5496,34 @@ function MessageBubble({
|
|
|
4966
5496
|
if (message.role === "visitor") {
|
|
4967
5497
|
const visitorText = visitorMessageDisplayText(message).trim();
|
|
4968
5498
|
if (!visitorText) return null;
|
|
4969
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.
|
|
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
|
+
);
|
|
4970
5527
|
}
|
|
4971
5528
|
const citations = message.citations ?? [];
|
|
4972
5529
|
const agentText = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "message-bubble__text", children: [
|
|
@@ -5003,40 +5560,48 @@ function MessageBubble({
|
|
|
5003
5560
|
] });
|
|
5004
5561
|
if (!displayText && !message.searchResults?.length && offers.length === 0)
|
|
5005
5562
|
return null;
|
|
5006
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
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
|
+
);
|
|
5031
5596
|
}
|
|
5032
5597
|
|
|
5033
5598
|
// src/react/components/MessageActions/MessageActions.tsx
|
|
5034
|
-
var
|
|
5599
|
+
var import_react12 = require("react");
|
|
5035
5600
|
var import_react_dom2 = require("react-dom");
|
|
5036
5601
|
|
|
5037
5602
|
// src/react/lib/speech.ts
|
|
5038
|
-
function toSpeechText(
|
|
5039
|
-
let out = hideToolCardFences(
|
|
5603
|
+
function toSpeechText(text3) {
|
|
5604
|
+
let out = hideToolCardFences(text3);
|
|
5040
5605
|
out = out.replace(/```[\s\S]*?```/g, " ");
|
|
5041
5606
|
out = out.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
5042
5607
|
out = out.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
|
|
@@ -5371,12 +5936,12 @@ function StopIcon() {
|
|
|
5371
5936
|
}
|
|
5372
5937
|
) });
|
|
5373
5938
|
}
|
|
5374
|
-
async function writeToClipboard(
|
|
5939
|
+
async function writeToClipboard(text3) {
|
|
5375
5940
|
try {
|
|
5376
|
-
await navigator.clipboard.writeText(
|
|
5941
|
+
await navigator.clipboard.writeText(text3);
|
|
5377
5942
|
} catch {
|
|
5378
5943
|
const textarea = document.createElement("textarea");
|
|
5379
|
-
textarea.value =
|
|
5944
|
+
textarea.value = text3;
|
|
5380
5945
|
textarea.style.position = "fixed";
|
|
5381
5946
|
textarea.style.opacity = "0";
|
|
5382
5947
|
document.body.appendChild(textarea);
|
|
@@ -5427,26 +5992,26 @@ function MessageActions({
|
|
|
5427
5992
|
speechText
|
|
5428
5993
|
}) {
|
|
5429
5994
|
const menuPortalRoot = useAgentRailMenuPortalRoot();
|
|
5430
|
-
const [copied, setCopied] = (0,
|
|
5431
|
-
const [rating, setRating] = (0,
|
|
5432
|
-
const [menuOpen, setMenuOpen] = (0,
|
|
5433
|
-
const [menuPosition, setMenuPosition] = (0,
|
|
5434
|
-
const [speaking, setSpeaking] = (0,
|
|
5435
|
-
const [speechSupported, setSpeechSupported] = (0,
|
|
5436
|
-
const copyTimerRef = (0,
|
|
5437
|
-
const menuRef = (0,
|
|
5438
|
-
const portaledMenuRef = (0,
|
|
5439
|
-
const moreButtonRef = (0,
|
|
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);
|
|
5440
6005
|
const answeredLabel = formatAnsweredAt(answeredAt ?? 0);
|
|
5441
6006
|
const resolvedSpeechText = speechText ?? toSpeechText(copyText);
|
|
5442
6007
|
const canOpenReceipt = Boolean(receiptSteps) && Boolean(onOpenReceipt);
|
|
5443
6008
|
const readAloudEligible = Boolean(readAloud && resolvedSpeechText);
|
|
5444
6009
|
const canReadAloud = readAloudEligible && speechSupported === true;
|
|
5445
6010
|
const showMenu = canOpenReceipt || (speechSupported === null ? readAloudEligible : canReadAloud);
|
|
5446
|
-
(0,
|
|
6011
|
+
(0, import_react12.useLayoutEffect)(() => {
|
|
5447
6012
|
setSpeechSupported(isSpeechSupported());
|
|
5448
6013
|
}, []);
|
|
5449
|
-
(0,
|
|
6014
|
+
(0, import_react12.useLayoutEffect)(() => {
|
|
5450
6015
|
if (!menuOpen || !moreButtonRef.current || !portaledMenuRef.current) {
|
|
5451
6016
|
setMenuPosition(null);
|
|
5452
6017
|
return;
|
|
@@ -5463,7 +6028,7 @@ function MessageActions({
|
|
|
5463
6028
|
})
|
|
5464
6029
|
);
|
|
5465
6030
|
}, [menuOpen, menuPortalRoot]);
|
|
5466
|
-
(0,
|
|
6031
|
+
(0, import_react12.useEffect)(() => {
|
|
5467
6032
|
const unsubscribe = subscribeSpeechInterrupts(() => setSpeaking(false));
|
|
5468
6033
|
return () => {
|
|
5469
6034
|
unsubscribe();
|
|
@@ -5473,7 +6038,7 @@ function MessageActions({
|
|
|
5473
6038
|
stopSpeech();
|
|
5474
6039
|
};
|
|
5475
6040
|
}, []);
|
|
5476
|
-
(0,
|
|
6041
|
+
(0, import_react12.useEffect)(() => {
|
|
5477
6042
|
if (!menuOpen) return;
|
|
5478
6043
|
const handlePointerDown = (event) => {
|
|
5479
6044
|
const target = event.target;
|
|
@@ -5642,7 +6207,7 @@ function MessageActions({
|
|
|
5642
6207
|
}
|
|
5643
6208
|
|
|
5644
6209
|
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
5645
|
-
var
|
|
6210
|
+
var import_react14 = require("react");
|
|
5646
6211
|
|
|
5647
6212
|
// src/react/components/ConfirmationCard/ConfirmationCard.tsx
|
|
5648
6213
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
@@ -5684,7 +6249,7 @@ function ConfirmationCard({
|
|
|
5684
6249
|
}
|
|
5685
6250
|
|
|
5686
6251
|
// src/react/components/ToolInputCard/ToolInputCard.tsx
|
|
5687
|
-
var
|
|
6252
|
+
var import_react13 = require("react");
|
|
5688
6253
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
5689
6254
|
function isRecord5(value) {
|
|
5690
6255
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -5807,13 +6372,13 @@ function valuesMatch(left, right) {
|
|
|
5807
6372
|
}
|
|
5808
6373
|
function FieldDescription({
|
|
5809
6374
|
field,
|
|
5810
|
-
id
|
|
6375
|
+
id: id2
|
|
5811
6376
|
}) {
|
|
5812
|
-
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;
|
|
5813
6378
|
}
|
|
5814
6379
|
function ChoiceField({
|
|
5815
6380
|
field,
|
|
5816
|
-
id,
|
|
6381
|
+
id: id2,
|
|
5817
6382
|
value,
|
|
5818
6383
|
disabled,
|
|
5819
6384
|
describedBy,
|
|
@@ -5831,7 +6396,7 @@ function ChoiceField({
|
|
|
5831
6396
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
5832
6397
|
"select",
|
|
5833
6398
|
{
|
|
5834
|
-
id,
|
|
6399
|
+
id: id2,
|
|
5835
6400
|
value: selectedIndex >= 0 ? String(selectedIndex) : "",
|
|
5836
6401
|
disabled,
|
|
5837
6402
|
required: field.required,
|
|
@@ -5854,7 +6419,7 @@ function ChoiceField({
|
|
|
5854
6419
|
{
|
|
5855
6420
|
className: "tool-input-card__choices",
|
|
5856
6421
|
role: multiple ? "group" : "radiogroup",
|
|
5857
|
-
"aria-labelledby": `${
|
|
6422
|
+
"aria-labelledby": `${id2}-label`,
|
|
5858
6423
|
"aria-describedby": describedBy,
|
|
5859
6424
|
"aria-invalid": Boolean(error),
|
|
5860
6425
|
"aria-required": field.required,
|
|
@@ -5865,7 +6430,7 @@ function ChoiceField({
|
|
|
5865
6430
|
"input",
|
|
5866
6431
|
{
|
|
5867
6432
|
type: multiple ? "checkbox" : "radio",
|
|
5868
|
-
name:
|
|
6433
|
+
name: id2,
|
|
5869
6434
|
value: String(index),
|
|
5870
6435
|
checked,
|
|
5871
6436
|
disabled,
|
|
@@ -5893,22 +6458,22 @@ function ToolField({
|
|
|
5893
6458
|
disabled,
|
|
5894
6459
|
error,
|
|
5895
6460
|
field,
|
|
5896
|
-
id,
|
|
6461
|
+
id: id2,
|
|
5897
6462
|
value,
|
|
5898
6463
|
onBlur,
|
|
5899
6464
|
onChange
|
|
5900
6465
|
}) {
|
|
5901
6466
|
const describedBy = [
|
|
5902
|
-
field.description ? `${
|
|
5903
|
-
error ? `${
|
|
6467
|
+
field.description ? `${id2}-description` : "",
|
|
6468
|
+
error ? `${id2}-error` : ""
|
|
5904
6469
|
].filter(Boolean).join(" ");
|
|
5905
6470
|
if (field.kind === "checkbox" || field.kind === "confirmation") {
|
|
5906
6471
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-input-card__field", children: [
|
|
5907
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "tool-input-card__check", htmlFor:
|
|
6472
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "tool-input-card__check", htmlFor: id2, children: [
|
|
5908
6473
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
5909
6474
|
"input",
|
|
5910
6475
|
{
|
|
5911
|
-
id,
|
|
6476
|
+
id: id2,
|
|
5912
6477
|
type: "checkbox",
|
|
5913
6478
|
checked: value === true,
|
|
5914
6479
|
disabled,
|
|
@@ -5920,18 +6485,18 @@ function ToolField({
|
|
|
5920
6485
|
),
|
|
5921
6486
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
|
|
5922
6487
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: field.label }),
|
|
5923
|
-
field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${
|
|
6488
|
+
field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-description`, children: field.description }) : null
|
|
5924
6489
|
] })
|
|
5925
6490
|
] }),
|
|
5926
|
-
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${
|
|
6491
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
|
|
5927
6492
|
] });
|
|
5928
6493
|
}
|
|
5929
|
-
const label = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { id: `${
|
|
6494
|
+
const label = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { id: `${id2}-label`, htmlFor: id2, children: [
|
|
5930
6495
|
field.label,
|
|
5931
6496
|
field.required ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { "aria-hidden": "true", children: " *" }) : null
|
|
5932
6497
|
] });
|
|
5933
6498
|
const common = {
|
|
5934
|
-
id,
|
|
6499
|
+
id: id2,
|
|
5935
6500
|
disabled,
|
|
5936
6501
|
required: field.required,
|
|
5937
6502
|
"aria-describedby": describedBy || void 0,
|
|
@@ -5944,7 +6509,7 @@ function ToolField({
|
|
|
5944
6509
|
ChoiceField,
|
|
5945
6510
|
{
|
|
5946
6511
|
field,
|
|
5947
|
-
id,
|
|
6512
|
+
id: id2,
|
|
5948
6513
|
value,
|
|
5949
6514
|
disabled,
|
|
5950
6515
|
describedBy: describedBy || void 0,
|
|
@@ -5981,7 +6546,7 @@ function ToolField({
|
|
|
5981
6546
|
onChange: (event) => onChange(Number(event.target.value))
|
|
5982
6547
|
}
|
|
5983
6548
|
),
|
|
5984
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("output", { htmlFor:
|
|
6549
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("output", { htmlFor: id2, children: numericValue })
|
|
5985
6550
|
] });
|
|
5986
6551
|
} else {
|
|
5987
6552
|
const type = field.kind === "date-time" ? "datetime-local" : field.kind === "calendar" ? "date" : field.kind;
|
|
@@ -6005,9 +6570,9 @@ function ToolField({
|
|
|
6005
6570
|
}
|
|
6006
6571
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-input-card__field", children: [
|
|
6007
6572
|
label,
|
|
6008
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldDescription, { field, id: `${
|
|
6573
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldDescription, { field, id: `${id2}-description` }),
|
|
6009
6574
|
control,
|
|
6010
|
-
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${
|
|
6575
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
|
|
6011
6576
|
] });
|
|
6012
6577
|
}
|
|
6013
6578
|
function ToolInputCard({
|
|
@@ -6015,11 +6580,11 @@ function ToolInputCard({
|
|
|
6015
6580
|
surface,
|
|
6016
6581
|
onSubmit
|
|
6017
6582
|
}) {
|
|
6018
|
-
const [values, setValues] = (0,
|
|
6583
|
+
const [values, setValues] = (0, import_react13.useState)(
|
|
6019
6584
|
() => initialValues(surface)
|
|
6020
6585
|
);
|
|
6021
|
-
const [touched, setTouched] = (0,
|
|
6022
|
-
const [submitted, setSubmitted] = (0,
|
|
6586
|
+
const [touched, setTouched] = (0, import_react13.useState)(() => /* @__PURE__ */ new Set());
|
|
6587
|
+
const [submitted, setSubmitted] = (0, import_react13.useState)(false);
|
|
6023
6588
|
const errors = Object.fromEntries(
|
|
6024
6589
|
surface.fields.map((field) => [
|
|
6025
6590
|
field.path,
|
|
@@ -6112,7 +6677,7 @@ function HumanInputCard({
|
|
|
6112
6677
|
request,
|
|
6113
6678
|
onRespond
|
|
6114
6679
|
}) {
|
|
6115
|
-
const [
|
|
6680
|
+
const [text3, setText] = (0, import_react14.useState)("");
|
|
6116
6681
|
const options = request.options ?? [];
|
|
6117
6682
|
const showText = request.display === "text" || request.allowFreeform && options.length === 0;
|
|
6118
6683
|
if (request.ui) {
|
|
@@ -6137,7 +6702,7 @@ function HumanInputCard({
|
|
|
6137
6702
|
}
|
|
6138
6703
|
function submitText(event) {
|
|
6139
6704
|
event.preventDefault();
|
|
6140
|
-
const value =
|
|
6705
|
+
const value = text3.trim();
|
|
6141
6706
|
if (!value || disabled) return;
|
|
6142
6707
|
onRespond?.({ requestId: request.requestId, text: value });
|
|
6143
6708
|
}
|
|
@@ -6155,12 +6720,12 @@ function HumanInputCard({
|
|
|
6155
6720
|
"input",
|
|
6156
6721
|
{
|
|
6157
6722
|
id: `human-input-text-${request.requestId}`,
|
|
6158
|
-
value:
|
|
6723
|
+
value: text3,
|
|
6159
6724
|
disabled,
|
|
6160
6725
|
onChange: (event) => setText(event.target.value)
|
|
6161
6726
|
}
|
|
6162
6727
|
),
|
|
6163
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", disabled: disabled || !
|
|
6728
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", disabled: disabled || !text3.trim(), children: "Send" })
|
|
6164
6729
|
] })
|
|
6165
6730
|
] }) : null,
|
|
6166
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
|
|
@@ -6170,7 +6735,7 @@ function HumanInputCard({
|
|
|
6170
6735
|
}
|
|
6171
6736
|
|
|
6172
6737
|
// src/react/components/LocationConsent/LocationConsent.tsx
|
|
6173
|
-
var
|
|
6738
|
+
var import_react15 = require("react");
|
|
6174
6739
|
|
|
6175
6740
|
// src/runtime/location-consent.ts
|
|
6176
6741
|
function isLocationConsentRequest(request) {
|
|
@@ -6218,16 +6783,16 @@ function LocationConsent({
|
|
|
6218
6783
|
request,
|
|
6219
6784
|
onRespond
|
|
6220
6785
|
}) {
|
|
6221
|
-
const [preference, setPreference] = (0,
|
|
6222
|
-
const [locating, setLocating] = (0,
|
|
6223
|
-
const [error, setError] = (0,
|
|
6224
|
-
const respond = (0,
|
|
6225
|
-
const answered = (0,
|
|
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);
|
|
6226
6791
|
const requestId = request?.requestId;
|
|
6227
|
-
(0,
|
|
6792
|
+
(0, import_react15.useEffect)(() => {
|
|
6228
6793
|
respond.current = onRespond;
|
|
6229
6794
|
}, [onRespond]);
|
|
6230
|
-
(0,
|
|
6795
|
+
(0, import_react15.useEffect)(() => {
|
|
6231
6796
|
if (!requestId || preference === "unset" || answered.current === requestId || !respond.current)
|
|
6232
6797
|
return;
|
|
6233
6798
|
if (preference === "off") {
|
|
@@ -6569,6 +7134,7 @@ function ChevronDownIcon() {
|
|
|
6569
7134
|
var DEFAULT_DISCLAIMER_LABEL = "Agent can make mistakes. Check important info.";
|
|
6570
7135
|
function AgentRail({
|
|
6571
7136
|
state,
|
|
7137
|
+
handoff,
|
|
6572
7138
|
theme,
|
|
6573
7139
|
colorScheme = "auto",
|
|
6574
7140
|
brandLabel = "",
|
|
@@ -6594,44 +7160,51 @@ function AgentRail({
|
|
|
6594
7160
|
onInputResponse,
|
|
6595
7161
|
onToolInput
|
|
6596
7162
|
}) {
|
|
6597
|
-
const railRef = (0,
|
|
6598
|
-
const overlayRef = (0,
|
|
6599
|
-
const transcriptRef = (0,
|
|
6600
|
-
const responseRef = (0,
|
|
6601
|
-
const threadRef = (0,
|
|
6602
|
-
const lastScrolledVisitorIdRef = (0,
|
|
6603
|
-
const pinnedToBottomRef = (0,
|
|
6604
|
-
const smoothScrollToLatestRef = (0,
|
|
6605
|
-
const lockedTranscriptScrollTopRef = (0,
|
|
6606
|
-
const [showJumpToLatest, setShowJumpToLatest] = (0,
|
|
6607
|
-
const [receiptOpen, setReceiptOpen] = (0,
|
|
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);
|
|
6608
7174
|
const resolvedBrandLabel = brandLabel.trim();
|
|
6609
7175
|
const resolvedBrandLogoUrl = brandLogoUrl?.trim();
|
|
6610
7176
|
const resolvedDisclaimerLabel = disclaimerLabel === void 0 ? DEFAULT_DISCLAIMER_LABEL : disclaimerLabel;
|
|
6611
|
-
const [failedLogoUrl, setFailedLogoUrl] = (0,
|
|
7177
|
+
const [failedLogoUrl, setFailedLogoUrl] = (0, import_react16.useState)(null);
|
|
6612
7178
|
const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
|
|
6613
7179
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
6614
7180
|
const railStyle = agentThemeStyle(theme, resolvedColorScheme);
|
|
6615
|
-
const
|
|
7181
|
+
const handoffActive = Boolean(handoff?.blocksAI);
|
|
7182
|
+
const handoffStatus = handoff?.page?.status;
|
|
7183
|
+
const locationRequest = handoffActive ? void 0 : state.pendingInputs?.find(isLocationConsentRequest);
|
|
6616
7184
|
const pendingInputRequests = (state.pendingInputs ?? []).filter(
|
|
6617
|
-
(request) => !isLocationConsentRequest(request) && shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
7185
|
+
(request) => !handoffActive && !isLocationConsentRequest(request) && shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
6618
7186
|
);
|
|
6619
7187
|
const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && (pendingInputRequests.length > 0 || Boolean(locationRequest));
|
|
6620
|
-
const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
|
|
7188
|
+
const semanticSurfaceDisabled = handoffActive || isBusy && state.phase !== "waiting-input";
|
|
6621
7189
|
const visitorToolResults = (state.toolResults ?? []).filter(
|
|
6622
7190
|
isRenderableVisitorToolResult
|
|
6623
7191
|
);
|
|
6624
7192
|
const activeVisitorToolInput = [...visitorToolResults].reverse().find((result) => result.kind === "input");
|
|
6625
7193
|
const visibleVisitorToolResults = activeVisitorToolInput ? [activeVisitorToolInput] : visitorToolResults;
|
|
6626
|
-
const activityActive = state.toolSteps.some(
|
|
7194
|
+
const activityActive = state.toolSteps.some(
|
|
7195
|
+
(step) => step.state === "active"
|
|
7196
|
+
);
|
|
6627
7197
|
const showActivity = state.toolSteps.length > 0 && pendingInputRequests.length === 0 && !state.pendingOffer && (activityActive || isBusy && !state.streamingText);
|
|
6628
|
-
const activitySteps = activityActive || !isBusy || state.streamingText ? state.toolSteps : [
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
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
|
+
];
|
|
6635
7208
|
const hasVisitorMessages2 = state.messages.some(
|
|
6636
7209
|
(message) => message.role === "visitor"
|
|
6637
7210
|
);
|
|
@@ -6660,7 +7233,7 @@ function AgentRail({
|
|
|
6660
7233
|
}
|
|
6661
7234
|
}
|
|
6662
7235
|
const lastIsAgent = lastMessage?.role === "agent";
|
|
6663
|
-
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());
|
|
6664
7237
|
const streamingMessage = state.streamingText && !lastIsAgent ? {
|
|
6665
7238
|
createdAt: 0,
|
|
6666
7239
|
id: "streaming-response",
|
|
@@ -6702,7 +7275,7 @@ function AgentRail({
|
|
|
6702
7275
|
...visibleVisitorToolResults
|
|
6703
7276
|
].reverse().find((result) => result.kind !== "input")?.id;
|
|
6704
7277
|
const receiptSteps = answerReceipt && state.toolSteps.length > 0 ? state.toolSteps : void 0;
|
|
6705
|
-
(0,
|
|
7278
|
+
(0, import_react16.useEffect)(() => {
|
|
6706
7279
|
if (state.phase !== "complete") {
|
|
6707
7280
|
setReceiptOpen(false);
|
|
6708
7281
|
}
|
|
@@ -6724,7 +7297,7 @@ function AgentRail({
|
|
|
6724
7297
|
onFollowUpSelect?.(label);
|
|
6725
7298
|
}
|
|
6726
7299
|
const latestVisitorId = visibleMessages[lastVisitorIndex]?.id;
|
|
6727
|
-
(0,
|
|
7300
|
+
(0, import_react16.useEffect)(() => {
|
|
6728
7301
|
const node = transcriptRef.current;
|
|
6729
7302
|
if (!node) return;
|
|
6730
7303
|
if (latestVisitorId !== lastScrolledVisitorIdRef.current) {
|
|
@@ -6754,7 +7327,7 @@ function AgentRail({
|
|
|
6754
7327
|
state.followUps,
|
|
6755
7328
|
state.journey
|
|
6756
7329
|
]);
|
|
6757
|
-
(0,
|
|
7330
|
+
(0, import_react16.useEffect)(() => {
|
|
6758
7331
|
const node = transcriptRef.current;
|
|
6759
7332
|
if (!node) return;
|
|
6760
7333
|
const handleScroll = () => {
|
|
@@ -6768,7 +7341,7 @@ function AgentRail({
|
|
|
6768
7341
|
handleScroll();
|
|
6769
7342
|
return () => node.removeEventListener("scroll", handleScroll);
|
|
6770
7343
|
}, []);
|
|
6771
|
-
(0,
|
|
7344
|
+
(0, import_react16.useEffect)(() => {
|
|
6772
7345
|
if (!receiptOpen) {
|
|
6773
7346
|
lockedTranscriptScrollTopRef.current = null;
|
|
6774
7347
|
return;
|
|
@@ -6820,249 +7393,302 @@ function AgentRail({
|
|
|
6820
7393
|
autoFocus: mobileFullscreen || expanded,
|
|
6821
7394
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
6822
7395
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
6823
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
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);
|
|
6847
7427
|
}
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
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,
|
|
6877
7514
|
{
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
}
|
|
6884
|
-
) : null
|
|
6885
|
-
] })
|
|
6886
|
-
] }) }),
|
|
6887
|
-
/* @__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: [
|
|
6888
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
6889
|
-
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7515
|
+
result
|
|
7516
|
+
},
|
|
7517
|
+
result.id
|
|
7518
|
+
)) : null,
|
|
7519
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6890
7520
|
MessageBubble,
|
|
6891
7521
|
{
|
|
6892
|
-
|
|
6893
|
-
|
|
7522
|
+
showProvenance: Boolean(
|
|
7523
|
+
handoff?.page?.enabled || handoffActive
|
|
7524
|
+
),
|
|
7525
|
+
message,
|
|
7526
|
+
bookingDisabled: isBusy || handoffActive,
|
|
6894
7527
|
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7528
|
+
offer: index === transcriptMessages.length - 1 ? state.pendingOffer : void 0,
|
|
6895
7529
|
onBook
|
|
6896
7530
|
}
|
|
6897
|
-
)
|
|
6898
|
-
|
|
6899
|
-
|
|
7531
|
+
),
|
|
7532
|
+
index === lastAgentIndex && showMessageActions && message.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7533
|
+
MessageActions,
|
|
6900
7534
|
{
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
AgentActivityBubble,
|
|
6909
|
-
{
|
|
6910
|
-
brandLabel: resolvedBrandLabel,
|
|
6911
|
-
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
6912
|
-
failed: state.phase === "error",
|
|
6913
|
-
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
|
|
6914
7542
|
}
|
|
6915
7543
|
) : null,
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { children: state.error })
|
|
7000
|
-
] }),
|
|
7001
|
-
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
7002
|
-
] }) : null
|
|
7003
|
-
] }) }),
|
|
7004
|
-
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)(
|
|
7005
|
-
"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",
|
|
7006
7627
|
{
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
children:
|
|
7628
|
+
type: "button",
|
|
7629
|
+
disabled: handoff.busy,
|
|
7630
|
+
onClick: () => void handoff.retry(),
|
|
7631
|
+
children: "Retry"
|
|
7011
7632
|
}
|
|
7012
|
-
)
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
className: "agent-rail__jump-to-latest",
|
|
7027
|
-
"aria-label": "Jump to the latest message",
|
|
7028
|
-
title: "Jump to the latest message",
|
|
7029
|
-
onClick: scrollToLatest,
|
|
7030
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronDownIcon, {})
|
|
7031
|
-
}
|
|
7032
|
-
) : null,
|
|
7033
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7034
|
-
Composer,
|
|
7035
|
-
{
|
|
7036
|
-
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
7037
|
-
disabled: isBusy,
|
|
7038
|
-
form: composerForm && lastMessage ? { ...composerForm, id: `${lastMessage.id}:${composerForm.id}` } : null,
|
|
7039
|
-
allowFormResume: !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
|
|
7040
|
-
placeholder: composerPlaceholder,
|
|
7041
|
-
onSubmit: handleSubmit
|
|
7042
|
-
},
|
|
7043
|
-
`${state.messages.find((message) => message.role === "visitor")?.id ?? "empty"}:${latestResultId ?? ""}`
|
|
7044
|
-
),
|
|
7045
|
-
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
|
|
7046
|
-
] })
|
|
7047
|
-
] }),
|
|
7048
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: overlayRef, className: "agent-rail__overlay" }),
|
|
7049
|
-
receiptOpen && receiptSteps ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7050
|
-
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",
|
|
7051
7647
|
{
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
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, {})
|
|
7055
7654
|
}
|
|
7056
|
-
) : null
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
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
|
+
] })
|
|
7060
7686
|
}
|
|
7061
7687
|
);
|
|
7062
7688
|
}
|
|
7063
7689
|
|
|
7064
7690
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
7065
|
-
var
|
|
7691
|
+
var import_react17 = require("react");
|
|
7066
7692
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
7067
7693
|
function ChatSparkIcon() {
|
|
7068
7694
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
@@ -7143,8 +7769,8 @@ function TabMarkIcon({
|
|
|
7143
7769
|
}) {
|
|
7144
7770
|
const custom = customIconUrl?.trim();
|
|
7145
7771
|
const logo = logoUrl?.trim();
|
|
7146
|
-
const [customFailed, setCustomFailed] = (0,
|
|
7147
|
-
const [logoFailed, setLogoFailed] = (0,
|
|
7772
|
+
const [customFailed, setCustomFailed] = (0, import_react17.useState)(false);
|
|
7773
|
+
const [logoFailed, setLogoFailed] = (0, import_react17.useState)(false);
|
|
7148
7774
|
if (custom && !customFailed) {
|
|
7149
7775
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
7150
7776
|
"img",
|
|
@@ -7332,8 +7958,8 @@ function findPrecedingVisitorText(messages, agentMessageId) {
|
|
|
7332
7958
|
}
|
|
7333
7959
|
return void 0;
|
|
7334
7960
|
}
|
|
7335
|
-
function normalizeAgentFeedbackAnswerText(
|
|
7336
|
-
const normalized =
|
|
7961
|
+
function normalizeAgentFeedbackAnswerText(text3) {
|
|
7962
|
+
const normalized = text3.replace(/\s+/g, " ").trim();
|
|
7337
7963
|
if (!normalized) return void 0;
|
|
7338
7964
|
return normalized.length > 4e3 ? `${normalized.slice(0, 3997)}...` : normalized;
|
|
7339
7965
|
}
|
|
@@ -7393,9 +8019,9 @@ function AgentWidget({
|
|
|
7393
8019
|
}) {
|
|
7394
8020
|
const isMobile = useIsMobile();
|
|
7395
8021
|
const placement = normalizeAgentPlacement(placementInput);
|
|
7396
|
-
const railSlotRef = (0,
|
|
7397
|
-
const [railCollapsed, setRailCollapsed] = (0,
|
|
7398
|
-
const [railExpanded, setRailExpanded] = (0,
|
|
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);
|
|
7399
8025
|
const pageShiftActive = shouldApplyPageShift({
|
|
7400
8026
|
pageShift,
|
|
7401
8027
|
isMobile,
|
|
@@ -7448,7 +8074,7 @@ function AgentWidget({
|
|
|
7448
8074
|
} : {},
|
|
7449
8075
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
7450
8076
|
};
|
|
7451
|
-
(0,
|
|
8077
|
+
(0, import_react18.useEffect)(() => {
|
|
7452
8078
|
if (!registerPanelController) return;
|
|
7453
8079
|
registerAgentPanelController(customerId, {
|
|
7454
8080
|
open: () => setRailCollapsed(false),
|
|
@@ -7483,7 +8109,7 @@ function AgentWidget({
|
|
|
7483
8109
|
})
|
|
7484
8110
|
);
|
|
7485
8111
|
}
|
|
7486
|
-
(0,
|
|
8112
|
+
(0, import_react18.useEffect)(() => {
|
|
7487
8113
|
if (railCollapsed) return;
|
|
7488
8114
|
const handleKeyDown = (event) => {
|
|
7489
8115
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -7765,7 +8391,7 @@ function mountAgent(input) {
|
|
|
7765
8391
|
const mountTarget = resolveMountHost(manifest, input.script ?? null);
|
|
7766
8392
|
const host = createHost(manifest.customerId);
|
|
7767
8393
|
mountTarget.append(host);
|
|
7768
|
-
const root = (0,
|
|
8394
|
+
const root = (0, import_client7.createRoot)(host);
|
|
7769
8395
|
root.render(/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AgentWidget2, { manifest }));
|
|
7770
8396
|
const handle = {
|
|
7771
8397
|
customerId: manifest.customerId,
|
|
@@ -7792,9 +8418,9 @@ function mountAgent(input) {
|
|
|
7792
8418
|
return handle;
|
|
7793
8419
|
}
|
|
7794
8420
|
function unmountAgent(customerId) {
|
|
7795
|
-
const
|
|
7796
|
-
if (!
|
|
7797
|
-
const handle = mountedHandles.get(
|
|
8421
|
+
const id2 = customerId ?? latestCustomerId;
|
|
8422
|
+
if (!id2) return false;
|
|
8423
|
+
const handle = mountedHandles.get(id2);
|
|
7798
8424
|
if (!handle) return false;
|
|
7799
8425
|
handle.unmount();
|
|
7800
8426
|
return true;
|