@webless/agent 0.6.3 → 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/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/react.cjs
CHANGED
|
@@ -24,18 +24,20 @@ __export(react_exports, {
|
|
|
24
24
|
AgentWidget: () => AgentWidget,
|
|
25
25
|
AssistEdgeTab: () => AssistEdgeTab,
|
|
26
26
|
DEFAULT_AGENT_PLACEMENT: () => DEFAULT_AGENT_PLACEMENT,
|
|
27
|
+
builtInVisitorToolResultRegistry: () => builtInVisitorToolResultRegistry,
|
|
27
28
|
createIdleSuggestions: () => createIdleSuggestions,
|
|
28
29
|
defaultAgentRailTheme: () => defaultAgentRailTheme,
|
|
29
30
|
defaultDarkAgentRailTheme: () => defaultDarkAgentRailTheme,
|
|
30
31
|
hasVisitorMessages: () => hasVisitorMessages,
|
|
31
32
|
isAgentBusy: () => isAgentBusy,
|
|
32
33
|
normalizeAgentPlacement: () => normalizeAgentPlacement,
|
|
34
|
+
presentVisitorToolResult: () => presentVisitorToolResult,
|
|
33
35
|
useAgentChat: () => useAgentChat
|
|
34
36
|
});
|
|
35
37
|
module.exports = __toCommonJS(react_exports);
|
|
36
38
|
|
|
37
39
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
38
|
-
var
|
|
40
|
+
var import_react11 = require("react");
|
|
39
41
|
|
|
40
42
|
// src/react/page-shift.ts
|
|
41
43
|
var import_react = require("react");
|
|
@@ -126,6 +128,26 @@ var import_client2 = require("eve/client");
|
|
|
126
128
|
// src/runtime/capability.ts
|
|
127
129
|
var import_client = require("eve/client");
|
|
128
130
|
var MAX_REFRESH_SKEW_MS = 3e4;
|
|
131
|
+
var LOCAL_LOOPBACK_ORIGINS = [
|
|
132
|
+
"http://127.0.0.1:3010",
|
|
133
|
+
"http://127.0.0.1:3001"
|
|
134
|
+
];
|
|
135
|
+
function isLoopbackRuntimeOrigin(origin) {
|
|
136
|
+
try {
|
|
137
|
+
const host = new URL(origin).hostname;
|
|
138
|
+
return host === "127.0.0.1" || host === "localhost";
|
|
139
|
+
} catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function localBootstrapOrigins(origin) {
|
|
144
|
+
const normalized = origin.replace(/\/$/, "");
|
|
145
|
+
if (!isLoopbackRuntimeOrigin(normalized)) return [normalized];
|
|
146
|
+
return [
|
|
147
|
+
normalized,
|
|
148
|
+
...LOCAL_LOOPBACK_ORIGINS.filter((candidate) => candidate !== normalized)
|
|
149
|
+
];
|
|
150
|
+
}
|
|
129
151
|
function isRecord(value) {
|
|
130
152
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
131
153
|
}
|
|
@@ -172,20 +194,31 @@ function createAgentRuntimeCapability(options) {
|
|
|
172
194
|
);
|
|
173
195
|
}
|
|
174
196
|
}
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
197
|
+
const bootstrapBody = JSON.stringify({
|
|
198
|
+
clientSessionId: options.visitorSessionId,
|
|
199
|
+
indexId: options.indexId,
|
|
200
|
+
...previewBuildId ? { previewBuildId } : {},
|
|
201
|
+
...previewGrant ? { previewGrant } : {},
|
|
202
|
+
version: options.version
|
|
203
|
+
});
|
|
204
|
+
const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
|
|
205
|
+
body: bootstrapBody,
|
|
206
|
+
headers: { "content-type": "application/json" },
|
|
207
|
+
method: "POST"
|
|
208
|
+
});
|
|
209
|
+
let response;
|
|
210
|
+
let lastError;
|
|
211
|
+
for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
|
|
212
|
+
try {
|
|
213
|
+
response = await postBootstrap(origin);
|
|
214
|
+
break;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
lastError = error;
|
|
187
217
|
}
|
|
188
|
-
|
|
218
|
+
}
|
|
219
|
+
if (!response) {
|
|
220
|
+
throw lastError instanceof Error ? lastError : new Error("Agent Runtime is unavailable.");
|
|
221
|
+
}
|
|
189
222
|
if (!response.ok) {
|
|
190
223
|
throw new Error(await readBootstrapError(response));
|
|
191
224
|
}
|
|
@@ -347,6 +380,226 @@ function clearPersistedAgentSession(visitorSessionId, options) {
|
|
|
347
380
|
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
348
381
|
}
|
|
349
382
|
|
|
383
|
+
// src/runtime/tool-ui.ts
|
|
384
|
+
var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
|
|
385
|
+
function isRecord2(value) {
|
|
386
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
387
|
+
}
|
|
388
|
+
function isJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
389
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
393
|
+
if (typeof value !== "object") return false;
|
|
394
|
+
if (seen.has(value)) return false;
|
|
395
|
+
seen.add(value);
|
|
396
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, seen)) : Object.entries(value).every(
|
|
397
|
+
([key, item]) => typeof key === "string" && isJsonValue(item, seen)
|
|
398
|
+
);
|
|
399
|
+
seen.delete(value);
|
|
400
|
+
return valid;
|
|
401
|
+
}
|
|
402
|
+
function boundedString(value, max) {
|
|
403
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > max) {
|
|
404
|
+
return void 0;
|
|
405
|
+
}
|
|
406
|
+
return value.trim();
|
|
407
|
+
}
|
|
408
|
+
function numberValue(value) {
|
|
409
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
410
|
+
}
|
|
411
|
+
function integerValue(value) {
|
|
412
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8e3 ? value : void 0;
|
|
413
|
+
}
|
|
414
|
+
function isFieldKind(value) {
|
|
415
|
+
return typeof value === "string" && [
|
|
416
|
+
"text",
|
|
417
|
+
"textarea",
|
|
418
|
+
"email",
|
|
419
|
+
"number",
|
|
420
|
+
"select",
|
|
421
|
+
"multi-select",
|
|
422
|
+
"checkbox",
|
|
423
|
+
"confirmation",
|
|
424
|
+
"radio",
|
|
425
|
+
"date",
|
|
426
|
+
"time",
|
|
427
|
+
"date-time",
|
|
428
|
+
"calendar",
|
|
429
|
+
"range",
|
|
430
|
+
"json"
|
|
431
|
+
].includes(value);
|
|
432
|
+
}
|
|
433
|
+
function parseField(value) {
|
|
434
|
+
if (!isRecord2(value)) return null;
|
|
435
|
+
if (!hasOnlyKeys(value, [
|
|
436
|
+
"description",
|
|
437
|
+
"kind",
|
|
438
|
+
"label",
|
|
439
|
+
"max",
|
|
440
|
+
"maxItems",
|
|
441
|
+
"maxLength",
|
|
442
|
+
"min",
|
|
443
|
+
"minLength",
|
|
444
|
+
"options",
|
|
445
|
+
"path",
|
|
446
|
+
"placeholder",
|
|
447
|
+
"required",
|
|
448
|
+
"step",
|
|
449
|
+
"defaultValue"
|
|
450
|
+
])) {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
if (!isFieldKind(value.kind)) return null;
|
|
454
|
+
const path = boundedString(value.path, 160);
|
|
455
|
+
const label = boundedString(value.label, 160);
|
|
456
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
457
|
+
const placeholder = value.placeholder === void 0 || typeof value.placeholder !== "string" ? void 0 : value.placeholder;
|
|
458
|
+
const required = value.required === void 0 || typeof value.required !== "boolean" ? void 0 : value.required;
|
|
459
|
+
const min = value.min === void 0 ? void 0 : numberValue(value.min);
|
|
460
|
+
const max = value.max === void 0 ? void 0 : numberValue(value.max);
|
|
461
|
+
const maxItems = value.maxItems === void 0 ? void 0 : integerValue(value.maxItems);
|
|
462
|
+
const step = value.step === void 0 ? void 0 : numberValue(value.step);
|
|
463
|
+
const minLength = value.minLength === void 0 ? void 0 : integerValue(value.minLength);
|
|
464
|
+
const maxLength = value.maxLength === void 0 ? void 0 : integerValue(value.maxLength);
|
|
465
|
+
const defaultValue = value.defaultValue === void 0 || !isJsonValue(value.defaultValue) ? void 0 : value.defaultValue;
|
|
466
|
+
if (!path || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || !label) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
if (value.description !== void 0 && !description) return null;
|
|
470
|
+
if (value.placeholder !== void 0 && placeholder === void 0) return null;
|
|
471
|
+
if (value.required !== void 0 && required === void 0) return null;
|
|
472
|
+
if (value.min !== void 0 && min === void 0) return null;
|
|
473
|
+
if (value.max !== void 0 && max === void 0) return null;
|
|
474
|
+
if (value.maxItems !== void 0 && (maxItems === void 0 || maxItems < 1 || maxItems > 100))
|
|
475
|
+
return null;
|
|
476
|
+
if (value.step !== void 0 && step === void 0) return null;
|
|
477
|
+
if (value.minLength !== void 0 && minLength === void 0) return null;
|
|
478
|
+
if (value.maxLength !== void 0 && maxLength === void 0) return null;
|
|
479
|
+
if (value.defaultValue !== void 0 && defaultValue === void 0)
|
|
480
|
+
return null;
|
|
481
|
+
if (value.options !== void 0) {
|
|
482
|
+
if (!Array.isArray(value.options) || value.options.length > 100)
|
|
483
|
+
return null;
|
|
484
|
+
for (const option of value.options) {
|
|
485
|
+
if (!isRecord2(option) || !boundedString(option.label, 160) || !isJsonValue(option.value)) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
kind: value.kind,
|
|
492
|
+
path,
|
|
493
|
+
label,
|
|
494
|
+
...description ? { description } : {},
|
|
495
|
+
...placeholder !== void 0 ? { placeholder } : {},
|
|
496
|
+
...required !== void 0 ? { required } : {},
|
|
497
|
+
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
498
|
+
...value.options !== void 0 ? { options: value.options } : {},
|
|
499
|
+
...min !== void 0 ? { min } : {},
|
|
500
|
+
...max !== void 0 ? { max } : {},
|
|
501
|
+
...maxItems !== void 0 ? { maxItems } : {},
|
|
502
|
+
...step !== void 0 ? { step } : {},
|
|
503
|
+
...minLength !== void 0 ? { minLength } : {},
|
|
504
|
+
...maxLength !== void 0 ? { maxLength } : {}
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function parseStep(value) {
|
|
508
|
+
if (!isRecord2(value)) return null;
|
|
509
|
+
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
const id = boundedString(value.id, 80);
|
|
513
|
+
const label = boundedString(value.label, 160);
|
|
514
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
515
|
+
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(
|
|
516
|
+
(path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
|
|
517
|
+
)) {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
id,
|
|
522
|
+
label,
|
|
523
|
+
fieldPaths: value.fieldPaths,
|
|
524
|
+
...description ? { description } : {}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function parseAction(value) {
|
|
528
|
+
if (!isRecord2(value)) return null;
|
|
529
|
+
if (!hasOnlyKeys(value, ["id", "label"])) return null;
|
|
530
|
+
const label = boundedString(value.label, 80);
|
|
531
|
+
if (value.id !== "submit" && value.id !== "back" && value.id !== "next" && value.id !== "reset" || !label) {
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
id: value.id,
|
|
536
|
+
label
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function parseAgentToolUiSurface(value) {
|
|
540
|
+
if (!isRecord2(value) || value.schemaVersion !== AGENT_TOOL_UI_SCHEMA_VERSION)
|
|
541
|
+
return null;
|
|
542
|
+
if (!hasOnlyKeys(value, [
|
|
543
|
+
"actions",
|
|
544
|
+
"description",
|
|
545
|
+
"fields",
|
|
546
|
+
"id",
|
|
547
|
+
"operationId",
|
|
548
|
+
"requestId",
|
|
549
|
+
"schemaVersion",
|
|
550
|
+
"steps",
|
|
551
|
+
"submitLabel",
|
|
552
|
+
"title",
|
|
553
|
+
"toolSlug",
|
|
554
|
+
"values"
|
|
555
|
+
])) {
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
const id = boundedString(value.id, 200);
|
|
559
|
+
const title = boundedString(value.title, 200);
|
|
560
|
+
const toolSlug = boundedString(value.toolSlug, 200);
|
|
561
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
562
|
+
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
563
|
+
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
564
|
+
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
565
|
+
if (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
const fields = value.fields.map(parseField);
|
|
569
|
+
if (fields.some((field) => field === null)) return null;
|
|
570
|
+
const steps = value.steps === void 0 ? void 0 : Array.isArray(value.steps) && value.steps.length <= 8 ? value.steps.map(parseStep) : null;
|
|
571
|
+
if (steps?.some((step) => step === null)) return null;
|
|
572
|
+
const actions = value.actions === void 0 ? void 0 : Array.isArray(value.actions) && value.actions.length <= 8 ? value.actions.map(parseAction) : null;
|
|
573
|
+
if (actions?.some((action) => action === null)) return null;
|
|
574
|
+
if (value.description !== void 0 && !description) return null;
|
|
575
|
+
if (value.operationId !== void 0 && !operationId) return null;
|
|
576
|
+
if (value.requestId !== void 0 && !requestId) return null;
|
|
577
|
+
if (value.submitLabel !== void 0 && !submitLabel) return null;
|
|
578
|
+
const values = value.values !== void 0 && isRecord2(value.values) ? value.values : void 0;
|
|
579
|
+
if (value.values !== void 0) {
|
|
580
|
+
if (!values || !Object.values(values).every((item) => isJsonValue(item)))
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
return {
|
|
584
|
+
schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
585
|
+
id,
|
|
586
|
+
title,
|
|
587
|
+
toolSlug,
|
|
588
|
+
fields,
|
|
589
|
+
...actions ? { actions } : {},
|
|
590
|
+
...description ? { description } : {},
|
|
591
|
+
...operationId ? { operationId } : {},
|
|
592
|
+
...requestId ? { requestId } : {},
|
|
593
|
+
...submitLabel ? { submitLabel } : {},
|
|
594
|
+
...steps ? { steps } : {},
|
|
595
|
+
...values ? { values } : {}
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function hasOnlyKeys(value, allowed) {
|
|
599
|
+
const allowedKeys = new Set(allowed);
|
|
600
|
+
return Object.keys(value).every((key) => allowedKeys.has(key));
|
|
601
|
+
}
|
|
602
|
+
|
|
350
603
|
// src/runtime/client.ts
|
|
351
604
|
function isTurnBoundary(event) {
|
|
352
605
|
return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
|
|
@@ -359,12 +612,8 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
359
612
|
if (event.type === "message.completed") {
|
|
360
613
|
handlers.onComplete?.();
|
|
361
614
|
}
|
|
362
|
-
if (event.type === "action.result")
|
|
363
|
-
|
|
364
|
-
if (result && typeof result === "object" && "output" in result) {
|
|
365
|
-
handlers.onActionResult?.(result.output);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
615
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
616
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
368
617
|
if (event.type !== "message.appended") return rendered;
|
|
369
618
|
const { messageDelta, messageSoFar } = event.data;
|
|
370
619
|
let delta = messageDelta;
|
|
@@ -375,9 +624,45 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
375
624
|
} else if (messageDelta) {
|
|
376
625
|
next += messageDelta;
|
|
377
626
|
}
|
|
378
|
-
if (delta) handlers.onDelta(delta);
|
|
627
|
+
if (delta) handlers.onDelta?.(delta);
|
|
379
628
|
return next;
|
|
380
629
|
}
|
|
630
|
+
function emitActionResult(event, handlers) {
|
|
631
|
+
const result = event.data.result;
|
|
632
|
+
if (result.kind === "tool-result") {
|
|
633
|
+
handlers.onToolResult?.({
|
|
634
|
+
callId: result.callId,
|
|
635
|
+
toolName: result.toolName,
|
|
636
|
+
status: event.data.status,
|
|
637
|
+
output: result.output,
|
|
638
|
+
...event.data.error ? { error: event.data.error } : {}
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
if ("output" in result) handlers.onActionResult?.(result.output);
|
|
642
|
+
}
|
|
643
|
+
function emitInputRequests(event, handlers) {
|
|
644
|
+
handlers.onInputRequest?.(
|
|
645
|
+
event.data.requests.map((request) => {
|
|
646
|
+
const ui = parseAgentToolUiSurface(
|
|
647
|
+
request.ui
|
|
648
|
+
);
|
|
649
|
+
return {
|
|
650
|
+
requestId: request.requestId,
|
|
651
|
+
kind: request.kind,
|
|
652
|
+
prompt: request.prompt,
|
|
653
|
+
...request.display ? { display: request.display } : {},
|
|
654
|
+
...request.allowFreeform !== void 0 ? { allowFreeform: request.allowFreeform } : {},
|
|
655
|
+
...request.options ? { options: request.options } : {},
|
|
656
|
+
...ui ? { ui } : {},
|
|
657
|
+
action: {
|
|
658
|
+
callId: request.action.callId,
|
|
659
|
+
kind: "tool-call",
|
|
660
|
+
toolName: request.action.toolName
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
})
|
|
664
|
+
);
|
|
665
|
+
}
|
|
381
666
|
function isResumeTurnMessage(received, candidate) {
|
|
382
667
|
if (received === candidate) return true;
|
|
383
668
|
return Boolean(candidate) && received.endsWith(`
|
|
@@ -653,9 +938,11 @@ var AgentSession = class {
|
|
|
653
938
|
let streamIndex = session?.state.streamIndex ?? 0;
|
|
654
939
|
let rendered = "";
|
|
655
940
|
const workItems = /* @__PURE__ */ new Map();
|
|
941
|
+
let requestedInput = false;
|
|
656
942
|
try {
|
|
657
943
|
for await (const event of response) {
|
|
658
944
|
if (signal.aborted) break;
|
|
945
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
659
946
|
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
660
947
|
streamIndex += 1;
|
|
661
948
|
if (session) {
|
|
@@ -673,7 +960,7 @@ var AgentSession = class {
|
|
|
673
960
|
this.persistSessionCursor(session);
|
|
674
961
|
}
|
|
675
962
|
}
|
|
676
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
963
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
677
964
|
throw new Error("Empty response from runtime");
|
|
678
965
|
}
|
|
679
966
|
return rendered.trim();
|
|
@@ -693,6 +980,9 @@ var AgentSession = class {
|
|
|
693
980
|
() => attached.snapshot({ signal })
|
|
694
981
|
);
|
|
695
982
|
const turnEvents = latestTurnEvents(snapshot.events);
|
|
983
|
+
const hasInputRequest = turnEvents.some(
|
|
984
|
+
(event) => event.type === "input.requested"
|
|
985
|
+
);
|
|
696
986
|
const received = turnEvents[0];
|
|
697
987
|
const lastSent = persisted.lastMessage;
|
|
698
988
|
const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
|
|
@@ -703,6 +993,8 @@ var AgentSession = class {
|
|
|
703
993
|
const workItems = /* @__PURE__ */ new Map();
|
|
704
994
|
for (const event of turnEvents) {
|
|
705
995
|
applyWorkEvent(event, handlers, workItems);
|
|
996
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
997
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
706
998
|
}
|
|
707
999
|
if (rendered.startsWith(initialText)) {
|
|
708
1000
|
const missedText = rendered.slice(initialText.length);
|
|
@@ -732,7 +1024,9 @@ var AgentSession = class {
|
|
|
732
1024
|
);
|
|
733
1025
|
}
|
|
734
1026
|
handlers.onComplete?.();
|
|
735
|
-
if (!rendered.trim()
|
|
1027
|
+
if (!rendered.trim() && !hasInputRequest) {
|
|
1028
|
+
throw new Error("Empty response from runtime");
|
|
1029
|
+
}
|
|
736
1030
|
return rendered.trim();
|
|
737
1031
|
}
|
|
738
1032
|
let streamIndex = snapshot.session.streamIndex;
|
|
@@ -751,7 +1045,55 @@ var AgentSession = class {
|
|
|
751
1045
|
session = client.sessions.attach(session.state.sessionId, { streamIndex });
|
|
752
1046
|
this.session = session;
|
|
753
1047
|
this.persistSessionCursor(session);
|
|
754
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
1048
|
+
if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
|
|
1049
|
+
throw new Error("Empty response from runtime");
|
|
1050
|
+
}
|
|
1051
|
+
return rendered.trim();
|
|
1052
|
+
}
|
|
1053
|
+
async respondTurn(responses, signal, handlers) {
|
|
1054
|
+
const client = this.ensureClient();
|
|
1055
|
+
const session = this.session ?? this.attachPersistedSession(client);
|
|
1056
|
+
if (!session) {
|
|
1057
|
+
throw new Error("No active session is waiting for input.");
|
|
1058
|
+
}
|
|
1059
|
+
this.session = session;
|
|
1060
|
+
const inputResponses = responses.map(
|
|
1061
|
+
({ requestId, optionId, text }) => ({
|
|
1062
|
+
requestId,
|
|
1063
|
+
...optionId ? { optionId } : {},
|
|
1064
|
+
...text ? { text } : {}
|
|
1065
|
+
})
|
|
1066
|
+
);
|
|
1067
|
+
const response = await withCapabilityRefresh(
|
|
1068
|
+
this.capability,
|
|
1069
|
+
() => session.respond(inputResponses, { signal })
|
|
1070
|
+
);
|
|
1071
|
+
this.activeResponse = response;
|
|
1072
|
+
let streamIndex = session.state.streamIndex;
|
|
1073
|
+
let rendered = "";
|
|
1074
|
+
let requestedInput = false;
|
|
1075
|
+
const workItems = /* @__PURE__ */ new Map();
|
|
1076
|
+
try {
|
|
1077
|
+
for await (const event of response) {
|
|
1078
|
+
if (signal.aborted) break;
|
|
1079
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
1080
|
+
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
1081
|
+
streamIndex += 1;
|
|
1082
|
+
savePersistedAgentSession(
|
|
1083
|
+
this.visitorSessionId,
|
|
1084
|
+
session.state.sessionId,
|
|
1085
|
+
streamIndex,
|
|
1086
|
+
this.storeOptions
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
} finally {
|
|
1090
|
+
this.activeResponse = void 0;
|
|
1091
|
+
this.session = client.sessions.attach(session.state.sessionId, {
|
|
1092
|
+
streamIndex
|
|
1093
|
+
});
|
|
1094
|
+
this.persistSessionCursor(this.session);
|
|
1095
|
+
}
|
|
1096
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
755
1097
|
throw new Error("Empty response from runtime");
|
|
756
1098
|
}
|
|
757
1099
|
return rendered.trim();
|
|
@@ -811,6 +1153,11 @@ function createAgentClient(options) {
|
|
|
811
1153
|
resumeOptions.handlers,
|
|
812
1154
|
resumeOptions.initialText
|
|
813
1155
|
),
|
|
1156
|
+
respondTurn: (respondOptions) => session.respondTurn(
|
|
1157
|
+
respondOptions.responses,
|
|
1158
|
+
respondOptions.signal ?? new AbortController().signal,
|
|
1159
|
+
respondOptions.handlers
|
|
1160
|
+
),
|
|
814
1161
|
reset: () => session.reset(),
|
|
815
1162
|
cancelActive: () => session.cancelActive(),
|
|
816
1163
|
getActiveSessionId: () => session.getActiveSessionId()
|
|
@@ -860,7 +1207,176 @@ function formatAgentError(error) {
|
|
|
860
1207
|
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
861
1208
|
}
|
|
862
1209
|
|
|
1210
|
+
// src/react/lib/composer-form.ts
|
|
1211
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1212
|
+
var MIN_FORM_FIELDS = 2;
|
|
1213
|
+
var MAX_FORM_FIELDS = 5;
|
|
1214
|
+
var FIELD_LIBRARY = [
|
|
1215
|
+
{
|
|
1216
|
+
id: "email",
|
|
1217
|
+
kind: "email",
|
|
1218
|
+
label: "Email",
|
|
1219
|
+
placeholder: "email@example.com",
|
|
1220
|
+
required: true,
|
|
1221
|
+
autocomplete: "email",
|
|
1222
|
+
patterns: [/\be-?mails?\b/i]
|
|
1223
|
+
},
|
|
1224
|
+
{
|
|
1225
|
+
id: "name",
|
|
1226
|
+
kind: "text",
|
|
1227
|
+
label: "Name",
|
|
1228
|
+
placeholder: "Your name",
|
|
1229
|
+
required: true,
|
|
1230
|
+
autocomplete: "name",
|
|
1231
|
+
patterns: [/\b((full|first|last)\s+)?names?\b/i],
|
|
1232
|
+
exclude: /\bcompany\s+names?\b/i
|
|
1233
|
+
},
|
|
1234
|
+
{
|
|
1235
|
+
id: "phone",
|
|
1236
|
+
kind: "tel",
|
|
1237
|
+
label: "Phone",
|
|
1238
|
+
placeholder: "+1 555 0100",
|
|
1239
|
+
required: true,
|
|
1240
|
+
autocomplete: "tel",
|
|
1241
|
+
patterns: [/\b(phone|mobile|cell)\b/i]
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
id: "company",
|
|
1245
|
+
kind: "text",
|
|
1246
|
+
label: "Company",
|
|
1247
|
+
placeholder: "Company name",
|
|
1248
|
+
required: true,
|
|
1249
|
+
autocomplete: "organization",
|
|
1250
|
+
patterns: [/\bcompan(y|ies)\b/i]
|
|
1251
|
+
},
|
|
1252
|
+
{
|
|
1253
|
+
id: "message",
|
|
1254
|
+
kind: "textarea",
|
|
1255
|
+
label: "Message",
|
|
1256
|
+
placeholder: "Message\u2026",
|
|
1257
|
+
required: true,
|
|
1258
|
+
patterns: [/\b(your message|a message|the message|inquiry|enquiry)\b/i],
|
|
1259
|
+
exclude: /\bin one message\b/i
|
|
1260
|
+
}
|
|
1261
|
+
];
|
|
1262
|
+
function asRecord(value) {
|
|
1263
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1264
|
+
}
|
|
1265
|
+
function asString(value) {
|
|
1266
|
+
return typeof value === "string" ? value.trim() : "";
|
|
1267
|
+
}
|
|
1268
|
+
function isFieldKind2(value) {
|
|
1269
|
+
return value === "text" || value === "email" || value === "tel" || value === "textarea";
|
|
1270
|
+
}
|
|
1271
|
+
function parseVisitorFormFields(value) {
|
|
1272
|
+
if (!Array.isArray(value)) return [];
|
|
1273
|
+
const fields = [];
|
|
1274
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1275
|
+
for (const item of value) {
|
|
1276
|
+
const record = asRecord(item);
|
|
1277
|
+
const id = asString(record?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
1278
|
+
const kind = asString(record?.kind);
|
|
1279
|
+
if (!record || !id || seen.has(id) || !isFieldKind2(kind)) continue;
|
|
1280
|
+
seen.add(id);
|
|
1281
|
+
const label = asString(record.label) || id;
|
|
1282
|
+
fields.push({
|
|
1283
|
+
id,
|
|
1284
|
+
kind,
|
|
1285
|
+
label,
|
|
1286
|
+
placeholder: asString(record.placeholder) || label,
|
|
1287
|
+
required: record.required !== false,
|
|
1288
|
+
...asString(record.autocomplete) ? { autocomplete: asString(record.autocomplete) } : {}
|
|
1289
|
+
});
|
|
1290
|
+
if (fields.length >= MAX_FORM_FIELDS) break;
|
|
1291
|
+
}
|
|
1292
|
+
return fields;
|
|
1293
|
+
}
|
|
1294
|
+
function readComposerControlValue(event) {
|
|
1295
|
+
const node = event.target ?? event.currentTarget ?? null;
|
|
1296
|
+
if (node && typeof node === "object" && "value" in node && typeof node.value === "string") {
|
|
1297
|
+
return node.value;
|
|
1298
|
+
}
|
|
1299
|
+
return "";
|
|
1300
|
+
}
|
|
1301
|
+
function isValidComposerFieldValue(field, value) {
|
|
1302
|
+
const trimmed = value.trim();
|
|
1303
|
+
if (!trimmed) return !field.required;
|
|
1304
|
+
if (field.kind === "email") return EMAIL_PATTERN.test(trimmed);
|
|
1305
|
+
if (field.kind === "tel") return trimmed.replace(/\D/g, "").length >= 7;
|
|
1306
|
+
return trimmed.length > 0;
|
|
1307
|
+
}
|
|
1308
|
+
function isComposerFormComplete(form, values) {
|
|
1309
|
+
return form.fields.every(
|
|
1310
|
+
(field) => isValidComposerFieldValue(field, values[field.id] ?? "")
|
|
1311
|
+
);
|
|
1312
|
+
}
|
|
1313
|
+
function formatComposerFormMessage(form, values) {
|
|
1314
|
+
return form.fields.map((field) => {
|
|
1315
|
+
const value = (values[field.id] ?? "").trim();
|
|
1316
|
+
return value ? `${field.label}: ${value}` : "";
|
|
1317
|
+
}).filter(Boolean).join("\n");
|
|
1318
|
+
}
|
|
1319
|
+
function looksLikeFieldCollection(text) {
|
|
1320
|
+
return /\b(please|need|send|share|enter|provide|add|collect|what|which|use)\b/i.test(
|
|
1321
|
+
text
|
|
1322
|
+
) || /:\s*$/m.test(text) || /^[-*•]\s+/m.test(text);
|
|
1323
|
+
}
|
|
1324
|
+
function looksLikeBookingCopy(text) {
|
|
1325
|
+
return /book(ing)? (card|a demo|this time)|pick a (date|time)|available (times|slots)|calendly/i.test(
|
|
1326
|
+
text
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
function inferComposerForm(text) {
|
|
1330
|
+
const cleaned = text.trim();
|
|
1331
|
+
if (!cleaned || looksLikeBookingCopy(cleaned) || !looksLikeFieldCollection(cleaned)) {
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
const fields = FIELD_LIBRARY.flatMap((field) => {
|
|
1335
|
+
if (field.exclude?.test(cleaned)) {
|
|
1336
|
+
const leftover = cleaned.replace(field.exclude, " ");
|
|
1337
|
+
if (!field.patterns.some((pattern) => pattern.test(leftover))) return [];
|
|
1338
|
+
} else if (!field.patterns.some((pattern) => pattern.test(cleaned))) {
|
|
1339
|
+
return [];
|
|
1340
|
+
}
|
|
1341
|
+
const { patterns: _patterns, exclude: _exclude, ...next } = field;
|
|
1342
|
+
return [next];
|
|
1343
|
+
}).slice(0, MAX_FORM_FIELDS);
|
|
1344
|
+
if (fields.length < MIN_FORM_FIELDS) return null;
|
|
1345
|
+
return {
|
|
1346
|
+
id: `inferred:${fields.map((field) => field.id).join("+")}`,
|
|
1347
|
+
fields
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
function resolveComposerForm(input) {
|
|
1351
|
+
if (input.enabled === false || input.hasBookingOffer) return null;
|
|
1352
|
+
const text = input.agentText.trim();
|
|
1353
|
+
if (!text) return null;
|
|
1354
|
+
const card = input.cards?.find((item) => item.type === "visitor_form");
|
|
1355
|
+
if (card?.fields && card.fields.length >= MIN_FORM_FIELDS) {
|
|
1356
|
+
return {
|
|
1357
|
+
id: `card:${card.fields.map((field) => field.id).join("+")}`,
|
|
1358
|
+
fields: card.fields.slice(0, MAX_FORM_FIELDS)
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
return inferComposerForm(text);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
863
1364
|
// src/react/lib/tool-card.ts
|
|
1365
|
+
function preferBookingOffer(current, next) {
|
|
1366
|
+
if (!current) return next;
|
|
1367
|
+
if (next.slots.length !== current.slots.length) {
|
|
1368
|
+
return next.slots.length > current.slots.length ? next : current;
|
|
1369
|
+
}
|
|
1370
|
+
if (next.eventTypes.length !== current.eventTypes.length) {
|
|
1371
|
+
return next.eventTypes.length > current.eventTypes.length ? next : current;
|
|
1372
|
+
}
|
|
1373
|
+
return next;
|
|
1374
|
+
}
|
|
1375
|
+
function looksLikeBookingReady(text) {
|
|
1376
|
+
return /demo option|options are ready|pick a (date|time)|available (times|slots)|book(ing)? (card|the demo)|schedule/i.test(
|
|
1377
|
+
text
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
864
1380
|
function bookingOfferIdentityKey(offer) {
|
|
865
1381
|
const eventTypes = offer.eventTypes.map(
|
|
866
1382
|
(item) => `${item.uri}:${item.duration ?? ""}:${item.locationKind ?? ""}:${item.location ?? ""}`
|
|
@@ -869,10 +1385,10 @@ function bookingOfferIdentityKey(offer) {
|
|
|
869
1385
|
return `${eventTypes}::${slots}` || "offer";
|
|
870
1386
|
}
|
|
871
1387
|
var FENCE_PATTERN = /```(?:webless-tool-card|json)\s*([\s\S]*?)```/gi;
|
|
872
|
-
function
|
|
1388
|
+
function asRecord2(value) {
|
|
873
1389
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
874
1390
|
}
|
|
875
|
-
function
|
|
1391
|
+
function asString2(value) {
|
|
876
1392
|
return typeof value === "string" ? value.trim() : "";
|
|
877
1393
|
}
|
|
878
1394
|
function isEventUri(value) {
|
|
@@ -882,24 +1398,28 @@ function isEventTypeUri(value) {
|
|
|
882
1398
|
return /^https:\/\/api\.calendly\.com\/event_types\/[^/]+$/i.test(value);
|
|
883
1399
|
}
|
|
884
1400
|
function parseToolCard(value) {
|
|
885
|
-
const record =
|
|
1401
|
+
const record = asRecord2(value);
|
|
886
1402
|
if (!record) return null;
|
|
887
|
-
if (record.booking_offer &&
|
|
1403
|
+
if (record.booking_offer && asString2(record.type) !== "booking_offer") {
|
|
888
1404
|
const nested = parseToolCard(record.booking_offer);
|
|
889
1405
|
if (nested) return nested;
|
|
890
1406
|
}
|
|
891
|
-
|
|
1407
|
+
if (record.visitor_booking && asString2(record.type) !== "booking_confirmed") {
|
|
1408
|
+
const nested = parseToolCard(record.visitor_booking);
|
|
1409
|
+
if (nested) return nested;
|
|
1410
|
+
}
|
|
1411
|
+
const type = asString2(record.type);
|
|
892
1412
|
if (type === "booking_offer") {
|
|
893
1413
|
const eventTypes = Array.isArray(record.eventTypes) ? record.eventTypes.flatMap((item) => {
|
|
894
|
-
const entry =
|
|
895
|
-
const uri =
|
|
896
|
-
if (!entry || !
|
|
1414
|
+
const entry = asRecord2(item);
|
|
1415
|
+
const uri = asString2(entry?.uri);
|
|
1416
|
+
if (!entry || !uri) return [];
|
|
897
1417
|
const duration = entry.duration;
|
|
898
|
-
const locationKind =
|
|
899
|
-
const location =
|
|
1418
|
+
const locationKind = asString2(entry.locationKind);
|
|
1419
|
+
const location = asString2(entry.location);
|
|
900
1420
|
return [
|
|
901
1421
|
{
|
|
902
|
-
name:
|
|
1422
|
+
name: asString2(entry.name) || "Meeting",
|
|
903
1423
|
uri,
|
|
904
1424
|
...typeof duration === "number" ? { duration } : {},
|
|
905
1425
|
...locationKind ? { locationKind } : {},
|
|
@@ -908,10 +1428,10 @@ function parseToolCard(value) {
|
|
|
908
1428
|
];
|
|
909
1429
|
}) : [];
|
|
910
1430
|
const slots = Array.isArray(record.slots) ? record.slots.flatMap((item) => {
|
|
911
|
-
const entry =
|
|
912
|
-
const startTime =
|
|
1431
|
+
const entry = asRecord2(item);
|
|
1432
|
+
const startTime = asString2(entry?.startTime);
|
|
913
1433
|
if (!entry || !startTime) return [];
|
|
914
|
-
const eventTypeUri =
|
|
1434
|
+
const eventTypeUri = asString2(entry.eventTypeUri);
|
|
915
1435
|
return [
|
|
916
1436
|
{
|
|
917
1437
|
startTime,
|
|
@@ -919,15 +1439,15 @@ function parseToolCard(value) {
|
|
|
919
1439
|
}
|
|
920
1440
|
];
|
|
921
1441
|
}) : [];
|
|
922
|
-
if (slots.length === 0) return null;
|
|
1442
|
+
if (eventTypes.length === 0 && slots.length === 0) return null;
|
|
923
1443
|
return { type: "booking_offer", eventTypes, slots };
|
|
924
1444
|
}
|
|
925
1445
|
if (type === "booking_confirmed") {
|
|
926
|
-
const eventUri =
|
|
1446
|
+
const eventUri = asString2(record.eventUri);
|
|
927
1447
|
if (!isEventUri(eventUri)) return null;
|
|
928
|
-
const inviteeUri =
|
|
929
|
-
const inviteeEmail =
|
|
930
|
-
const startTime =
|
|
1448
|
+
const inviteeUri = asString2(record.inviteeUri);
|
|
1449
|
+
const inviteeEmail = asString2(record.inviteeEmail);
|
|
1450
|
+
const startTime = asString2(record.startTime);
|
|
931
1451
|
return {
|
|
932
1452
|
type: "booking_confirmed",
|
|
933
1453
|
eventUri,
|
|
@@ -937,10 +1457,15 @@ function parseToolCard(value) {
|
|
|
937
1457
|
};
|
|
938
1458
|
}
|
|
939
1459
|
if (type === "booking_canceled") {
|
|
940
|
-
const eventUri =
|
|
1460
|
+
const eventUri = asString2(record.eventUri);
|
|
941
1461
|
if (!isEventUri(eventUri)) return null;
|
|
942
1462
|
return { type: "booking_canceled", eventUri };
|
|
943
1463
|
}
|
|
1464
|
+
if (type === "visitor_form") {
|
|
1465
|
+
const fields = parseVisitorFormFields(record.fields);
|
|
1466
|
+
if (fields.length < 2) return null;
|
|
1467
|
+
return { type: "visitor_form", fields };
|
|
1468
|
+
}
|
|
944
1469
|
return null;
|
|
945
1470
|
}
|
|
946
1471
|
function formatBookingOfferFence(offer) {
|
|
@@ -954,11 +1479,10 @@ function formatBookingOfferFence(offer) {
|
|
|
954
1479
|
"```"
|
|
955
1480
|
].join("\n");
|
|
956
1481
|
}
|
|
957
|
-
function
|
|
958
|
-
const record =
|
|
959
|
-
const data =
|
|
960
|
-
|
|
961
|
-
return card?.type === "booking_offer" ? card : null;
|
|
1482
|
+
function bookingCardFromActionOutput(output) {
|
|
1483
|
+
const record = asRecord2(output);
|
|
1484
|
+
const data = asRecord2(record?.data) ?? record;
|
|
1485
|
+
return parseToolCard(data);
|
|
962
1486
|
}
|
|
963
1487
|
function ensureBookingOfferText(text, offer) {
|
|
964
1488
|
if (!offer) return text;
|
|
@@ -973,6 +1497,21 @@ ${formatBookingOfferFence(offer)}`;
|
|
|
973
1497
|
function hideToolCardFences(text) {
|
|
974
1498
|
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();
|
|
975
1499
|
}
|
|
1500
|
+
var BOOKING_CARD_FALLBACK = "Pick a date and time that works for you.";
|
|
1501
|
+
function looksLikeBookingAvailabilityDump(text) {
|
|
1502
|
+
const cleaned = text.trim();
|
|
1503
|
+
if (!cleaned) return false;
|
|
1504
|
+
const isoCount = (cleaned.match(/\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/g) ?? []).length;
|
|
1505
|
+
const utcCount = (cleaned.match(/\bUTC\b/g) ?? []).length;
|
|
1506
|
+
return isoCount >= 2 || utcCount >= 2 || /api\.calendly\.com|calendly\.com\//i.test(cleaned) || /webless-tool-card/i.test(cleaned);
|
|
1507
|
+
}
|
|
1508
|
+
function sanitizeBookingOfferCopy(text) {
|
|
1509
|
+
const cleaned = hideToolCardFences(text);
|
|
1510
|
+
if (!cleaned || looksLikeBookingAvailabilityDump(cleaned)) {
|
|
1511
|
+
return BOOKING_CARD_FALLBACK;
|
|
1512
|
+
}
|
|
1513
|
+
return cleaned;
|
|
1514
|
+
}
|
|
976
1515
|
function visitorTimeZone() {
|
|
977
1516
|
try {
|
|
978
1517
|
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
@@ -1074,6 +1613,7 @@ function formatSlotLabel(startTime) {
|
|
|
1074
1613
|
function formatBookingRequest(input) {
|
|
1075
1614
|
return [
|
|
1076
1615
|
"Book this meeting now with CALENDLY_POST_INVITEE.",
|
|
1616
|
+
"Execute CALENDLY_POST_INVITEE in this turn with the fields below.",
|
|
1077
1617
|
"Do not open a Calendly URL and do not list other scheduled events.",
|
|
1078
1618
|
"Do not invent a location kind. Use only the location fields below.",
|
|
1079
1619
|
`event_type: ${input.eventTypeUri}`,
|
|
@@ -1101,7 +1641,7 @@ function visitorBookingPrefix(booking) {
|
|
|
1101
1641
|
}
|
|
1102
1642
|
|
|
1103
1643
|
// src/react/persisted-conversation.ts
|
|
1104
|
-
var CONVERSATION_VERSION =
|
|
1644
|
+
var CONVERSATION_VERSION = 3;
|
|
1105
1645
|
function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
1106
1646
|
return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
|
|
1107
1647
|
}
|
|
@@ -1141,30 +1681,171 @@ function parseToolStep(value) {
|
|
|
1141
1681
|
...typeof record.detail === "string" && record.detail ? { detail: record.detail } : {}
|
|
1142
1682
|
};
|
|
1143
1683
|
}
|
|
1684
|
+
function parseInputOption(value) {
|
|
1685
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1686
|
+
const record = value;
|
|
1687
|
+
if (typeof record.id !== "string" || typeof record.label !== "string") {
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
return {
|
|
1691
|
+
id: record.id,
|
|
1692
|
+
label: record.label,
|
|
1693
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
1694
|
+
...record.style === "default" || record.style === "primary" || record.style === "danger" ? { style: record.style } : {}
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
function parseInputRequest(value) {
|
|
1698
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1699
|
+
const record = value;
|
|
1700
|
+
if (typeof record.requestId !== "string" || record.kind !== "question" && record.kind !== "session-limit" && record.kind !== "tool-approval" || typeof record.prompt !== "string") {
|
|
1701
|
+
return null;
|
|
1702
|
+
}
|
|
1703
|
+
const action = record.action;
|
|
1704
|
+
if (typeof action !== "object" || action === null) return null;
|
|
1705
|
+
const actionRecord = action;
|
|
1706
|
+
if (typeof actionRecord.callId !== "string" || actionRecord.kind !== "tool-call" || typeof actionRecord.toolName !== "string") {
|
|
1707
|
+
return null;
|
|
1708
|
+
}
|
|
1709
|
+
const options = Array.isArray(record.options) ? record.options.map(parseInputOption) : void 0;
|
|
1710
|
+
if (Array.isArray(record.options) && options?.some((option) => option === null)) {
|
|
1711
|
+
return null;
|
|
1712
|
+
}
|
|
1713
|
+
const ui = record.ui ? parseAgentToolUiSurface(record.ui) : void 0;
|
|
1714
|
+
if (record.ui && !ui) return null;
|
|
1715
|
+
return {
|
|
1716
|
+
requestId: record.requestId,
|
|
1717
|
+
kind: record.kind,
|
|
1718
|
+
prompt: record.prompt,
|
|
1719
|
+
action: {
|
|
1720
|
+
callId: actionRecord.callId,
|
|
1721
|
+
kind: "tool-call",
|
|
1722
|
+
toolName: actionRecord.toolName
|
|
1723
|
+
},
|
|
1724
|
+
...record.display === "confirmation" || record.display === "select" || record.display === "text" ? { display: record.display } : {},
|
|
1725
|
+
...record.allowFreeform === true ? { allowFreeform: true } : {},
|
|
1726
|
+
...options && options.length > 0 ? {
|
|
1727
|
+
options: options.filter(
|
|
1728
|
+
(option) => option !== null
|
|
1729
|
+
)
|
|
1730
|
+
} : {},
|
|
1731
|
+
...ui ? { ui } : {}
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
function parseToolResult(value) {
|
|
1735
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1736
|
+
const record = value;
|
|
1737
|
+
if (typeof record.id !== "string" || typeof record.toolName !== "string" || record.status !== "completed" && record.status !== "failed" && record.status !== "rejected") {
|
|
1738
|
+
return null;
|
|
1739
|
+
}
|
|
1740
|
+
if (record.kind === "input") {
|
|
1741
|
+
const surface = parseAgentToolUiSurface(
|
|
1742
|
+
record.surface
|
|
1743
|
+
);
|
|
1744
|
+
return surface ? {
|
|
1745
|
+
id: record.id,
|
|
1746
|
+
toolName: record.toolName,
|
|
1747
|
+
status: record.status,
|
|
1748
|
+
kind: "input",
|
|
1749
|
+
surface
|
|
1750
|
+
} : null;
|
|
1751
|
+
}
|
|
1752
|
+
if (record.kind === "entity" && typeof record.title === "string") {
|
|
1753
|
+
return {
|
|
1754
|
+
id: record.id,
|
|
1755
|
+
toolName: record.toolName,
|
|
1756
|
+
status: record.status,
|
|
1757
|
+
kind: "entity",
|
|
1758
|
+
title: record.title,
|
|
1759
|
+
...typeof record.description === "string" ? { description: record.description } : {}
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
if (record.kind === "collection" && typeof record.title === "string" && Array.isArray(record.items)) {
|
|
1763
|
+
return {
|
|
1764
|
+
id: record.id,
|
|
1765
|
+
toolName: record.toolName,
|
|
1766
|
+
status: record.status,
|
|
1767
|
+
kind: "collection",
|
|
1768
|
+
title: record.title,
|
|
1769
|
+
items: record.items.flatMap((item) => {
|
|
1770
|
+
if (typeof item !== "object" || item === null) return [];
|
|
1771
|
+
const entry = item;
|
|
1772
|
+
if (typeof entry.title !== "string") return [];
|
|
1773
|
+
return [
|
|
1774
|
+
{
|
|
1775
|
+
title: entry.title,
|
|
1776
|
+
...typeof entry.description === "string" ? { description: entry.description } : {},
|
|
1777
|
+
...typeof entry.href === "string" ? { href: entry.href } : {}
|
|
1778
|
+
}
|
|
1779
|
+
];
|
|
1780
|
+
})
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
if (record.kind === "signature" && typeof record.title === "string") {
|
|
1784
|
+
return {
|
|
1785
|
+
id: record.id,
|
|
1786
|
+
toolName: record.toolName,
|
|
1787
|
+
status: record.status,
|
|
1788
|
+
kind: "signature",
|
|
1789
|
+
title: record.title,
|
|
1790
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
1791
|
+
...typeof record.statusLabel === "string" ? { statusLabel: record.statusLabel } : {}
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
if (record.kind !== "summary" || typeof record.title !== "string")
|
|
1795
|
+
return null;
|
|
1796
|
+
return {
|
|
1797
|
+
id: record.id,
|
|
1798
|
+
toolName: record.toolName,
|
|
1799
|
+
status: record.status,
|
|
1800
|
+
kind: "summary",
|
|
1801
|
+
title: record.title,
|
|
1802
|
+
...typeof record.description === "string" ? { description: record.description } : {}
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1144
1805
|
function visitorTurnText(message) {
|
|
1145
1806
|
return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
|
|
1146
1807
|
}
|
|
1147
1808
|
function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
1148
1809
|
if (typeof sessionStorage === "undefined") return null;
|
|
1149
|
-
const raw = sessionStorage.getItem(
|
|
1810
|
+
const raw = sessionStorage.getItem(
|
|
1811
|
+
conversationKey(storageKeyPrefix, visitorSessionId)
|
|
1812
|
+
);
|
|
1150
1813
|
if (!raw) return null;
|
|
1151
1814
|
try {
|
|
1152
1815
|
const value = JSON.parse(raw);
|
|
1153
1816
|
if (typeof value !== "object" || value === null) return null;
|
|
1154
1817
|
const record = value;
|
|
1155
|
-
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)) {
|
|
1818
|
+
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)) {
|
|
1156
1819
|
return null;
|
|
1157
1820
|
}
|
|
1158
1821
|
const messages = record.messages.map(parseMessage);
|
|
1159
1822
|
if (messages.some((message) => message === null)) return null;
|
|
1160
|
-
const storedToolSteps = record.version === CONVERSATION_VERSION && Array.isArray(record.toolSteps) ? record.toolSteps : [];
|
|
1823
|
+
const storedToolSteps = (record.version === 2 || record.version === CONVERSATION_VERSION) && Array.isArray(record.toolSteps) ? record.toolSteps : [];
|
|
1161
1824
|
const toolSteps = storedToolSteps.map(parseToolStep);
|
|
1162
1825
|
if (toolSteps.some((step) => step === null)) return null;
|
|
1826
|
+
const storedToolResults = record.version === CONVERSATION_VERSION && Array.isArray(record.toolResults) ? record.toolResults : [];
|
|
1827
|
+
const toolResults = storedToolResults.map(parseToolResult);
|
|
1828
|
+
if (toolResults.some((result) => result === null)) return null;
|
|
1829
|
+
const storedPendingInputs = Array.isArray(record.pendingInputs) ? record.pendingInputs : [];
|
|
1830
|
+
const pendingInputs = storedPendingInputs.map(parseInputRequest);
|
|
1831
|
+
if (pendingInputs.some((request) => request === null)) return null;
|
|
1163
1832
|
return {
|
|
1164
|
-
messages: messages.filter(
|
|
1833
|
+
messages: messages.filter(
|
|
1834
|
+
(message) => message !== null
|
|
1835
|
+
),
|
|
1165
1836
|
pending: record.pending,
|
|
1166
1837
|
streamingText: record.streamingText,
|
|
1167
|
-
toolSteps: toolSteps.filter((step) => step !== null)
|
|
1838
|
+
toolSteps: toolSteps.filter((step) => step !== null),
|
|
1839
|
+
...Array.isArray(record.toolResults) ? {
|
|
1840
|
+
toolResults: toolResults.filter(
|
|
1841
|
+
(result) => result !== null
|
|
1842
|
+
)
|
|
1843
|
+
} : {},
|
|
1844
|
+
...pendingInputs.length > 0 ? {
|
|
1845
|
+
pendingInputs: pendingInputs.filter(
|
|
1846
|
+
(request) => request !== null
|
|
1847
|
+
)
|
|
1848
|
+
} : {}
|
|
1168
1849
|
};
|
|
1169
1850
|
} catch {
|
|
1170
1851
|
return null;
|
|
@@ -1179,7 +1860,9 @@ function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conv
|
|
|
1179
1860
|
}
|
|
1180
1861
|
function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
1181
1862
|
if (typeof sessionStorage === "undefined") return;
|
|
1182
|
-
sessionStorage.removeItem(
|
|
1863
|
+
sessionStorage.removeItem(
|
|
1864
|
+
conversationKey(storageKeyPrefix, visitorSessionId)
|
|
1865
|
+
);
|
|
1183
1866
|
clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId);
|
|
1184
1867
|
}
|
|
1185
1868
|
function pendingBookingKey(storageKeyPrefix, visitorSessionId) {
|
|
@@ -1215,52 +1898,482 @@ function savePendingWidgetBooking(storageKeyPrefix, visitorSessionId, booking) {
|
|
|
1215
1898
|
}
|
|
1216
1899
|
function clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
|
|
1217
1900
|
if (typeof sessionStorage === "undefined") return;
|
|
1218
|
-
sessionStorage.removeItem(
|
|
1901
|
+
sessionStorage.removeItem(
|
|
1902
|
+
pendingBookingKey(storageKeyPrefix, visitorSessionId)
|
|
1903
|
+
);
|
|
1219
1904
|
}
|
|
1220
1905
|
|
|
1221
|
-
// src/
|
|
1222
|
-
var
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
text: greeting,
|
|
1231
|
-
createdAt: 0
|
|
1232
|
-
}
|
|
1233
|
-
],
|
|
1234
|
-
toolSteps: [],
|
|
1235
|
-
journey: null,
|
|
1236
|
-
followUps: [],
|
|
1237
|
-
streamingText: "",
|
|
1238
|
-
pendingOffer: null,
|
|
1239
|
-
error: null
|
|
1240
|
-
};
|
|
1906
|
+
// src/runtime/tool-result-envelope.ts
|
|
1907
|
+
var ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
1908
|
+
"schemaVersion",
|
|
1909
|
+
"output",
|
|
1910
|
+
"presentationKinds",
|
|
1911
|
+
"ui"
|
|
1912
|
+
]);
|
|
1913
|
+
function isRecord3(value) {
|
|
1914
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1241
1915
|
}
|
|
1242
|
-
function
|
|
1243
|
-
if (
|
|
1916
|
+
function isJsonValue2(value, seen = /* @__PURE__ */ new Set()) {
|
|
1917
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
1918
|
+
return true;
|
|
1919
|
+
}
|
|
1920
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1921
|
+
if (typeof value !== "object") return false;
|
|
1922
|
+
if (seen.has(value)) return false;
|
|
1923
|
+
seen.add(value);
|
|
1924
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue2(item, seen)) : Object.entries(value).every(
|
|
1925
|
+
([key, item]) => typeof key === "string" && isJsonValue2(item, seen)
|
|
1926
|
+
);
|
|
1927
|
+
seen.delete(value);
|
|
1928
|
+
return valid;
|
|
1929
|
+
}
|
|
1930
|
+
function decodeEnvelope(value) {
|
|
1931
|
+
if (typeof value !== "string") return value;
|
|
1932
|
+
try {
|
|
1933
|
+
return JSON.parse(value);
|
|
1934
|
+
} catch {
|
|
1935
|
+
return null;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
function parseAgentToolResultEnvelope(value) {
|
|
1939
|
+
const decoded = decodeEnvelope(value);
|
|
1940
|
+
if (!isRecord3(decoded)) return null;
|
|
1941
|
+
const keys = Object.keys(decoded);
|
|
1942
|
+
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) {
|
|
1943
|
+
return null;
|
|
1944
|
+
}
|
|
1945
|
+
const presentationKinds = decoded.presentationKinds.flatMap((kind) => {
|
|
1946
|
+
if (typeof kind !== "string") return [];
|
|
1947
|
+
const normalized = kind.trim();
|
|
1948
|
+
return normalized && normalized.length <= 128 ? [normalized] : [];
|
|
1949
|
+
});
|
|
1950
|
+
if (presentationKinds.length !== decoded.presentationKinds.length) {
|
|
1951
|
+
return null;
|
|
1952
|
+
}
|
|
1953
|
+
const ui = decoded.ui === void 0 ? void 0 : parseAgentToolUiSurface(decoded.ui);
|
|
1954
|
+
if (decoded.ui !== void 0 && !ui) return null;
|
|
1244
1955
|
return {
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
toolSteps: conversation.toolSteps
|
|
1956
|
+
schemaVersion: "webless.tool-result.v1",
|
|
1957
|
+
output: decoded.output,
|
|
1958
|
+
presentationKinds,
|
|
1959
|
+
...ui ? { ui } : {}
|
|
1250
1960
|
};
|
|
1251
1961
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1962
|
+
|
|
1963
|
+
// src/react/lib/tool-result.ts
|
|
1964
|
+
var MAX_TEXT_LENGTH = 240;
|
|
1965
|
+
var MAX_DETAILS = 6;
|
|
1966
|
+
var MAX_LINKS = 4;
|
|
1967
|
+
var MAX_COLLECTION_ITEMS = 8;
|
|
1968
|
+
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;
|
|
1969
|
+
function safeText(value) {
|
|
1970
|
+
if (typeof value !== "string") return "";
|
|
1971
|
+
return value.trim().slice(0, MAX_TEXT_LENGTH);
|
|
1972
|
+
}
|
|
1973
|
+
function safeHref(value) {
|
|
1974
|
+
const text = safeText(value);
|
|
1975
|
+
if (!text) return "";
|
|
1976
|
+
try {
|
|
1977
|
+
const url = new URL(text);
|
|
1978
|
+
return url.protocol === "https:" ? url.toString() : "";
|
|
1979
|
+
} catch {
|
|
1980
|
+
return "";
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
function fallbackTitle(result) {
|
|
1984
|
+
if (result.status === "failed") return "Couldn\u2019t complete this action";
|
|
1985
|
+
if (result.status === "rejected") return "Action not approved";
|
|
1986
|
+
return "Action completed";
|
|
1987
|
+
}
|
|
1988
|
+
function bookingPresenter(kind) {
|
|
1989
|
+
return {
|
|
1990
|
+
kind,
|
|
1991
|
+
present: ({ envelope }) => {
|
|
1992
|
+
const card = bookingCardFromActionOutput(envelope.output);
|
|
1993
|
+
return card && card.type === kind ? { kind: "booking", card } : null;
|
|
1994
|
+
}
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
function asRecord3(value) {
|
|
1998
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1999
|
+
}
|
|
2000
|
+
function parseFacts(value) {
|
|
2001
|
+
if (!Array.isArray(value)) return [];
|
|
2002
|
+
return value.flatMap((item) => {
|
|
2003
|
+
const fact = asRecord3(item);
|
|
2004
|
+
const label = safeText(fact?.label);
|
|
2005
|
+
const factValue = safeText(fact?.value);
|
|
2006
|
+
if (!label || !factValue) return [];
|
|
2007
|
+
if (INTERNAL_FACT_LABEL.test(label)) return [];
|
|
2008
|
+
return [{ label, value: factValue }];
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
function parseLinks(output, defaultLabel) {
|
|
2012
|
+
const href = safeHref(output.href);
|
|
2013
|
+
const linkLabel = safeText(output.linkLabel) || defaultLabel;
|
|
2014
|
+
return href ? [{ label: linkLabel, href }] : [];
|
|
2015
|
+
}
|
|
2016
|
+
function entityResultPresenter() {
|
|
2017
|
+
return {
|
|
2018
|
+
kind: "entity_result",
|
|
2019
|
+
present: ({ envelope, result }) => {
|
|
2020
|
+
const output = asRecord3(envelope.output);
|
|
2021
|
+
if (!output) return null;
|
|
2022
|
+
const title = safeText(output.title) || fallbackTitle(result);
|
|
2023
|
+
const description = safeText(output.description);
|
|
2024
|
+
const facts = parseFacts(output.facts).filter((fact) => fact.value !== description && fact.value !== title).slice(0, MAX_DETAILS);
|
|
2025
|
+
return {
|
|
2026
|
+
kind: "entity",
|
|
2027
|
+
title,
|
|
2028
|
+
...description && description !== title ? { description } : {},
|
|
2029
|
+
...facts.length ? { details: facts } : {},
|
|
2030
|
+
...parseLinks(output, "Open record").length ? { links: parseLinks(output, "Open record") } : {}
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
function collectionResultPresenter() {
|
|
2036
|
+
return {
|
|
2037
|
+
kind: "collection",
|
|
2038
|
+
present: ({ envelope, result }) => {
|
|
2039
|
+
const output = asRecord3(envelope.output);
|
|
2040
|
+
if (!output) return null;
|
|
2041
|
+
const title = safeText(output.title) || fallbackTitleFromKind("collection");
|
|
2042
|
+
const items = Array.isArray(output.items) ? output.items.slice(0, MAX_COLLECTION_ITEMS).flatMap((item) => {
|
|
2043
|
+
const entry = asRecord3(item);
|
|
2044
|
+
if (!entry) return [];
|
|
2045
|
+
const itemTitle = safeText(entry.title);
|
|
2046
|
+
if (!itemTitle) return [];
|
|
2047
|
+
const description = safeText(entry.description);
|
|
2048
|
+
const details = parseFacts(entry.facts).slice(0, MAX_DETAILS);
|
|
2049
|
+
const href = safeHref(entry.href);
|
|
2050
|
+
return [
|
|
2051
|
+
{
|
|
2052
|
+
title: itemTitle,
|
|
2053
|
+
...description ? { description } : {},
|
|
2054
|
+
...details.length ? { details } : {},
|
|
2055
|
+
...href ? { href } : {}
|
|
2056
|
+
}
|
|
2057
|
+
];
|
|
2058
|
+
}) : [];
|
|
2059
|
+
return {
|
|
2060
|
+
kind: "collection",
|
|
2061
|
+
title,
|
|
2062
|
+
items
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
function signatureResultPresenter() {
|
|
2068
|
+
return {
|
|
2069
|
+
kind: "signature",
|
|
2070
|
+
present: ({ envelope, result }) => {
|
|
2071
|
+
const output = asRecord3(envelope.output);
|
|
2072
|
+
if (!output) return null;
|
|
2073
|
+
const title = safeText(output.title) || fallbackTitleFromKind("signature");
|
|
2074
|
+
const description = safeText(output.description);
|
|
2075
|
+
const statusLabel = safeText(output.status);
|
|
2076
|
+
return {
|
|
2077
|
+
kind: "signature",
|
|
2078
|
+
title,
|
|
2079
|
+
...description ? { description } : {},
|
|
2080
|
+
...statusLabel ? { statusLabel } : {},
|
|
2081
|
+
...parseLinks(output, "Sign now").length ? { links: parseLinks(output, "Sign now") } : {}
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
function resultCardPresenter(kind) {
|
|
2087
|
+
return {
|
|
2088
|
+
kind,
|
|
2089
|
+
present: ({ envelope, result }) => {
|
|
2090
|
+
const output = asRecord3(envelope.output);
|
|
2091
|
+
if (!output) return null;
|
|
2092
|
+
const title = safeText(output.title) || fallbackTitleFromKind(kind);
|
|
2093
|
+
const description = safeText(output.description);
|
|
2094
|
+
const facts = parseFacts(output.facts).slice(0, MAX_DETAILS);
|
|
2095
|
+
return {
|
|
2096
|
+
kind: "summary",
|
|
2097
|
+
title,
|
|
2098
|
+
...description && description !== title ? { description } : {},
|
|
2099
|
+
...facts.length ? { details: facts } : {},
|
|
2100
|
+
...parseLinks(output, "Open").length ? { links: parseLinks(output, "Open") } : {}
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
function fallbackTitleFromKind(kind) {
|
|
2106
|
+
if (kind === "document") return "Document ready";
|
|
2107
|
+
if (kind === "signature") return "Ready to sign";
|
|
2108
|
+
if (kind === "payment") return "Payment link";
|
|
2109
|
+
if (kind === "collection") return "Results";
|
|
2110
|
+
if (kind === "confirmation_result") return "Confirmed";
|
|
2111
|
+
return "Saved";
|
|
2112
|
+
}
|
|
2113
|
+
var builtInVisitorToolResultRegistry = [
|
|
2114
|
+
bookingPresenter("booking_offer"),
|
|
2115
|
+
bookingPresenter("booking_confirmed"),
|
|
2116
|
+
bookingPresenter("booking_canceled"),
|
|
2117
|
+
entityResultPresenter(),
|
|
2118
|
+
collectionResultPresenter(),
|
|
2119
|
+
signatureResultPresenter(),
|
|
2120
|
+
resultCardPresenter("document"),
|
|
2121
|
+
resultCardPresenter("payment"),
|
|
2122
|
+
resultCardPresenter("confirmation_result")
|
|
2123
|
+
];
|
|
2124
|
+
function resolvePresenterOutput(result, envelope, registry) {
|
|
2125
|
+
const presenters = [...registry, ...builtInVisitorToolResultRegistry];
|
|
2126
|
+
for (const presentationKind of envelope.presentationKinds) {
|
|
2127
|
+
const presenter = presenters.find(
|
|
2128
|
+
(candidate) => candidate.kind === presentationKind
|
|
2129
|
+
);
|
|
2130
|
+
if (!presenter) continue;
|
|
2131
|
+
try {
|
|
2132
|
+
const presented = presenter.present({
|
|
2133
|
+
envelope,
|
|
2134
|
+
presentationKind,
|
|
2135
|
+
result
|
|
2136
|
+
});
|
|
2137
|
+
if (presented) return presented;
|
|
2138
|
+
} catch {
|
|
2139
|
+
return null;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
function finalizeSummaryPresentation(result, proposed) {
|
|
2145
|
+
const title = safeText(proposed.title) || fallbackTitle(result);
|
|
2146
|
+
const description = safeText(proposed.description);
|
|
2147
|
+
const details = proposed.details?.slice(0, MAX_DETAILS).flatMap((detail) => {
|
|
2148
|
+
const label = safeText(detail.label);
|
|
2149
|
+
const value = safeText(detail.value);
|
|
2150
|
+
if (!label || !value) return [];
|
|
2151
|
+
if (INTERNAL_FACT_LABEL.test(label)) return [];
|
|
2152
|
+
if (value === title || value === description) return [];
|
|
2153
|
+
return [{ label, value }];
|
|
2154
|
+
});
|
|
2155
|
+
const links = proposed.links?.slice(0, MAX_LINKS).flatMap((link) => {
|
|
2156
|
+
const label = safeText(link.label);
|
|
2157
|
+
const href = safeHref(link.href);
|
|
2158
|
+
return label && href ? [{ label, href }] : [];
|
|
2159
|
+
});
|
|
2160
|
+
return {
|
|
2161
|
+
id: result.callId,
|
|
2162
|
+
toolName: result.toolName,
|
|
2163
|
+
status: result.status,
|
|
2164
|
+
kind: "summary",
|
|
2165
|
+
title,
|
|
2166
|
+
...description && description !== title ? { description } : {},
|
|
2167
|
+
...details?.length ? { details } : {},
|
|
2168
|
+
...links?.length ? { links } : {}
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
function presentVisitorToolResult(result, registry = []) {
|
|
2172
|
+
const envelope = parseAgentToolResultEnvelope(result.output) ?? legacyBookingEnvelope(result.output);
|
|
2173
|
+
const proposed = envelope ? resolvePresenterOutput(result, envelope, registry) : null;
|
|
2174
|
+
if (proposed?.kind === "booking") {
|
|
2175
|
+
return {
|
|
2176
|
+
id: result.callId,
|
|
2177
|
+
toolName: result.toolName,
|
|
2178
|
+
status: result.status,
|
|
2179
|
+
kind: "booking",
|
|
2180
|
+
card: proposed.card
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
if (envelope?.ui && envelope.presentationKinds.includes("tool_input")) {
|
|
2184
|
+
return {
|
|
2185
|
+
id: result.callId,
|
|
2186
|
+
toolName: result.toolName,
|
|
2187
|
+
status: result.status,
|
|
2188
|
+
kind: "hidden"
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
if (!proposed) {
|
|
2192
|
+
if (result.status === "failed" || result.status === "rejected") {
|
|
2193
|
+
return {
|
|
2194
|
+
id: result.callId,
|
|
2195
|
+
toolName: result.toolName,
|
|
2196
|
+
status: result.status,
|
|
2197
|
+
kind: "summary",
|
|
2198
|
+
title: fallbackTitle(result)
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
return {
|
|
2202
|
+
id: result.callId,
|
|
2203
|
+
toolName: result.toolName,
|
|
2204
|
+
status: result.status,
|
|
2205
|
+
kind: "hidden"
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
if (proposed.kind === "entity") {
|
|
2209
|
+
return {
|
|
2210
|
+
id: result.callId,
|
|
2211
|
+
toolName: result.toolName,
|
|
2212
|
+
status: result.status,
|
|
2213
|
+
...proposed
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
if (proposed.kind === "collection") {
|
|
2217
|
+
return {
|
|
2218
|
+
id: result.callId,
|
|
2219
|
+
toolName: result.toolName,
|
|
2220
|
+
status: result.status,
|
|
2221
|
+
...proposed
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
if (proposed.kind === "signature") {
|
|
2225
|
+
return {
|
|
2226
|
+
id: result.callId,
|
|
2227
|
+
toolName: result.toolName,
|
|
2228
|
+
status: result.status,
|
|
2229
|
+
...proposed
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
return finalizeSummaryPresentation(result, proposed);
|
|
2233
|
+
}
|
|
2234
|
+
function legacyBookingEnvelope(output) {
|
|
2235
|
+
const card = bookingCardFromActionOutput(output);
|
|
2236
|
+
return card ? {
|
|
2237
|
+
schemaVersion: "webless.tool-result.v1",
|
|
2238
|
+
output: card,
|
|
2239
|
+
presentationKinds: [card.type]
|
|
2240
|
+
} : null;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
// src/react/lib/visitor-input.ts
|
|
2244
|
+
function shouldRenderVisitorInputCard(request) {
|
|
2245
|
+
if (request.kind === "tool-approval" || request.kind === "session-limit") {
|
|
2246
|
+
return true;
|
|
2247
|
+
}
|
|
2248
|
+
if (request.kind === "question") {
|
|
2249
|
+
return (request.options?.length ?? 0) > 0;
|
|
2250
|
+
}
|
|
2251
|
+
return false;
|
|
2252
|
+
}
|
|
2253
|
+
function isChatCollectibleInputRequest(request) {
|
|
2254
|
+
return request.kind === "question" && (request.options?.length ?? 0) === 0;
|
|
2255
|
+
}
|
|
2256
|
+
function appendChatCollectiblePrompts(messages, requests) {
|
|
2257
|
+
const next = [...messages];
|
|
2258
|
+
for (const request of requests.filter(isChatCollectibleInputRequest)) {
|
|
2259
|
+
const prompt = request.prompt.trim();
|
|
2260
|
+
if (!prompt) continue;
|
|
2261
|
+
const last = next.at(-1);
|
|
2262
|
+
if (last?.role === "agent" && last.text.trim() === prompt) continue;
|
|
2263
|
+
next.push({
|
|
2264
|
+
id: `agent-input-${request.requestId}`,
|
|
2265
|
+
role: "agent",
|
|
2266
|
+
text: prompt,
|
|
2267
|
+
createdAt: Date.now()
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
return next;
|
|
2271
|
+
}
|
|
2272
|
+
function chatInputResponseForText(requests, text) {
|
|
2273
|
+
const trimmed = text.trim();
|
|
2274
|
+
if (!trimmed) return null;
|
|
2275
|
+
const pending = requests.find(isChatCollectibleInputRequest);
|
|
2276
|
+
if (!pending) return null;
|
|
2277
|
+
return { requestId: pending.requestId, text: trimmed };
|
|
2278
|
+
}
|
|
2279
|
+
function normalizeAssistantDedupeKey(text) {
|
|
2280
|
+
return text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
2281
|
+
}
|
|
2282
|
+
function isNearDuplicateAssistantText(left, right) {
|
|
2283
|
+
const a = normalizeAssistantDedupeKey(left);
|
|
2284
|
+
const b = normalizeAssistantDedupeKey(right);
|
|
2285
|
+
if (!a || !b) return false;
|
|
2286
|
+
if (a === b) return true;
|
|
2287
|
+
const shorter = a.length <= b.length ? a : b;
|
|
2288
|
+
const longer = a.length <= b.length ? b : a;
|
|
2289
|
+
if (shorter.length < 40) return false;
|
|
2290
|
+
return longer.startsWith(
|
|
2291
|
+
shorter.slice(0, Math.floor(shorter.length * 0.85))
|
|
2292
|
+
);
|
|
2293
|
+
}
|
|
2294
|
+
function appendAgentTurnMessage(messages, displayText) {
|
|
2295
|
+
const trimmed = displayText.trim();
|
|
2296
|
+
if (!trimmed) return [...messages];
|
|
2297
|
+
const agentMessage = {
|
|
2298
|
+
id: `agent-${Date.now()}`,
|
|
2299
|
+
role: "agent",
|
|
2300
|
+
text: trimmed,
|
|
2301
|
+
createdAt: Date.now()
|
|
2302
|
+
};
|
|
2303
|
+
const last = messages.at(-1);
|
|
2304
|
+
if (last?.role === "agent" && (last.id.startsWith("agent-input-") || isNearDuplicateAssistantText(last.text, trimmed))) {
|
|
2305
|
+
return [...messages.slice(0, -1), agentMessage];
|
|
2306
|
+
}
|
|
2307
|
+
return [...messages, agentMessage];
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// src/react/hooks/useAgentChat.ts
|
|
2311
|
+
var DEFAULT_GREETING = "Hi! I can search this site or bring in a specialist when it helps. What would you like to know?";
|
|
2312
|
+
function createInitialState(greeting = DEFAULT_GREETING) {
|
|
2313
|
+
return {
|
|
2314
|
+
phase: "idle",
|
|
2315
|
+
messages: [
|
|
2316
|
+
{
|
|
2317
|
+
id: "greeting",
|
|
2318
|
+
role: "agent",
|
|
2319
|
+
text: greeting,
|
|
2320
|
+
createdAt: 0
|
|
2321
|
+
}
|
|
2322
|
+
],
|
|
2323
|
+
toolSteps: [],
|
|
2324
|
+
journey: null,
|
|
2325
|
+
followUps: [],
|
|
2326
|
+
streamingText: "",
|
|
2327
|
+
pendingOffer: null,
|
|
2328
|
+
pendingInputs: [],
|
|
2329
|
+
toolResults: [],
|
|
2330
|
+
error: null
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
function stateFromConversation(conversation, initialState) {
|
|
2334
|
+
if (!conversation || conversation.messages.length === 0) return initialState;
|
|
2335
|
+
const pendingInputs = conversation.pendingInputs ?? [];
|
|
2336
|
+
const hasWaitingInput = pendingInputs.length > 0;
|
|
2337
|
+
return {
|
|
2338
|
+
...initialState,
|
|
2339
|
+
messages: conversation.messages,
|
|
2340
|
+
phase: hasWaitingInput ? "waiting-input" : conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
|
|
2341
|
+
streamingText: conversation.streamingText,
|
|
2342
|
+
toolSteps: conversation.toolSteps,
|
|
2343
|
+
toolResults: conversation.toolResults,
|
|
2344
|
+
...hasWaitingInput ? { pendingInputs } : {}
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
function upsertToolStep(steps, item) {
|
|
2348
|
+
const next = {
|
|
2349
|
+
id: item.id,
|
|
2350
|
+
kind: item.kind,
|
|
2351
|
+
label: item.label,
|
|
2352
|
+
state: item.state,
|
|
1258
2353
|
...item.detail ? { detail: item.detail } : {}
|
|
1259
2354
|
};
|
|
1260
2355
|
const index = steps.findIndex((step) => step.id === item.id);
|
|
1261
2356
|
if (index < 0) return [...steps, next];
|
|
1262
2357
|
return steps.map((step, stepIndex) => stepIndex === index ? next : step);
|
|
1263
2358
|
}
|
|
2359
|
+
function applyBookingOffer(prev, card) {
|
|
2360
|
+
const pendingOffer = preferBookingOffer(prev.pendingOffer, card);
|
|
2361
|
+
const last = prev.messages.at(-1);
|
|
2362
|
+
if (last?.role === "agent" && prev.phase === "complete") {
|
|
2363
|
+
return {
|
|
2364
|
+
...prev,
|
|
2365
|
+
pendingOffer,
|
|
2366
|
+
messages: [
|
|
2367
|
+
...prev.messages.slice(0, -1),
|
|
2368
|
+
{
|
|
2369
|
+
...last,
|
|
2370
|
+
text: ensureBookingOfferText(last.text, pendingOffer)
|
|
2371
|
+
}
|
|
2372
|
+
]
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
return { ...prev, pendingOffer };
|
|
2376
|
+
}
|
|
1264
2377
|
function completeActivePlanning(steps) {
|
|
1265
2378
|
return steps.map(
|
|
1266
2379
|
(step) => step.kind === "planning" && step.state === "active" ? {
|
|
@@ -1270,6 +2383,9 @@ function completeActivePlanning(steps) {
|
|
|
1270
2383
|
} : step
|
|
1271
2384
|
);
|
|
1272
2385
|
}
|
|
2386
|
+
function isJsonRecord(value) {
|
|
2387
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2388
|
+
}
|
|
1273
2389
|
function useAgentChat({
|
|
1274
2390
|
customerId,
|
|
1275
2391
|
getUnpublishedPreviewGrant,
|
|
@@ -1279,7 +2395,8 @@ function useAgentChat({
|
|
|
1279
2395
|
runtimeOrigin,
|
|
1280
2396
|
visitorSessionId,
|
|
1281
2397
|
storageKeyPrefix,
|
|
1282
|
-
greeting
|
|
2398
|
+
greeting,
|
|
2399
|
+
toolResultRegistry
|
|
1283
2400
|
}) {
|
|
1284
2401
|
const initialState = (0, import_react2.useMemo)(
|
|
1285
2402
|
() => createInitialState(greeting?.trim() || DEFAULT_GREETING),
|
|
@@ -1287,6 +2404,8 @@ function useAgentChat({
|
|
|
1287
2404
|
);
|
|
1288
2405
|
const previewGrantProviderRef = (0, import_react2.useRef)(getUnpublishedPreviewGrant);
|
|
1289
2406
|
previewGrantProviderRef.current = getUnpublishedPreviewGrant;
|
|
2407
|
+
const toolResultRegistryRef = (0, import_react2.useRef)(toolResultRegistry);
|
|
2408
|
+
toolResultRegistryRef.current = toolResultRegistry;
|
|
1290
2409
|
const resolveUnpublishedPreviewGrant = () => {
|
|
1291
2410
|
const provider = previewGrantProviderRef.current;
|
|
1292
2411
|
if (!provider) {
|
|
@@ -1381,14 +2500,18 @@ function useAgentChat({
|
|
|
1381
2500
|
messages: state.messages,
|
|
1382
2501
|
pending: isAgentBusy(state.phase),
|
|
1383
2502
|
streamingText: state.streamingText,
|
|
1384
|
-
toolSteps: state.toolSteps
|
|
2503
|
+
toolSteps: state.toolSteps,
|
|
2504
|
+
toolResults: state.toolResults ?? [],
|
|
2505
|
+
...state.pendingInputs && state.pendingInputs.length > 0 ? { pendingInputs: state.pendingInputs } : {}
|
|
1385
2506
|
});
|
|
1386
2507
|
}, [
|
|
1387
2508
|
resolvedStorageKeyPrefix,
|
|
1388
2509
|
state.messages,
|
|
2510
|
+
state.pendingInputs,
|
|
1389
2511
|
state.phase,
|
|
1390
2512
|
state.streamingText,
|
|
1391
2513
|
state.toolSteps,
|
|
2514
|
+
state.toolResults,
|
|
1392
2515
|
visitorId
|
|
1393
2516
|
]);
|
|
1394
2517
|
const reset = (0, import_react2.useCallback)(() => {
|
|
@@ -1401,13 +2524,20 @@ function useAgentChat({
|
|
|
1401
2524
|
}, [initialState, resolvedStorageKeyPrefix, visitorId]);
|
|
1402
2525
|
const runTurn = (0, import_react2.useCallback)(
|
|
1403
2526
|
async (input) => {
|
|
1404
|
-
const {
|
|
2527
|
+
const {
|
|
2528
|
+
controller,
|
|
2529
|
+
initialText = "",
|
|
2530
|
+
responses,
|
|
2531
|
+
resume,
|
|
2532
|
+
visitorText
|
|
2533
|
+
} = input;
|
|
1405
2534
|
const { signal } = controller;
|
|
1406
2535
|
const isActiveRun = () => runRef.current === controller && !signal.aborted;
|
|
1407
2536
|
try {
|
|
1408
2537
|
let streamStarted = Boolean(initialText);
|
|
1409
2538
|
let streamed = initialText;
|
|
1410
2539
|
const capturedOffers = [];
|
|
2540
|
+
let capturedInputCount = 0;
|
|
1411
2541
|
const handlers = {
|
|
1412
2542
|
onWork: (item) => {
|
|
1413
2543
|
if (!isActiveRun()) return;
|
|
@@ -1417,11 +2547,62 @@ function useAgentChat({
|
|
|
1417
2547
|
toolSteps: upsertToolStep(prev.toolSteps, item)
|
|
1418
2548
|
}));
|
|
1419
2549
|
},
|
|
1420
|
-
|
|
1421
|
-
const
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
2550
|
+
onToolResult: (result) => {
|
|
2551
|
+
const presentation = presentVisitorToolResult(
|
|
2552
|
+
result,
|
|
2553
|
+
toolResultRegistryRef.current
|
|
2554
|
+
);
|
|
2555
|
+
if (presentation.kind === "booking") {
|
|
2556
|
+
const card = presentation.card;
|
|
2557
|
+
if (card.type === "booking_offer") {
|
|
2558
|
+
capturedOffers.push(card);
|
|
2559
|
+
setState((prev) => applyBookingOffer(prev, card));
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
if (!isActiveRun()) return;
|
|
2563
|
+
if (card.type === "booking_confirmed") {
|
|
2564
|
+
pendingBookingRef.current = card;
|
|
2565
|
+
savePendingWidgetBooking(
|
|
2566
|
+
resolvedStorageKeyPrefix,
|
|
2567
|
+
visitorId,
|
|
2568
|
+
card
|
|
2569
|
+
);
|
|
2570
|
+
} else if (card.type === "booking_canceled" && pendingBookingRef.current?.eventUri === card.eventUri) {
|
|
2571
|
+
pendingBookingRef.current = null;
|
|
2572
|
+
clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
|
|
2573
|
+
}
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
if (!isActiveRun()) return;
|
|
2577
|
+
if (presentation.kind === "hidden") return;
|
|
2578
|
+
if (presentation.kind === "input") return;
|
|
2579
|
+
setState((prev) => ({
|
|
2580
|
+
...prev,
|
|
2581
|
+
toolResults: [
|
|
2582
|
+
...(prev.toolResults ?? []).filter(
|
|
2583
|
+
(item) => item.id !== presentation.id
|
|
2584
|
+
),
|
|
2585
|
+
presentation
|
|
2586
|
+
]
|
|
2587
|
+
}));
|
|
2588
|
+
},
|
|
2589
|
+
onInputRequest: (requests) => {
|
|
2590
|
+
if (!isActiveRun()) return;
|
|
2591
|
+
capturedInputCount += requests.length;
|
|
2592
|
+
const cardRequests = requests.filter(shouldRenderVisitorInputCard);
|
|
2593
|
+
const chatRequests = requests.filter(isChatCollectibleInputRequest);
|
|
2594
|
+
setState((prev) => ({
|
|
2595
|
+
...prev,
|
|
2596
|
+
phase: cardRequests.length > 0 ? "waiting-input" : "complete",
|
|
2597
|
+
pendingInputs: [...requests],
|
|
2598
|
+
...chatRequests.length > 0 ? {
|
|
2599
|
+
messages: appendChatCollectiblePrompts(
|
|
2600
|
+
prev.messages,
|
|
2601
|
+
chatRequests
|
|
2602
|
+
),
|
|
2603
|
+
toolSteps: completeActivePlanning(prev.toolSteps)
|
|
2604
|
+
} : {}
|
|
2605
|
+
}));
|
|
1425
2606
|
},
|
|
1426
2607
|
onDelta: (delta) => {
|
|
1427
2608
|
if (!isActiveRun()) return;
|
|
@@ -1447,7 +2628,11 @@ function useAgentChat({
|
|
|
1447
2628
|
streamStarted = true;
|
|
1448
2629
|
}
|
|
1449
2630
|
};
|
|
1450
|
-
let finalText =
|
|
2631
|
+
let finalText = responses ? await clientRef.current.respondTurn({
|
|
2632
|
+
handlers,
|
|
2633
|
+
responses,
|
|
2634
|
+
signal
|
|
2635
|
+
}) : resume ? await clientRef.current.resumeTurn({
|
|
1451
2636
|
handlers,
|
|
1452
2637
|
initialText,
|
|
1453
2638
|
message: visitorText,
|
|
@@ -1459,17 +2644,15 @@ function useAgentChat({
|
|
|
1459
2644
|
signal
|
|
1460
2645
|
});
|
|
1461
2646
|
}
|
|
1462
|
-
if (!isActiveRun() || finalText === null) return;
|
|
2647
|
+
if (!isActiveRun() || finalText === null) return null;
|
|
2648
|
+
if (!finalText.trim() && capturedInputCount > 0) {
|
|
2649
|
+
runRef.current = null;
|
|
2650
|
+
return null;
|
|
2651
|
+
}
|
|
1463
2652
|
const displayText = ensureBookingOfferText(
|
|
1464
2653
|
finalText,
|
|
1465
2654
|
capturedOffers.at(-1) ?? null
|
|
1466
2655
|
);
|
|
1467
|
-
const agentMessage = {
|
|
1468
|
-
id: `agent-${Date.now()}`,
|
|
1469
|
-
role: "agent",
|
|
1470
|
-
text: displayText,
|
|
1471
|
-
createdAt: Date.now()
|
|
1472
|
-
};
|
|
1473
2656
|
const parsedCards = extractToolCards(displayText);
|
|
1474
2657
|
for (const card of parsedCards) {
|
|
1475
2658
|
if (card.type === "booking_confirmed") {
|
|
@@ -1483,21 +2666,25 @@ function useAgentChat({
|
|
|
1483
2666
|
}
|
|
1484
2667
|
setState((prev) => ({
|
|
1485
2668
|
...prev,
|
|
1486
|
-
phase: "complete",
|
|
1487
|
-
messages:
|
|
2669
|
+
phase: (prev.pendingInputs ?? []).some(shouldRenderVisitorInputCard) ? "waiting-input" : "complete",
|
|
2670
|
+
messages: appendAgentTurnMessage(prev.messages, displayText),
|
|
1488
2671
|
toolSteps: completeActivePlanning(prev.toolSteps),
|
|
1489
2672
|
streamingText: "",
|
|
1490
|
-
pendingOffer:
|
|
2673
|
+
pendingOffer: capturedOffers.at(-1) ?? prev.pendingOffer,
|
|
2674
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2675
|
+
isChatCollectibleInputRequest
|
|
2676
|
+
),
|
|
1491
2677
|
followUps: [],
|
|
1492
2678
|
journey: null
|
|
1493
2679
|
}));
|
|
1494
2680
|
runRef.current = null;
|
|
2681
|
+
return displayText;
|
|
1495
2682
|
} catch (error) {
|
|
1496
2683
|
if (error instanceof DOMException && error.name === "AbortError")
|
|
1497
|
-
return;
|
|
1498
|
-
if (!isActiveRun()) return;
|
|
2684
|
+
return null;
|
|
2685
|
+
if (!isActiveRun()) return null;
|
|
1499
2686
|
const message = formatAgentError(error);
|
|
1500
|
-
if (!message) return;
|
|
2687
|
+
if (!message) return null;
|
|
1501
2688
|
setState((prev) => ({
|
|
1502
2689
|
...prev,
|
|
1503
2690
|
phase: "error",
|
|
@@ -1513,6 +2700,7 @@ function useAgentChat({
|
|
|
1513
2700
|
error: message
|
|
1514
2701
|
}));
|
|
1515
2702
|
runRef.current = null;
|
|
2703
|
+
return null;
|
|
1516
2704
|
}
|
|
1517
2705
|
},
|
|
1518
2706
|
[resolvedStorageKeyPrefix, visitorId]
|
|
@@ -1540,6 +2728,49 @@ function useAgentChat({
|
|
|
1540
2728
|
);
|
|
1541
2729
|
const submit = (0, import_react2.useCallback)(
|
|
1542
2730
|
async (visitorText, options) => {
|
|
2731
|
+
const trimmed = visitorText.trim();
|
|
2732
|
+
if (!trimmed) return null;
|
|
2733
|
+
const chatResponse = chatInputResponseForText(
|
|
2734
|
+
state.pendingInputs ?? [],
|
|
2735
|
+
trimmed
|
|
2736
|
+
);
|
|
2737
|
+
if (chatResponse) {
|
|
2738
|
+
const visitorMessage2 = {
|
|
2739
|
+
id: `visitor-${Date.now()}`,
|
|
2740
|
+
role: "visitor",
|
|
2741
|
+
text: trimmed,
|
|
2742
|
+
createdAt: Date.now()
|
|
2743
|
+
};
|
|
2744
|
+
if (runRef.current) {
|
|
2745
|
+
runRef.current.abort();
|
|
2746
|
+
clientRef.current.cancelActive();
|
|
2747
|
+
}
|
|
2748
|
+
const controller2 = new AbortController();
|
|
2749
|
+
runRef.current = controller2;
|
|
2750
|
+
setState((prev) => ({
|
|
2751
|
+
...prev,
|
|
2752
|
+
phase: "running-tools",
|
|
2753
|
+
messages: [...prev.messages, visitorMessage2],
|
|
2754
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2755
|
+
(request) => request.requestId !== chatResponse.requestId
|
|
2756
|
+
),
|
|
2757
|
+
toolSteps: [
|
|
2758
|
+
{
|
|
2759
|
+
id: "planning",
|
|
2760
|
+
kind: "planning",
|
|
2761
|
+
label: "Understanding your question",
|
|
2762
|
+
state: "active"
|
|
2763
|
+
}
|
|
2764
|
+
],
|
|
2765
|
+
error: null
|
|
2766
|
+
}));
|
|
2767
|
+
return await runTurn({
|
|
2768
|
+
controller: controller2,
|
|
2769
|
+
responses: [chatResponse],
|
|
2770
|
+
resume: false,
|
|
2771
|
+
visitorText: ""
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
1543
2774
|
if (runRef.current) {
|
|
1544
2775
|
runRef.current.abort();
|
|
1545
2776
|
clientRef.current.cancelActive();
|
|
@@ -1574,11 +2805,17 @@ ${outgoing}` : outgoing;
|
|
|
1574
2805
|
followUps: [],
|
|
1575
2806
|
streamingText: "",
|
|
1576
2807
|
pendingOffer: null,
|
|
2808
|
+
pendingInputs: [],
|
|
2809
|
+
toolResults: [],
|
|
1577
2810
|
error: null
|
|
1578
2811
|
}));
|
|
1579
|
-
await runTurn({
|
|
2812
|
+
return await runTurn({
|
|
2813
|
+
controller,
|
|
2814
|
+
resume: false,
|
|
2815
|
+
visitorText: runtimeText
|
|
2816
|
+
});
|
|
1580
2817
|
},
|
|
1581
|
-
[runTurn]
|
|
2818
|
+
[runTurn, state.pendingInputs]
|
|
1582
2819
|
);
|
|
1583
2820
|
const retry = (0, import_react2.useCallback)(async () => {
|
|
1584
2821
|
const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
|
|
@@ -1604,6 +2841,8 @@ ${outgoing}` : outgoing;
|
|
|
1604
2841
|
followUps: [],
|
|
1605
2842
|
streamingText: "",
|
|
1606
2843
|
pendingOffer: null,
|
|
2844
|
+
pendingInputs: [],
|
|
2845
|
+
toolResults: [],
|
|
1607
2846
|
error: null
|
|
1608
2847
|
}));
|
|
1609
2848
|
await runTurn({
|
|
@@ -1612,6 +2851,50 @@ ${outgoing}` : outgoing;
|
|
|
1612
2851
|
visitorText: visitorTurnText(visitorMessage)
|
|
1613
2852
|
});
|
|
1614
2853
|
}, [runTurn, state.messages]);
|
|
2854
|
+
const respondToToolInput = (0, import_react2.useCallback)(
|
|
2855
|
+
async (surface, values) => {
|
|
2856
|
+
const details = JSON.stringify(values);
|
|
2857
|
+
await submit(`${surface.title} details provided`, {
|
|
2858
|
+
runtimeText: [
|
|
2859
|
+
`Structured input provided for ${surface.toolSlug}.`,
|
|
2860
|
+
surface.operationId ? `Operation: ${surface.operationId}.` : "",
|
|
2861
|
+
`Use these exact values and retry the action: ${details}`,
|
|
2862
|
+
"Do not invent or alter any values."
|
|
2863
|
+
].filter(Boolean).join("\n")
|
|
2864
|
+
});
|
|
2865
|
+
},
|
|
2866
|
+
[submit]
|
|
2867
|
+
);
|
|
2868
|
+
const respondToInput = (0, import_react2.useCallback)(
|
|
2869
|
+
async (response) => {
|
|
2870
|
+
if (runRef.current) return;
|
|
2871
|
+
const pending = state.pendingInputs?.find(
|
|
2872
|
+
(request) => request.requestId === response.requestId
|
|
2873
|
+
);
|
|
2874
|
+
if (!pending) return;
|
|
2875
|
+
if (pending.ui && isJsonRecord(response.value)) {
|
|
2876
|
+
await respondToToolInput(pending.ui, response.value);
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
const controller = new AbortController();
|
|
2880
|
+
runRef.current = controller;
|
|
2881
|
+
setState((prev) => ({
|
|
2882
|
+
...prev,
|
|
2883
|
+
phase: "running-tools",
|
|
2884
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2885
|
+
(request) => request.requestId !== response.requestId
|
|
2886
|
+
),
|
|
2887
|
+
error: null
|
|
2888
|
+
}));
|
|
2889
|
+
await runTurn({
|
|
2890
|
+
controller,
|
|
2891
|
+
responses: [response],
|
|
2892
|
+
resume: false,
|
|
2893
|
+
visitorText: ""
|
|
2894
|
+
});
|
|
2895
|
+
},
|
|
2896
|
+
[respondToToolInput, runTurn, state.pendingInputs]
|
|
2897
|
+
);
|
|
1615
2898
|
(0, import_react2.useEffect)(() => {
|
|
1616
2899
|
const conversation = loadPersistedAgentConversation(
|
|
1617
2900
|
resolvedStorageKeyPrefix,
|
|
@@ -1645,6 +2928,8 @@ ${outgoing}` : outgoing;
|
|
|
1645
2928
|
state,
|
|
1646
2929
|
reset,
|
|
1647
2930
|
retry,
|
|
2931
|
+
respondToInput,
|
|
2932
|
+
respondToToolInput,
|
|
1648
2933
|
submit,
|
|
1649
2934
|
rememberBooking,
|
|
1650
2935
|
forgetBooking,
|
|
@@ -1709,7 +2994,7 @@ function unregisterAgentPanelController(customerId) {
|
|
|
1709
2994
|
}
|
|
1710
2995
|
|
|
1711
2996
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
1712
|
-
var
|
|
2997
|
+
var import_react10 = require("react");
|
|
1713
2998
|
|
|
1714
2999
|
// src/react/types/conversation.ts
|
|
1715
3000
|
var defaultAgentRailTheme = {
|
|
@@ -1723,8 +3008,8 @@ var defaultAgentRailTheme = {
|
|
|
1723
3008
|
textMuted: "#5a6378",
|
|
1724
3009
|
textSubtle: "#8a94a8",
|
|
1725
3010
|
border: "rgb(42 51 70 / 0.1)",
|
|
1726
|
-
visitorBubble: "#
|
|
1727
|
-
visitorText: "#
|
|
3011
|
+
visitorBubble: "#f3edff",
|
|
3012
|
+
visitorText: "#171b2a",
|
|
1728
3013
|
success: "#18794e",
|
|
1729
3014
|
danger: "#c94b63",
|
|
1730
3015
|
fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
|
|
@@ -1741,7 +3026,8 @@ var defaultDarkAgentRailTheme = {
|
|
|
1741
3026
|
textMuted: "#b6bfce",
|
|
1742
3027
|
textSubtle: "#919cad",
|
|
1743
3028
|
border: "rgb(226 232 240 / 0.16)",
|
|
1744
|
-
visitorBubble: "#
|
|
3029
|
+
visitorBubble: "#2b2140",
|
|
3030
|
+
visitorText: "#f5f7fb",
|
|
1745
3031
|
success: "#55cf91",
|
|
1746
3032
|
danger: "#ff8da1"
|
|
1747
3033
|
};
|
|
@@ -1779,10 +3065,20 @@ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
|
|
|
1779
3065
|
// src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
|
|
1780
3066
|
var import_react5 = require("react");
|
|
1781
3067
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
3068
|
+
function joinLabels(labels) {
|
|
3069
|
+
if (labels.length <= 1) return labels[0] ?? "";
|
|
3070
|
+
if (labels.length === 2) return `${labels[0]} and ${labels[1]}`;
|
|
3071
|
+
return `${labels.slice(0, -1).join(", ")} and ${labels.at(-1) ?? ""}`;
|
|
3072
|
+
}
|
|
1782
3073
|
function workSummary(steps, failed, brandLabel) {
|
|
3074
|
+
const activeSpecialists = steps.filter(
|
|
3075
|
+
(step) => step.kind === "specialist" && step.state === "active"
|
|
3076
|
+
);
|
|
3077
|
+
if (activeSpecialists.length > 0)
|
|
3078
|
+
return `Working with ${joinLabels(
|
|
3079
|
+
activeSpecialists.map((step) => step.label)
|
|
3080
|
+
)}`;
|
|
1783
3081
|
const active = [...steps].reverse().find((step) => step.state === "active");
|
|
1784
|
-
if (active?.kind === "specialist")
|
|
1785
|
-
return `${active.label} is reviewing your question`;
|
|
1786
3082
|
if (active?.kind === "search") return "Searching this site";
|
|
1787
3083
|
if (active)
|
|
1788
3084
|
return brandLabel ? `${brandLabel} is choosing the best way to help` : "Choosing the best way to help";
|
|
@@ -1800,7 +3096,7 @@ function workSummary(steps, failed, brandLabel) {
|
|
|
1800
3096
|
if (specialists.length === 1)
|
|
1801
3097
|
return `Brought in ${specialists[0]?.label}`;
|
|
1802
3098
|
if (searched) return "Searched this site";
|
|
1803
|
-
return "Answer ready";
|
|
3099
|
+
return brandLabel ? `Answered with ${brandLabel}` : "Answer ready";
|
|
1804
3100
|
}
|
|
1805
3101
|
function stepLabel(step, brandLabel) {
|
|
1806
3102
|
return step.kind === "planning" ? brandLabel : step.label;
|
|
@@ -1818,36 +3114,11 @@ function stepDetail(step, steps) {
|
|
|
1818
3114
|
return "Searched this site";
|
|
1819
3115
|
return step.detail;
|
|
1820
3116
|
}
|
|
1821
|
-
function SearchIcon() {
|
|
1822
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
|
|
1823
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "7", cy: "7", r: "3.75", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1824
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1825
|
-
"path",
|
|
1826
|
-
{
|
|
1827
|
-
d: "m10 10 3 3",
|
|
1828
|
-
stroke: "currentColor",
|
|
1829
|
-
strokeWidth: "1.4",
|
|
1830
|
-
strokeLinecap: "round"
|
|
1831
|
-
}
|
|
1832
|
-
)
|
|
1833
|
-
] });
|
|
1834
|
-
}
|
|
1835
|
-
function PlanningIcon() {
|
|
1836
|
-
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)(
|
|
1837
|
-
"path",
|
|
1838
|
-
{
|
|
1839
|
-
d: "M4 4.5h8M4 8h5.5M4 11.5h7",
|
|
1840
|
-
stroke: "currentColor",
|
|
1841
|
-
strokeWidth: "1.4",
|
|
1842
|
-
strokeLinecap: "round"
|
|
1843
|
-
}
|
|
1844
|
-
) });
|
|
1845
|
-
}
|
|
1846
3117
|
function AgentActivityBubble({
|
|
1847
3118
|
brandLabel = "",
|
|
1848
|
-
brandLogoUrl,
|
|
1849
3119
|
failed = false,
|
|
1850
|
-
steps
|
|
3120
|
+
steps,
|
|
3121
|
+
onRetryStep
|
|
1851
3122
|
}) {
|
|
1852
3123
|
const active = steps.some((step) => step.state === "active");
|
|
1853
3124
|
const receiptId = steps.map((step) => step.id).join(":");
|
|
@@ -1855,6 +3126,10 @@ function AgentActivityBubble({
|
|
|
1855
3126
|
null
|
|
1856
3127
|
);
|
|
1857
3128
|
const detailsOpen = active || expandedReceiptId === receiptId;
|
|
3129
|
+
const delegationCount = steps.filter(
|
|
3130
|
+
(step) => step.kind === "specialist"
|
|
3131
|
+
).length;
|
|
3132
|
+
const delegated = delegationCount > 0 && steps.some((step) => step.kind === "planning");
|
|
1858
3133
|
const visibleSteps = steps.filter(
|
|
1859
3134
|
(step) => step.kind !== "planning" || Boolean(brandLabel)
|
|
1860
3135
|
);
|
|
@@ -1870,7 +3145,7 @@ function AgentActivityBubble({
|
|
|
1870
3145
|
workSummary(steps, failed, brandLabel)
|
|
1871
3146
|
] }),
|
|
1872
3147
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-activity-bubble__details", children: [
|
|
1873
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
3148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1874
3149
|
"button",
|
|
1875
3150
|
{
|
|
1876
3151
|
type: "button",
|
|
@@ -1882,15 +3157,19 @@ function AgentActivityBubble({
|
|
|
1882
3157
|
(current) => current === receiptId ? null : receiptId
|
|
1883
3158
|
);
|
|
1884
3159
|
},
|
|
1885
|
-
children:
|
|
3160
|
+
children: [
|
|
3161
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__summary-title", children: "How this answer was made" }),
|
|
3162
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__summary-toggle", children: detailsOpen ? "Hide work" : "Show work" })
|
|
3163
|
+
]
|
|
1886
3164
|
}
|
|
1887
3165
|
),
|
|
1888
3166
|
detailsOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: visibleSteps.map((step) => {
|
|
1889
3167
|
const detail = stepDetail(step, steps);
|
|
3168
|
+
const child = delegated && step.kind === "specialist";
|
|
1890
3169
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1891
3170
|
"li",
|
|
1892
3171
|
{
|
|
1893
|
-
className: "agent-activity-bubble__step"
|
|
3172
|
+
className: `agent-activity-bubble__step${child ? " agent-activity-bubble__step--child" : ""}`,
|
|
1894
3173
|
"data-kind": step.kind,
|
|
1895
3174
|
"data-state": step.state,
|
|
1896
3175
|
children: [
|
|
@@ -1898,25 +3177,27 @@ function AgentActivityBubble({
|
|
|
1898
3177
|
"span",
|
|
1899
3178
|
{
|
|
1900
3179
|
className: "agent-activity-bubble__step-icon",
|
|
1901
|
-
"aria-hidden": "true"
|
|
1902
|
-
children: step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1903
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlanningIcon, {}),
|
|
1904
|
-
brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1905
|
-
"img",
|
|
1906
|
-
{
|
|
1907
|
-
src: brandLogoUrl,
|
|
1908
|
-
alt: "",
|
|
1909
|
-
onError: (event) => {
|
|
1910
|
-
event.currentTarget.hidden = true;
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
) : null
|
|
1914
|
-
] }) : step.kind === "search" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
|
|
3180
|
+
"aria-hidden": "true"
|
|
1915
3181
|
}
|
|
1916
3182
|
),
|
|
1917
3183
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
|
|
1918
3184
|
/* @__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) }) }),
|
|
1919
|
-
detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
|
|
3185
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null,
|
|
3186
|
+
delegated && step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__delegation", children: [
|
|
3187
|
+
"Delegated ",
|
|
3188
|
+
delegationCount,
|
|
3189
|
+
" ",
|
|
3190
|
+
delegationCount === 1 ? "task" : "tasks"
|
|
3191
|
+
] }) : null,
|
|
3192
|
+
step.state === "error" && onRetryStep ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3193
|
+
"button",
|
|
3194
|
+
{
|
|
3195
|
+
type: "button",
|
|
3196
|
+
className: "agent-activity-bubble__step-retry",
|
|
3197
|
+
onClick: () => onRetryStep(step),
|
|
3198
|
+
children: "Retry"
|
|
3199
|
+
}
|
|
3200
|
+
) : null
|
|
1920
3201
|
] })
|
|
1921
3202
|
]
|
|
1922
3203
|
},
|
|
@@ -1931,59 +3212,190 @@ function AgentActivityBubble({
|
|
|
1931
3212
|
var import_react6 = require("react");
|
|
1932
3213
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1933
3214
|
function SendIcon() {
|
|
1934
|
-
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)(
|
|
3215
|
+
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)(
|
|
3216
|
+
"path",
|
|
3217
|
+
{
|
|
3218
|
+
d: "M8 12V4M8 4l-3 3M8 4l3 3",
|
|
3219
|
+
stroke: "currentColor",
|
|
3220
|
+
strokeWidth: "1.5",
|
|
3221
|
+
strokeLinecap: "round",
|
|
3222
|
+
strokeLinejoin: "round"
|
|
3223
|
+
}
|
|
3224
|
+
) });
|
|
3225
|
+
}
|
|
3226
|
+
function emptyValues(form) {
|
|
3227
|
+
const values = {};
|
|
3228
|
+
for (const field of form?.fields ?? []) values[field.id] = "";
|
|
3229
|
+
return values;
|
|
1935
3230
|
}
|
|
1936
3231
|
function Composer({
|
|
1937
3232
|
disabled = false,
|
|
1938
3233
|
placeholder = "Ask anything\u2026",
|
|
1939
3234
|
variant = "default",
|
|
3235
|
+
form = null,
|
|
1940
3236
|
onSubmit
|
|
1941
3237
|
}) {
|
|
1942
3238
|
const [value, setValue] = (0, import_react6.useState)("");
|
|
3239
|
+
const [values, setValues] = (0, import_react6.useState)(
|
|
3240
|
+
() => emptyValues(form)
|
|
3241
|
+
);
|
|
3242
|
+
const [blurred, setBlurred] = (0, import_react6.useState)({});
|
|
1943
3243
|
const inputRef = (0, import_react6.useRef)(null);
|
|
1944
|
-
|
|
3244
|
+
const firstFieldRef = (0, import_react6.useRef)(null);
|
|
3245
|
+
const formId = (0, import_react6.useId)();
|
|
3246
|
+
const activeForm = form;
|
|
3247
|
+
const canSendForm = activeForm ? isComposerFormComplete(activeForm, values) : Boolean(value.trim());
|
|
3248
|
+
(0, import_react6.useEffect)(() => {
|
|
3249
|
+
setValues(emptyValues(form));
|
|
3250
|
+
setBlurred({});
|
|
3251
|
+
}, [form?.id]);
|
|
3252
|
+
(0, import_react6.useEffect)(() => {
|
|
3253
|
+
if (activeForm) firstFieldRef.current?.focus();
|
|
3254
|
+
}, [activeForm?.id]);
|
|
3255
|
+
function submitChat() {
|
|
1945
3256
|
const trimmed = value.trim();
|
|
1946
3257
|
if (!trimmed || disabled) return;
|
|
1947
3258
|
onSubmit?.(trimmed);
|
|
1948
3259
|
setValue("");
|
|
1949
3260
|
inputRef.current?.focus();
|
|
1950
3261
|
}
|
|
3262
|
+
function submitForm() {
|
|
3263
|
+
if (!activeForm || disabled || !canSendForm) return;
|
|
3264
|
+
onSubmit?.(formatComposerFormMessage(activeForm, values));
|
|
3265
|
+
setValues(emptyValues(activeForm));
|
|
3266
|
+
setBlurred({});
|
|
3267
|
+
}
|
|
1951
3268
|
function handleSubmit(event) {
|
|
1952
3269
|
event.preventDefault();
|
|
1953
|
-
|
|
3270
|
+
if (activeForm) submitForm();
|
|
3271
|
+
else submitChat();
|
|
1954
3272
|
}
|
|
1955
|
-
function
|
|
3273
|
+
function handleChatKeyDown(event) {
|
|
1956
3274
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
1957
3275
|
event.preventDefault();
|
|
1958
|
-
|
|
3276
|
+
submitChat();
|
|
1959
3277
|
}
|
|
1960
3278
|
}
|
|
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
|
-
|
|
1986
|
-
|
|
3279
|
+
function handleFormKeyDown(event) {
|
|
3280
|
+
const target = event.target;
|
|
3281
|
+
const isTextarea = Boolean(target && "tagName" in target && target.tagName === "TEXTAREA");
|
|
3282
|
+
if (event.key === "Enter" && !event.shiftKey && !isTextarea) {
|
|
3283
|
+
event.preventDefault();
|
|
3284
|
+
submitForm();
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3288
|
+
"form",
|
|
3289
|
+
{
|
|
3290
|
+
className: [
|
|
3291
|
+
"composer",
|
|
3292
|
+
variant === "dock" ? "composer--dock" : "",
|
|
3293
|
+
activeForm ? "composer--form" : ""
|
|
3294
|
+
].filter(Boolean).join(" "),
|
|
3295
|
+
onSubmit: handleSubmit,
|
|
3296
|
+
children: activeForm ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__sheet", role: "group", "aria-label": "Required details", children: [
|
|
3297
|
+
activeForm.fields.map((field, index) => {
|
|
3298
|
+
const fieldId = `${formId}-${field.id}`;
|
|
3299
|
+
const invalid = Boolean(blurred[field.id]) && !isValidComposerFieldValue(field, values[field.id] ?? "");
|
|
3300
|
+
const controlProps = {
|
|
3301
|
+
id: fieldId,
|
|
3302
|
+
name: field.id,
|
|
3303
|
+
disabled,
|
|
3304
|
+
required: field.required,
|
|
3305
|
+
autoComplete: field.autocomplete,
|
|
3306
|
+
placeholder: field.placeholder,
|
|
3307
|
+
spellCheck: false,
|
|
3308
|
+
value: values[field.id] ?? "",
|
|
3309
|
+
"aria-invalid": invalid || void 0,
|
|
3310
|
+
"aria-describedby": invalid ? `${fieldId}-error` : void 0,
|
|
3311
|
+
onBlur: () => setBlurred((current) => ({ ...current, [field.id]: true })),
|
|
3312
|
+
onChange: (event) => {
|
|
3313
|
+
const next = readComposerControlValue(event);
|
|
3314
|
+
setValues((current) => ({
|
|
3315
|
+
...current,
|
|
3316
|
+
[field.id]: next
|
|
3317
|
+
}));
|
|
3318
|
+
},
|
|
3319
|
+
onKeyDown: handleFormKeyDown
|
|
3320
|
+
};
|
|
3321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
3322
|
+
"div",
|
|
3323
|
+
{
|
|
3324
|
+
className: [
|
|
3325
|
+
"composer__row",
|
|
3326
|
+
field.kind === "textarea" ? "composer__row--grow" : ""
|
|
3327
|
+
].filter(Boolean).join(" "),
|
|
3328
|
+
children: [
|
|
3329
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "composer__label", htmlFor: fieldId, children: [
|
|
3330
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "composer__sr-only", children: field.label }),
|
|
3331
|
+
field.kind === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3332
|
+
"textarea",
|
|
3333
|
+
{
|
|
3334
|
+
...controlProps,
|
|
3335
|
+
ref: index === 0 ? (node) => {
|
|
3336
|
+
firstFieldRef.current = node;
|
|
3337
|
+
} : void 0,
|
|
3338
|
+
className: "composer__control composer__control--area",
|
|
3339
|
+
rows: 3
|
|
3340
|
+
}
|
|
3341
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3342
|
+
"input",
|
|
3343
|
+
{
|
|
3344
|
+
...controlProps,
|
|
3345
|
+
ref: index === 0 ? (node) => {
|
|
3346
|
+
firstFieldRef.current = node;
|
|
3347
|
+
} : void 0,
|
|
3348
|
+
className: "composer__control",
|
|
3349
|
+
type: field.kind,
|
|
3350
|
+
inputMode: field.kind === "tel" ? "tel" : void 0
|
|
3351
|
+
}
|
|
3352
|
+
)
|
|
3353
|
+
] }),
|
|
3354
|
+
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
|
|
3355
|
+
]
|
|
3356
|
+
},
|
|
3357
|
+
field.id
|
|
3358
|
+
);
|
|
3359
|
+
}),
|
|
3360
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "composer__toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3361
|
+
"button",
|
|
3362
|
+
{
|
|
3363
|
+
type: "submit",
|
|
3364
|
+
className: "composer__send",
|
|
3365
|
+
disabled: disabled || !canSendForm,
|
|
3366
|
+
"aria-label": "Send details",
|
|
3367
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
3368
|
+
}
|
|
3369
|
+
) })
|
|
3370
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__field", children: [
|
|
3371
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3372
|
+
"textarea",
|
|
3373
|
+
{
|
|
3374
|
+
ref: inputRef,
|
|
3375
|
+
className: "composer__input",
|
|
3376
|
+
rows: 1,
|
|
3377
|
+
value,
|
|
3378
|
+
placeholder,
|
|
3379
|
+
disabled,
|
|
3380
|
+
spellCheck: false,
|
|
3381
|
+
"aria-label": "Message",
|
|
3382
|
+
onChange: (event) => setValue(event.target.value),
|
|
3383
|
+
onKeyDown: handleChatKeyDown
|
|
3384
|
+
}
|
|
3385
|
+
),
|
|
3386
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3387
|
+
"button",
|
|
3388
|
+
{
|
|
3389
|
+
type: "submit",
|
|
3390
|
+
className: "composer__send",
|
|
3391
|
+
disabled: disabled || !value.trim(),
|
|
3392
|
+
"aria-label": "Send message",
|
|
3393
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
3394
|
+
}
|
|
3395
|
+
)
|
|
3396
|
+
] })
|
|
3397
|
+
}
|
|
3398
|
+
);
|
|
1987
3399
|
}
|
|
1988
3400
|
|
|
1989
3401
|
// src/react/components/FollowUpChips/FollowUpChips.tsx
|
|
@@ -2031,9 +3443,15 @@ var import_react8 = require("react");
|
|
|
2031
3443
|
// src/react/components/BookingCard/BookingCard.tsx
|
|
2032
3444
|
var import_react7 = require("react");
|
|
2033
3445
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
3446
|
+
var BOOKING_STEPS = [
|
|
3447
|
+
{ id: "date", label: "Date" },
|
|
3448
|
+
{ id: "time", label: "Time" },
|
|
3449
|
+
{ id: "details", label: "Details" }
|
|
3450
|
+
];
|
|
2034
3451
|
function monthFromKey(key) {
|
|
2035
3452
|
const [year, month] = key.split("-").map(Number);
|
|
2036
|
-
if (!year || !month)
|
|
3453
|
+
if (!year || !month)
|
|
3454
|
+
return { year: (/* @__PURE__ */ new Date()).getFullYear(), month: (/* @__PURE__ */ new Date()).getMonth() };
|
|
2037
3455
|
return { year, month: month - 1 };
|
|
2038
3456
|
}
|
|
2039
3457
|
function dateKeyFromParts(year, month, day) {
|
|
@@ -2066,6 +3484,7 @@ function BookingCard({
|
|
|
2066
3484
|
const [startTime, setStartTime] = (0, import_react7.useState)("");
|
|
2067
3485
|
const [name, setName] = (0, import_react7.useState)("");
|
|
2068
3486
|
const [email, setEmail] = (0, import_react7.useState)("");
|
|
3487
|
+
const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
|
|
2069
3488
|
const slots = (0, import_react7.useMemo)(
|
|
2070
3489
|
() => bookingSlotsForEventType(offer.slots, eventTypeUri),
|
|
2071
3490
|
[eventTypeUri, offer.slots]
|
|
@@ -2086,14 +3505,18 @@ function BookingCard({
|
|
|
2086
3505
|
setSelectedDate("");
|
|
2087
3506
|
setStartTime("");
|
|
2088
3507
|
setVisibleMonth(
|
|
2089
|
-
firstAvailableBookingMonth(
|
|
3508
|
+
firstAvailableBookingMonth(
|
|
3509
|
+
bookingSlotsForEventType(offer.slots, nextType)
|
|
3510
|
+
)
|
|
2090
3511
|
);
|
|
2091
3512
|
}
|
|
2092
3513
|
const daySlots = (0, import_react7.useMemo)(
|
|
2093
3514
|
() => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
|
|
2094
3515
|
[selectedDate, slots]
|
|
2095
3516
|
);
|
|
2096
|
-
const selectedType = offer.eventTypes.find(
|
|
3517
|
+
const selectedType = offer.eventTypes.find(
|
|
3518
|
+
(item) => item.uri === eventTypeUri
|
|
3519
|
+
);
|
|
2097
3520
|
const selectedSample = availableByDate.get(selectedDate) ?? startTime;
|
|
2098
3521
|
const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
|
|
2099
3522
|
const weekdays = (0, import_react7.useMemo)(() => weekdayLabels(), []);
|
|
@@ -2139,24 +3562,47 @@ function BookingCard({
|
|
|
2139
3562
|
});
|
|
2140
3563
|
}
|
|
2141
3564
|
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: [
|
|
3565
|
+
/* @__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)(
|
|
3566
|
+
"li",
|
|
3567
|
+
{
|
|
3568
|
+
className: [
|
|
3569
|
+
"booking-card__step-indicator",
|
|
3570
|
+
index === stepIndex ? "booking-card__step-indicator--active" : "",
|
|
3571
|
+
index < stepIndex ? "booking-card__step-indicator--complete" : ""
|
|
3572
|
+
].filter(Boolean).join(" "),
|
|
3573
|
+
"aria-current": index === stepIndex ? "step" : void 0,
|
|
3574
|
+
children: [
|
|
3575
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", children: index + 1 }),
|
|
3576
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: item.label })
|
|
3577
|
+
]
|
|
3578
|
+
},
|
|
3579
|
+
item.id
|
|
3580
|
+
)) }),
|
|
2142
3581
|
step === "date" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2143
3582
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: selectedType?.name || "Pick a date" }),
|
|
2144
3583
|
timeZone ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "booking-card__tz", children: [
|
|
2145
3584
|
"Times in ",
|
|
2146
3585
|
timeZone
|
|
2147
3586
|
] }) : null,
|
|
2148
|
-
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
"
|
|
2152
|
-
{
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
3587
|
+
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3588
|
+
"label",
|
|
3589
|
+
{
|
|
3590
|
+
className: "booking-card__field",
|
|
3591
|
+
htmlFor: `${fieldId}-type`,
|
|
3592
|
+
children: [
|
|
3593
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Meeting" }),
|
|
3594
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3595
|
+
"select",
|
|
3596
|
+
{
|
|
3597
|
+
id: `${fieldId}-type`,
|
|
3598
|
+
value: eventTypeUri,
|
|
3599
|
+
onChange: (event) => selectEventType(event.target.value),
|
|
3600
|
+
children: offer.eventTypes.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: item.uri, children: item.name }, item.uri))
|
|
3601
|
+
}
|
|
3602
|
+
)
|
|
3603
|
+
]
|
|
3604
|
+
}
|
|
3605
|
+
) : null,
|
|
2160
3606
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__month", children: [
|
|
2161
3607
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2162
3608
|
"button",
|
|
@@ -2183,29 +3629,44 @@ function BookingCard({
|
|
|
2183
3629
|
)
|
|
2184
3630
|
] }),
|
|
2185
3631
|
/* @__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)) }),
|
|
2186
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("
|
|
2187
|
-
|
|
2188
|
-
|
|
3632
|
+
offer.slots.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__loading", role: "status", children: "Finding available times\u2026" }) : null,
|
|
3633
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3634
|
+
"div",
|
|
3635
|
+
{
|
|
3636
|
+
className: "booking-card__calendar",
|
|
3637
|
+
role: "grid",
|
|
3638
|
+
"aria-label": "Available dates",
|
|
3639
|
+
children: cells.map((cell, index) => {
|
|
3640
|
+
if (!cell) {
|
|
3641
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3642
|
+
"span",
|
|
3643
|
+
{
|
|
3644
|
+
className: "booking-card__day"
|
|
3645
|
+
},
|
|
3646
|
+
`empty-${index}`
|
|
3647
|
+
);
|
|
3648
|
+
}
|
|
3649
|
+
const available = availableByDate.has(cell.key);
|
|
3650
|
+
const selected = cell.key === selectedDate;
|
|
3651
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3652
|
+
"button",
|
|
3653
|
+
{
|
|
3654
|
+
type: "button",
|
|
3655
|
+
className: [
|
|
3656
|
+
"booking-card__day",
|
|
3657
|
+
available ? "booking-card__day--available" : "",
|
|
3658
|
+
selected ? "booking-card__day--selected" : ""
|
|
3659
|
+
].filter(Boolean).join(" "),
|
|
3660
|
+
disabled: !available,
|
|
3661
|
+
"aria-pressed": selected,
|
|
3662
|
+
onClick: () => selectDate(cell.key),
|
|
3663
|
+
children: cell.day
|
|
3664
|
+
},
|
|
3665
|
+
cell.key
|
|
3666
|
+
);
|
|
3667
|
+
})
|
|
2189
3668
|
}
|
|
2190
|
-
|
|
2191
|
-
const selected = cell.key === selectedDate;
|
|
2192
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2193
|
-
"button",
|
|
2194
|
-
{
|
|
2195
|
-
type: "button",
|
|
2196
|
-
className: [
|
|
2197
|
-
"booking-card__day",
|
|
2198
|
-
available ? "booking-card__day--available" : "",
|
|
2199
|
-
selected ? "booking-card__day--selected" : ""
|
|
2200
|
-
].filter(Boolean).join(" "),
|
|
2201
|
-
disabled: !available,
|
|
2202
|
-
"aria-pressed": selected,
|
|
2203
|
-
onClick: () => selectDate(cell.key),
|
|
2204
|
-
children: cell.day
|
|
2205
|
-
},
|
|
2206
|
-
cell.key
|
|
2207
|
-
);
|
|
2208
|
-
}) })
|
|
3669
|
+
)
|
|
2209
3670
|
] }, "date") : null,
|
|
2210
3671
|
step === "time" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2211
3672
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step-bar", children: [
|
|
@@ -2257,35 +3718,57 @@ function BookingCard({
|
|
|
2257
3718
|
] })
|
|
2258
3719
|
] }),
|
|
2259
3720
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__identity", children: [
|
|
2260
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
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
|
-
|
|
2286
|
-
|
|
3721
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3722
|
+
"label",
|
|
3723
|
+
{
|
|
3724
|
+
className: "booking-card__field",
|
|
3725
|
+
htmlFor: `${fieldId}-name`,
|
|
3726
|
+
children: [
|
|
3727
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Name" }),
|
|
3728
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3729
|
+
"input",
|
|
3730
|
+
{
|
|
3731
|
+
id: `${fieldId}-name`,
|
|
3732
|
+
autoComplete: "name",
|
|
3733
|
+
value: name,
|
|
3734
|
+
onChange: (event) => setName(event.target.value),
|
|
3735
|
+
required: true
|
|
3736
|
+
}
|
|
3737
|
+
)
|
|
3738
|
+
]
|
|
3739
|
+
}
|
|
3740
|
+
),
|
|
3741
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3742
|
+
"label",
|
|
3743
|
+
{
|
|
3744
|
+
className: "booking-card__field",
|
|
3745
|
+
htmlFor: `${fieldId}-email`,
|
|
3746
|
+
children: [
|
|
3747
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Email" }),
|
|
3748
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3749
|
+
"input",
|
|
3750
|
+
{
|
|
3751
|
+
id: `${fieldId}-email`,
|
|
3752
|
+
type: "email",
|
|
3753
|
+
autoComplete: "email",
|
|
3754
|
+
value: email,
|
|
3755
|
+
onChange: (event) => setEmail(event.target.value),
|
|
3756
|
+
required: true
|
|
3757
|
+
}
|
|
3758
|
+
)
|
|
3759
|
+
]
|
|
3760
|
+
}
|
|
3761
|
+
)
|
|
2287
3762
|
] }),
|
|
2288
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3763
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3764
|
+
"button",
|
|
3765
|
+
{
|
|
3766
|
+
type: "submit",
|
|
3767
|
+
className: "booking-card__submit",
|
|
3768
|
+
disabled: !name.trim() || !email.trim(),
|
|
3769
|
+
children: "Book this time"
|
|
3770
|
+
}
|
|
3771
|
+
)
|
|
2289
3772
|
] }, "details") : null
|
|
2290
3773
|
] }) });
|
|
2291
3774
|
}
|
|
@@ -2294,6 +3777,43 @@ function BookingCard({
|
|
|
2294
3777
|
var import_streamdown = require("streamdown");
|
|
2295
3778
|
var import_styles = require("streamdown/styles.css");
|
|
2296
3779
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3780
|
+
function normalizeDedupeText(text) {
|
|
3781
|
+
return text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
3782
|
+
}
|
|
3783
|
+
function paragraphsAreNearDuplicates(first, second) {
|
|
3784
|
+
const left = normalizeDedupeText(first);
|
|
3785
|
+
const right = normalizeDedupeText(second);
|
|
3786
|
+
if (left.length < 40 || right.length < 40) return false;
|
|
3787
|
+
if (left === right) return true;
|
|
3788
|
+
const shorter = left.length <= right.length ? left : right;
|
|
3789
|
+
const longer = left.length <= right.length ? right : left;
|
|
3790
|
+
return longer.startsWith(
|
|
3791
|
+
shorter.slice(0, Math.floor(shorter.length * 0.85))
|
|
3792
|
+
);
|
|
3793
|
+
}
|
|
3794
|
+
function paragraphsShareOpening(first, second) {
|
|
3795
|
+
const opening = first.split("\n")[0]?.trim();
|
|
3796
|
+
if (!opening || opening.length < 20) return false;
|
|
3797
|
+
return second.trim().startsWith(opening);
|
|
3798
|
+
}
|
|
3799
|
+
function collapseRepeatedText(text) {
|
|
3800
|
+
const trimmed = text.trim();
|
|
3801
|
+
if (trimmed.length < 40) return trimmed;
|
|
3802
|
+
const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
|
|
3803
|
+
if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
|
|
3804
|
+
return paragraphs[0];
|
|
3805
|
+
}
|
|
3806
|
+
if (paragraphs.length >= 2 && paragraphs.length % 2 === 0) {
|
|
3807
|
+
const mid2 = paragraphs.length / 2;
|
|
3808
|
+
const first = paragraphs.slice(0, mid2).join("\n\n");
|
|
3809
|
+
const second = paragraphs.slice(mid2).join("\n\n");
|
|
3810
|
+
if (first === second) return first;
|
|
3811
|
+
}
|
|
3812
|
+
const mid = Math.floor(trimmed.length / 2);
|
|
3813
|
+
const left = trimmed.slice(0, mid).trim();
|
|
3814
|
+
const right = trimmed.slice(mid).trim();
|
|
3815
|
+
return left.length >= 20 && left === right ? left : trimmed;
|
|
3816
|
+
}
|
|
2297
3817
|
function MessageBubble({
|
|
2298
3818
|
message,
|
|
2299
3819
|
brandLogoUrl,
|
|
@@ -2310,24 +3830,41 @@ function MessageBubble({
|
|
|
2310
3830
|
const offers = offer ? [offer] : extractedOffers;
|
|
2311
3831
|
const visibleText = hideToolCardFences(message.text);
|
|
2312
3832
|
const isStreaming = message.role === "agent" && Boolean(message.streaming);
|
|
2313
|
-
const displayText =
|
|
3833
|
+
const displayText = collapseRepeatedText(
|
|
3834
|
+
offers.length > 0 ? sanitizeBookingOfferCopy(visibleText) : looksLikeBookingAvailabilityDump(visibleText) ? sanitizeBookingOfferCopy(visibleText) : visibleText || (isStreaming ? "" : message.text)
|
|
3835
|
+
);
|
|
2314
3836
|
if (message.role === "visitor") {
|
|
2315
3837
|
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 }) });
|
|
2316
3838
|
}
|
|
2317
|
-
const
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
3839
|
+
const citations = message.citations ?? [];
|
|
3840
|
+
const agentText = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__text", children: [
|
|
3841
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3842
|
+
import_streamdown.Streamdown,
|
|
3843
|
+
{
|
|
3844
|
+
animated: isStreaming,
|
|
3845
|
+
caret: "circle",
|
|
3846
|
+
className: "message-bubble__markdown",
|
|
3847
|
+
controls: false,
|
|
3848
|
+
isAnimating: isStreaming,
|
|
3849
|
+
linkSafety: { enabled: false },
|
|
3850
|
+
mode: isStreaming ? "streaming" : "static",
|
|
3851
|
+
skipHtml: true,
|
|
3852
|
+
children: displayText
|
|
3853
|
+
}
|
|
3854
|
+
),
|
|
3855
|
+
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)(
|
|
3856
|
+
"a",
|
|
3857
|
+
{
|
|
3858
|
+
href: citation.url,
|
|
3859
|
+
target: "_blank",
|
|
3860
|
+
rel: "noreferrer",
|
|
3861
|
+
children: [
|
|
3862
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__source-icon", "aria-hidden": "true", children: "\u25A6" }),
|
|
3863
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: citation.label })
|
|
3864
|
+
]
|
|
3865
|
+
}
|
|
3866
|
+
) }, citation.id)) }) : null
|
|
3867
|
+
] });
|
|
2331
3868
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
|
|
2332
3869
|
displayText ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__agent-row", children: [
|
|
2333
3870
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
@@ -2353,10 +3890,262 @@ function MessageBubble({
|
|
|
2353
3890
|
] });
|
|
2354
3891
|
}
|
|
2355
3892
|
|
|
2356
|
-
// src/react/components/
|
|
3893
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3894
|
+
var import_react9 = require("react");
|
|
3895
|
+
|
|
3896
|
+
// src/react/components/ConfirmationCard/ConfirmationCard.tsx
|
|
2357
3897
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3898
|
+
function ConfirmationCard({
|
|
3899
|
+
disabled = false,
|
|
3900
|
+
request,
|
|
3901
|
+
onRespond
|
|
3902
|
+
}) {
|
|
3903
|
+
const options = request.options ?? [];
|
|
3904
|
+
const heading = request.kind === "tool-approval" ? "Confirm this action" : request.prompt;
|
|
3905
|
+
const prompt = request.kind === "tool-approval" ? request.prompt : void 0;
|
|
3906
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3907
|
+
"section",
|
|
3908
|
+
{
|
|
3909
|
+
className: "confirmation-card",
|
|
3910
|
+
"aria-labelledby": `confirmation-${request.requestId}`,
|
|
3911
|
+
children: [
|
|
3912
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "confirmation-card__heading", children: [
|
|
3913
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { id: `confirmation-${request.requestId}`, children: heading }),
|
|
3914
|
+
prompt ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: prompt }) : null
|
|
3915
|
+
] }),
|
|
3916
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "confirmation-card__actions", children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3917
|
+
"button",
|
|
3918
|
+
{
|
|
3919
|
+
type: "button",
|
|
3920
|
+
className: `confirmation-card__action confirmation-card__action--${option.style ?? "default"}`,
|
|
3921
|
+
disabled,
|
|
3922
|
+
onClick: () => onRespond?.({
|
|
3923
|
+
requestId: request.requestId,
|
|
3924
|
+
optionId: option.id
|
|
3925
|
+
}),
|
|
3926
|
+
children: option.label
|
|
3927
|
+
},
|
|
3928
|
+
option.id
|
|
3929
|
+
)) })
|
|
3930
|
+
]
|
|
3931
|
+
}
|
|
3932
|
+
);
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3935
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3936
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3937
|
+
function HumanInputCard({
|
|
3938
|
+
disabled = false,
|
|
3939
|
+
request,
|
|
3940
|
+
onRespond
|
|
3941
|
+
}) {
|
|
3942
|
+
const [text, setText] = (0, import_react9.useState)("");
|
|
3943
|
+
const options = request.options ?? [];
|
|
3944
|
+
const showText = request.display === "text" || request.allowFreeform && options.length === 0;
|
|
3945
|
+
if (options.length > 0) {
|
|
3946
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3947
|
+
ConfirmationCard,
|
|
3948
|
+
{
|
|
3949
|
+
disabled,
|
|
3950
|
+
request,
|
|
3951
|
+
onRespond
|
|
3952
|
+
}
|
|
3953
|
+
);
|
|
3954
|
+
}
|
|
3955
|
+
function submitText(event) {
|
|
3956
|
+
event.preventDefault();
|
|
3957
|
+
const value = text.trim();
|
|
3958
|
+
if (!value || disabled) return;
|
|
3959
|
+
onRespond?.({ requestId: request.requestId, text: value });
|
|
3960
|
+
}
|
|
3961
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3962
|
+
"section",
|
|
3963
|
+
{
|
|
3964
|
+
className: "human-input-card",
|
|
3965
|
+
"aria-labelledby": `human-input-${request.requestId}`,
|
|
3966
|
+
children: [
|
|
3967
|
+
/* @__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 }) }),
|
|
3968
|
+
showText ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("form", { onSubmit: submitText, children: [
|
|
3969
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: `human-input-text-${request.requestId}`, children: "Response" }),
|
|
3970
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
3971
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3972
|
+
"input",
|
|
3973
|
+
{
|
|
3974
|
+
id: `human-input-text-${request.requestId}`,
|
|
3975
|
+
value: text,
|
|
3976
|
+
disabled,
|
|
3977
|
+
onChange: (event) => setText(event.target.value)
|
|
3978
|
+
}
|
|
3979
|
+
),
|
|
3980
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "submit", disabled: disabled || !text.trim(), children: "Send" })
|
|
3981
|
+
] })
|
|
3982
|
+
] }) : null,
|
|
3983
|
+
!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
|
|
3984
|
+
]
|
|
3985
|
+
}
|
|
3986
|
+
);
|
|
3987
|
+
}
|
|
3988
|
+
|
|
3989
|
+
// src/react/components/CollectionResultCard/CollectionResultCard.tsx
|
|
3990
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
3991
|
+
function CollectionResultCard({
|
|
3992
|
+
result
|
|
3993
|
+
}) {
|
|
3994
|
+
const empty = result.items.length === 0;
|
|
3995
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
3996
|
+
"section",
|
|
3997
|
+
{
|
|
3998
|
+
className: `collection-result-card tool-result-card tool-result-card--${result.status}`,
|
|
3999
|
+
"aria-label": result.title,
|
|
4000
|
+
children: [
|
|
4001
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4002
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4003
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("strong", { children: result.title })
|
|
4004
|
+
] }),
|
|
4005
|
+
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: [
|
|
4006
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "collection-result-card__item-title", children: item.title }),
|
|
4007
|
+
item.description ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: item.description }) : null,
|
|
4008
|
+
item.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dl", { children: item.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
4009
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dt", { children: detail.label }),
|
|
4010
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dd", { children: detail.value })
|
|
4011
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4012
|
+
item.href ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
|
|
4013
|
+
] }, item.title)) })
|
|
4014
|
+
]
|
|
4015
|
+
}
|
|
4016
|
+
);
|
|
4017
|
+
}
|
|
4018
|
+
|
|
4019
|
+
// src/react/components/EntityResultCard/EntityResultCard.tsx
|
|
4020
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
4021
|
+
function EntityResultCard({
|
|
4022
|
+
result
|
|
4023
|
+
}) {
|
|
4024
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
4025
|
+
"section",
|
|
4026
|
+
{
|
|
4027
|
+
className: `entity-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4028
|
+
"aria-label": result.title,
|
|
4029
|
+
children: [
|
|
4030
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4031
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4032
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: result.title })
|
|
4033
|
+
] }),
|
|
4034
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { children: result.description }) : null,
|
|
4035
|
+
result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
4036
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dt", { children: detail.label }),
|
|
4037
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dd", { children: detail.value })
|
|
4038
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4039
|
+
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)(
|
|
4040
|
+
"a",
|
|
4041
|
+
{
|
|
4042
|
+
href: link.href,
|
|
4043
|
+
target: "_blank",
|
|
4044
|
+
rel: "noreferrer",
|
|
4045
|
+
children: link.label
|
|
4046
|
+
},
|
|
4047
|
+
link.href
|
|
4048
|
+
)) }) : null
|
|
4049
|
+
]
|
|
4050
|
+
}
|
|
4051
|
+
);
|
|
4052
|
+
}
|
|
4053
|
+
|
|
4054
|
+
// src/react/components/SignatureResultCard/SignatureResultCard.tsx
|
|
4055
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4056
|
+
function SignatureResultCard({
|
|
4057
|
+
result
|
|
4058
|
+
}) {
|
|
4059
|
+
const primaryLink = result.links?.[0];
|
|
4060
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
4061
|
+
"section",
|
|
4062
|
+
{
|
|
4063
|
+
className: `signature-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4064
|
+
"aria-label": result.title,
|
|
4065
|
+
children: [
|
|
4066
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4067
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4068
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: result.title })
|
|
4069
|
+
] }),
|
|
4070
|
+
result.statusLabel ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
|
|
4071
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: result.description }) : null,
|
|
4072
|
+
primaryLink ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4073
|
+
"a",
|
|
4074
|
+
{
|
|
4075
|
+
className: "signature-result-card__cta",
|
|
4076
|
+
href: primaryLink.href,
|
|
4077
|
+
target: "_blank",
|
|
4078
|
+
rel: "noreferrer",
|
|
4079
|
+
children: primaryLink.label
|
|
4080
|
+
}
|
|
4081
|
+
) : null
|
|
4082
|
+
]
|
|
4083
|
+
}
|
|
4084
|
+
);
|
|
4085
|
+
}
|
|
4086
|
+
|
|
4087
|
+
// src/react/components/ToolResultCard/ToolResultCard.tsx
|
|
4088
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
4089
|
+
function ToolResultCard({
|
|
4090
|
+
result
|
|
4091
|
+
}) {
|
|
4092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
4093
|
+
"section",
|
|
4094
|
+
{
|
|
4095
|
+
className: `tool-result-card tool-result-card--${result.status}`,
|
|
4096
|
+
"aria-label": result.title,
|
|
4097
|
+
children: [
|
|
4098
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4099
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4100
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: result.title })
|
|
4101
|
+
] }),
|
|
4102
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: result.description }) : null,
|
|
4103
|
+
result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
4104
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dt", { children: detail.label }),
|
|
4105
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dd", { children: detail.value })
|
|
4106
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4107
|
+
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)(
|
|
4108
|
+
"a",
|
|
4109
|
+
{
|
|
4110
|
+
href: link.href,
|
|
4111
|
+
target: "_blank",
|
|
4112
|
+
rel: "noreferrer",
|
|
4113
|
+
children: link.label
|
|
4114
|
+
},
|
|
4115
|
+
link.href
|
|
4116
|
+
)) }) : null
|
|
4117
|
+
]
|
|
4118
|
+
}
|
|
4119
|
+
);
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4122
|
+
// src/react/components/VisitorToolResultView/VisitorToolResultView.tsx
|
|
4123
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
4124
|
+
function VisitorToolResultView({
|
|
4125
|
+
result
|
|
4126
|
+
}) {
|
|
4127
|
+
if (result.kind === "entity") {
|
|
4128
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EntityResultCard, { result });
|
|
4129
|
+
}
|
|
4130
|
+
if (result.kind === "collection") {
|
|
4131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CollectionResultCard, { result });
|
|
4132
|
+
}
|
|
4133
|
+
if (result.kind === "signature") {
|
|
4134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SignatureResultCard, { result });
|
|
4135
|
+
}
|
|
4136
|
+
if (result.kind === "summary") {
|
|
4137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ToolResultCard, { result });
|
|
4138
|
+
}
|
|
4139
|
+
return null;
|
|
4140
|
+
}
|
|
4141
|
+
function isRenderableVisitorToolResult(result) {
|
|
4142
|
+
return result.kind === "summary" || result.kind === "entity" || result.kind === "collection" || result.kind === "signature";
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
// src/react/components/AgentRail/AgentRail.tsx
|
|
4146
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2358
4147
|
function MinimizeIcon() {
|
|
2359
|
-
return /* @__PURE__ */ (0,
|
|
4148
|
+
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)(
|
|
2360
4149
|
"path",
|
|
2361
4150
|
{
|
|
2362
4151
|
d: "M3.5 8h9",
|
|
@@ -2367,7 +4156,7 @@ function MinimizeIcon() {
|
|
|
2367
4156
|
) });
|
|
2368
4157
|
}
|
|
2369
4158
|
function CloseIcon() {
|
|
2370
|
-
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)(
|
|
2371
4160
|
"path",
|
|
2372
4161
|
{
|
|
2373
4162
|
d: "M4 4l8 8M12 4l-8 8",
|
|
@@ -2378,7 +4167,7 @@ function CloseIcon() {
|
|
|
2378
4167
|
) });
|
|
2379
4168
|
}
|
|
2380
4169
|
function NewChatIcon() {
|
|
2381
|
-
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)(
|
|
2382
4171
|
"path",
|
|
2383
4172
|
{
|
|
2384
4173
|
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",
|
|
@@ -2390,7 +4179,7 @@ function NewChatIcon() {
|
|
|
2390
4179
|
) });
|
|
2391
4180
|
}
|
|
2392
4181
|
function ExpandIcon() {
|
|
2393
|
-
return /* @__PURE__ */ (0,
|
|
4182
|
+
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)(
|
|
2394
4183
|
"path",
|
|
2395
4184
|
{
|
|
2396
4185
|
d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
|
|
@@ -2402,7 +4191,7 @@ function ExpandIcon() {
|
|
|
2402
4191
|
) });
|
|
2403
4192
|
}
|
|
2404
4193
|
function RestoreIcon() {
|
|
2405
|
-
return /* @__PURE__ */ (0,
|
|
4194
|
+
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)(
|
|
2406
4195
|
"path",
|
|
2407
4196
|
{
|
|
2408
4197
|
d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
|
|
@@ -2430,28 +4219,29 @@ function AgentRail({
|
|
|
2430
4219
|
onRetry,
|
|
2431
4220
|
onSubmit,
|
|
2432
4221
|
onFollowUpSelect,
|
|
2433
|
-
onBook
|
|
4222
|
+
onBook,
|
|
4223
|
+
onInputResponse
|
|
2434
4224
|
}) {
|
|
2435
|
-
const transcriptRef = (0,
|
|
4225
|
+
const transcriptRef = (0, import_react10.useRef)(null);
|
|
2436
4226
|
const resolvedBrandLabel = brandLabel.trim();
|
|
2437
4227
|
const resolvedBrandLogoUrl = brandLogoUrl?.trim();
|
|
2438
|
-
const [failedLogoUrl, setFailedLogoUrl] = (0,
|
|
4228
|
+
const [failedLogoUrl, setFailedLogoUrl] = (0, import_react10.useState)(null);
|
|
2439
4229
|
const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
|
|
2440
4230
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2441
4231
|
const brandedTheme = { ...defaultAgentRailTheme, ...theme };
|
|
2442
4232
|
const resolvedTheme = resolvedColorScheme === "dark" ? {
|
|
2443
4233
|
...brandedTheme,
|
|
2444
4234
|
brand: theme?.brand ?? defaultDarkAgentRailTheme.brand,
|
|
2445
|
-
brandDeep: defaultDarkAgentRailTheme.brandDeep,
|
|
2446
|
-
brandSoft: `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
|
|
2447
|
-
border: defaultDarkAgentRailTheme.border,
|
|
2448
|
-
danger: defaultDarkAgentRailTheme.danger,
|
|
2449
|
-
success: defaultDarkAgentRailTheme.success,
|
|
2450
|
-
surface: defaultDarkAgentRailTheme.surface,
|
|
2451
|
-
surfaceMuted: defaultDarkAgentRailTheme.surfaceMuted,
|
|
2452
|
-
text: defaultDarkAgentRailTheme.text,
|
|
2453
|
-
textMuted: defaultDarkAgentRailTheme.textMuted,
|
|
2454
|
-
textSubtle: defaultDarkAgentRailTheme.textSubtle,
|
|
4235
|
+
brandDeep: theme?.brandDeep ?? defaultDarkAgentRailTheme.brandDeep,
|
|
4236
|
+
brandSoft: theme?.brandSoft ?? `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
|
|
4237
|
+
border: theme?.border ?? defaultDarkAgentRailTheme.border,
|
|
4238
|
+
danger: theme?.danger ?? defaultDarkAgentRailTheme.danger,
|
|
4239
|
+
success: theme?.success ?? defaultDarkAgentRailTheme.success,
|
|
4240
|
+
surface: theme?.surface ?? defaultDarkAgentRailTheme.surface,
|
|
4241
|
+
surfaceMuted: theme?.surfaceMuted ?? defaultDarkAgentRailTheme.surfaceMuted,
|
|
4242
|
+
text: theme?.text ?? defaultDarkAgentRailTheme.text,
|
|
4243
|
+
textMuted: theme?.textMuted ?? defaultDarkAgentRailTheme.textMuted,
|
|
4244
|
+
textSubtle: theme?.textSubtle ?? defaultDarkAgentRailTheme.textSubtle,
|
|
2455
4245
|
visitorBubble: theme?.visitorBubble ?? theme?.brand ?? defaultDarkAgentRailTheme.visitorBubble
|
|
2456
4246
|
} : brandedTheme;
|
|
2457
4247
|
const railStyle = {
|
|
@@ -2474,8 +4264,15 @@ function AgentRail({
|
|
|
2474
4264
|
"--as-font-display": resolvedTheme.fontDisplay,
|
|
2475
4265
|
colorScheme: resolvedColorScheme
|
|
2476
4266
|
};
|
|
2477
|
-
const
|
|
4267
|
+
const pendingInputRequests = (state.pendingInputs ?? []).filter(
|
|
4268
|
+
(request) => shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
4269
|
+
);
|
|
4270
|
+
const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && pendingInputRequests.length > 0;
|
|
4271
|
+
const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
|
|
2478
4272
|
const showActivity = state.toolSteps.length > 0;
|
|
4273
|
+
const visitorToolResults = (state.toolResults ?? []).filter(
|
|
4274
|
+
isRenderableVisitorToolResult
|
|
4275
|
+
);
|
|
2479
4276
|
const hasVisitorMessages2 = state.messages.some(
|
|
2480
4277
|
(message) => message.role === "visitor"
|
|
2481
4278
|
);
|
|
@@ -2485,22 +4282,44 @@ function AgentRail({
|
|
|
2485
4282
|
);
|
|
2486
4283
|
const visibleMessages = hasVisitorMessages2 ? state.messages : [];
|
|
2487
4284
|
const lastMessage = visibleMessages.at(-1);
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
4285
|
+
let lastAgentIndex = -1;
|
|
4286
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4287
|
+
if (visibleMessages[index]?.role === "agent") {
|
|
4288
|
+
lastAgentIndex = index;
|
|
4289
|
+
break;
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4292
|
+
let lastVisitorIndex = -1;
|
|
4293
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4294
|
+
if (visibleMessages[index]?.role === "visitor") {
|
|
4295
|
+
lastVisitorIndex = index;
|
|
4296
|
+
break;
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
const lastIsAgent = lastMessage?.role === "agent";
|
|
4300
|
+
const streamingMessage = state.phase === "streaming" && state.streamingText && !lastIsAgent ? {
|
|
2491
4301
|
createdAt: 0,
|
|
2492
4302
|
id: "streaming-response",
|
|
2493
4303
|
role: "agent",
|
|
2494
4304
|
streaming: true,
|
|
2495
4305
|
text: state.streamingText
|
|
2496
|
-
} : state.pendingOffer ? {
|
|
4306
|
+
} : state.pendingOffer && !lastIsAgent ? {
|
|
2497
4307
|
createdAt: 0,
|
|
2498
4308
|
id: "pending-booking",
|
|
2499
4309
|
role: "agent",
|
|
2500
4310
|
streaming: false,
|
|
2501
4311
|
text: "Pick a date and time that works for you."
|
|
2502
4312
|
} : null;
|
|
2503
|
-
|
|
4313
|
+
const bookingReadyText = state.streamingText || (lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "");
|
|
4314
|
+
const waitingForBooking = !state.pendingOffer && looksLikeBookingReady(bookingReadyText) && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
|
|
4315
|
+
const lastAgentText = lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "";
|
|
4316
|
+
const composerForm = resolveComposerForm({
|
|
4317
|
+
agentText: lastAgentText,
|
|
4318
|
+
cards: extractToolCards(lastAgentText),
|
|
4319
|
+
hasBookingOffer: Boolean(state.pendingOffer) || waitingForBooking,
|
|
4320
|
+
enabled: lastIsAgent && !isBusy
|
|
4321
|
+
});
|
|
4322
|
+
(0, import_react10.useEffect)(() => {
|
|
2504
4323
|
const node = transcriptRef.current;
|
|
2505
4324
|
if (!node) return;
|
|
2506
4325
|
node.scrollTop = node.scrollHeight;
|
|
@@ -2511,11 +4330,13 @@ function AgentRail({
|
|
|
2511
4330
|
state.followUps,
|
|
2512
4331
|
state.journey
|
|
2513
4332
|
]);
|
|
2514
|
-
return /* @__PURE__ */ (0,
|
|
4333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2515
4334
|
"aside",
|
|
2516
4335
|
{
|
|
2517
|
-
className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4336
|
+
className: `agent-rail not-typeset${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4337
|
+
"data-not-typeset": "",
|
|
2518
4338
|
"data-color-scheme": resolvedColorScheme,
|
|
4339
|
+
spellCheck: false,
|
|
2519
4340
|
style: railStyle,
|
|
2520
4341
|
"aria-label": "Agent conversation",
|
|
2521
4342
|
"aria-modal": mobileFullscreen || expanded ? true : void 0,
|
|
@@ -2523,28 +4344,28 @@ function AgentRail({
|
|
|
2523
4344
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
2524
4345
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
2525
4346
|
children: [
|
|
2526
|
-
/* @__PURE__ */ (0,
|
|
2527
|
-
onCollapse ? /* @__PURE__ */ (0,
|
|
4347
|
+
/* @__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: [
|
|
4348
|
+
onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2528
4349
|
"button",
|
|
2529
4350
|
{
|
|
2530
4351
|
type: "button",
|
|
2531
4352
|
className: "agent-rail__collapse",
|
|
2532
4353
|
"aria-label": "Collapse assist",
|
|
2533
4354
|
onClick: onCollapse,
|
|
2534
|
-
children: /* @__PURE__ */ (0,
|
|
4355
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MinimizeIcon, {})
|
|
2535
4356
|
}
|
|
2536
|
-
) : onClose ? /* @__PURE__ */ (0,
|
|
4357
|
+
) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2537
4358
|
"button",
|
|
2538
4359
|
{
|
|
2539
4360
|
type: "button",
|
|
2540
4361
|
className: "agent-rail__close",
|
|
2541
4362
|
"aria-label": "Close agent",
|
|
2542
4363
|
onClick: onClose,
|
|
2543
|
-
children: /* @__PURE__ */ (0,
|
|
4364
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CloseIcon, {})
|
|
2544
4365
|
}
|
|
2545
|
-
) : /* @__PURE__ */ (0,
|
|
2546
|
-
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0,
|
|
2547
|
-
showBrandLogo ? /* @__PURE__ */ (0,
|
|
4366
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
|
|
4367
|
+
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "agent-rail__identity", children: [
|
|
4368
|
+
showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2548
4369
|
"img",
|
|
2549
4370
|
{
|
|
2550
4371
|
className: "agent-rail__brand-logo",
|
|
@@ -2555,10 +4376,10 @@ function AgentRail({
|
|
|
2555
4376
|
}
|
|
2556
4377
|
}
|
|
2557
4378
|
) }) : null,
|
|
2558
|
-
resolvedBrandLabel ? /* @__PURE__ */ (0,
|
|
4379
|
+
resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
|
|
2559
4380
|
] }) : null,
|
|
2560
|
-
/* @__PURE__ */ (0,
|
|
2561
|
-
onReset ? /* @__PURE__ */ (0,
|
|
4381
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "agent-rail__actions", children: [
|
|
4382
|
+
onReset ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2562
4383
|
"button",
|
|
2563
4384
|
{
|
|
2564
4385
|
type: "button",
|
|
@@ -2566,24 +4387,24 @@ function AgentRail({
|
|
|
2566
4387
|
"aria-label": "Start a new conversation",
|
|
2567
4388
|
disabled: !hasVisitorMessages2,
|
|
2568
4389
|
onClick: onReset,
|
|
2569
|
-
children: /* @__PURE__ */ (0,
|
|
4390
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NewChatIcon, {})
|
|
2570
4391
|
}
|
|
2571
4392
|
) : null,
|
|
2572
|
-
onExpandToggle ? /* @__PURE__ */ (0,
|
|
4393
|
+
onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2573
4394
|
"button",
|
|
2574
4395
|
{
|
|
2575
4396
|
type: "button",
|
|
2576
4397
|
className: "agent-rail__expand",
|
|
2577
4398
|
"aria-label": expanded ? "Exit full screen" : "Open full screen",
|
|
2578
4399
|
onClick: onExpandToggle,
|
|
2579
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
4400
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ExpandIcon, {})
|
|
2580
4401
|
}
|
|
2581
4402
|
) : null
|
|
2582
4403
|
] })
|
|
2583
4404
|
] }) }),
|
|
2584
|
-
/* @__PURE__ */ (0,
|
|
2585
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ (0,
|
|
2586
|
-
greeting?.role === "agent" ? /* @__PURE__ */ (0,
|
|
4405
|
+
/* @__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: [
|
|
4406
|
+
!hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
4407
|
+
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2587
4408
|
MessageBubble,
|
|
2588
4409
|
{
|
|
2589
4410
|
message: greeting,
|
|
@@ -2591,7 +4412,7 @@ function AgentRail({
|
|
|
2591
4412
|
onBook
|
|
2592
4413
|
}
|
|
2593
4414
|
) : null,
|
|
2594
|
-
showIdleFollowUps ? /* @__PURE__ */ (0,
|
|
4415
|
+
showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2595
4416
|
FollowUpChips,
|
|
2596
4417
|
{
|
|
2597
4418
|
suggestions: state.followUps,
|
|
@@ -2599,64 +4420,94 @@ function AgentRail({
|
|
|
2599
4420
|
label: "Start here",
|
|
2600
4421
|
onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
|
|
2601
4422
|
}
|
|
2602
|
-
) }) : null
|
|
4423
|
+
) }) : null,
|
|
4424
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4425
|
+
AgentActivityBubble,
|
|
4426
|
+
{
|
|
4427
|
+
brandLabel: resolvedBrandLabel,
|
|
4428
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4429
|
+
failed: state.phase === "error",
|
|
4430
|
+
steps: state.toolSteps
|
|
4431
|
+
}
|
|
4432
|
+
) : null,
|
|
4433
|
+
visitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VisitorToolResultView, { result }, result.id)),
|
|
4434
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4435
|
+
HumanInputCard,
|
|
4436
|
+
{
|
|
4437
|
+
request,
|
|
4438
|
+
onRespond: onInputResponse
|
|
4439
|
+
},
|
|
4440
|
+
request.requestId
|
|
4441
|
+
))
|
|
2603
4442
|
] }) : null,
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
4443
|
+
visibleMessages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__turn-block", children: [
|
|
4444
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4445
|
+
MessageBubble,
|
|
4446
|
+
{
|
|
4447
|
+
message,
|
|
4448
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4449
|
+
offer: index === lastAgentIndex ? state.pendingOffer : void 0,
|
|
4450
|
+
onBook
|
|
4451
|
+
}
|
|
4452
|
+
),
|
|
4453
|
+
index === lastVisitorIndex ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
4454
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4455
|
+
AgentActivityBubble,
|
|
4456
|
+
{
|
|
4457
|
+
brandLabel: resolvedBrandLabel,
|
|
4458
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4459
|
+
failed: state.phase === "error",
|
|
4460
|
+
steps: state.toolSteps
|
|
4461
|
+
}
|
|
4462
|
+
) : null,
|
|
4463
|
+
visitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VisitorToolResultView, { result }, result.id)),
|
|
4464
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4465
|
+
HumanInputCard,
|
|
4466
|
+
{
|
|
4467
|
+
request,
|
|
4468
|
+
onRespond: onInputResponse
|
|
4469
|
+
},
|
|
4470
|
+
request.requestId
|
|
4471
|
+
))
|
|
4472
|
+
] }) : null
|
|
4473
|
+
] }, message.id)),
|
|
4474
|
+
streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2623
4475
|
MessageBubble,
|
|
2624
4476
|
{
|
|
2625
|
-
message:
|
|
4477
|
+
message: streamingMessage,
|
|
2626
4478
|
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4479
|
+
offer: state.pendingOffer,
|
|
2627
4480
|
onBook
|
|
2628
4481
|
}
|
|
2629
4482
|
) : null,
|
|
2630
|
-
|
|
2631
|
-
|
|
4483
|
+
waitingForBooking ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4484
|
+
BookingCard,
|
|
2632
4485
|
{
|
|
2633
|
-
|
|
2634
|
-
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
2635
|
-
offer: state.pendingOffer,
|
|
2636
|
-
onBook
|
|
4486
|
+
offer: { type: "booking_offer", eventTypes: [], slots: [] }
|
|
2637
4487
|
}
|
|
2638
4488
|
) : null,
|
|
2639
|
-
state.error ? /* @__PURE__ */ (0,
|
|
2640
|
-
/* @__PURE__ */ (0,
|
|
2641
|
-
/* @__PURE__ */ (0,
|
|
2642
|
-
/* @__PURE__ */ (0,
|
|
4489
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
|
|
4490
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
4491
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "Something went wrong" }),
|
|
4492
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: state.error })
|
|
2643
4493
|
] }),
|
|
2644
|
-
onRetry ? /* @__PURE__ */ (0,
|
|
4494
|
+
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
2645
4495
|
] }) : null
|
|
2646
4496
|
] }) }),
|
|
2647
|
-
/* @__PURE__ */ (0,
|
|
2648
|
-
/* @__PURE__ */ (0,
|
|
4497
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
|
|
4498
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2649
4499
|
Composer,
|
|
2650
4500
|
{
|
|
2651
4501
|
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
2652
4502
|
disabled: isBusy,
|
|
4503
|
+
form: composerForm,
|
|
2653
4504
|
placeholder: composerPlaceholder,
|
|
2654
4505
|
onSubmit
|
|
2655
4506
|
}
|
|
2656
4507
|
),
|
|
2657
|
-
/* @__PURE__ */ (0,
|
|
2658
|
-
/* @__PURE__ */ (0,
|
|
2659
|
-
/* @__PURE__ */ (0,
|
|
4508
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { children: [
|
|
4509
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "AI can make mistakes. Check important info." }),
|
|
4510
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: poweredByLabel })
|
|
2660
4511
|
] }) })
|
|
2661
4512
|
] })
|
|
2662
4513
|
]
|
|
@@ -2665,9 +4516,9 @@ function AgentRail({
|
|
|
2665
4516
|
}
|
|
2666
4517
|
|
|
2667
4518
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
2668
|
-
var
|
|
4519
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2669
4520
|
function SparklesIcon() {
|
|
2670
|
-
return /* @__PURE__ */ (0,
|
|
4521
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2671
4522
|
"svg",
|
|
2672
4523
|
{
|
|
2673
4524
|
className: "assist-edge-tab__sparkles",
|
|
@@ -2675,21 +4526,21 @@ function SparklesIcon() {
|
|
|
2675
4526
|
fill: "none",
|
|
2676
4527
|
"aria-hidden": "true",
|
|
2677
4528
|
children: [
|
|
2678
|
-
/* @__PURE__ */ (0,
|
|
4529
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2679
4530
|
"path",
|
|
2680
4531
|
{
|
|
2681
4532
|
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",
|
|
2682
4533
|
fill: "currentColor"
|
|
2683
4534
|
}
|
|
2684
4535
|
),
|
|
2685
|
-
/* @__PURE__ */ (0,
|
|
4536
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2686
4537
|
"path",
|
|
2687
4538
|
{
|
|
2688
4539
|
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",
|
|
2689
4540
|
fill: "currentColor"
|
|
2690
4541
|
}
|
|
2691
4542
|
),
|
|
2692
|
-
/* @__PURE__ */ (0,
|
|
4543
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2693
4544
|
"path",
|
|
2694
4545
|
{
|
|
2695
4546
|
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",
|
|
@@ -2703,7 +4554,7 @@ function SparklesIcon() {
|
|
|
2703
4554
|
function TabMarkIcon({ customIconUrl }) {
|
|
2704
4555
|
const url = customIconUrl?.trim();
|
|
2705
4556
|
if (url) {
|
|
2706
|
-
return /* @__PURE__ */ (0,
|
|
4557
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2707
4558
|
"img",
|
|
2708
4559
|
{
|
|
2709
4560
|
alt: "",
|
|
@@ -2713,10 +4564,10 @@ function TabMarkIcon({ customIconUrl }) {
|
|
|
2713
4564
|
}
|
|
2714
4565
|
);
|
|
2715
4566
|
}
|
|
2716
|
-
return /* @__PURE__ */ (0,
|
|
4567
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SparklesIcon, {});
|
|
2717
4568
|
}
|
|
2718
4569
|
function ChevronLeftIcon() {
|
|
2719
|
-
return /* @__PURE__ */ (0,
|
|
4570
|
+
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)(
|
|
2720
4571
|
"path",
|
|
2721
4572
|
{
|
|
2722
4573
|
d: "M10 4L6 8l4 4",
|
|
@@ -2728,7 +4579,7 @@ function ChevronLeftIcon() {
|
|
|
2728
4579
|
) });
|
|
2729
4580
|
}
|
|
2730
4581
|
function ChevronDownIcon() {
|
|
2731
|
-
return /* @__PURE__ */ (0,
|
|
4582
|
+
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)(
|
|
2732
4583
|
"path",
|
|
2733
4584
|
{
|
|
2734
4585
|
d: "M4 6l4 4 4-4",
|
|
@@ -2740,7 +4591,7 @@ function ChevronDownIcon() {
|
|
|
2740
4591
|
) });
|
|
2741
4592
|
}
|
|
2742
4593
|
function DragDots() {
|
|
2743
|
-
return /* @__PURE__ */ (0,
|
|
4594
|
+
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)) });
|
|
2744
4595
|
}
|
|
2745
4596
|
var VARIANT_COPY = {
|
|
2746
4597
|
outline: { label: "Ask anything", aria: "Ask anything" },
|
|
@@ -2769,6 +4620,7 @@ function AssistEdgeTab({
|
|
|
2769
4620
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2770
4621
|
const copy = VARIANT_COPY[variant];
|
|
2771
4622
|
const visibleLabel = label?.trim() || copy.label;
|
|
4623
|
+
const alignment = along < 50 ? "start" : along > 50 ? "end" : "center";
|
|
2772
4624
|
const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
|
|
2773
4625
|
const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
|
|
2774
4626
|
const resolvedBorderColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.border : borderColor;
|
|
@@ -2785,11 +4637,11 @@ function AssistEdgeTab({
|
|
|
2785
4637
|
...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
|
|
2786
4638
|
colorScheme: resolvedColorScheme
|
|
2787
4639
|
};
|
|
2788
|
-
return /* @__PURE__ */ (0,
|
|
4640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2789
4641
|
"button",
|
|
2790
4642
|
{
|
|
2791
4643
|
type: "button",
|
|
2792
|
-
className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
|
|
4644
|
+
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" : ""}`,
|
|
2793
4645
|
"data-color-scheme": resolvedColorScheme,
|
|
2794
4646
|
style,
|
|
2795
4647
|
"aria-label": `Open ${visibleLabel}`,
|
|
@@ -2797,15 +4649,15 @@ function AssistEdgeTab({
|
|
|
2797
4649
|
tabIndex: visible ? 0 : -1,
|
|
2798
4650
|
onClick: onOpen,
|
|
2799
4651
|
children: [
|
|
2800
|
-
mobile ? /* @__PURE__ */ (0,
|
|
2801
|
-
/* @__PURE__ */ (0,
|
|
4652
|
+
mobile ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4653
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2802
4654
|
"span",
|
|
2803
4655
|
{
|
|
2804
4656
|
className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
|
|
2805
4657
|
"aria-hidden": "true",
|
|
2806
4658
|
children: [
|
|
2807
|
-
/* @__PURE__ */ (0,
|
|
2808
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4659
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4660
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2809
4661
|
"img",
|
|
2810
4662
|
{
|
|
2811
4663
|
className: "assist-edge-tab__logo",
|
|
@@ -2819,11 +4671,11 @@ function AssistEdgeTab({
|
|
|
2819
4671
|
]
|
|
2820
4672
|
}
|
|
2821
4673
|
),
|
|
2822
|
-
/* @__PURE__ */ (0,
|
|
2823
|
-
] }) : variant === "outline" ? /* @__PURE__ */ (0,
|
|
2824
|
-
/* @__PURE__ */ (0,
|
|
2825
|
-
/* @__PURE__ */ (0,
|
|
2826
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4674
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel })
|
|
4675
|
+
] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4676
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4677
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4678
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2827
4679
|
"img",
|
|
2828
4680
|
{
|
|
2829
4681
|
className: "assist-edge-tab__logo",
|
|
@@ -2835,18 +4687,18 @@ function AssistEdgeTab({
|
|
|
2835
4687
|
}
|
|
2836
4688
|
) : null
|
|
2837
4689
|
] }),
|
|
2838
|
-
/* @__PURE__ */ (0,
|
|
2839
|
-
/* @__PURE__ */ (0,
|
|
4690
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4691
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronDownIcon, {})
|
|
2840
4692
|
] }) : null,
|
|
2841
|
-
variant === "ask" ? /* @__PURE__ */ (0,
|
|
2842
|
-
/* @__PURE__ */ (0,
|
|
2843
|
-
/* @__PURE__ */ (0,
|
|
2844
|
-
/* @__PURE__ */ (0,
|
|
4693
|
+
variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4694
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronLeftIcon, {}),
|
|
4695
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4696
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DragDots, {})
|
|
2845
4697
|
] }) : null,
|
|
2846
|
-
variant === "fill" ? /* @__PURE__ */ (0,
|
|
2847
|
-
/* @__PURE__ */ (0,
|
|
2848
|
-
/* @__PURE__ */ (0,
|
|
2849
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4698
|
+
variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4699
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4700
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4701
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2850
4702
|
"img",
|
|
2851
4703
|
{
|
|
2852
4704
|
className: "assist-edge-tab__logo",
|
|
@@ -2858,8 +4710,8 @@ function AssistEdgeTab({
|
|
|
2858
4710
|
}
|
|
2859
4711
|
) : null
|
|
2860
4712
|
] }),
|
|
2861
|
-
/* @__PURE__ */ (0,
|
|
2862
|
-
/* @__PURE__ */ (0,
|
|
4713
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4714
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronLeftIcon, {})
|
|
2863
4715
|
] }) : null
|
|
2864
4716
|
]
|
|
2865
4717
|
}
|
|
@@ -2867,7 +4719,7 @@ function AssistEdgeTab({
|
|
|
2867
4719
|
}
|
|
2868
4720
|
|
|
2869
4721
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
2870
|
-
var
|
|
4722
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2871
4723
|
function AgentWidget({
|
|
2872
4724
|
indexId,
|
|
2873
4725
|
customerId,
|
|
@@ -2880,13 +4732,14 @@ function AgentWidget({
|
|
|
2880
4732
|
pageShift = true,
|
|
2881
4733
|
registerPanelController = false,
|
|
2882
4734
|
colorScheme = "auto",
|
|
2883
|
-
branding
|
|
4735
|
+
branding,
|
|
4736
|
+
toolResultRegistry
|
|
2884
4737
|
}) {
|
|
2885
4738
|
const isMobile = useIsMobile();
|
|
2886
4739
|
const placement = normalizeAgentPlacement(placementInput);
|
|
2887
|
-
const railSlotRef = (0,
|
|
2888
|
-
const [railCollapsed, setRailCollapsed] = (0,
|
|
2889
|
-
const [railExpanded, setRailExpanded] = (0,
|
|
4740
|
+
const railSlotRef = (0, import_react11.useRef)(null);
|
|
4741
|
+
const [railCollapsed, setRailCollapsed] = (0, import_react11.useState)(defaultCollapsed);
|
|
4742
|
+
const [railExpanded, setRailExpanded] = (0, import_react11.useState)(false);
|
|
2890
4743
|
const pageShiftActive = shouldApplyPageShift({
|
|
2891
4744
|
pageShift,
|
|
2892
4745
|
isMobile,
|
|
@@ -2897,14 +4750,15 @@ function AgentWidget({
|
|
|
2897
4750
|
active: pageShiftActive,
|
|
2898
4751
|
railSlotRef
|
|
2899
4752
|
});
|
|
2900
|
-
const { state, reset, retry, submit } = useAgentChat({
|
|
4753
|
+
const { state, reset, retry, respondToInput, respondToToolInput, submit } = useAgentChat({
|
|
2901
4754
|
customerId,
|
|
2902
4755
|
getUnpublishedPreviewGrant,
|
|
2903
4756
|
indexId,
|
|
2904
4757
|
previewBuildId,
|
|
2905
4758
|
version,
|
|
2906
4759
|
runtimeOrigin,
|
|
2907
|
-
greeting: branding?.greeting
|
|
4760
|
+
greeting: branding?.greeting,
|
|
4761
|
+
toolResultRegistry
|
|
2908
4762
|
});
|
|
2909
4763
|
const agentName = branding?.agentName ?? "";
|
|
2910
4764
|
const tabLabel = branding?.tabLabel ?? agentName;
|
|
@@ -2925,22 +4779,24 @@ function AgentWidget({
|
|
|
2925
4779
|
} : {},
|
|
2926
4780
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
2927
4781
|
};
|
|
2928
|
-
(0,
|
|
4782
|
+
(0, import_react11.useEffect)(() => {
|
|
2929
4783
|
if (!registerPanelController) return;
|
|
2930
4784
|
registerAgentPanelController(customerId, {
|
|
2931
4785
|
open: () => setRailCollapsed(false),
|
|
2932
4786
|
close: () => {
|
|
2933
4787
|
setRailCollapsed(true);
|
|
2934
4788
|
setRailExpanded(false);
|
|
2935
|
-
}
|
|
4789
|
+
},
|
|
4790
|
+
reset,
|
|
4791
|
+
submit
|
|
2936
4792
|
});
|
|
2937
4793
|
return () => unregisterAgentPanelController(customerId);
|
|
2938
|
-
}, [customerId, registerPanelController]);
|
|
4794
|
+
}, [customerId, registerPanelController, reset, submit]);
|
|
2939
4795
|
async function handleSubmit(message) {
|
|
2940
4796
|
if (isMobile) setRailCollapsed(false);
|
|
2941
4797
|
await submit(message);
|
|
2942
4798
|
}
|
|
2943
|
-
(0,
|
|
4799
|
+
(0, import_react11.useEffect)(() => {
|
|
2944
4800
|
if (railCollapsed) return;
|
|
2945
4801
|
const handleKeyDown = (event) => {
|
|
2946
4802
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -2974,19 +4830,19 @@ function AgentWidget({
|
|
|
2974
4830
|
window.addEventListener("keydown", handleKeyDown);
|
|
2975
4831
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2976
4832
|
}, [isMobile, railCollapsed, railExpanded]);
|
|
2977
|
-
return /* @__PURE__ */ (0,
|
|
2978
|
-
/* @__PURE__ */ (0,
|
|
4833
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "webless-agent-root", children: [
|
|
4834
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2979
4835
|
"div",
|
|
2980
4836
|
{
|
|
2981
4837
|
className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
|
|
2982
|
-
children: /* @__PURE__ */ (0,
|
|
4838
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2983
4839
|
"div",
|
|
2984
4840
|
{
|
|
2985
4841
|
ref: railSlotRef,
|
|
2986
4842
|
className: "webless-agent-root__rail-slot",
|
|
2987
4843
|
inert: railCollapsed || void 0,
|
|
2988
4844
|
"aria-hidden": railCollapsed,
|
|
2989
|
-
children: /* @__PURE__ */ (0,
|
|
4845
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2990
4846
|
AgentRail,
|
|
2991
4847
|
{
|
|
2992
4848
|
theme,
|
|
@@ -3004,6 +4860,8 @@ function AgentWidget({
|
|
|
3004
4860
|
onSubmit: handleSubmit,
|
|
3005
4861
|
onReset: reset,
|
|
3006
4862
|
onRetry: () => void retry(),
|
|
4863
|
+
onInputResponse: (response) => void respondToInput(response),
|
|
4864
|
+
onToolInput: (surface, values) => void respondToToolInput(surface, values),
|
|
3007
4865
|
onFollowUpSelect: (label) => void handleSubmit(label),
|
|
3008
4866
|
onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
|
|
3009
4867
|
}
|
|
@@ -3012,7 +4870,7 @@ function AgentWidget({
|
|
|
3012
4870
|
)
|
|
3013
4871
|
}
|
|
3014
4872
|
),
|
|
3015
|
-
railCollapsed ? /* @__PURE__ */ (0,
|
|
4873
|
+
railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3016
4874
|
AssistEdgeTab,
|
|
3017
4875
|
{
|
|
3018
4876
|
variant: placement.variant,
|
|
@@ -3042,12 +4900,14 @@ function AgentWidget({
|
|
|
3042
4900
|
AgentWidget,
|
|
3043
4901
|
AssistEdgeTab,
|
|
3044
4902
|
DEFAULT_AGENT_PLACEMENT,
|
|
4903
|
+
builtInVisitorToolResultRegistry,
|
|
3045
4904
|
createIdleSuggestions,
|
|
3046
4905
|
defaultAgentRailTheme,
|
|
3047
4906
|
defaultDarkAgentRailTheme,
|
|
3048
4907
|
hasVisitorMessages,
|
|
3049
4908
|
isAgentBusy,
|
|
3050
4909
|
normalizeAgentPlacement,
|
|
4910
|
+
presentVisitorToolResult,
|
|
3051
4911
|
useAgentChat
|
|
3052
4912
|
});
|
|
3053
4913
|
//# sourceMappingURL=react.cjs.map
|