@webless/agent 0.6.3 → 0.6.5
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-HXKD46I6.js → chunk-U2TIUZJM.js} +2540 -666
- package/dist/chunk-U2TIUZJM.js.map +1 -0
- package/dist/embed.cjs +2276 -407
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +749 -112
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +9 -3
- package/dist/embed.d.ts +9 -3
- package/dist/embed.js +6 -2
- package/dist/embed.js.map +1 -1
- package/dist/index.cjs +431 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -1
- package/dist/index.d.ts +102 -1
- package/dist/index.js +428 -23
- package/dist/index.js.map +1 -1
- package/dist/manifest-Cad-TQJw.d.cts +391 -0
- package/dist/manifest-Cad-TQJw.d.ts +391 -0
- package/dist/react.cjs +2263 -406
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +749 -112
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +13 -7
- package/dist/react.d.ts +13 -7
- package/dist/react.js +5 -1
- package/package.json +1 -1
- package/dist/chunk-HXKD46I6.js.map +0 -1
- package/dist/manifest-H6oHn8Pc.d.cts +0 -185
- package/dist/manifest-H6oHn8Pc.d.ts +0 -185
package/dist/embed.cjs
CHANGED
|
@@ -27,6 +27,8 @@ __export(embed_exports, {
|
|
|
27
27
|
mountAgent: () => mountAgent,
|
|
28
28
|
normalizeAgentPlacement: () => normalizeAgentPlacement,
|
|
29
29
|
normalizeAgentTagManifest: () => normalizeAgentTagManifest,
|
|
30
|
+
resetAgentPanel: () => resetAgentPanel,
|
|
31
|
+
submitAgentPanel: () => submitAgentPanel,
|
|
30
32
|
unmountAgent: () => unmountAgent
|
|
31
33
|
});
|
|
32
34
|
module.exports = __toCommonJS(embed_exports);
|
|
@@ -48,9 +50,21 @@ function openAgentPanel(customerId) {
|
|
|
48
50
|
function closeAgentPanel(customerId) {
|
|
49
51
|
controllers.get(customerId)?.close();
|
|
50
52
|
}
|
|
53
|
+
function resetAgentPanel(customerId) {
|
|
54
|
+
controllers.get(customerId)?.reset();
|
|
55
|
+
}
|
|
56
|
+
function submitAgentPanel(customerId, message, options) {
|
|
57
|
+
const controller = controllers.get(customerId);
|
|
58
|
+
if (!controller) {
|
|
59
|
+
return Promise.reject(
|
|
60
|
+
new Error(`Agent panel "${customerId}" is not mounted.`)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return controller.submit(message, options);
|
|
64
|
+
}
|
|
51
65
|
|
|
52
66
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
53
|
-
var
|
|
67
|
+
var import_react11 = require("react");
|
|
54
68
|
|
|
55
69
|
// src/react/page-shift.ts
|
|
56
70
|
var import_react = require("react");
|
|
@@ -141,6 +155,26 @@ var import_client2 = require("eve/client");
|
|
|
141
155
|
// src/runtime/capability.ts
|
|
142
156
|
var import_client = require("eve/client");
|
|
143
157
|
var MAX_REFRESH_SKEW_MS = 3e4;
|
|
158
|
+
var LOCAL_LOOPBACK_ORIGINS = [
|
|
159
|
+
"http://127.0.0.1:3010",
|
|
160
|
+
"http://127.0.0.1:3001"
|
|
161
|
+
];
|
|
162
|
+
function isLoopbackRuntimeOrigin(origin) {
|
|
163
|
+
try {
|
|
164
|
+
const host = new URL(origin).hostname;
|
|
165
|
+
return host === "127.0.0.1" || host === "localhost";
|
|
166
|
+
} catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function localBootstrapOrigins(origin) {
|
|
171
|
+
const normalized = origin.replace(/\/$/, "");
|
|
172
|
+
if (!isLoopbackRuntimeOrigin(normalized)) return [normalized];
|
|
173
|
+
return [
|
|
174
|
+
normalized,
|
|
175
|
+
...LOCAL_LOOPBACK_ORIGINS.filter((candidate) => candidate !== normalized)
|
|
176
|
+
];
|
|
177
|
+
}
|
|
144
178
|
function isRecord(value) {
|
|
145
179
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
146
180
|
}
|
|
@@ -187,20 +221,31 @@ function createAgentRuntimeCapability(options) {
|
|
|
187
221
|
);
|
|
188
222
|
}
|
|
189
223
|
}
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
224
|
+
const bootstrapBody = JSON.stringify({
|
|
225
|
+
clientSessionId: options.visitorSessionId,
|
|
226
|
+
indexId: options.indexId,
|
|
227
|
+
...previewBuildId ? { previewBuildId } : {},
|
|
228
|
+
...previewGrant ? { previewGrant } : {},
|
|
229
|
+
version: options.version
|
|
230
|
+
});
|
|
231
|
+
const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
|
|
232
|
+
body: bootstrapBody,
|
|
233
|
+
headers: { "content-type": "application/json" },
|
|
234
|
+
method: "POST"
|
|
235
|
+
});
|
|
236
|
+
let response;
|
|
237
|
+
let lastError;
|
|
238
|
+
for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
|
|
239
|
+
try {
|
|
240
|
+
response = await postBootstrap(origin);
|
|
241
|
+
break;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
lastError = error;
|
|
202
244
|
}
|
|
203
|
-
|
|
245
|
+
}
|
|
246
|
+
if (!response) {
|
|
247
|
+
throw lastError instanceof Error ? lastError : new Error("Agent Runtime is unavailable.");
|
|
248
|
+
}
|
|
204
249
|
if (!response.ok) {
|
|
205
250
|
throw new Error(await readBootstrapError(response));
|
|
206
251
|
}
|
|
@@ -362,6 +407,226 @@ function clearPersistedAgentSession(visitorSessionId, options) {
|
|
|
362
407
|
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
363
408
|
}
|
|
364
409
|
|
|
410
|
+
// src/runtime/tool-ui.ts
|
|
411
|
+
var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
|
|
412
|
+
function isRecord2(value) {
|
|
413
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
414
|
+
}
|
|
415
|
+
function isJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
416
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
420
|
+
if (typeof value !== "object") return false;
|
|
421
|
+
if (seen.has(value)) return false;
|
|
422
|
+
seen.add(value);
|
|
423
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, seen)) : Object.entries(value).every(
|
|
424
|
+
([key, item]) => typeof key === "string" && isJsonValue(item, seen)
|
|
425
|
+
);
|
|
426
|
+
seen.delete(value);
|
|
427
|
+
return valid;
|
|
428
|
+
}
|
|
429
|
+
function boundedString(value, max) {
|
|
430
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > max) {
|
|
431
|
+
return void 0;
|
|
432
|
+
}
|
|
433
|
+
return value.trim();
|
|
434
|
+
}
|
|
435
|
+
function numberValue(value) {
|
|
436
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
437
|
+
}
|
|
438
|
+
function integerValue(value) {
|
|
439
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8e3 ? value : void 0;
|
|
440
|
+
}
|
|
441
|
+
function isFieldKind(value) {
|
|
442
|
+
return typeof value === "string" && [
|
|
443
|
+
"text",
|
|
444
|
+
"textarea",
|
|
445
|
+
"email",
|
|
446
|
+
"number",
|
|
447
|
+
"select",
|
|
448
|
+
"multi-select",
|
|
449
|
+
"checkbox",
|
|
450
|
+
"confirmation",
|
|
451
|
+
"radio",
|
|
452
|
+
"date",
|
|
453
|
+
"time",
|
|
454
|
+
"date-time",
|
|
455
|
+
"calendar",
|
|
456
|
+
"range",
|
|
457
|
+
"json"
|
|
458
|
+
].includes(value);
|
|
459
|
+
}
|
|
460
|
+
function parseField(value) {
|
|
461
|
+
if (!isRecord2(value)) return null;
|
|
462
|
+
if (!hasOnlyKeys(value, [
|
|
463
|
+
"description",
|
|
464
|
+
"kind",
|
|
465
|
+
"label",
|
|
466
|
+
"max",
|
|
467
|
+
"maxItems",
|
|
468
|
+
"maxLength",
|
|
469
|
+
"min",
|
|
470
|
+
"minLength",
|
|
471
|
+
"options",
|
|
472
|
+
"path",
|
|
473
|
+
"placeholder",
|
|
474
|
+
"required",
|
|
475
|
+
"step",
|
|
476
|
+
"defaultValue"
|
|
477
|
+
])) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
if (!isFieldKind(value.kind)) return null;
|
|
481
|
+
const path = boundedString(value.path, 160);
|
|
482
|
+
const label = boundedString(value.label, 160);
|
|
483
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
484
|
+
const placeholder = value.placeholder === void 0 || typeof value.placeholder !== "string" ? void 0 : value.placeholder;
|
|
485
|
+
const required = value.required === void 0 || typeof value.required !== "boolean" ? void 0 : value.required;
|
|
486
|
+
const min = value.min === void 0 ? void 0 : numberValue(value.min);
|
|
487
|
+
const max = value.max === void 0 ? void 0 : numberValue(value.max);
|
|
488
|
+
const maxItems = value.maxItems === void 0 ? void 0 : integerValue(value.maxItems);
|
|
489
|
+
const step = value.step === void 0 ? void 0 : numberValue(value.step);
|
|
490
|
+
const minLength = value.minLength === void 0 ? void 0 : integerValue(value.minLength);
|
|
491
|
+
const maxLength = value.maxLength === void 0 ? void 0 : integerValue(value.maxLength);
|
|
492
|
+
const defaultValue = value.defaultValue === void 0 || !isJsonValue(value.defaultValue) ? void 0 : value.defaultValue;
|
|
493
|
+
if (!path || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || !label) {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
if (value.description !== void 0 && !description) return null;
|
|
497
|
+
if (value.placeholder !== void 0 && placeholder === void 0) return null;
|
|
498
|
+
if (value.required !== void 0 && required === void 0) return null;
|
|
499
|
+
if (value.min !== void 0 && min === void 0) return null;
|
|
500
|
+
if (value.max !== void 0 && max === void 0) return null;
|
|
501
|
+
if (value.maxItems !== void 0 && (maxItems === void 0 || maxItems < 1 || maxItems > 100))
|
|
502
|
+
return null;
|
|
503
|
+
if (value.step !== void 0 && step === void 0) return null;
|
|
504
|
+
if (value.minLength !== void 0 && minLength === void 0) return null;
|
|
505
|
+
if (value.maxLength !== void 0 && maxLength === void 0) return null;
|
|
506
|
+
if (value.defaultValue !== void 0 && defaultValue === void 0)
|
|
507
|
+
return null;
|
|
508
|
+
if (value.options !== void 0) {
|
|
509
|
+
if (!Array.isArray(value.options) || value.options.length > 100)
|
|
510
|
+
return null;
|
|
511
|
+
for (const option of value.options) {
|
|
512
|
+
if (!isRecord2(option) || !boundedString(option.label, 160) || !isJsonValue(option.value)) {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return {
|
|
518
|
+
kind: value.kind,
|
|
519
|
+
path,
|
|
520
|
+
label,
|
|
521
|
+
...description ? { description } : {},
|
|
522
|
+
...placeholder !== void 0 ? { placeholder } : {},
|
|
523
|
+
...required !== void 0 ? { required } : {},
|
|
524
|
+
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
525
|
+
...value.options !== void 0 ? { options: value.options } : {},
|
|
526
|
+
...min !== void 0 ? { min } : {},
|
|
527
|
+
...max !== void 0 ? { max } : {},
|
|
528
|
+
...maxItems !== void 0 ? { maxItems } : {},
|
|
529
|
+
...step !== void 0 ? { step } : {},
|
|
530
|
+
...minLength !== void 0 ? { minLength } : {},
|
|
531
|
+
...maxLength !== void 0 ? { maxLength } : {}
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function parseStep(value) {
|
|
535
|
+
if (!isRecord2(value)) return null;
|
|
536
|
+
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
const id = boundedString(value.id, 80);
|
|
540
|
+
const label = boundedString(value.label, 160);
|
|
541
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
542
|
+
if (!id || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(id) || !label || value.description !== void 0 && !description || !Array.isArray(value.fieldPaths) || value.fieldPaths.length < 1 || value.fieldPaths.length > 32 || value.fieldPaths.some(
|
|
543
|
+
(path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
|
|
544
|
+
)) {
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
return {
|
|
548
|
+
id,
|
|
549
|
+
label,
|
|
550
|
+
fieldPaths: value.fieldPaths,
|
|
551
|
+
...description ? { description } : {}
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
function parseAction(value) {
|
|
555
|
+
if (!isRecord2(value)) return null;
|
|
556
|
+
if (!hasOnlyKeys(value, ["id", "label"])) return null;
|
|
557
|
+
const label = boundedString(value.label, 80);
|
|
558
|
+
if (value.id !== "submit" && value.id !== "back" && value.id !== "next" && value.id !== "reset" || !label) {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
id: value.id,
|
|
563
|
+
label
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
function parseAgentToolUiSurface(value) {
|
|
567
|
+
if (!isRecord2(value) || value.schemaVersion !== AGENT_TOOL_UI_SCHEMA_VERSION)
|
|
568
|
+
return null;
|
|
569
|
+
if (!hasOnlyKeys(value, [
|
|
570
|
+
"actions",
|
|
571
|
+
"description",
|
|
572
|
+
"fields",
|
|
573
|
+
"id",
|
|
574
|
+
"operationId",
|
|
575
|
+
"requestId",
|
|
576
|
+
"schemaVersion",
|
|
577
|
+
"steps",
|
|
578
|
+
"submitLabel",
|
|
579
|
+
"title",
|
|
580
|
+
"toolSlug",
|
|
581
|
+
"values"
|
|
582
|
+
])) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
const id = boundedString(value.id, 200);
|
|
586
|
+
const title = boundedString(value.title, 200);
|
|
587
|
+
const toolSlug = boundedString(value.toolSlug, 200);
|
|
588
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
589
|
+
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
590
|
+
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
591
|
+
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
592
|
+
if (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
const fields = value.fields.map(parseField);
|
|
596
|
+
if (fields.some((field) => field === null)) return null;
|
|
597
|
+
const steps = value.steps === void 0 ? void 0 : Array.isArray(value.steps) && value.steps.length <= 8 ? value.steps.map(parseStep) : null;
|
|
598
|
+
if (steps?.some((step) => step === null)) return null;
|
|
599
|
+
const actions = value.actions === void 0 ? void 0 : Array.isArray(value.actions) && value.actions.length <= 8 ? value.actions.map(parseAction) : null;
|
|
600
|
+
if (actions?.some((action) => action === null)) return null;
|
|
601
|
+
if (value.description !== void 0 && !description) return null;
|
|
602
|
+
if (value.operationId !== void 0 && !operationId) return null;
|
|
603
|
+
if (value.requestId !== void 0 && !requestId) return null;
|
|
604
|
+
if (value.submitLabel !== void 0 && !submitLabel) return null;
|
|
605
|
+
const values = value.values !== void 0 && isRecord2(value.values) ? value.values : void 0;
|
|
606
|
+
if (value.values !== void 0) {
|
|
607
|
+
if (!values || !Object.values(values).every((item) => isJsonValue(item)))
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
return {
|
|
611
|
+
schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
612
|
+
id,
|
|
613
|
+
title,
|
|
614
|
+
toolSlug,
|
|
615
|
+
fields,
|
|
616
|
+
...actions ? { actions } : {},
|
|
617
|
+
...description ? { description } : {},
|
|
618
|
+
...operationId ? { operationId } : {},
|
|
619
|
+
...requestId ? { requestId } : {},
|
|
620
|
+
...submitLabel ? { submitLabel } : {},
|
|
621
|
+
...steps ? { steps } : {},
|
|
622
|
+
...values ? { values } : {}
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function hasOnlyKeys(value, allowed) {
|
|
626
|
+
const allowedKeys = new Set(allowed);
|
|
627
|
+
return Object.keys(value).every((key) => allowedKeys.has(key));
|
|
628
|
+
}
|
|
629
|
+
|
|
365
630
|
// src/runtime/client.ts
|
|
366
631
|
function isTurnBoundary(event) {
|
|
367
632
|
return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
|
|
@@ -374,12 +639,8 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
374
639
|
if (event.type === "message.completed") {
|
|
375
640
|
handlers.onComplete?.();
|
|
376
641
|
}
|
|
377
|
-
if (event.type === "action.result")
|
|
378
|
-
|
|
379
|
-
if (result && typeof result === "object" && "output" in result) {
|
|
380
|
-
handlers.onActionResult?.(result.output);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
642
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
643
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
383
644
|
if (event.type !== "message.appended") return rendered;
|
|
384
645
|
const { messageDelta, messageSoFar } = event.data;
|
|
385
646
|
let delta = messageDelta;
|
|
@@ -390,9 +651,45 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
390
651
|
} else if (messageDelta) {
|
|
391
652
|
next += messageDelta;
|
|
392
653
|
}
|
|
393
|
-
if (delta) handlers.onDelta(delta);
|
|
654
|
+
if (delta) handlers.onDelta?.(delta);
|
|
394
655
|
return next;
|
|
395
656
|
}
|
|
657
|
+
function emitActionResult(event, handlers) {
|
|
658
|
+
const result = event.data.result;
|
|
659
|
+
if (result.kind === "tool-result") {
|
|
660
|
+
handlers.onToolResult?.({
|
|
661
|
+
callId: result.callId,
|
|
662
|
+
toolName: result.toolName,
|
|
663
|
+
status: event.data.status,
|
|
664
|
+
output: result.output,
|
|
665
|
+
...event.data.error ? { error: event.data.error } : {}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
if ("output" in result) handlers.onActionResult?.(result.output);
|
|
669
|
+
}
|
|
670
|
+
function emitInputRequests(event, handlers) {
|
|
671
|
+
handlers.onInputRequest?.(
|
|
672
|
+
event.data.requests.map((request) => {
|
|
673
|
+
const ui = parseAgentToolUiSurface(
|
|
674
|
+
request.ui
|
|
675
|
+
);
|
|
676
|
+
return {
|
|
677
|
+
requestId: request.requestId,
|
|
678
|
+
kind: request.kind,
|
|
679
|
+
prompt: request.prompt,
|
|
680
|
+
...request.display ? { display: request.display } : {},
|
|
681
|
+
...request.allowFreeform !== void 0 ? { allowFreeform: request.allowFreeform } : {},
|
|
682
|
+
...request.options ? { options: request.options } : {},
|
|
683
|
+
...ui ? { ui } : {},
|
|
684
|
+
action: {
|
|
685
|
+
callId: request.action.callId,
|
|
686
|
+
kind: "tool-call",
|
|
687
|
+
toolName: request.action.toolName
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
})
|
|
691
|
+
);
|
|
692
|
+
}
|
|
396
693
|
function isResumeTurnMessage(received, candidate) {
|
|
397
694
|
if (received === candidate) return true;
|
|
398
695
|
return Boolean(candidate) && received.endsWith(`
|
|
@@ -668,9 +965,11 @@ var AgentSession = class {
|
|
|
668
965
|
let streamIndex = session?.state.streamIndex ?? 0;
|
|
669
966
|
let rendered = "";
|
|
670
967
|
const workItems = /* @__PURE__ */ new Map();
|
|
968
|
+
let requestedInput = false;
|
|
671
969
|
try {
|
|
672
970
|
for await (const event of response) {
|
|
673
971
|
if (signal.aborted) break;
|
|
972
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
674
973
|
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
675
974
|
streamIndex += 1;
|
|
676
975
|
if (session) {
|
|
@@ -688,7 +987,7 @@ var AgentSession = class {
|
|
|
688
987
|
this.persistSessionCursor(session);
|
|
689
988
|
}
|
|
690
989
|
}
|
|
691
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
990
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
692
991
|
throw new Error("Empty response from runtime");
|
|
693
992
|
}
|
|
694
993
|
return rendered.trim();
|
|
@@ -708,6 +1007,9 @@ var AgentSession = class {
|
|
|
708
1007
|
() => attached.snapshot({ signal })
|
|
709
1008
|
);
|
|
710
1009
|
const turnEvents = latestTurnEvents(snapshot.events);
|
|
1010
|
+
const hasInputRequest = turnEvents.some(
|
|
1011
|
+
(event) => event.type === "input.requested"
|
|
1012
|
+
);
|
|
711
1013
|
const received = turnEvents[0];
|
|
712
1014
|
const lastSent = persisted.lastMessage;
|
|
713
1015
|
const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
|
|
@@ -718,6 +1020,8 @@ var AgentSession = class {
|
|
|
718
1020
|
const workItems = /* @__PURE__ */ new Map();
|
|
719
1021
|
for (const event of turnEvents) {
|
|
720
1022
|
applyWorkEvent(event, handlers, workItems);
|
|
1023
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
1024
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
721
1025
|
}
|
|
722
1026
|
if (rendered.startsWith(initialText)) {
|
|
723
1027
|
const missedText = rendered.slice(initialText.length);
|
|
@@ -747,7 +1051,9 @@ var AgentSession = class {
|
|
|
747
1051
|
);
|
|
748
1052
|
}
|
|
749
1053
|
handlers.onComplete?.();
|
|
750
|
-
if (!rendered.trim()
|
|
1054
|
+
if (!rendered.trim() && !hasInputRequest) {
|
|
1055
|
+
throw new Error("Empty response from runtime");
|
|
1056
|
+
}
|
|
751
1057
|
return rendered.trim();
|
|
752
1058
|
}
|
|
753
1059
|
let streamIndex = snapshot.session.streamIndex;
|
|
@@ -766,7 +1072,55 @@ var AgentSession = class {
|
|
|
766
1072
|
session = client.sessions.attach(session.state.sessionId, { streamIndex });
|
|
767
1073
|
this.session = session;
|
|
768
1074
|
this.persistSessionCursor(session);
|
|
769
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
1075
|
+
if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
|
|
1076
|
+
throw new Error("Empty response from runtime");
|
|
1077
|
+
}
|
|
1078
|
+
return rendered.trim();
|
|
1079
|
+
}
|
|
1080
|
+
async respondTurn(responses, signal, handlers) {
|
|
1081
|
+
const client = this.ensureClient();
|
|
1082
|
+
const session = this.session ?? this.attachPersistedSession(client);
|
|
1083
|
+
if (!session) {
|
|
1084
|
+
throw new Error("No active session is waiting for input.");
|
|
1085
|
+
}
|
|
1086
|
+
this.session = session;
|
|
1087
|
+
const inputResponses = responses.map(
|
|
1088
|
+
({ requestId, optionId, text }) => ({
|
|
1089
|
+
requestId,
|
|
1090
|
+
...optionId ? { optionId } : {},
|
|
1091
|
+
...text ? { text } : {}
|
|
1092
|
+
})
|
|
1093
|
+
);
|
|
1094
|
+
const response = await withCapabilityRefresh(
|
|
1095
|
+
this.capability,
|
|
1096
|
+
() => session.respond(inputResponses, { signal })
|
|
1097
|
+
);
|
|
1098
|
+
this.activeResponse = response;
|
|
1099
|
+
let streamIndex = session.state.streamIndex;
|
|
1100
|
+
let rendered = "";
|
|
1101
|
+
let requestedInput = false;
|
|
1102
|
+
const workItems = /* @__PURE__ */ new Map();
|
|
1103
|
+
try {
|
|
1104
|
+
for await (const event of response) {
|
|
1105
|
+
if (signal.aborted) break;
|
|
1106
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
1107
|
+
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
1108
|
+
streamIndex += 1;
|
|
1109
|
+
savePersistedAgentSession(
|
|
1110
|
+
this.visitorSessionId,
|
|
1111
|
+
session.state.sessionId,
|
|
1112
|
+
streamIndex,
|
|
1113
|
+
this.storeOptions
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
} finally {
|
|
1117
|
+
this.activeResponse = void 0;
|
|
1118
|
+
this.session = client.sessions.attach(session.state.sessionId, {
|
|
1119
|
+
streamIndex
|
|
1120
|
+
});
|
|
1121
|
+
this.persistSessionCursor(this.session);
|
|
1122
|
+
}
|
|
1123
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
770
1124
|
throw new Error("Empty response from runtime");
|
|
771
1125
|
}
|
|
772
1126
|
return rendered.trim();
|
|
@@ -826,6 +1180,11 @@ function createAgentClient(options) {
|
|
|
826
1180
|
resumeOptions.handlers,
|
|
827
1181
|
resumeOptions.initialText
|
|
828
1182
|
),
|
|
1183
|
+
respondTurn: (respondOptions) => session.respondTurn(
|
|
1184
|
+
respondOptions.responses,
|
|
1185
|
+
respondOptions.signal ?? new AbortController().signal,
|
|
1186
|
+
respondOptions.handlers
|
|
1187
|
+
),
|
|
829
1188
|
reset: () => session.reset(),
|
|
830
1189
|
cancelActive: () => session.cancelActive(),
|
|
831
1190
|
getActiveSessionId: () => session.getActiveSessionId()
|
|
@@ -875,7 +1234,176 @@ function formatAgentError(error) {
|
|
|
875
1234
|
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
876
1235
|
}
|
|
877
1236
|
|
|
1237
|
+
// src/react/lib/composer-form.ts
|
|
1238
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1239
|
+
var MIN_FORM_FIELDS = 2;
|
|
1240
|
+
var MAX_FORM_FIELDS = 5;
|
|
1241
|
+
var FIELD_LIBRARY = [
|
|
1242
|
+
{
|
|
1243
|
+
id: "email",
|
|
1244
|
+
kind: "email",
|
|
1245
|
+
label: "Email",
|
|
1246
|
+
placeholder: "email@example.com",
|
|
1247
|
+
required: true,
|
|
1248
|
+
autocomplete: "email",
|
|
1249
|
+
patterns: [/\be-?mails?\b/i]
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
id: "name",
|
|
1253
|
+
kind: "text",
|
|
1254
|
+
label: "Name",
|
|
1255
|
+
placeholder: "Your name",
|
|
1256
|
+
required: true,
|
|
1257
|
+
autocomplete: "name",
|
|
1258
|
+
patterns: [/\b((full|first|last)\s+)?names?\b/i],
|
|
1259
|
+
exclude: /\bcompany\s+names?\b/i
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
id: "phone",
|
|
1263
|
+
kind: "tel",
|
|
1264
|
+
label: "Phone",
|
|
1265
|
+
placeholder: "+1 555 0100",
|
|
1266
|
+
required: true,
|
|
1267
|
+
autocomplete: "tel",
|
|
1268
|
+
patterns: [/\b(phone|mobile|cell)\b/i]
|
|
1269
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
id: "company",
|
|
1272
|
+
kind: "text",
|
|
1273
|
+
label: "Company",
|
|
1274
|
+
placeholder: "Company name",
|
|
1275
|
+
required: true,
|
|
1276
|
+
autocomplete: "organization",
|
|
1277
|
+
patterns: [/\bcompan(y|ies)\b/i]
|
|
1278
|
+
},
|
|
1279
|
+
{
|
|
1280
|
+
id: "message",
|
|
1281
|
+
kind: "textarea",
|
|
1282
|
+
label: "Message",
|
|
1283
|
+
placeholder: "Message\u2026",
|
|
1284
|
+
required: true,
|
|
1285
|
+
patterns: [/\b(your message|a message|the message|inquiry|enquiry)\b/i],
|
|
1286
|
+
exclude: /\bin one message\b/i
|
|
1287
|
+
}
|
|
1288
|
+
];
|
|
1289
|
+
function asRecord(value) {
|
|
1290
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1291
|
+
}
|
|
1292
|
+
function asString(value) {
|
|
1293
|
+
return typeof value === "string" ? value.trim() : "";
|
|
1294
|
+
}
|
|
1295
|
+
function isFieldKind2(value) {
|
|
1296
|
+
return value === "text" || value === "email" || value === "tel" || value === "textarea";
|
|
1297
|
+
}
|
|
1298
|
+
function parseVisitorFormFields(value) {
|
|
1299
|
+
if (!Array.isArray(value)) return [];
|
|
1300
|
+
const fields = [];
|
|
1301
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1302
|
+
for (const item of value) {
|
|
1303
|
+
const record = asRecord(item);
|
|
1304
|
+
const id = asString(record?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
1305
|
+
const kind = asString(record?.kind);
|
|
1306
|
+
if (!record || !id || seen.has(id) || !isFieldKind2(kind)) continue;
|
|
1307
|
+
seen.add(id);
|
|
1308
|
+
const label = asString(record.label) || id;
|
|
1309
|
+
fields.push({
|
|
1310
|
+
id,
|
|
1311
|
+
kind,
|
|
1312
|
+
label,
|
|
1313
|
+
placeholder: asString(record.placeholder) || label,
|
|
1314
|
+
required: record.required !== false,
|
|
1315
|
+
...asString(record.autocomplete) ? { autocomplete: asString(record.autocomplete) } : {}
|
|
1316
|
+
});
|
|
1317
|
+
if (fields.length >= MAX_FORM_FIELDS) break;
|
|
1318
|
+
}
|
|
1319
|
+
return fields;
|
|
1320
|
+
}
|
|
1321
|
+
function readComposerControlValue(event) {
|
|
1322
|
+
const node = event.target ?? event.currentTarget ?? null;
|
|
1323
|
+
if (node && typeof node === "object" && "value" in node && typeof node.value === "string") {
|
|
1324
|
+
return node.value;
|
|
1325
|
+
}
|
|
1326
|
+
return "";
|
|
1327
|
+
}
|
|
1328
|
+
function isValidComposerFieldValue(field, value) {
|
|
1329
|
+
const trimmed = value.trim();
|
|
1330
|
+
if (!trimmed) return !field.required;
|
|
1331
|
+
if (field.kind === "email") return EMAIL_PATTERN.test(trimmed);
|
|
1332
|
+
if (field.kind === "tel") return trimmed.replace(/\D/g, "").length >= 7;
|
|
1333
|
+
return trimmed.length > 0;
|
|
1334
|
+
}
|
|
1335
|
+
function isComposerFormComplete(form, values) {
|
|
1336
|
+
return form.fields.every(
|
|
1337
|
+
(field) => isValidComposerFieldValue(field, values[field.id] ?? "")
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
function formatComposerFormMessage(form, values) {
|
|
1341
|
+
return form.fields.map((field) => {
|
|
1342
|
+
const value = (values[field.id] ?? "").trim();
|
|
1343
|
+
return value ? `${field.label}: ${value}` : "";
|
|
1344
|
+
}).filter(Boolean).join("\n");
|
|
1345
|
+
}
|
|
1346
|
+
function looksLikeFieldCollection(text) {
|
|
1347
|
+
return /\b(please|need|send|share|enter|provide|add|collect|what|which|use)\b/i.test(
|
|
1348
|
+
text
|
|
1349
|
+
) || /:\s*$/m.test(text) || /^[-*•]\s+/m.test(text);
|
|
1350
|
+
}
|
|
1351
|
+
function looksLikeBookingCopy(text) {
|
|
1352
|
+
return /book(ing)? (card|a demo|this time)|pick a (date|time)|available (times|slots)|calendly/i.test(
|
|
1353
|
+
text
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
function inferComposerForm(text) {
|
|
1357
|
+
const cleaned = text.trim();
|
|
1358
|
+
if (!cleaned || looksLikeBookingCopy(cleaned) || !looksLikeFieldCollection(cleaned)) {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
const fields = FIELD_LIBRARY.flatMap((field) => {
|
|
1362
|
+
if (field.exclude?.test(cleaned)) {
|
|
1363
|
+
const leftover = cleaned.replace(field.exclude, " ");
|
|
1364
|
+
if (!field.patterns.some((pattern) => pattern.test(leftover))) return [];
|
|
1365
|
+
} else if (!field.patterns.some((pattern) => pattern.test(cleaned))) {
|
|
1366
|
+
return [];
|
|
1367
|
+
}
|
|
1368
|
+
const { patterns: _patterns, exclude: _exclude, ...next } = field;
|
|
1369
|
+
return [next];
|
|
1370
|
+
}).slice(0, MAX_FORM_FIELDS);
|
|
1371
|
+
if (fields.length < MIN_FORM_FIELDS) return null;
|
|
1372
|
+
return {
|
|
1373
|
+
id: `inferred:${fields.map((field) => field.id).join("+")}`,
|
|
1374
|
+
fields
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
function resolveComposerForm(input) {
|
|
1378
|
+
if (input.enabled === false || input.hasBookingOffer) return null;
|
|
1379
|
+
const text = input.agentText.trim();
|
|
1380
|
+
if (!text) return null;
|
|
1381
|
+
const card = input.cards?.find((item) => item.type === "visitor_form");
|
|
1382
|
+
if (card?.fields && card.fields.length >= MIN_FORM_FIELDS) {
|
|
1383
|
+
return {
|
|
1384
|
+
id: `card:${card.fields.map((field) => field.id).join("+")}`,
|
|
1385
|
+
fields: card.fields.slice(0, MAX_FORM_FIELDS)
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
return inferComposerForm(text);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
878
1391
|
// src/react/lib/tool-card.ts
|
|
1392
|
+
function preferBookingOffer(current, next) {
|
|
1393
|
+
if (!current) return next;
|
|
1394
|
+
if (next.slots.length !== current.slots.length) {
|
|
1395
|
+
return next.slots.length > current.slots.length ? next : current;
|
|
1396
|
+
}
|
|
1397
|
+
if (next.eventTypes.length !== current.eventTypes.length) {
|
|
1398
|
+
return next.eventTypes.length > current.eventTypes.length ? next : current;
|
|
1399
|
+
}
|
|
1400
|
+
return next;
|
|
1401
|
+
}
|
|
1402
|
+
function looksLikeBookingReady(text) {
|
|
1403
|
+
return /demo option|options are ready|pick a (date|time)|available (times|slots)|book(ing)? (card|the demo)|schedule/i.test(
|
|
1404
|
+
text
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
879
1407
|
function bookingOfferIdentityKey(offer) {
|
|
880
1408
|
const eventTypes = offer.eventTypes.map(
|
|
881
1409
|
(item) => `${item.uri}:${item.duration ?? ""}:${item.locationKind ?? ""}:${item.location ?? ""}`
|
|
@@ -884,10 +1412,10 @@ function bookingOfferIdentityKey(offer) {
|
|
|
884
1412
|
return `${eventTypes}::${slots}` || "offer";
|
|
885
1413
|
}
|
|
886
1414
|
var FENCE_PATTERN = /```(?:webless-tool-card|json)\s*([\s\S]*?)```/gi;
|
|
887
|
-
function
|
|
1415
|
+
function asRecord2(value) {
|
|
888
1416
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
889
1417
|
}
|
|
890
|
-
function
|
|
1418
|
+
function asString2(value) {
|
|
891
1419
|
return typeof value === "string" ? value.trim() : "";
|
|
892
1420
|
}
|
|
893
1421
|
function isEventUri(value) {
|
|
@@ -897,24 +1425,28 @@ function isEventTypeUri(value) {
|
|
|
897
1425
|
return /^https:\/\/api\.calendly\.com\/event_types\/[^/]+$/i.test(value);
|
|
898
1426
|
}
|
|
899
1427
|
function parseToolCard(value) {
|
|
900
|
-
const record =
|
|
1428
|
+
const record = asRecord2(value);
|
|
901
1429
|
if (!record) return null;
|
|
902
|
-
if (record.booking_offer &&
|
|
1430
|
+
if (record.booking_offer && asString2(record.type) !== "booking_offer") {
|
|
903
1431
|
const nested = parseToolCard(record.booking_offer);
|
|
904
1432
|
if (nested) return nested;
|
|
905
1433
|
}
|
|
906
|
-
|
|
1434
|
+
if (record.visitor_booking && asString2(record.type) !== "booking_confirmed") {
|
|
1435
|
+
const nested = parseToolCard(record.visitor_booking);
|
|
1436
|
+
if (nested) return nested;
|
|
1437
|
+
}
|
|
1438
|
+
const type = asString2(record.type);
|
|
907
1439
|
if (type === "booking_offer") {
|
|
908
1440
|
const eventTypes = Array.isArray(record.eventTypes) ? record.eventTypes.flatMap((item) => {
|
|
909
|
-
const entry =
|
|
910
|
-
const uri =
|
|
911
|
-
if (!entry || !
|
|
1441
|
+
const entry = asRecord2(item);
|
|
1442
|
+
const uri = asString2(entry?.uri);
|
|
1443
|
+
if (!entry || !uri) return [];
|
|
912
1444
|
const duration = entry.duration;
|
|
913
|
-
const locationKind =
|
|
914
|
-
const location =
|
|
1445
|
+
const locationKind = asString2(entry.locationKind);
|
|
1446
|
+
const location = asString2(entry.location);
|
|
915
1447
|
return [
|
|
916
1448
|
{
|
|
917
|
-
name:
|
|
1449
|
+
name: asString2(entry.name) || "Meeting",
|
|
918
1450
|
uri,
|
|
919
1451
|
...typeof duration === "number" ? { duration } : {},
|
|
920
1452
|
...locationKind ? { locationKind } : {},
|
|
@@ -923,10 +1455,10 @@ function parseToolCard(value) {
|
|
|
923
1455
|
];
|
|
924
1456
|
}) : [];
|
|
925
1457
|
const slots = Array.isArray(record.slots) ? record.slots.flatMap((item) => {
|
|
926
|
-
const entry =
|
|
927
|
-
const startTime =
|
|
1458
|
+
const entry = asRecord2(item);
|
|
1459
|
+
const startTime = asString2(entry?.startTime);
|
|
928
1460
|
if (!entry || !startTime) return [];
|
|
929
|
-
const eventTypeUri =
|
|
1461
|
+
const eventTypeUri = asString2(entry.eventTypeUri);
|
|
930
1462
|
return [
|
|
931
1463
|
{
|
|
932
1464
|
startTime,
|
|
@@ -934,15 +1466,15 @@ function parseToolCard(value) {
|
|
|
934
1466
|
}
|
|
935
1467
|
];
|
|
936
1468
|
}) : [];
|
|
937
|
-
if (slots.length === 0) return null;
|
|
1469
|
+
if (eventTypes.length === 0 && slots.length === 0) return null;
|
|
938
1470
|
return { type: "booking_offer", eventTypes, slots };
|
|
939
1471
|
}
|
|
940
1472
|
if (type === "booking_confirmed") {
|
|
941
|
-
const eventUri =
|
|
1473
|
+
const eventUri = asString2(record.eventUri);
|
|
942
1474
|
if (!isEventUri(eventUri)) return null;
|
|
943
|
-
const inviteeUri =
|
|
944
|
-
const inviteeEmail =
|
|
945
|
-
const startTime =
|
|
1475
|
+
const inviteeUri = asString2(record.inviteeUri);
|
|
1476
|
+
const inviteeEmail = asString2(record.inviteeEmail);
|
|
1477
|
+
const startTime = asString2(record.startTime);
|
|
946
1478
|
return {
|
|
947
1479
|
type: "booking_confirmed",
|
|
948
1480
|
eventUri,
|
|
@@ -952,10 +1484,15 @@ function parseToolCard(value) {
|
|
|
952
1484
|
};
|
|
953
1485
|
}
|
|
954
1486
|
if (type === "booking_canceled") {
|
|
955
|
-
const eventUri =
|
|
1487
|
+
const eventUri = asString2(record.eventUri);
|
|
956
1488
|
if (!isEventUri(eventUri)) return null;
|
|
957
1489
|
return { type: "booking_canceled", eventUri };
|
|
958
1490
|
}
|
|
1491
|
+
if (type === "visitor_form") {
|
|
1492
|
+
const fields = parseVisitorFormFields(record.fields);
|
|
1493
|
+
if (fields.length < 2) return null;
|
|
1494
|
+
return { type: "visitor_form", fields };
|
|
1495
|
+
}
|
|
959
1496
|
return null;
|
|
960
1497
|
}
|
|
961
1498
|
function formatBookingOfferFence(offer) {
|
|
@@ -969,11 +1506,10 @@ function formatBookingOfferFence(offer) {
|
|
|
969
1506
|
"```"
|
|
970
1507
|
].join("\n");
|
|
971
1508
|
}
|
|
972
|
-
function
|
|
973
|
-
const record =
|
|
974
|
-
const data =
|
|
975
|
-
|
|
976
|
-
return card?.type === "booking_offer" ? card : null;
|
|
1509
|
+
function bookingCardFromActionOutput(output) {
|
|
1510
|
+
const record = asRecord2(output);
|
|
1511
|
+
const data = asRecord2(record?.data) ?? record;
|
|
1512
|
+
return parseToolCard(data);
|
|
977
1513
|
}
|
|
978
1514
|
function ensureBookingOfferText(text, offer) {
|
|
979
1515
|
if (!offer) return text;
|
|
@@ -988,6 +1524,21 @@ ${formatBookingOfferFence(offer)}`;
|
|
|
988
1524
|
function hideToolCardFences(text) {
|
|
989
1525
|
return text.replace(/```(?:webless-tool-card|json)\s*[\s\S]*?```/gi, "").replace(/```(?:webless-tool-card|json)[\s\S]*$/i, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
990
1526
|
}
|
|
1527
|
+
var BOOKING_CARD_FALLBACK = "Pick a date and time that works for you.";
|
|
1528
|
+
function looksLikeBookingAvailabilityDump(text) {
|
|
1529
|
+
const cleaned = text.trim();
|
|
1530
|
+
if (!cleaned) return false;
|
|
1531
|
+
const isoCount = (cleaned.match(/\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/g) ?? []).length;
|
|
1532
|
+
const utcCount = (cleaned.match(/\bUTC\b/g) ?? []).length;
|
|
1533
|
+
return isoCount >= 2 || utcCount >= 2 || /api\.calendly\.com|calendly\.com\//i.test(cleaned) || /webless-tool-card/i.test(cleaned);
|
|
1534
|
+
}
|
|
1535
|
+
function sanitizeBookingOfferCopy(text) {
|
|
1536
|
+
const cleaned = hideToolCardFences(text);
|
|
1537
|
+
if (!cleaned || looksLikeBookingAvailabilityDump(cleaned)) {
|
|
1538
|
+
return BOOKING_CARD_FALLBACK;
|
|
1539
|
+
}
|
|
1540
|
+
return cleaned;
|
|
1541
|
+
}
|
|
991
1542
|
function visitorTimeZone() {
|
|
992
1543
|
try {
|
|
993
1544
|
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
@@ -1089,6 +1640,7 @@ function formatSlotLabel(startTime) {
|
|
|
1089
1640
|
function formatBookingRequest(input) {
|
|
1090
1641
|
return [
|
|
1091
1642
|
"Book this meeting now with CALENDLY_POST_INVITEE.",
|
|
1643
|
+
"Execute CALENDLY_POST_INVITEE in this turn with the fields below.",
|
|
1092
1644
|
"Do not open a Calendly URL and do not list other scheduled events.",
|
|
1093
1645
|
"Do not invent a location kind. Use only the location fields below.",
|
|
1094
1646
|
`event_type: ${input.eventTypeUri}`,
|
|
@@ -1116,7 +1668,7 @@ function visitorBookingPrefix(booking) {
|
|
|
1116
1668
|
}
|
|
1117
1669
|
|
|
1118
1670
|
// src/react/persisted-conversation.ts
|
|
1119
|
-
var CONVERSATION_VERSION =
|
|
1671
|
+
var CONVERSATION_VERSION = 3;
|
|
1120
1672
|
function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
1121
1673
|
return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
|
|
1122
1674
|
}
|
|
@@ -1156,30 +1708,171 @@ function parseToolStep(value) {
|
|
|
1156
1708
|
...typeof record.detail === "string" && record.detail ? { detail: record.detail } : {}
|
|
1157
1709
|
};
|
|
1158
1710
|
}
|
|
1711
|
+
function parseInputOption(value) {
|
|
1712
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1713
|
+
const record = value;
|
|
1714
|
+
if (typeof record.id !== "string" || typeof record.label !== "string") {
|
|
1715
|
+
return null;
|
|
1716
|
+
}
|
|
1717
|
+
return {
|
|
1718
|
+
id: record.id,
|
|
1719
|
+
label: record.label,
|
|
1720
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
1721
|
+
...record.style === "default" || record.style === "primary" || record.style === "danger" ? { style: record.style } : {}
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
function parseInputRequest(value) {
|
|
1725
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1726
|
+
const record = value;
|
|
1727
|
+
if (typeof record.requestId !== "string" || record.kind !== "question" && record.kind !== "session-limit" && record.kind !== "tool-approval" || typeof record.prompt !== "string") {
|
|
1728
|
+
return null;
|
|
1729
|
+
}
|
|
1730
|
+
const action = record.action;
|
|
1731
|
+
if (typeof action !== "object" || action === null) return null;
|
|
1732
|
+
const actionRecord = action;
|
|
1733
|
+
if (typeof actionRecord.callId !== "string" || actionRecord.kind !== "tool-call" || typeof actionRecord.toolName !== "string") {
|
|
1734
|
+
return null;
|
|
1735
|
+
}
|
|
1736
|
+
const options = Array.isArray(record.options) ? record.options.map(parseInputOption) : void 0;
|
|
1737
|
+
if (Array.isArray(record.options) && options?.some((option) => option === null)) {
|
|
1738
|
+
return null;
|
|
1739
|
+
}
|
|
1740
|
+
const ui = record.ui ? parseAgentToolUiSurface(record.ui) : void 0;
|
|
1741
|
+
if (record.ui && !ui) return null;
|
|
1742
|
+
return {
|
|
1743
|
+
requestId: record.requestId,
|
|
1744
|
+
kind: record.kind,
|
|
1745
|
+
prompt: record.prompt,
|
|
1746
|
+
action: {
|
|
1747
|
+
callId: actionRecord.callId,
|
|
1748
|
+
kind: "tool-call",
|
|
1749
|
+
toolName: actionRecord.toolName
|
|
1750
|
+
},
|
|
1751
|
+
...record.display === "confirmation" || record.display === "select" || record.display === "text" ? { display: record.display } : {},
|
|
1752
|
+
...record.allowFreeform === true ? { allowFreeform: true } : {},
|
|
1753
|
+
...options && options.length > 0 ? {
|
|
1754
|
+
options: options.filter(
|
|
1755
|
+
(option) => option !== null
|
|
1756
|
+
)
|
|
1757
|
+
} : {},
|
|
1758
|
+
...ui ? { ui } : {}
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
function parseToolResult(value) {
|
|
1762
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1763
|
+
const record = value;
|
|
1764
|
+
if (typeof record.id !== "string" || typeof record.toolName !== "string" || record.status !== "completed" && record.status !== "failed" && record.status !== "rejected") {
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
if (record.kind === "input") {
|
|
1768
|
+
const surface = parseAgentToolUiSurface(
|
|
1769
|
+
record.surface
|
|
1770
|
+
);
|
|
1771
|
+
return surface ? {
|
|
1772
|
+
id: record.id,
|
|
1773
|
+
toolName: record.toolName,
|
|
1774
|
+
status: record.status,
|
|
1775
|
+
kind: "input",
|
|
1776
|
+
surface
|
|
1777
|
+
} : null;
|
|
1778
|
+
}
|
|
1779
|
+
if (record.kind === "entity" && typeof record.title === "string") {
|
|
1780
|
+
return {
|
|
1781
|
+
id: record.id,
|
|
1782
|
+
toolName: record.toolName,
|
|
1783
|
+
status: record.status,
|
|
1784
|
+
kind: "entity",
|
|
1785
|
+
title: record.title,
|
|
1786
|
+
...typeof record.description === "string" ? { description: record.description } : {}
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
if (record.kind === "collection" && typeof record.title === "string" && Array.isArray(record.items)) {
|
|
1790
|
+
return {
|
|
1791
|
+
id: record.id,
|
|
1792
|
+
toolName: record.toolName,
|
|
1793
|
+
status: record.status,
|
|
1794
|
+
kind: "collection",
|
|
1795
|
+
title: record.title,
|
|
1796
|
+
items: record.items.flatMap((item) => {
|
|
1797
|
+
if (typeof item !== "object" || item === null) return [];
|
|
1798
|
+
const entry = item;
|
|
1799
|
+
if (typeof entry.title !== "string") return [];
|
|
1800
|
+
return [
|
|
1801
|
+
{
|
|
1802
|
+
title: entry.title,
|
|
1803
|
+
...typeof entry.description === "string" ? { description: entry.description } : {},
|
|
1804
|
+
...typeof entry.href === "string" ? { href: entry.href } : {}
|
|
1805
|
+
}
|
|
1806
|
+
];
|
|
1807
|
+
})
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
if (record.kind === "signature" && typeof record.title === "string") {
|
|
1811
|
+
return {
|
|
1812
|
+
id: record.id,
|
|
1813
|
+
toolName: record.toolName,
|
|
1814
|
+
status: record.status,
|
|
1815
|
+
kind: "signature",
|
|
1816
|
+
title: record.title,
|
|
1817
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
1818
|
+
...typeof record.statusLabel === "string" ? { statusLabel: record.statusLabel } : {}
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
if (record.kind !== "summary" || typeof record.title !== "string")
|
|
1822
|
+
return null;
|
|
1823
|
+
return {
|
|
1824
|
+
id: record.id,
|
|
1825
|
+
toolName: record.toolName,
|
|
1826
|
+
status: record.status,
|
|
1827
|
+
kind: "summary",
|
|
1828
|
+
title: record.title,
|
|
1829
|
+
...typeof record.description === "string" ? { description: record.description } : {}
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1159
1832
|
function visitorTurnText(message) {
|
|
1160
1833
|
return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
|
|
1161
1834
|
}
|
|
1162
1835
|
function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
1163
1836
|
if (typeof sessionStorage === "undefined") return null;
|
|
1164
|
-
const raw = sessionStorage.getItem(
|
|
1837
|
+
const raw = sessionStorage.getItem(
|
|
1838
|
+
conversationKey(storageKeyPrefix, visitorSessionId)
|
|
1839
|
+
);
|
|
1165
1840
|
if (!raw) return null;
|
|
1166
1841
|
try {
|
|
1167
1842
|
const value = JSON.parse(raw);
|
|
1168
1843
|
if (typeof value !== "object" || value === null) return null;
|
|
1169
1844
|
const record = value;
|
|
1170
|
-
if (record.version !== 1 && record.version !== CONVERSATION_VERSION || !Array.isArray(record.messages) || typeof record.pending !== "boolean" || typeof record.streamingText !== "string" || record.version === CONVERSATION_VERSION && !Array.isArray(record.toolSteps)) {
|
|
1845
|
+
if (record.version !== 1 && record.version !== 2 && record.version !== CONVERSATION_VERSION || !Array.isArray(record.messages) || typeof record.pending !== "boolean" || typeof record.streamingText !== "string" || record.version === CONVERSATION_VERSION && !Array.isArray(record.toolSteps)) {
|
|
1171
1846
|
return null;
|
|
1172
1847
|
}
|
|
1173
1848
|
const messages = record.messages.map(parseMessage);
|
|
1174
1849
|
if (messages.some((message) => message === null)) return null;
|
|
1175
|
-
const storedToolSteps = record.version === CONVERSATION_VERSION && Array.isArray(record.toolSteps) ? record.toolSteps : [];
|
|
1850
|
+
const storedToolSteps = (record.version === 2 || record.version === CONVERSATION_VERSION) && Array.isArray(record.toolSteps) ? record.toolSteps : [];
|
|
1176
1851
|
const toolSteps = storedToolSteps.map(parseToolStep);
|
|
1177
1852
|
if (toolSteps.some((step) => step === null)) return null;
|
|
1853
|
+
const storedToolResults = record.version === CONVERSATION_VERSION && Array.isArray(record.toolResults) ? record.toolResults : [];
|
|
1854
|
+
const toolResults = storedToolResults.map(parseToolResult);
|
|
1855
|
+
if (toolResults.some((result) => result === null)) return null;
|
|
1856
|
+
const storedPendingInputs = Array.isArray(record.pendingInputs) ? record.pendingInputs : [];
|
|
1857
|
+
const pendingInputs = storedPendingInputs.map(parseInputRequest);
|
|
1858
|
+
if (pendingInputs.some((request) => request === null)) return null;
|
|
1178
1859
|
return {
|
|
1179
|
-
messages: messages.filter(
|
|
1860
|
+
messages: messages.filter(
|
|
1861
|
+
(message) => message !== null
|
|
1862
|
+
),
|
|
1180
1863
|
pending: record.pending,
|
|
1181
1864
|
streamingText: record.streamingText,
|
|
1182
|
-
toolSteps: toolSteps.filter((step) => step !== null)
|
|
1865
|
+
toolSteps: toolSteps.filter((step) => step !== null),
|
|
1866
|
+
...Array.isArray(record.toolResults) ? {
|
|
1867
|
+
toolResults: toolResults.filter(
|
|
1868
|
+
(result) => result !== null
|
|
1869
|
+
)
|
|
1870
|
+
} : {},
|
|
1871
|
+
...pendingInputs.length > 0 ? {
|
|
1872
|
+
pendingInputs: pendingInputs.filter(
|
|
1873
|
+
(request) => request !== null
|
|
1874
|
+
)
|
|
1875
|
+
} : {}
|
|
1183
1876
|
};
|
|
1184
1877
|
} catch {
|
|
1185
1878
|
return null;
|
|
@@ -1194,7 +1887,9 @@ function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conv
|
|
|
1194
1887
|
}
|
|
1195
1888
|
function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
1196
1889
|
if (typeof sessionStorage === "undefined") return;
|
|
1197
|
-
sessionStorage.removeItem(
|
|
1890
|
+
sessionStorage.removeItem(
|
|
1891
|
+
conversationKey(storageKeyPrefix, visitorSessionId)
|
|
1892
|
+
);
|
|
1198
1893
|
clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId);
|
|
1199
1894
|
}
|
|
1200
1895
|
function pendingBookingKey(storageKeyPrefix, visitorSessionId) {
|
|
@@ -1230,45 +1925,457 @@ function savePendingWidgetBooking(storageKeyPrefix, visitorSessionId, booking) {
|
|
|
1230
1925
|
}
|
|
1231
1926
|
function clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
|
|
1232
1927
|
if (typeof sessionStorage === "undefined") return;
|
|
1233
|
-
sessionStorage.removeItem(
|
|
1928
|
+
sessionStorage.removeItem(
|
|
1929
|
+
pendingBookingKey(storageKeyPrefix, visitorSessionId)
|
|
1930
|
+
);
|
|
1234
1931
|
}
|
|
1235
1932
|
|
|
1236
|
-
// src/
|
|
1237
|
-
var
|
|
1238
|
-
|
|
1933
|
+
// src/runtime/tool-result-envelope.ts
|
|
1934
|
+
var ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
1935
|
+
"schemaVersion",
|
|
1936
|
+
"output",
|
|
1937
|
+
"presentationKinds",
|
|
1938
|
+
"ui"
|
|
1939
|
+
]);
|
|
1940
|
+
function isRecord3(value) {
|
|
1941
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1942
|
+
}
|
|
1943
|
+
function isJsonValue2(value, seen = /* @__PURE__ */ new Set()) {
|
|
1944
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
1945
|
+
return true;
|
|
1946
|
+
}
|
|
1947
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1948
|
+
if (typeof value !== "object") return false;
|
|
1949
|
+
if (seen.has(value)) return false;
|
|
1950
|
+
seen.add(value);
|
|
1951
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue2(item, seen)) : Object.entries(value).every(
|
|
1952
|
+
([key, item]) => typeof key === "string" && isJsonValue2(item, seen)
|
|
1953
|
+
);
|
|
1954
|
+
seen.delete(value);
|
|
1955
|
+
return valid;
|
|
1956
|
+
}
|
|
1957
|
+
function decodeEnvelope(value) {
|
|
1958
|
+
if (typeof value !== "string") return value;
|
|
1959
|
+
try {
|
|
1960
|
+
return JSON.parse(value);
|
|
1961
|
+
} catch {
|
|
1962
|
+
return null;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
function parseAgentToolResultEnvelope(value) {
|
|
1966
|
+
const decoded = decodeEnvelope(value);
|
|
1967
|
+
if (!isRecord3(decoded)) return null;
|
|
1968
|
+
const keys = Object.keys(decoded);
|
|
1969
|
+
if (keys.some((key) => !ENVELOPE_KEYS.has(key)) || decoded.schemaVersion !== "webless.tool-result.v1" || !isJsonValue2(decoded.output) || !Array.isArray(decoded.presentationKinds) || decoded.presentationKinds.length < 1 || decoded.presentationKinds.length > 16) {
|
|
1970
|
+
return null;
|
|
1971
|
+
}
|
|
1972
|
+
const presentationKinds = decoded.presentationKinds.flatMap((kind) => {
|
|
1973
|
+
if (typeof kind !== "string") return [];
|
|
1974
|
+
const normalized = kind.trim();
|
|
1975
|
+
return normalized && normalized.length <= 128 ? [normalized] : [];
|
|
1976
|
+
});
|
|
1977
|
+
if (presentationKinds.length !== decoded.presentationKinds.length) {
|
|
1978
|
+
return null;
|
|
1979
|
+
}
|
|
1980
|
+
const ui = decoded.ui === void 0 ? void 0 : parseAgentToolUiSurface(decoded.ui);
|
|
1981
|
+
if (decoded.ui !== void 0 && !ui) return null;
|
|
1239
1982
|
return {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
role: "agent",
|
|
1245
|
-
text: greeting,
|
|
1246
|
-
createdAt: 0
|
|
1247
|
-
}
|
|
1248
|
-
],
|
|
1249
|
-
toolSteps: [],
|
|
1250
|
-
journey: null,
|
|
1251
|
-
followUps: [],
|
|
1252
|
-
streamingText: "",
|
|
1253
|
-
pendingOffer: null,
|
|
1254
|
-
error: null
|
|
1983
|
+
schemaVersion: "webless.tool-result.v1",
|
|
1984
|
+
output: decoded.output,
|
|
1985
|
+
presentationKinds,
|
|
1986
|
+
...ui ? { ui } : {}
|
|
1255
1987
|
};
|
|
1256
1988
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1989
|
+
|
|
1990
|
+
// src/react/lib/tool-result.ts
|
|
1991
|
+
var MAX_TEXT_LENGTH = 240;
|
|
1992
|
+
var MAX_DETAILS = 6;
|
|
1993
|
+
var MAX_LINKS = 4;
|
|
1994
|
+
var MAX_COLLECTION_ITEMS = 8;
|
|
1995
|
+
var INTERNAL_FACT_LABEL = /^(hs\b|createdate|created at|updatedate|updated at|firstname|first name|lastname|last name|vid|objectid|object id|all contact)/iu;
|
|
1996
|
+
function safeText(value) {
|
|
1997
|
+
if (typeof value !== "string") return "";
|
|
1998
|
+
return value.trim().slice(0, MAX_TEXT_LENGTH);
|
|
1999
|
+
}
|
|
2000
|
+
function safeHref(value) {
|
|
2001
|
+
const text = safeText(value);
|
|
2002
|
+
if (!text) return "";
|
|
2003
|
+
try {
|
|
2004
|
+
const url = new URL(text);
|
|
2005
|
+
return url.protocol === "https:" ? url.toString() : "";
|
|
2006
|
+
} catch {
|
|
2007
|
+
return "";
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
function fallbackTitle(result) {
|
|
2011
|
+
if (result.status === "failed") return "Couldn\u2019t complete this action";
|
|
2012
|
+
if (result.status === "rejected") return "Action not approved";
|
|
2013
|
+
return "Action completed";
|
|
2014
|
+
}
|
|
2015
|
+
function bookingPresenter(kind) {
|
|
1259
2016
|
return {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
2017
|
+
kind,
|
|
2018
|
+
present: ({ envelope }) => {
|
|
2019
|
+
const card = bookingCardFromActionOutput(envelope.output);
|
|
2020
|
+
return card && card.type === kind ? { kind: "booking", card } : null;
|
|
2021
|
+
}
|
|
1265
2022
|
};
|
|
1266
2023
|
}
|
|
1267
|
-
function
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
2024
|
+
function asRecord3(value) {
|
|
2025
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2026
|
+
}
|
|
2027
|
+
function parseFacts(value) {
|
|
2028
|
+
if (!Array.isArray(value)) return [];
|
|
2029
|
+
return value.flatMap((item) => {
|
|
2030
|
+
const fact = asRecord3(item);
|
|
2031
|
+
const label = safeText(fact?.label);
|
|
2032
|
+
const factValue = safeText(fact?.value);
|
|
2033
|
+
if (!label || !factValue) return [];
|
|
2034
|
+
if (INTERNAL_FACT_LABEL.test(label)) return [];
|
|
2035
|
+
return [{ label, value: factValue }];
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
function parseLinks(output, defaultLabel) {
|
|
2039
|
+
const href = safeHref(output.href);
|
|
2040
|
+
const linkLabel = safeText(output.linkLabel) || defaultLabel;
|
|
2041
|
+
return href ? [{ label: linkLabel, href }] : [];
|
|
2042
|
+
}
|
|
2043
|
+
function entityResultPresenter() {
|
|
2044
|
+
return {
|
|
2045
|
+
kind: "entity_result",
|
|
2046
|
+
present: ({ envelope, result }) => {
|
|
2047
|
+
const output = asRecord3(envelope.output);
|
|
2048
|
+
if (!output) return null;
|
|
2049
|
+
const title = safeText(output.title) || fallbackTitle(result);
|
|
2050
|
+
const description = safeText(output.description);
|
|
2051
|
+
const facts = parseFacts(output.facts).filter((fact) => fact.value !== description && fact.value !== title).slice(0, MAX_DETAILS);
|
|
2052
|
+
return {
|
|
2053
|
+
kind: "entity",
|
|
2054
|
+
title,
|
|
2055
|
+
...description && description !== title ? { description } : {},
|
|
2056
|
+
...facts.length ? { details: facts } : {},
|
|
2057
|
+
...parseLinks(output, "Open record").length ? { links: parseLinks(output, "Open record") } : {}
|
|
2058
|
+
};
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
function collectionResultPresenter() {
|
|
2063
|
+
return {
|
|
2064
|
+
kind: "collection",
|
|
2065
|
+
present: ({ envelope, result }) => {
|
|
2066
|
+
const output = asRecord3(envelope.output);
|
|
2067
|
+
if (!output) return null;
|
|
2068
|
+
const title = safeText(output.title) || fallbackTitleFromKind("collection");
|
|
2069
|
+
const items = Array.isArray(output.items) ? output.items.slice(0, MAX_COLLECTION_ITEMS).flatMap((item) => {
|
|
2070
|
+
const entry = asRecord3(item);
|
|
2071
|
+
if (!entry) return [];
|
|
2072
|
+
const itemTitle = safeText(entry.title);
|
|
2073
|
+
if (!itemTitle) return [];
|
|
2074
|
+
const description = safeText(entry.description);
|
|
2075
|
+
const details = parseFacts(entry.facts).slice(0, MAX_DETAILS);
|
|
2076
|
+
const href = safeHref(entry.href);
|
|
2077
|
+
return [
|
|
2078
|
+
{
|
|
2079
|
+
title: itemTitle,
|
|
2080
|
+
...description ? { description } : {},
|
|
2081
|
+
...details.length ? { details } : {},
|
|
2082
|
+
...href ? { href } : {}
|
|
2083
|
+
}
|
|
2084
|
+
];
|
|
2085
|
+
}) : [];
|
|
2086
|
+
return {
|
|
2087
|
+
kind: "collection",
|
|
2088
|
+
title,
|
|
2089
|
+
items
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
function signatureResultPresenter() {
|
|
2095
|
+
return {
|
|
2096
|
+
kind: "signature",
|
|
2097
|
+
present: ({ envelope, result }) => {
|
|
2098
|
+
const output = asRecord3(envelope.output);
|
|
2099
|
+
if (!output) return null;
|
|
2100
|
+
const title = safeText(output.title) || fallbackTitleFromKind("signature");
|
|
2101
|
+
const description = safeText(output.description);
|
|
2102
|
+
const statusLabel = safeText(output.status);
|
|
2103
|
+
return {
|
|
2104
|
+
kind: "signature",
|
|
2105
|
+
title,
|
|
2106
|
+
...description ? { description } : {},
|
|
2107
|
+
...statusLabel ? { statusLabel } : {},
|
|
2108
|
+
...parseLinks(output, "Sign now").length ? { links: parseLinks(output, "Sign now") } : {}
|
|
2109
|
+
};
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
function resultCardPresenter(kind) {
|
|
2114
|
+
return {
|
|
2115
|
+
kind,
|
|
2116
|
+
present: ({ envelope, result }) => {
|
|
2117
|
+
const output = asRecord3(envelope.output);
|
|
2118
|
+
if (!output) return null;
|
|
2119
|
+
const title = safeText(output.title) || fallbackTitleFromKind(kind);
|
|
2120
|
+
const description = safeText(output.description);
|
|
2121
|
+
const facts = parseFacts(output.facts).slice(0, MAX_DETAILS);
|
|
2122
|
+
return {
|
|
2123
|
+
kind: "summary",
|
|
2124
|
+
title,
|
|
2125
|
+
...description && description !== title ? { description } : {},
|
|
2126
|
+
...facts.length ? { details: facts } : {},
|
|
2127
|
+
...parseLinks(output, "Open").length ? { links: parseLinks(output, "Open") } : {}
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
};
|
|
2131
|
+
}
|
|
2132
|
+
function fallbackTitleFromKind(kind) {
|
|
2133
|
+
if (kind === "document") return "Document ready";
|
|
2134
|
+
if (kind === "signature") return "Ready to sign";
|
|
2135
|
+
if (kind === "payment") return "Payment link";
|
|
2136
|
+
if (kind === "collection") return "Results";
|
|
2137
|
+
if (kind === "confirmation_result") return "Confirmed";
|
|
2138
|
+
return "Saved";
|
|
2139
|
+
}
|
|
2140
|
+
var builtInVisitorToolResultRegistry = [
|
|
2141
|
+
bookingPresenter("booking_offer"),
|
|
2142
|
+
bookingPresenter("booking_confirmed"),
|
|
2143
|
+
bookingPresenter("booking_canceled"),
|
|
2144
|
+
entityResultPresenter(),
|
|
2145
|
+
collectionResultPresenter(),
|
|
2146
|
+
signatureResultPresenter(),
|
|
2147
|
+
resultCardPresenter("document"),
|
|
2148
|
+
resultCardPresenter("payment"),
|
|
2149
|
+
resultCardPresenter("confirmation_result")
|
|
2150
|
+
];
|
|
2151
|
+
function resolvePresenterOutput(result, envelope, registry) {
|
|
2152
|
+
const presenters = [...registry, ...builtInVisitorToolResultRegistry];
|
|
2153
|
+
for (const presentationKind of envelope.presentationKinds) {
|
|
2154
|
+
const presenter = presenters.find(
|
|
2155
|
+
(candidate) => candidate.kind === presentationKind
|
|
2156
|
+
);
|
|
2157
|
+
if (!presenter) continue;
|
|
2158
|
+
try {
|
|
2159
|
+
const presented = presenter.present({
|
|
2160
|
+
envelope,
|
|
2161
|
+
presentationKind,
|
|
2162
|
+
result
|
|
2163
|
+
});
|
|
2164
|
+
if (presented) return presented;
|
|
2165
|
+
} catch {
|
|
2166
|
+
return null;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
return null;
|
|
2170
|
+
}
|
|
2171
|
+
function finalizeSummaryPresentation(result, proposed) {
|
|
2172
|
+
const title = safeText(proposed.title) || fallbackTitle(result);
|
|
2173
|
+
const description = safeText(proposed.description);
|
|
2174
|
+
const details = proposed.details?.slice(0, MAX_DETAILS).flatMap((detail) => {
|
|
2175
|
+
const label = safeText(detail.label);
|
|
2176
|
+
const value = safeText(detail.value);
|
|
2177
|
+
if (!label || !value) return [];
|
|
2178
|
+
if (INTERNAL_FACT_LABEL.test(label)) return [];
|
|
2179
|
+
if (value === title || value === description) return [];
|
|
2180
|
+
return [{ label, value }];
|
|
2181
|
+
});
|
|
2182
|
+
const links = proposed.links?.slice(0, MAX_LINKS).flatMap((link) => {
|
|
2183
|
+
const label = safeText(link.label);
|
|
2184
|
+
const href = safeHref(link.href);
|
|
2185
|
+
return label && href ? [{ label, href }] : [];
|
|
2186
|
+
});
|
|
2187
|
+
return {
|
|
2188
|
+
id: result.callId,
|
|
2189
|
+
toolName: result.toolName,
|
|
2190
|
+
status: result.status,
|
|
2191
|
+
kind: "summary",
|
|
2192
|
+
title,
|
|
2193
|
+
...description && description !== title ? { description } : {},
|
|
2194
|
+
...details?.length ? { details } : {},
|
|
2195
|
+
...links?.length ? { links } : {}
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function presentVisitorToolResult(result, registry = []) {
|
|
2199
|
+
const envelope = parseAgentToolResultEnvelope(result.output) ?? legacyBookingEnvelope(result.output);
|
|
2200
|
+
const proposed = envelope ? resolvePresenterOutput(result, envelope, registry) : null;
|
|
2201
|
+
if (proposed?.kind === "booking") {
|
|
2202
|
+
return {
|
|
2203
|
+
id: result.callId,
|
|
2204
|
+
toolName: result.toolName,
|
|
2205
|
+
status: result.status,
|
|
2206
|
+
kind: "booking",
|
|
2207
|
+
card: proposed.card
|
|
2208
|
+
};
|
|
2209
|
+
}
|
|
2210
|
+
if (envelope?.ui && envelope.presentationKinds.includes("tool_input")) {
|
|
2211
|
+
return {
|
|
2212
|
+
id: result.callId,
|
|
2213
|
+
toolName: result.toolName,
|
|
2214
|
+
status: result.status,
|
|
2215
|
+
kind: "hidden"
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
if (!proposed) {
|
|
2219
|
+
if (result.status === "failed" || result.status === "rejected") {
|
|
2220
|
+
return {
|
|
2221
|
+
id: result.callId,
|
|
2222
|
+
toolName: result.toolName,
|
|
2223
|
+
status: result.status,
|
|
2224
|
+
kind: "summary",
|
|
2225
|
+
title: fallbackTitle(result)
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2228
|
+
return {
|
|
2229
|
+
id: result.callId,
|
|
2230
|
+
toolName: result.toolName,
|
|
2231
|
+
status: result.status,
|
|
2232
|
+
kind: "hidden"
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
if (proposed.kind === "entity") {
|
|
2236
|
+
return {
|
|
2237
|
+
id: result.callId,
|
|
2238
|
+
toolName: result.toolName,
|
|
2239
|
+
status: result.status,
|
|
2240
|
+
...proposed
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
if (proposed.kind === "collection") {
|
|
2244
|
+
return {
|
|
2245
|
+
id: result.callId,
|
|
2246
|
+
toolName: result.toolName,
|
|
2247
|
+
status: result.status,
|
|
2248
|
+
...proposed
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
if (proposed.kind === "signature") {
|
|
2252
|
+
return {
|
|
2253
|
+
id: result.callId,
|
|
2254
|
+
toolName: result.toolName,
|
|
2255
|
+
status: result.status,
|
|
2256
|
+
...proposed
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
return finalizeSummaryPresentation(result, proposed);
|
|
2260
|
+
}
|
|
2261
|
+
function legacyBookingEnvelope(output) {
|
|
2262
|
+
const card = bookingCardFromActionOutput(output);
|
|
2263
|
+
return card ? {
|
|
2264
|
+
schemaVersion: "webless.tool-result.v1",
|
|
2265
|
+
output: card,
|
|
2266
|
+
presentationKinds: [card.type]
|
|
2267
|
+
} : null;
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
// src/react/lib/visitor-input.ts
|
|
2271
|
+
function shouldRenderVisitorInputCard(request) {
|
|
2272
|
+
if (request.kind === "tool-approval" || request.kind === "session-limit") {
|
|
2273
|
+
return true;
|
|
2274
|
+
}
|
|
2275
|
+
if (request.kind === "question") {
|
|
2276
|
+
return (request.options?.length ?? 0) > 0;
|
|
2277
|
+
}
|
|
2278
|
+
return false;
|
|
2279
|
+
}
|
|
2280
|
+
function isChatCollectibleInputRequest(request) {
|
|
2281
|
+
return request.kind === "question" && (request.options?.length ?? 0) === 0;
|
|
2282
|
+
}
|
|
2283
|
+
function appendChatCollectiblePrompts(messages, requests) {
|
|
2284
|
+
const next = [...messages];
|
|
2285
|
+
for (const request of requests.filter(isChatCollectibleInputRequest)) {
|
|
2286
|
+
const prompt = request.prompt.trim();
|
|
2287
|
+
if (!prompt) continue;
|
|
2288
|
+
const last = next.at(-1);
|
|
2289
|
+
if (last?.role === "agent" && last.text.trim() === prompt) continue;
|
|
2290
|
+
next.push({
|
|
2291
|
+
id: `agent-input-${request.requestId}`,
|
|
2292
|
+
role: "agent",
|
|
2293
|
+
text: prompt,
|
|
2294
|
+
createdAt: Date.now()
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
return next;
|
|
2298
|
+
}
|
|
2299
|
+
function chatInputResponseForText(requests, text) {
|
|
2300
|
+
const trimmed = text.trim();
|
|
2301
|
+
if (!trimmed) return null;
|
|
2302
|
+
const pending = requests.find(isChatCollectibleInputRequest);
|
|
2303
|
+
if (!pending) return null;
|
|
2304
|
+
return { requestId: pending.requestId, text: trimmed };
|
|
2305
|
+
}
|
|
2306
|
+
function normalizeAssistantDedupeKey(text) {
|
|
2307
|
+
return text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
2308
|
+
}
|
|
2309
|
+
function isNearDuplicateAssistantText(left, right) {
|
|
2310
|
+
const a = normalizeAssistantDedupeKey(left);
|
|
2311
|
+
const b = normalizeAssistantDedupeKey(right);
|
|
2312
|
+
if (!a || !b) return false;
|
|
2313
|
+
if (a === b) return true;
|
|
2314
|
+
const shorter = a.length <= b.length ? a : b;
|
|
2315
|
+
const longer = a.length <= b.length ? b : a;
|
|
2316
|
+
if (shorter.length < 40) return false;
|
|
2317
|
+
return longer.startsWith(
|
|
2318
|
+
shorter.slice(0, Math.floor(shorter.length * 0.85))
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2321
|
+
function appendAgentTurnMessage(messages, displayText) {
|
|
2322
|
+
const trimmed = displayText.trim();
|
|
2323
|
+
if (!trimmed) return [...messages];
|
|
2324
|
+
const agentMessage = {
|
|
2325
|
+
id: `agent-${Date.now()}`,
|
|
2326
|
+
role: "agent",
|
|
2327
|
+
text: trimmed,
|
|
2328
|
+
createdAt: Date.now()
|
|
2329
|
+
};
|
|
2330
|
+
const last = messages.at(-1);
|
|
2331
|
+
if (last?.role === "agent" && (last.id.startsWith("agent-input-") || isNearDuplicateAssistantText(last.text, trimmed))) {
|
|
2332
|
+
return [...messages.slice(0, -1), agentMessage];
|
|
2333
|
+
}
|
|
2334
|
+
return [...messages, agentMessage];
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
// src/react/hooks/useAgentChat.ts
|
|
2338
|
+
var DEFAULT_GREETING = "Hi! I can search this site or bring in a specialist when it helps. What would you like to know?";
|
|
2339
|
+
function createInitialState(greeting = DEFAULT_GREETING) {
|
|
2340
|
+
return {
|
|
2341
|
+
phase: "idle",
|
|
2342
|
+
messages: [
|
|
2343
|
+
{
|
|
2344
|
+
id: "greeting",
|
|
2345
|
+
role: "agent",
|
|
2346
|
+
text: greeting,
|
|
2347
|
+
createdAt: 0
|
|
2348
|
+
}
|
|
2349
|
+
],
|
|
2350
|
+
toolSteps: [],
|
|
2351
|
+
journey: null,
|
|
2352
|
+
followUps: [],
|
|
2353
|
+
streamingText: "",
|
|
2354
|
+
pendingOffer: null,
|
|
2355
|
+
pendingInputs: [],
|
|
2356
|
+
toolResults: [],
|
|
2357
|
+
error: null
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
function stateFromConversation(conversation, initialState) {
|
|
2361
|
+
if (!conversation || conversation.messages.length === 0) return initialState;
|
|
2362
|
+
const pendingInputs = conversation.pendingInputs ?? [];
|
|
2363
|
+
const hasWaitingInput = pendingInputs.length > 0;
|
|
2364
|
+
return {
|
|
2365
|
+
...initialState,
|
|
2366
|
+
messages: conversation.messages,
|
|
2367
|
+
phase: hasWaitingInput ? "waiting-input" : conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
|
|
2368
|
+
streamingText: conversation.streamingText,
|
|
2369
|
+
toolSteps: conversation.toolSteps,
|
|
2370
|
+
toolResults: conversation.toolResults,
|
|
2371
|
+
...hasWaitingInput ? { pendingInputs } : {}
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
function upsertToolStep(steps, item) {
|
|
2375
|
+
const next = {
|
|
2376
|
+
id: item.id,
|
|
2377
|
+
kind: item.kind,
|
|
2378
|
+
label: item.label,
|
|
1272
2379
|
state: item.state,
|
|
1273
2380
|
...item.detail ? { detail: item.detail } : {}
|
|
1274
2381
|
};
|
|
@@ -1276,6 +2383,24 @@ function upsertToolStep(steps, item) {
|
|
|
1276
2383
|
if (index < 0) return [...steps, next];
|
|
1277
2384
|
return steps.map((step, stepIndex) => stepIndex === index ? next : step);
|
|
1278
2385
|
}
|
|
2386
|
+
function applyBookingOffer(prev, card) {
|
|
2387
|
+
const pendingOffer = preferBookingOffer(prev.pendingOffer, card);
|
|
2388
|
+
const last = prev.messages.at(-1);
|
|
2389
|
+
if (last?.role === "agent" && prev.phase === "complete") {
|
|
2390
|
+
return {
|
|
2391
|
+
...prev,
|
|
2392
|
+
pendingOffer,
|
|
2393
|
+
messages: [
|
|
2394
|
+
...prev.messages.slice(0, -1),
|
|
2395
|
+
{
|
|
2396
|
+
...last,
|
|
2397
|
+
text: ensureBookingOfferText(last.text, pendingOffer)
|
|
2398
|
+
}
|
|
2399
|
+
]
|
|
2400
|
+
};
|
|
2401
|
+
}
|
|
2402
|
+
return { ...prev, pendingOffer };
|
|
2403
|
+
}
|
|
1279
2404
|
function completeActivePlanning(steps) {
|
|
1280
2405
|
return steps.map(
|
|
1281
2406
|
(step) => step.kind === "planning" && step.state === "active" ? {
|
|
@@ -1285,6 +2410,9 @@ function completeActivePlanning(steps) {
|
|
|
1285
2410
|
} : step
|
|
1286
2411
|
);
|
|
1287
2412
|
}
|
|
2413
|
+
function isJsonRecord(value) {
|
|
2414
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2415
|
+
}
|
|
1288
2416
|
function useAgentChat({
|
|
1289
2417
|
customerId,
|
|
1290
2418
|
getUnpublishedPreviewGrant,
|
|
@@ -1294,7 +2422,8 @@ function useAgentChat({
|
|
|
1294
2422
|
runtimeOrigin,
|
|
1295
2423
|
visitorSessionId,
|
|
1296
2424
|
storageKeyPrefix,
|
|
1297
|
-
greeting
|
|
2425
|
+
greeting,
|
|
2426
|
+
toolResultRegistry
|
|
1298
2427
|
}) {
|
|
1299
2428
|
const initialState = (0, import_react2.useMemo)(
|
|
1300
2429
|
() => createInitialState(greeting?.trim() || DEFAULT_GREETING),
|
|
@@ -1302,6 +2431,8 @@ function useAgentChat({
|
|
|
1302
2431
|
);
|
|
1303
2432
|
const previewGrantProviderRef = (0, import_react2.useRef)(getUnpublishedPreviewGrant);
|
|
1304
2433
|
previewGrantProviderRef.current = getUnpublishedPreviewGrant;
|
|
2434
|
+
const toolResultRegistryRef = (0, import_react2.useRef)(toolResultRegistry);
|
|
2435
|
+
toolResultRegistryRef.current = toolResultRegistry;
|
|
1305
2436
|
const resolveUnpublishedPreviewGrant = () => {
|
|
1306
2437
|
const provider = previewGrantProviderRef.current;
|
|
1307
2438
|
if (!provider) {
|
|
@@ -1396,14 +2527,18 @@ function useAgentChat({
|
|
|
1396
2527
|
messages: state.messages,
|
|
1397
2528
|
pending: isAgentBusy(state.phase),
|
|
1398
2529
|
streamingText: state.streamingText,
|
|
1399
|
-
toolSteps: state.toolSteps
|
|
2530
|
+
toolSteps: state.toolSteps,
|
|
2531
|
+
toolResults: state.toolResults ?? [],
|
|
2532
|
+
...state.pendingInputs && state.pendingInputs.length > 0 ? { pendingInputs: state.pendingInputs } : {}
|
|
1400
2533
|
});
|
|
1401
2534
|
}, [
|
|
1402
2535
|
resolvedStorageKeyPrefix,
|
|
1403
2536
|
state.messages,
|
|
2537
|
+
state.pendingInputs,
|
|
1404
2538
|
state.phase,
|
|
1405
2539
|
state.streamingText,
|
|
1406
2540
|
state.toolSteps,
|
|
2541
|
+
state.toolResults,
|
|
1407
2542
|
visitorId
|
|
1408
2543
|
]);
|
|
1409
2544
|
const reset = (0, import_react2.useCallback)(() => {
|
|
@@ -1416,13 +2551,20 @@ function useAgentChat({
|
|
|
1416
2551
|
}, [initialState, resolvedStorageKeyPrefix, visitorId]);
|
|
1417
2552
|
const runTurn = (0, import_react2.useCallback)(
|
|
1418
2553
|
async (input) => {
|
|
1419
|
-
const {
|
|
2554
|
+
const {
|
|
2555
|
+
controller,
|
|
2556
|
+
initialText = "",
|
|
2557
|
+
responses,
|
|
2558
|
+
resume,
|
|
2559
|
+
visitorText
|
|
2560
|
+
} = input;
|
|
1420
2561
|
const { signal } = controller;
|
|
1421
2562
|
const isActiveRun = () => runRef.current === controller && !signal.aborted;
|
|
1422
2563
|
try {
|
|
1423
2564
|
let streamStarted = Boolean(initialText);
|
|
1424
2565
|
let streamed = initialText;
|
|
1425
2566
|
const capturedOffers = [];
|
|
2567
|
+
let capturedInputCount = 0;
|
|
1426
2568
|
const handlers = {
|
|
1427
2569
|
onWork: (item) => {
|
|
1428
2570
|
if (!isActiveRun()) return;
|
|
@@ -1432,11 +2574,62 @@ function useAgentChat({
|
|
|
1432
2574
|
toolSteps: upsertToolStep(prev.toolSteps, item)
|
|
1433
2575
|
}));
|
|
1434
2576
|
},
|
|
1435
|
-
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
2577
|
+
onToolResult: (result) => {
|
|
2578
|
+
const presentation = presentVisitorToolResult(
|
|
2579
|
+
result,
|
|
2580
|
+
toolResultRegistryRef.current
|
|
2581
|
+
);
|
|
2582
|
+
if (presentation.kind === "booking") {
|
|
2583
|
+
const card = presentation.card;
|
|
2584
|
+
if (card.type === "booking_offer") {
|
|
2585
|
+
capturedOffers.push(card);
|
|
2586
|
+
setState((prev) => applyBookingOffer(prev, card));
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
if (!isActiveRun()) return;
|
|
2590
|
+
if (card.type === "booking_confirmed") {
|
|
2591
|
+
pendingBookingRef.current = card;
|
|
2592
|
+
savePendingWidgetBooking(
|
|
2593
|
+
resolvedStorageKeyPrefix,
|
|
2594
|
+
visitorId,
|
|
2595
|
+
card
|
|
2596
|
+
);
|
|
2597
|
+
} else if (card.type === "booking_canceled" && pendingBookingRef.current?.eventUri === card.eventUri) {
|
|
2598
|
+
pendingBookingRef.current = null;
|
|
2599
|
+
clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
|
|
2600
|
+
}
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
if (!isActiveRun()) return;
|
|
2604
|
+
if (presentation.kind === "hidden") return;
|
|
2605
|
+
if (presentation.kind === "input") return;
|
|
2606
|
+
setState((prev) => ({
|
|
2607
|
+
...prev,
|
|
2608
|
+
toolResults: [
|
|
2609
|
+
...(prev.toolResults ?? []).filter(
|
|
2610
|
+
(item) => item.id !== presentation.id
|
|
2611
|
+
),
|
|
2612
|
+
presentation
|
|
2613
|
+
]
|
|
2614
|
+
}));
|
|
2615
|
+
},
|
|
2616
|
+
onInputRequest: (requests) => {
|
|
2617
|
+
if (!isActiveRun()) return;
|
|
2618
|
+
capturedInputCount += requests.length;
|
|
2619
|
+
const cardRequests = requests.filter(shouldRenderVisitorInputCard);
|
|
2620
|
+
const chatRequests = requests.filter(isChatCollectibleInputRequest);
|
|
2621
|
+
setState((prev) => ({
|
|
2622
|
+
...prev,
|
|
2623
|
+
phase: cardRequests.length > 0 ? "waiting-input" : "complete",
|
|
2624
|
+
pendingInputs: [...requests],
|
|
2625
|
+
...chatRequests.length > 0 ? {
|
|
2626
|
+
messages: appendChatCollectiblePrompts(
|
|
2627
|
+
prev.messages,
|
|
2628
|
+
chatRequests
|
|
2629
|
+
),
|
|
2630
|
+
toolSteps: completeActivePlanning(prev.toolSteps)
|
|
2631
|
+
} : {}
|
|
2632
|
+
}));
|
|
1440
2633
|
},
|
|
1441
2634
|
onDelta: (delta) => {
|
|
1442
2635
|
if (!isActiveRun()) return;
|
|
@@ -1462,7 +2655,11 @@ function useAgentChat({
|
|
|
1462
2655
|
streamStarted = true;
|
|
1463
2656
|
}
|
|
1464
2657
|
};
|
|
1465
|
-
let finalText =
|
|
2658
|
+
let finalText = responses ? await clientRef.current.respondTurn({
|
|
2659
|
+
handlers,
|
|
2660
|
+
responses,
|
|
2661
|
+
signal
|
|
2662
|
+
}) : resume ? await clientRef.current.resumeTurn({
|
|
1466
2663
|
handlers,
|
|
1467
2664
|
initialText,
|
|
1468
2665
|
message: visitorText,
|
|
@@ -1474,17 +2671,15 @@ function useAgentChat({
|
|
|
1474
2671
|
signal
|
|
1475
2672
|
});
|
|
1476
2673
|
}
|
|
1477
|
-
if (!isActiveRun() || finalText === null) return;
|
|
2674
|
+
if (!isActiveRun() || finalText === null) return null;
|
|
2675
|
+
if (!finalText.trim() && capturedInputCount > 0) {
|
|
2676
|
+
runRef.current = null;
|
|
2677
|
+
return null;
|
|
2678
|
+
}
|
|
1478
2679
|
const displayText = ensureBookingOfferText(
|
|
1479
2680
|
finalText,
|
|
1480
2681
|
capturedOffers.at(-1) ?? null
|
|
1481
2682
|
);
|
|
1482
|
-
const agentMessage = {
|
|
1483
|
-
id: `agent-${Date.now()}`,
|
|
1484
|
-
role: "agent",
|
|
1485
|
-
text: displayText,
|
|
1486
|
-
createdAt: Date.now()
|
|
1487
|
-
};
|
|
1488
2683
|
const parsedCards = extractToolCards(displayText);
|
|
1489
2684
|
for (const card of parsedCards) {
|
|
1490
2685
|
if (card.type === "booking_confirmed") {
|
|
@@ -1498,21 +2693,25 @@ function useAgentChat({
|
|
|
1498
2693
|
}
|
|
1499
2694
|
setState((prev) => ({
|
|
1500
2695
|
...prev,
|
|
1501
|
-
phase: "complete",
|
|
1502
|
-
messages:
|
|
2696
|
+
phase: (prev.pendingInputs ?? []).some(shouldRenderVisitorInputCard) ? "waiting-input" : "complete",
|
|
2697
|
+
messages: appendAgentTurnMessage(prev.messages, displayText),
|
|
1503
2698
|
toolSteps: completeActivePlanning(prev.toolSteps),
|
|
1504
2699
|
streamingText: "",
|
|
1505
|
-
pendingOffer:
|
|
2700
|
+
pendingOffer: capturedOffers.at(-1) ?? prev.pendingOffer,
|
|
2701
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2702
|
+
isChatCollectibleInputRequest
|
|
2703
|
+
),
|
|
1506
2704
|
followUps: [],
|
|
1507
2705
|
journey: null
|
|
1508
2706
|
}));
|
|
1509
2707
|
runRef.current = null;
|
|
2708
|
+
return displayText;
|
|
1510
2709
|
} catch (error) {
|
|
1511
2710
|
if (error instanceof DOMException && error.name === "AbortError")
|
|
1512
|
-
return;
|
|
1513
|
-
if (!isActiveRun()) return;
|
|
2711
|
+
return null;
|
|
2712
|
+
if (!isActiveRun()) return null;
|
|
1514
2713
|
const message = formatAgentError(error);
|
|
1515
|
-
if (!message) return;
|
|
2714
|
+
if (!message) return null;
|
|
1516
2715
|
setState((prev) => ({
|
|
1517
2716
|
...prev,
|
|
1518
2717
|
phase: "error",
|
|
@@ -1528,6 +2727,7 @@ function useAgentChat({
|
|
|
1528
2727
|
error: message
|
|
1529
2728
|
}));
|
|
1530
2729
|
runRef.current = null;
|
|
2730
|
+
return null;
|
|
1531
2731
|
}
|
|
1532
2732
|
},
|
|
1533
2733
|
[resolvedStorageKeyPrefix, visitorId]
|
|
@@ -1555,6 +2755,49 @@ function useAgentChat({
|
|
|
1555
2755
|
);
|
|
1556
2756
|
const submit = (0, import_react2.useCallback)(
|
|
1557
2757
|
async (visitorText, options) => {
|
|
2758
|
+
const trimmed = visitorText.trim();
|
|
2759
|
+
if (!trimmed) return null;
|
|
2760
|
+
const chatResponse = chatInputResponseForText(
|
|
2761
|
+
state.pendingInputs ?? [],
|
|
2762
|
+
trimmed
|
|
2763
|
+
);
|
|
2764
|
+
if (chatResponse) {
|
|
2765
|
+
const visitorMessage2 = {
|
|
2766
|
+
id: `visitor-${Date.now()}`,
|
|
2767
|
+
role: "visitor",
|
|
2768
|
+
text: trimmed,
|
|
2769
|
+
createdAt: Date.now()
|
|
2770
|
+
};
|
|
2771
|
+
if (runRef.current) {
|
|
2772
|
+
runRef.current.abort();
|
|
2773
|
+
clientRef.current.cancelActive();
|
|
2774
|
+
}
|
|
2775
|
+
const controller2 = new AbortController();
|
|
2776
|
+
runRef.current = controller2;
|
|
2777
|
+
setState((prev) => ({
|
|
2778
|
+
...prev,
|
|
2779
|
+
phase: "running-tools",
|
|
2780
|
+
messages: [...prev.messages, visitorMessage2],
|
|
2781
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2782
|
+
(request) => request.requestId !== chatResponse.requestId
|
|
2783
|
+
),
|
|
2784
|
+
toolSteps: [
|
|
2785
|
+
{
|
|
2786
|
+
id: "planning",
|
|
2787
|
+
kind: "planning",
|
|
2788
|
+
label: "Understanding your question",
|
|
2789
|
+
state: "active"
|
|
2790
|
+
}
|
|
2791
|
+
],
|
|
2792
|
+
error: null
|
|
2793
|
+
}));
|
|
2794
|
+
return await runTurn({
|
|
2795
|
+
controller: controller2,
|
|
2796
|
+
responses: [chatResponse],
|
|
2797
|
+
resume: false,
|
|
2798
|
+
visitorText: ""
|
|
2799
|
+
});
|
|
2800
|
+
}
|
|
1558
2801
|
if (runRef.current) {
|
|
1559
2802
|
runRef.current.abort();
|
|
1560
2803
|
clientRef.current.cancelActive();
|
|
@@ -1589,11 +2832,17 @@ ${outgoing}` : outgoing;
|
|
|
1589
2832
|
followUps: [],
|
|
1590
2833
|
streamingText: "",
|
|
1591
2834
|
pendingOffer: null,
|
|
2835
|
+
pendingInputs: [],
|
|
2836
|
+
toolResults: [],
|
|
1592
2837
|
error: null
|
|
1593
2838
|
}));
|
|
1594
|
-
await runTurn({
|
|
2839
|
+
return await runTurn({
|
|
2840
|
+
controller,
|
|
2841
|
+
resume: false,
|
|
2842
|
+
visitorText: runtimeText
|
|
2843
|
+
});
|
|
1595
2844
|
},
|
|
1596
|
-
[runTurn]
|
|
2845
|
+
[runTurn, state.pendingInputs]
|
|
1597
2846
|
);
|
|
1598
2847
|
const retry = (0, import_react2.useCallback)(async () => {
|
|
1599
2848
|
const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
|
|
@@ -1619,6 +2868,8 @@ ${outgoing}` : outgoing;
|
|
|
1619
2868
|
followUps: [],
|
|
1620
2869
|
streamingText: "",
|
|
1621
2870
|
pendingOffer: null,
|
|
2871
|
+
pendingInputs: [],
|
|
2872
|
+
toolResults: [],
|
|
1622
2873
|
error: null
|
|
1623
2874
|
}));
|
|
1624
2875
|
await runTurn({
|
|
@@ -1627,6 +2878,50 @@ ${outgoing}` : outgoing;
|
|
|
1627
2878
|
visitorText: visitorTurnText(visitorMessage)
|
|
1628
2879
|
});
|
|
1629
2880
|
}, [runTurn, state.messages]);
|
|
2881
|
+
const respondToToolInput = (0, import_react2.useCallback)(
|
|
2882
|
+
async (surface, values) => {
|
|
2883
|
+
const details = JSON.stringify(values);
|
|
2884
|
+
await submit(`${surface.title} details provided`, {
|
|
2885
|
+
runtimeText: [
|
|
2886
|
+
`Structured input provided for ${surface.toolSlug}.`,
|
|
2887
|
+
surface.operationId ? `Operation: ${surface.operationId}.` : "",
|
|
2888
|
+
`Use these exact values and retry the action: ${details}`,
|
|
2889
|
+
"Do not invent or alter any values."
|
|
2890
|
+
].filter(Boolean).join("\n")
|
|
2891
|
+
});
|
|
2892
|
+
},
|
|
2893
|
+
[submit]
|
|
2894
|
+
);
|
|
2895
|
+
const respondToInput = (0, import_react2.useCallback)(
|
|
2896
|
+
async (response) => {
|
|
2897
|
+
if (runRef.current) return;
|
|
2898
|
+
const pending = state.pendingInputs?.find(
|
|
2899
|
+
(request) => request.requestId === response.requestId
|
|
2900
|
+
);
|
|
2901
|
+
if (!pending) return;
|
|
2902
|
+
if (pending.ui && isJsonRecord(response.value)) {
|
|
2903
|
+
await respondToToolInput(pending.ui, response.value);
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
const controller = new AbortController();
|
|
2907
|
+
runRef.current = controller;
|
|
2908
|
+
setState((prev) => ({
|
|
2909
|
+
...prev,
|
|
2910
|
+
phase: "running-tools",
|
|
2911
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2912
|
+
(request) => request.requestId !== response.requestId
|
|
2913
|
+
),
|
|
2914
|
+
error: null
|
|
2915
|
+
}));
|
|
2916
|
+
await runTurn({
|
|
2917
|
+
controller,
|
|
2918
|
+
responses: [response],
|
|
2919
|
+
resume: false,
|
|
2920
|
+
visitorText: ""
|
|
2921
|
+
});
|
|
2922
|
+
},
|
|
2923
|
+
[respondToToolInput, runTurn, state.pendingInputs]
|
|
2924
|
+
);
|
|
1630
2925
|
(0, import_react2.useEffect)(() => {
|
|
1631
2926
|
const conversation = loadPersistedAgentConversation(
|
|
1632
2927
|
resolvedStorageKeyPrefix,
|
|
@@ -1660,6 +2955,8 @@ ${outgoing}` : outgoing;
|
|
|
1660
2955
|
state,
|
|
1661
2956
|
reset,
|
|
1662
2957
|
retry,
|
|
2958
|
+
respondToInput,
|
|
2959
|
+
respondToToolInput,
|
|
1663
2960
|
submit,
|
|
1664
2961
|
rememberBooking,
|
|
1665
2962
|
forgetBooking,
|
|
@@ -1708,7 +3005,7 @@ function normalizeAgentPlacement(placement) {
|
|
|
1708
3005
|
}
|
|
1709
3006
|
|
|
1710
3007
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
1711
|
-
var
|
|
3008
|
+
var import_react10 = require("react");
|
|
1712
3009
|
|
|
1713
3010
|
// src/react/types/conversation.ts
|
|
1714
3011
|
var defaultAgentRailTheme = {
|
|
@@ -1722,8 +3019,8 @@ var defaultAgentRailTheme = {
|
|
|
1722
3019
|
textMuted: "#5a6378",
|
|
1723
3020
|
textSubtle: "#8a94a8",
|
|
1724
3021
|
border: "rgb(42 51 70 / 0.1)",
|
|
1725
|
-
visitorBubble: "#
|
|
1726
|
-
visitorText: "#
|
|
3022
|
+
visitorBubble: "#f3edff",
|
|
3023
|
+
visitorText: "#171b2a",
|
|
1727
3024
|
success: "#18794e",
|
|
1728
3025
|
danger: "#c94b63",
|
|
1729
3026
|
fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
|
|
@@ -1740,7 +3037,8 @@ var defaultDarkAgentRailTheme = {
|
|
|
1740
3037
|
textMuted: "#b6bfce",
|
|
1741
3038
|
textSubtle: "#919cad",
|
|
1742
3039
|
border: "rgb(226 232 240 / 0.16)",
|
|
1743
|
-
visitorBubble: "#
|
|
3040
|
+
visitorBubble: "#2b2140",
|
|
3041
|
+
visitorText: "#f5f7fb",
|
|
1744
3042
|
success: "#55cf91",
|
|
1745
3043
|
danger: "#ff8da1"
|
|
1746
3044
|
};
|
|
@@ -1778,10 +3076,20 @@ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
|
|
|
1778
3076
|
// src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
|
|
1779
3077
|
var import_react5 = require("react");
|
|
1780
3078
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
3079
|
+
function joinLabels(labels) {
|
|
3080
|
+
if (labels.length <= 1) return labels[0] ?? "";
|
|
3081
|
+
if (labels.length === 2) return `${labels[0]} and ${labels[1]}`;
|
|
3082
|
+
return `${labels.slice(0, -1).join(", ")} and ${labels.at(-1) ?? ""}`;
|
|
3083
|
+
}
|
|
1781
3084
|
function workSummary(steps, failed, brandLabel) {
|
|
3085
|
+
const activeSpecialists = steps.filter(
|
|
3086
|
+
(step) => step.kind === "specialist" && step.state === "active"
|
|
3087
|
+
);
|
|
3088
|
+
if (activeSpecialists.length > 0)
|
|
3089
|
+
return `Working with ${joinLabels(
|
|
3090
|
+
activeSpecialists.map((step) => step.label)
|
|
3091
|
+
)}`;
|
|
1782
3092
|
const active = [...steps].reverse().find((step) => step.state === "active");
|
|
1783
|
-
if (active?.kind === "specialist")
|
|
1784
|
-
return `${active.label} is reviewing your question`;
|
|
1785
3093
|
if (active?.kind === "search") return "Searching this site";
|
|
1786
3094
|
if (active)
|
|
1787
3095
|
return brandLabel ? `${brandLabel} is choosing the best way to help` : "Choosing the best way to help";
|
|
@@ -1799,7 +3107,7 @@ function workSummary(steps, failed, brandLabel) {
|
|
|
1799
3107
|
if (specialists.length === 1)
|
|
1800
3108
|
return `Brought in ${specialists[0]?.label}`;
|
|
1801
3109
|
if (searched) return "Searched this site";
|
|
1802
|
-
return "Answer ready";
|
|
3110
|
+
return brandLabel ? `Answered with ${brandLabel}` : "Answer ready";
|
|
1803
3111
|
}
|
|
1804
3112
|
function stepLabel(step, brandLabel) {
|
|
1805
3113
|
return step.kind === "planning" ? brandLabel : step.label;
|
|
@@ -1817,36 +3125,11 @@ function stepDetail(step, steps) {
|
|
|
1817
3125
|
return "Searched this site";
|
|
1818
3126
|
return step.detail;
|
|
1819
3127
|
}
|
|
1820
|
-
function SearchIcon() {
|
|
1821
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
|
|
1822
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "7", cy: "7", r: "3.75", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1823
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1824
|
-
"path",
|
|
1825
|
-
{
|
|
1826
|
-
d: "m10 10 3 3",
|
|
1827
|
-
stroke: "currentColor",
|
|
1828
|
-
strokeWidth: "1.4",
|
|
1829
|
-
strokeLinecap: "round"
|
|
1830
|
-
}
|
|
1831
|
-
)
|
|
1832
|
-
] });
|
|
1833
|
-
}
|
|
1834
|
-
function PlanningIcon() {
|
|
1835
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1836
|
-
"path",
|
|
1837
|
-
{
|
|
1838
|
-
d: "M4 4.5h8M4 8h5.5M4 11.5h7",
|
|
1839
|
-
stroke: "currentColor",
|
|
1840
|
-
strokeWidth: "1.4",
|
|
1841
|
-
strokeLinecap: "round"
|
|
1842
|
-
}
|
|
1843
|
-
) });
|
|
1844
|
-
}
|
|
1845
3128
|
function AgentActivityBubble({
|
|
1846
3129
|
brandLabel = "",
|
|
1847
|
-
brandLogoUrl,
|
|
1848
3130
|
failed = false,
|
|
1849
|
-
steps
|
|
3131
|
+
steps,
|
|
3132
|
+
onRetryStep
|
|
1850
3133
|
}) {
|
|
1851
3134
|
const active = steps.some((step) => step.state === "active");
|
|
1852
3135
|
const receiptId = steps.map((step) => step.id).join(":");
|
|
@@ -1854,6 +3137,10 @@ function AgentActivityBubble({
|
|
|
1854
3137
|
null
|
|
1855
3138
|
);
|
|
1856
3139
|
const detailsOpen = active || expandedReceiptId === receiptId;
|
|
3140
|
+
const delegationCount = steps.filter(
|
|
3141
|
+
(step) => step.kind === "specialist"
|
|
3142
|
+
).length;
|
|
3143
|
+
const delegated = delegationCount > 0 && steps.some((step) => step.kind === "planning");
|
|
1857
3144
|
const visibleSteps = steps.filter(
|
|
1858
3145
|
(step) => step.kind !== "planning" || Boolean(brandLabel)
|
|
1859
3146
|
);
|
|
@@ -1886,10 +3173,11 @@ function AgentActivityBubble({
|
|
|
1886
3173
|
),
|
|
1887
3174
|
detailsOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: visibleSteps.map((step) => {
|
|
1888
3175
|
const detail = stepDetail(step, steps);
|
|
3176
|
+
const child = delegated && step.kind === "specialist";
|
|
1889
3177
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1890
3178
|
"li",
|
|
1891
3179
|
{
|
|
1892
|
-
className: "agent-activity-bubble__step"
|
|
3180
|
+
className: `agent-activity-bubble__step${child ? " agent-activity-bubble__step--child" : ""}`,
|
|
1893
3181
|
"data-kind": step.kind,
|
|
1894
3182
|
"data-state": step.state,
|
|
1895
3183
|
children: [
|
|
@@ -1897,25 +3185,27 @@ function AgentActivityBubble({
|
|
|
1897
3185
|
"span",
|
|
1898
3186
|
{
|
|
1899
3187
|
className: "agent-activity-bubble__step-icon",
|
|
1900
|
-
"aria-hidden": "true"
|
|
1901
|
-
children: step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1902
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlanningIcon, {}),
|
|
1903
|
-
brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1904
|
-
"img",
|
|
1905
|
-
{
|
|
1906
|
-
src: brandLogoUrl,
|
|
1907
|
-
alt: "",
|
|
1908
|
-
onError: (event) => {
|
|
1909
|
-
event.currentTarget.hidden = true;
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
) : null
|
|
1913
|
-
] }) : step.kind === "search" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
|
|
3188
|
+
"aria-hidden": "true"
|
|
1914
3189
|
}
|
|
1915
3190
|
),
|
|
1916
3191
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
|
|
1917
3192
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-heading", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stepLabel(step, brandLabel) }) }),
|
|
1918
|
-
detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
|
|
3193
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null,
|
|
3194
|
+
delegated && step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__delegation", children: [
|
|
3195
|
+
"Delegated ",
|
|
3196
|
+
delegationCount,
|
|
3197
|
+
" ",
|
|
3198
|
+
delegationCount === 1 ? "task" : "tasks"
|
|
3199
|
+
] }) : null,
|
|
3200
|
+
step.state === "error" && onRetryStep ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3201
|
+
"button",
|
|
3202
|
+
{
|
|
3203
|
+
type: "button",
|
|
3204
|
+
className: "agent-activity-bubble__step-retry",
|
|
3205
|
+
onClick: () => onRetryStep(step),
|
|
3206
|
+
children: "Retry"
|
|
3207
|
+
}
|
|
3208
|
+
) : null
|
|
1919
3209
|
] })
|
|
1920
3210
|
]
|
|
1921
3211
|
},
|
|
@@ -1930,59 +3220,190 @@ function AgentActivityBubble({
|
|
|
1930
3220
|
var import_react6 = require("react");
|
|
1931
3221
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1932
3222
|
function SendIcon() {
|
|
1933
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3223
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3224
|
+
"path",
|
|
3225
|
+
{
|
|
3226
|
+
d: "M8 12V4M8 4l-3 3M8 4l3 3",
|
|
3227
|
+
stroke: "currentColor",
|
|
3228
|
+
strokeWidth: "1.5",
|
|
3229
|
+
strokeLinecap: "round",
|
|
3230
|
+
strokeLinejoin: "round"
|
|
3231
|
+
}
|
|
3232
|
+
) });
|
|
3233
|
+
}
|
|
3234
|
+
function emptyValues(form) {
|
|
3235
|
+
const values = {};
|
|
3236
|
+
for (const field of form?.fields ?? []) values[field.id] = "";
|
|
3237
|
+
return values;
|
|
1934
3238
|
}
|
|
1935
3239
|
function Composer({
|
|
1936
3240
|
disabled = false,
|
|
1937
3241
|
placeholder = "Ask anything\u2026",
|
|
1938
3242
|
variant = "default",
|
|
3243
|
+
form = null,
|
|
1939
3244
|
onSubmit
|
|
1940
3245
|
}) {
|
|
1941
3246
|
const [value, setValue] = (0, import_react6.useState)("");
|
|
3247
|
+
const [values, setValues] = (0, import_react6.useState)(
|
|
3248
|
+
() => emptyValues(form)
|
|
3249
|
+
);
|
|
3250
|
+
const [blurred, setBlurred] = (0, import_react6.useState)({});
|
|
1942
3251
|
const inputRef = (0, import_react6.useRef)(null);
|
|
1943
|
-
|
|
3252
|
+
const firstFieldRef = (0, import_react6.useRef)(null);
|
|
3253
|
+
const formId = (0, import_react6.useId)();
|
|
3254
|
+
const activeForm = form;
|
|
3255
|
+
const canSendForm = activeForm ? isComposerFormComplete(activeForm, values) : Boolean(value.trim());
|
|
3256
|
+
(0, import_react6.useEffect)(() => {
|
|
3257
|
+
setValues(emptyValues(form));
|
|
3258
|
+
setBlurred({});
|
|
3259
|
+
}, [form?.id]);
|
|
3260
|
+
(0, import_react6.useEffect)(() => {
|
|
3261
|
+
if (activeForm) firstFieldRef.current?.focus();
|
|
3262
|
+
}, [activeForm?.id]);
|
|
3263
|
+
function submitChat() {
|
|
1944
3264
|
const trimmed = value.trim();
|
|
1945
3265
|
if (!trimmed || disabled) return;
|
|
1946
3266
|
onSubmit?.(trimmed);
|
|
1947
3267
|
setValue("");
|
|
1948
3268
|
inputRef.current?.focus();
|
|
1949
3269
|
}
|
|
3270
|
+
function submitForm() {
|
|
3271
|
+
if (!activeForm || disabled || !canSendForm) return;
|
|
3272
|
+
onSubmit?.(formatComposerFormMessage(activeForm, values));
|
|
3273
|
+
setValues(emptyValues(activeForm));
|
|
3274
|
+
setBlurred({});
|
|
3275
|
+
}
|
|
1950
3276
|
function handleSubmit(event) {
|
|
1951
3277
|
event.preventDefault();
|
|
1952
|
-
|
|
3278
|
+
if (activeForm) submitForm();
|
|
3279
|
+
else submitChat();
|
|
1953
3280
|
}
|
|
1954
|
-
function
|
|
3281
|
+
function handleChatKeyDown(event) {
|
|
1955
3282
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
1956
3283
|
event.preventDefault();
|
|
1957
|
-
|
|
3284
|
+
submitChat();
|
|
1958
3285
|
}
|
|
1959
3286
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
{
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
3287
|
+
function handleFormKeyDown(event) {
|
|
3288
|
+
const target = event.target;
|
|
3289
|
+
const isTextarea = Boolean(target && "tagName" in target && target.tagName === "TEXTAREA");
|
|
3290
|
+
if (event.key === "Enter" && !event.shiftKey && !isTextarea) {
|
|
3291
|
+
event.preventDefault();
|
|
3292
|
+
submitForm();
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3296
|
+
"form",
|
|
3297
|
+
{
|
|
3298
|
+
className: [
|
|
3299
|
+
"composer",
|
|
3300
|
+
variant === "dock" ? "composer--dock" : "",
|
|
3301
|
+
activeForm ? "composer--form" : ""
|
|
3302
|
+
].filter(Boolean).join(" "),
|
|
3303
|
+
onSubmit: handleSubmit,
|
|
3304
|
+
children: activeForm ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__sheet", role: "group", "aria-label": "Required details", children: [
|
|
3305
|
+
activeForm.fields.map((field, index) => {
|
|
3306
|
+
const fieldId = `${formId}-${field.id}`;
|
|
3307
|
+
const invalid = Boolean(blurred[field.id]) && !isValidComposerFieldValue(field, values[field.id] ?? "");
|
|
3308
|
+
const controlProps = {
|
|
3309
|
+
id: fieldId,
|
|
3310
|
+
name: field.id,
|
|
3311
|
+
disabled,
|
|
3312
|
+
required: field.required,
|
|
3313
|
+
autoComplete: field.autocomplete,
|
|
3314
|
+
placeholder: field.placeholder,
|
|
3315
|
+
spellCheck: false,
|
|
3316
|
+
value: values[field.id] ?? "",
|
|
3317
|
+
"aria-invalid": invalid || void 0,
|
|
3318
|
+
"aria-describedby": invalid ? `${fieldId}-error` : void 0,
|
|
3319
|
+
onBlur: () => setBlurred((current) => ({ ...current, [field.id]: true })),
|
|
3320
|
+
onChange: (event) => {
|
|
3321
|
+
const next = readComposerControlValue(event);
|
|
3322
|
+
setValues((current) => ({
|
|
3323
|
+
...current,
|
|
3324
|
+
[field.id]: next
|
|
3325
|
+
}));
|
|
3326
|
+
},
|
|
3327
|
+
onKeyDown: handleFormKeyDown
|
|
3328
|
+
};
|
|
3329
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
3330
|
+
"div",
|
|
3331
|
+
{
|
|
3332
|
+
className: [
|
|
3333
|
+
"composer__row",
|
|
3334
|
+
field.kind === "textarea" ? "composer__row--grow" : ""
|
|
3335
|
+
].filter(Boolean).join(" "),
|
|
3336
|
+
children: [
|
|
3337
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "composer__label", htmlFor: fieldId, children: [
|
|
3338
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "composer__sr-only", children: field.label }),
|
|
3339
|
+
field.kind === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3340
|
+
"textarea",
|
|
3341
|
+
{
|
|
3342
|
+
...controlProps,
|
|
3343
|
+
ref: index === 0 ? (node) => {
|
|
3344
|
+
firstFieldRef.current = node;
|
|
3345
|
+
} : void 0,
|
|
3346
|
+
className: "composer__control composer__control--area",
|
|
3347
|
+
rows: 3
|
|
3348
|
+
}
|
|
3349
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3350
|
+
"input",
|
|
3351
|
+
{
|
|
3352
|
+
...controlProps,
|
|
3353
|
+
ref: index === 0 ? (node) => {
|
|
3354
|
+
firstFieldRef.current = node;
|
|
3355
|
+
} : void 0,
|
|
3356
|
+
className: "composer__control",
|
|
3357
|
+
type: field.kind,
|
|
3358
|
+
inputMode: field.kind === "tel" ? "tel" : void 0
|
|
3359
|
+
}
|
|
3360
|
+
)
|
|
3361
|
+
] }),
|
|
3362
|
+
invalid ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "composer__error", id: `${fieldId}-error`, children: field.kind === "email" ? "Enter a valid email to continue." : `Add your ${field.label.toLowerCase()} to continue.` }) : null
|
|
3363
|
+
]
|
|
3364
|
+
},
|
|
3365
|
+
field.id
|
|
3366
|
+
);
|
|
3367
|
+
}),
|
|
3368
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "composer__toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3369
|
+
"button",
|
|
3370
|
+
{
|
|
3371
|
+
type: "submit",
|
|
3372
|
+
className: "composer__send",
|
|
3373
|
+
disabled: disabled || !canSendForm,
|
|
3374
|
+
"aria-label": "Send details",
|
|
3375
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
3376
|
+
}
|
|
3377
|
+
) })
|
|
3378
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__field", children: [
|
|
3379
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3380
|
+
"textarea",
|
|
3381
|
+
{
|
|
3382
|
+
ref: inputRef,
|
|
3383
|
+
className: "composer__input",
|
|
3384
|
+
rows: 1,
|
|
3385
|
+
value,
|
|
3386
|
+
placeholder,
|
|
3387
|
+
disabled,
|
|
3388
|
+
spellCheck: false,
|
|
3389
|
+
"aria-label": "Message",
|
|
3390
|
+
onChange: (event) => setValue(event.target.value),
|
|
3391
|
+
onKeyDown: handleChatKeyDown
|
|
3392
|
+
}
|
|
3393
|
+
),
|
|
3394
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3395
|
+
"button",
|
|
3396
|
+
{
|
|
3397
|
+
type: "submit",
|
|
3398
|
+
className: "composer__send",
|
|
3399
|
+
disabled: disabled || !value.trim(),
|
|
3400
|
+
"aria-label": "Send message",
|
|
3401
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
3402
|
+
}
|
|
3403
|
+
)
|
|
3404
|
+
] })
|
|
3405
|
+
}
|
|
3406
|
+
);
|
|
1986
3407
|
}
|
|
1987
3408
|
|
|
1988
3409
|
// src/react/components/FollowUpChips/FollowUpChips.tsx
|
|
@@ -2030,9 +3451,15 @@ var import_react8 = require("react");
|
|
|
2030
3451
|
// src/react/components/BookingCard/BookingCard.tsx
|
|
2031
3452
|
var import_react7 = require("react");
|
|
2032
3453
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
3454
|
+
var BOOKING_STEPS = [
|
|
3455
|
+
{ id: "date", label: "Date" },
|
|
3456
|
+
{ id: "time", label: "Time" },
|
|
3457
|
+
{ id: "details", label: "Details" }
|
|
3458
|
+
];
|
|
2033
3459
|
function monthFromKey(key) {
|
|
2034
3460
|
const [year, month] = key.split("-").map(Number);
|
|
2035
|
-
if (!year || !month)
|
|
3461
|
+
if (!year || !month)
|
|
3462
|
+
return { year: (/* @__PURE__ */ new Date()).getFullYear(), month: (/* @__PURE__ */ new Date()).getMonth() };
|
|
2036
3463
|
return { year, month: month - 1 };
|
|
2037
3464
|
}
|
|
2038
3465
|
function dateKeyFromParts(year, month, day) {
|
|
@@ -2065,6 +3492,7 @@ function BookingCard({
|
|
|
2065
3492
|
const [startTime, setStartTime] = (0, import_react7.useState)("");
|
|
2066
3493
|
const [name, setName] = (0, import_react7.useState)("");
|
|
2067
3494
|
const [email, setEmail] = (0, import_react7.useState)("");
|
|
3495
|
+
const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
|
|
2068
3496
|
const slots = (0, import_react7.useMemo)(
|
|
2069
3497
|
() => bookingSlotsForEventType(offer.slots, eventTypeUri),
|
|
2070
3498
|
[eventTypeUri, offer.slots]
|
|
@@ -2085,14 +3513,18 @@ function BookingCard({
|
|
|
2085
3513
|
setSelectedDate("");
|
|
2086
3514
|
setStartTime("");
|
|
2087
3515
|
setVisibleMonth(
|
|
2088
|
-
firstAvailableBookingMonth(
|
|
3516
|
+
firstAvailableBookingMonth(
|
|
3517
|
+
bookingSlotsForEventType(offer.slots, nextType)
|
|
3518
|
+
)
|
|
2089
3519
|
);
|
|
2090
3520
|
}
|
|
2091
3521
|
const daySlots = (0, import_react7.useMemo)(
|
|
2092
3522
|
() => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
|
|
2093
3523
|
[selectedDate, slots]
|
|
2094
3524
|
);
|
|
2095
|
-
const selectedType = offer.eventTypes.find(
|
|
3525
|
+
const selectedType = offer.eventTypes.find(
|
|
3526
|
+
(item) => item.uri === eventTypeUri
|
|
3527
|
+
);
|
|
2096
3528
|
const selectedSample = availableByDate.get(selectedDate) ?? startTime;
|
|
2097
3529
|
const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
|
|
2098
3530
|
const weekdays = (0, import_react7.useMemo)(() => weekdayLabels(), []);
|
|
@@ -2138,24 +3570,47 @@ function BookingCard({
|
|
|
2138
3570
|
});
|
|
2139
3571
|
}
|
|
2140
3572
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { className: "booking-card", "aria-label": "Book a meeting", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { className: "booking-card__form", onSubmit: handleSubmit, children: [
|
|
3573
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ol", { className: "booking-card__steps", "aria-label": "Booking steps", children: BOOKING_STEPS.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3574
|
+
"li",
|
|
3575
|
+
{
|
|
3576
|
+
className: [
|
|
3577
|
+
"booking-card__step-indicator",
|
|
3578
|
+
index === stepIndex ? "booking-card__step-indicator--active" : "",
|
|
3579
|
+
index < stepIndex ? "booking-card__step-indicator--complete" : ""
|
|
3580
|
+
].filter(Boolean).join(" "),
|
|
3581
|
+
"aria-current": index === stepIndex ? "step" : void 0,
|
|
3582
|
+
children: [
|
|
3583
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", children: index + 1 }),
|
|
3584
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: item.label })
|
|
3585
|
+
]
|
|
3586
|
+
},
|
|
3587
|
+
item.id
|
|
3588
|
+
)) }),
|
|
2141
3589
|
step === "date" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2142
3590
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: selectedType?.name || "Pick a date" }),
|
|
2143
3591
|
timeZone ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "booking-card__tz", children: [
|
|
2144
3592
|
"Times in ",
|
|
2145
3593
|
timeZone
|
|
2146
3594
|
] }) : null,
|
|
2147
|
-
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
"
|
|
2151
|
-
{
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
3595
|
+
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3596
|
+
"label",
|
|
3597
|
+
{
|
|
3598
|
+
className: "booking-card__field",
|
|
3599
|
+
htmlFor: `${fieldId}-type`,
|
|
3600
|
+
children: [
|
|
3601
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Meeting" }),
|
|
3602
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3603
|
+
"select",
|
|
3604
|
+
{
|
|
3605
|
+
id: `${fieldId}-type`,
|
|
3606
|
+
value: eventTypeUri,
|
|
3607
|
+
onChange: (event) => selectEventType(event.target.value),
|
|
3608
|
+
children: offer.eventTypes.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: item.uri, children: item.name }, item.uri))
|
|
3609
|
+
}
|
|
3610
|
+
)
|
|
3611
|
+
]
|
|
3612
|
+
}
|
|
3613
|
+
) : null,
|
|
2159
3614
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__month", children: [
|
|
2160
3615
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2161
3616
|
"button",
|
|
@@ -2182,29 +3637,44 @@ function BookingCard({
|
|
|
2182
3637
|
)
|
|
2183
3638
|
] }),
|
|
2184
3639
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "booking-card__weekdays", children: weekdays.map((label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: label }, label)) }),
|
|
2185
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("
|
|
2186
|
-
|
|
2187
|
-
|
|
3640
|
+
offer.slots.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__loading", role: "status", children: "Finding available times\u2026" }) : null,
|
|
3641
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3642
|
+
"div",
|
|
3643
|
+
{
|
|
3644
|
+
className: "booking-card__calendar",
|
|
3645
|
+
role: "grid",
|
|
3646
|
+
"aria-label": "Available dates",
|
|
3647
|
+
children: cells.map((cell, index) => {
|
|
3648
|
+
if (!cell) {
|
|
3649
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3650
|
+
"span",
|
|
3651
|
+
{
|
|
3652
|
+
className: "booking-card__day"
|
|
3653
|
+
},
|
|
3654
|
+
`empty-${index}`
|
|
3655
|
+
);
|
|
3656
|
+
}
|
|
3657
|
+
const available = availableByDate.has(cell.key);
|
|
3658
|
+
const selected = cell.key === selectedDate;
|
|
3659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3660
|
+
"button",
|
|
3661
|
+
{
|
|
3662
|
+
type: "button",
|
|
3663
|
+
className: [
|
|
3664
|
+
"booking-card__day",
|
|
3665
|
+
available ? "booking-card__day--available" : "",
|
|
3666
|
+
selected ? "booking-card__day--selected" : ""
|
|
3667
|
+
].filter(Boolean).join(" "),
|
|
3668
|
+
disabled: !available,
|
|
3669
|
+
"aria-pressed": selected,
|
|
3670
|
+
onClick: () => selectDate(cell.key),
|
|
3671
|
+
children: cell.day
|
|
3672
|
+
},
|
|
3673
|
+
cell.key
|
|
3674
|
+
);
|
|
3675
|
+
})
|
|
2188
3676
|
}
|
|
2189
|
-
|
|
2190
|
-
const selected = cell.key === selectedDate;
|
|
2191
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2192
|
-
"button",
|
|
2193
|
-
{
|
|
2194
|
-
type: "button",
|
|
2195
|
-
className: [
|
|
2196
|
-
"booking-card__day",
|
|
2197
|
-
available ? "booking-card__day--available" : "",
|
|
2198
|
-
selected ? "booking-card__day--selected" : ""
|
|
2199
|
-
].filter(Boolean).join(" "),
|
|
2200
|
-
disabled: !available,
|
|
2201
|
-
"aria-pressed": selected,
|
|
2202
|
-
onClick: () => selectDate(cell.key),
|
|
2203
|
-
children: cell.day
|
|
2204
|
-
},
|
|
2205
|
-
cell.key
|
|
2206
|
-
);
|
|
2207
|
-
}) })
|
|
3677
|
+
)
|
|
2208
3678
|
] }, "date") : null,
|
|
2209
3679
|
step === "time" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2210
3680
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step-bar", children: [
|
|
@@ -2256,35 +3726,57 @@ function BookingCard({
|
|
|
2256
3726
|
] })
|
|
2257
3727
|
] }),
|
|
2258
3728
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__identity", children: [
|
|
2259
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
"
|
|
2263
|
-
{
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
|
|
3729
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3730
|
+
"label",
|
|
3731
|
+
{
|
|
3732
|
+
className: "booking-card__field",
|
|
3733
|
+
htmlFor: `${fieldId}-name`,
|
|
3734
|
+
children: [
|
|
3735
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Name" }),
|
|
3736
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3737
|
+
"input",
|
|
3738
|
+
{
|
|
3739
|
+
id: `${fieldId}-name`,
|
|
3740
|
+
autoComplete: "name",
|
|
3741
|
+
value: name,
|
|
3742
|
+
onChange: (event) => setName(event.target.value),
|
|
3743
|
+
required: true
|
|
3744
|
+
}
|
|
3745
|
+
)
|
|
3746
|
+
]
|
|
3747
|
+
}
|
|
3748
|
+
),
|
|
3749
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3750
|
+
"label",
|
|
3751
|
+
{
|
|
3752
|
+
className: "booking-card__field",
|
|
3753
|
+
htmlFor: `${fieldId}-email`,
|
|
3754
|
+
children: [
|
|
3755
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Email" }),
|
|
3756
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3757
|
+
"input",
|
|
3758
|
+
{
|
|
3759
|
+
id: `${fieldId}-email`,
|
|
3760
|
+
type: "email",
|
|
3761
|
+
autoComplete: "email",
|
|
3762
|
+
value: email,
|
|
3763
|
+
onChange: (event) => setEmail(event.target.value),
|
|
3764
|
+
required: true
|
|
3765
|
+
}
|
|
3766
|
+
)
|
|
3767
|
+
]
|
|
3768
|
+
}
|
|
3769
|
+
)
|
|
2286
3770
|
] }),
|
|
2287
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3771
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3772
|
+
"button",
|
|
3773
|
+
{
|
|
3774
|
+
type: "submit",
|
|
3775
|
+
className: "booking-card__submit",
|
|
3776
|
+
disabled: !name.trim() || !email.trim(),
|
|
3777
|
+
children: "Book this time"
|
|
3778
|
+
}
|
|
3779
|
+
)
|
|
2288
3780
|
] }, "details") : null
|
|
2289
3781
|
] }) });
|
|
2290
3782
|
}
|
|
@@ -2293,6 +3785,43 @@ function BookingCard({
|
|
|
2293
3785
|
var import_streamdown = require("streamdown");
|
|
2294
3786
|
var import_styles = require("streamdown/styles.css");
|
|
2295
3787
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3788
|
+
function normalizeDedupeText(text) {
|
|
3789
|
+
return text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
3790
|
+
}
|
|
3791
|
+
function paragraphsAreNearDuplicates(first, second) {
|
|
3792
|
+
const left = normalizeDedupeText(first);
|
|
3793
|
+
const right = normalizeDedupeText(second);
|
|
3794
|
+
if (left.length < 40 || right.length < 40) return false;
|
|
3795
|
+
if (left === right) return true;
|
|
3796
|
+
const shorter = left.length <= right.length ? left : right;
|
|
3797
|
+
const longer = left.length <= right.length ? right : left;
|
|
3798
|
+
return longer.startsWith(
|
|
3799
|
+
shorter.slice(0, Math.floor(shorter.length * 0.85))
|
|
3800
|
+
);
|
|
3801
|
+
}
|
|
3802
|
+
function paragraphsShareOpening(first, second) {
|
|
3803
|
+
const opening = first.split("\n")[0]?.trim();
|
|
3804
|
+
if (!opening || opening.length < 20) return false;
|
|
3805
|
+
return second.trim().startsWith(opening);
|
|
3806
|
+
}
|
|
3807
|
+
function collapseRepeatedText(text) {
|
|
3808
|
+
const trimmed = text.trim();
|
|
3809
|
+
if (trimmed.length < 40) return trimmed;
|
|
3810
|
+
const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
|
|
3811
|
+
if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
|
|
3812
|
+
return paragraphs[0];
|
|
3813
|
+
}
|
|
3814
|
+
if (paragraphs.length >= 2 && paragraphs.length % 2 === 0) {
|
|
3815
|
+
const mid2 = paragraphs.length / 2;
|
|
3816
|
+
const first = paragraphs.slice(0, mid2).join("\n\n");
|
|
3817
|
+
const second = paragraphs.slice(mid2).join("\n\n");
|
|
3818
|
+
if (first === second) return first;
|
|
3819
|
+
}
|
|
3820
|
+
const mid = Math.floor(trimmed.length / 2);
|
|
3821
|
+
const left = trimmed.slice(0, mid).trim();
|
|
3822
|
+
const right = trimmed.slice(mid).trim();
|
|
3823
|
+
return left.length >= 20 && left === right ? left : trimmed;
|
|
3824
|
+
}
|
|
2296
3825
|
function MessageBubble({
|
|
2297
3826
|
message,
|
|
2298
3827
|
brandLogoUrl,
|
|
@@ -2309,24 +3838,41 @@ function MessageBubble({
|
|
|
2309
3838
|
const offers = offer ? [offer] : extractedOffers;
|
|
2310
3839
|
const visibleText = hideToolCardFences(message.text);
|
|
2311
3840
|
const isStreaming = message.role === "agent" && Boolean(message.streaming);
|
|
2312
|
-
const displayText =
|
|
3841
|
+
const displayText = collapseRepeatedText(
|
|
3842
|
+
offers.length > 0 ? sanitizeBookingOfferCopy(visibleText) : looksLikeBookingAvailabilityDump(visibleText) ? sanitizeBookingOfferCopy(visibleText) : visibleText || (isStreaming ? "" : message.text)
|
|
3843
|
+
);
|
|
2313
3844
|
if (message.role === "visitor") {
|
|
2314
3845
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "message-bubble__text", children: message.text }) });
|
|
2315
3846
|
}
|
|
2316
|
-
const
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
3847
|
+
const citations = message.citations ?? [];
|
|
3848
|
+
const agentText = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__text", children: [
|
|
3849
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3850
|
+
import_streamdown.Streamdown,
|
|
3851
|
+
{
|
|
3852
|
+
animated: isStreaming,
|
|
3853
|
+
caret: "circle",
|
|
3854
|
+
className: "message-bubble__markdown",
|
|
3855
|
+
controls: false,
|
|
3856
|
+
isAnimating: isStreaming,
|
|
3857
|
+
linkSafety: { enabled: false },
|
|
3858
|
+
mode: isStreaming ? "streaming" : "static",
|
|
3859
|
+
skipHtml: true,
|
|
3860
|
+
children: displayText
|
|
3861
|
+
}
|
|
3862
|
+
),
|
|
3863
|
+
citations.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "message-bubble__sources", "aria-label": "Sources", children: citations.map((citation) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
3864
|
+
"a",
|
|
3865
|
+
{
|
|
3866
|
+
href: citation.url,
|
|
3867
|
+
target: "_blank",
|
|
3868
|
+
rel: "noreferrer",
|
|
3869
|
+
children: [
|
|
3870
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__source-icon", "aria-hidden": "true", children: "\u25A6" }),
|
|
3871
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: citation.label })
|
|
3872
|
+
]
|
|
3873
|
+
}
|
|
3874
|
+
) }, citation.id)) }) : null
|
|
3875
|
+
] });
|
|
2330
3876
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
|
|
2331
3877
|
displayText ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__agent-row", children: [
|
|
2332
3878
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
@@ -2352,10 +3898,262 @@ function MessageBubble({
|
|
|
2352
3898
|
] });
|
|
2353
3899
|
}
|
|
2354
3900
|
|
|
2355
|
-
// src/react/components/
|
|
3901
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3902
|
+
var import_react9 = require("react");
|
|
3903
|
+
|
|
3904
|
+
// src/react/components/ConfirmationCard/ConfirmationCard.tsx
|
|
2356
3905
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3906
|
+
function ConfirmationCard({
|
|
3907
|
+
disabled = false,
|
|
3908
|
+
request,
|
|
3909
|
+
onRespond
|
|
3910
|
+
}) {
|
|
3911
|
+
const options = request.options ?? [];
|
|
3912
|
+
const heading = request.kind === "tool-approval" ? "Confirm this action" : request.prompt;
|
|
3913
|
+
const prompt = request.kind === "tool-approval" ? request.prompt : void 0;
|
|
3914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3915
|
+
"section",
|
|
3916
|
+
{
|
|
3917
|
+
className: "confirmation-card",
|
|
3918
|
+
"aria-labelledby": `confirmation-${request.requestId}`,
|
|
3919
|
+
children: [
|
|
3920
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "confirmation-card__heading", children: [
|
|
3921
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { id: `confirmation-${request.requestId}`, children: heading }),
|
|
3922
|
+
prompt ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: prompt }) : null
|
|
3923
|
+
] }),
|
|
3924
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "confirmation-card__actions", children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3925
|
+
"button",
|
|
3926
|
+
{
|
|
3927
|
+
type: "button",
|
|
3928
|
+
className: `confirmation-card__action confirmation-card__action--${option.style ?? "default"}`,
|
|
3929
|
+
disabled,
|
|
3930
|
+
onClick: () => onRespond?.({
|
|
3931
|
+
requestId: request.requestId,
|
|
3932
|
+
optionId: option.id
|
|
3933
|
+
}),
|
|
3934
|
+
children: option.label
|
|
3935
|
+
},
|
|
3936
|
+
option.id
|
|
3937
|
+
)) })
|
|
3938
|
+
]
|
|
3939
|
+
}
|
|
3940
|
+
);
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3944
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3945
|
+
function HumanInputCard({
|
|
3946
|
+
disabled = false,
|
|
3947
|
+
request,
|
|
3948
|
+
onRespond
|
|
3949
|
+
}) {
|
|
3950
|
+
const [text, setText] = (0, import_react9.useState)("");
|
|
3951
|
+
const options = request.options ?? [];
|
|
3952
|
+
const showText = request.display === "text" || request.allowFreeform && options.length === 0;
|
|
3953
|
+
if (options.length > 0) {
|
|
3954
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3955
|
+
ConfirmationCard,
|
|
3956
|
+
{
|
|
3957
|
+
disabled,
|
|
3958
|
+
request,
|
|
3959
|
+
onRespond
|
|
3960
|
+
}
|
|
3961
|
+
);
|
|
3962
|
+
}
|
|
3963
|
+
function submitText(event) {
|
|
3964
|
+
event.preventDefault();
|
|
3965
|
+
const value = text.trim();
|
|
3966
|
+
if (!value || disabled) return;
|
|
3967
|
+
onRespond?.({ requestId: request.requestId, text: value });
|
|
3968
|
+
}
|
|
3969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3970
|
+
"section",
|
|
3971
|
+
{
|
|
3972
|
+
className: "human-input-card",
|
|
3973
|
+
"aria-labelledby": `human-input-${request.requestId}`,
|
|
3974
|
+
children: [
|
|
3975
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "human-input-card__heading", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { id: `human-input-${request.requestId}`, children: request.prompt }) }),
|
|
3976
|
+
showText ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("form", { onSubmit: submitText, children: [
|
|
3977
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: `human-input-text-${request.requestId}`, children: "Response" }),
|
|
3978
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
3979
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3980
|
+
"input",
|
|
3981
|
+
{
|
|
3982
|
+
id: `human-input-text-${request.requestId}`,
|
|
3983
|
+
value: text,
|
|
3984
|
+
disabled,
|
|
3985
|
+
onChange: (event) => setText(event.target.value)
|
|
3986
|
+
}
|
|
3987
|
+
),
|
|
3988
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "submit", disabled: disabled || !text.trim(), children: "Send" })
|
|
3989
|
+
] })
|
|
3990
|
+
] }) : null,
|
|
3991
|
+
!showText && options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "human-input-card__unavailable", role: "status", children: "This request can\u2019t be answered here." }) : null
|
|
3992
|
+
]
|
|
3993
|
+
}
|
|
3994
|
+
);
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3997
|
+
// src/react/components/CollectionResultCard/CollectionResultCard.tsx
|
|
3998
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
3999
|
+
function CollectionResultCard({
|
|
4000
|
+
result
|
|
4001
|
+
}) {
|
|
4002
|
+
const empty = result.items.length === 0;
|
|
4003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4004
|
+
"section",
|
|
4005
|
+
{
|
|
4006
|
+
className: `collection-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4007
|
+
"aria-label": result.title,
|
|
4008
|
+
children: [
|
|
4009
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4010
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4011
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("strong", { children: result.title })
|
|
4012
|
+
] }),
|
|
4013
|
+
empty ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "collection-result-card__empty", children: "No matching record for the email you shared." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "collection-result-card__list", children: result.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { children: [
|
|
4014
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "collection-result-card__item-title", children: item.title }),
|
|
4015
|
+
item.description ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: item.description }) : null,
|
|
4016
|
+
item.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dl", { children: item.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
4017
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dt", { children: detail.label }),
|
|
4018
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dd", { children: detail.value })
|
|
4019
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4020
|
+
item.href ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
|
|
4021
|
+
] }, item.title)) })
|
|
4022
|
+
]
|
|
4023
|
+
}
|
|
4024
|
+
);
|
|
4025
|
+
}
|
|
4026
|
+
|
|
4027
|
+
// src/react/components/EntityResultCard/EntityResultCard.tsx
|
|
4028
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
4029
|
+
function EntityResultCard({
|
|
4030
|
+
result
|
|
4031
|
+
}) {
|
|
4032
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
4033
|
+
"section",
|
|
4034
|
+
{
|
|
4035
|
+
className: `entity-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4036
|
+
"aria-label": result.title,
|
|
4037
|
+
children: [
|
|
4038
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4039
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4040
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: result.title })
|
|
4041
|
+
] }),
|
|
4042
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { children: result.description }) : null,
|
|
4043
|
+
result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
4044
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dt", { children: detail.label }),
|
|
4045
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dd", { children: detail.value })
|
|
4046
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4047
|
+
result.links?.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
4048
|
+
"a",
|
|
4049
|
+
{
|
|
4050
|
+
href: link.href,
|
|
4051
|
+
target: "_blank",
|
|
4052
|
+
rel: "noreferrer",
|
|
4053
|
+
children: link.label
|
|
4054
|
+
},
|
|
4055
|
+
link.href
|
|
4056
|
+
)) }) : null
|
|
4057
|
+
]
|
|
4058
|
+
}
|
|
4059
|
+
);
|
|
4060
|
+
}
|
|
4061
|
+
|
|
4062
|
+
// src/react/components/SignatureResultCard/SignatureResultCard.tsx
|
|
4063
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4064
|
+
function SignatureResultCard({
|
|
4065
|
+
result
|
|
4066
|
+
}) {
|
|
4067
|
+
const primaryLink = result.links?.[0];
|
|
4068
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
4069
|
+
"section",
|
|
4070
|
+
{
|
|
4071
|
+
className: `signature-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4072
|
+
"aria-label": result.title,
|
|
4073
|
+
children: [
|
|
4074
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4075
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4076
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: result.title })
|
|
4077
|
+
] }),
|
|
4078
|
+
result.statusLabel ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
|
|
4079
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: result.description }) : null,
|
|
4080
|
+
primaryLink ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4081
|
+
"a",
|
|
4082
|
+
{
|
|
4083
|
+
className: "signature-result-card__cta",
|
|
4084
|
+
href: primaryLink.href,
|
|
4085
|
+
target: "_blank",
|
|
4086
|
+
rel: "noreferrer",
|
|
4087
|
+
children: primaryLink.label
|
|
4088
|
+
}
|
|
4089
|
+
) : null
|
|
4090
|
+
]
|
|
4091
|
+
}
|
|
4092
|
+
);
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
// src/react/components/ToolResultCard/ToolResultCard.tsx
|
|
4096
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
4097
|
+
function ToolResultCard({
|
|
4098
|
+
result
|
|
4099
|
+
}) {
|
|
4100
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
4101
|
+
"section",
|
|
4102
|
+
{
|
|
4103
|
+
className: `tool-result-card tool-result-card--${result.status}`,
|
|
4104
|
+
"aria-label": result.title,
|
|
4105
|
+
children: [
|
|
4106
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4107
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4108
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: result.title })
|
|
4109
|
+
] }),
|
|
4110
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: result.description }) : null,
|
|
4111
|
+
result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
4112
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dt", { children: detail.label }),
|
|
4113
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dd", { children: detail.value })
|
|
4114
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4115
|
+
result.links?.length ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
4116
|
+
"a",
|
|
4117
|
+
{
|
|
4118
|
+
href: link.href,
|
|
4119
|
+
target: "_blank",
|
|
4120
|
+
rel: "noreferrer",
|
|
4121
|
+
children: link.label
|
|
4122
|
+
},
|
|
4123
|
+
link.href
|
|
4124
|
+
)) }) : null
|
|
4125
|
+
]
|
|
4126
|
+
}
|
|
4127
|
+
);
|
|
4128
|
+
}
|
|
4129
|
+
|
|
4130
|
+
// src/react/components/VisitorToolResultView/VisitorToolResultView.tsx
|
|
4131
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
4132
|
+
function VisitorToolResultView({
|
|
4133
|
+
result
|
|
4134
|
+
}) {
|
|
4135
|
+
if (result.kind === "entity") {
|
|
4136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EntityResultCard, { result });
|
|
4137
|
+
}
|
|
4138
|
+
if (result.kind === "collection") {
|
|
4139
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CollectionResultCard, { result });
|
|
4140
|
+
}
|
|
4141
|
+
if (result.kind === "signature") {
|
|
4142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SignatureResultCard, { result });
|
|
4143
|
+
}
|
|
4144
|
+
if (result.kind === "summary") {
|
|
4145
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ToolResultCard, { result });
|
|
4146
|
+
}
|
|
4147
|
+
return null;
|
|
4148
|
+
}
|
|
4149
|
+
function isRenderableVisitorToolResult(result) {
|
|
4150
|
+
return result.kind === "summary" || result.kind === "entity" || result.kind === "collection" || result.kind === "signature";
|
|
4151
|
+
}
|
|
4152
|
+
|
|
4153
|
+
// src/react/components/AgentRail/AgentRail.tsx
|
|
4154
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2357
4155
|
function MinimizeIcon() {
|
|
2358
|
-
return /* @__PURE__ */ (0,
|
|
4156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2359
4157
|
"path",
|
|
2360
4158
|
{
|
|
2361
4159
|
d: "M3.5 8h9",
|
|
@@ -2366,7 +4164,7 @@ function MinimizeIcon() {
|
|
|
2366
4164
|
) });
|
|
2367
4165
|
}
|
|
2368
4166
|
function CloseIcon() {
|
|
2369
|
-
return /* @__PURE__ */ (0,
|
|
4167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2370
4168
|
"path",
|
|
2371
4169
|
{
|
|
2372
4170
|
d: "M4 4l8 8M12 4l-8 8",
|
|
@@ -2377,7 +4175,7 @@ function CloseIcon() {
|
|
|
2377
4175
|
) });
|
|
2378
4176
|
}
|
|
2379
4177
|
function NewChatIcon() {
|
|
2380
|
-
return /* @__PURE__ */ (0,
|
|
4178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2381
4179
|
"path",
|
|
2382
4180
|
{
|
|
2383
4181
|
d: "M9.5 3.5h3v3M12.25 3.75 8 8M7 4H4.5A1.5 1.5 0 0 0 3 5.5v6A1.5 1.5 0 0 0 4.5 13h6a1.5 1.5 0 0 0 1.5-1.5V9",
|
|
@@ -2389,7 +4187,7 @@ function NewChatIcon() {
|
|
|
2389
4187
|
) });
|
|
2390
4188
|
}
|
|
2391
4189
|
function ExpandIcon() {
|
|
2392
|
-
return /* @__PURE__ */ (0,
|
|
4190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2393
4191
|
"path",
|
|
2394
4192
|
{
|
|
2395
4193
|
d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
|
|
@@ -2401,7 +4199,7 @@ function ExpandIcon() {
|
|
|
2401
4199
|
) });
|
|
2402
4200
|
}
|
|
2403
4201
|
function RestoreIcon() {
|
|
2404
|
-
return /* @__PURE__ */ (0,
|
|
4202
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2405
4203
|
"path",
|
|
2406
4204
|
{
|
|
2407
4205
|
d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
|
|
@@ -2429,28 +4227,29 @@ function AgentRail({
|
|
|
2429
4227
|
onRetry,
|
|
2430
4228
|
onSubmit,
|
|
2431
4229
|
onFollowUpSelect,
|
|
2432
|
-
onBook
|
|
4230
|
+
onBook,
|
|
4231
|
+
onInputResponse
|
|
2433
4232
|
}) {
|
|
2434
|
-
const transcriptRef = (0,
|
|
4233
|
+
const transcriptRef = (0, import_react10.useRef)(null);
|
|
2435
4234
|
const resolvedBrandLabel = brandLabel.trim();
|
|
2436
4235
|
const resolvedBrandLogoUrl = brandLogoUrl?.trim();
|
|
2437
|
-
const [failedLogoUrl, setFailedLogoUrl] = (0,
|
|
4236
|
+
const [failedLogoUrl, setFailedLogoUrl] = (0, import_react10.useState)(null);
|
|
2438
4237
|
const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
|
|
2439
4238
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2440
4239
|
const brandedTheme = { ...defaultAgentRailTheme, ...theme };
|
|
2441
4240
|
const resolvedTheme = resolvedColorScheme === "dark" ? {
|
|
2442
4241
|
...brandedTheme,
|
|
2443
4242
|
brand: theme?.brand ?? defaultDarkAgentRailTheme.brand,
|
|
2444
|
-
brandDeep: defaultDarkAgentRailTheme.brandDeep,
|
|
2445
|
-
brandSoft: `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
|
|
2446
|
-
border: defaultDarkAgentRailTheme.border,
|
|
2447
|
-
danger: defaultDarkAgentRailTheme.danger,
|
|
2448
|
-
success: defaultDarkAgentRailTheme.success,
|
|
2449
|
-
surface: defaultDarkAgentRailTheme.surface,
|
|
2450
|
-
surfaceMuted: defaultDarkAgentRailTheme.surfaceMuted,
|
|
2451
|
-
text: defaultDarkAgentRailTheme.text,
|
|
2452
|
-
textMuted: defaultDarkAgentRailTheme.textMuted,
|
|
2453
|
-
textSubtle: defaultDarkAgentRailTheme.textSubtle,
|
|
4243
|
+
brandDeep: theme?.brandDeep ?? defaultDarkAgentRailTheme.brandDeep,
|
|
4244
|
+
brandSoft: theme?.brandSoft ?? `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
|
|
4245
|
+
border: theme?.border ?? defaultDarkAgentRailTheme.border,
|
|
4246
|
+
danger: theme?.danger ?? defaultDarkAgentRailTheme.danger,
|
|
4247
|
+
success: theme?.success ?? defaultDarkAgentRailTheme.success,
|
|
4248
|
+
surface: theme?.surface ?? defaultDarkAgentRailTheme.surface,
|
|
4249
|
+
surfaceMuted: theme?.surfaceMuted ?? defaultDarkAgentRailTheme.surfaceMuted,
|
|
4250
|
+
text: theme?.text ?? defaultDarkAgentRailTheme.text,
|
|
4251
|
+
textMuted: theme?.textMuted ?? defaultDarkAgentRailTheme.textMuted,
|
|
4252
|
+
textSubtle: theme?.textSubtle ?? defaultDarkAgentRailTheme.textSubtle,
|
|
2454
4253
|
visitorBubble: theme?.visitorBubble ?? theme?.brand ?? defaultDarkAgentRailTheme.visitorBubble
|
|
2455
4254
|
} : brandedTheme;
|
|
2456
4255
|
const railStyle = {
|
|
@@ -2473,8 +4272,15 @@ function AgentRail({
|
|
|
2473
4272
|
"--as-font-display": resolvedTheme.fontDisplay,
|
|
2474
4273
|
colorScheme: resolvedColorScheme
|
|
2475
4274
|
};
|
|
2476
|
-
const
|
|
4275
|
+
const pendingInputRequests = (state.pendingInputs ?? []).filter(
|
|
4276
|
+
(request) => shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
4277
|
+
);
|
|
4278
|
+
const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && pendingInputRequests.length > 0;
|
|
4279
|
+
const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
|
|
2477
4280
|
const showActivity = state.toolSteps.length > 0;
|
|
4281
|
+
const visitorToolResults = (state.toolResults ?? []).filter(
|
|
4282
|
+
isRenderableVisitorToolResult
|
|
4283
|
+
);
|
|
2478
4284
|
const hasVisitorMessages2 = state.messages.some(
|
|
2479
4285
|
(message) => message.role === "visitor"
|
|
2480
4286
|
);
|
|
@@ -2484,22 +4290,44 @@ function AgentRail({
|
|
|
2484
4290
|
);
|
|
2485
4291
|
const visibleMessages = hasVisitorMessages2 ? state.messages : [];
|
|
2486
4292
|
const lastMessage = visibleMessages.at(-1);
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
4293
|
+
let lastAgentIndex = -1;
|
|
4294
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4295
|
+
if (visibleMessages[index]?.role === "agent") {
|
|
4296
|
+
lastAgentIndex = index;
|
|
4297
|
+
break;
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
let lastVisitorIndex = -1;
|
|
4301
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4302
|
+
if (visibleMessages[index]?.role === "visitor") {
|
|
4303
|
+
lastVisitorIndex = index;
|
|
4304
|
+
break;
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
const lastIsAgent = lastMessage?.role === "agent";
|
|
4308
|
+
const streamingMessage = state.phase === "streaming" && state.streamingText && !lastIsAgent ? {
|
|
2490
4309
|
createdAt: 0,
|
|
2491
4310
|
id: "streaming-response",
|
|
2492
4311
|
role: "agent",
|
|
2493
4312
|
streaming: true,
|
|
2494
4313
|
text: state.streamingText
|
|
2495
|
-
} : state.pendingOffer ? {
|
|
4314
|
+
} : state.pendingOffer && !lastIsAgent ? {
|
|
2496
4315
|
createdAt: 0,
|
|
2497
4316
|
id: "pending-booking",
|
|
2498
4317
|
role: "agent",
|
|
2499
4318
|
streaming: false,
|
|
2500
4319
|
text: "Pick a date and time that works for you."
|
|
2501
4320
|
} : null;
|
|
2502
|
-
|
|
4321
|
+
const bookingReadyText = state.streamingText || (lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "");
|
|
4322
|
+
const waitingForBooking = !state.pendingOffer && looksLikeBookingReady(bookingReadyText) && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
|
|
4323
|
+
const lastAgentText = lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "";
|
|
4324
|
+
const composerForm = resolveComposerForm({
|
|
4325
|
+
agentText: lastAgentText,
|
|
4326
|
+
cards: extractToolCards(lastAgentText),
|
|
4327
|
+
hasBookingOffer: Boolean(state.pendingOffer) || waitingForBooking,
|
|
4328
|
+
enabled: lastIsAgent && !isBusy
|
|
4329
|
+
});
|
|
4330
|
+
(0, import_react10.useEffect)(() => {
|
|
2503
4331
|
const node = transcriptRef.current;
|
|
2504
4332
|
if (!node) return;
|
|
2505
4333
|
node.scrollTop = node.scrollHeight;
|
|
@@ -2510,11 +4338,13 @@ function AgentRail({
|
|
|
2510
4338
|
state.followUps,
|
|
2511
4339
|
state.journey
|
|
2512
4340
|
]);
|
|
2513
|
-
return /* @__PURE__ */ (0,
|
|
4341
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2514
4342
|
"aside",
|
|
2515
4343
|
{
|
|
2516
|
-
className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4344
|
+
className: `agent-rail not-typeset${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4345
|
+
"data-not-typeset": "",
|
|
2517
4346
|
"data-color-scheme": resolvedColorScheme,
|
|
4347
|
+
spellCheck: false,
|
|
2518
4348
|
style: railStyle,
|
|
2519
4349
|
"aria-label": "Agent conversation",
|
|
2520
4350
|
"aria-modal": mobileFullscreen || expanded ? true : void 0,
|
|
@@ -2522,28 +4352,28 @@ function AgentRail({
|
|
|
2522
4352
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
2523
4353
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
2524
4354
|
children: [
|
|
2525
|
-
/* @__PURE__ */ (0,
|
|
2526
|
-
onCollapse ? /* @__PURE__ */ (0,
|
|
4355
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__brand-row", children: [
|
|
4356
|
+
onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2527
4357
|
"button",
|
|
2528
4358
|
{
|
|
2529
4359
|
type: "button",
|
|
2530
4360
|
className: "agent-rail__collapse",
|
|
2531
4361
|
"aria-label": "Collapse assist",
|
|
2532
4362
|
onClick: onCollapse,
|
|
2533
|
-
children: /* @__PURE__ */ (0,
|
|
4363
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MinimizeIcon, {})
|
|
2534
4364
|
}
|
|
2535
|
-
) : onClose ? /* @__PURE__ */ (0,
|
|
4365
|
+
) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2536
4366
|
"button",
|
|
2537
4367
|
{
|
|
2538
4368
|
type: "button",
|
|
2539
4369
|
className: "agent-rail__close",
|
|
2540
4370
|
"aria-label": "Close agent",
|
|
2541
4371
|
onClick: onClose,
|
|
2542
|
-
children: /* @__PURE__ */ (0,
|
|
4372
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CloseIcon, {})
|
|
2543
4373
|
}
|
|
2544
|
-
) : /* @__PURE__ */ (0,
|
|
2545
|
-
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0,
|
|
2546
|
-
showBrandLogo ? /* @__PURE__ */ (0,
|
|
4374
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
|
|
4375
|
+
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "agent-rail__identity", children: [
|
|
4376
|
+
showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2547
4377
|
"img",
|
|
2548
4378
|
{
|
|
2549
4379
|
className: "agent-rail__brand-logo",
|
|
@@ -2554,10 +4384,10 @@ function AgentRail({
|
|
|
2554
4384
|
}
|
|
2555
4385
|
}
|
|
2556
4386
|
) }) : null,
|
|
2557
|
-
resolvedBrandLabel ? /* @__PURE__ */ (0,
|
|
4387
|
+
resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
|
|
2558
4388
|
] }) : null,
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2560
|
-
onReset ? /* @__PURE__ */ (0,
|
|
4389
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "agent-rail__actions", children: [
|
|
4390
|
+
onReset ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2561
4391
|
"button",
|
|
2562
4392
|
{
|
|
2563
4393
|
type: "button",
|
|
@@ -2565,24 +4395,24 @@ function AgentRail({
|
|
|
2565
4395
|
"aria-label": "Start a new conversation",
|
|
2566
4396
|
disabled: !hasVisitorMessages2,
|
|
2567
4397
|
onClick: onReset,
|
|
2568
|
-
children: /* @__PURE__ */ (0,
|
|
4398
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NewChatIcon, {})
|
|
2569
4399
|
}
|
|
2570
4400
|
) : null,
|
|
2571
|
-
onExpandToggle ? /* @__PURE__ */ (0,
|
|
4401
|
+
onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2572
4402
|
"button",
|
|
2573
4403
|
{
|
|
2574
4404
|
type: "button",
|
|
2575
4405
|
className: "agent-rail__expand",
|
|
2576
4406
|
"aria-label": expanded ? "Exit full screen" : "Open full screen",
|
|
2577
4407
|
onClick: onExpandToggle,
|
|
2578
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
4408
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ExpandIcon, {})
|
|
2579
4409
|
}
|
|
2580
4410
|
) : null
|
|
2581
4411
|
] })
|
|
2582
4412
|
] }) }),
|
|
2583
|
-
/* @__PURE__ */ (0,
|
|
2584
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ (0,
|
|
2585
|
-
greeting?.role === "agent" ? /* @__PURE__ */ (0,
|
|
4413
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__thread", children: [
|
|
4414
|
+
!hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
4415
|
+
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2586
4416
|
MessageBubble,
|
|
2587
4417
|
{
|
|
2588
4418
|
message: greeting,
|
|
@@ -2590,7 +4420,7 @@ function AgentRail({
|
|
|
2590
4420
|
onBook
|
|
2591
4421
|
}
|
|
2592
4422
|
) : null,
|
|
2593
|
-
showIdleFollowUps ? /* @__PURE__ */ (0,
|
|
4423
|
+
showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2594
4424
|
FollowUpChips,
|
|
2595
4425
|
{
|
|
2596
4426
|
suggestions: state.followUps,
|
|
@@ -2598,64 +4428,94 @@ function AgentRail({
|
|
|
2598
4428
|
label: "Start here",
|
|
2599
4429
|
onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
|
|
2600
4430
|
}
|
|
2601
|
-
) }) : null
|
|
4431
|
+
) }) : null,
|
|
4432
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4433
|
+
AgentActivityBubble,
|
|
4434
|
+
{
|
|
4435
|
+
brandLabel: resolvedBrandLabel,
|
|
4436
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4437
|
+
failed: state.phase === "error",
|
|
4438
|
+
steps: state.toolSteps
|
|
4439
|
+
}
|
|
4440
|
+
) : null,
|
|
4441
|
+
visitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VisitorToolResultView, { result }, result.id)),
|
|
4442
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4443
|
+
HumanInputCard,
|
|
4444
|
+
{
|
|
4445
|
+
request,
|
|
4446
|
+
onRespond: onInputResponse
|
|
4447
|
+
},
|
|
4448
|
+
request.requestId
|
|
4449
|
+
))
|
|
2602
4450
|
] }) : null,
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
4451
|
+
visibleMessages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__turn-block", children: [
|
|
4452
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4453
|
+
MessageBubble,
|
|
4454
|
+
{
|
|
4455
|
+
message,
|
|
4456
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4457
|
+
offer: index === lastAgentIndex ? state.pendingOffer : void 0,
|
|
4458
|
+
onBook
|
|
4459
|
+
}
|
|
4460
|
+
),
|
|
4461
|
+
index === lastVisitorIndex ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
4462
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4463
|
+
AgentActivityBubble,
|
|
4464
|
+
{
|
|
4465
|
+
brandLabel: resolvedBrandLabel,
|
|
4466
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4467
|
+
failed: state.phase === "error",
|
|
4468
|
+
steps: state.toolSteps
|
|
4469
|
+
}
|
|
4470
|
+
) : null,
|
|
4471
|
+
visitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VisitorToolResultView, { result }, result.id)),
|
|
4472
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4473
|
+
HumanInputCard,
|
|
4474
|
+
{
|
|
4475
|
+
request,
|
|
4476
|
+
onRespond: onInputResponse
|
|
4477
|
+
},
|
|
4478
|
+
request.requestId
|
|
4479
|
+
))
|
|
4480
|
+
] }) : null
|
|
4481
|
+
] }, message.id)),
|
|
4482
|
+
streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2622
4483
|
MessageBubble,
|
|
2623
4484
|
{
|
|
2624
|
-
message:
|
|
4485
|
+
message: streamingMessage,
|
|
2625
4486
|
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4487
|
+
offer: state.pendingOffer,
|
|
2626
4488
|
onBook
|
|
2627
4489
|
}
|
|
2628
4490
|
) : null,
|
|
2629
|
-
|
|
2630
|
-
|
|
4491
|
+
waitingForBooking ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4492
|
+
BookingCard,
|
|
2631
4493
|
{
|
|
2632
|
-
|
|
2633
|
-
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
2634
|
-
offer: state.pendingOffer,
|
|
2635
|
-
onBook
|
|
4494
|
+
offer: { type: "booking_offer", eventTypes: [], slots: [] }
|
|
2636
4495
|
}
|
|
2637
4496
|
) : null,
|
|
2638
|
-
state.error ? /* @__PURE__ */ (0,
|
|
2639
|
-
/* @__PURE__ */ (0,
|
|
2640
|
-
/* @__PURE__ */ (0,
|
|
2641
|
-
/* @__PURE__ */ (0,
|
|
4497
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
|
|
4498
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
4499
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "Something went wrong" }),
|
|
4500
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: state.error })
|
|
2642
4501
|
] }),
|
|
2643
|
-
onRetry ? /* @__PURE__ */ (0,
|
|
4502
|
+
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
2644
4503
|
] }) : null
|
|
2645
4504
|
] }) }),
|
|
2646
|
-
/* @__PURE__ */ (0,
|
|
2647
|
-
/* @__PURE__ */ (0,
|
|
4505
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
|
|
4506
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2648
4507
|
Composer,
|
|
2649
4508
|
{
|
|
2650
4509
|
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
2651
4510
|
disabled: isBusy,
|
|
4511
|
+
form: composerForm,
|
|
2652
4512
|
placeholder: composerPlaceholder,
|
|
2653
4513
|
onSubmit
|
|
2654
4514
|
}
|
|
2655
4515
|
),
|
|
2656
|
-
/* @__PURE__ */ (0,
|
|
2657
|
-
/* @__PURE__ */ (0,
|
|
2658
|
-
/* @__PURE__ */ (0,
|
|
4516
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { children: [
|
|
4517
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "AI can make mistakes. Check important info." }),
|
|
4518
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: poweredByLabel })
|
|
2659
4519
|
] }) })
|
|
2660
4520
|
] })
|
|
2661
4521
|
]
|
|
@@ -2664,9 +4524,9 @@ function AgentRail({
|
|
|
2664
4524
|
}
|
|
2665
4525
|
|
|
2666
4526
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
2667
|
-
var
|
|
4527
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2668
4528
|
function SparklesIcon() {
|
|
2669
|
-
return /* @__PURE__ */ (0,
|
|
4529
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2670
4530
|
"svg",
|
|
2671
4531
|
{
|
|
2672
4532
|
className: "assist-edge-tab__sparkles",
|
|
@@ -2674,21 +4534,21 @@ function SparklesIcon() {
|
|
|
2674
4534
|
fill: "none",
|
|
2675
4535
|
"aria-hidden": "true",
|
|
2676
4536
|
children: [
|
|
2677
|
-
/* @__PURE__ */ (0,
|
|
4537
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2678
4538
|
"path",
|
|
2679
4539
|
{
|
|
2680
4540
|
d: "M8 1.2l.95 2.7 2.85.05-2.25 1.75.8 2.75L8 6.7 5.65 8.45l.8-2.75L4.2 3.95l2.85-.05L8 1.2z",
|
|
2681
4541
|
fill: "currentColor"
|
|
2682
4542
|
}
|
|
2683
4543
|
),
|
|
2684
|
-
/* @__PURE__ */ (0,
|
|
4544
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2685
4545
|
"path",
|
|
2686
4546
|
{
|
|
2687
4547
|
d: "M14.2 6.4l.55 1.55 1.65.03-1.3 1 .46 1.58-1.36-1-1.36 1 .46-1.58-1.3-1 1.65-.03.55-1.55z",
|
|
2688
4548
|
fill: "currentColor"
|
|
2689
4549
|
}
|
|
2690
4550
|
),
|
|
2691
|
-
/* @__PURE__ */ (0,
|
|
4551
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2692
4552
|
"path",
|
|
2693
4553
|
{
|
|
2694
4554
|
d: "M3.1 9.1l.4 1.15 1.22.02-.96.74.34 1.17-1-.74-1 .74.34-1.17-.96-.74 1.22-.02.4-1.15z",
|
|
@@ -2702,7 +4562,7 @@ function SparklesIcon() {
|
|
|
2702
4562
|
function TabMarkIcon({ customIconUrl }) {
|
|
2703
4563
|
const url = customIconUrl?.trim();
|
|
2704
4564
|
if (url) {
|
|
2705
|
-
return /* @__PURE__ */ (0,
|
|
4565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2706
4566
|
"img",
|
|
2707
4567
|
{
|
|
2708
4568
|
alt: "",
|
|
@@ -2712,10 +4572,10 @@ function TabMarkIcon({ customIconUrl }) {
|
|
|
2712
4572
|
}
|
|
2713
4573
|
);
|
|
2714
4574
|
}
|
|
2715
|
-
return /* @__PURE__ */ (0,
|
|
4575
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SparklesIcon, {});
|
|
2716
4576
|
}
|
|
2717
4577
|
function ChevronLeftIcon() {
|
|
2718
|
-
return /* @__PURE__ */ (0,
|
|
4578
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2719
4579
|
"path",
|
|
2720
4580
|
{
|
|
2721
4581
|
d: "M10 4L6 8l4 4",
|
|
@@ -2727,7 +4587,7 @@ function ChevronLeftIcon() {
|
|
|
2727
4587
|
) });
|
|
2728
4588
|
}
|
|
2729
4589
|
function ChevronDownIcon() {
|
|
2730
|
-
return /* @__PURE__ */ (0,
|
|
4590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2731
4591
|
"path",
|
|
2732
4592
|
{
|
|
2733
4593
|
d: "M4 6l4 4 4-4",
|
|
@@ -2739,7 +4599,7 @@ function ChevronDownIcon() {
|
|
|
2739
4599
|
) });
|
|
2740
4600
|
}
|
|
2741
4601
|
function DragDots() {
|
|
2742
|
-
return /* @__PURE__ */ (0,
|
|
4602
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("i", {}, index)) });
|
|
2743
4603
|
}
|
|
2744
4604
|
var VARIANT_COPY = {
|
|
2745
4605
|
outline: { label: "Ask anything", aria: "Ask anything" },
|
|
@@ -2768,6 +4628,7 @@ function AssistEdgeTab({
|
|
|
2768
4628
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2769
4629
|
const copy = VARIANT_COPY[variant];
|
|
2770
4630
|
const visibleLabel = label?.trim() || copy.label;
|
|
4631
|
+
const alignment = along < 50 ? "start" : along > 50 ? "end" : "center";
|
|
2771
4632
|
const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
|
|
2772
4633
|
const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
|
|
2773
4634
|
const resolvedBorderColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.border : borderColor;
|
|
@@ -2784,11 +4645,11 @@ function AssistEdgeTab({
|
|
|
2784
4645
|
...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
|
|
2785
4646
|
colorScheme: resolvedColorScheme
|
|
2786
4647
|
};
|
|
2787
|
-
return /* @__PURE__ */ (0,
|
|
4648
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2788
4649
|
"button",
|
|
2789
4650
|
{
|
|
2790
4651
|
type: "button",
|
|
2791
|
-
className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
|
|
4652
|
+
className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side} assist-edge-tab--align-${alignment}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
|
|
2792
4653
|
"data-color-scheme": resolvedColorScheme,
|
|
2793
4654
|
style,
|
|
2794
4655
|
"aria-label": `Open ${visibleLabel}`,
|
|
@@ -2796,15 +4657,15 @@ function AssistEdgeTab({
|
|
|
2796
4657
|
tabIndex: visible ? 0 : -1,
|
|
2797
4658
|
onClick: onOpen,
|
|
2798
4659
|
children: [
|
|
2799
|
-
mobile ? /* @__PURE__ */ (0,
|
|
2800
|
-
/* @__PURE__ */ (0,
|
|
4660
|
+
mobile ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4661
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2801
4662
|
"span",
|
|
2802
4663
|
{
|
|
2803
4664
|
className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
|
|
2804
4665
|
"aria-hidden": "true",
|
|
2805
4666
|
children: [
|
|
2806
|
-
/* @__PURE__ */ (0,
|
|
2807
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4667
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4668
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2808
4669
|
"img",
|
|
2809
4670
|
{
|
|
2810
4671
|
className: "assist-edge-tab__logo",
|
|
@@ -2818,11 +4679,11 @@ function AssistEdgeTab({
|
|
|
2818
4679
|
]
|
|
2819
4680
|
}
|
|
2820
4681
|
),
|
|
2821
|
-
/* @__PURE__ */ (0,
|
|
2822
|
-
] }) : variant === "outline" ? /* @__PURE__ */ (0,
|
|
2823
|
-
/* @__PURE__ */ (0,
|
|
2824
|
-
/* @__PURE__ */ (0,
|
|
2825
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4682
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel })
|
|
4683
|
+
] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4684
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4685
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4686
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2826
4687
|
"img",
|
|
2827
4688
|
{
|
|
2828
4689
|
className: "assist-edge-tab__logo",
|
|
@@ -2834,18 +4695,18 @@ function AssistEdgeTab({
|
|
|
2834
4695
|
}
|
|
2835
4696
|
) : null
|
|
2836
4697
|
] }),
|
|
2837
|
-
/* @__PURE__ */ (0,
|
|
2838
|
-
/* @__PURE__ */ (0,
|
|
4698
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4699
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronDownIcon, {})
|
|
2839
4700
|
] }) : null,
|
|
2840
|
-
variant === "ask" ? /* @__PURE__ */ (0,
|
|
2841
|
-
/* @__PURE__ */ (0,
|
|
2842
|
-
/* @__PURE__ */ (0,
|
|
2843
|
-
/* @__PURE__ */ (0,
|
|
4701
|
+
variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4702
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronLeftIcon, {}),
|
|
4703
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4704
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DragDots, {})
|
|
2844
4705
|
] }) : null,
|
|
2845
|
-
variant === "fill" ? /* @__PURE__ */ (0,
|
|
2846
|
-
/* @__PURE__ */ (0,
|
|
2847
|
-
/* @__PURE__ */ (0,
|
|
2848
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4706
|
+
variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4707
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4708
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4709
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2849
4710
|
"img",
|
|
2850
4711
|
{
|
|
2851
4712
|
className: "assist-edge-tab__logo",
|
|
@@ -2857,8 +4718,8 @@ function AssistEdgeTab({
|
|
|
2857
4718
|
}
|
|
2858
4719
|
) : null
|
|
2859
4720
|
] }),
|
|
2860
|
-
/* @__PURE__ */ (0,
|
|
2861
|
-
/* @__PURE__ */ (0,
|
|
4721
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4722
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronLeftIcon, {})
|
|
2862
4723
|
] }) : null
|
|
2863
4724
|
]
|
|
2864
4725
|
}
|
|
@@ -2866,7 +4727,7 @@ function AssistEdgeTab({
|
|
|
2866
4727
|
}
|
|
2867
4728
|
|
|
2868
4729
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
2869
|
-
var
|
|
4730
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2870
4731
|
function AgentWidget({
|
|
2871
4732
|
indexId,
|
|
2872
4733
|
customerId,
|
|
@@ -2879,13 +4740,14 @@ function AgentWidget({
|
|
|
2879
4740
|
pageShift = true,
|
|
2880
4741
|
registerPanelController = false,
|
|
2881
4742
|
colorScheme = "auto",
|
|
2882
|
-
branding
|
|
4743
|
+
branding,
|
|
4744
|
+
toolResultRegistry
|
|
2883
4745
|
}) {
|
|
2884
4746
|
const isMobile = useIsMobile();
|
|
2885
4747
|
const placement = normalizeAgentPlacement(placementInput);
|
|
2886
|
-
const railSlotRef = (0,
|
|
2887
|
-
const [railCollapsed, setRailCollapsed] = (0,
|
|
2888
|
-
const [railExpanded, setRailExpanded] = (0,
|
|
4748
|
+
const railSlotRef = (0, import_react11.useRef)(null);
|
|
4749
|
+
const [railCollapsed, setRailCollapsed] = (0, import_react11.useState)(defaultCollapsed);
|
|
4750
|
+
const [railExpanded, setRailExpanded] = (0, import_react11.useState)(false);
|
|
2889
4751
|
const pageShiftActive = shouldApplyPageShift({
|
|
2890
4752
|
pageShift,
|
|
2891
4753
|
isMobile,
|
|
@@ -2896,14 +4758,15 @@ function AgentWidget({
|
|
|
2896
4758
|
active: pageShiftActive,
|
|
2897
4759
|
railSlotRef
|
|
2898
4760
|
});
|
|
2899
|
-
const { state, reset, retry, submit } = useAgentChat({
|
|
4761
|
+
const { state, reset, retry, respondToInput, respondToToolInput, submit } = useAgentChat({
|
|
2900
4762
|
customerId,
|
|
2901
4763
|
getUnpublishedPreviewGrant,
|
|
2902
4764
|
indexId,
|
|
2903
4765
|
previewBuildId,
|
|
2904
4766
|
version,
|
|
2905
4767
|
runtimeOrigin,
|
|
2906
|
-
greeting: branding?.greeting
|
|
4768
|
+
greeting: branding?.greeting,
|
|
4769
|
+
toolResultRegistry
|
|
2907
4770
|
});
|
|
2908
4771
|
const agentName = branding?.agentName ?? "";
|
|
2909
4772
|
const tabLabel = branding?.tabLabel ?? agentName;
|
|
@@ -2924,22 +4787,24 @@ function AgentWidget({
|
|
|
2924
4787
|
} : {},
|
|
2925
4788
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
2926
4789
|
};
|
|
2927
|
-
(0,
|
|
4790
|
+
(0, import_react11.useEffect)(() => {
|
|
2928
4791
|
if (!registerPanelController) return;
|
|
2929
4792
|
registerAgentPanelController(customerId, {
|
|
2930
4793
|
open: () => setRailCollapsed(false),
|
|
2931
4794
|
close: () => {
|
|
2932
4795
|
setRailCollapsed(true);
|
|
2933
4796
|
setRailExpanded(false);
|
|
2934
|
-
}
|
|
4797
|
+
},
|
|
4798
|
+
reset,
|
|
4799
|
+
submit
|
|
2935
4800
|
});
|
|
2936
4801
|
return () => unregisterAgentPanelController(customerId);
|
|
2937
|
-
}, [customerId, registerPanelController]);
|
|
4802
|
+
}, [customerId, registerPanelController, reset, submit]);
|
|
2938
4803
|
async function handleSubmit(message) {
|
|
2939
4804
|
if (isMobile) setRailCollapsed(false);
|
|
2940
4805
|
await submit(message);
|
|
2941
4806
|
}
|
|
2942
|
-
(0,
|
|
4807
|
+
(0, import_react11.useEffect)(() => {
|
|
2943
4808
|
if (railCollapsed) return;
|
|
2944
4809
|
const handleKeyDown = (event) => {
|
|
2945
4810
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -2973,19 +4838,19 @@ function AgentWidget({
|
|
|
2973
4838
|
window.addEventListener("keydown", handleKeyDown);
|
|
2974
4839
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2975
4840
|
}, [isMobile, railCollapsed, railExpanded]);
|
|
2976
|
-
return /* @__PURE__ */ (0,
|
|
2977
|
-
/* @__PURE__ */ (0,
|
|
4841
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "webless-agent-root", children: [
|
|
4842
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2978
4843
|
"div",
|
|
2979
4844
|
{
|
|
2980
4845
|
className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
|
|
2981
|
-
children: /* @__PURE__ */ (0,
|
|
4846
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2982
4847
|
"div",
|
|
2983
4848
|
{
|
|
2984
4849
|
ref: railSlotRef,
|
|
2985
4850
|
className: "webless-agent-root__rail-slot",
|
|
2986
4851
|
inert: railCollapsed || void 0,
|
|
2987
4852
|
"aria-hidden": railCollapsed,
|
|
2988
|
-
children: /* @__PURE__ */ (0,
|
|
4853
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2989
4854
|
AgentRail,
|
|
2990
4855
|
{
|
|
2991
4856
|
theme,
|
|
@@ -3003,6 +4868,8 @@ function AgentWidget({
|
|
|
3003
4868
|
onSubmit: handleSubmit,
|
|
3004
4869
|
onReset: reset,
|
|
3005
4870
|
onRetry: () => void retry(),
|
|
4871
|
+
onInputResponse: (response) => void respondToInput(response),
|
|
4872
|
+
onToolInput: (surface, values) => void respondToToolInput(surface, values),
|
|
3006
4873
|
onFollowUpSelect: (label) => void handleSubmit(label),
|
|
3007
4874
|
onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
|
|
3008
4875
|
}
|
|
@@ -3011,7 +4878,7 @@ function AgentWidget({
|
|
|
3011
4878
|
)
|
|
3012
4879
|
}
|
|
3013
4880
|
),
|
|
3014
|
-
railCollapsed ? /* @__PURE__ */ (0,
|
|
4881
|
+
railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3015
4882
|
AssistEdgeTab,
|
|
3016
4883
|
{
|
|
3017
4884
|
variant: placement.variant,
|
|
@@ -3050,12 +4917,12 @@ function readUnpublishedPreviewBuildId(href) {
|
|
|
3050
4917
|
}
|
|
3051
4918
|
|
|
3052
4919
|
// src/embed/AgentWidget.tsx
|
|
3053
|
-
var
|
|
4920
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
3054
4921
|
function AgentWidget2({
|
|
3055
4922
|
manifest
|
|
3056
4923
|
}) {
|
|
3057
4924
|
const defaultCollapsed = manifest.version !== "unpublished";
|
|
3058
|
-
return /* @__PURE__ */ (0,
|
|
4925
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3059
4926
|
AgentWidget,
|
|
3060
4927
|
{
|
|
3061
4928
|
indexId: manifest.indexId,
|
|
@@ -3139,7 +5006,7 @@ function normalizeAgentBranding(branding) {
|
|
|
3139
5006
|
}
|
|
3140
5007
|
|
|
3141
5008
|
// src/embed/mount.tsx
|
|
3142
|
-
var
|
|
5009
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
3143
5010
|
var mountedHandles = /* @__PURE__ */ new Map();
|
|
3144
5011
|
var latestCustomerId = null;
|
|
3145
5012
|
function resolveMountHost(manifest, script) {
|
|
@@ -3169,7 +5036,7 @@ function mountAgent(input) {
|
|
|
3169
5036
|
const host = createHost(manifest.customerId);
|
|
3170
5037
|
mountTarget.append(host);
|
|
3171
5038
|
const root = (0, import_client5.createRoot)(host);
|
|
3172
|
-
root.render(/* @__PURE__ */ (0,
|
|
5039
|
+
root.render(/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AgentWidget2, { manifest }));
|
|
3173
5040
|
const handle = {
|
|
3174
5041
|
customerId: manifest.customerId,
|
|
3175
5042
|
manifest,
|
|
@@ -3262,6 +5129,8 @@ function createWeblessAgentPublicApi(manifest) {
|
|
|
3262
5129
|
mountAgent,
|
|
3263
5130
|
normalizeAgentPlacement,
|
|
3264
5131
|
normalizeAgentTagManifest,
|
|
5132
|
+
resetAgentPanel,
|
|
5133
|
+
submitAgentPanel,
|
|
3265
5134
|
unmountAgent
|
|
3266
5135
|
});
|
|
3267
5136
|
//# sourceMappingURL=embed.cjs.map
|