@webless/agent 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HXKD46I6.js → chunk-U2TIUZJM.js} +2540 -666
- package/dist/chunk-U2TIUZJM.js.map +1 -0
- package/dist/embed.cjs +2276 -407
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +749 -112
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +9 -3
- package/dist/embed.d.ts +9 -3
- package/dist/embed.js +6 -2
- package/dist/embed.js.map +1 -1
- package/dist/index.cjs +431 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -1
- package/dist/index.d.ts +102 -1
- package/dist/index.js +428 -23
- package/dist/index.js.map +1 -1
- package/dist/manifest-Cad-TQJw.d.cts +391 -0
- package/dist/manifest-Cad-TQJw.d.ts +391 -0
- package/dist/react.cjs +2263 -406
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +749 -112
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +13 -7
- package/dist/react.d.ts +13 -7
- package/dist/react.js +5 -1
- package/package.json +1 -1
- package/dist/chunk-HXKD46I6.js.map +0 -1
- package/dist/manifest-H6oHn8Pc.d.cts +0 -185
- package/dist/manifest-H6oHn8Pc.d.ts +0 -185
package/dist/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
|
);
|
|
@@ -1887,10 +3162,11 @@ function AgentActivityBubble({
|
|
|
1887
3162
|
),
|
|
1888
3163
|
detailsOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: visibleSteps.map((step) => {
|
|
1889
3164
|
const detail = stepDetail(step, steps);
|
|
3165
|
+
const child = delegated && step.kind === "specialist";
|
|
1890
3166
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1891
3167
|
"li",
|
|
1892
3168
|
{
|
|
1893
|
-
className: "agent-activity-bubble__step"
|
|
3169
|
+
className: `agent-activity-bubble__step${child ? " agent-activity-bubble__step--child" : ""}`,
|
|
1894
3170
|
"data-kind": step.kind,
|
|
1895
3171
|
"data-state": step.state,
|
|
1896
3172
|
children: [
|
|
@@ -1898,25 +3174,27 @@ function AgentActivityBubble({
|
|
|
1898
3174
|
"span",
|
|
1899
3175
|
{
|
|
1900
3176
|
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()
|
|
3177
|
+
"aria-hidden": "true"
|
|
1915
3178
|
}
|
|
1916
3179
|
),
|
|
1917
3180
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
|
|
1918
3181
|
/* @__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
|
|
3182
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null,
|
|
3183
|
+
delegated && step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__delegation", children: [
|
|
3184
|
+
"Delegated ",
|
|
3185
|
+
delegationCount,
|
|
3186
|
+
" ",
|
|
3187
|
+
delegationCount === 1 ? "task" : "tasks"
|
|
3188
|
+
] }) : null,
|
|
3189
|
+
step.state === "error" && onRetryStep ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3190
|
+
"button",
|
|
3191
|
+
{
|
|
3192
|
+
type: "button",
|
|
3193
|
+
className: "agent-activity-bubble__step-retry",
|
|
3194
|
+
onClick: () => onRetryStep(step),
|
|
3195
|
+
children: "Retry"
|
|
3196
|
+
}
|
|
3197
|
+
) : null
|
|
1920
3198
|
] })
|
|
1921
3199
|
]
|
|
1922
3200
|
},
|
|
@@ -1931,59 +3209,190 @@ function AgentActivityBubble({
|
|
|
1931
3209
|
var import_react6 = require("react");
|
|
1932
3210
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1933
3211
|
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)(
|
|
3212
|
+
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)(
|
|
3213
|
+
"path",
|
|
3214
|
+
{
|
|
3215
|
+
d: "M8 12V4M8 4l-3 3M8 4l3 3",
|
|
3216
|
+
stroke: "currentColor",
|
|
3217
|
+
strokeWidth: "1.5",
|
|
3218
|
+
strokeLinecap: "round",
|
|
3219
|
+
strokeLinejoin: "round"
|
|
3220
|
+
}
|
|
3221
|
+
) });
|
|
3222
|
+
}
|
|
3223
|
+
function emptyValues(form) {
|
|
3224
|
+
const values = {};
|
|
3225
|
+
for (const field of form?.fields ?? []) values[field.id] = "";
|
|
3226
|
+
return values;
|
|
1935
3227
|
}
|
|
1936
3228
|
function Composer({
|
|
1937
3229
|
disabled = false,
|
|
1938
3230
|
placeholder = "Ask anything\u2026",
|
|
1939
3231
|
variant = "default",
|
|
3232
|
+
form = null,
|
|
1940
3233
|
onSubmit
|
|
1941
3234
|
}) {
|
|
1942
3235
|
const [value, setValue] = (0, import_react6.useState)("");
|
|
3236
|
+
const [values, setValues] = (0, import_react6.useState)(
|
|
3237
|
+
() => emptyValues(form)
|
|
3238
|
+
);
|
|
3239
|
+
const [blurred, setBlurred] = (0, import_react6.useState)({});
|
|
1943
3240
|
const inputRef = (0, import_react6.useRef)(null);
|
|
1944
|
-
|
|
3241
|
+
const firstFieldRef = (0, import_react6.useRef)(null);
|
|
3242
|
+
const formId = (0, import_react6.useId)();
|
|
3243
|
+
const activeForm = form;
|
|
3244
|
+
const canSendForm = activeForm ? isComposerFormComplete(activeForm, values) : Boolean(value.trim());
|
|
3245
|
+
(0, import_react6.useEffect)(() => {
|
|
3246
|
+
setValues(emptyValues(form));
|
|
3247
|
+
setBlurred({});
|
|
3248
|
+
}, [form?.id]);
|
|
3249
|
+
(0, import_react6.useEffect)(() => {
|
|
3250
|
+
if (activeForm) firstFieldRef.current?.focus();
|
|
3251
|
+
}, [activeForm?.id]);
|
|
3252
|
+
function submitChat() {
|
|
1945
3253
|
const trimmed = value.trim();
|
|
1946
3254
|
if (!trimmed || disabled) return;
|
|
1947
3255
|
onSubmit?.(trimmed);
|
|
1948
3256
|
setValue("");
|
|
1949
3257
|
inputRef.current?.focus();
|
|
1950
3258
|
}
|
|
3259
|
+
function submitForm() {
|
|
3260
|
+
if (!activeForm || disabled || !canSendForm) return;
|
|
3261
|
+
onSubmit?.(formatComposerFormMessage(activeForm, values));
|
|
3262
|
+
setValues(emptyValues(activeForm));
|
|
3263
|
+
setBlurred({});
|
|
3264
|
+
}
|
|
1951
3265
|
function handleSubmit(event) {
|
|
1952
3266
|
event.preventDefault();
|
|
1953
|
-
|
|
3267
|
+
if (activeForm) submitForm();
|
|
3268
|
+
else submitChat();
|
|
1954
3269
|
}
|
|
1955
|
-
function
|
|
3270
|
+
function handleChatKeyDown(event) {
|
|
1956
3271
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
1957
3272
|
event.preventDefault();
|
|
1958
|
-
|
|
3273
|
+
submitChat();
|
|
1959
3274
|
}
|
|
1960
3275
|
}
|
|
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
|
-
|
|
3276
|
+
function handleFormKeyDown(event) {
|
|
3277
|
+
const target = event.target;
|
|
3278
|
+
const isTextarea = Boolean(target && "tagName" in target && target.tagName === "TEXTAREA");
|
|
3279
|
+
if (event.key === "Enter" && !event.shiftKey && !isTextarea) {
|
|
3280
|
+
event.preventDefault();
|
|
3281
|
+
submitForm();
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3285
|
+
"form",
|
|
3286
|
+
{
|
|
3287
|
+
className: [
|
|
3288
|
+
"composer",
|
|
3289
|
+
variant === "dock" ? "composer--dock" : "",
|
|
3290
|
+
activeForm ? "composer--form" : ""
|
|
3291
|
+
].filter(Boolean).join(" "),
|
|
3292
|
+
onSubmit: handleSubmit,
|
|
3293
|
+
children: activeForm ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__sheet", role: "group", "aria-label": "Required details", children: [
|
|
3294
|
+
activeForm.fields.map((field, index) => {
|
|
3295
|
+
const fieldId = `${formId}-${field.id}`;
|
|
3296
|
+
const invalid = Boolean(blurred[field.id]) && !isValidComposerFieldValue(field, values[field.id] ?? "");
|
|
3297
|
+
const controlProps = {
|
|
3298
|
+
id: fieldId,
|
|
3299
|
+
name: field.id,
|
|
3300
|
+
disabled,
|
|
3301
|
+
required: field.required,
|
|
3302
|
+
autoComplete: field.autocomplete,
|
|
3303
|
+
placeholder: field.placeholder,
|
|
3304
|
+
spellCheck: false,
|
|
3305
|
+
value: values[field.id] ?? "",
|
|
3306
|
+
"aria-invalid": invalid || void 0,
|
|
3307
|
+
"aria-describedby": invalid ? `${fieldId}-error` : void 0,
|
|
3308
|
+
onBlur: () => setBlurred((current) => ({ ...current, [field.id]: true })),
|
|
3309
|
+
onChange: (event) => {
|
|
3310
|
+
const next = readComposerControlValue(event);
|
|
3311
|
+
setValues((current) => ({
|
|
3312
|
+
...current,
|
|
3313
|
+
[field.id]: next
|
|
3314
|
+
}));
|
|
3315
|
+
},
|
|
3316
|
+
onKeyDown: handleFormKeyDown
|
|
3317
|
+
};
|
|
3318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
3319
|
+
"div",
|
|
3320
|
+
{
|
|
3321
|
+
className: [
|
|
3322
|
+
"composer__row",
|
|
3323
|
+
field.kind === "textarea" ? "composer__row--grow" : ""
|
|
3324
|
+
].filter(Boolean).join(" "),
|
|
3325
|
+
children: [
|
|
3326
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "composer__label", htmlFor: fieldId, children: [
|
|
3327
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "composer__sr-only", children: field.label }),
|
|
3328
|
+
field.kind === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3329
|
+
"textarea",
|
|
3330
|
+
{
|
|
3331
|
+
...controlProps,
|
|
3332
|
+
ref: index === 0 ? (node) => {
|
|
3333
|
+
firstFieldRef.current = node;
|
|
3334
|
+
} : void 0,
|
|
3335
|
+
className: "composer__control composer__control--area",
|
|
3336
|
+
rows: 3
|
|
3337
|
+
}
|
|
3338
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3339
|
+
"input",
|
|
3340
|
+
{
|
|
3341
|
+
...controlProps,
|
|
3342
|
+
ref: index === 0 ? (node) => {
|
|
3343
|
+
firstFieldRef.current = node;
|
|
3344
|
+
} : void 0,
|
|
3345
|
+
className: "composer__control",
|
|
3346
|
+
type: field.kind,
|
|
3347
|
+
inputMode: field.kind === "tel" ? "tel" : void 0
|
|
3348
|
+
}
|
|
3349
|
+
)
|
|
3350
|
+
] }),
|
|
3351
|
+
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
|
|
3352
|
+
]
|
|
3353
|
+
},
|
|
3354
|
+
field.id
|
|
3355
|
+
);
|
|
3356
|
+
}),
|
|
3357
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "composer__toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3358
|
+
"button",
|
|
3359
|
+
{
|
|
3360
|
+
type: "submit",
|
|
3361
|
+
className: "composer__send",
|
|
3362
|
+
disabled: disabled || !canSendForm,
|
|
3363
|
+
"aria-label": "Send details",
|
|
3364
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
3365
|
+
}
|
|
3366
|
+
) })
|
|
3367
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "composer__field", children: [
|
|
3368
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3369
|
+
"textarea",
|
|
3370
|
+
{
|
|
3371
|
+
ref: inputRef,
|
|
3372
|
+
className: "composer__input",
|
|
3373
|
+
rows: 1,
|
|
3374
|
+
value,
|
|
3375
|
+
placeholder,
|
|
3376
|
+
disabled,
|
|
3377
|
+
spellCheck: false,
|
|
3378
|
+
"aria-label": "Message",
|
|
3379
|
+
onChange: (event) => setValue(event.target.value),
|
|
3380
|
+
onKeyDown: handleChatKeyDown
|
|
3381
|
+
}
|
|
3382
|
+
),
|
|
3383
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3384
|
+
"button",
|
|
3385
|
+
{
|
|
3386
|
+
type: "submit",
|
|
3387
|
+
className: "composer__send",
|
|
3388
|
+
disabled: disabled || !value.trim(),
|
|
3389
|
+
"aria-label": "Send message",
|
|
3390
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SendIcon, {})
|
|
3391
|
+
}
|
|
3392
|
+
)
|
|
3393
|
+
] })
|
|
3394
|
+
}
|
|
3395
|
+
);
|
|
1987
3396
|
}
|
|
1988
3397
|
|
|
1989
3398
|
// src/react/components/FollowUpChips/FollowUpChips.tsx
|
|
@@ -2031,9 +3440,15 @@ var import_react8 = require("react");
|
|
|
2031
3440
|
// src/react/components/BookingCard/BookingCard.tsx
|
|
2032
3441
|
var import_react7 = require("react");
|
|
2033
3442
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
3443
|
+
var BOOKING_STEPS = [
|
|
3444
|
+
{ id: "date", label: "Date" },
|
|
3445
|
+
{ id: "time", label: "Time" },
|
|
3446
|
+
{ id: "details", label: "Details" }
|
|
3447
|
+
];
|
|
2034
3448
|
function monthFromKey(key) {
|
|
2035
3449
|
const [year, month] = key.split("-").map(Number);
|
|
2036
|
-
if (!year || !month)
|
|
3450
|
+
if (!year || !month)
|
|
3451
|
+
return { year: (/* @__PURE__ */ new Date()).getFullYear(), month: (/* @__PURE__ */ new Date()).getMonth() };
|
|
2037
3452
|
return { year, month: month - 1 };
|
|
2038
3453
|
}
|
|
2039
3454
|
function dateKeyFromParts(year, month, day) {
|
|
@@ -2066,6 +3481,7 @@ function BookingCard({
|
|
|
2066
3481
|
const [startTime, setStartTime] = (0, import_react7.useState)("");
|
|
2067
3482
|
const [name, setName] = (0, import_react7.useState)("");
|
|
2068
3483
|
const [email, setEmail] = (0, import_react7.useState)("");
|
|
3484
|
+
const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
|
|
2069
3485
|
const slots = (0, import_react7.useMemo)(
|
|
2070
3486
|
() => bookingSlotsForEventType(offer.slots, eventTypeUri),
|
|
2071
3487
|
[eventTypeUri, offer.slots]
|
|
@@ -2086,14 +3502,18 @@ function BookingCard({
|
|
|
2086
3502
|
setSelectedDate("");
|
|
2087
3503
|
setStartTime("");
|
|
2088
3504
|
setVisibleMonth(
|
|
2089
|
-
firstAvailableBookingMonth(
|
|
3505
|
+
firstAvailableBookingMonth(
|
|
3506
|
+
bookingSlotsForEventType(offer.slots, nextType)
|
|
3507
|
+
)
|
|
2090
3508
|
);
|
|
2091
3509
|
}
|
|
2092
3510
|
const daySlots = (0, import_react7.useMemo)(
|
|
2093
3511
|
() => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
|
|
2094
3512
|
[selectedDate, slots]
|
|
2095
3513
|
);
|
|
2096
|
-
const selectedType = offer.eventTypes.find(
|
|
3514
|
+
const selectedType = offer.eventTypes.find(
|
|
3515
|
+
(item) => item.uri === eventTypeUri
|
|
3516
|
+
);
|
|
2097
3517
|
const selectedSample = availableByDate.get(selectedDate) ?? startTime;
|
|
2098
3518
|
const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
|
|
2099
3519
|
const weekdays = (0, import_react7.useMemo)(() => weekdayLabels(), []);
|
|
@@ -2139,24 +3559,47 @@ function BookingCard({
|
|
|
2139
3559
|
});
|
|
2140
3560
|
}
|
|
2141
3561
|
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: [
|
|
3562
|
+
/* @__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)(
|
|
3563
|
+
"li",
|
|
3564
|
+
{
|
|
3565
|
+
className: [
|
|
3566
|
+
"booking-card__step-indicator",
|
|
3567
|
+
index === stepIndex ? "booking-card__step-indicator--active" : "",
|
|
3568
|
+
index < stepIndex ? "booking-card__step-indicator--complete" : ""
|
|
3569
|
+
].filter(Boolean).join(" "),
|
|
3570
|
+
"aria-current": index === stepIndex ? "step" : void 0,
|
|
3571
|
+
children: [
|
|
3572
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", children: index + 1 }),
|
|
3573
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: item.label })
|
|
3574
|
+
]
|
|
3575
|
+
},
|
|
3576
|
+
item.id
|
|
3577
|
+
)) }),
|
|
2142
3578
|
step === "date" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2143
3579
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: selectedType?.name || "Pick a date" }),
|
|
2144
3580
|
timeZone ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "booking-card__tz", children: [
|
|
2145
3581
|
"Times in ",
|
|
2146
3582
|
timeZone
|
|
2147
3583
|
] }) : null,
|
|
2148
|
-
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
"
|
|
2152
|
-
{
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
3584
|
+
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3585
|
+
"label",
|
|
3586
|
+
{
|
|
3587
|
+
className: "booking-card__field",
|
|
3588
|
+
htmlFor: `${fieldId}-type`,
|
|
3589
|
+
children: [
|
|
3590
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Meeting" }),
|
|
3591
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3592
|
+
"select",
|
|
3593
|
+
{
|
|
3594
|
+
id: `${fieldId}-type`,
|
|
3595
|
+
value: eventTypeUri,
|
|
3596
|
+
onChange: (event) => selectEventType(event.target.value),
|
|
3597
|
+
children: offer.eventTypes.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: item.uri, children: item.name }, item.uri))
|
|
3598
|
+
}
|
|
3599
|
+
)
|
|
3600
|
+
]
|
|
3601
|
+
}
|
|
3602
|
+
) : null,
|
|
2160
3603
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__month", children: [
|
|
2161
3604
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2162
3605
|
"button",
|
|
@@ -2183,29 +3626,44 @@ function BookingCard({
|
|
|
2183
3626
|
)
|
|
2184
3627
|
] }),
|
|
2185
3628
|
/* @__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
|
-
|
|
3629
|
+
offer.slots.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__loading", role: "status", children: "Finding available times\u2026" }) : null,
|
|
3630
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3631
|
+
"div",
|
|
3632
|
+
{
|
|
3633
|
+
className: "booking-card__calendar",
|
|
3634
|
+
role: "grid",
|
|
3635
|
+
"aria-label": "Available dates",
|
|
3636
|
+
children: cells.map((cell, index) => {
|
|
3637
|
+
if (!cell) {
|
|
3638
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3639
|
+
"span",
|
|
3640
|
+
{
|
|
3641
|
+
className: "booking-card__day"
|
|
3642
|
+
},
|
|
3643
|
+
`empty-${index}`
|
|
3644
|
+
);
|
|
3645
|
+
}
|
|
3646
|
+
const available = availableByDate.has(cell.key);
|
|
3647
|
+
const selected = cell.key === selectedDate;
|
|
3648
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3649
|
+
"button",
|
|
3650
|
+
{
|
|
3651
|
+
type: "button",
|
|
3652
|
+
className: [
|
|
3653
|
+
"booking-card__day",
|
|
3654
|
+
available ? "booking-card__day--available" : "",
|
|
3655
|
+
selected ? "booking-card__day--selected" : ""
|
|
3656
|
+
].filter(Boolean).join(" "),
|
|
3657
|
+
disabled: !available,
|
|
3658
|
+
"aria-pressed": selected,
|
|
3659
|
+
onClick: () => selectDate(cell.key),
|
|
3660
|
+
children: cell.day
|
|
3661
|
+
},
|
|
3662
|
+
cell.key
|
|
3663
|
+
);
|
|
3664
|
+
})
|
|
2189
3665
|
}
|
|
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
|
-
}) })
|
|
3666
|
+
)
|
|
2209
3667
|
] }, "date") : null,
|
|
2210
3668
|
step === "time" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2211
3669
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step-bar", children: [
|
|
@@ -2257,35 +3715,57 @@ function BookingCard({
|
|
|
2257
3715
|
] })
|
|
2258
3716
|
] }),
|
|
2259
3717
|
/* @__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
|
-
|
|
3718
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3719
|
+
"label",
|
|
3720
|
+
{
|
|
3721
|
+
className: "booking-card__field",
|
|
3722
|
+
htmlFor: `${fieldId}-name`,
|
|
3723
|
+
children: [
|
|
3724
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Name" }),
|
|
3725
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3726
|
+
"input",
|
|
3727
|
+
{
|
|
3728
|
+
id: `${fieldId}-name`,
|
|
3729
|
+
autoComplete: "name",
|
|
3730
|
+
value: name,
|
|
3731
|
+
onChange: (event) => setName(event.target.value),
|
|
3732
|
+
required: true
|
|
3733
|
+
}
|
|
3734
|
+
)
|
|
3735
|
+
]
|
|
3736
|
+
}
|
|
3737
|
+
),
|
|
3738
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3739
|
+
"label",
|
|
3740
|
+
{
|
|
3741
|
+
className: "booking-card__field",
|
|
3742
|
+
htmlFor: `${fieldId}-email`,
|
|
3743
|
+
children: [
|
|
3744
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Email" }),
|
|
3745
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3746
|
+
"input",
|
|
3747
|
+
{
|
|
3748
|
+
id: `${fieldId}-email`,
|
|
3749
|
+
type: "email",
|
|
3750
|
+
autoComplete: "email",
|
|
3751
|
+
value: email,
|
|
3752
|
+
onChange: (event) => setEmail(event.target.value),
|
|
3753
|
+
required: true
|
|
3754
|
+
}
|
|
3755
|
+
)
|
|
3756
|
+
]
|
|
3757
|
+
}
|
|
3758
|
+
)
|
|
2287
3759
|
] }),
|
|
2288
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3760
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3761
|
+
"button",
|
|
3762
|
+
{
|
|
3763
|
+
type: "submit",
|
|
3764
|
+
className: "booking-card__submit",
|
|
3765
|
+
disabled: !name.trim() || !email.trim(),
|
|
3766
|
+
children: "Book this time"
|
|
3767
|
+
}
|
|
3768
|
+
)
|
|
2289
3769
|
] }, "details") : null
|
|
2290
3770
|
] }) });
|
|
2291
3771
|
}
|
|
@@ -2294,6 +3774,43 @@ function BookingCard({
|
|
|
2294
3774
|
var import_streamdown = require("streamdown");
|
|
2295
3775
|
var import_styles = require("streamdown/styles.css");
|
|
2296
3776
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3777
|
+
function normalizeDedupeText(text) {
|
|
3778
|
+
return text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
3779
|
+
}
|
|
3780
|
+
function paragraphsAreNearDuplicates(first, second) {
|
|
3781
|
+
const left = normalizeDedupeText(first);
|
|
3782
|
+
const right = normalizeDedupeText(second);
|
|
3783
|
+
if (left.length < 40 || right.length < 40) return false;
|
|
3784
|
+
if (left === right) return true;
|
|
3785
|
+
const shorter = left.length <= right.length ? left : right;
|
|
3786
|
+
const longer = left.length <= right.length ? right : left;
|
|
3787
|
+
return longer.startsWith(
|
|
3788
|
+
shorter.slice(0, Math.floor(shorter.length * 0.85))
|
|
3789
|
+
);
|
|
3790
|
+
}
|
|
3791
|
+
function paragraphsShareOpening(first, second) {
|
|
3792
|
+
const opening = first.split("\n")[0]?.trim();
|
|
3793
|
+
if (!opening || opening.length < 20) return false;
|
|
3794
|
+
return second.trim().startsWith(opening);
|
|
3795
|
+
}
|
|
3796
|
+
function collapseRepeatedText(text) {
|
|
3797
|
+
const trimmed = text.trim();
|
|
3798
|
+
if (trimmed.length < 40) return trimmed;
|
|
3799
|
+
const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
|
|
3800
|
+
if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
|
|
3801
|
+
return paragraphs[0];
|
|
3802
|
+
}
|
|
3803
|
+
if (paragraphs.length >= 2 && paragraphs.length % 2 === 0) {
|
|
3804
|
+
const mid2 = paragraphs.length / 2;
|
|
3805
|
+
const first = paragraphs.slice(0, mid2).join("\n\n");
|
|
3806
|
+
const second = paragraphs.slice(mid2).join("\n\n");
|
|
3807
|
+
if (first === second) return first;
|
|
3808
|
+
}
|
|
3809
|
+
const mid = Math.floor(trimmed.length / 2);
|
|
3810
|
+
const left = trimmed.slice(0, mid).trim();
|
|
3811
|
+
const right = trimmed.slice(mid).trim();
|
|
3812
|
+
return left.length >= 20 && left === right ? left : trimmed;
|
|
3813
|
+
}
|
|
2297
3814
|
function MessageBubble({
|
|
2298
3815
|
message,
|
|
2299
3816
|
brandLogoUrl,
|
|
@@ -2310,24 +3827,41 @@ function MessageBubble({
|
|
|
2310
3827
|
const offers = offer ? [offer] : extractedOffers;
|
|
2311
3828
|
const visibleText = hideToolCardFences(message.text);
|
|
2312
3829
|
const isStreaming = message.role === "agent" && Boolean(message.streaming);
|
|
2313
|
-
const displayText =
|
|
3830
|
+
const displayText = collapseRepeatedText(
|
|
3831
|
+
offers.length > 0 ? sanitizeBookingOfferCopy(visibleText) : looksLikeBookingAvailabilityDump(visibleText) ? sanitizeBookingOfferCopy(visibleText) : visibleText || (isStreaming ? "" : message.text)
|
|
3832
|
+
);
|
|
2314
3833
|
if (message.role === "visitor") {
|
|
2315
3834
|
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
3835
|
}
|
|
2317
|
-
const
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
3836
|
+
const citations = message.citations ?? [];
|
|
3837
|
+
const agentText = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__text", children: [
|
|
3838
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3839
|
+
import_streamdown.Streamdown,
|
|
3840
|
+
{
|
|
3841
|
+
animated: isStreaming,
|
|
3842
|
+
caret: "circle",
|
|
3843
|
+
className: "message-bubble__markdown",
|
|
3844
|
+
controls: false,
|
|
3845
|
+
isAnimating: isStreaming,
|
|
3846
|
+
linkSafety: { enabled: false },
|
|
3847
|
+
mode: isStreaming ? "streaming" : "static",
|
|
3848
|
+
skipHtml: true,
|
|
3849
|
+
children: displayText
|
|
3850
|
+
}
|
|
3851
|
+
),
|
|
3852
|
+
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)(
|
|
3853
|
+
"a",
|
|
3854
|
+
{
|
|
3855
|
+
href: citation.url,
|
|
3856
|
+
target: "_blank",
|
|
3857
|
+
rel: "noreferrer",
|
|
3858
|
+
children: [
|
|
3859
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__source-icon", "aria-hidden": "true", children: "\u25A6" }),
|
|
3860
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: citation.label })
|
|
3861
|
+
]
|
|
3862
|
+
}
|
|
3863
|
+
) }, citation.id)) }) : null
|
|
3864
|
+
] });
|
|
2331
3865
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
|
|
2332
3866
|
displayText ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__agent-row", children: [
|
|
2333
3867
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
@@ -2353,10 +3887,262 @@ function MessageBubble({
|
|
|
2353
3887
|
] });
|
|
2354
3888
|
}
|
|
2355
3889
|
|
|
2356
|
-
// src/react/components/
|
|
3890
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3891
|
+
var import_react9 = require("react");
|
|
3892
|
+
|
|
3893
|
+
// src/react/components/ConfirmationCard/ConfirmationCard.tsx
|
|
2357
3894
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3895
|
+
function ConfirmationCard({
|
|
3896
|
+
disabled = false,
|
|
3897
|
+
request,
|
|
3898
|
+
onRespond
|
|
3899
|
+
}) {
|
|
3900
|
+
const options = request.options ?? [];
|
|
3901
|
+
const heading = request.kind === "tool-approval" ? "Confirm this action" : request.prompt;
|
|
3902
|
+
const prompt = request.kind === "tool-approval" ? request.prompt : void 0;
|
|
3903
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3904
|
+
"section",
|
|
3905
|
+
{
|
|
3906
|
+
className: "confirmation-card",
|
|
3907
|
+
"aria-labelledby": `confirmation-${request.requestId}`,
|
|
3908
|
+
children: [
|
|
3909
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "confirmation-card__heading", children: [
|
|
3910
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { id: `confirmation-${request.requestId}`, children: heading }),
|
|
3911
|
+
prompt ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: prompt }) : null
|
|
3912
|
+
] }),
|
|
3913
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "confirmation-card__actions", children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3914
|
+
"button",
|
|
3915
|
+
{
|
|
3916
|
+
type: "button",
|
|
3917
|
+
className: `confirmation-card__action confirmation-card__action--${option.style ?? "default"}`,
|
|
3918
|
+
disabled,
|
|
3919
|
+
onClick: () => onRespond?.({
|
|
3920
|
+
requestId: request.requestId,
|
|
3921
|
+
optionId: option.id
|
|
3922
|
+
}),
|
|
3923
|
+
children: option.label
|
|
3924
|
+
},
|
|
3925
|
+
option.id
|
|
3926
|
+
)) })
|
|
3927
|
+
]
|
|
3928
|
+
}
|
|
3929
|
+
);
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3932
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3933
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3934
|
+
function HumanInputCard({
|
|
3935
|
+
disabled = false,
|
|
3936
|
+
request,
|
|
3937
|
+
onRespond
|
|
3938
|
+
}) {
|
|
3939
|
+
const [text, setText] = (0, import_react9.useState)("");
|
|
3940
|
+
const options = request.options ?? [];
|
|
3941
|
+
const showText = request.display === "text" || request.allowFreeform && options.length === 0;
|
|
3942
|
+
if (options.length > 0) {
|
|
3943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3944
|
+
ConfirmationCard,
|
|
3945
|
+
{
|
|
3946
|
+
disabled,
|
|
3947
|
+
request,
|
|
3948
|
+
onRespond
|
|
3949
|
+
}
|
|
3950
|
+
);
|
|
3951
|
+
}
|
|
3952
|
+
function submitText(event) {
|
|
3953
|
+
event.preventDefault();
|
|
3954
|
+
const value = text.trim();
|
|
3955
|
+
if (!value || disabled) return;
|
|
3956
|
+
onRespond?.({ requestId: request.requestId, text: value });
|
|
3957
|
+
}
|
|
3958
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3959
|
+
"section",
|
|
3960
|
+
{
|
|
3961
|
+
className: "human-input-card",
|
|
3962
|
+
"aria-labelledby": `human-input-${request.requestId}`,
|
|
3963
|
+
children: [
|
|
3964
|
+
/* @__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 }) }),
|
|
3965
|
+
showText ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("form", { onSubmit: submitText, children: [
|
|
3966
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: `human-input-text-${request.requestId}`, children: "Response" }),
|
|
3967
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
3968
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3969
|
+
"input",
|
|
3970
|
+
{
|
|
3971
|
+
id: `human-input-text-${request.requestId}`,
|
|
3972
|
+
value: text,
|
|
3973
|
+
disabled,
|
|
3974
|
+
onChange: (event) => setText(event.target.value)
|
|
3975
|
+
}
|
|
3976
|
+
),
|
|
3977
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "submit", disabled: disabled || !text.trim(), children: "Send" })
|
|
3978
|
+
] })
|
|
3979
|
+
] }) : null,
|
|
3980
|
+
!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
|
|
3981
|
+
]
|
|
3982
|
+
}
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
// src/react/components/CollectionResultCard/CollectionResultCard.tsx
|
|
3987
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
3988
|
+
function CollectionResultCard({
|
|
3989
|
+
result
|
|
3990
|
+
}) {
|
|
3991
|
+
const empty = result.items.length === 0;
|
|
3992
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
3993
|
+
"section",
|
|
3994
|
+
{
|
|
3995
|
+
className: `collection-result-card tool-result-card tool-result-card--${result.status}`,
|
|
3996
|
+
"aria-label": result.title,
|
|
3997
|
+
children: [
|
|
3998
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
3999
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4000
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("strong", { children: result.title })
|
|
4001
|
+
] }),
|
|
4002
|
+
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: [
|
|
4003
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "collection-result-card__item-title", children: item.title }),
|
|
4004
|
+
item.description ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: item.description }) : null,
|
|
4005
|
+
item.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dl", { children: item.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
4006
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dt", { children: detail.label }),
|
|
4007
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dd", { children: detail.value })
|
|
4008
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4009
|
+
item.href ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
|
|
4010
|
+
] }, item.title)) })
|
|
4011
|
+
]
|
|
4012
|
+
}
|
|
4013
|
+
);
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
// src/react/components/EntityResultCard/EntityResultCard.tsx
|
|
4017
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
4018
|
+
function EntityResultCard({
|
|
4019
|
+
result
|
|
4020
|
+
}) {
|
|
4021
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
4022
|
+
"section",
|
|
4023
|
+
{
|
|
4024
|
+
className: `entity-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4025
|
+
"aria-label": result.title,
|
|
4026
|
+
children: [
|
|
4027
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4028
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4029
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: result.title })
|
|
4030
|
+
] }),
|
|
4031
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { children: result.description }) : null,
|
|
4032
|
+
result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
4033
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dt", { children: detail.label }),
|
|
4034
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("dd", { children: detail.value })
|
|
4035
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4036
|
+
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)(
|
|
4037
|
+
"a",
|
|
4038
|
+
{
|
|
4039
|
+
href: link.href,
|
|
4040
|
+
target: "_blank",
|
|
4041
|
+
rel: "noreferrer",
|
|
4042
|
+
children: link.label
|
|
4043
|
+
},
|
|
4044
|
+
link.href
|
|
4045
|
+
)) }) : null
|
|
4046
|
+
]
|
|
4047
|
+
}
|
|
4048
|
+
);
|
|
4049
|
+
}
|
|
4050
|
+
|
|
4051
|
+
// src/react/components/SignatureResultCard/SignatureResultCard.tsx
|
|
4052
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4053
|
+
function SignatureResultCard({
|
|
4054
|
+
result
|
|
4055
|
+
}) {
|
|
4056
|
+
const primaryLink = result.links?.[0];
|
|
4057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
4058
|
+
"section",
|
|
4059
|
+
{
|
|
4060
|
+
className: `signature-result-card tool-result-card tool-result-card--${result.status}`,
|
|
4061
|
+
"aria-label": result.title,
|
|
4062
|
+
children: [
|
|
4063
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4064
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4065
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: result.title })
|
|
4066
|
+
] }),
|
|
4067
|
+
result.statusLabel ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
|
|
4068
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: result.description }) : null,
|
|
4069
|
+
primaryLink ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4070
|
+
"a",
|
|
4071
|
+
{
|
|
4072
|
+
className: "signature-result-card__cta",
|
|
4073
|
+
href: primaryLink.href,
|
|
4074
|
+
target: "_blank",
|
|
4075
|
+
rel: "noreferrer",
|
|
4076
|
+
children: primaryLink.label
|
|
4077
|
+
}
|
|
4078
|
+
) : null
|
|
4079
|
+
]
|
|
4080
|
+
}
|
|
4081
|
+
);
|
|
4082
|
+
}
|
|
4083
|
+
|
|
4084
|
+
// src/react/components/ToolResultCard/ToolResultCard.tsx
|
|
4085
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
4086
|
+
function ToolResultCard({
|
|
4087
|
+
result
|
|
4088
|
+
}) {
|
|
4089
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
4090
|
+
"section",
|
|
4091
|
+
{
|
|
4092
|
+
className: `tool-result-card tool-result-card--${result.status}`,
|
|
4093
|
+
"aria-label": result.title,
|
|
4094
|
+
children: [
|
|
4095
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "tool-result-card__heading", children: [
|
|
4096
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
4097
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: result.title })
|
|
4098
|
+
] }),
|
|
4099
|
+
result.description ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: result.description }) : null,
|
|
4100
|
+
result.details?.length ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dl", { children: result.details.map((detail) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
4101
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dt", { children: detail.label }),
|
|
4102
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("dd", { children: detail.value })
|
|
4103
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
4104
|
+
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)(
|
|
4105
|
+
"a",
|
|
4106
|
+
{
|
|
4107
|
+
href: link.href,
|
|
4108
|
+
target: "_blank",
|
|
4109
|
+
rel: "noreferrer",
|
|
4110
|
+
children: link.label
|
|
4111
|
+
},
|
|
4112
|
+
link.href
|
|
4113
|
+
)) }) : null
|
|
4114
|
+
]
|
|
4115
|
+
}
|
|
4116
|
+
);
|
|
4117
|
+
}
|
|
4118
|
+
|
|
4119
|
+
// src/react/components/VisitorToolResultView/VisitorToolResultView.tsx
|
|
4120
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
4121
|
+
function VisitorToolResultView({
|
|
4122
|
+
result
|
|
4123
|
+
}) {
|
|
4124
|
+
if (result.kind === "entity") {
|
|
4125
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EntityResultCard, { result });
|
|
4126
|
+
}
|
|
4127
|
+
if (result.kind === "collection") {
|
|
4128
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CollectionResultCard, { result });
|
|
4129
|
+
}
|
|
4130
|
+
if (result.kind === "signature") {
|
|
4131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SignatureResultCard, { result });
|
|
4132
|
+
}
|
|
4133
|
+
if (result.kind === "summary") {
|
|
4134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ToolResultCard, { result });
|
|
4135
|
+
}
|
|
4136
|
+
return null;
|
|
4137
|
+
}
|
|
4138
|
+
function isRenderableVisitorToolResult(result) {
|
|
4139
|
+
return result.kind === "summary" || result.kind === "entity" || result.kind === "collection" || result.kind === "signature";
|
|
4140
|
+
}
|
|
4141
|
+
|
|
4142
|
+
// src/react/components/AgentRail/AgentRail.tsx
|
|
4143
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2358
4144
|
function MinimizeIcon() {
|
|
2359
|
-
return /* @__PURE__ */ (0,
|
|
4145
|
+
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
4146
|
"path",
|
|
2361
4147
|
{
|
|
2362
4148
|
d: "M3.5 8h9",
|
|
@@ -2367,7 +4153,7 @@ function MinimizeIcon() {
|
|
|
2367
4153
|
) });
|
|
2368
4154
|
}
|
|
2369
4155
|
function CloseIcon() {
|
|
2370
|
-
return /* @__PURE__ */ (0,
|
|
4156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2371
4157
|
"path",
|
|
2372
4158
|
{
|
|
2373
4159
|
d: "M4 4l8 8M12 4l-8 8",
|
|
@@ -2378,7 +4164,7 @@ function CloseIcon() {
|
|
|
2378
4164
|
) });
|
|
2379
4165
|
}
|
|
2380
4166
|
function NewChatIcon() {
|
|
2381
|
-
return /* @__PURE__ */ (0,
|
|
4167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2382
4168
|
"path",
|
|
2383
4169
|
{
|
|
2384
4170
|
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 +4176,7 @@ function NewChatIcon() {
|
|
|
2390
4176
|
) });
|
|
2391
4177
|
}
|
|
2392
4178
|
function ExpandIcon() {
|
|
2393
|
-
return /* @__PURE__ */ (0,
|
|
4179
|
+
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
4180
|
"path",
|
|
2395
4181
|
{
|
|
2396
4182
|
d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
|
|
@@ -2402,7 +4188,7 @@ function ExpandIcon() {
|
|
|
2402
4188
|
) });
|
|
2403
4189
|
}
|
|
2404
4190
|
function RestoreIcon() {
|
|
2405
|
-
return /* @__PURE__ */ (0,
|
|
4191
|
+
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
4192
|
"path",
|
|
2407
4193
|
{
|
|
2408
4194
|
d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
|
|
@@ -2430,28 +4216,29 @@ function AgentRail({
|
|
|
2430
4216
|
onRetry,
|
|
2431
4217
|
onSubmit,
|
|
2432
4218
|
onFollowUpSelect,
|
|
2433
|
-
onBook
|
|
4219
|
+
onBook,
|
|
4220
|
+
onInputResponse
|
|
2434
4221
|
}) {
|
|
2435
|
-
const transcriptRef = (0,
|
|
4222
|
+
const transcriptRef = (0, import_react10.useRef)(null);
|
|
2436
4223
|
const resolvedBrandLabel = brandLabel.trim();
|
|
2437
4224
|
const resolvedBrandLogoUrl = brandLogoUrl?.trim();
|
|
2438
|
-
const [failedLogoUrl, setFailedLogoUrl] = (0,
|
|
4225
|
+
const [failedLogoUrl, setFailedLogoUrl] = (0, import_react10.useState)(null);
|
|
2439
4226
|
const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
|
|
2440
4227
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2441
4228
|
const brandedTheme = { ...defaultAgentRailTheme, ...theme };
|
|
2442
4229
|
const resolvedTheme = resolvedColorScheme === "dark" ? {
|
|
2443
4230
|
...brandedTheme,
|
|
2444
4231
|
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,
|
|
4232
|
+
brandDeep: theme?.brandDeep ?? defaultDarkAgentRailTheme.brandDeep,
|
|
4233
|
+
brandSoft: theme?.brandSoft ?? `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
|
|
4234
|
+
border: theme?.border ?? defaultDarkAgentRailTheme.border,
|
|
4235
|
+
danger: theme?.danger ?? defaultDarkAgentRailTheme.danger,
|
|
4236
|
+
success: theme?.success ?? defaultDarkAgentRailTheme.success,
|
|
4237
|
+
surface: theme?.surface ?? defaultDarkAgentRailTheme.surface,
|
|
4238
|
+
surfaceMuted: theme?.surfaceMuted ?? defaultDarkAgentRailTheme.surfaceMuted,
|
|
4239
|
+
text: theme?.text ?? defaultDarkAgentRailTheme.text,
|
|
4240
|
+
textMuted: theme?.textMuted ?? defaultDarkAgentRailTheme.textMuted,
|
|
4241
|
+
textSubtle: theme?.textSubtle ?? defaultDarkAgentRailTheme.textSubtle,
|
|
2455
4242
|
visitorBubble: theme?.visitorBubble ?? theme?.brand ?? defaultDarkAgentRailTheme.visitorBubble
|
|
2456
4243
|
} : brandedTheme;
|
|
2457
4244
|
const railStyle = {
|
|
@@ -2474,8 +4261,15 @@ function AgentRail({
|
|
|
2474
4261
|
"--as-font-display": resolvedTheme.fontDisplay,
|
|
2475
4262
|
colorScheme: resolvedColorScheme
|
|
2476
4263
|
};
|
|
2477
|
-
const
|
|
4264
|
+
const pendingInputRequests = (state.pendingInputs ?? []).filter(
|
|
4265
|
+
(request) => shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
4266
|
+
);
|
|
4267
|
+
const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && pendingInputRequests.length > 0;
|
|
4268
|
+
const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
|
|
2478
4269
|
const showActivity = state.toolSteps.length > 0;
|
|
4270
|
+
const visitorToolResults = (state.toolResults ?? []).filter(
|
|
4271
|
+
isRenderableVisitorToolResult
|
|
4272
|
+
);
|
|
2479
4273
|
const hasVisitorMessages2 = state.messages.some(
|
|
2480
4274
|
(message) => message.role === "visitor"
|
|
2481
4275
|
);
|
|
@@ -2485,22 +4279,44 @@ function AgentRail({
|
|
|
2485
4279
|
);
|
|
2486
4280
|
const visibleMessages = hasVisitorMessages2 ? state.messages : [];
|
|
2487
4281
|
const lastMessage = visibleMessages.at(-1);
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
4282
|
+
let lastAgentIndex = -1;
|
|
4283
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4284
|
+
if (visibleMessages[index]?.role === "agent") {
|
|
4285
|
+
lastAgentIndex = index;
|
|
4286
|
+
break;
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
let lastVisitorIndex = -1;
|
|
4290
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4291
|
+
if (visibleMessages[index]?.role === "visitor") {
|
|
4292
|
+
lastVisitorIndex = index;
|
|
4293
|
+
break;
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
const lastIsAgent = lastMessage?.role === "agent";
|
|
4297
|
+
const streamingMessage = state.phase === "streaming" && state.streamingText && !lastIsAgent ? {
|
|
2491
4298
|
createdAt: 0,
|
|
2492
4299
|
id: "streaming-response",
|
|
2493
4300
|
role: "agent",
|
|
2494
4301
|
streaming: true,
|
|
2495
4302
|
text: state.streamingText
|
|
2496
|
-
} : state.pendingOffer ? {
|
|
4303
|
+
} : state.pendingOffer && !lastIsAgent ? {
|
|
2497
4304
|
createdAt: 0,
|
|
2498
4305
|
id: "pending-booking",
|
|
2499
4306
|
role: "agent",
|
|
2500
4307
|
streaming: false,
|
|
2501
4308
|
text: "Pick a date and time that works for you."
|
|
2502
4309
|
} : null;
|
|
2503
|
-
|
|
4310
|
+
const bookingReadyText = state.streamingText || (lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "");
|
|
4311
|
+
const waitingForBooking = !state.pendingOffer && looksLikeBookingReady(bookingReadyText) && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
|
|
4312
|
+
const lastAgentText = lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "";
|
|
4313
|
+
const composerForm = resolveComposerForm({
|
|
4314
|
+
agentText: lastAgentText,
|
|
4315
|
+
cards: extractToolCards(lastAgentText),
|
|
4316
|
+
hasBookingOffer: Boolean(state.pendingOffer) || waitingForBooking,
|
|
4317
|
+
enabled: lastIsAgent && !isBusy
|
|
4318
|
+
});
|
|
4319
|
+
(0, import_react10.useEffect)(() => {
|
|
2504
4320
|
const node = transcriptRef.current;
|
|
2505
4321
|
if (!node) return;
|
|
2506
4322
|
node.scrollTop = node.scrollHeight;
|
|
@@ -2511,11 +4327,13 @@ function AgentRail({
|
|
|
2511
4327
|
state.followUps,
|
|
2512
4328
|
state.journey
|
|
2513
4329
|
]);
|
|
2514
|
-
return /* @__PURE__ */ (0,
|
|
4330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2515
4331
|
"aside",
|
|
2516
4332
|
{
|
|
2517
|
-
className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4333
|
+
className: `agent-rail not-typeset${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4334
|
+
"data-not-typeset": "",
|
|
2518
4335
|
"data-color-scheme": resolvedColorScheme,
|
|
4336
|
+
spellCheck: false,
|
|
2519
4337
|
style: railStyle,
|
|
2520
4338
|
"aria-label": "Agent conversation",
|
|
2521
4339
|
"aria-modal": mobileFullscreen || expanded ? true : void 0,
|
|
@@ -2523,28 +4341,28 @@ function AgentRail({
|
|
|
2523
4341
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
2524
4342
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
2525
4343
|
children: [
|
|
2526
|
-
/* @__PURE__ */ (0,
|
|
2527
|
-
onCollapse ? /* @__PURE__ */ (0,
|
|
4344
|
+
/* @__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: [
|
|
4345
|
+
onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2528
4346
|
"button",
|
|
2529
4347
|
{
|
|
2530
4348
|
type: "button",
|
|
2531
4349
|
className: "agent-rail__collapse",
|
|
2532
4350
|
"aria-label": "Collapse assist",
|
|
2533
4351
|
onClick: onCollapse,
|
|
2534
|
-
children: /* @__PURE__ */ (0,
|
|
4352
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MinimizeIcon, {})
|
|
2535
4353
|
}
|
|
2536
|
-
) : onClose ? /* @__PURE__ */ (0,
|
|
4354
|
+
) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2537
4355
|
"button",
|
|
2538
4356
|
{
|
|
2539
4357
|
type: "button",
|
|
2540
4358
|
className: "agent-rail__close",
|
|
2541
4359
|
"aria-label": "Close agent",
|
|
2542
4360
|
onClick: onClose,
|
|
2543
|
-
children: /* @__PURE__ */ (0,
|
|
4361
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CloseIcon, {})
|
|
2544
4362
|
}
|
|
2545
|
-
) : /* @__PURE__ */ (0,
|
|
2546
|
-
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0,
|
|
2547
|
-
showBrandLogo ? /* @__PURE__ */ (0,
|
|
4363
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
|
|
4364
|
+
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "agent-rail__identity", children: [
|
|
4365
|
+
showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2548
4366
|
"img",
|
|
2549
4367
|
{
|
|
2550
4368
|
className: "agent-rail__brand-logo",
|
|
@@ -2555,10 +4373,10 @@ function AgentRail({
|
|
|
2555
4373
|
}
|
|
2556
4374
|
}
|
|
2557
4375
|
) }) : null,
|
|
2558
|
-
resolvedBrandLabel ? /* @__PURE__ */ (0,
|
|
4376
|
+
resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
|
|
2559
4377
|
] }) : null,
|
|
2560
|
-
/* @__PURE__ */ (0,
|
|
2561
|
-
onReset ? /* @__PURE__ */ (0,
|
|
4378
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "agent-rail__actions", children: [
|
|
4379
|
+
onReset ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2562
4380
|
"button",
|
|
2563
4381
|
{
|
|
2564
4382
|
type: "button",
|
|
@@ -2566,24 +4384,24 @@ function AgentRail({
|
|
|
2566
4384
|
"aria-label": "Start a new conversation",
|
|
2567
4385
|
disabled: !hasVisitorMessages2,
|
|
2568
4386
|
onClick: onReset,
|
|
2569
|
-
children: /* @__PURE__ */ (0,
|
|
4387
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NewChatIcon, {})
|
|
2570
4388
|
}
|
|
2571
4389
|
) : null,
|
|
2572
|
-
onExpandToggle ? /* @__PURE__ */ (0,
|
|
4390
|
+
onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2573
4391
|
"button",
|
|
2574
4392
|
{
|
|
2575
4393
|
type: "button",
|
|
2576
4394
|
className: "agent-rail__expand",
|
|
2577
4395
|
"aria-label": expanded ? "Exit full screen" : "Open full screen",
|
|
2578
4396
|
onClick: onExpandToggle,
|
|
2579
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
4397
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ExpandIcon, {})
|
|
2580
4398
|
}
|
|
2581
4399
|
) : null
|
|
2582
4400
|
] })
|
|
2583
4401
|
] }) }),
|
|
2584
|
-
/* @__PURE__ */ (0,
|
|
2585
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ (0,
|
|
2586
|
-
greeting?.role === "agent" ? /* @__PURE__ */ (0,
|
|
4402
|
+
/* @__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: [
|
|
4403
|
+
!hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
4404
|
+
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2587
4405
|
MessageBubble,
|
|
2588
4406
|
{
|
|
2589
4407
|
message: greeting,
|
|
@@ -2591,7 +4409,7 @@ function AgentRail({
|
|
|
2591
4409
|
onBook
|
|
2592
4410
|
}
|
|
2593
4411
|
) : null,
|
|
2594
|
-
showIdleFollowUps ? /* @__PURE__ */ (0,
|
|
4412
|
+
showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2595
4413
|
FollowUpChips,
|
|
2596
4414
|
{
|
|
2597
4415
|
suggestions: state.followUps,
|
|
@@ -2599,64 +4417,94 @@ function AgentRail({
|
|
|
2599
4417
|
label: "Start here",
|
|
2600
4418
|
onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
|
|
2601
4419
|
}
|
|
2602
|
-
) }) : null
|
|
4420
|
+
) }) : null,
|
|
4421
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4422
|
+
AgentActivityBubble,
|
|
4423
|
+
{
|
|
4424
|
+
brandLabel: resolvedBrandLabel,
|
|
4425
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4426
|
+
failed: state.phase === "error",
|
|
4427
|
+
steps: state.toolSteps
|
|
4428
|
+
}
|
|
4429
|
+
) : null,
|
|
4430
|
+
visitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VisitorToolResultView, { result }, result.id)),
|
|
4431
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4432
|
+
HumanInputCard,
|
|
4433
|
+
{
|
|
4434
|
+
request,
|
|
4435
|
+
onRespond: onInputResponse
|
|
4436
|
+
},
|
|
4437
|
+
request.requestId
|
|
4438
|
+
))
|
|
2603
4439
|
] }) : null,
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
4440
|
+
visibleMessages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__turn-block", children: [
|
|
4441
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4442
|
+
MessageBubble,
|
|
4443
|
+
{
|
|
4444
|
+
message,
|
|
4445
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4446
|
+
offer: index === lastAgentIndex ? state.pendingOffer : void 0,
|
|
4447
|
+
onBook
|
|
4448
|
+
}
|
|
4449
|
+
),
|
|
4450
|
+
index === lastVisitorIndex ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
4451
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4452
|
+
AgentActivityBubble,
|
|
4453
|
+
{
|
|
4454
|
+
brandLabel: resolvedBrandLabel,
|
|
4455
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4456
|
+
failed: state.phase === "error",
|
|
4457
|
+
steps: state.toolSteps
|
|
4458
|
+
}
|
|
4459
|
+
) : null,
|
|
4460
|
+
visitorToolResults.map((result) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VisitorToolResultView, { result }, result.id)),
|
|
4461
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4462
|
+
HumanInputCard,
|
|
4463
|
+
{
|
|
4464
|
+
request,
|
|
4465
|
+
onRespond: onInputResponse
|
|
4466
|
+
},
|
|
4467
|
+
request.requestId
|
|
4468
|
+
))
|
|
4469
|
+
] }) : null
|
|
4470
|
+
] }, message.id)),
|
|
4471
|
+
streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2623
4472
|
MessageBubble,
|
|
2624
4473
|
{
|
|
2625
|
-
message:
|
|
4474
|
+
message: streamingMessage,
|
|
2626
4475
|
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4476
|
+
offer: state.pendingOffer,
|
|
2627
4477
|
onBook
|
|
2628
4478
|
}
|
|
2629
4479
|
) : null,
|
|
2630
|
-
|
|
2631
|
-
|
|
4480
|
+
waitingForBooking ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4481
|
+
BookingCard,
|
|
2632
4482
|
{
|
|
2633
|
-
|
|
2634
|
-
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
2635
|
-
offer: state.pendingOffer,
|
|
2636
|
-
onBook
|
|
4483
|
+
offer: { type: "booking_offer", eventTypes: [], slots: [] }
|
|
2637
4484
|
}
|
|
2638
4485
|
) : null,
|
|
2639
|
-
state.error ? /* @__PURE__ */ (0,
|
|
2640
|
-
/* @__PURE__ */ (0,
|
|
2641
|
-
/* @__PURE__ */ (0,
|
|
2642
|
-
/* @__PURE__ */ (0,
|
|
4486
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
|
|
4487
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
4488
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "Something went wrong" }),
|
|
4489
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: state.error })
|
|
2643
4490
|
] }),
|
|
2644
|
-
onRetry ? /* @__PURE__ */ (0,
|
|
4491
|
+
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
2645
4492
|
] }) : null
|
|
2646
4493
|
] }) }),
|
|
2647
|
-
/* @__PURE__ */ (0,
|
|
2648
|
-
/* @__PURE__ */ (0,
|
|
4494
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
|
|
4495
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2649
4496
|
Composer,
|
|
2650
4497
|
{
|
|
2651
4498
|
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
2652
4499
|
disabled: isBusy,
|
|
4500
|
+
form: composerForm,
|
|
2653
4501
|
placeholder: composerPlaceholder,
|
|
2654
4502
|
onSubmit
|
|
2655
4503
|
}
|
|
2656
4504
|
),
|
|
2657
|
-
/* @__PURE__ */ (0,
|
|
2658
|
-
/* @__PURE__ */ (0,
|
|
2659
|
-
/* @__PURE__ */ (0,
|
|
4505
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { children: [
|
|
4506
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "AI can make mistakes. Check important info." }),
|
|
4507
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: poweredByLabel })
|
|
2660
4508
|
] }) })
|
|
2661
4509
|
] })
|
|
2662
4510
|
]
|
|
@@ -2665,9 +4513,9 @@ function AgentRail({
|
|
|
2665
4513
|
}
|
|
2666
4514
|
|
|
2667
4515
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
2668
|
-
var
|
|
4516
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2669
4517
|
function SparklesIcon() {
|
|
2670
|
-
return /* @__PURE__ */ (0,
|
|
4518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2671
4519
|
"svg",
|
|
2672
4520
|
{
|
|
2673
4521
|
className: "assist-edge-tab__sparkles",
|
|
@@ -2675,21 +4523,21 @@ function SparklesIcon() {
|
|
|
2675
4523
|
fill: "none",
|
|
2676
4524
|
"aria-hidden": "true",
|
|
2677
4525
|
children: [
|
|
2678
|
-
/* @__PURE__ */ (0,
|
|
4526
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2679
4527
|
"path",
|
|
2680
4528
|
{
|
|
2681
4529
|
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
4530
|
fill: "currentColor"
|
|
2683
4531
|
}
|
|
2684
4532
|
),
|
|
2685
|
-
/* @__PURE__ */ (0,
|
|
4533
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2686
4534
|
"path",
|
|
2687
4535
|
{
|
|
2688
4536
|
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
4537
|
fill: "currentColor"
|
|
2690
4538
|
}
|
|
2691
4539
|
),
|
|
2692
|
-
/* @__PURE__ */ (0,
|
|
4540
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2693
4541
|
"path",
|
|
2694
4542
|
{
|
|
2695
4543
|
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 +4551,7 @@ function SparklesIcon() {
|
|
|
2703
4551
|
function TabMarkIcon({ customIconUrl }) {
|
|
2704
4552
|
const url = customIconUrl?.trim();
|
|
2705
4553
|
if (url) {
|
|
2706
|
-
return /* @__PURE__ */ (0,
|
|
4554
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2707
4555
|
"img",
|
|
2708
4556
|
{
|
|
2709
4557
|
alt: "",
|
|
@@ -2713,10 +4561,10 @@ function TabMarkIcon({ customIconUrl }) {
|
|
|
2713
4561
|
}
|
|
2714
4562
|
);
|
|
2715
4563
|
}
|
|
2716
|
-
return /* @__PURE__ */ (0,
|
|
4564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SparklesIcon, {});
|
|
2717
4565
|
}
|
|
2718
4566
|
function ChevronLeftIcon() {
|
|
2719
|
-
return /* @__PURE__ */ (0,
|
|
4567
|
+
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
4568
|
"path",
|
|
2721
4569
|
{
|
|
2722
4570
|
d: "M10 4L6 8l4 4",
|
|
@@ -2728,7 +4576,7 @@ function ChevronLeftIcon() {
|
|
|
2728
4576
|
) });
|
|
2729
4577
|
}
|
|
2730
4578
|
function ChevronDownIcon() {
|
|
2731
|
-
return /* @__PURE__ */ (0,
|
|
4579
|
+
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
4580
|
"path",
|
|
2733
4581
|
{
|
|
2734
4582
|
d: "M4 6l4 4 4-4",
|
|
@@ -2740,7 +4588,7 @@ function ChevronDownIcon() {
|
|
|
2740
4588
|
) });
|
|
2741
4589
|
}
|
|
2742
4590
|
function DragDots() {
|
|
2743
|
-
return /* @__PURE__ */ (0,
|
|
4591
|
+
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
4592
|
}
|
|
2745
4593
|
var VARIANT_COPY = {
|
|
2746
4594
|
outline: { label: "Ask anything", aria: "Ask anything" },
|
|
@@ -2769,6 +4617,7 @@ function AssistEdgeTab({
|
|
|
2769
4617
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2770
4618
|
const copy = VARIANT_COPY[variant];
|
|
2771
4619
|
const visibleLabel = label?.trim() || copy.label;
|
|
4620
|
+
const alignment = along < 50 ? "start" : along > 50 ? "end" : "center";
|
|
2772
4621
|
const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
|
|
2773
4622
|
const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
|
|
2774
4623
|
const resolvedBorderColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.border : borderColor;
|
|
@@ -2785,11 +4634,11 @@ function AssistEdgeTab({
|
|
|
2785
4634
|
...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
|
|
2786
4635
|
colorScheme: resolvedColorScheme
|
|
2787
4636
|
};
|
|
2788
|
-
return /* @__PURE__ */ (0,
|
|
4637
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2789
4638
|
"button",
|
|
2790
4639
|
{
|
|
2791
4640
|
type: "button",
|
|
2792
|
-
className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
|
|
4641
|
+
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
4642
|
"data-color-scheme": resolvedColorScheme,
|
|
2794
4643
|
style,
|
|
2795
4644
|
"aria-label": `Open ${visibleLabel}`,
|
|
@@ -2797,15 +4646,15 @@ function AssistEdgeTab({
|
|
|
2797
4646
|
tabIndex: visible ? 0 : -1,
|
|
2798
4647
|
onClick: onOpen,
|
|
2799
4648
|
children: [
|
|
2800
|
-
mobile ? /* @__PURE__ */ (0,
|
|
2801
|
-
/* @__PURE__ */ (0,
|
|
4649
|
+
mobile ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4650
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2802
4651
|
"span",
|
|
2803
4652
|
{
|
|
2804
4653
|
className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
|
|
2805
4654
|
"aria-hidden": "true",
|
|
2806
4655
|
children: [
|
|
2807
|
-
/* @__PURE__ */ (0,
|
|
2808
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4656
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4657
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2809
4658
|
"img",
|
|
2810
4659
|
{
|
|
2811
4660
|
className: "assist-edge-tab__logo",
|
|
@@ -2819,11 +4668,11 @@ function AssistEdgeTab({
|
|
|
2819
4668
|
]
|
|
2820
4669
|
}
|
|
2821
4670
|
),
|
|
2822
|
-
/* @__PURE__ */ (0,
|
|
2823
|
-
] }) : variant === "outline" ? /* @__PURE__ */ (0,
|
|
2824
|
-
/* @__PURE__ */ (0,
|
|
2825
|
-
/* @__PURE__ */ (0,
|
|
2826
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4671
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel })
|
|
4672
|
+
] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4673
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4674
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4675
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2827
4676
|
"img",
|
|
2828
4677
|
{
|
|
2829
4678
|
className: "assist-edge-tab__logo",
|
|
@@ -2835,18 +4684,18 @@ function AssistEdgeTab({
|
|
|
2835
4684
|
}
|
|
2836
4685
|
) : null
|
|
2837
4686
|
] }),
|
|
2838
|
-
/* @__PURE__ */ (0,
|
|
2839
|
-
/* @__PURE__ */ (0,
|
|
4687
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4688
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronDownIcon, {})
|
|
2840
4689
|
] }) : null,
|
|
2841
|
-
variant === "ask" ? /* @__PURE__ */ (0,
|
|
2842
|
-
/* @__PURE__ */ (0,
|
|
2843
|
-
/* @__PURE__ */ (0,
|
|
2844
|
-
/* @__PURE__ */ (0,
|
|
4690
|
+
variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4691
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronLeftIcon, {}),
|
|
4692
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4693
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DragDots, {})
|
|
2845
4694
|
] }) : null,
|
|
2846
|
-
variant === "fill" ? /* @__PURE__ */ (0,
|
|
2847
|
-
/* @__PURE__ */ (0,
|
|
2848
|
-
/* @__PURE__ */ (0,
|
|
2849
|
-
showLogo ? /* @__PURE__ */ (0,
|
|
4695
|
+
variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
4696
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4697
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TabMarkIcon, { customIconUrl }),
|
|
4698
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2850
4699
|
"img",
|
|
2851
4700
|
{
|
|
2852
4701
|
className: "assist-edge-tab__logo",
|
|
@@ -2858,8 +4707,8 @@ function AssistEdgeTab({
|
|
|
2858
4707
|
}
|
|
2859
4708
|
) : null
|
|
2860
4709
|
] }),
|
|
2861
|
-
/* @__PURE__ */ (0,
|
|
2862
|
-
/* @__PURE__ */ (0,
|
|
4710
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4711
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronLeftIcon, {})
|
|
2863
4712
|
] }) : null
|
|
2864
4713
|
]
|
|
2865
4714
|
}
|
|
@@ -2867,7 +4716,7 @@ function AssistEdgeTab({
|
|
|
2867
4716
|
}
|
|
2868
4717
|
|
|
2869
4718
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
2870
|
-
var
|
|
4719
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2871
4720
|
function AgentWidget({
|
|
2872
4721
|
indexId,
|
|
2873
4722
|
customerId,
|
|
@@ -2880,13 +4729,14 @@ function AgentWidget({
|
|
|
2880
4729
|
pageShift = true,
|
|
2881
4730
|
registerPanelController = false,
|
|
2882
4731
|
colorScheme = "auto",
|
|
2883
|
-
branding
|
|
4732
|
+
branding,
|
|
4733
|
+
toolResultRegistry
|
|
2884
4734
|
}) {
|
|
2885
4735
|
const isMobile = useIsMobile();
|
|
2886
4736
|
const placement = normalizeAgentPlacement(placementInput);
|
|
2887
|
-
const railSlotRef = (0,
|
|
2888
|
-
const [railCollapsed, setRailCollapsed] = (0,
|
|
2889
|
-
const [railExpanded, setRailExpanded] = (0,
|
|
4737
|
+
const railSlotRef = (0, import_react11.useRef)(null);
|
|
4738
|
+
const [railCollapsed, setRailCollapsed] = (0, import_react11.useState)(defaultCollapsed);
|
|
4739
|
+
const [railExpanded, setRailExpanded] = (0, import_react11.useState)(false);
|
|
2890
4740
|
const pageShiftActive = shouldApplyPageShift({
|
|
2891
4741
|
pageShift,
|
|
2892
4742
|
isMobile,
|
|
@@ -2897,14 +4747,15 @@ function AgentWidget({
|
|
|
2897
4747
|
active: pageShiftActive,
|
|
2898
4748
|
railSlotRef
|
|
2899
4749
|
});
|
|
2900
|
-
const { state, reset, retry, submit } = useAgentChat({
|
|
4750
|
+
const { state, reset, retry, respondToInput, respondToToolInput, submit } = useAgentChat({
|
|
2901
4751
|
customerId,
|
|
2902
4752
|
getUnpublishedPreviewGrant,
|
|
2903
4753
|
indexId,
|
|
2904
4754
|
previewBuildId,
|
|
2905
4755
|
version,
|
|
2906
4756
|
runtimeOrigin,
|
|
2907
|
-
greeting: branding?.greeting
|
|
4757
|
+
greeting: branding?.greeting,
|
|
4758
|
+
toolResultRegistry
|
|
2908
4759
|
});
|
|
2909
4760
|
const agentName = branding?.agentName ?? "";
|
|
2910
4761
|
const tabLabel = branding?.tabLabel ?? agentName;
|
|
@@ -2925,22 +4776,24 @@ function AgentWidget({
|
|
|
2925
4776
|
} : {},
|
|
2926
4777
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
2927
4778
|
};
|
|
2928
|
-
(0,
|
|
4779
|
+
(0, import_react11.useEffect)(() => {
|
|
2929
4780
|
if (!registerPanelController) return;
|
|
2930
4781
|
registerAgentPanelController(customerId, {
|
|
2931
4782
|
open: () => setRailCollapsed(false),
|
|
2932
4783
|
close: () => {
|
|
2933
4784
|
setRailCollapsed(true);
|
|
2934
4785
|
setRailExpanded(false);
|
|
2935
|
-
}
|
|
4786
|
+
},
|
|
4787
|
+
reset,
|
|
4788
|
+
submit
|
|
2936
4789
|
});
|
|
2937
4790
|
return () => unregisterAgentPanelController(customerId);
|
|
2938
|
-
}, [customerId, registerPanelController]);
|
|
4791
|
+
}, [customerId, registerPanelController, reset, submit]);
|
|
2939
4792
|
async function handleSubmit(message) {
|
|
2940
4793
|
if (isMobile) setRailCollapsed(false);
|
|
2941
4794
|
await submit(message);
|
|
2942
4795
|
}
|
|
2943
|
-
(0,
|
|
4796
|
+
(0, import_react11.useEffect)(() => {
|
|
2944
4797
|
if (railCollapsed) return;
|
|
2945
4798
|
const handleKeyDown = (event) => {
|
|
2946
4799
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -2974,19 +4827,19 @@ function AgentWidget({
|
|
|
2974
4827
|
window.addEventListener("keydown", handleKeyDown);
|
|
2975
4828
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2976
4829
|
}, [isMobile, railCollapsed, railExpanded]);
|
|
2977
|
-
return /* @__PURE__ */ (0,
|
|
2978
|
-
/* @__PURE__ */ (0,
|
|
4830
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "webless-agent-root", children: [
|
|
4831
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2979
4832
|
"div",
|
|
2980
4833
|
{
|
|
2981
4834
|
className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
|
|
2982
|
-
children: /* @__PURE__ */ (0,
|
|
4835
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2983
4836
|
"div",
|
|
2984
4837
|
{
|
|
2985
4838
|
ref: railSlotRef,
|
|
2986
4839
|
className: "webless-agent-root__rail-slot",
|
|
2987
4840
|
inert: railCollapsed || void 0,
|
|
2988
4841
|
"aria-hidden": railCollapsed,
|
|
2989
|
-
children: /* @__PURE__ */ (0,
|
|
4842
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2990
4843
|
AgentRail,
|
|
2991
4844
|
{
|
|
2992
4845
|
theme,
|
|
@@ -3004,6 +4857,8 @@ function AgentWidget({
|
|
|
3004
4857
|
onSubmit: handleSubmit,
|
|
3005
4858
|
onReset: reset,
|
|
3006
4859
|
onRetry: () => void retry(),
|
|
4860
|
+
onInputResponse: (response) => void respondToInput(response),
|
|
4861
|
+
onToolInput: (surface, values) => void respondToToolInput(surface, values),
|
|
3007
4862
|
onFollowUpSelect: (label) => void handleSubmit(label),
|
|
3008
4863
|
onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
|
|
3009
4864
|
}
|
|
@@ -3012,7 +4867,7 @@ function AgentWidget({
|
|
|
3012
4867
|
)
|
|
3013
4868
|
}
|
|
3014
4869
|
),
|
|
3015
|
-
railCollapsed ? /* @__PURE__ */ (0,
|
|
4870
|
+
railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3016
4871
|
AssistEdgeTab,
|
|
3017
4872
|
{
|
|
3018
4873
|
variant: placement.variant,
|
|
@@ -3042,12 +4897,14 @@ function AgentWidget({
|
|
|
3042
4897
|
AgentWidget,
|
|
3043
4898
|
AssistEdgeTab,
|
|
3044
4899
|
DEFAULT_AGENT_PLACEMENT,
|
|
4900
|
+
builtInVisitorToolResultRegistry,
|
|
3045
4901
|
createIdleSuggestions,
|
|
3046
4902
|
defaultAgentRailTheme,
|
|
3047
4903
|
defaultDarkAgentRailTheme,
|
|
3048
4904
|
hasVisitorMessages,
|
|
3049
4905
|
isAgentBusy,
|
|
3050
4906
|
normalizeAgentPlacement,
|
|
4907
|
+
presentVisitorToolResult,
|
|
3051
4908
|
useAgentChat
|
|
3052
4909
|
});
|
|
3053
4910
|
//# sourceMappingURL=react.cjs.map
|