@webless/agent 0.6.2 → 0.6.4
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/README.md +22 -41
- package/dist/{chunk-HXKD46I6.js → chunk-Y4DCEJDC.js} +2542 -665
- package/dist/chunk-Y4DCEJDC.js.map +1 -0
- package/dist/embed.cjs +2281 -409
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +770 -140
- 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 +2268 -408
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +770 -140
- 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
|
);
|
|
@@ -1869,7 +3156,7 @@ function AgentActivityBubble({
|
|
|
1869
3156
|
workSummary(steps, failed, brandLabel)
|
|
1870
3157
|
] }),
|
|
1871
3158
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-activity-bubble__details", children: [
|
|
1872
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
3159
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1873
3160
|
"button",
|
|
1874
3161
|
{
|
|
1875
3162
|
type: "button",
|
|
@@ -1881,15 +3168,19 @@ function AgentActivityBubble({
|
|
|
1881
3168
|
(current) => current === receiptId ? null : receiptId
|
|
1882
3169
|
);
|
|
1883
3170
|
},
|
|
1884
|
-
children:
|
|
3171
|
+
children: [
|
|
3172
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__summary-title", children: "How this answer was made" }),
|
|
3173
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__summary-toggle", children: detailsOpen ? "Hide work" : "Show work" })
|
|
3174
|
+
]
|
|
1885
3175
|
}
|
|
1886
3176
|
),
|
|
1887
3177
|
detailsOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: visibleSteps.map((step) => {
|
|
1888
3178
|
const detail = stepDetail(step, steps);
|
|
3179
|
+
const child = delegated && step.kind === "specialist";
|
|
1889
3180
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1890
3181
|
"li",
|
|
1891
3182
|
{
|
|
1892
|
-
className: "agent-activity-bubble__step"
|
|
3183
|
+
className: `agent-activity-bubble__step${child ? " agent-activity-bubble__step--child" : ""}`,
|
|
1893
3184
|
"data-kind": step.kind,
|
|
1894
3185
|
"data-state": step.state,
|
|
1895
3186
|
children: [
|
|
@@ -1897,25 +3188,27 @@ function AgentActivityBubble({
|
|
|
1897
3188
|
"span",
|
|
1898
3189
|
{
|
|
1899
3190
|
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()
|
|
3191
|
+
"aria-hidden": "true"
|
|
1914
3192
|
}
|
|
1915
3193
|
),
|
|
1916
3194
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
|
|
1917
3195
|
/* @__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
|
|
3196
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null,
|
|
3197
|
+
delegated && step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__delegation", children: [
|
|
3198
|
+
"Delegated ",
|
|
3199
|
+
delegationCount,
|
|
3200
|
+
" ",
|
|
3201
|
+
delegationCount === 1 ? "task" : "tasks"
|
|
3202
|
+
] }) : null,
|
|
3203
|
+
step.state === "error" && onRetryStep ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3204
|
+
"button",
|
|
3205
|
+
{
|
|
3206
|
+
type: "button",
|
|
3207
|
+
className: "agent-activity-bubble__step-retry",
|
|
3208
|
+
onClick: () => onRetryStep(step),
|
|
3209
|
+
children: "Retry"
|
|
3210
|
+
}
|
|
3211
|
+
) : null
|
|
1919
3212
|
] })
|
|
1920
3213
|
]
|
|
1921
3214
|
},
|
|
@@ -1930,59 +3223,190 @@ function AgentActivityBubble({
|
|
|
1930
3223
|
var import_react6 = require("react");
|
|
1931
3224
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1932
3225
|
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)(
|
|
3226
|
+
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)(
|
|
3227
|
+
"path",
|
|
3228
|
+
{
|
|
3229
|
+
d: "M8 12V4M8 4l-3 3M8 4l3 3",
|
|
3230
|
+
stroke: "currentColor",
|
|
3231
|
+
strokeWidth: "1.5",
|
|
3232
|
+
strokeLinecap: "round",
|
|
3233
|
+
strokeLinejoin: "round"
|
|
3234
|
+
}
|
|
3235
|
+
) });
|
|
3236
|
+
}
|
|
3237
|
+
function emptyValues(form) {
|
|
3238
|
+
const values = {};
|
|
3239
|
+
for (const field of form?.fields ?? []) values[field.id] = "";
|
|
3240
|
+
return values;
|
|
1934
3241
|
}
|
|
1935
3242
|
function Composer({
|
|
1936
3243
|
disabled = false,
|
|
1937
3244
|
placeholder = "Ask anything\u2026",
|
|
1938
3245
|
variant = "default",
|
|
3246
|
+
form = null,
|
|
1939
3247
|
onSubmit
|
|
1940
3248
|
}) {
|
|
1941
3249
|
const [value, setValue] = (0, import_react6.useState)("");
|
|
3250
|
+
const [values, setValues] = (0, import_react6.useState)(
|
|
3251
|
+
() => emptyValues(form)
|
|
3252
|
+
);
|
|
3253
|
+
const [blurred, setBlurred] = (0, import_react6.useState)({});
|
|
1942
3254
|
const inputRef = (0, import_react6.useRef)(null);
|
|
1943
|
-
|
|
3255
|
+
const firstFieldRef = (0, import_react6.useRef)(null);
|
|
3256
|
+
const formId = (0, import_react6.useId)();
|
|
3257
|
+
const activeForm = form;
|
|
3258
|
+
const canSendForm = activeForm ? isComposerFormComplete(activeForm, values) : Boolean(value.trim());
|
|
3259
|
+
(0, import_react6.useEffect)(() => {
|
|
3260
|
+
setValues(emptyValues(form));
|
|
3261
|
+
setBlurred({});
|
|
3262
|
+
}, [form?.id]);
|
|
3263
|
+
(0, import_react6.useEffect)(() => {
|
|
3264
|
+
if (activeForm) firstFieldRef.current?.focus();
|
|
3265
|
+
}, [activeForm?.id]);
|
|
3266
|
+
function submitChat() {
|
|
1944
3267
|
const trimmed = value.trim();
|
|
1945
3268
|
if (!trimmed || disabled) return;
|
|
1946
3269
|
onSubmit?.(trimmed);
|
|
1947
3270
|
setValue("");
|
|
1948
3271
|
inputRef.current?.focus();
|
|
1949
3272
|
}
|
|
3273
|
+
function submitForm() {
|
|
3274
|
+
if (!activeForm || disabled || !canSendForm) return;
|
|
3275
|
+
onSubmit?.(formatComposerFormMessage(activeForm, values));
|
|
3276
|
+
setValues(emptyValues(activeForm));
|
|
3277
|
+
setBlurred({});
|
|
3278
|
+
}
|
|
1950
3279
|
function handleSubmit(event) {
|
|
1951
3280
|
event.preventDefault();
|
|
1952
|
-
|
|
3281
|
+
if (activeForm) submitForm();
|
|
3282
|
+
else submitChat();
|
|
1953
3283
|
}
|
|
1954
|
-
function
|
|
3284
|
+
function handleChatKeyDown(event) {
|
|
1955
3285
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
1956
3286
|
event.preventDefault();
|
|
1957
|
-
|
|
3287
|
+
submitChat();
|
|
1958
3288
|
}
|
|
1959
3289
|
}
|
|
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
|
-
|
|
3290
|
+
function handleFormKeyDown(event) {
|
|
3291
|
+
const target = event.target;
|
|
3292
|
+
const isTextarea = Boolean(target && "tagName" in target && target.tagName === "TEXTAREA");
|
|
3293
|
+
if (event.key === "Enter" && !event.shiftKey && !isTextarea) {
|
|
3294
|
+
event.preventDefault();
|
|
3295
|
+
submitForm();
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3299
|
+
"form",
|
|
3300
|
+
{
|
|
3301
|
+
className: [
|
|
3302
|
+
"composer",
|
|
3303
|
+
variant === "dock" ? "composer--dock" : "",
|
|
3304
|
+
activeForm ? "composer--form" : ""
|
|
3305
|
+
].filter(Boolean).join(" "),
|
|
3306
|
+
onSubmit: handleSubmit,
|
|
3307
|
+
children: activeForm ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__sheet", role: "group", "aria-label": "Required details", children: [
|
|
3308
|
+
activeForm.fields.map((field, index) => {
|
|
3309
|
+
const fieldId = `${formId}-${field.id}`;
|
|
3310
|
+
const invalid = Boolean(blurred[field.id]) && !isValidComposerFieldValue(field, values[field.id] ?? "");
|
|
3311
|
+
const controlProps = {
|
|
3312
|
+
id: fieldId,
|
|
3313
|
+
name: field.id,
|
|
3314
|
+
disabled,
|
|
3315
|
+
required: field.required,
|
|
3316
|
+
autoComplete: field.autocomplete,
|
|
3317
|
+
placeholder: field.placeholder,
|
|
3318
|
+
spellCheck: false,
|
|
3319
|
+
value: values[field.id] ?? "",
|
|
3320
|
+
"aria-invalid": invalid || void 0,
|
|
3321
|
+
"aria-describedby": invalid ? `${fieldId}-error` : void 0,
|
|
3322
|
+
onBlur: () => setBlurred((current) => ({ ...current, [field.id]: true })),
|
|
3323
|
+
onChange: (event) => {
|
|
3324
|
+
const next = readComposerControlValue(event);
|
|
3325
|
+
setValues((current) => ({
|
|
3326
|
+
...current,
|
|
3327
|
+
[field.id]: next
|
|
3328
|
+
}));
|
|
3329
|
+
},
|
|
3330
|
+
onKeyDown: handleFormKeyDown
|
|
3331
|
+
};
|
|
3332
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
3333
|
+
"div",
|
|
3334
|
+
{
|
|
3335
|
+
className: [
|
|
3336
|
+
"composer__row",
|
|
3337
|
+
field.kind === "textarea" ? "composer__row--grow" : ""
|
|
3338
|
+
].filter(Boolean).join(" "),
|
|
3339
|
+
children: [
|
|
3340
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "composer__label", htmlFor: fieldId, children: [
|
|
3341
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "composer__sr-only", children: field.label }),
|
|
3342
|
+
field.kind === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3343
|
+
"textarea",
|
|
3344
|
+
{
|
|
3345
|
+
...controlProps,
|
|
3346
|
+
ref: index === 0 ? (node) => {
|
|
3347
|
+
firstFieldRef.current = node;
|
|
3348
|
+
} : void 0,
|
|
3349
|
+
className: "composer__control composer__control--area",
|
|
3350
|
+
rows: 3
|
|
3351
|
+
}
|
|
3352
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3353
|
+
"input",
|
|
3354
|
+
{
|
|
3355
|
+
...controlProps,
|
|
3356
|
+
ref: index === 0 ? (node) => {
|
|
3357
|
+
firstFieldRef.current = node;
|
|
3358
|
+
} : void 0,
|
|
3359
|
+
className: "composer__control",
|
|
3360
|
+
type: field.kind,
|
|
3361
|
+
inputMode: field.kind === "tel" ? "tel" : void 0
|
|
3362
|
+
}
|
|
3363
|
+
)
|
|
3364
|
+
] }),
|
|
3365
|
+
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
|
|
3366
|
+
]
|
|
3367
|
+
},
|
|
3368
|
+
field.id
|
|
3369
|
+
);
|
|
3370
|
+
}),
|
|
3371
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "composer__toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3372
|
+
"button",
|
|
3373
|
+
{
|
|
3374
|
+
type: "submit",
|
|
3375
|
+
className: "composer__send",
|
|
3376
|
+
disabled: disabled || !canSendForm,
|
|
3377
|
+
"aria-label": "Send details",
|
|
3378
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
3379
|
+
}
|
|
3380
|
+
) })
|
|
3381
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__field", children: [
|
|
3382
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3383
|
+
"textarea",
|
|
3384
|
+
{
|
|
3385
|
+
ref: inputRef,
|
|
3386
|
+
className: "composer__input",
|
|
3387
|
+
rows: 1,
|
|
3388
|
+
value,
|
|
3389
|
+
placeholder,
|
|
3390
|
+
disabled,
|
|
3391
|
+
spellCheck: false,
|
|
3392
|
+
"aria-label": "Message",
|
|
3393
|
+
onChange: (event) => setValue(event.target.value),
|
|
3394
|
+
onKeyDown: handleChatKeyDown
|
|
3395
|
+
}
|
|
3396
|
+
),
|
|
3397
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3398
|
+
"button",
|
|
3399
|
+
{
|
|
3400
|
+
type: "submit",
|
|
3401
|
+
className: "composer__send",
|
|
3402
|
+
disabled: disabled || !value.trim(),
|
|
3403
|
+
"aria-label": "Send message",
|
|
3404
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
3405
|
+
}
|
|
3406
|
+
)
|
|
3407
|
+
] })
|
|
3408
|
+
}
|
|
3409
|
+
);
|
|
1986
3410
|
}
|
|
1987
3411
|
|
|
1988
3412
|
// src/react/components/FollowUpChips/FollowUpChips.tsx
|
|
@@ -2030,9 +3454,15 @@ var import_react8 = require("react");
|
|
|
2030
3454
|
// src/react/components/BookingCard/BookingCard.tsx
|
|
2031
3455
|
var import_react7 = require("react");
|
|
2032
3456
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
3457
|
+
var BOOKING_STEPS = [
|
|
3458
|
+
{ id: "date", label: "Date" },
|
|
3459
|
+
{ id: "time", label: "Time" },
|
|
3460
|
+
{ id: "details", label: "Details" }
|
|
3461
|
+
];
|
|
2033
3462
|
function monthFromKey(key) {
|
|
2034
3463
|
const [year, month] = key.split("-").map(Number);
|
|
2035
|
-
if (!year || !month)
|
|
3464
|
+
if (!year || !month)
|
|
3465
|
+
return { year: (/* @__PURE__ */ new Date()).getFullYear(), month: (/* @__PURE__ */ new Date()).getMonth() };
|
|
2036
3466
|
return { year, month: month - 1 };
|
|
2037
3467
|
}
|
|
2038
3468
|
function dateKeyFromParts(year, month, day) {
|
|
@@ -2065,6 +3495,7 @@ function BookingCard({
|
|
|
2065
3495
|
const [startTime, setStartTime] = (0, import_react7.useState)("");
|
|
2066
3496
|
const [name, setName] = (0, import_react7.useState)("");
|
|
2067
3497
|
const [email, setEmail] = (0, import_react7.useState)("");
|
|
3498
|
+
const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
|
|
2068
3499
|
const slots = (0, import_react7.useMemo)(
|
|
2069
3500
|
() => bookingSlotsForEventType(offer.slots, eventTypeUri),
|
|
2070
3501
|
[eventTypeUri, offer.slots]
|
|
@@ -2085,14 +3516,18 @@ function BookingCard({
|
|
|
2085
3516
|
setSelectedDate("");
|
|
2086
3517
|
setStartTime("");
|
|
2087
3518
|
setVisibleMonth(
|
|
2088
|
-
firstAvailableBookingMonth(
|
|
3519
|
+
firstAvailableBookingMonth(
|
|
3520
|
+
bookingSlotsForEventType(offer.slots, nextType)
|
|
3521
|
+
)
|
|
2089
3522
|
);
|
|
2090
3523
|
}
|
|
2091
3524
|
const daySlots = (0, import_react7.useMemo)(
|
|
2092
3525
|
() => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
|
|
2093
3526
|
[selectedDate, slots]
|
|
2094
3527
|
);
|
|
2095
|
-
const selectedType = offer.eventTypes.find(
|
|
3528
|
+
const selectedType = offer.eventTypes.find(
|
|
3529
|
+
(item) => item.uri === eventTypeUri
|
|
3530
|
+
);
|
|
2096
3531
|
const selectedSample = availableByDate.get(selectedDate) ?? startTime;
|
|
2097
3532
|
const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
|
|
2098
3533
|
const weekdays = (0, import_react7.useMemo)(() => weekdayLabels(), []);
|
|
@@ -2138,24 +3573,47 @@ function BookingCard({
|
|
|
2138
3573
|
});
|
|
2139
3574
|
}
|
|
2140
3575
|
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: [
|
|
3576
|
+
/* @__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)(
|
|
3577
|
+
"li",
|
|
3578
|
+
{
|
|
3579
|
+
className: [
|
|
3580
|
+
"booking-card__step-indicator",
|
|
3581
|
+
index === stepIndex ? "booking-card__step-indicator--active" : "",
|
|
3582
|
+
index < stepIndex ? "booking-card__step-indicator--complete" : ""
|
|
3583
|
+
].filter(Boolean).join(" "),
|
|
3584
|
+
"aria-current": index === stepIndex ? "step" : void 0,
|
|
3585
|
+
children: [
|
|
3586
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", children: index + 1 }),
|
|
3587
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: item.label })
|
|
3588
|
+
]
|
|
3589
|
+
},
|
|
3590
|
+
item.id
|
|
3591
|
+
)) }),
|
|
2141
3592
|
step === "date" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2142
3593
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: selectedType?.name || "Pick a date" }),
|
|
2143
3594
|
timeZone ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "booking-card__tz", children: [
|
|
2144
3595
|
"Times in ",
|
|
2145
3596
|
timeZone
|
|
2146
3597
|
] }) : null,
|
|
2147
|
-
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
"
|
|
2151
|
-
{
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
3598
|
+
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3599
|
+
"label",
|
|
3600
|
+
{
|
|
3601
|
+
className: "booking-card__field",
|
|
3602
|
+
htmlFor: `${fieldId}-type`,
|
|
3603
|
+
children: [
|
|
3604
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Meeting" }),
|
|
3605
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3606
|
+
"select",
|
|
3607
|
+
{
|
|
3608
|
+
id: `${fieldId}-type`,
|
|
3609
|
+
value: eventTypeUri,
|
|
3610
|
+
onChange: (event) => selectEventType(event.target.value),
|
|
3611
|
+
children: offer.eventTypes.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: item.uri, children: item.name }, item.uri))
|
|
3612
|
+
}
|
|
3613
|
+
)
|
|
3614
|
+
]
|
|
3615
|
+
}
|
|
3616
|
+
) : null,
|
|
2159
3617
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__month", children: [
|
|
2160
3618
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2161
3619
|
"button",
|
|
@@ -2182,29 +3640,44 @@ function BookingCard({
|
|
|
2182
3640
|
)
|
|
2183
3641
|
] }),
|
|
2184
3642
|
/* @__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
|
-
|
|
3643
|
+
offer.slots.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__loading", role: "status", children: "Finding available times\u2026" }) : null,
|
|
3644
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3645
|
+
"div",
|
|
3646
|
+
{
|
|
3647
|
+
className: "booking-card__calendar",
|
|
3648
|
+
role: "grid",
|
|
3649
|
+
"aria-label": "Available dates",
|
|
3650
|
+
children: cells.map((cell, index) => {
|
|
3651
|
+
if (!cell) {
|
|
3652
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3653
|
+
"span",
|
|
3654
|
+
{
|
|
3655
|
+
className: "booking-card__day"
|
|
3656
|
+
},
|
|
3657
|
+
`empty-${index}`
|
|
3658
|
+
);
|
|
3659
|
+
}
|
|
3660
|
+
const available = availableByDate.has(cell.key);
|
|
3661
|
+
const selected = cell.key === selectedDate;
|
|
3662
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3663
|
+
"button",
|
|
3664
|
+
{
|
|
3665
|
+
type: "button",
|
|
3666
|
+
className: [
|
|
3667
|
+
"booking-card__day",
|
|
3668
|
+
available ? "booking-card__day--available" : "",
|
|
3669
|
+
selected ? "booking-card__day--selected" : ""
|
|
3670
|
+
].filter(Boolean).join(" "),
|
|
3671
|
+
disabled: !available,
|
|
3672
|
+
"aria-pressed": selected,
|
|
3673
|
+
onClick: () => selectDate(cell.key),
|
|
3674
|
+
children: cell.day
|
|
3675
|
+
},
|
|
3676
|
+
cell.key
|
|
3677
|
+
);
|
|
3678
|
+
})
|
|
2188
3679
|
}
|
|
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
|
-
}) })
|
|
3680
|
+
)
|
|
2208
3681
|
] }, "date") : null,
|
|
2209
3682
|
step === "time" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2210
3683
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step-bar", children: [
|
|
@@ -2256,35 +3729,57 @@ function BookingCard({
|
|
|
2256
3729
|
] })
|
|
2257
3730
|
] }),
|
|
2258
3731
|
/* @__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
|
-
|
|
3732
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3733
|
+
"label",
|
|
3734
|
+
{
|
|
3735
|
+
className: "booking-card__field",
|
|
3736
|
+
htmlFor: `${fieldId}-name`,
|
|
3737
|
+
children: [
|
|
3738
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Name" }),
|
|
3739
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3740
|
+
"input",
|
|
3741
|
+
{
|
|
3742
|
+
id: `${fieldId}-name`,
|
|
3743
|
+
autoComplete: "name",
|
|
3744
|
+
value: name,
|
|
3745
|
+
onChange: (event) => setName(event.target.value),
|
|
3746
|
+
required: true
|
|
3747
|
+
}
|
|
3748
|
+
)
|
|
3749
|
+
]
|
|
3750
|
+
}
|
|
3751
|
+
),
|
|
3752
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3753
|
+
"label",
|
|
3754
|
+
{
|
|
3755
|
+
className: "booking-card__field",
|
|
3756
|
+
htmlFor: `${fieldId}-email`,
|
|
3757
|
+
children: [
|
|
3758
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Email" }),
|
|
3759
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3760
|
+
"input",
|
|
3761
|
+
{
|
|
3762
|
+
id: `${fieldId}-email`,
|
|
3763
|
+
type: "email",
|
|
3764
|
+
autoComplete: "email",
|
|
3765
|
+
value: email,
|
|
3766
|
+
onChange: (event) => setEmail(event.target.value),
|
|
3767
|
+
required: true
|
|
3768
|
+
}
|
|
3769
|
+
)
|
|
3770
|
+
]
|
|
3771
|
+
}
|
|
3772
|
+
)
|
|
2286
3773
|
] }),
|
|
2287
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3774
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3775
|
+
"button",
|
|
3776
|
+
{
|
|
3777
|
+
type: "submit",
|
|
3778
|
+
className: "booking-card__submit",
|
|
3779
|
+
disabled: !name.trim() || !email.trim(),
|
|
3780
|
+
children: "Book this time"
|
|
3781
|
+
}
|
|
3782
|
+
)
|
|
2288
3783
|
] }, "details") : null
|
|
2289
3784
|
] }) });
|
|
2290
3785
|
}
|
|
@@ -2293,6 +3788,43 @@ function BookingCard({
|
|
|
2293
3788
|
var import_streamdown = require("streamdown");
|
|
2294
3789
|
var import_styles = require("streamdown/styles.css");
|
|
2295
3790
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3791
|
+
function normalizeDedupeText(text) {
|
|
3792
|
+
return text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
3793
|
+
}
|
|
3794
|
+
function paragraphsAreNearDuplicates(first, second) {
|
|
3795
|
+
const left = normalizeDedupeText(first);
|
|
3796
|
+
const right = normalizeDedupeText(second);
|
|
3797
|
+
if (left.length < 40 || right.length < 40) return false;
|
|
3798
|
+
if (left === right) return true;
|
|
3799
|
+
const shorter = left.length <= right.length ? left : right;
|
|
3800
|
+
const longer = left.length <= right.length ? right : left;
|
|
3801
|
+
return longer.startsWith(
|
|
3802
|
+
shorter.slice(0, Math.floor(shorter.length * 0.85))
|
|
3803
|
+
);
|
|
3804
|
+
}
|
|
3805
|
+
function paragraphsShareOpening(first, second) {
|
|
3806
|
+
const opening = first.split("\n")[0]?.trim();
|
|
3807
|
+
if (!opening || opening.length < 20) return false;
|
|
3808
|
+
return second.trim().startsWith(opening);
|
|
3809
|
+
}
|
|
3810
|
+
function collapseRepeatedText(text) {
|
|
3811
|
+
const trimmed = text.trim();
|
|
3812
|
+
if (trimmed.length < 40) return trimmed;
|
|
3813
|
+
const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
|
|
3814
|
+
if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
|
|
3815
|
+
return paragraphs[0];
|
|
3816
|
+
}
|
|
3817
|
+
if (paragraphs.length >= 2 && paragraphs.length % 2 === 0) {
|
|
3818
|
+
const mid2 = paragraphs.length / 2;
|
|
3819
|
+
const first = paragraphs.slice(0, mid2).join("\n\n");
|
|
3820
|
+
const second = paragraphs.slice(mid2).join("\n\n");
|
|
3821
|
+
if (first === second) return first;
|
|
3822
|
+
}
|
|
3823
|
+
const mid = Math.floor(trimmed.length / 2);
|
|
3824
|
+
const left = trimmed.slice(0, mid).trim();
|
|
3825
|
+
const right = trimmed.slice(mid).trim();
|
|
3826
|
+
return left.length >= 20 && left === right ? left : trimmed;
|
|
3827
|
+
}
|
|
2296
3828
|
function MessageBubble({
|
|
2297
3829
|
message,
|
|
2298
3830
|
brandLogoUrl,
|
|
@@ -2309,24 +3841,41 @@ function MessageBubble({
|
|
|
2309
3841
|
const offers = offer ? [offer] : extractedOffers;
|
|
2310
3842
|
const visibleText = hideToolCardFences(message.text);
|
|
2311
3843
|
const isStreaming = message.role === "agent" && Boolean(message.streaming);
|
|
2312
|
-
const displayText =
|
|
3844
|
+
const displayText = collapseRepeatedText(
|
|
3845
|
+
offers.length > 0 ? sanitizeBookingOfferCopy(visibleText) : looksLikeBookingAvailabilityDump(visibleText) ? sanitizeBookingOfferCopy(visibleText) : visibleText || (isStreaming ? "" : message.text)
|
|
3846
|
+
);
|
|
2313
3847
|
if (message.role === "visitor") {
|
|
2314
3848
|
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
3849
|
}
|
|
2316
|
-
const
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
3850
|
+
const citations = message.citations ?? [];
|
|
3851
|
+
const agentText = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__text", children: [
|
|
3852
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3853
|
+
import_streamdown.Streamdown,
|
|
3854
|
+
{
|
|
3855
|
+
animated: isStreaming,
|
|
3856
|
+
caret: "circle",
|
|
3857
|
+
className: "message-bubble__markdown",
|
|
3858
|
+
controls: false,
|
|
3859
|
+
isAnimating: isStreaming,
|
|
3860
|
+
linkSafety: { enabled: false },
|
|
3861
|
+
mode: isStreaming ? "streaming" : "static",
|
|
3862
|
+
skipHtml: true,
|
|
3863
|
+
children: displayText
|
|
3864
|
+
}
|
|
3865
|
+
),
|
|
3866
|
+
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)(
|
|
3867
|
+
"a",
|
|
3868
|
+
{
|
|
3869
|
+
href: citation.url,
|
|
3870
|
+
target: "_blank",
|
|
3871
|
+
rel: "noreferrer",
|
|
3872
|
+
children: [
|
|
3873
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__source-icon", "aria-hidden": "true", children: "\u25A6" }),
|
|
3874
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: citation.label })
|
|
3875
|
+
]
|
|
3876
|
+
}
|
|
3877
|
+
) }, citation.id)) }) : null
|
|
3878
|
+
] });
|
|
2330
3879
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
|
|
2331
3880
|
displayText ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__agent-row", children: [
|
|
2332
3881
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
@@ -2352,10 +3901,262 @@ function MessageBubble({
|
|
|
2352
3901
|
] });
|
|
2353
3902
|
}
|
|
2354
3903
|
|
|
2355
|
-
// src/react/components/
|
|
3904
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3905
|
+
var import_react9 = require("react");
|
|
3906
|
+
|
|
3907
|
+
// src/react/components/ConfirmationCard/ConfirmationCard.tsx
|
|
2356
3908
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3909
|
+
function ConfirmationCard({
|
|
3910
|
+
disabled = false,
|
|
3911
|
+
request,
|
|
3912
|
+
onRespond
|
|
3913
|
+
}) {
|
|
3914
|
+
const options = request.options ?? [];
|
|
3915
|
+
const heading = request.kind === "tool-approval" ? "Confirm this action" : request.prompt;
|
|
3916
|
+
const prompt = request.kind === "tool-approval" ? request.prompt : void 0;
|
|
3917
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3918
|
+
"section",
|
|
3919
|
+
{
|
|
3920
|
+
className: "confirmation-card",
|
|
3921
|
+
"aria-labelledby": `confirmation-${request.requestId}`,
|
|
3922
|
+
children: [
|
|
3923
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "confirmation-card__heading", children: [
|
|
3924
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { id: `confirmation-${request.requestId}`, children: heading }),
|
|
3925
|
+
prompt ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: prompt }) : null
|
|
3926
|
+
] }),
|
|
3927
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "confirmation-card__actions", children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3928
|
+
"button",
|
|
3929
|
+
{
|
|
3930
|
+
type: "button",
|
|
3931
|
+
className: `confirmation-card__action confirmation-card__action--${option.style ?? "default"}`,
|
|
3932
|
+
disabled,
|
|
3933
|
+
onClick: () => onRespond?.({
|
|
3934
|
+
requestId: request.requestId,
|
|
3935
|
+
optionId: option.id
|
|
3936
|
+
}),
|
|
3937
|
+
children: option.label
|
|
3938
|
+
},
|
|
3939
|
+
option.id
|
|
3940
|
+
)) })
|
|
3941
|
+
]
|
|
3942
|
+
}
|
|
3943
|
+
);
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3947
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3948
|
+
function HumanInputCard({
|
|
3949
|
+
disabled = false,
|
|
3950
|
+
request,
|
|
3951
|
+
onRespond
|
|
3952
|
+
}) {
|
|
3953
|
+
const [text, setText] = (0, import_react9.useState)("");
|
|
3954
|
+
const options = request.options ?? [];
|
|
3955
|
+
const showText = request.display === "text" || request.allowFreeform && options.length === 0;
|
|
3956
|
+
if (options.length > 0) {
|
|
3957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3958
|
+
ConfirmationCard,
|
|
3959
|
+
{
|
|
3960
|
+
disabled,
|
|
3961
|
+
request,
|
|
3962
|
+
onRespond
|
|
3963
|
+
}
|
|
3964
|
+
);
|
|
3965
|
+
}
|
|
3966
|
+
function submitText(event) {
|
|
3967
|
+
event.preventDefault();
|
|
3968
|
+
const value = text.trim();
|
|
3969
|
+
if (!value || disabled) return;
|
|
3970
|
+
onRespond?.({ requestId: request.requestId, text: value });
|
|
3971
|
+
}
|
|
3972
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3973
|
+
"section",
|
|
3974
|
+
{
|
|
3975
|
+
className: "human-input-card",
|
|
3976
|
+
"aria-labelledby": `human-input-${request.requestId}`,
|
|
3977
|
+
children: [
|
|
3978
|
+
/* @__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 }) }),
|
|
3979
|
+
showText ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("form", { onSubmit: submitText, children: [
|
|
3980
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: `human-input-text-${request.requestId}`, children: "Response" }),
|
|
3981
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
3982
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3983
|
+
"input",
|
|
3984
|
+
{
|
|
3985
|
+
id: `human-input-text-${request.requestId}`,
|
|
3986
|
+
value: text,
|
|
3987
|
+
disabled,
|
|
3988
|
+
onChange: (event) => setText(event.target.value)
|
|
3989
|
+
}
|
|
3990
|
+
),
|
|
3991
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "submit", disabled: disabled || !text.trim(), children: "Send" })
|
|
3992
|
+
] })
|
|
3993
|
+
] }) : null,
|
|
3994
|
+
!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
|
|
3995
|
+
]
|
|
3996
|
+
}
|
|
3997
|
+
);
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
// src/react/components/CollectionResultCard/CollectionResultCard.tsx
|
|
4001
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
4002
|
+
function CollectionResultCard({
|
|
4003
|
+
result
|
|
4004
|
+
}) {
|
|
4005
|
+
const empty = result.items.length === 0;
|
|
4006
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4007
|
+
"section",
|
|
4008
|
+
{
|
|
4009
|
+
className: `collection-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4010
|
+
"aria-label": result.title,
|
|
4011
|
+
children: [
|
|
4012
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4013
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4014
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("strong", { children: result.title })
|
|
4015
|
+
] }),
|
|
4016
|
+
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: [
|
|
4017
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "collection-result-card__item-title", children: item.title }),
|
|
4018
|
+
item.description ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: item.description }) : null,
|
|
4019
|
+
item.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dl", { children: item.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
4020
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dt", { children: detail.label }),
|
|
4021
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dd", { children: detail.value })
|
|
4022
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4023
|
+
item.href ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
|
|
4024
|
+
] }, item.title)) })
|
|
4025
|
+
]
|
|
4026
|
+
}
|
|
4027
|
+
);
|
|
4028
|
+
}
|
|
4029
|
+
|
|
4030
|
+
// src/react/components/EntityResultCard/EntityResultCard.tsx
|
|
4031
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
4032
|
+
function EntityResultCard({
|
|
4033
|
+
result
|
|
4034
|
+
}) {
|
|
4035
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
4036
|
+
"section",
|
|
4037
|
+
{
|
|
4038
|
+
className: `entity-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4039
|
+
"aria-label": result.title,
|
|
4040
|
+
children: [
|
|
4041
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4042
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4043
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: result.title })
|
|
4044
|
+
] }),
|
|
4045
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { children: result.description }) : null,
|
|
4046
|
+
result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
4047
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dt", { children: detail.label }),
|
|
4048
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dd", { children: detail.value })
|
|
4049
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4050
|
+
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)(
|
|
4051
|
+
"a",
|
|
4052
|
+
{
|
|
4053
|
+
href: link.href,
|
|
4054
|
+
target: "_blank",
|
|
4055
|
+
rel: "noreferrer",
|
|
4056
|
+
children: link.label
|
|
4057
|
+
},
|
|
4058
|
+
link.href
|
|
4059
|
+
)) }) : null
|
|
4060
|
+
]
|
|
4061
|
+
}
|
|
4062
|
+
);
|
|
4063
|
+
}
|
|
4064
|
+
|
|
4065
|
+
// src/react/components/SignatureResultCard/SignatureResultCard.tsx
|
|
4066
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4067
|
+
function SignatureResultCard({
|
|
4068
|
+
result
|
|
4069
|
+
}) {
|
|
4070
|
+
const primaryLink = result.links?.[0];
|
|
4071
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
4072
|
+
"section",
|
|
4073
|
+
{
|
|
4074
|
+
className: `signature-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4075
|
+
"aria-label": result.title,
|
|
4076
|
+
children: [
|
|
4077
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4078
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4079
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: result.title })
|
|
4080
|
+
] }),
|
|
4081
|
+
result.statusLabel ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
|
|
4082
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: result.description }) : null,
|
|
4083
|
+
primaryLink ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4084
|
+
"a",
|
|
4085
|
+
{
|
|
4086
|
+
className: "signature-result-card__cta",
|
|
4087
|
+
href: primaryLink.href,
|
|
4088
|
+
target: "_blank",
|
|
4089
|
+
rel: "noreferrer",
|
|
4090
|
+
children: primaryLink.label
|
|
4091
|
+
}
|
|
4092
|
+
) : null
|
|
4093
|
+
]
|
|
4094
|
+
}
|
|
4095
|
+
);
|
|
4096
|
+
}
|
|
4097
|
+
|
|
4098
|
+
// src/react/components/ToolResultCard/ToolResultCard.tsx
|
|
4099
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
4100
|
+
function ToolResultCard({
|
|
4101
|
+
result
|
|
4102
|
+
}) {
|
|
4103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
4104
|
+
"section",
|
|
4105
|
+
{
|
|
4106
|
+
className: `tool-result-card tool-result-card--${result.status}`,
|
|
4107
|
+
"aria-label": result.title,
|
|
4108
|
+
children: [
|
|
4109
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4110
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4111
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: result.title })
|
|
4112
|
+
] }),
|
|
4113
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: result.description }) : null,
|
|
4114
|
+
result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
4115
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dt", { children: detail.label }),
|
|
4116
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dd", { children: detail.value })
|
|
4117
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4118
|
+
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)(
|
|
4119
|
+
"a",
|
|
4120
|
+
{
|
|
4121
|
+
href: link.href,
|
|
4122
|
+
target: "_blank",
|
|
4123
|
+
rel: "noreferrer",
|
|
4124
|
+
children: link.label
|
|
4125
|
+
},
|
|
4126
|
+
link.href
|
|
4127
|
+
)) }) : null
|
|
4128
|
+
]
|
|
4129
|
+
}
|
|
4130
|
+
);
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
// src/react/components/VisitorToolResultView/VisitorToolResultView.tsx
|
|
4134
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
4135
|
+
function VisitorToolResultView({
|
|
4136
|
+
result
|
|
4137
|
+
}) {
|
|
4138
|
+
if (result.kind === "entity") {
|
|
4139
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EntityResultCard, { result });
|
|
4140
|
+
}
|
|
4141
|
+
if (result.kind === "collection") {
|
|
4142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CollectionResultCard, { result });
|
|
4143
|
+
}
|
|
4144
|
+
if (result.kind === "signature") {
|
|
4145
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SignatureResultCard, { result });
|
|
4146
|
+
}
|
|
4147
|
+
if (result.kind === "summary") {
|
|
4148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ToolResultCard, { result });
|
|
4149
|
+
}
|
|
4150
|
+
return null;
|
|
4151
|
+
}
|
|
4152
|
+
function isRenderableVisitorToolResult(result) {
|
|
4153
|
+
return result.kind === "summary" || result.kind === "entity" || result.kind === "collection" || result.kind === "signature";
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4156
|
+
// src/react/components/AgentRail/AgentRail.tsx
|
|
4157
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2357
4158
|
function MinimizeIcon() {
|
|
2358
|
-
return /* @__PURE__ */ (0,
|
|
4159
|
+
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
4160
|
"path",
|
|
2360
4161
|
{
|
|
2361
4162
|
d: "M3.5 8h9",
|
|
@@ -2366,7 +4167,7 @@ function MinimizeIcon() {
|
|
|
2366
4167
|
) });
|
|
2367
4168
|
}
|
|
2368
4169
|
function CloseIcon() {
|
|
2369
|
-
return /* @__PURE__ */ (0,
|
|
4170
|
+
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
4171
|
"path",
|
|
2371
4172
|
{
|
|
2372
4173
|
d: "M4 4l8 8M12 4l-8 8",
|
|
@@ -2377,7 +4178,7 @@ function CloseIcon() {
|
|
|
2377
4178
|
) });
|
|
2378
4179
|
}
|
|
2379
4180
|
function NewChatIcon() {
|
|
2380
|
-
return /* @__PURE__ */ (0,
|
|
4181
|
+
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
4182
|
"path",
|
|
2382
4183
|
{
|
|
2383
4184
|
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 +4190,7 @@ function NewChatIcon() {
|
|
|
2389
4190
|
) });
|
|
2390
4191
|
}
|
|
2391
4192
|
function ExpandIcon() {
|
|
2392
|
-
return /* @__PURE__ */ (0,
|
|
4193
|
+
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
4194
|
"path",
|
|
2394
4195
|
{
|
|
2395
4196
|
d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
|
|
@@ -2401,7 +4202,7 @@ function ExpandIcon() {
|
|
|
2401
4202
|
) });
|
|
2402
4203
|
}
|
|
2403
4204
|
function RestoreIcon() {
|
|
2404
|
-
return /* @__PURE__ */ (0,
|
|
4205
|
+
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
4206
|
"path",
|
|
2406
4207
|
{
|
|
2407
4208
|
d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
|
|
@@ -2429,28 +4230,29 @@ function AgentRail({
|
|
|
2429
4230
|
onRetry,
|
|
2430
4231
|
onSubmit,
|
|
2431
4232
|
onFollowUpSelect,
|
|
2432
|
-
onBook
|
|
4233
|
+
onBook,
|
|
4234
|
+
onInputResponse
|
|
2433
4235
|
}) {
|
|
2434
|
-
const transcriptRef = (0,
|
|
4236
|
+
const transcriptRef = (0, import_react10.useRef)(null);
|
|
2435
4237
|
const resolvedBrandLabel = brandLabel.trim();
|
|
2436
4238
|
const resolvedBrandLogoUrl = brandLogoUrl?.trim();
|
|
2437
|
-
const [failedLogoUrl, setFailedLogoUrl] = (0,
|
|
4239
|
+
const [failedLogoUrl, setFailedLogoUrl] = (0, import_react10.useState)(null);
|
|
2438
4240
|
const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
|
|
2439
4241
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2440
4242
|
const brandedTheme = { ...defaultAgentRailTheme, ...theme };
|
|
2441
4243
|
const resolvedTheme = resolvedColorScheme === "dark" ? {
|
|
2442
4244
|
...brandedTheme,
|
|
2443
4245
|
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,
|
|
4246
|
+
brandDeep: theme?.brandDeep ?? defaultDarkAgentRailTheme.brandDeep,
|
|
4247
|
+
brandSoft: theme?.brandSoft ?? `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
|
|
4248
|
+
border: theme?.border ?? defaultDarkAgentRailTheme.border,
|
|
4249
|
+
danger: theme?.danger ?? defaultDarkAgentRailTheme.danger,
|
|
4250
|
+
success: theme?.success ?? defaultDarkAgentRailTheme.success,
|
|
4251
|
+
surface: theme?.surface ?? defaultDarkAgentRailTheme.surface,
|
|
4252
|
+
surfaceMuted: theme?.surfaceMuted ?? defaultDarkAgentRailTheme.surfaceMuted,
|
|
4253
|
+
text: theme?.text ?? defaultDarkAgentRailTheme.text,
|
|
4254
|
+
textMuted: theme?.textMuted ?? defaultDarkAgentRailTheme.textMuted,
|
|
4255
|
+
textSubtle: theme?.textSubtle ?? defaultDarkAgentRailTheme.textSubtle,
|
|
2454
4256
|
visitorBubble: theme?.visitorBubble ?? theme?.brand ?? defaultDarkAgentRailTheme.visitorBubble
|
|
2455
4257
|
} : brandedTheme;
|
|
2456
4258
|
const railStyle = {
|
|
@@ -2473,8 +4275,15 @@ function AgentRail({
|
|
|
2473
4275
|
"--as-font-display": resolvedTheme.fontDisplay,
|
|
2474
4276
|
colorScheme: resolvedColorScheme
|
|
2475
4277
|
};
|
|
2476
|
-
const
|
|
4278
|
+
const pendingInputRequests = (state.pendingInputs ?? []).filter(
|
|
4279
|
+
(request) => shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
4280
|
+
);
|
|
4281
|
+
const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && pendingInputRequests.length > 0;
|
|
4282
|
+
const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
|
|
2477
4283
|
const showActivity = state.toolSteps.length > 0;
|
|
4284
|
+
const visitorToolResults = (state.toolResults ?? []).filter(
|
|
4285
|
+
isRenderableVisitorToolResult
|
|
4286
|
+
);
|
|
2478
4287
|
const hasVisitorMessages2 = state.messages.some(
|
|
2479
4288
|
(message) => message.role === "visitor"
|
|
2480
4289
|
);
|
|
@@ -2484,22 +4293,44 @@ function AgentRail({
|
|
|
2484
4293
|
);
|
|
2485
4294
|
const visibleMessages = hasVisitorMessages2 ? state.messages : [];
|
|
2486
4295
|
const lastMessage = visibleMessages.at(-1);
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
4296
|
+
let lastAgentIndex = -1;
|
|
4297
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4298
|
+
if (visibleMessages[index]?.role === "agent") {
|
|
4299
|
+
lastAgentIndex = index;
|
|
4300
|
+
break;
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
let lastVisitorIndex = -1;
|
|
4304
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4305
|
+
if (visibleMessages[index]?.role === "visitor") {
|
|
4306
|
+
lastVisitorIndex = index;
|
|
4307
|
+
break;
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
const lastIsAgent = lastMessage?.role === "agent";
|
|
4311
|
+
const streamingMessage = state.phase === "streaming" && state.streamingText && !lastIsAgent ? {
|
|
2490
4312
|
createdAt: 0,
|
|
2491
4313
|
id: "streaming-response",
|
|
2492
4314
|
role: "agent",
|
|
2493
4315
|
streaming: true,
|
|
2494
4316
|
text: state.streamingText
|
|
2495
|
-
} : state.pendingOffer ? {
|
|
4317
|
+
} : state.pendingOffer && !lastIsAgent ? {
|
|
2496
4318
|
createdAt: 0,
|
|
2497
4319
|
id: "pending-booking",
|
|
2498
4320
|
role: "agent",
|
|
2499
4321
|
streaming: false,
|
|
2500
4322
|
text: "Pick a date and time that works for you."
|
|
2501
4323
|
} : null;
|
|
2502
|
-
|
|
4324
|
+
const bookingReadyText = state.streamingText || (lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "");
|
|
4325
|
+
const waitingForBooking = !state.pendingOffer && looksLikeBookingReady(bookingReadyText) && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
|
|
4326
|
+
const lastAgentText = lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "";
|
|
4327
|
+
const composerForm = resolveComposerForm({
|
|
4328
|
+
agentText: lastAgentText,
|
|
4329
|
+
cards: extractToolCards(lastAgentText),
|
|
4330
|
+
hasBookingOffer: Boolean(state.pendingOffer) || waitingForBooking,
|
|
4331
|
+
enabled: lastIsAgent && !isBusy
|
|
4332
|
+
});
|
|
4333
|
+
(0, import_react10.useEffect)(() => {
|
|
2503
4334
|
const node = transcriptRef.current;
|
|
2504
4335
|
if (!node) return;
|
|
2505
4336
|
node.scrollTop = node.scrollHeight;
|
|
@@ -2510,11 +4341,13 @@ function AgentRail({
|
|
|
2510
4341
|
state.followUps,
|
|
2511
4342
|
state.journey
|
|
2512
4343
|
]);
|
|
2513
|
-
return /* @__PURE__ */ (0,
|
|
4344
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2514
4345
|
"aside",
|
|
2515
4346
|
{
|
|
2516
|
-
className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4347
|
+
className: `agent-rail not-typeset${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4348
|
+
"data-not-typeset": "",
|
|
2517
4349
|
"data-color-scheme": resolvedColorScheme,
|
|
4350
|
+
spellCheck: false,
|
|
2518
4351
|
style: railStyle,
|
|
2519
4352
|
"aria-label": "Agent conversation",
|
|
2520
4353
|
"aria-modal": mobileFullscreen || expanded ? true : void 0,
|
|
@@ -2522,28 +4355,28 @@ function AgentRail({
|
|
|
2522
4355
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
2523
4356
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
2524
4357
|
children: [
|
|
2525
|
-
/* @__PURE__ */ (0,
|
|
2526
|
-
onCollapse ? /* @__PURE__ */ (0,
|
|
4358
|
+
/* @__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: [
|
|
4359
|
+
onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2527
4360
|
"button",
|
|
2528
4361
|
{
|
|
2529
4362
|
type: "button",
|
|
2530
4363
|
className: "agent-rail__collapse",
|
|
2531
4364
|
"aria-label": "Collapse assist",
|
|
2532
4365
|
onClick: onCollapse,
|
|
2533
|
-
children: /* @__PURE__ */ (0,
|
|
4366
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MinimizeIcon, {})
|
|
2534
4367
|
}
|
|
2535
|
-
) : onClose ? /* @__PURE__ */ (0,
|
|
4368
|
+
) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2536
4369
|
"button",
|
|
2537
4370
|
{
|
|
2538
4371
|
type: "button",
|
|
2539
4372
|
className: "agent-rail__close",
|
|
2540
4373
|
"aria-label": "Close agent",
|
|
2541
4374
|
onClick: onClose,
|
|
2542
|
-
children: /* @__PURE__ */ (0,
|
|
4375
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CloseIcon, {})
|
|
2543
4376
|
}
|
|
2544
|
-
) : /* @__PURE__ */ (0,
|
|
2545
|
-
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0,
|
|
2546
|
-
showBrandLogo ? /* @__PURE__ */ (0,
|
|
4377
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
|
|
4378
|
+
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "agent-rail__identity", children: [
|
|
4379
|
+
showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2547
4380
|
"img",
|
|
2548
4381
|
{
|
|
2549
4382
|
className: "agent-rail__brand-logo",
|
|
@@ -2554,10 +4387,10 @@ function AgentRail({
|
|
|
2554
4387
|
}
|
|
2555
4388
|
}
|
|
2556
4389
|
) }) : null,
|
|
2557
|
-
resolvedBrandLabel ? /* @__PURE__ */ (0,
|
|
4390
|
+
resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
|
|
2558
4391
|
] }) : null,
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2560
|
-
onReset ? /* @__PURE__ */ (0,
|
|
4392
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "agent-rail__actions", children: [
|
|
4393
|
+
onReset ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2561
4394
|
"button",
|
|
2562
4395
|
{
|
|
2563
4396
|
type: "button",
|
|
@@ -2565,24 +4398,24 @@ function AgentRail({
|
|
|
2565
4398
|
"aria-label": "Start a new conversation",
|
|
2566
4399
|
disabled: !hasVisitorMessages2,
|
|
2567
4400
|
onClick: onReset,
|
|
2568
|
-
children: /* @__PURE__ */ (0,
|
|
4401
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NewChatIcon, {})
|
|
2569
4402
|
}
|
|
2570
4403
|
) : null,
|
|
2571
|
-
onExpandToggle ? /* @__PURE__ */ (0,
|
|
4404
|
+
onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2572
4405
|
"button",
|
|
2573
4406
|
{
|
|
2574
4407
|
type: "button",
|
|
2575
4408
|
className: "agent-rail__expand",
|
|
2576
4409
|
"aria-label": expanded ? "Exit full screen" : "Open full screen",
|
|
2577
4410
|
onClick: onExpandToggle,
|
|
2578
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
4411
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ExpandIcon, {})
|
|
2579
4412
|
}
|
|
2580
4413
|
) : null
|
|
2581
4414
|
] })
|
|
2582
4415
|
] }) }),
|
|
2583
|
-
/* @__PURE__ */ (0,
|
|
2584
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ (0,
|
|
2585
|
-
greeting?.role === "agent" ? /* @__PURE__ */ (0,
|
|
4416
|
+
/* @__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: [
|
|
4417
|
+
!hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
4418
|
+
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2586
4419
|
MessageBubble,
|
|
2587
4420
|
{
|
|
2588
4421
|
message: greeting,
|
|
@@ -2590,7 +4423,7 @@ function AgentRail({
|
|
|
2590
4423
|
onBook
|
|
2591
4424
|
}
|
|
2592
4425
|
) : null,
|
|
2593
|
-
showIdleFollowUps ? /* @__PURE__ */ (0,
|
|
4426
|
+
showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2594
4427
|
FollowUpChips,
|
|
2595
4428
|
{
|
|
2596
4429
|
suggestions: state.followUps,
|
|
@@ -2598,64 +4431,94 @@ function AgentRail({
|
|
|
2598
4431
|
label: "Start here",
|
|
2599
4432
|
onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
|
|
2600
4433
|
}
|
|
2601
|
-
) }) : null
|
|
4434
|
+
) }) : null,
|
|
4435
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4436
|
+
AgentActivityBubble,
|
|
4437
|
+
{
|
|
4438
|
+
brandLabel: resolvedBrandLabel,
|
|
4439
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4440
|
+
failed: state.phase === "error",
|
|
4441
|
+
steps: state.toolSteps
|
|
4442
|
+
}
|
|
4443
|
+
) : null,
|
|
4444
|
+
visitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VisitorToolResultView, { result }, result.id)),
|
|
4445
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4446
|
+
HumanInputCard,
|
|
4447
|
+
{
|
|
4448
|
+
request,
|
|
4449
|
+
onRespond: onInputResponse
|
|
4450
|
+
},
|
|
4451
|
+
request.requestId
|
|
4452
|
+
))
|
|
2602
4453
|
] }) : null,
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
4454
|
+
visibleMessages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__turn-block", children: [
|
|
4455
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4456
|
+
MessageBubble,
|
|
4457
|
+
{
|
|
4458
|
+
message,
|
|
4459
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4460
|
+
offer: index === lastAgentIndex ? state.pendingOffer : void 0,
|
|
4461
|
+
onBook
|
|
4462
|
+
}
|
|
4463
|
+
),
|
|
4464
|
+
index === lastVisitorIndex ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
4465
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4466
|
+
AgentActivityBubble,
|
|
4467
|
+
{
|
|
4468
|
+
brandLabel: resolvedBrandLabel,
|
|
4469
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4470
|
+
failed: state.phase === "error",
|
|
4471
|
+
steps: state.toolSteps
|
|
4472
|
+
}
|
|
4473
|
+
) : null,
|
|
4474
|
+
visitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VisitorToolResultView, { result }, result.id)),
|
|
4475
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4476
|
+
HumanInputCard,
|
|
4477
|
+
{
|
|
4478
|
+
request,
|
|
4479
|
+
onRespond: onInputResponse
|
|
4480
|
+
},
|
|
4481
|
+
request.requestId
|
|
4482
|
+
))
|
|
4483
|
+
] }) : null
|
|
4484
|
+
] }, message.id)),
|
|
4485
|
+
streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2622
4486
|
MessageBubble,
|
|
2623
4487
|
{
|
|
2624
|
-
message:
|
|
4488
|
+
message: streamingMessage,
|
|
2625
4489
|
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4490
|
+
offer: state.pendingOffer,
|
|
2626
4491
|
onBook
|
|
2627
4492
|
}
|
|
2628
4493
|
) : null,
|
|
2629
|
-
|
|
2630
|
-
|
|
4494
|
+
waitingForBooking ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4495
|
+
BookingCard,
|
|
2631
4496
|
{
|
|
2632
|
-
|
|
2633
|
-
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
2634
|
-
offer: state.pendingOffer,
|
|
2635
|
-
onBook
|
|
4497
|
+
offer: { type: "booking_offer", eventTypes: [], slots: [] }
|
|
2636
4498
|
}
|
|
2637
4499
|
) : null,
|
|
2638
|
-
state.error ? /* @__PURE__ */ (0,
|
|
2639
|
-
/* @__PURE__ */ (0,
|
|
2640
|
-
/* @__PURE__ */ (0,
|
|
2641
|
-
/* @__PURE__ */ (0,
|
|
4500
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
|
|
4501
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
4502
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "Something went wrong" }),
|
|
4503
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: state.error })
|
|
2642
4504
|
] }),
|
|
2643
|
-
onRetry ? /* @__PURE__ */ (0,
|
|
4505
|
+
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
2644
4506
|
] }) : null
|
|
2645
4507
|
] }) }),
|
|
2646
|
-
/* @__PURE__ */ (0,
|
|
2647
|
-
/* @__PURE__ */ (0,
|
|
4508
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
|
|
4509
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2648
4510
|
Composer,
|
|
2649
4511
|
{
|
|
2650
4512
|
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
2651
4513
|
disabled: isBusy,
|
|
4514
|
+
form: composerForm,
|
|
2652
4515
|
placeholder: composerPlaceholder,
|
|
2653
4516
|
onSubmit
|
|
2654
4517
|
}
|
|
2655
4518
|
),
|
|
2656
|
-
/* @__PURE__ */ (0,
|
|
2657
|
-
/* @__PURE__ */ (0,
|
|
2658
|
-
/* @__PURE__ */ (0,
|
|
4519
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { children: [
|
|
4520
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "AI can make mistakes. Check important info." }),
|
|
4521
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: poweredByLabel })
|
|
2659
4522
|
] }) })
|
|
2660
4523
|
] })
|
|
2661
4524
|
]
|
|
@@ -2664,9 +4527,9 @@ function AgentRail({
|
|
|
2664
4527
|
}
|
|
2665
4528
|
|
|
2666
4529
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
2667
|
-
var
|
|
4530
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2668
4531
|
function SparklesIcon() {
|
|
2669
|
-
return /* @__PURE__ */ (0,
|
|
4532
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2670
4533
|
"svg",
|
|
2671
4534
|
{
|
|
2672
4535
|
className: "assist-edge-tab__sparkles",
|
|
@@ -2674,21 +4537,21 @@ function SparklesIcon() {
|
|
|
2674
4537
|
fill: "none",
|
|
2675
4538
|
"aria-hidden": "true",
|
|
2676
4539
|
children: [
|
|
2677
|
-
/* @__PURE__ */ (0,
|
|
4540
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2678
4541
|
"path",
|
|
2679
4542
|
{
|
|
2680
4543
|
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
4544
|
fill: "currentColor"
|
|
2682
4545
|
}
|
|
2683
4546
|
),
|
|
2684
|
-
/* @__PURE__ */ (0,
|
|
4547
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2685
4548
|
"path",
|
|
2686
4549
|
{
|
|
2687
4550
|
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
4551
|
fill: "currentColor"
|
|
2689
4552
|
}
|
|
2690
4553
|
),
|
|
2691
|
-
/* @__PURE__ */ (0,
|
|
4554
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2692
4555
|
"path",
|
|
2693
4556
|
{
|
|
2694
4557
|
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 +4565,7 @@ function SparklesIcon() {
|
|
|
2702
4565
|
function TabMarkIcon({ customIconUrl }) {
|
|
2703
4566
|
const url = customIconUrl?.trim();
|
|
2704
4567
|
if (url) {
|
|
2705
|
-
return /* @__PURE__ */ (0,
|
|
4568
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2706
4569
|
"img",
|
|
2707
4570
|
{
|
|
2708
4571
|
alt: "",
|
|
@@ -2712,10 +4575,10 @@ function TabMarkIcon({ customIconUrl }) {
|
|
|
2712
4575
|
}
|
|
2713
4576
|
);
|
|
2714
4577
|
}
|
|
2715
|
-
return /* @__PURE__ */ (0,
|
|
4578
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SparklesIcon, {});
|
|
2716
4579
|
}
|
|
2717
4580
|
function ChevronLeftIcon() {
|
|
2718
|
-
return /* @__PURE__ */ (0,
|
|
4581
|
+
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
4582
|
"path",
|
|
2720
4583
|
{
|
|
2721
4584
|
d: "M10 4L6 8l4 4",
|
|
@@ -2727,7 +4590,7 @@ function ChevronLeftIcon() {
|
|
|
2727
4590
|
) });
|
|
2728
4591
|
}
|
|
2729
4592
|
function ChevronDownIcon() {
|
|
2730
|
-
return /* @__PURE__ */ (0,
|
|
4593
|
+
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
4594
|
"path",
|
|
2732
4595
|
{
|
|
2733
4596
|
d: "M4 6l4 4 4-4",
|
|
@@ -2739,7 +4602,7 @@ function ChevronDownIcon() {
|
|
|
2739
4602
|
) });
|
|
2740
4603
|
}
|
|
2741
4604
|
function DragDots() {
|
|
2742
|
-
return /* @__PURE__ */ (0,
|
|
4605
|
+
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
4606
|
}
|
|
2744
4607
|
var VARIANT_COPY = {
|
|
2745
4608
|
outline: { label: "Ask anything", aria: "Ask anything" },
|
|
@@ -2768,6 +4631,7 @@ function AssistEdgeTab({
|
|
|
2768
4631
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2769
4632
|
const copy = VARIANT_COPY[variant];
|
|
2770
4633
|
const visibleLabel = label?.trim() || copy.label;
|
|
4634
|
+
const alignment = along < 50 ? "start" : along > 50 ? "end" : "center";
|
|
2771
4635
|
const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
|
|
2772
4636
|
const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
|
|
2773
4637
|
const resolvedBorderColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.border : borderColor;
|
|
@@ -2784,11 +4648,11 @@ function AssistEdgeTab({
|
|
|
2784
4648
|
...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
|
|
2785
4649
|
colorScheme: resolvedColorScheme
|
|
2786
4650
|
};
|
|
2787
|
-
return /* @__PURE__ */ (0,
|
|
4651
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2788
4652
|
"button",
|
|
2789
4653
|
{
|
|
2790
4654
|
type: "button",
|
|
2791
|
-
className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
|
|
4655
|
+
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
4656
|
"data-color-scheme": resolvedColorScheme,
|
|
2793
4657
|
style,
|
|
2794
4658
|
"aria-label": `Open ${visibleLabel}`,
|
|
@@ -2796,15 +4660,15 @@ function AssistEdgeTab({
|
|
|
2796
4660
|
tabIndex: visible ? 0 : -1,
|
|
2797
4661
|
onClick: onOpen,
|
|
2798
4662
|
children: [
|
|
2799
|
-
mobile ? /* @__PURE__ */ (0,
|
|
2800
|
-
/* @__PURE__ */ (0,
|
|
4663
|
+
mobile ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4664
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2801
4665
|
"span",
|
|
2802
4666
|
{
|
|
2803
4667
|
className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
|
|
2804
4668
|
"aria-hidden": "true",
|
|
2805
4669
|
children: [
|
|
2806
|
-
/* @__PURE__ */ (0,
|
|
2807
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4670
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4671
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2808
4672
|
"img",
|
|
2809
4673
|
{
|
|
2810
4674
|
className: "assist-edge-tab__logo",
|
|
@@ -2818,11 +4682,11 @@ function AssistEdgeTab({
|
|
|
2818
4682
|
]
|
|
2819
4683
|
}
|
|
2820
4684
|
),
|
|
2821
|
-
/* @__PURE__ */ (0,
|
|
2822
|
-
] }) : variant === "outline" ? /* @__PURE__ */ (0,
|
|
2823
|
-
/* @__PURE__ */ (0,
|
|
2824
|
-
/* @__PURE__ */ (0,
|
|
2825
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4685
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel })
|
|
4686
|
+
] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4687
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4688
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4689
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2826
4690
|
"img",
|
|
2827
4691
|
{
|
|
2828
4692
|
className: "assist-edge-tab__logo",
|
|
@@ -2834,18 +4698,18 @@ function AssistEdgeTab({
|
|
|
2834
4698
|
}
|
|
2835
4699
|
) : null
|
|
2836
4700
|
] }),
|
|
2837
|
-
/* @__PURE__ */ (0,
|
|
2838
|
-
/* @__PURE__ */ (0,
|
|
4701
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4702
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronDownIcon, {})
|
|
2839
4703
|
] }) : null,
|
|
2840
|
-
variant === "ask" ? /* @__PURE__ */ (0,
|
|
2841
|
-
/* @__PURE__ */ (0,
|
|
2842
|
-
/* @__PURE__ */ (0,
|
|
2843
|
-
/* @__PURE__ */ (0,
|
|
4704
|
+
variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4705
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronLeftIcon, {}),
|
|
4706
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4707
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DragDots, {})
|
|
2844
4708
|
] }) : null,
|
|
2845
|
-
variant === "fill" ? /* @__PURE__ */ (0,
|
|
2846
|
-
/* @__PURE__ */ (0,
|
|
2847
|
-
/* @__PURE__ */ (0,
|
|
2848
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4709
|
+
variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4710
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4711
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4712
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2849
4713
|
"img",
|
|
2850
4714
|
{
|
|
2851
4715
|
className: "assist-edge-tab__logo",
|
|
@@ -2857,8 +4721,8 @@ function AssistEdgeTab({
|
|
|
2857
4721
|
}
|
|
2858
4722
|
) : null
|
|
2859
4723
|
] }),
|
|
2860
|
-
/* @__PURE__ */ (0,
|
|
2861
|
-
/* @__PURE__ */ (0,
|
|
4724
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4725
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronLeftIcon, {})
|
|
2862
4726
|
] }) : null
|
|
2863
4727
|
]
|
|
2864
4728
|
}
|
|
@@ -2866,7 +4730,7 @@ function AssistEdgeTab({
|
|
|
2866
4730
|
}
|
|
2867
4731
|
|
|
2868
4732
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
2869
|
-
var
|
|
4733
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2870
4734
|
function AgentWidget({
|
|
2871
4735
|
indexId,
|
|
2872
4736
|
customerId,
|
|
@@ -2879,13 +4743,14 @@ function AgentWidget({
|
|
|
2879
4743
|
pageShift = true,
|
|
2880
4744
|
registerPanelController = false,
|
|
2881
4745
|
colorScheme = "auto",
|
|
2882
|
-
branding
|
|
4746
|
+
branding,
|
|
4747
|
+
toolResultRegistry
|
|
2883
4748
|
}) {
|
|
2884
4749
|
const isMobile = useIsMobile();
|
|
2885
4750
|
const placement = normalizeAgentPlacement(placementInput);
|
|
2886
|
-
const railSlotRef = (0,
|
|
2887
|
-
const [railCollapsed, setRailCollapsed] = (0,
|
|
2888
|
-
const [railExpanded, setRailExpanded] = (0,
|
|
4751
|
+
const railSlotRef = (0, import_react11.useRef)(null);
|
|
4752
|
+
const [railCollapsed, setRailCollapsed] = (0, import_react11.useState)(defaultCollapsed);
|
|
4753
|
+
const [railExpanded, setRailExpanded] = (0, import_react11.useState)(false);
|
|
2889
4754
|
const pageShiftActive = shouldApplyPageShift({
|
|
2890
4755
|
pageShift,
|
|
2891
4756
|
isMobile,
|
|
@@ -2896,14 +4761,15 @@ function AgentWidget({
|
|
|
2896
4761
|
active: pageShiftActive,
|
|
2897
4762
|
railSlotRef
|
|
2898
4763
|
});
|
|
2899
|
-
const { state, reset, retry, submit } = useAgentChat({
|
|
4764
|
+
const { state, reset, retry, respondToInput, respondToToolInput, submit } = useAgentChat({
|
|
2900
4765
|
customerId,
|
|
2901
4766
|
getUnpublishedPreviewGrant,
|
|
2902
4767
|
indexId,
|
|
2903
4768
|
previewBuildId,
|
|
2904
4769
|
version,
|
|
2905
4770
|
runtimeOrigin,
|
|
2906
|
-
greeting: branding?.greeting
|
|
4771
|
+
greeting: branding?.greeting,
|
|
4772
|
+
toolResultRegistry
|
|
2907
4773
|
});
|
|
2908
4774
|
const agentName = branding?.agentName ?? "";
|
|
2909
4775
|
const tabLabel = branding?.tabLabel ?? agentName;
|
|
@@ -2924,22 +4790,24 @@ function AgentWidget({
|
|
|
2924
4790
|
} : {},
|
|
2925
4791
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
2926
4792
|
};
|
|
2927
|
-
(0,
|
|
4793
|
+
(0, import_react11.useEffect)(() => {
|
|
2928
4794
|
if (!registerPanelController) return;
|
|
2929
4795
|
registerAgentPanelController(customerId, {
|
|
2930
4796
|
open: () => setRailCollapsed(false),
|
|
2931
4797
|
close: () => {
|
|
2932
4798
|
setRailCollapsed(true);
|
|
2933
4799
|
setRailExpanded(false);
|
|
2934
|
-
}
|
|
4800
|
+
},
|
|
4801
|
+
reset,
|
|
4802
|
+
submit
|
|
2935
4803
|
});
|
|
2936
4804
|
return () => unregisterAgentPanelController(customerId);
|
|
2937
|
-
}, [customerId, registerPanelController]);
|
|
4805
|
+
}, [customerId, registerPanelController, reset, submit]);
|
|
2938
4806
|
async function handleSubmit(message) {
|
|
2939
4807
|
if (isMobile) setRailCollapsed(false);
|
|
2940
4808
|
await submit(message);
|
|
2941
4809
|
}
|
|
2942
|
-
(0,
|
|
4810
|
+
(0, import_react11.useEffect)(() => {
|
|
2943
4811
|
if (railCollapsed) return;
|
|
2944
4812
|
const handleKeyDown = (event) => {
|
|
2945
4813
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -2973,19 +4841,19 @@ function AgentWidget({
|
|
|
2973
4841
|
window.addEventListener("keydown", handleKeyDown);
|
|
2974
4842
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2975
4843
|
}, [isMobile, railCollapsed, railExpanded]);
|
|
2976
|
-
return /* @__PURE__ */ (0,
|
|
2977
|
-
/* @__PURE__ */ (0,
|
|
4844
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "webless-agent-root", children: [
|
|
4845
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2978
4846
|
"div",
|
|
2979
4847
|
{
|
|
2980
4848
|
className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
|
|
2981
|
-
children: /* @__PURE__ */ (0,
|
|
4849
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2982
4850
|
"div",
|
|
2983
4851
|
{
|
|
2984
4852
|
ref: railSlotRef,
|
|
2985
4853
|
className: "webless-agent-root__rail-slot",
|
|
2986
4854
|
inert: railCollapsed || void 0,
|
|
2987
4855
|
"aria-hidden": railCollapsed,
|
|
2988
|
-
children: /* @__PURE__ */ (0,
|
|
4856
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2989
4857
|
AgentRail,
|
|
2990
4858
|
{
|
|
2991
4859
|
theme,
|
|
@@ -3003,6 +4871,8 @@ function AgentWidget({
|
|
|
3003
4871
|
onSubmit: handleSubmit,
|
|
3004
4872
|
onReset: reset,
|
|
3005
4873
|
onRetry: () => void retry(),
|
|
4874
|
+
onInputResponse: (response) => void respondToInput(response),
|
|
4875
|
+
onToolInput: (surface, values) => void respondToToolInput(surface, values),
|
|
3006
4876
|
onFollowUpSelect: (label) => void handleSubmit(label),
|
|
3007
4877
|
onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
|
|
3008
4878
|
}
|
|
@@ -3011,7 +4881,7 @@ function AgentWidget({
|
|
|
3011
4881
|
)
|
|
3012
4882
|
}
|
|
3013
4883
|
),
|
|
3014
|
-
railCollapsed ? /* @__PURE__ */ (0,
|
|
4884
|
+
railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3015
4885
|
AssistEdgeTab,
|
|
3016
4886
|
{
|
|
3017
4887
|
variant: placement.variant,
|
|
@@ -3050,12 +4920,12 @@ function readUnpublishedPreviewBuildId(href) {
|
|
|
3050
4920
|
}
|
|
3051
4921
|
|
|
3052
4922
|
// src/embed/AgentWidget.tsx
|
|
3053
|
-
var
|
|
4923
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
3054
4924
|
function AgentWidget2({
|
|
3055
4925
|
manifest
|
|
3056
4926
|
}) {
|
|
3057
4927
|
const defaultCollapsed = manifest.version !== "unpublished";
|
|
3058
|
-
return /* @__PURE__ */ (0,
|
|
4928
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3059
4929
|
AgentWidget,
|
|
3060
4930
|
{
|
|
3061
4931
|
indexId: manifest.indexId,
|
|
@@ -3139,7 +5009,7 @@ function normalizeAgentBranding(branding) {
|
|
|
3139
5009
|
}
|
|
3140
5010
|
|
|
3141
5011
|
// src/embed/mount.tsx
|
|
3142
|
-
var
|
|
5012
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
3143
5013
|
var mountedHandles = /* @__PURE__ */ new Map();
|
|
3144
5014
|
var latestCustomerId = null;
|
|
3145
5015
|
function resolveMountHost(manifest, script) {
|
|
@@ -3169,7 +5039,7 @@ function mountAgent(input) {
|
|
|
3169
5039
|
const host = createHost(manifest.customerId);
|
|
3170
5040
|
mountTarget.append(host);
|
|
3171
5041
|
const root = (0, import_client5.createRoot)(host);
|
|
3172
|
-
root.render(/* @__PURE__ */ (0,
|
|
5042
|
+
root.render(/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AgentWidget2, { manifest }));
|
|
3173
5043
|
const handle = {
|
|
3174
5044
|
customerId: manifest.customerId,
|
|
3175
5045
|
manifest,
|
|
@@ -3262,6 +5132,8 @@ function createWeblessAgentPublicApi(manifest) {
|
|
|
3262
5132
|
mountAgent,
|
|
3263
5133
|
normalizeAgentPlacement,
|
|
3264
5134
|
normalizeAgentTagManifest,
|
|
5135
|
+
resetAgentPanel,
|
|
5136
|
+
submitAgentPanel,
|
|
3265
5137
|
unmountAgent
|
|
3266
5138
|
});
|
|
3267
5139
|
//# sourceMappingURL=embed.cjs.map
|