@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/react.cjs
CHANGED
|
@@ -46,7 +46,7 @@ __export(react_exports, {
|
|
|
46
46
|
module.exports = __toCommonJS(react_exports);
|
|
47
47
|
|
|
48
48
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
49
|
-
var
|
|
49
|
+
var import_react18 = require("react");
|
|
50
50
|
|
|
51
51
|
// src/react/page-shift.ts
|
|
52
52
|
var import_react = require("react");
|
|
@@ -128,8 +128,168 @@ function usePageShift(input) {
|
|
|
128
128
|
}, [active, railSlotRef]);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
// src/react/hooks/
|
|
131
|
+
// src/react/hooks/useAgentHandoff.ts
|
|
132
|
+
var import_client = require("eve/client");
|
|
132
133
|
var import_react2 = require("react");
|
|
134
|
+
function useAgentHandoff(options) {
|
|
135
|
+
const [page, setPage] = (0, import_react2.useState)(null);
|
|
136
|
+
const [busy, setBusy] = (0, import_react2.useState)(false);
|
|
137
|
+
const [rejected, setRejected] = (0, import_react2.useState)(false);
|
|
138
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
139
|
+
const callbacks = (0, import_react2.useRef)(options);
|
|
140
|
+
callbacks.current = options;
|
|
141
|
+
const pending = (0, import_react2.useRef)(null);
|
|
142
|
+
const inFlight = (0, import_react2.useRef)(null);
|
|
143
|
+
const pageRef = (0, import_react2.useRef)(null);
|
|
144
|
+
const refreshRef = (0, import_react2.useRef)(null);
|
|
145
|
+
const generation = (0, import_react2.useRef)(0);
|
|
146
|
+
(0, import_react2.useEffect)(() => {
|
|
147
|
+
const token = ++generation.current;
|
|
148
|
+
const controller = new AbortController();
|
|
149
|
+
let timer;
|
|
150
|
+
let reading = null;
|
|
151
|
+
let cursor = 0;
|
|
152
|
+
pageRef.current = null;
|
|
153
|
+
pending.current = null;
|
|
154
|
+
inFlight.current?.abort();
|
|
155
|
+
inFlight.current = null;
|
|
156
|
+
setRejected(false);
|
|
157
|
+
setPage(null);
|
|
158
|
+
setError(null);
|
|
159
|
+
setBusy(false);
|
|
160
|
+
if (!options.enabled || !options.sessionId) return;
|
|
161
|
+
const active = () => !controller.signal.aborted && generation.current === token;
|
|
162
|
+
const refresh = () => {
|
|
163
|
+
reading ??= (async () => {
|
|
164
|
+
do {
|
|
165
|
+
const next = await options.client.handoff.read({
|
|
166
|
+
after: cursor,
|
|
167
|
+
signal: AbortSignal.any([
|
|
168
|
+
controller.signal,
|
|
169
|
+
AbortSignal.timeout(15e3)
|
|
170
|
+
])
|
|
171
|
+
});
|
|
172
|
+
if (!active()) return;
|
|
173
|
+
callbacks.current.onEvents(next.events);
|
|
174
|
+
if (next.status !== "ai") callbacks.current.onOwnership();
|
|
175
|
+
cursor = next.cursor;
|
|
176
|
+
pageRef.current = next;
|
|
177
|
+
setPage(next);
|
|
178
|
+
setRejected(false);
|
|
179
|
+
if (!pending.current) setError(null);
|
|
180
|
+
if (!next.hasMore) break;
|
|
181
|
+
} while (active());
|
|
182
|
+
})().finally(() => {
|
|
183
|
+
reading = null;
|
|
184
|
+
});
|
|
185
|
+
return reading;
|
|
186
|
+
};
|
|
187
|
+
refreshRef.current = refresh;
|
|
188
|
+
const poll = async () => {
|
|
189
|
+
try {
|
|
190
|
+
await refresh();
|
|
191
|
+
} catch (cause) {
|
|
192
|
+
if (active()) {
|
|
193
|
+
const denied = cause instanceof import_client.ClientError && cause.status === 403;
|
|
194
|
+
setRejected(denied);
|
|
195
|
+
setError(
|
|
196
|
+
denied ? "This preview has changed. Restart the preview chat to test the current version." : "Reconnecting to your conversation\u2026"
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
} finally {
|
|
200
|
+
if (active()) timer = setTimeout(() => void poll(), 1500);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
void poll();
|
|
204
|
+
return () => {
|
|
205
|
+
controller.abort();
|
|
206
|
+
inFlight.current?.abort();
|
|
207
|
+
clearTimeout(timer);
|
|
208
|
+
refreshRef.current = null;
|
|
209
|
+
};
|
|
210
|
+
}, [options.client, options.enabled, options.sessionId]);
|
|
211
|
+
async function execute(operation) {
|
|
212
|
+
if (inFlight.current) return;
|
|
213
|
+
const controller = new AbortController();
|
|
214
|
+
inFlight.current = controller;
|
|
215
|
+
const signal = AbortSignal.any([
|
|
216
|
+
controller.signal,
|
|
217
|
+
AbortSignal.timeout(15e3)
|
|
218
|
+
]);
|
|
219
|
+
const token = generation.current;
|
|
220
|
+
pending.current = operation;
|
|
221
|
+
setBusy(true);
|
|
222
|
+
setError(null);
|
|
223
|
+
try {
|
|
224
|
+
if (operation.type === "start")
|
|
225
|
+
await options.client.handoff.start(operation.operationId, signal);
|
|
226
|
+
else if (operation.type === "message") {
|
|
227
|
+
const { type: _, ...command } = operation;
|
|
228
|
+
await options.client.handoff.send(command, signal);
|
|
229
|
+
} else {
|
|
230
|
+
const { type: _, ...command } = operation;
|
|
231
|
+
await options.client.handoff.returnToAI(command, signal);
|
|
232
|
+
}
|
|
233
|
+
if (token !== generation.current) return;
|
|
234
|
+
pending.current = null;
|
|
235
|
+
await refreshRef.current?.();
|
|
236
|
+
} catch {
|
|
237
|
+
if (token === generation.current)
|
|
238
|
+
setError(
|
|
239
|
+
"Could not confirm delivery. Retry to check the same request."
|
|
240
|
+
);
|
|
241
|
+
} finally {
|
|
242
|
+
if (inFlight.current === controller) inFlight.current = null;
|
|
243
|
+
if (token === generation.current) setBusy(false);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const binding = () => {
|
|
247
|
+
const current = pageRef.current;
|
|
248
|
+
if (!current?.handoffId) throw new Error("No active human conversation.");
|
|
249
|
+
return { handoffId: current.handoffId, epoch: current.epoch };
|
|
250
|
+
};
|
|
251
|
+
return {
|
|
252
|
+
page,
|
|
253
|
+
busy,
|
|
254
|
+
error,
|
|
255
|
+
hasPending: Boolean(pending.current),
|
|
256
|
+
canReset: !busy && !pending.current && (rejected || page?.status === "ai" || page?.status === "failed" || !options.sessionId),
|
|
257
|
+
blocksAI: Boolean(options.enabled && options.sessionId && !page) || busy || Boolean(pending.current) || Boolean(page && page.status !== "ai"),
|
|
258
|
+
start: () => execute(
|
|
259
|
+
pending.current ?? { type: "start", operationId: crypto.randomUUID() }
|
|
260
|
+
),
|
|
261
|
+
send: (message) => {
|
|
262
|
+
if (pending.current)
|
|
263
|
+
throw new Error(
|
|
264
|
+
"Retry the pending request before sending another message."
|
|
265
|
+
);
|
|
266
|
+
return execute({
|
|
267
|
+
type: "message",
|
|
268
|
+
operationId: crypto.randomUUID(),
|
|
269
|
+
...binding(),
|
|
270
|
+
message
|
|
271
|
+
});
|
|
272
|
+
},
|
|
273
|
+
returnToAI: () => execute(
|
|
274
|
+
pending.current ?? {
|
|
275
|
+
type: "return",
|
|
276
|
+
operationId: crypto.randomUUID(),
|
|
277
|
+
...binding()
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
retry: async () => {
|
|
281
|
+
if (pending.current) return execute(pending.current);
|
|
282
|
+
try {
|
|
283
|
+
await refreshRef.current?.();
|
|
284
|
+
} catch {
|
|
285
|
+
setError("Reconnecting to your conversation\u2026");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/react/hooks/useAgentChat.ts
|
|
292
|
+
var import_react3 = require("react");
|
|
133
293
|
|
|
134
294
|
// src/runtime/tool-ui.ts
|
|
135
295
|
var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
|
|
@@ -271,16 +431,16 @@ function parseStep(value) {
|
|
|
271
431
|
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
272
432
|
return null;
|
|
273
433
|
}
|
|
274
|
-
const
|
|
434
|
+
const id2 = boundedString(value.id, 80);
|
|
275
435
|
const label = boundedString(value.label, 160);
|
|
276
436
|
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
277
|
-
if (!
|
|
437
|
+
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(
|
|
278
438
|
(path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
|
|
279
439
|
)) {
|
|
280
440
|
return null;
|
|
281
441
|
}
|
|
282
442
|
return {
|
|
283
|
-
id,
|
|
443
|
+
id: id2,
|
|
284
444
|
label,
|
|
285
445
|
fieldPaths: value.fieldPaths,
|
|
286
446
|
...description ? { description } : {}
|
|
@@ -317,14 +477,14 @@ function parseAgentToolUiSurface(value) {
|
|
|
317
477
|
])) {
|
|
318
478
|
return null;
|
|
319
479
|
}
|
|
320
|
-
const
|
|
480
|
+
const id2 = boundedString(value.id, 200);
|
|
321
481
|
const title = boundedString(value.title, 200);
|
|
322
482
|
const toolSlug = boundedString(value.toolSlug, 200);
|
|
323
483
|
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
324
484
|
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
325
485
|
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
326
486
|
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
327
|
-
if (!
|
|
487
|
+
if (!id2 || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
328
488
|
return null;
|
|
329
489
|
}
|
|
330
490
|
const fields = value.fields.map(parseField);
|
|
@@ -344,7 +504,7 @@ function parseAgentToolUiSurface(value) {
|
|
|
344
504
|
}
|
|
345
505
|
return {
|
|
346
506
|
schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
347
|
-
id,
|
|
507
|
+
id: id2,
|
|
348
508
|
title,
|
|
349
509
|
toolSlug,
|
|
350
510
|
fields,
|
|
@@ -455,10 +615,205 @@ function completeConnectedToolWork(item, result) {
|
|
|
455
615
|
}
|
|
456
616
|
|
|
457
617
|
// src/runtime/client.ts
|
|
458
|
-
var
|
|
618
|
+
var import_client4 = require("eve/client");
|
|
619
|
+
|
|
620
|
+
// src/runtime/handoff.ts
|
|
621
|
+
var import_client3 = require("eve/client");
|
|
622
|
+
|
|
623
|
+
// src/runtime/generated/handoff-contract.ts
|
|
624
|
+
var import_zod = require("zod");
|
|
625
|
+
var id = import_zod.z.string().min(1).max(200);
|
|
626
|
+
var sequence = import_zod.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
627
|
+
var text = import_zod.z.string().trim().min(1).max(16e3);
|
|
628
|
+
var agentRuntimeHandoffVersion = "webless.ai/agent-runtime-handoff/v1";
|
|
629
|
+
var agentRuntimeHandoffStatusSchema = import_zod.z.enum([
|
|
630
|
+
"ai",
|
|
631
|
+
"requesting",
|
|
632
|
+
"queued",
|
|
633
|
+
"human",
|
|
634
|
+
"resolved",
|
|
635
|
+
"failed"
|
|
636
|
+
]);
|
|
637
|
+
var agentRuntimeHandoffActorSchema = import_zod.z.strictObject({
|
|
638
|
+
id,
|
|
639
|
+
name: import_zod.z.string().trim().min(1).max(200)
|
|
640
|
+
});
|
|
641
|
+
var eventBase = {
|
|
642
|
+
id,
|
|
643
|
+
sequence: sequence.refine((value) => value > 0),
|
|
644
|
+
handoffId: id,
|
|
645
|
+
epoch: sequence.refine((value) => value > 0),
|
|
646
|
+
createdAt: import_zod.z.iso.datetime()
|
|
647
|
+
};
|
|
648
|
+
var agentRuntimeHandoffEventSchema = import_zod.z.discriminatedUnion("type", [
|
|
649
|
+
import_zod.z.strictObject({
|
|
650
|
+
...eventBase,
|
|
651
|
+
type: import_zod.z.literal("status"),
|
|
652
|
+
status: agentRuntimeHandoffStatusSchema,
|
|
653
|
+
actor: agentRuntimeHandoffActorSchema.nullable()
|
|
654
|
+
}),
|
|
655
|
+
import_zod.z.strictObject({
|
|
656
|
+
...eventBase,
|
|
657
|
+
type: import_zod.z.literal("visitor.message"),
|
|
658
|
+
message: text,
|
|
659
|
+
operationId: id
|
|
660
|
+
}),
|
|
661
|
+
import_zod.z.strictObject({
|
|
662
|
+
...eventBase,
|
|
663
|
+
type: import_zod.z.literal("human.message"),
|
|
664
|
+
message: text,
|
|
665
|
+
actor: agentRuntimeHandoffActorSchema
|
|
666
|
+
})
|
|
667
|
+
]);
|
|
668
|
+
var agentRuntimeHandoffReadRequestSchema = import_zod.z.strictObject({
|
|
669
|
+
after: sequence.default(0)
|
|
670
|
+
});
|
|
671
|
+
var agentRuntimeHandoffPageSchema = import_zod.z.strictObject({
|
|
672
|
+
apiVersion: import_zod.z.literal(agentRuntimeHandoffVersion),
|
|
673
|
+
sessionId: id,
|
|
674
|
+
enabled: import_zod.z.boolean(),
|
|
675
|
+
status: agentRuntimeHandoffStatusSchema,
|
|
676
|
+
handoffId: id.nullable(),
|
|
677
|
+
epoch: sequence,
|
|
678
|
+
after: sequence,
|
|
679
|
+
cursor: sequence,
|
|
680
|
+
hasMore: import_zod.z.boolean(),
|
|
681
|
+
events: import_zod.z.array(agentRuntimeHandoffEventSchema).max(100)
|
|
682
|
+
}).superRefine((page, ctx) => {
|
|
683
|
+
let cursor = page.after;
|
|
684
|
+
let previousEpoch = 0;
|
|
685
|
+
const statuses = /* @__PURE__ */ new Map();
|
|
686
|
+
const ids = /* @__PURE__ */ new Set();
|
|
687
|
+
const handoffs = /* @__PURE__ */ new Map();
|
|
688
|
+
const terminalEpochs = /* @__PURE__ */ new Set();
|
|
689
|
+
let currentStatus;
|
|
690
|
+
for (const event of page.events) {
|
|
691
|
+
if (event.epoch < previousEpoch)
|
|
692
|
+
ctx.addIssue({
|
|
693
|
+
code: "custom",
|
|
694
|
+
message: "Handoff epochs cannot decrease."
|
|
695
|
+
});
|
|
696
|
+
previousEpoch = event.epoch;
|
|
697
|
+
if (ids.has(event.id))
|
|
698
|
+
ctx.addIssue({
|
|
699
|
+
code: "custom",
|
|
700
|
+
message: "Duplicate handoff event ID."
|
|
701
|
+
});
|
|
702
|
+
ids.add(event.id);
|
|
703
|
+
const handoffId = handoffs.get(event.epoch);
|
|
704
|
+
if (handoffId !== void 0 && handoffId !== event.handoffId || event.epoch === page.epoch && event.handoffId !== page.handoffId)
|
|
705
|
+
ctx.addIssue({
|
|
706
|
+
code: "custom",
|
|
707
|
+
message: "Inconsistent handoff epoch identity."
|
|
708
|
+
});
|
|
709
|
+
handoffs.set(event.epoch, event.handoffId);
|
|
710
|
+
if (terminalEpochs.has(event.epoch) && (event.type !== "status" || event.status !== "ai"))
|
|
711
|
+
ctx.addIssue({
|
|
712
|
+
code: "custom",
|
|
713
|
+
message: "Handoff event follows resolution."
|
|
714
|
+
});
|
|
715
|
+
if (event.type === "status") {
|
|
716
|
+
if (event.status === "human" && event.actor === null)
|
|
717
|
+
ctx.addIssue({
|
|
718
|
+
code: "custom",
|
|
719
|
+
message: "Human ownership requires a representative."
|
|
720
|
+
});
|
|
721
|
+
const previous = statuses.get(event.epoch);
|
|
722
|
+
const transitions = {
|
|
723
|
+
ai: [],
|
|
724
|
+
requesting: ["queued", "resolved", "failed"],
|
|
725
|
+
queued: ["human", "resolved", "failed"],
|
|
726
|
+
human: ["resolved", "failed"],
|
|
727
|
+
resolved: ["ai"],
|
|
728
|
+
failed: []
|
|
729
|
+
};
|
|
730
|
+
if (previous !== void 0 && !transitions[previous].includes(event.status))
|
|
731
|
+
ctx.addIssue({
|
|
732
|
+
code: "custom",
|
|
733
|
+
message: "Invalid handoff ownership transition."
|
|
734
|
+
});
|
|
735
|
+
statuses.set(event.epoch, event.status);
|
|
736
|
+
if (["resolved", "failed", "ai"].includes(event.status))
|
|
737
|
+
terminalEpochs.add(event.epoch);
|
|
738
|
+
if (event.epoch === page.epoch) currentStatus = event.status;
|
|
739
|
+
}
|
|
740
|
+
if (event.sequence !== cursor + 1) {
|
|
741
|
+
ctx.addIssue({
|
|
742
|
+
code: "custom",
|
|
743
|
+
message: "Handoff event gap or replay."
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
cursor = event.sequence;
|
|
747
|
+
if (event.epoch > page.epoch) {
|
|
748
|
+
ctx.addIssue({
|
|
749
|
+
code: "custom",
|
|
750
|
+
message: "Event exceeds current epoch."
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
if (!page.hasMore && currentStatus !== void 0 && currentStatus !== page.status)
|
|
755
|
+
ctx.addIssue({
|
|
756
|
+
code: "custom",
|
|
757
|
+
message: "Handoff ownership contradicts its final status event."
|
|
758
|
+
});
|
|
759
|
+
if (page.cursor !== cursor || page.hasMore && page.events.length === 0) {
|
|
760
|
+
ctx.addIssue({ code: "custom", message: "Invalid handoff page cursor." });
|
|
761
|
+
}
|
|
762
|
+
if (page.epoch === 0 !== (page.handoffId === null)) {
|
|
763
|
+
ctx.addIssue({ code: "custom", message: "Invalid handoff identity." });
|
|
764
|
+
}
|
|
765
|
+
if (page.handoffId === null && page.status !== "ai") {
|
|
766
|
+
ctx.addIssue({
|
|
767
|
+
code: "custom",
|
|
768
|
+
message: "Handoff identity is required."
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
var agentRuntimeHandoffStartSchema = import_zod.z.strictObject({
|
|
773
|
+
operationId: id
|
|
774
|
+
});
|
|
775
|
+
var agentRuntimeHandoffCommandSchema = import_zod.z.strictObject({
|
|
776
|
+
operationId: id,
|
|
777
|
+
handoffId: id,
|
|
778
|
+
epoch: sequence.refine((value) => value > 0)
|
|
779
|
+
});
|
|
780
|
+
var agentRuntimeHandoffMessageSchema = agentRuntimeHandoffCommandSchema.extend({ message: text });
|
|
781
|
+
var agentRuntimeHandoffBindingSchema = import_zod.z.strictObject({
|
|
782
|
+
tenantId: id,
|
|
783
|
+
indexId: id,
|
|
784
|
+
visitorSubject: id,
|
|
785
|
+
sessionId: id,
|
|
786
|
+
handoffId: id,
|
|
787
|
+
epoch: sequence.refine((value) => value > 0)
|
|
788
|
+
});
|
|
789
|
+
var providerEventBase = {
|
|
790
|
+
apiVersion: import_zod.z.literal(agentRuntimeHandoffVersion),
|
|
791
|
+
binding: agentRuntimeHandoffBindingSchema,
|
|
792
|
+
eventId: id,
|
|
793
|
+
sequence: sequence.refine((value) => value > 0)
|
|
794
|
+
};
|
|
795
|
+
var agentRuntimeHandoffProviderEventSchema = import_zod.z.discriminatedUnion(
|
|
796
|
+
"type",
|
|
797
|
+
[
|
|
798
|
+
import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("queued") }),
|
|
799
|
+
import_zod.z.strictObject({
|
|
800
|
+
...providerEventBase,
|
|
801
|
+
type: import_zod.z.literal("assigned"),
|
|
802
|
+
actor: agentRuntimeHandoffActorSchema
|
|
803
|
+
}),
|
|
804
|
+
import_zod.z.strictObject({
|
|
805
|
+
...providerEventBase,
|
|
806
|
+
type: import_zod.z.literal("human.message"),
|
|
807
|
+
actor: agentRuntimeHandoffActorSchema,
|
|
808
|
+
message: text
|
|
809
|
+
}),
|
|
810
|
+
import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("resolved") }),
|
|
811
|
+
import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("failed") })
|
|
812
|
+
]
|
|
813
|
+
);
|
|
459
814
|
|
|
460
815
|
// src/runtime/capability.ts
|
|
461
|
-
var
|
|
816
|
+
var import_client2 = require("eve/client");
|
|
462
817
|
var MAX_REFRESH_SKEW_MS = 3e4;
|
|
463
818
|
var LOCAL_LOOPBACK_ORIGINS = [
|
|
464
819
|
"http://127.0.0.1:3010",
|
|
@@ -603,7 +958,7 @@ async function withCapabilityRefresh(capability, request) {
|
|
|
603
958
|
try {
|
|
604
959
|
return await request();
|
|
605
960
|
} catch (error) {
|
|
606
|
-
if (!(error instanceof
|
|
961
|
+
if (!(error instanceof import_client2.ClientError) || error.status !== 401) {
|
|
607
962
|
throw error;
|
|
608
963
|
}
|
|
609
964
|
capability.invalidate();
|
|
@@ -611,6 +966,75 @@ async function withCapabilityRefresh(capability, request) {
|
|
|
611
966
|
}
|
|
612
967
|
}
|
|
613
968
|
|
|
969
|
+
// src/runtime/handoff.ts
|
|
970
|
+
function createHandoffClient(options) {
|
|
971
|
+
const target = () => {
|
|
972
|
+
const sessionId = options.getSessionId();
|
|
973
|
+
if (!sessionId)
|
|
974
|
+
throw new Error(
|
|
975
|
+
"Start a conversation before requesting a representative."
|
|
976
|
+
);
|
|
977
|
+
return {
|
|
978
|
+
sessionId,
|
|
979
|
+
path: `/webless/v1/session/${encodeURIComponent(sessionId)}/handoff`
|
|
980
|
+
};
|
|
981
|
+
};
|
|
982
|
+
const request = (path, init) => withCapabilityRefresh(options.capability, async () => {
|
|
983
|
+
const response = await options.getClient().fetch(path, {
|
|
984
|
+
...init,
|
|
985
|
+
cache: "no-store",
|
|
986
|
+
redirect: "error"
|
|
987
|
+
});
|
|
988
|
+
if (!response.ok) {
|
|
989
|
+
throw new import_client3.ClientError(
|
|
990
|
+
response.status,
|
|
991
|
+
await response.text(),
|
|
992
|
+
response.headers
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
return response;
|
|
996
|
+
});
|
|
997
|
+
const command = async (suffix, body, signal) => {
|
|
998
|
+
const { path } = target();
|
|
999
|
+
await request(path + suffix, {
|
|
1000
|
+
method: "POST",
|
|
1001
|
+
headers: { "content-type": "application/json" },
|
|
1002
|
+
body: JSON.stringify(body),
|
|
1003
|
+
signal
|
|
1004
|
+
});
|
|
1005
|
+
};
|
|
1006
|
+
return {
|
|
1007
|
+
async read(input = {}) {
|
|
1008
|
+
const { after } = agentRuntimeHandoffReadRequestSchema.parse({
|
|
1009
|
+
after: input.after
|
|
1010
|
+
});
|
|
1011
|
+
const { sessionId, path } = target();
|
|
1012
|
+
const response = await request(`${path}?after=${after}`, {
|
|
1013
|
+
signal: input.signal
|
|
1014
|
+
});
|
|
1015
|
+
const value = await response.json();
|
|
1016
|
+
const page = agentRuntimeHandoffPageSchema.parse(value);
|
|
1017
|
+
if (page.sessionId !== sessionId || page.after !== after) {
|
|
1018
|
+
throw new Error(
|
|
1019
|
+
"The handoff response does not belong to this conversation or cursor."
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
return page;
|
|
1023
|
+
},
|
|
1024
|
+
start: (operationId, signal) => command(
|
|
1025
|
+
"",
|
|
1026
|
+
agentRuntimeHandoffStartSchema.parse({ operationId }),
|
|
1027
|
+
signal
|
|
1028
|
+
),
|
|
1029
|
+
send: (input, signal) => command(
|
|
1030
|
+
"/messages",
|
|
1031
|
+
agentRuntimeHandoffMessageSchema.parse(input),
|
|
1032
|
+
signal
|
|
1033
|
+
),
|
|
1034
|
+
returnToAI: (input, signal) => command("/return", agentRuntimeHandoffCommandSchema.parse(input), signal)
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
|
|
614
1038
|
// src/runtime/config.ts
|
|
615
1039
|
var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
|
|
616
1040
|
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
@@ -1201,6 +1625,11 @@ var AgentSession = class {
|
|
|
1201
1625
|
version,
|
|
1202
1626
|
visitorSessionId
|
|
1203
1627
|
});
|
|
1628
|
+
this.handoff = createHandoffClient({
|
|
1629
|
+
getClient: () => this.ensureClient(),
|
|
1630
|
+
getSessionId: () => this.getActiveSessionId(),
|
|
1631
|
+
capability: this.capability
|
|
1632
|
+
});
|
|
1204
1633
|
}
|
|
1205
1634
|
indexId;
|
|
1206
1635
|
version;
|
|
@@ -1213,6 +1642,7 @@ var AgentSession = class {
|
|
|
1213
1642
|
activeResponse;
|
|
1214
1643
|
childStreams;
|
|
1215
1644
|
capability;
|
|
1645
|
+
handoff;
|
|
1216
1646
|
getActiveSessionId() {
|
|
1217
1647
|
return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
|
|
1218
1648
|
}
|
|
@@ -1287,7 +1717,7 @@ var AgentSession = class {
|
|
|
1287
1717
|
}
|
|
1288
1718
|
this.activeResponse = void 0;
|
|
1289
1719
|
this.session = void 0;
|
|
1290
|
-
this.client = new
|
|
1720
|
+
this.client = new import_client4.Client({
|
|
1291
1721
|
auth: { bearer: () => this.capability.getAccessToken() },
|
|
1292
1722
|
host: config.host,
|
|
1293
1723
|
redirect: "error"
|
|
@@ -1323,7 +1753,7 @@ var AgentSession = class {
|
|
|
1323
1753
|
() => activeSession.send(message, { signal })
|
|
1324
1754
|
);
|
|
1325
1755
|
} catch (error) {
|
|
1326
|
-
if (error instanceof
|
|
1756
|
+
if (error instanceof import_client4.ClientError && error.status === 409 && error.code === "session_not_active") {
|
|
1327
1757
|
clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
|
|
1328
1758
|
this.session = void 0;
|
|
1329
1759
|
session = void 0;
|
|
@@ -1497,10 +1927,10 @@ var AgentSession = class {
|
|
|
1497
1927
|
}
|
|
1498
1928
|
this.session = session;
|
|
1499
1929
|
const inputResponses = responses.map(
|
|
1500
|
-
({ requestId, optionId, text:
|
|
1930
|
+
({ requestId, optionId, text: text3 }) => ({
|
|
1501
1931
|
requestId,
|
|
1502
1932
|
...optionId ? { optionId } : {},
|
|
1503
|
-
...
|
|
1933
|
+
...text3 ? { text: text3 } : {}
|
|
1504
1934
|
})
|
|
1505
1935
|
);
|
|
1506
1936
|
const response = await withCapabilityRefresh(
|
|
@@ -1590,6 +2020,7 @@ function createAgentClient(options) {
|
|
|
1590
2020
|
);
|
|
1591
2021
|
return {
|
|
1592
2022
|
indexId,
|
|
2023
|
+
handoff: session.handoff,
|
|
1593
2024
|
version,
|
|
1594
2025
|
runtimeOrigin,
|
|
1595
2026
|
visitorSessionId,
|
|
@@ -1621,7 +2052,7 @@ function createAgentClient(options) {
|
|
|
1621
2052
|
}
|
|
1622
2053
|
|
|
1623
2054
|
// src/runtime/errors.ts
|
|
1624
|
-
var
|
|
2055
|
+
var import_client5 = require("eve/client");
|
|
1625
2056
|
var TRANSIENT_AGENT_ERROR_MESSAGE = "The agent run stopped before the action finished. Please try again.";
|
|
1626
2057
|
var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
|
|
1627
2058
|
function isTransientRuntimeMessage(message) {
|
|
@@ -1633,7 +2064,7 @@ function isPreviewAuthorizationMessage(message) {
|
|
|
1633
2064
|
return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
|
|
1634
2065
|
}
|
|
1635
2066
|
function formatAgentError(error) {
|
|
1636
|
-
if (error instanceof
|
|
2067
|
+
if (error instanceof import_client5.ClientError) {
|
|
1637
2068
|
if (error.status === 401 && error.code === "index_required") {
|
|
1638
2069
|
return "Missing indexId \u2014 pass a published index id to createAgentClient().";
|
|
1639
2070
|
}
|
|
@@ -1667,14 +2098,14 @@ function formatAgentError(error) {
|
|
|
1667
2098
|
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.";
|
|
1668
2099
|
function visitorMessageDisplayText(message) {
|
|
1669
2100
|
if (message.role !== "visitor") return message.text;
|
|
1670
|
-
const
|
|
1671
|
-
if (!
|
|
1672
|
-
if (
|
|
1673
|
-
const afterDismissal =
|
|
2101
|
+
const text3 = message.text.trim();
|
|
2102
|
+
if (!text3) return message.text;
|
|
2103
|
+
if (text3.startsWith(COMPOSER_FORM_SKIP_DISMISSAL)) {
|
|
2104
|
+
const afterDismissal = text3.slice(COMPOSER_FORM_SKIP_DISMISSAL.length).trim().replace(/^\n+/, "").trim();
|
|
1674
2105
|
if (afterDismissal) return afterDismissal;
|
|
1675
2106
|
}
|
|
1676
2107
|
const runtime = message.runtimeText?.trim();
|
|
1677
|
-
if (runtime &&
|
|
2108
|
+
if (runtime && text3 === runtime && runtime.includes(COMPOSER_FORM_SKIP_DISMISSAL)) {
|
|
1678
2109
|
const afterDismissal = runtime.slice(
|
|
1679
2110
|
runtime.indexOf(COMPOSER_FORM_SKIP_DISMISSAL) + COMPOSER_FORM_SKIP_DISMISSAL.length
|
|
1680
2111
|
).trim().replace(/^\n+/, "").trim();
|
|
@@ -1748,13 +2179,13 @@ function parseVisitorFormFields(value) {
|
|
|
1748
2179
|
const seen = /* @__PURE__ */ new Set();
|
|
1749
2180
|
for (const item of value) {
|
|
1750
2181
|
const record2 = asRecord(item);
|
|
1751
|
-
const
|
|
2182
|
+
const id2 = asString(record2?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
1752
2183
|
const kind = asString(record2?.kind);
|
|
1753
|
-
if (!record2 || !
|
|
1754
|
-
seen.add(
|
|
1755
|
-
const label = asString(record2.label) ||
|
|
2184
|
+
if (!record2 || !id2 || seen.has(id2) || !isFieldKind2(kind)) continue;
|
|
2185
|
+
seen.add(id2);
|
|
2186
|
+
const label = asString(record2.label) || id2;
|
|
1756
2187
|
fields.push({
|
|
1757
|
-
id,
|
|
2188
|
+
id: id2,
|
|
1758
2189
|
kind,
|
|
1759
2190
|
label,
|
|
1760
2191
|
placeholder: asString(record2.placeholder) || label,
|
|
@@ -1790,52 +2221,52 @@ function formatComposerFormMessage(form, values) {
|
|
|
1790
2221
|
return value ? `${field.label}: ${value}` : "";
|
|
1791
2222
|
}).filter(Boolean).join("\n");
|
|
1792
2223
|
}
|
|
1793
|
-
function looksLikeFieldCollection(
|
|
2224
|
+
function looksLikeFieldCollection(text3) {
|
|
1794
2225
|
return /\b(please|need|send|share|enter|provide|add|collect|what|which|use)\b/i.test(
|
|
1795
|
-
|
|
1796
|
-
) || /:\s*$/m.test(
|
|
2226
|
+
text3
|
|
2227
|
+
) || /:\s*$/m.test(text3) || /^[-*•]\s+/m.test(text3);
|
|
1797
2228
|
}
|
|
1798
|
-
function looksLikeBookingCopy(
|
|
2229
|
+
function looksLikeBookingCopy(text3) {
|
|
1799
2230
|
return /book(ing)? (card|a demo|this time)|pick a (date|time)|available (times|slots)|calendly/i.test(
|
|
1800
|
-
|
|
2231
|
+
text3
|
|
1801
2232
|
);
|
|
1802
2233
|
}
|
|
1803
|
-
function echoedLabeledFieldIds(
|
|
2234
|
+
function echoedLabeledFieldIds(text3) {
|
|
1804
2235
|
const ids = /* @__PURE__ */ new Set();
|
|
1805
|
-
if (/\bname\s*:\s+\S+/i.test(
|
|
1806
|
-
if (/\be-?mail\s*:\s+\S+/i.test(
|
|
1807
|
-
if (/\bphone\s*:\s+\S+/i.test(
|
|
1808
|
-
if (/\bcompany\s*:\s+\S+/i.test(
|
|
2236
|
+
if (/\bname\s*:\s+\S+/i.test(text3)) ids.add("name");
|
|
2237
|
+
if (/\be-?mail\s*:\s+\S+/i.test(text3)) ids.add("email");
|
|
2238
|
+
if (/\bphone\s*:\s+\S+/i.test(text3)) ids.add("phone");
|
|
2239
|
+
if (/\bcompany\s*:\s+\S+/i.test(text3)) ids.add("company");
|
|
1809
2240
|
return ids;
|
|
1810
2241
|
}
|
|
1811
|
-
function matchLibraryFields(
|
|
2242
|
+
function matchLibraryFields(text3) {
|
|
1812
2243
|
return FIELD_LIBRARY.flatMap((field) => {
|
|
1813
|
-
if (field.exclude?.test(
|
|
1814
|
-
const leftover =
|
|
2244
|
+
if (field.exclude?.test(text3)) {
|
|
2245
|
+
const leftover = text3.replace(field.exclude, " ");
|
|
1815
2246
|
if (!field.patterns.some((pattern) => pattern.test(leftover))) return [];
|
|
1816
|
-
} else if (!field.patterns.some((pattern) => pattern.test(
|
|
2247
|
+
} else if (!field.patterns.some((pattern) => pattern.test(text3))) {
|
|
1817
2248
|
return [];
|
|
1818
2249
|
}
|
|
1819
2250
|
const { patterns: _patterns, exclude: _exclude, ...next } = field;
|
|
1820
2251
|
return [next];
|
|
1821
2252
|
}).slice(0, MAX_FORM_FIELDS);
|
|
1822
2253
|
}
|
|
1823
|
-
function looksLikeCompletedActionRecap(
|
|
1824
|
-
const confirmingCreate = /\bshould i create this\b/i.test(
|
|
1825
|
-
const echoingFilledFields = /\b(?:name|email|phone|company)\s*:\s+\S+/i.test(
|
|
2254
|
+
function looksLikeCompletedActionRecap(text3) {
|
|
2255
|
+
const confirmingCreate = /\bshould i create this\b/i.test(text3);
|
|
2256
|
+
const echoingFilledFields = /\b(?:name|email|phone|company)\s*:\s+\S+/i.test(text3) && /[^\s@]+@[^\s@]+\.[^\s@]+/i.test(text3);
|
|
1826
2257
|
if (echoingFilledFields) {
|
|
1827
|
-
if (looksLikeFieldCollection(
|
|
1828
|
-
const echoed = echoedLabeledFieldIds(
|
|
1829
|
-
if (matchLibraryFields(
|
|
2258
|
+
if (looksLikeFieldCollection(text3)) {
|
|
2259
|
+
const echoed = echoedLabeledFieldIds(text3);
|
|
2260
|
+
if (matchLibraryFields(text3).some((field) => !echoed.has(field.id))) {
|
|
1830
2261
|
return false;
|
|
1831
2262
|
}
|
|
1832
2263
|
}
|
|
1833
2264
|
return true;
|
|
1834
2265
|
}
|
|
1835
|
-
return confirmingCreate && !looksLikeFieldCollection(
|
|
2266
|
+
return confirmingCreate && !looksLikeFieldCollection(text3);
|
|
1836
2267
|
}
|
|
1837
|
-
function inferComposerForm(
|
|
1838
|
-
const cleaned =
|
|
2268
|
+
function inferComposerForm(text3) {
|
|
2269
|
+
const cleaned = text3.trim();
|
|
1839
2270
|
if (!cleaned || looksLikeBookingCopy(cleaned) || looksLikeCompletedActionRecap(cleaned) || !looksLikeFieldCollection(cleaned)) {
|
|
1840
2271
|
return null;
|
|
1841
2272
|
}
|
|
@@ -1850,8 +2281,8 @@ function resolveComposerForm(input) {
|
|
|
1850
2281
|
if (input.enabled === false || input.hasBookingOffer || input.hasPendingConfirmation) {
|
|
1851
2282
|
return null;
|
|
1852
2283
|
}
|
|
1853
|
-
const
|
|
1854
|
-
if (!
|
|
2284
|
+
const text3 = input.agentText.trim();
|
|
2285
|
+
if (!text3) return null;
|
|
1855
2286
|
const card = input.cards?.find((item) => item.type === "visitor_form");
|
|
1856
2287
|
if (card?.fields && card.fields.length >= MIN_FORM_FIELDS) {
|
|
1857
2288
|
return {
|
|
@@ -1859,7 +2290,7 @@ function resolveComposerForm(input) {
|
|
|
1859
2290
|
fields: card.fields.slice(0, MAX_FORM_FIELDS)
|
|
1860
2291
|
};
|
|
1861
2292
|
}
|
|
1862
|
-
return inferComposerForm(
|
|
2293
|
+
return inferComposerForm(text3);
|
|
1863
2294
|
}
|
|
1864
2295
|
|
|
1865
2296
|
// src/react/lib/tool-card.ts
|
|
@@ -1873,9 +2304,9 @@ function preferBookingOffer(current, next) {
|
|
|
1873
2304
|
}
|
|
1874
2305
|
return next;
|
|
1875
2306
|
}
|
|
1876
|
-
function looksLikeBookingReady(
|
|
2307
|
+
function looksLikeBookingReady(text3) {
|
|
1877
2308
|
return /demo option|options are ready|pick a (date|time)|available (times|slots)|book(ing)? (card|the demo)|\bschedule\b/i.test(
|
|
1878
|
-
|
|
2309
|
+
text3
|
|
1879
2310
|
);
|
|
1880
2311
|
}
|
|
1881
2312
|
function bookingOfferIdentityKey(offer) {
|
|
@@ -1985,29 +2416,29 @@ function bookingCardFromActionOutput(output) {
|
|
|
1985
2416
|
const data = asRecord2(record2?.output) ?? asRecord2(record2?.data) ?? record2;
|
|
1986
2417
|
return parseToolCard(data);
|
|
1987
2418
|
}
|
|
1988
|
-
function ensureBookingOfferText(
|
|
1989
|
-
if (!offer) return
|
|
1990
|
-
if (extractToolCards(
|
|
1991
|
-
return
|
|
2419
|
+
function ensureBookingOfferText(text3, offer) {
|
|
2420
|
+
if (!offer) return text3;
|
|
2421
|
+
if (extractToolCards(text3).some((card) => card.type === "booking_offer")) {
|
|
2422
|
+
return text3;
|
|
1992
2423
|
}
|
|
1993
|
-
const visible = stripToolCards(
|
|
2424
|
+
const visible = stripToolCards(text3).trim() || text3.trim();
|
|
1994
2425
|
return `${visible}
|
|
1995
2426
|
|
|
1996
2427
|
${formatBookingOfferFence(offer)}`;
|
|
1997
2428
|
}
|
|
1998
|
-
function hideToolCardFences(
|
|
1999
|
-
return
|
|
2429
|
+
function hideToolCardFences(text3) {
|
|
2430
|
+
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();
|
|
2000
2431
|
}
|
|
2001
2432
|
var BOOKING_CARD_FALLBACK = "Pick a date and time that works for you.";
|
|
2002
|
-
function looksLikeBookingAvailabilityDump(
|
|
2003
|
-
const cleaned =
|
|
2433
|
+
function looksLikeBookingAvailabilityDump(text3) {
|
|
2434
|
+
const cleaned = text3.trim();
|
|
2004
2435
|
if (!cleaned) return false;
|
|
2005
2436
|
const isoCount = (cleaned.match(/\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/g) ?? []).length;
|
|
2006
2437
|
const utcCount = (cleaned.match(/\bUTC\b/g) ?? []).length;
|
|
2007
2438
|
return isoCount >= 2 || utcCount >= 2 || /api\.calendly\.com|calendly\.com\//i.test(cleaned) || /webless-tool-card/i.test(cleaned);
|
|
2008
2439
|
}
|
|
2009
|
-
function sanitizeBookingOfferCopy(
|
|
2010
|
-
const cleaned = hideToolCardFences(
|
|
2440
|
+
function sanitizeBookingOfferCopy(text3) {
|
|
2441
|
+
const cleaned = hideToolCardFences(text3);
|
|
2011
2442
|
if (!cleaned || looksLikeBookingAvailabilityDump(cleaned)) {
|
|
2012
2443
|
return BOOKING_CARD_FALLBACK;
|
|
2013
2444
|
}
|
|
@@ -2020,9 +2451,9 @@ function visitorTimeZone() {
|
|
|
2020
2451
|
return "UTC";
|
|
2021
2452
|
}
|
|
2022
2453
|
}
|
|
2023
|
-
function extractToolCards(
|
|
2454
|
+
function extractToolCards(text3) {
|
|
2024
2455
|
const cards = [];
|
|
2025
|
-
for (const match of
|
|
2456
|
+
for (const match of text3.matchAll(FENCE_PATTERN)) {
|
|
2026
2457
|
try {
|
|
2027
2458
|
const card = parseToolCard(JSON.parse(match[1] ?? ""));
|
|
2028
2459
|
if (card) cards.push(card);
|
|
@@ -2031,8 +2462,8 @@ function extractToolCards(text2) {
|
|
|
2031
2462
|
}
|
|
2032
2463
|
return cards;
|
|
2033
2464
|
}
|
|
2034
|
-
function stripToolCards(
|
|
2035
|
-
return
|
|
2465
|
+
function stripToolCards(text3) {
|
|
2466
|
+
return text3.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
2036
2467
|
}
|
|
2037
2468
|
function localDateKey(date) {
|
|
2038
2469
|
if (Number.isNaN(date.getTime())) return "";
|
|
@@ -2145,7 +2576,7 @@ function visitorBookingPrefix(booking) {
|
|
|
2145
2576
|
function record(value) {
|
|
2146
2577
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2147
2578
|
}
|
|
2148
|
-
function
|
|
2579
|
+
function text2(value, max = 500) {
|
|
2149
2580
|
return typeof value === "string" && value.trim().length > 0 && value.trim().length <= max;
|
|
2150
2581
|
}
|
|
2151
2582
|
function safeSearchUrl(value) {
|
|
@@ -2166,7 +2597,7 @@ function parseAgentSearchReferences(value) {
|
|
|
2166
2597
|
const source = record(value2);
|
|
2167
2598
|
if (!source || Object.keys(source).some(
|
|
2168
2599
|
(key) => !["id", "title", "url"].includes(key)
|
|
2169
|
-
) || !
|
|
2600
|
+
) || !text2(source.id, Infinity) || !text2(source.title) || source.url !== void 0 && typeof source.url !== "string")
|
|
2170
2601
|
return null;
|
|
2171
2602
|
const url = safeSearchUrl(source.url);
|
|
2172
2603
|
sources.push({
|
|
@@ -2178,7 +2609,7 @@ function parseAgentSearchReferences(value) {
|
|
|
2178
2609
|
let cta;
|
|
2179
2610
|
if (data.cta !== void 0) {
|
|
2180
2611
|
const action = record(data.cta);
|
|
2181
|
-
if (!action || Object.keys(action).some((key) => !["label", "url"].includes(key)) || !
|
|
2612
|
+
if (!action || Object.keys(action).some((key) => !["label", "url"].includes(key)) || !text2(action.label) || action.url !== void 0 && typeof action.url !== "string")
|
|
2182
2613
|
return null;
|
|
2183
2614
|
const url = safeSearchUrl(action.url);
|
|
2184
2615
|
cta = { label: action.label.trim(), ...url ? { url } : {} };
|
|
@@ -2197,7 +2628,7 @@ function parseAgentSearchDiscoveryOutput(value) {
|
|
|
2197
2628
|
const data = record(decoded);
|
|
2198
2629
|
if (!data || Object.keys(data).some(
|
|
2199
2630
|
(key) => !["answer", "sources", "cta", "suggestions"].includes(key)
|
|
2200
|
-
) || !
|
|
2631
|
+
) || !text2(data.answer, 5e4) || !Array.isArray(data.suggestions) || data.suggestions.length > 8 || !data.suggestions.every((item) => text2(item)))
|
|
2201
2632
|
return null;
|
|
2202
2633
|
const references = parseAgentSearchReferences(data);
|
|
2203
2634
|
return references ? {
|
|
@@ -2215,9 +2646,21 @@ function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
|
2215
2646
|
function parseMessage(value) {
|
|
2216
2647
|
if (typeof value !== "object" || value === null) return null;
|
|
2217
2648
|
const record2 = value;
|
|
2218
|
-
if (typeof record2.id !== "string" || record2.role !== "agent" && record2.role !== "visitor" || typeof record2.text !== "string" || typeof record2.createdAt !== "number" || !Number.isFinite(record2.createdAt)) {
|
|
2649
|
+
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)) {
|
|
2219
2650
|
return null;
|
|
2220
2651
|
}
|
|
2652
|
+
if (record2.role === "human") {
|
|
2653
|
+
const person = record2.representative;
|
|
2654
|
+
if (typeof person !== "object" || person === null || !("id" in person) || !("name" in person) || typeof person.id !== "string" || typeof person.name !== "string")
|
|
2655
|
+
return null;
|
|
2656
|
+
return {
|
|
2657
|
+
id: record2.id,
|
|
2658
|
+
role: "human",
|
|
2659
|
+
text: record2.text,
|
|
2660
|
+
createdAt: record2.createdAt,
|
|
2661
|
+
representative: { id: person.id, name: person.name }
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2221
2664
|
if (record2.role === "visitor") {
|
|
2222
2665
|
return {
|
|
2223
2666
|
id: record2.id,
|
|
@@ -2512,10 +2955,10 @@ function safeText(value) {
|
|
|
2512
2955
|
return value.trim().slice(0, MAX_TEXT_LENGTH);
|
|
2513
2956
|
}
|
|
2514
2957
|
function safeHref(value) {
|
|
2515
|
-
const
|
|
2516
|
-
if (!
|
|
2958
|
+
const text3 = safeText(value);
|
|
2959
|
+
if (!text3) return "";
|
|
2517
2960
|
try {
|
|
2518
|
-
const url = new URL(
|
|
2961
|
+
const url = new URL(text3);
|
|
2519
2962
|
return url.protocol === "https:" ? url.toString() : "";
|
|
2520
2963
|
} catch {
|
|
2521
2964
|
return "";
|
|
@@ -2878,15 +3321,15 @@ function appendChatCollectiblePrompts(messages, requests) {
|
|
|
2878
3321
|
}
|
|
2879
3322
|
return next;
|
|
2880
3323
|
}
|
|
2881
|
-
function chatInputResponseForText(requests,
|
|
2882
|
-
const trimmed =
|
|
3324
|
+
function chatInputResponseForText(requests, text3) {
|
|
3325
|
+
const trimmed = text3.trim();
|
|
2883
3326
|
if (!trimmed) return null;
|
|
2884
3327
|
const pending = requests.find(isChatCollectibleInputRequest);
|
|
2885
3328
|
if (!pending) return null;
|
|
2886
3329
|
return { requestId: pending.requestId, text: trimmed };
|
|
2887
3330
|
}
|
|
2888
|
-
function normalizeAssistantDedupeKey(
|
|
2889
|
-
return
|
|
3331
|
+
function normalizeAssistantDedupeKey(text3) {
|
|
3332
|
+
return text3.trim().replace(/\s+/g, " ").toLowerCase();
|
|
2890
3333
|
}
|
|
2891
3334
|
function isNearDuplicateAssistantText(left, right) {
|
|
2892
3335
|
const a = normalizeAssistantDedupeKey(left);
|
|
@@ -2999,6 +3442,7 @@ function isJsonRecord(value) {
|
|
|
2999
3442
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3000
3443
|
}
|
|
3001
3444
|
function useAgentChat({
|
|
3445
|
+
handoffEnabled = false,
|
|
3002
3446
|
customerId,
|
|
3003
3447
|
getUnpublishedPreviewGrant,
|
|
3004
3448
|
indexId,
|
|
@@ -3010,13 +3454,13 @@ function useAgentChat({
|
|
|
3010
3454
|
greeting,
|
|
3011
3455
|
toolResultRegistry
|
|
3012
3456
|
}) {
|
|
3013
|
-
const initialState = (0,
|
|
3457
|
+
const initialState = (0, import_react3.useMemo)(
|
|
3014
3458
|
() => createInitialState(greeting?.trim() || DEFAULT_GREETING),
|
|
3015
3459
|
[greeting]
|
|
3016
3460
|
);
|
|
3017
|
-
const previewGrantProviderRef = (0,
|
|
3461
|
+
const previewGrantProviderRef = (0, import_react3.useRef)(getUnpublishedPreviewGrant);
|
|
3018
3462
|
previewGrantProviderRef.current = getUnpublishedPreviewGrant;
|
|
3019
|
-
const toolResultRegistryRef = (0,
|
|
3463
|
+
const toolResultRegistryRef = (0, import_react3.useRef)(toolResultRegistry);
|
|
3020
3464
|
toolResultRegistryRef.current = toolResultRegistry;
|
|
3021
3465
|
const resolveUnpublishedPreviewGrant = () => {
|
|
3022
3466
|
const provider = previewGrantProviderRef.current;
|
|
@@ -3029,7 +3473,7 @@ function useAgentChat({
|
|
|
3029
3473
|
}
|
|
3030
3474
|
return provider();
|
|
3031
3475
|
};
|
|
3032
|
-
const resolvedStorageKeyPrefix = (0,
|
|
3476
|
+
const resolvedStorageKeyPrefix = (0, import_react3.useMemo)(
|
|
3033
3477
|
() => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({
|
|
3034
3478
|
customerId,
|
|
3035
3479
|
indexId,
|
|
@@ -3038,23 +3482,23 @@ function useAgentChat({
|
|
|
3038
3482
|
}),
|
|
3039
3483
|
[customerId, indexId, runtimeOrigin, storageKeyPrefix, version]
|
|
3040
3484
|
);
|
|
3041
|
-
const visitorId = (0,
|
|
3485
|
+
const visitorId = (0, import_react3.useMemo)(
|
|
3042
3486
|
() => visitorSessionId?.trim() || getOrCreateVisitorSessionId({
|
|
3043
3487
|
storageKeyPrefix: resolvedStorageKeyPrefix
|
|
3044
3488
|
}),
|
|
3045
3489
|
[resolvedStorageKeyPrefix, visitorSessionId]
|
|
3046
3490
|
);
|
|
3047
|
-
const [state, setState] = (0,
|
|
3491
|
+
const [state, setState] = (0, import_react3.useState)(
|
|
3048
3492
|
() => stateFromConversation(
|
|
3049
3493
|
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
|
|
3050
3494
|
initialState
|
|
3051
3495
|
)
|
|
3052
3496
|
);
|
|
3053
|
-
const pendingBookingRef = (0,
|
|
3497
|
+
const pendingBookingRef = (0, import_react3.useRef)(
|
|
3054
3498
|
loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
|
|
3055
3499
|
);
|
|
3056
|
-
const runRef = (0,
|
|
3057
|
-
const clientRef = (0,
|
|
3500
|
+
const runRef = (0, import_react3.useRef)(null);
|
|
3501
|
+
const clientRef = (0, import_react3.useRef)(
|
|
3058
3502
|
createAgentClient({
|
|
3059
3503
|
locationConsent: true,
|
|
3060
3504
|
customerId,
|
|
@@ -3068,8 +3512,8 @@ function useAgentChat({
|
|
|
3068
3512
|
})
|
|
3069
3513
|
);
|
|
3070
3514
|
const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${previewBuildId ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
|
|
3071
|
-
const identityRef = (0,
|
|
3072
|
-
(0,
|
|
3515
|
+
const identityRef = (0, import_react3.useRef)(identityKey);
|
|
3516
|
+
(0, import_react3.useEffect)(() => {
|
|
3073
3517
|
if (identityRef.current === identityKey) {
|
|
3074
3518
|
return;
|
|
3075
3519
|
}
|
|
@@ -3108,7 +3552,7 @@ function useAgentChat({
|
|
|
3108
3552
|
version,
|
|
3109
3553
|
visitorId
|
|
3110
3554
|
]);
|
|
3111
|
-
(0,
|
|
3555
|
+
(0, import_react3.useEffect)(() => {
|
|
3112
3556
|
if (!hasVisitorMessages(state.messages)) return;
|
|
3113
3557
|
savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
|
|
3114
3558
|
messages: state.messages,
|
|
@@ -3128,16 +3572,77 @@ function useAgentChat({
|
|
|
3128
3572
|
state.toolResults,
|
|
3129
3573
|
visitorId
|
|
3130
3574
|
]);
|
|
3131
|
-
const
|
|
3575
|
+
const handoff = useAgentHandoff({
|
|
3576
|
+
enabled: handoffEnabled,
|
|
3577
|
+
client: clientRef.current,
|
|
3578
|
+
sessionId: clientRef.current.getActiveSessionId(),
|
|
3579
|
+
onOwnership: () => {
|
|
3580
|
+
runRef.current?.abort();
|
|
3581
|
+
runRef.current = null;
|
|
3582
|
+
setState(
|
|
3583
|
+
(prev) => prev.phase === "complete" && !prev.streamingText && !prev.pendingInputs?.length && !prev.toolSteps.length ? prev : {
|
|
3584
|
+
...prev,
|
|
3585
|
+
phase: "complete",
|
|
3586
|
+
streamingText: "",
|
|
3587
|
+
pendingInputs: [],
|
|
3588
|
+
toolSteps: [],
|
|
3589
|
+
toolResults: [],
|
|
3590
|
+
pendingOffer: null,
|
|
3591
|
+
error: null
|
|
3592
|
+
}
|
|
3593
|
+
);
|
|
3594
|
+
},
|
|
3595
|
+
onEvents: (events) => {
|
|
3596
|
+
setState((prev) => {
|
|
3597
|
+
const seen = new Set(prev.messages.map((message) => message.id));
|
|
3598
|
+
const messages = [];
|
|
3599
|
+
for (const event of events) {
|
|
3600
|
+
if (event.type === "status" || seen.has(`handoff-${event.id}`))
|
|
3601
|
+
continue;
|
|
3602
|
+
messages.push(
|
|
3603
|
+
event.type === "human.message" ? {
|
|
3604
|
+
id: `handoff-${event.id}`,
|
|
3605
|
+
role: "human",
|
|
3606
|
+
text: event.message,
|
|
3607
|
+
createdAt: Date.parse(event.createdAt),
|
|
3608
|
+
representative: event.actor
|
|
3609
|
+
} : {
|
|
3610
|
+
id: `handoff-${event.id}`,
|
|
3611
|
+
role: "visitor",
|
|
3612
|
+
text: event.message,
|
|
3613
|
+
createdAt: Date.parse(event.createdAt)
|
|
3614
|
+
}
|
|
3615
|
+
);
|
|
3616
|
+
}
|
|
3617
|
+
return messages.length ? {
|
|
3618
|
+
...prev,
|
|
3619
|
+
messages: [...prev.messages, ...messages].sort(
|
|
3620
|
+
(a, b) => a.createdAt - b.createdAt
|
|
3621
|
+
)
|
|
3622
|
+
} : prev;
|
|
3623
|
+
});
|
|
3624
|
+
}
|
|
3625
|
+
});
|
|
3626
|
+
const handoffBlocksAI = (0, import_react3.useRef)(handoff.blocksAI);
|
|
3627
|
+
handoffBlocksAI.current = handoff.blocksAI;
|
|
3628
|
+
const reset = (0, import_react3.useCallback)(() => {
|
|
3629
|
+
if (handoff.blocksAI && !handoff.canReset) return;
|
|
3132
3630
|
runRef.current?.abort();
|
|
3133
3631
|
runRef.current = null;
|
|
3134
3632
|
clientRef.current.reset();
|
|
3135
3633
|
pendingBookingRef.current = null;
|
|
3136
3634
|
clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
|
|
3137
3635
|
setState(initialState);
|
|
3138
|
-
}, [
|
|
3139
|
-
|
|
3636
|
+
}, [
|
|
3637
|
+
handoff.blocksAI,
|
|
3638
|
+
handoff.canReset,
|
|
3639
|
+
initialState,
|
|
3640
|
+
resolvedStorageKeyPrefix,
|
|
3641
|
+
visitorId
|
|
3642
|
+
]);
|
|
3643
|
+
const runTurn = (0, import_react3.useCallback)(
|
|
3140
3644
|
async (input) => {
|
|
3645
|
+
if (handoffBlocksAI.current) return null;
|
|
3141
3646
|
const {
|
|
3142
3647
|
controller,
|
|
3143
3648
|
initialText = "",
|
|
@@ -3261,6 +3766,7 @@ function useAgentChat({
|
|
|
3261
3766
|
signal
|
|
3262
3767
|
});
|
|
3263
3768
|
if (resume && finalText === null) {
|
|
3769
|
+
if (!isActiveRun() || handoffBlocksAI.current) return null;
|
|
3264
3770
|
finalText = await clientRef.current.sendTurn(visitorText, {
|
|
3265
3771
|
handlers,
|
|
3266
3772
|
signal
|
|
@@ -3293,8 +3799,12 @@ function useAgentChat({
|
|
|
3293
3799
|
messages: appendAgentTurnMessage(
|
|
3294
3800
|
prev.messages,
|
|
3295
3801
|
displayText,
|
|
3296
|
-
(prev.toolResults ?? []).filter(
|
|
3297
|
-
|
|
3802
|
+
(prev.toolResults ?? []).filter(
|
|
3803
|
+
(result) => result.kind === "search"
|
|
3804
|
+
),
|
|
3805
|
+
(prev.toolResults ?? []).filter(
|
|
3806
|
+
(result) => result.kind !== "search" && result.kind !== "input"
|
|
3807
|
+
)
|
|
3298
3808
|
),
|
|
3299
3809
|
toolResults: (prev.toolResults ?? []).filter(
|
|
3300
3810
|
(result) => result.kind === "input"
|
|
@@ -3335,7 +3845,7 @@ function useAgentChat({
|
|
|
3335
3845
|
},
|
|
3336
3846
|
[resolvedStorageKeyPrefix, visitorId]
|
|
3337
3847
|
);
|
|
3338
|
-
const rememberBooking = (0,
|
|
3848
|
+
const rememberBooking = (0, import_react3.useCallback)(
|
|
3339
3849
|
(booking) => {
|
|
3340
3850
|
const current = pendingBookingRef.current;
|
|
3341
3851
|
if (current?.eventUri === booking.eventUri && current.inviteeEmail === booking.inviteeEmail && current.inviteeUri === booking.inviteeUri) {
|
|
@@ -3346,7 +3856,7 @@ function useAgentChat({
|
|
|
3346
3856
|
},
|
|
3347
3857
|
[resolvedStorageKeyPrefix, visitorId]
|
|
3348
3858
|
);
|
|
3349
|
-
const forgetBooking = (0,
|
|
3859
|
+
const forgetBooking = (0, import_react3.useCallback)(
|
|
3350
3860
|
(eventUri) => {
|
|
3351
3861
|
const current = pendingBookingRef.current;
|
|
3352
3862
|
if (!current) return;
|
|
@@ -3356,12 +3866,21 @@ function useAgentChat({
|
|
|
3356
3866
|
},
|
|
3357
3867
|
[resolvedStorageKeyPrefix, visitorId]
|
|
3358
3868
|
);
|
|
3359
|
-
const submit = (0,
|
|
3869
|
+
const submit = (0, import_react3.useCallback)(
|
|
3360
3870
|
async (visitorText, options) => {
|
|
3361
3871
|
const trimmed = visitorText.trim();
|
|
3362
3872
|
const outgoing = options?.runtimeText ?? visitorText;
|
|
3363
3873
|
if (!outgoing.trim()) return null;
|
|
3364
3874
|
const declinedComposerFormId = options?.declinedComposerFormId?.trim();
|
|
3875
|
+
if (handoff.blocksAI) {
|
|
3876
|
+
if (!["requesting", "queued", "human"].includes(
|
|
3877
|
+
handoff.page?.status ?? ""
|
|
3878
|
+
) || handoff.busy)
|
|
3879
|
+
return null;
|
|
3880
|
+
if (!trimmed) return null;
|
|
3881
|
+
await handoff.send(trimmed);
|
|
3882
|
+
return null;
|
|
3883
|
+
}
|
|
3365
3884
|
const chatResponse = chatInputResponseForText(
|
|
3366
3885
|
state.pendingInputs ?? [],
|
|
3367
3886
|
outgoing.trim()
|
|
@@ -3463,9 +3982,10 @@ ${outgoing}` : outgoing;
|
|
|
3463
3982
|
visitorText: runtimeText
|
|
3464
3983
|
});
|
|
3465
3984
|
},
|
|
3466
|
-
[runTurn, state.pendingInputs]
|
|
3985
|
+
[handoff, runTurn, state.pendingInputs]
|
|
3467
3986
|
);
|
|
3468
|
-
const retry = (0,
|
|
3987
|
+
const retry = (0, import_react3.useCallback)(async () => {
|
|
3988
|
+
if (handoffBlocksAI.current) return;
|
|
3469
3989
|
const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
|
|
3470
3990
|
if (!visitorMessage) return;
|
|
3471
3991
|
if (runRef.current) {
|
|
@@ -3498,7 +4018,8 @@ ${outgoing}` : outgoing;
|
|
|
3498
4018
|
visitorText: visitorTurnText(visitorMessage)
|
|
3499
4019
|
});
|
|
3500
4020
|
}, [runTurn, state.messages]);
|
|
3501
|
-
const regenerate = (0,
|
|
4021
|
+
const regenerate = (0, import_react3.useCallback)(async () => {
|
|
4022
|
+
if (handoffBlocksAI.current) return null;
|
|
3502
4023
|
let lastVisitorIndex = -1;
|
|
3503
4024
|
for (let index = state.messages.length - 1; index >= 0; index -= 1) {
|
|
3504
4025
|
if (state.messages[index]?.role === "visitor") {
|
|
@@ -3541,7 +4062,7 @@ ${outgoing}` : outgoing;
|
|
|
3541
4062
|
visitorText: visitorTurnText(visitorMessage)
|
|
3542
4063
|
});
|
|
3543
4064
|
}, [runTurn, state.messages]);
|
|
3544
|
-
const respondToToolInput = (0,
|
|
4065
|
+
const respondToToolInput = (0, import_react3.useCallback)(
|
|
3545
4066
|
async (surface, values) => {
|
|
3546
4067
|
await submit(`${surface.title} submitted`, {
|
|
3547
4068
|
runtimeText: [
|
|
@@ -3554,9 +4075,9 @@ ${outgoing}` : outgoing;
|
|
|
3554
4075
|
},
|
|
3555
4076
|
[submit]
|
|
3556
4077
|
);
|
|
3557
|
-
const respondToInput = (0,
|
|
4078
|
+
const respondToInput = (0, import_react3.useCallback)(
|
|
3558
4079
|
async (response) => {
|
|
3559
|
-
if (runRef.current) return;
|
|
4080
|
+
if (handoffBlocksAI.current || runRef.current) return;
|
|
3560
4081
|
const pending = state.pendingInputs?.find(
|
|
3561
4082
|
(request) => request.requestId === response.requestId
|
|
3562
4083
|
);
|
|
@@ -3584,7 +4105,8 @@ ${outgoing}` : outgoing;
|
|
|
3584
4105
|
},
|
|
3585
4106
|
[respondToToolInput, runTurn, state.pendingInputs]
|
|
3586
4107
|
);
|
|
3587
|
-
(0,
|
|
4108
|
+
(0, import_react3.useEffect)(() => {
|
|
4109
|
+
if (handoff.blocksAI) return;
|
|
3588
4110
|
const conversation = loadPersistedAgentConversation(
|
|
3589
4111
|
resolvedStorageKeyPrefix,
|
|
3590
4112
|
visitorId
|
|
@@ -3606,14 +4128,21 @@ ${outgoing}` : outgoing;
|
|
|
3606
4128
|
}
|
|
3607
4129
|
controller.abort();
|
|
3608
4130
|
};
|
|
3609
|
-
}, [
|
|
3610
|
-
|
|
4131
|
+
}, [
|
|
4132
|
+
handoff.blocksAI,
|
|
4133
|
+
identityKey,
|
|
4134
|
+
resolvedStorageKeyPrefix,
|
|
4135
|
+
runTurn,
|
|
4136
|
+
visitorId
|
|
4137
|
+
]);
|
|
4138
|
+
(0, import_react3.useEffect)(() => {
|
|
3611
4139
|
return () => {
|
|
3612
4140
|
runRef.current?.abort();
|
|
3613
4141
|
runRef.current = null;
|
|
3614
4142
|
};
|
|
3615
4143
|
}, []);
|
|
3616
4144
|
return {
|
|
4145
|
+
handoff,
|
|
3617
4146
|
state,
|
|
3618
4147
|
reset,
|
|
3619
4148
|
retry,
|
|
@@ -3642,12 +4171,12 @@ function isAgentBusy(phase) {
|
|
|
3642
4171
|
}
|
|
3643
4172
|
|
|
3644
4173
|
// src/react/hooks/useIsMobile.ts
|
|
3645
|
-
var
|
|
4174
|
+
var import_react4 = require("react");
|
|
3646
4175
|
function useIsMobile(breakpoint = 767) {
|
|
3647
|
-
const [isMobile, setIsMobile] = (0,
|
|
4176
|
+
const [isMobile, setIsMobile] = (0, import_react4.useState)(
|
|
3648
4177
|
() => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
|
|
3649
4178
|
);
|
|
3650
|
-
(0,
|
|
4179
|
+
(0, import_react4.useEffect)(() => {
|
|
3651
4180
|
const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
|
|
3652
4181
|
const onChange = () => setIsMobile(media.matches);
|
|
3653
4182
|
onChange();
|
|
@@ -3684,7 +4213,7 @@ function unregisterAgentPanelController(customerId) {
|
|
|
3684
4213
|
}
|
|
3685
4214
|
|
|
3686
4215
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
3687
|
-
var
|
|
4216
|
+
var import_react16 = require("react");
|
|
3688
4217
|
|
|
3689
4218
|
// src/react/types/conversation.ts
|
|
3690
4219
|
var defaultAgentRailTheme = {
|
|
@@ -3767,7 +4296,7 @@ function agentThemeStyle(theme, resolvedColorScheme) {
|
|
|
3767
4296
|
}
|
|
3768
4297
|
|
|
3769
4298
|
// src/react/hooks/useAgentColorScheme.ts
|
|
3770
|
-
var
|
|
4299
|
+
var import_react5 = require("react");
|
|
3771
4300
|
var DARK_MODE_QUERY = "(prefers-color-scheme: dark)";
|
|
3772
4301
|
function subscribeToDarkMode(onChange) {
|
|
3773
4302
|
if (typeof window === "undefined" || !window.matchMedia) {
|
|
@@ -3785,7 +4314,7 @@ function getPrefersDarkMode() {
|
|
|
3785
4314
|
return typeof window !== "undefined" && Boolean(window.matchMedia?.(DARK_MODE_QUERY).matches);
|
|
3786
4315
|
}
|
|
3787
4316
|
function useAgentColorScheme(colorScheme = "auto") {
|
|
3788
|
-
const prefersDarkMode = (0,
|
|
4317
|
+
const prefersDarkMode = (0, import_react5.useSyncExternalStore)(
|
|
3789
4318
|
subscribeToDarkMode,
|
|
3790
4319
|
getPrefersDarkMode,
|
|
3791
4320
|
() => false
|
|
@@ -3797,7 +4326,7 @@ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
|
|
|
3797
4326
|
}
|
|
3798
4327
|
|
|
3799
4328
|
// src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
|
|
3800
|
-
var
|
|
4329
|
+
var import_react6 = require("react");
|
|
3801
4330
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
3802
4331
|
function joinLabels(labels) {
|
|
3803
4332
|
if (labels.length <= 1) return labels[0] ?? "";
|
|
@@ -3916,7 +4445,7 @@ function AgentActivityBubble({
|
|
|
3916
4445
|
const statusText = workSummary(steps, failed, brandLabel);
|
|
3917
4446
|
const softReview = statusText === AGENT_SOFT_REVIEW_STATUS;
|
|
3918
4447
|
const receiptId = steps.map((step) => step.id).join(":");
|
|
3919
|
-
const [expandedReceiptId, setExpandedReceiptId] = (0,
|
|
4448
|
+
const [expandedReceiptId, setExpandedReceiptId] = (0, import_react6.useState)(
|
|
3920
4449
|
null
|
|
3921
4450
|
);
|
|
3922
4451
|
const detailsOpen = !active && expandedReceiptId === receiptId;
|
|
@@ -3967,17 +4496,17 @@ function AgentActivityBubble({
|
|
|
3967
4496
|
}
|
|
3968
4497
|
|
|
3969
4498
|
// src/react/components/AnswerReceiptDialog/AnswerReceiptDialog.tsx
|
|
3970
|
-
var
|
|
4499
|
+
var import_react8 = require("react");
|
|
3971
4500
|
var import_react_dom = require("react-dom");
|
|
3972
4501
|
|
|
3973
4502
|
// src/react/components/AgentRail/AgentRailOverlayContext.tsx
|
|
3974
|
-
var
|
|
3975
|
-
var AgentRailOverlayContext = (0,
|
|
4503
|
+
var import_react7 = require("react");
|
|
4504
|
+
var AgentRailOverlayContext = (0, import_react7.createContext)(null);
|
|
3976
4505
|
function useAgentRailPortalRoots() {
|
|
3977
|
-
return (0,
|
|
4506
|
+
return (0, import_react7.useContext)(AgentRailOverlayContext);
|
|
3978
4507
|
}
|
|
3979
4508
|
function useAgentRailMenuPortalRoot() {
|
|
3980
|
-
return (0,
|
|
4509
|
+
return (0, import_react7.useContext)(AgentRailOverlayContext)?.railRef ?? null;
|
|
3981
4510
|
}
|
|
3982
4511
|
|
|
3983
4512
|
// src/react/components/AnswerReceiptDialog/AnswerReceiptDialog.tsx
|
|
@@ -4001,10 +4530,10 @@ function AnswerReceiptDialog({
|
|
|
4001
4530
|
}) {
|
|
4002
4531
|
const portalRoots = useAgentRailPortalRoots();
|
|
4003
4532
|
const overlayRoot = portalRoots?.overlayRef ?? null;
|
|
4004
|
-
const cardRef = (0,
|
|
4005
|
-
const closeButtonRef = (0,
|
|
4006
|
-
const previouslyFocusedRef = (0,
|
|
4007
|
-
(0,
|
|
4533
|
+
const cardRef = (0, import_react8.useRef)(null);
|
|
4534
|
+
const closeButtonRef = (0, import_react8.useRef)(null);
|
|
4535
|
+
const previouslyFocusedRef = (0, import_react8.useRef)(null);
|
|
4536
|
+
(0, import_react8.useEffect)(() => {
|
|
4008
4537
|
previouslyFocusedRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
4009
4538
|
closeButtonRef.current?.focus({ preventScroll: true });
|
|
4010
4539
|
const handleKeyDown = (event) => {
|
|
@@ -4089,7 +4618,7 @@ function AnswerReceiptDialog({
|
|
|
4089
4618
|
}
|
|
4090
4619
|
|
|
4091
4620
|
// src/react/components/Composer/Composer.tsx
|
|
4092
|
-
var
|
|
4621
|
+
var import_react9 = require("react");
|
|
4093
4622
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
4094
4623
|
var FORM_SUBMITTED_MESSAGE = "Shared my details";
|
|
4095
4624
|
function SendIcon() {
|
|
@@ -4151,17 +4680,17 @@ function Composer({
|
|
|
4151
4680
|
allowFormResume = true,
|
|
4152
4681
|
onSubmit
|
|
4153
4682
|
}) {
|
|
4154
|
-
const [value, setValue] = (0,
|
|
4155
|
-
const [draft, setDraft] = (0,
|
|
4156
|
-
const inputRef = (0,
|
|
4157
|
-
const firstFieldRef = (0,
|
|
4158
|
-
const formRef = (0,
|
|
4159
|
-
const formId = (0,
|
|
4683
|
+
const [value, setValue] = (0, import_react9.useState)("");
|
|
4684
|
+
const [draft, setDraft] = (0, import_react9.useState)(() => createDraft(form));
|
|
4685
|
+
const inputRef = (0, import_react9.useRef)(null);
|
|
4686
|
+
const firstFieldRef = (0, import_react9.useRef)(null);
|
|
4687
|
+
const formRef = (0, import_react9.useRef)(null);
|
|
4688
|
+
const formId = (0, import_react9.useId)();
|
|
4160
4689
|
if (form && form.id !== draft.form?.id) setDraft(createDraft(form));
|
|
4161
4690
|
const savedForm = allowFormResume && !draft.submitted ? draft.form : null;
|
|
4162
4691
|
const activeForm = !disabled && draft.expanded ? savedForm : null;
|
|
4163
4692
|
const canSend = !disabled && Boolean(value.trim() || activeForm);
|
|
4164
|
-
(0,
|
|
4693
|
+
(0, import_react9.useEffect)(() => {
|
|
4165
4694
|
if (activeForm) firstFieldRef.current?.focus();
|
|
4166
4695
|
else if ((savedForm || draft.submitted) && !disabled)
|
|
4167
4696
|
inputRef.current?.focus();
|
|
@@ -4422,7 +4951,7 @@ function FollowUpChips({
|
|
|
4422
4951
|
}
|
|
4423
4952
|
|
|
4424
4953
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
4425
|
-
var
|
|
4954
|
+
var import_react11 = require("react");
|
|
4426
4955
|
|
|
4427
4956
|
// src/react/components/SearchReferences/SearchReferences.tsx
|
|
4428
4957
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
@@ -4513,7 +5042,7 @@ function SearchReferences({
|
|
|
4513
5042
|
}
|
|
4514
5043
|
|
|
4515
5044
|
// src/react/components/BookingCard/BookingCard.tsx
|
|
4516
|
-
var
|
|
5045
|
+
var import_react10 = require("react");
|
|
4517
5046
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
4518
5047
|
var BOOKING_STEPS = [
|
|
4519
5048
|
{ id: "date", label: "Date" },
|
|
@@ -4568,27 +5097,27 @@ function InteractiveBookingCard({
|
|
|
4568
5097
|
offer,
|
|
4569
5098
|
onBook
|
|
4570
5099
|
}) {
|
|
4571
|
-
const fieldId = (0,
|
|
5100
|
+
const fieldId = (0, import_react10.useId)();
|
|
4572
5101
|
const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
|
|
4573
|
-
const [step, setStep] = (0,
|
|
4574
|
-
const [eventTypeUri, setEventTypeUri] = (0,
|
|
4575
|
-
const [selectedDate, setSelectedDate] = (0,
|
|
4576
|
-
const [startTime, setStartTime] = (0,
|
|
4577
|
-
const [name, setName] = (0,
|
|
4578
|
-
const [email, setEmail] = (0,
|
|
4579
|
-
const activeStepRef = (0,
|
|
4580
|
-
const previousStepRef = (0,
|
|
5102
|
+
const [step, setStep] = (0, import_react10.useState)("date");
|
|
5103
|
+
const [eventTypeUri, setEventTypeUri] = (0, import_react10.useState)(defaultType);
|
|
5104
|
+
const [selectedDate, setSelectedDate] = (0, import_react10.useState)("");
|
|
5105
|
+
const [startTime, setStartTime] = (0, import_react10.useState)("");
|
|
5106
|
+
const [name, setName] = (0, import_react10.useState)("");
|
|
5107
|
+
const [email, setEmail] = (0, import_react10.useState)("");
|
|
5108
|
+
const activeStepRef = (0, import_react10.useRef)(null);
|
|
5109
|
+
const previousStepRef = (0, import_react10.useRef)(step);
|
|
4581
5110
|
const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
|
|
4582
|
-
(0,
|
|
5111
|
+
(0, import_react10.useEffect)(() => {
|
|
4583
5112
|
if (previousStepRef.current === step) return;
|
|
4584
5113
|
previousStepRef.current = step;
|
|
4585
5114
|
activeStepRef.current?.scrollIntoView({ block: "nearest" });
|
|
4586
5115
|
}, [step]);
|
|
4587
|
-
const slots = (0,
|
|
5116
|
+
const slots = (0, import_react10.useMemo)(
|
|
4588
5117
|
() => bookingSlotsForEventType(offer.slots, eventTypeUri),
|
|
4589
5118
|
[eventTypeUri, offer.slots]
|
|
4590
5119
|
);
|
|
4591
|
-
const availableByDate = (0,
|
|
5120
|
+
const availableByDate = (0, import_react10.useMemo)(() => {
|
|
4592
5121
|
const next = /* @__PURE__ */ new Map();
|
|
4593
5122
|
for (const slot of slots) {
|
|
4594
5123
|
const key = slotDateKey(slot.startTime);
|
|
@@ -4596,7 +5125,7 @@ function InteractiveBookingCard({
|
|
|
4596
5125
|
}
|
|
4597
5126
|
return next;
|
|
4598
5127
|
}, [slots]);
|
|
4599
|
-
const [visibleMonth, setVisibleMonth] = (0,
|
|
5128
|
+
const [visibleMonth, setVisibleMonth] = (0, import_react10.useState)(
|
|
4600
5129
|
() => firstAvailableBookingMonth(slots)
|
|
4601
5130
|
);
|
|
4602
5131
|
function selectEventType(nextType) {
|
|
@@ -4609,7 +5138,7 @@ function InteractiveBookingCard({
|
|
|
4609
5138
|
)
|
|
4610
5139
|
);
|
|
4611
5140
|
}
|
|
4612
|
-
const daySlots = (0,
|
|
5141
|
+
const daySlots = (0, import_react10.useMemo)(
|
|
4613
5142
|
() => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
|
|
4614
5143
|
[selectedDate, slots]
|
|
4615
5144
|
);
|
|
@@ -4618,7 +5147,7 @@ function InteractiveBookingCard({
|
|
|
4618
5147
|
);
|
|
4619
5148
|
const selectedSample = availableByDate.get(selectedDate) ?? startTime;
|
|
4620
5149
|
const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
|
|
4621
|
-
const weekdays = (0,
|
|
5150
|
+
const weekdays = (0, import_react10.useMemo)(() => weekdayLabels(), []);
|
|
4622
5151
|
const cells = calendarCells(visibleMonth.year, visibleMonth.month);
|
|
4623
5152
|
const canPrevMonth = [...availableByDate.keys()].some((key) => {
|
|
4624
5153
|
const month = monthFromKey(key);
|
|
@@ -4904,8 +5433,8 @@ function resolveMessageUrl(safeUrl, baseUrl) {
|
|
|
4904
5433
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
4905
5434
|
var import_styles = require("streamdown/styles.css");
|
|
4906
5435
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
4907
|
-
function normalizeDedupeText(
|
|
4908
|
-
return
|
|
5436
|
+
function normalizeDedupeText(text3) {
|
|
5437
|
+
return text3.trim().replace(/\s+/g, " ").toLowerCase();
|
|
4909
5438
|
}
|
|
4910
5439
|
function paragraphsAreNearDuplicates(first, second) {
|
|
4911
5440
|
const left = normalizeDedupeText(first);
|
|
@@ -4921,8 +5450,8 @@ function paragraphsShareOpening(first, second) {
|
|
|
4921
5450
|
if (!opening || opening.length < 20) return false;
|
|
4922
5451
|
return second.trim().startsWith(opening);
|
|
4923
5452
|
}
|
|
4924
|
-
function collapseRepeatedText(
|
|
4925
|
-
const trimmed =
|
|
5453
|
+
function collapseRepeatedText(text3) {
|
|
5454
|
+
const trimmed = text3.trim();
|
|
4926
5455
|
if (trimmed.length < 40) return trimmed;
|
|
4927
5456
|
const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
|
|
4928
5457
|
if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
|
|
@@ -4943,13 +5472,14 @@ function MessageBubble({
|
|
|
4943
5472
|
message,
|
|
4944
5473
|
brandLogoUrl,
|
|
4945
5474
|
bookingDisabled = false,
|
|
5475
|
+
showProvenance = false,
|
|
4946
5476
|
bookingReadOnly = false,
|
|
4947
5477
|
linkBaseUrl,
|
|
4948
5478
|
offer,
|
|
4949
5479
|
onBook
|
|
4950
5480
|
}) {
|
|
4951
5481
|
const resolvedLogoUrl = brandLogoUrl?.trim();
|
|
4952
|
-
const [failedLogoUrl, setFailedLogoUrl] = (0,
|
|
5482
|
+
const [failedLogoUrl, setFailedLogoUrl] = (0, import_react11.useState)(null);
|
|
4953
5483
|
const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
|
|
4954
5484
|
const cards = message.role === "agent" ? extractToolCards(message.text) : [];
|
|
4955
5485
|
const extractedOffers = cards.filter(
|
|
@@ -4964,7 +5494,34 @@ function MessageBubble({
|
|
|
4964
5494
|
if (message.role === "visitor") {
|
|
4965
5495
|
const visitorText = visitorMessageDisplayText(message).trim();
|
|
4966
5496
|
if (!visitorText) return null;
|
|
4967
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.
|
|
5497
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
5498
|
+
"article",
|
|
5499
|
+
{
|
|
5500
|
+
className: "message-bubble message-bubble--visitor",
|
|
5501
|
+
"aria-label": "Message from visitor",
|
|
5502
|
+
children: [
|
|
5503
|
+
showProvenance ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "message-bubble__speaker", children: "You \xB7 Visitor" }) : null,
|
|
5504
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "message-bubble__text", children: visitorText })
|
|
5505
|
+
]
|
|
5506
|
+
}
|
|
5507
|
+
);
|
|
5508
|
+
}
|
|
5509
|
+
if (message.role === "human") {
|
|
5510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
5511
|
+
"article",
|
|
5512
|
+
{
|
|
5513
|
+
className: "message-bubble message-bubble--human",
|
|
5514
|
+
"aria-label": `Message from ${message.representative.name}`,
|
|
5515
|
+
children: [
|
|
5516
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("strong", { className: "message-bubble__representative", children: [
|
|
5517
|
+
message.representative.name,
|
|
5518
|
+
" ",
|
|
5519
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Human representative" })
|
|
5520
|
+
] }),
|
|
5521
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "message-bubble__text", children: message.text })
|
|
5522
|
+
]
|
|
5523
|
+
}
|
|
5524
|
+
);
|
|
4968
5525
|
}
|
|
4969
5526
|
const citations = message.citations ?? [];
|
|
4970
5527
|
const agentText = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "message-bubble__text", children: [
|
|
@@ -5001,40 +5558,48 @@ function MessageBubble({
|
|
|
5001
5558
|
] });
|
|
5002
5559
|
if (!displayText && !message.searchResults?.length && offers.length === 0)
|
|
5003
5560
|
return null;
|
|
5004
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5561
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
5562
|
+
"article",
|
|
5563
|
+
{
|
|
5564
|
+
className: "message-bubble message-bubble--agent",
|
|
5565
|
+
"aria-label": "Message from AI assistant",
|
|
5566
|
+
children: [
|
|
5567
|
+
showProvenance ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "message-bubble__speaker", children: "AI assistant" }) : null,
|
|
5568
|
+
displayText || message.searchResults?.length ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "message-bubble__agent-row", children: [
|
|
5569
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
5570
|
+
"img",
|
|
5571
|
+
{
|
|
5572
|
+
src: resolvedLogoUrl,
|
|
5573
|
+
alt: "",
|
|
5574
|
+
onError: () => {
|
|
5575
|
+
setFailedLogoUrl(resolvedLogoUrl ?? null);
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
) }),
|
|
5579
|
+
agentText
|
|
5580
|
+
] }) : agentText : null,
|
|
5581
|
+
offers.map((nextOffer, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
5582
|
+
BookingCard,
|
|
5583
|
+
{
|
|
5584
|
+
disabled: bookingDisabled,
|
|
5585
|
+
readOnly: bookingReadOnly,
|
|
5586
|
+
offer: nextOffer,
|
|
5587
|
+
onBook
|
|
5588
|
+
},
|
|
5589
|
+
`${bookingOfferIdentityKey(nextOffer)}-${index}`
|
|
5590
|
+
))
|
|
5591
|
+
]
|
|
5592
|
+
}
|
|
5593
|
+
);
|
|
5029
5594
|
}
|
|
5030
5595
|
|
|
5031
5596
|
// src/react/components/MessageActions/MessageActions.tsx
|
|
5032
|
-
var
|
|
5597
|
+
var import_react12 = require("react");
|
|
5033
5598
|
var import_react_dom2 = require("react-dom");
|
|
5034
5599
|
|
|
5035
5600
|
// src/react/lib/speech.ts
|
|
5036
|
-
function toSpeechText(
|
|
5037
|
-
let out = hideToolCardFences(
|
|
5601
|
+
function toSpeechText(text3) {
|
|
5602
|
+
let out = hideToolCardFences(text3);
|
|
5038
5603
|
out = out.replace(/```[\s\S]*?```/g, " ");
|
|
5039
5604
|
out = out.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
5040
5605
|
out = out.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
|
|
@@ -5369,12 +5934,12 @@ function StopIcon() {
|
|
|
5369
5934
|
}
|
|
5370
5935
|
) });
|
|
5371
5936
|
}
|
|
5372
|
-
async function writeToClipboard(
|
|
5937
|
+
async function writeToClipboard(text3) {
|
|
5373
5938
|
try {
|
|
5374
|
-
await navigator.clipboard.writeText(
|
|
5939
|
+
await navigator.clipboard.writeText(text3);
|
|
5375
5940
|
} catch {
|
|
5376
5941
|
const textarea = document.createElement("textarea");
|
|
5377
|
-
textarea.value =
|
|
5942
|
+
textarea.value = text3;
|
|
5378
5943
|
textarea.style.position = "fixed";
|
|
5379
5944
|
textarea.style.opacity = "0";
|
|
5380
5945
|
document.body.appendChild(textarea);
|
|
@@ -5425,26 +5990,26 @@ function MessageActions({
|
|
|
5425
5990
|
speechText
|
|
5426
5991
|
}) {
|
|
5427
5992
|
const menuPortalRoot = useAgentRailMenuPortalRoot();
|
|
5428
|
-
const [copied, setCopied] = (0,
|
|
5429
|
-
const [rating, setRating] = (0,
|
|
5430
|
-
const [menuOpen, setMenuOpen] = (0,
|
|
5431
|
-
const [menuPosition, setMenuPosition] = (0,
|
|
5432
|
-
const [speaking, setSpeaking] = (0,
|
|
5433
|
-
const [speechSupported, setSpeechSupported] = (0,
|
|
5434
|
-
const copyTimerRef = (0,
|
|
5435
|
-
const menuRef = (0,
|
|
5436
|
-
const portaledMenuRef = (0,
|
|
5437
|
-
const moreButtonRef = (0,
|
|
5993
|
+
const [copied, setCopied] = (0, import_react12.useState)(false);
|
|
5994
|
+
const [rating, setRating] = (0, import_react12.useState)(null);
|
|
5995
|
+
const [menuOpen, setMenuOpen] = (0, import_react12.useState)(false);
|
|
5996
|
+
const [menuPosition, setMenuPosition] = (0, import_react12.useState)(null);
|
|
5997
|
+
const [speaking, setSpeaking] = (0, import_react12.useState)(false);
|
|
5998
|
+
const [speechSupported, setSpeechSupported] = (0, import_react12.useState)(null);
|
|
5999
|
+
const copyTimerRef = (0, import_react12.useRef)(null);
|
|
6000
|
+
const menuRef = (0, import_react12.useRef)(null);
|
|
6001
|
+
const portaledMenuRef = (0, import_react12.useRef)(null);
|
|
6002
|
+
const moreButtonRef = (0, import_react12.useRef)(null);
|
|
5438
6003
|
const answeredLabel = formatAnsweredAt(answeredAt ?? 0);
|
|
5439
6004
|
const resolvedSpeechText = speechText ?? toSpeechText(copyText);
|
|
5440
6005
|
const canOpenReceipt = Boolean(receiptSteps) && Boolean(onOpenReceipt);
|
|
5441
6006
|
const readAloudEligible = Boolean(readAloud && resolvedSpeechText);
|
|
5442
6007
|
const canReadAloud = readAloudEligible && speechSupported === true;
|
|
5443
6008
|
const showMenu = canOpenReceipt || (speechSupported === null ? readAloudEligible : canReadAloud);
|
|
5444
|
-
(0,
|
|
6009
|
+
(0, import_react12.useLayoutEffect)(() => {
|
|
5445
6010
|
setSpeechSupported(isSpeechSupported());
|
|
5446
6011
|
}, []);
|
|
5447
|
-
(0,
|
|
6012
|
+
(0, import_react12.useLayoutEffect)(() => {
|
|
5448
6013
|
if (!menuOpen || !moreButtonRef.current || !portaledMenuRef.current) {
|
|
5449
6014
|
setMenuPosition(null);
|
|
5450
6015
|
return;
|
|
@@ -5461,7 +6026,7 @@ function MessageActions({
|
|
|
5461
6026
|
})
|
|
5462
6027
|
);
|
|
5463
6028
|
}, [menuOpen, menuPortalRoot]);
|
|
5464
|
-
(0,
|
|
6029
|
+
(0, import_react12.useEffect)(() => {
|
|
5465
6030
|
const unsubscribe = subscribeSpeechInterrupts(() => setSpeaking(false));
|
|
5466
6031
|
return () => {
|
|
5467
6032
|
unsubscribe();
|
|
@@ -5471,7 +6036,7 @@ function MessageActions({
|
|
|
5471
6036
|
stopSpeech();
|
|
5472
6037
|
};
|
|
5473
6038
|
}, []);
|
|
5474
|
-
(0,
|
|
6039
|
+
(0, import_react12.useEffect)(() => {
|
|
5475
6040
|
if (!menuOpen) return;
|
|
5476
6041
|
const handlePointerDown = (event) => {
|
|
5477
6042
|
const target = event.target;
|
|
@@ -5640,7 +6205,7 @@ function MessageActions({
|
|
|
5640
6205
|
}
|
|
5641
6206
|
|
|
5642
6207
|
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
5643
|
-
var
|
|
6208
|
+
var import_react14 = require("react");
|
|
5644
6209
|
|
|
5645
6210
|
// src/react/components/ConfirmationCard/ConfirmationCard.tsx
|
|
5646
6211
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
@@ -5682,7 +6247,7 @@ function ConfirmationCard({
|
|
|
5682
6247
|
}
|
|
5683
6248
|
|
|
5684
6249
|
// src/react/components/ToolInputCard/ToolInputCard.tsx
|
|
5685
|
-
var
|
|
6250
|
+
var import_react13 = require("react");
|
|
5686
6251
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
5687
6252
|
function isRecord5(value) {
|
|
5688
6253
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -5805,13 +6370,13 @@ function valuesMatch(left, right) {
|
|
|
5805
6370
|
}
|
|
5806
6371
|
function FieldDescription({
|
|
5807
6372
|
field,
|
|
5808
|
-
id
|
|
6373
|
+
id: id2
|
|
5809
6374
|
}) {
|
|
5810
|
-
return field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id, className: "tool-input-card__description", children: field.description }) : null;
|
|
6375
|
+
return field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: id2, className: "tool-input-card__description", children: field.description }) : null;
|
|
5811
6376
|
}
|
|
5812
6377
|
function ChoiceField({
|
|
5813
6378
|
field,
|
|
5814
|
-
id,
|
|
6379
|
+
id: id2,
|
|
5815
6380
|
value,
|
|
5816
6381
|
disabled,
|
|
5817
6382
|
describedBy,
|
|
@@ -5829,7 +6394,7 @@ function ChoiceField({
|
|
|
5829
6394
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
5830
6395
|
"select",
|
|
5831
6396
|
{
|
|
5832
|
-
id,
|
|
6397
|
+
id: id2,
|
|
5833
6398
|
value: selectedIndex >= 0 ? String(selectedIndex) : "",
|
|
5834
6399
|
disabled,
|
|
5835
6400
|
required: field.required,
|
|
@@ -5852,7 +6417,7 @@ function ChoiceField({
|
|
|
5852
6417
|
{
|
|
5853
6418
|
className: "tool-input-card__choices",
|
|
5854
6419
|
role: multiple ? "group" : "radiogroup",
|
|
5855
|
-
"aria-labelledby": `${
|
|
6420
|
+
"aria-labelledby": `${id2}-label`,
|
|
5856
6421
|
"aria-describedby": describedBy,
|
|
5857
6422
|
"aria-invalid": Boolean(error),
|
|
5858
6423
|
"aria-required": field.required,
|
|
@@ -5863,7 +6428,7 @@ function ChoiceField({
|
|
|
5863
6428
|
"input",
|
|
5864
6429
|
{
|
|
5865
6430
|
type: multiple ? "checkbox" : "radio",
|
|
5866
|
-
name:
|
|
6431
|
+
name: id2,
|
|
5867
6432
|
value: String(index),
|
|
5868
6433
|
checked,
|
|
5869
6434
|
disabled,
|
|
@@ -5891,22 +6456,22 @@ function ToolField({
|
|
|
5891
6456
|
disabled,
|
|
5892
6457
|
error,
|
|
5893
6458
|
field,
|
|
5894
|
-
id,
|
|
6459
|
+
id: id2,
|
|
5895
6460
|
value,
|
|
5896
6461
|
onBlur,
|
|
5897
6462
|
onChange
|
|
5898
6463
|
}) {
|
|
5899
6464
|
const describedBy = [
|
|
5900
|
-
field.description ? `${
|
|
5901
|
-
error ? `${
|
|
6465
|
+
field.description ? `${id2}-description` : "",
|
|
6466
|
+
error ? `${id2}-error` : ""
|
|
5902
6467
|
].filter(Boolean).join(" ");
|
|
5903
6468
|
if (field.kind === "checkbox" || field.kind === "confirmation") {
|
|
5904
6469
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-input-card__field", children: [
|
|
5905
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "tool-input-card__check", htmlFor:
|
|
6470
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "tool-input-card__check", htmlFor: id2, children: [
|
|
5906
6471
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
5907
6472
|
"input",
|
|
5908
6473
|
{
|
|
5909
|
-
id,
|
|
6474
|
+
id: id2,
|
|
5910
6475
|
type: "checkbox",
|
|
5911
6476
|
checked: value === true,
|
|
5912
6477
|
disabled,
|
|
@@ -5918,18 +6483,18 @@ function ToolField({
|
|
|
5918
6483
|
),
|
|
5919
6484
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
|
|
5920
6485
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: field.label }),
|
|
5921
|
-
field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${
|
|
6486
|
+
field.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-description`, children: field.description }) : null
|
|
5922
6487
|
] })
|
|
5923
6488
|
] }),
|
|
5924
|
-
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${
|
|
6489
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
|
|
5925
6490
|
] });
|
|
5926
6491
|
}
|
|
5927
|
-
const label = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { id: `${
|
|
6492
|
+
const label = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { id: `${id2}-label`, htmlFor: id2, children: [
|
|
5928
6493
|
field.label,
|
|
5929
6494
|
field.required ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { "aria-hidden": "true", children: " *" }) : null
|
|
5930
6495
|
] });
|
|
5931
6496
|
const common = {
|
|
5932
|
-
id,
|
|
6497
|
+
id: id2,
|
|
5933
6498
|
disabled,
|
|
5934
6499
|
required: field.required,
|
|
5935
6500
|
"aria-describedby": describedBy || void 0,
|
|
@@ -5942,7 +6507,7 @@ function ToolField({
|
|
|
5942
6507
|
ChoiceField,
|
|
5943
6508
|
{
|
|
5944
6509
|
field,
|
|
5945
|
-
id,
|
|
6510
|
+
id: id2,
|
|
5946
6511
|
value,
|
|
5947
6512
|
disabled,
|
|
5948
6513
|
describedBy: describedBy || void 0,
|
|
@@ -5979,7 +6544,7 @@ function ToolField({
|
|
|
5979
6544
|
onChange: (event) => onChange(Number(event.target.value))
|
|
5980
6545
|
}
|
|
5981
6546
|
),
|
|
5982
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("output", { htmlFor:
|
|
6547
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("output", { htmlFor: id2, children: numericValue })
|
|
5983
6548
|
] });
|
|
5984
6549
|
} else {
|
|
5985
6550
|
const type = field.kind === "date-time" ? "datetime-local" : field.kind === "calendar" ? "date" : field.kind;
|
|
@@ -6003,9 +6568,9 @@ function ToolField({
|
|
|
6003
6568
|
}
|
|
6004
6569
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-input-card__field", children: [
|
|
6005
6570
|
label,
|
|
6006
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldDescription, { field, id: `${
|
|
6571
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldDescription, { field, id: `${id2}-description` }),
|
|
6007
6572
|
control,
|
|
6008
|
-
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${
|
|
6573
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { id: `${id2}-error`, className: "tool-input-card__error", children: error }) : null
|
|
6009
6574
|
] });
|
|
6010
6575
|
}
|
|
6011
6576
|
function ToolInputCard({
|
|
@@ -6013,11 +6578,11 @@ function ToolInputCard({
|
|
|
6013
6578
|
surface,
|
|
6014
6579
|
onSubmit
|
|
6015
6580
|
}) {
|
|
6016
|
-
const [values, setValues] = (0,
|
|
6581
|
+
const [values, setValues] = (0, import_react13.useState)(
|
|
6017
6582
|
() => initialValues(surface)
|
|
6018
6583
|
);
|
|
6019
|
-
const [touched, setTouched] = (0,
|
|
6020
|
-
const [submitted, setSubmitted] = (0,
|
|
6584
|
+
const [touched, setTouched] = (0, import_react13.useState)(() => /* @__PURE__ */ new Set());
|
|
6585
|
+
const [submitted, setSubmitted] = (0, import_react13.useState)(false);
|
|
6021
6586
|
const errors = Object.fromEntries(
|
|
6022
6587
|
surface.fields.map((field) => [
|
|
6023
6588
|
field.path,
|
|
@@ -6110,7 +6675,7 @@ function HumanInputCard({
|
|
|
6110
6675
|
request,
|
|
6111
6676
|
onRespond
|
|
6112
6677
|
}) {
|
|
6113
|
-
const [
|
|
6678
|
+
const [text3, setText] = (0, import_react14.useState)("");
|
|
6114
6679
|
const options = request.options ?? [];
|
|
6115
6680
|
const showText = request.display === "text" || request.allowFreeform && options.length === 0;
|
|
6116
6681
|
if (request.ui) {
|
|
@@ -6135,7 +6700,7 @@ function HumanInputCard({
|
|
|
6135
6700
|
}
|
|
6136
6701
|
function submitText(event) {
|
|
6137
6702
|
event.preventDefault();
|
|
6138
|
-
const value =
|
|
6703
|
+
const value = text3.trim();
|
|
6139
6704
|
if (!value || disabled) return;
|
|
6140
6705
|
onRespond?.({ requestId: request.requestId, text: value });
|
|
6141
6706
|
}
|
|
@@ -6153,12 +6718,12 @@ function HumanInputCard({
|
|
|
6153
6718
|
"input",
|
|
6154
6719
|
{
|
|
6155
6720
|
id: `human-input-text-${request.requestId}`,
|
|
6156
|
-
value:
|
|
6721
|
+
value: text3,
|
|
6157
6722
|
disabled,
|
|
6158
6723
|
onChange: (event) => setText(event.target.value)
|
|
6159
6724
|
}
|
|
6160
6725
|
),
|
|
6161
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", disabled: disabled || !
|
|
6726
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", disabled: disabled || !text3.trim(), children: "Send" })
|
|
6162
6727
|
] })
|
|
6163
6728
|
] }) : null,
|
|
6164
6729
|
!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
|
|
@@ -6168,7 +6733,7 @@ function HumanInputCard({
|
|
|
6168
6733
|
}
|
|
6169
6734
|
|
|
6170
6735
|
// src/react/components/LocationConsent/LocationConsent.tsx
|
|
6171
|
-
var
|
|
6736
|
+
var import_react15 = require("react");
|
|
6172
6737
|
|
|
6173
6738
|
// src/runtime/location-consent.ts
|
|
6174
6739
|
function isLocationConsentRequest(request) {
|
|
@@ -6216,16 +6781,16 @@ function LocationConsent({
|
|
|
6216
6781
|
request,
|
|
6217
6782
|
onRespond
|
|
6218
6783
|
}) {
|
|
6219
|
-
const [preference, setPreference] = (0,
|
|
6220
|
-
const [locating, setLocating] = (0,
|
|
6221
|
-
const [error, setError] = (0,
|
|
6222
|
-
const respond = (0,
|
|
6223
|
-
const answered = (0,
|
|
6784
|
+
const [preference, setPreference] = (0, import_react15.useState)("unset");
|
|
6785
|
+
const [locating, setLocating] = (0, import_react15.useState)(false);
|
|
6786
|
+
const [error, setError] = (0, import_react15.useState)(null);
|
|
6787
|
+
const respond = (0, import_react15.useRef)(onRespond);
|
|
6788
|
+
const answered = (0, import_react15.useRef)(null);
|
|
6224
6789
|
const requestId = request?.requestId;
|
|
6225
|
-
(0,
|
|
6790
|
+
(0, import_react15.useEffect)(() => {
|
|
6226
6791
|
respond.current = onRespond;
|
|
6227
6792
|
}, [onRespond]);
|
|
6228
|
-
(0,
|
|
6793
|
+
(0, import_react15.useEffect)(() => {
|
|
6229
6794
|
if (!requestId || preference === "unset" || answered.current === requestId || !respond.current)
|
|
6230
6795
|
return;
|
|
6231
6796
|
if (preference === "off") {
|
|
@@ -6567,6 +7132,7 @@ function ChevronDownIcon() {
|
|
|
6567
7132
|
var DEFAULT_DISCLAIMER_LABEL = "Agent can make mistakes. Check important info.";
|
|
6568
7133
|
function AgentRail({
|
|
6569
7134
|
state,
|
|
7135
|
+
handoff,
|
|
6570
7136
|
theme,
|
|
6571
7137
|
colorScheme = "auto",
|
|
6572
7138
|
brandLabel = "",
|
|
@@ -6592,44 +7158,51 @@ function AgentRail({
|
|
|
6592
7158
|
onInputResponse,
|
|
6593
7159
|
onToolInput
|
|
6594
7160
|
}) {
|
|
6595
|
-
const railRef = (0,
|
|
6596
|
-
const overlayRef = (0,
|
|
6597
|
-
const transcriptRef = (0,
|
|
6598
|
-
const responseRef = (0,
|
|
6599
|
-
const threadRef = (0,
|
|
6600
|
-
const lastScrolledVisitorIdRef = (0,
|
|
6601
|
-
const pinnedToBottomRef = (0,
|
|
6602
|
-
const smoothScrollToLatestRef = (0,
|
|
6603
|
-
const lockedTranscriptScrollTopRef = (0,
|
|
6604
|
-
const [showJumpToLatest, setShowJumpToLatest] = (0,
|
|
6605
|
-
const [receiptOpen, setReceiptOpen] = (0,
|
|
7161
|
+
const railRef = (0, import_react16.useRef)(null);
|
|
7162
|
+
const overlayRef = (0, import_react16.useRef)(null);
|
|
7163
|
+
const transcriptRef = (0, import_react16.useRef)(null);
|
|
7164
|
+
const responseRef = (0, import_react16.useRef)(null);
|
|
7165
|
+
const threadRef = (0, import_react16.useRef)(null);
|
|
7166
|
+
const lastScrolledVisitorIdRef = (0, import_react16.useRef)(void 0);
|
|
7167
|
+
const pinnedToBottomRef = (0, import_react16.useRef)(true);
|
|
7168
|
+
const smoothScrollToLatestRef = (0, import_react16.useRef)(false);
|
|
7169
|
+
const lockedTranscriptScrollTopRef = (0, import_react16.useRef)(null);
|
|
7170
|
+
const [showJumpToLatest, setShowJumpToLatest] = (0, import_react16.useState)(false);
|
|
7171
|
+
const [receiptOpen, setReceiptOpen] = (0, import_react16.useState)(false);
|
|
6606
7172
|
const resolvedBrandLabel = brandLabel.trim();
|
|
6607
7173
|
const resolvedBrandLogoUrl = brandLogoUrl?.trim();
|
|
6608
7174
|
const resolvedDisclaimerLabel = disclaimerLabel === void 0 ? DEFAULT_DISCLAIMER_LABEL : disclaimerLabel;
|
|
6609
|
-
const [failedLogoUrl, setFailedLogoUrl] = (0,
|
|
7175
|
+
const [failedLogoUrl, setFailedLogoUrl] = (0, import_react16.useState)(null);
|
|
6610
7176
|
const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
|
|
6611
7177
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
6612
7178
|
const railStyle = agentThemeStyle(theme, resolvedColorScheme);
|
|
6613
|
-
const
|
|
7179
|
+
const handoffActive = Boolean(handoff?.blocksAI);
|
|
7180
|
+
const handoffStatus = handoff?.page?.status;
|
|
7181
|
+
const locationRequest = handoffActive ? void 0 : state.pendingInputs?.find(isLocationConsentRequest);
|
|
6614
7182
|
const pendingInputRequests = (state.pendingInputs ?? []).filter(
|
|
6615
|
-
(request) => !isLocationConsentRequest(request) && shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
7183
|
+
(request) => !handoffActive && !isLocationConsentRequest(request) && shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
6616
7184
|
);
|
|
6617
7185
|
const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && (pendingInputRequests.length > 0 || Boolean(locationRequest));
|
|
6618
|
-
const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
|
|
7186
|
+
const semanticSurfaceDisabled = handoffActive || isBusy && state.phase !== "waiting-input";
|
|
6619
7187
|
const visitorToolResults = (state.toolResults ?? []).filter(
|
|
6620
7188
|
isRenderableVisitorToolResult
|
|
6621
7189
|
);
|
|
6622
7190
|
const activeVisitorToolInput = [...visitorToolResults].reverse().find((result) => result.kind === "input");
|
|
6623
7191
|
const visibleVisitorToolResults = activeVisitorToolInput ? [activeVisitorToolInput] : visitorToolResults;
|
|
6624
|
-
const activityActive = state.toolSteps.some(
|
|
7192
|
+
const activityActive = state.toolSteps.some(
|
|
7193
|
+
(step) => step.state === "active"
|
|
7194
|
+
);
|
|
6625
7195
|
const showActivity = state.toolSteps.length > 0 && pendingInputRequests.length === 0 && !state.pendingOffer && (activityActive || isBusy && !state.streamingText);
|
|
6626
|
-
const activitySteps = activityActive || !isBusy || state.streamingText ? state.toolSteps : [
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
7196
|
+
const activitySteps = activityActive || !isBusy || state.streamingText ? state.toolSteps : [
|
|
7197
|
+
...state.toolSteps,
|
|
7198
|
+
{
|
|
7199
|
+
id: "preparing-answer",
|
|
7200
|
+
kind: "tool",
|
|
7201
|
+
label: "Preparing your answer",
|
|
7202
|
+
detail: "Preparing your answer",
|
|
7203
|
+
state: "active"
|
|
7204
|
+
}
|
|
7205
|
+
];
|
|
6633
7206
|
const hasVisitorMessages2 = state.messages.some(
|
|
6634
7207
|
(message) => message.role === "visitor"
|
|
6635
7208
|
);
|
|
@@ -6658,7 +7231,7 @@ function AgentRail({
|
|
|
6658
7231
|
}
|
|
6659
7232
|
}
|
|
6660
7233
|
const lastIsAgent = lastMessage?.role === "agent";
|
|
6661
|
-
const showMessageActions = state.phase === "complete" && lastIsAgent && hasVisitorMessages2 && Boolean(hideToolCardFences(lastMessage.text).trim());
|
|
7234
|
+
const showMessageActions = !handoffActive && state.phase === "complete" && lastIsAgent && hasVisitorMessages2 && Boolean(hideToolCardFences(lastMessage.text).trim());
|
|
6662
7235
|
const streamingMessage = state.streamingText && !lastIsAgent ? {
|
|
6663
7236
|
createdAt: 0,
|
|
6664
7237
|
id: "streaming-response",
|
|
@@ -6700,7 +7273,7 @@ function AgentRail({
|
|
|
6700
7273
|
...visibleVisitorToolResults
|
|
6701
7274
|
].reverse().find((result) => result.kind !== "input")?.id;
|
|
6702
7275
|
const receiptSteps = answerReceipt && state.toolSteps.length > 0 ? state.toolSteps : void 0;
|
|
6703
|
-
(0,
|
|
7276
|
+
(0, import_react16.useEffect)(() => {
|
|
6704
7277
|
if (state.phase !== "complete") {
|
|
6705
7278
|
setReceiptOpen(false);
|
|
6706
7279
|
}
|
|
@@ -6722,7 +7295,7 @@ function AgentRail({
|
|
|
6722
7295
|
onFollowUpSelect?.(label);
|
|
6723
7296
|
}
|
|
6724
7297
|
const latestVisitorId = visibleMessages[lastVisitorIndex]?.id;
|
|
6725
|
-
(0,
|
|
7298
|
+
(0, import_react16.useEffect)(() => {
|
|
6726
7299
|
const node = transcriptRef.current;
|
|
6727
7300
|
if (!node) return;
|
|
6728
7301
|
if (latestVisitorId !== lastScrolledVisitorIdRef.current) {
|
|
@@ -6752,7 +7325,7 @@ function AgentRail({
|
|
|
6752
7325
|
state.followUps,
|
|
6753
7326
|
state.journey
|
|
6754
7327
|
]);
|
|
6755
|
-
(0,
|
|
7328
|
+
(0, import_react16.useEffect)(() => {
|
|
6756
7329
|
const node = transcriptRef.current;
|
|
6757
7330
|
if (!node) return;
|
|
6758
7331
|
const handleScroll = () => {
|
|
@@ -6766,7 +7339,7 @@ function AgentRail({
|
|
|
6766
7339
|
handleScroll();
|
|
6767
7340
|
return () => node.removeEventListener("scroll", handleScroll);
|
|
6768
7341
|
}, []);
|
|
6769
|
-
(0,
|
|
7342
|
+
(0, import_react16.useEffect)(() => {
|
|
6770
7343
|
if (!receiptOpen) {
|
|
6771
7344
|
lockedTranscriptScrollTopRef.current = null;
|
|
6772
7345
|
return;
|
|
@@ -6818,249 +7391,302 @@ function AgentRail({
|
|
|
6818
7391
|
autoFocus: mobileFullscreen || expanded,
|
|
6819
7392
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
6820
7393
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
6821
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
7394
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(AgentRailOverlayContext.Provider, { value: { railRef, overlayRef }, children: [
|
|
7395
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "agent-rail__surface", inert: receiptOpen || void 0, children: [
|
|
7396
|
+
/* @__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: [
|
|
7397
|
+
onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7398
|
+
"button",
|
|
7399
|
+
{
|
|
7400
|
+
type: "button",
|
|
7401
|
+
className: "agent-rail__collapse",
|
|
7402
|
+
"aria-label": "Collapse assist",
|
|
7403
|
+
onClick: onCollapse,
|
|
7404
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MinimizeIcon, {})
|
|
7405
|
+
}
|
|
7406
|
+
) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7407
|
+
"button",
|
|
7408
|
+
{
|
|
7409
|
+
type: "button",
|
|
7410
|
+
className: "agent-rail__close",
|
|
7411
|
+
"aria-label": "Close agent",
|
|
7412
|
+
onClick: onClose,
|
|
7413
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CloseIcon2, {})
|
|
7414
|
+
}
|
|
7415
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
|
|
7416
|
+
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "agent-rail__identity", children: [
|
|
7417
|
+
showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7418
|
+
"img",
|
|
7419
|
+
{
|
|
7420
|
+
className: "agent-rail__brand-logo",
|
|
7421
|
+
src: resolvedBrandLogoUrl,
|
|
7422
|
+
alt: "",
|
|
7423
|
+
onError: () => {
|
|
7424
|
+
setFailedLogoUrl(resolvedBrandLogoUrl ?? null);
|
|
6845
7425
|
}
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
7426
|
+
}
|
|
7427
|
+
) }) : null,
|
|
7428
|
+
resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
|
|
7429
|
+
] }) : null,
|
|
7430
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "agent-rail__actions", children: [
|
|
7431
|
+
onReset ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7432
|
+
"button",
|
|
7433
|
+
{
|
|
7434
|
+
type: "button",
|
|
7435
|
+
className: "agent-rail__new-chat",
|
|
7436
|
+
"aria-label": "Start a new conversation",
|
|
7437
|
+
disabled: !hasVisitorMessages2 || handoffActive && !handoff?.canReset,
|
|
7438
|
+
onClick: handleReset,
|
|
7439
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NewChatIcon, {})
|
|
7440
|
+
}
|
|
7441
|
+
) : null,
|
|
7442
|
+
onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7443
|
+
"button",
|
|
7444
|
+
{
|
|
7445
|
+
type: "button",
|
|
7446
|
+
className: "agent-rail__expand",
|
|
7447
|
+
"aria-label": expanded ? "Exit full screen" : "Open full screen",
|
|
7448
|
+
onClick: onExpandToggle,
|
|
7449
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ExpandIcon, {})
|
|
7450
|
+
}
|
|
7451
|
+
) : null
|
|
7452
|
+
] })
|
|
7453
|
+
] }) }),
|
|
7454
|
+
/* @__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: [
|
|
7455
|
+
!hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
7456
|
+
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7457
|
+
MessageBubble,
|
|
7458
|
+
{
|
|
7459
|
+
showProvenance: Boolean(
|
|
7460
|
+
handoff?.page?.enabled || handoffActive
|
|
7461
|
+
),
|
|
7462
|
+
message: greeting,
|
|
7463
|
+
bookingDisabled: isBusy || handoffActive,
|
|
7464
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7465
|
+
onBook
|
|
7466
|
+
}
|
|
7467
|
+
) : null,
|
|
7468
|
+
showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7469
|
+
FollowUpChips,
|
|
7470
|
+
{
|
|
7471
|
+
suggestions: state.followUps,
|
|
7472
|
+
disabled: isBusy || handoffActive,
|
|
7473
|
+
label: "Start here",
|
|
7474
|
+
onSelect: (suggestion) => handleFollowUpSelect(suggestion.label)
|
|
7475
|
+
}
|
|
7476
|
+
) }) : null,
|
|
7477
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7478
|
+
AgentActivityBubble,
|
|
7479
|
+
{
|
|
7480
|
+
brandLabel: resolvedBrandLabel,
|
|
7481
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7482
|
+
failed: state.phase === "error",
|
|
7483
|
+
steps: activitySteps
|
|
7484
|
+
}
|
|
7485
|
+
) : null,
|
|
7486
|
+
visibleVisitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7487
|
+
VisitorToolResultView,
|
|
7488
|
+
{
|
|
7489
|
+
result,
|
|
7490
|
+
disabled: semanticSurfaceDisabled,
|
|
7491
|
+
onToolInput
|
|
7492
|
+
},
|
|
7493
|
+
result.id
|
|
7494
|
+
)),
|
|
7495
|
+
pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7496
|
+
HumanInputCard,
|
|
7497
|
+
{
|
|
7498
|
+
request,
|
|
7499
|
+
onRespond: onInputResponse
|
|
7500
|
+
},
|
|
7501
|
+
request.requestId
|
|
7502
|
+
))
|
|
7503
|
+
] }) : null,
|
|
7504
|
+
transcriptMessages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
7505
|
+
"div",
|
|
7506
|
+
{
|
|
7507
|
+
ref: index === lastVisitorIndex + 1 ? responseRef : void 0,
|
|
7508
|
+
className: "agent-rail__turn-block",
|
|
7509
|
+
children: [
|
|
7510
|
+
message.role === "agent" ? message.toolResults?.map((result) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7511
|
+
VisitorToolResultView,
|
|
6875
7512
|
{
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
}
|
|
6882
|
-
) : null
|
|
6883
|
-
] })
|
|
6884
|
-
] }) }),
|
|
6885
|
-
/* @__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: [
|
|
6886
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
6887
|
-
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7513
|
+
result
|
|
7514
|
+
},
|
|
7515
|
+
result.id
|
|
7516
|
+
)) : null,
|
|
7517
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6888
7518
|
MessageBubble,
|
|
6889
7519
|
{
|
|
6890
|
-
|
|
6891
|
-
|
|
7520
|
+
showProvenance: Boolean(
|
|
7521
|
+
handoff?.page?.enabled || handoffActive
|
|
7522
|
+
),
|
|
7523
|
+
message,
|
|
7524
|
+
bookingDisabled: isBusy || handoffActive,
|
|
6892
7525
|
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7526
|
+
offer: index === transcriptMessages.length - 1 ? state.pendingOffer : void 0,
|
|
6893
7527
|
onBook
|
|
6894
7528
|
}
|
|
6895
|
-
)
|
|
6896
|
-
|
|
6897
|
-
|
|
7529
|
+
),
|
|
7530
|
+
index === lastAgentIndex && showMessageActions && message.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7531
|
+
MessageActions,
|
|
6898
7532
|
{
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
AgentActivityBubble,
|
|
6907
|
-
{
|
|
6908
|
-
brandLabel: resolvedBrandLabel,
|
|
6909
|
-
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
6910
|
-
failed: state.phase === "error",
|
|
6911
|
-
steps: activitySteps
|
|
7533
|
+
answeredAt: message.createdAt,
|
|
7534
|
+
copyText: hideToolCardFences(message.text).trim() || message.text,
|
|
7535
|
+
readAloud,
|
|
7536
|
+
receiptSteps,
|
|
7537
|
+
onOpenReceipt: receiptSteps ? openReceipt : void 0,
|
|
7538
|
+
onRegenerate: onRegenerate ? handleRegenerate : void 0,
|
|
7539
|
+
onFeedback: onFeedback ? (rating) => onFeedback(rating, message) : void 0
|
|
6912
7540
|
}
|
|
6913
7541
|
) : null,
|
|
6914
|
-
|
|
6915
|
-
|
|
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
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { children: state.error })
|
|
6998
|
-
] }),
|
|
6999
|
-
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
7000
|
-
] }) : null
|
|
7001
|
-
] }) }),
|
|
7002
|
-
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)(
|
|
7003
|
-
"a",
|
|
7542
|
+
index === lastVisitorIndex ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
|
|
7543
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7544
|
+
AgentActivityBubble,
|
|
7545
|
+
{
|
|
7546
|
+
brandLabel: resolvedBrandLabel,
|
|
7547
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
7548
|
+
failed: state.phase === "error",
|
|
7549
|
+
steps: activitySteps
|
|
7550
|
+
}
|
|
7551
|
+
) : null,
|
|
7552
|
+
visibleVisitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7553
|
+
VisitorToolResultView,
|
|
7554
|
+
{
|
|
7555
|
+
result,
|
|
7556
|
+
disabled: semanticSurfaceDisabled,
|
|
7557
|
+
onToolInput
|
|
7558
|
+
},
|
|
7559
|
+
result.id
|
|
7560
|
+
)),
|
|
7561
|
+
pendingInputRequests.slice(0, 1).map((request) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7562
|
+
HumanInputCard,
|
|
7563
|
+
{
|
|
7564
|
+
request,
|
|
7565
|
+
onRespond: onInputResponse
|
|
7566
|
+
},
|
|
7567
|
+
request.requestId
|
|
7568
|
+
))
|
|
7569
|
+
] }) : null
|
|
7570
|
+
]
|
|
7571
|
+
},
|
|
7572
|
+
message.role === "agent" && transcriptMessages[index - 1]?.role === "visitor" ? `answer-${transcriptMessages[index - 1]?.id}` : message.id
|
|
7573
|
+
)),
|
|
7574
|
+
waitingForBooking ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(BookingCardLoader, {}) : null,
|
|
7575
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
|
|
7576
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
7577
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("strong", { children: "Something went wrong" }),
|
|
7578
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { children: state.error })
|
|
7579
|
+
] }),
|
|
7580
|
+
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
7581
|
+
] }) : null
|
|
7582
|
+
] }) }),
|
|
7583
|
+
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)(
|
|
7584
|
+
"a",
|
|
7585
|
+
{
|
|
7586
|
+
href: disclaimerLink,
|
|
7587
|
+
rel: "noopener noreferrer",
|
|
7588
|
+
target: "_blank",
|
|
7589
|
+
children: resolvedDisclaimerLabel
|
|
7590
|
+
}
|
|
7591
|
+
) : resolvedDisclaimerLabel }) }) : null,
|
|
7592
|
+
handoff && (handoff.page?.enabled || handoffActive) ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "agent-rail__handoff", children: [
|
|
7593
|
+
/* @__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?" }),
|
|
7594
|
+
handoffStatus === "ai" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7595
|
+
"button",
|
|
7596
|
+
{
|
|
7597
|
+
type: "button",
|
|
7598
|
+
disabled: handoff.busy || handoff.hasPending || isBusy,
|
|
7599
|
+
onClick: () => void handoff.start(),
|
|
7600
|
+
children: "Talk to a person"
|
|
7601
|
+
}
|
|
7602
|
+
) : null,
|
|
7603
|
+
handoffStatus === "resolved" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7604
|
+
"button",
|
|
7605
|
+
{
|
|
7606
|
+
type: "button",
|
|
7607
|
+
disabled: handoff.busy || handoff.hasPending,
|
|
7608
|
+
onClick: () => void handoff.returnToAI(),
|
|
7609
|
+
children: "Return to AI"
|
|
7610
|
+
}
|
|
7611
|
+
) : null,
|
|
7612
|
+
handoffStatus === "failed" && onReset ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7613
|
+
"button",
|
|
7614
|
+
{
|
|
7615
|
+
type: "button",
|
|
7616
|
+
disabled: !handoff.canReset,
|
|
7617
|
+
onClick: handleReset,
|
|
7618
|
+
children: "Start a new conversation"
|
|
7619
|
+
}
|
|
7620
|
+
) : null,
|
|
7621
|
+
handoff.error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { role: "alert", children: [
|
|
7622
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: handoff.error }),
|
|
7623
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7624
|
+
"button",
|
|
7004
7625
|
{
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
children:
|
|
7626
|
+
type: "button",
|
|
7627
|
+
disabled: handoff.busy,
|
|
7628
|
+
onClick: () => void handoff.retry(),
|
|
7629
|
+
children: "Retry"
|
|
7009
7630
|
}
|
|
7010
|
-
)
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
className: "agent-rail__jump-to-latest",
|
|
7025
|
-
"aria-label": "Jump to the latest message",
|
|
7026
|
-
title: "Jump to the latest message",
|
|
7027
|
-
onClick: scrollToLatest,
|
|
7028
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronDownIcon, {})
|
|
7029
|
-
}
|
|
7030
|
-
) : null,
|
|
7031
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7032
|
-
Composer,
|
|
7033
|
-
{
|
|
7034
|
-
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
7035
|
-
disabled: isBusy,
|
|
7036
|
-
form: composerForm && lastMessage ? { ...composerForm, id: `${lastMessage.id}:${composerForm.id}` } : null,
|
|
7037
|
-
allowFormResume: !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
|
|
7038
|
-
placeholder: composerPlaceholder,
|
|
7039
|
-
onSubmit: handleSubmit
|
|
7040
|
-
},
|
|
7041
|
-
`${state.messages.find((message) => message.role === "visitor")?.id ?? "empty"}:${latestResultId ?? ""}`
|
|
7042
|
-
),
|
|
7043
|
-
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
|
|
7044
|
-
] })
|
|
7045
|
-
] }),
|
|
7046
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: overlayRef, className: "agent-rail__overlay" }),
|
|
7047
|
-
receiptOpen && receiptSteps ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7048
|
-
AnswerReceiptDialog,
|
|
7631
|
+
)
|
|
7632
|
+
] }) : null
|
|
7633
|
+
] }) : null,
|
|
7634
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
|
|
7635
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7636
|
+
LocationConsent,
|
|
7637
|
+
{
|
|
7638
|
+
request: locationRequest,
|
|
7639
|
+
onRespond: onInputResponse
|
|
7640
|
+
},
|
|
7641
|
+
state.messages.find((message) => message.role === "visitor")?.id ?? "empty"
|
|
7642
|
+
),
|
|
7643
|
+
showJumpToLatest ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7644
|
+
"button",
|
|
7049
7645
|
{
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7646
|
+
type: "button",
|
|
7647
|
+
className: "agent-rail__jump-to-latest",
|
|
7648
|
+
"aria-label": "Jump to the latest message",
|
|
7649
|
+
title: "Jump to the latest message",
|
|
7650
|
+
onClick: scrollToLatest,
|
|
7651
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronDownIcon, {})
|
|
7053
7652
|
}
|
|
7054
|
-
) : null
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7653
|
+
) : null,
|
|
7654
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7655
|
+
Composer,
|
|
7656
|
+
{
|
|
7657
|
+
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
7658
|
+
disabled: isBusy || Boolean(handoff?.busy || handoff?.hasPending) || handoffActive && !["requesting", "queued", "human"].includes(
|
|
7659
|
+
handoffStatus ?? ""
|
|
7660
|
+
),
|
|
7661
|
+
form: !handoffActive && composerForm && lastMessage ? {
|
|
7662
|
+
...composerForm,
|
|
7663
|
+
id: `${lastMessage.id}:${composerForm.id}`
|
|
7664
|
+
} : null,
|
|
7665
|
+
allowFormResume: !handoffActive && !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
|
|
7666
|
+
placeholder: handoffActive ? "Message support\u2026" : composerPlaceholder,
|
|
7667
|
+
onSubmit: handleSubmit
|
|
7668
|
+
},
|
|
7669
|
+
`${state.messages.find((message) => message.role === "visitor")?.id ?? "empty"}:${latestResultId ?? ""}`
|
|
7670
|
+
),
|
|
7671
|
+
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
|
|
7672
|
+
] })
|
|
7673
|
+
] }),
|
|
7674
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: overlayRef, className: "agent-rail__overlay" }),
|
|
7675
|
+
receiptOpen && receiptSteps ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7676
|
+
AnswerReceiptDialog,
|
|
7677
|
+
{
|
|
7678
|
+
brandLabel: resolvedBrandLabel,
|
|
7679
|
+
onClose: () => setReceiptOpen(false),
|
|
7680
|
+
steps: receiptSteps
|
|
7681
|
+
}
|
|
7682
|
+
) : null
|
|
7683
|
+
] })
|
|
7058
7684
|
}
|
|
7059
7685
|
);
|
|
7060
7686
|
}
|
|
7061
7687
|
|
|
7062
7688
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
7063
|
-
var
|
|
7689
|
+
var import_react17 = require("react");
|
|
7064
7690
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
7065
7691
|
function ChatSparkIcon() {
|
|
7066
7692
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
@@ -7141,8 +7767,8 @@ function TabMarkIcon({
|
|
|
7141
7767
|
}) {
|
|
7142
7768
|
const custom = customIconUrl?.trim();
|
|
7143
7769
|
const logo = logoUrl?.trim();
|
|
7144
|
-
const [customFailed, setCustomFailed] = (0,
|
|
7145
|
-
const [logoFailed, setLogoFailed] = (0,
|
|
7770
|
+
const [customFailed, setCustomFailed] = (0, import_react17.useState)(false);
|
|
7771
|
+
const [logoFailed, setLogoFailed] = (0, import_react17.useState)(false);
|
|
7146
7772
|
if (custom && !customFailed) {
|
|
7147
7773
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
7148
7774
|
"img",
|
|
@@ -7330,8 +7956,8 @@ function findPrecedingVisitorText(messages, agentMessageId) {
|
|
|
7330
7956
|
}
|
|
7331
7957
|
return void 0;
|
|
7332
7958
|
}
|
|
7333
|
-
function normalizeAgentFeedbackAnswerText(
|
|
7334
|
-
const normalized =
|
|
7959
|
+
function normalizeAgentFeedbackAnswerText(text3) {
|
|
7960
|
+
const normalized = text3.replace(/\s+/g, " ").trim();
|
|
7335
7961
|
if (!normalized) return void 0;
|
|
7336
7962
|
return normalized.length > 4e3 ? `${normalized.slice(0, 3997)}...` : normalized;
|
|
7337
7963
|
}
|
|
@@ -7391,9 +8017,9 @@ function AgentWidget({
|
|
|
7391
8017
|
}) {
|
|
7392
8018
|
const isMobile = useIsMobile();
|
|
7393
8019
|
const placement = normalizeAgentPlacement(placementInput);
|
|
7394
|
-
const railSlotRef = (0,
|
|
7395
|
-
const [railCollapsed, setRailCollapsed] = (0,
|
|
7396
|
-
const [railExpanded, setRailExpanded] = (0,
|
|
8020
|
+
const railSlotRef = (0, import_react18.useRef)(null);
|
|
8021
|
+
const [railCollapsed, setRailCollapsed] = (0, import_react18.useState)(defaultCollapsed);
|
|
8022
|
+
const [railExpanded, setRailExpanded] = (0, import_react18.useState)(false);
|
|
7397
8023
|
const pageShiftActive = shouldApplyPageShift({
|
|
7398
8024
|
pageShift,
|
|
7399
8025
|
isMobile,
|
|
@@ -7446,7 +8072,7 @@ function AgentWidget({
|
|
|
7446
8072
|
} : {},
|
|
7447
8073
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
7448
8074
|
};
|
|
7449
|
-
(0,
|
|
8075
|
+
(0, import_react18.useEffect)(() => {
|
|
7450
8076
|
if (!registerPanelController) return;
|
|
7451
8077
|
registerAgentPanelController(customerId, {
|
|
7452
8078
|
open: () => setRailCollapsed(false),
|
|
@@ -7481,7 +8107,7 @@ function AgentWidget({
|
|
|
7481
8107
|
})
|
|
7482
8108
|
);
|
|
7483
8109
|
}
|
|
7484
|
-
(0,
|
|
8110
|
+
(0, import_react18.useEffect)(() => {
|
|
7485
8111
|
if (railCollapsed) return;
|
|
7486
8112
|
const handleKeyDown = (event) => {
|
|
7487
8113
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -7591,7 +8217,7 @@ function AgentWidget({
|
|
|
7591
8217
|
}
|
|
7592
8218
|
|
|
7593
8219
|
// src/react/components/AgentTranscript/AgentTranscript.tsx
|
|
7594
|
-
var
|
|
8220
|
+
var import_react19 = require("react");
|
|
7595
8221
|
|
|
7596
8222
|
// src/react/components/AgentTranscript/TranscriptActivity.tsx
|
|
7597
8223
|
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
@@ -7659,9 +8285,9 @@ function groupTranscriptActivities(activities) {
|
|
|
7659
8285
|
label: activity.createdAt < previous.startedAt ? activity.label : previous.label
|
|
7660
8286
|
});
|
|
7661
8287
|
}
|
|
7662
|
-
return [...calls.values()].map(({ latest, id, startedAt, label }) => ({
|
|
8288
|
+
return [...calls.values()].map(({ latest, id: id2, startedAt, label }) => ({
|
|
7663
8289
|
...latest,
|
|
7664
|
-
id,
|
|
8290
|
+
id: id2,
|
|
7665
8291
|
label,
|
|
7666
8292
|
createdAt: startedAt
|
|
7667
8293
|
}));
|
|
@@ -7712,7 +8338,7 @@ function AgentTranscript({
|
|
|
7712
8338
|
const previous = entries[index - 1];
|
|
7713
8339
|
const validTimestamp = Number.isFinite(date.getTime());
|
|
7714
8340
|
const showTimestamp = validTimestamp && (previous ? entry.createdAt - previous.createdAt >= 5 * 60 * 1e3 || date.toISOString().slice(0, 10) !== new Date(previous.createdAt).toJSON()?.slice(0, 10) : showInitialTimestamp);
|
|
7715
|
-
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
8341
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react19.Fragment, { children: [
|
|
7716
8342
|
showTimestamp ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { className: "agent-transcript__time-marker", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("time", { dateTime: date.toISOString(), children: formatTimestamp(entry.createdAt) }) }) : null,
|
|
7717
8343
|
entry.kind === "activity" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TranscriptActivity, { activity: entry }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
7718
8344
|
"li",
|