@webless/agent 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HXKD46I6.js → chunk-Y4DCEJDC.js} +2542 -665
- package/dist/chunk-Y4DCEJDC.js.map +1 -0
- package/dist/embed.cjs +2281 -409
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +770 -140
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +9 -3
- package/dist/embed.d.ts +9 -3
- package/dist/embed.js +6 -2
- package/dist/embed.js.map +1 -1
- package/dist/index.cjs +431 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -1
- package/dist/index.d.ts +102 -1
- package/dist/index.js +428 -23
- package/dist/index.js.map +1 -1
- package/dist/manifest-Cad-TQJw.d.cts +391 -0
- package/dist/manifest-Cad-TQJw.d.ts +391 -0
- package/dist/react.cjs +2268 -408
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +770 -140
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +13 -7
- package/dist/react.d.ts +13 -7
- package/dist/react.js +5 -1
- package/package.json +1 -1
- package/dist/chunk-HXKD46I6.js.map +0 -1
- package/dist/manifest-H6oHn8Pc.d.cts +0 -185
- package/dist/manifest-H6oHn8Pc.d.ts +0 -185
|
@@ -1,3 +1,993 @@
|
|
|
1
|
+
// src/runtime/tool-ui.ts
|
|
2
|
+
var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function isJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
7
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
11
|
+
if (typeof value !== "object") return false;
|
|
12
|
+
if (seen.has(value)) return false;
|
|
13
|
+
seen.add(value);
|
|
14
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, seen)) : Object.entries(value).every(
|
|
15
|
+
([key, item]) => typeof key === "string" && isJsonValue(item, seen)
|
|
16
|
+
);
|
|
17
|
+
seen.delete(value);
|
|
18
|
+
return valid;
|
|
19
|
+
}
|
|
20
|
+
function boundedString(value, max) {
|
|
21
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > max) {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
return value.trim();
|
|
25
|
+
}
|
|
26
|
+
function numberValue(value) {
|
|
27
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
28
|
+
}
|
|
29
|
+
function integerValue(value) {
|
|
30
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8e3 ? value : void 0;
|
|
31
|
+
}
|
|
32
|
+
function isFieldKind(value) {
|
|
33
|
+
return typeof value === "string" && [
|
|
34
|
+
"text",
|
|
35
|
+
"textarea",
|
|
36
|
+
"email",
|
|
37
|
+
"number",
|
|
38
|
+
"select",
|
|
39
|
+
"multi-select",
|
|
40
|
+
"checkbox",
|
|
41
|
+
"confirmation",
|
|
42
|
+
"radio",
|
|
43
|
+
"date",
|
|
44
|
+
"time",
|
|
45
|
+
"date-time",
|
|
46
|
+
"calendar",
|
|
47
|
+
"range",
|
|
48
|
+
"json"
|
|
49
|
+
].includes(value);
|
|
50
|
+
}
|
|
51
|
+
function parseField(value) {
|
|
52
|
+
if (!isRecord(value)) return null;
|
|
53
|
+
if (!hasOnlyKeys(value, [
|
|
54
|
+
"description",
|
|
55
|
+
"kind",
|
|
56
|
+
"label",
|
|
57
|
+
"max",
|
|
58
|
+
"maxItems",
|
|
59
|
+
"maxLength",
|
|
60
|
+
"min",
|
|
61
|
+
"minLength",
|
|
62
|
+
"options",
|
|
63
|
+
"path",
|
|
64
|
+
"placeholder",
|
|
65
|
+
"required",
|
|
66
|
+
"step",
|
|
67
|
+
"defaultValue"
|
|
68
|
+
])) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (!isFieldKind(value.kind)) return null;
|
|
72
|
+
const path = boundedString(value.path, 160);
|
|
73
|
+
const label = boundedString(value.label, 160);
|
|
74
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
75
|
+
const placeholder = value.placeholder === void 0 || typeof value.placeholder !== "string" ? void 0 : value.placeholder;
|
|
76
|
+
const required = value.required === void 0 || typeof value.required !== "boolean" ? void 0 : value.required;
|
|
77
|
+
const min = value.min === void 0 ? void 0 : numberValue(value.min);
|
|
78
|
+
const max = value.max === void 0 ? void 0 : numberValue(value.max);
|
|
79
|
+
const maxItems = value.maxItems === void 0 ? void 0 : integerValue(value.maxItems);
|
|
80
|
+
const step = value.step === void 0 ? void 0 : numberValue(value.step);
|
|
81
|
+
const minLength = value.minLength === void 0 ? void 0 : integerValue(value.minLength);
|
|
82
|
+
const maxLength = value.maxLength === void 0 ? void 0 : integerValue(value.maxLength);
|
|
83
|
+
const defaultValue = value.defaultValue === void 0 || !isJsonValue(value.defaultValue) ? void 0 : value.defaultValue;
|
|
84
|
+
if (!path || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || !label) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (value.description !== void 0 && !description) return null;
|
|
88
|
+
if (value.placeholder !== void 0 && placeholder === void 0) return null;
|
|
89
|
+
if (value.required !== void 0 && required === void 0) return null;
|
|
90
|
+
if (value.min !== void 0 && min === void 0) return null;
|
|
91
|
+
if (value.max !== void 0 && max === void 0) return null;
|
|
92
|
+
if (value.maxItems !== void 0 && (maxItems === void 0 || maxItems < 1 || maxItems > 100))
|
|
93
|
+
return null;
|
|
94
|
+
if (value.step !== void 0 && step === void 0) return null;
|
|
95
|
+
if (value.minLength !== void 0 && minLength === void 0) return null;
|
|
96
|
+
if (value.maxLength !== void 0 && maxLength === void 0) return null;
|
|
97
|
+
if (value.defaultValue !== void 0 && defaultValue === void 0)
|
|
98
|
+
return null;
|
|
99
|
+
if (value.options !== void 0) {
|
|
100
|
+
if (!Array.isArray(value.options) || value.options.length > 100)
|
|
101
|
+
return null;
|
|
102
|
+
for (const option of value.options) {
|
|
103
|
+
if (!isRecord(option) || !boundedString(option.label, 160) || !isJsonValue(option.value)) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
kind: value.kind,
|
|
110
|
+
path,
|
|
111
|
+
label,
|
|
112
|
+
...description ? { description } : {},
|
|
113
|
+
...placeholder !== void 0 ? { placeholder } : {},
|
|
114
|
+
...required !== void 0 ? { required } : {},
|
|
115
|
+
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
116
|
+
...value.options !== void 0 ? { options: value.options } : {},
|
|
117
|
+
...min !== void 0 ? { min } : {},
|
|
118
|
+
...max !== void 0 ? { max } : {},
|
|
119
|
+
...maxItems !== void 0 ? { maxItems } : {},
|
|
120
|
+
...step !== void 0 ? { step } : {},
|
|
121
|
+
...minLength !== void 0 ? { minLength } : {},
|
|
122
|
+
...maxLength !== void 0 ? { maxLength } : {}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function parseStep(value) {
|
|
126
|
+
if (!isRecord(value)) return null;
|
|
127
|
+
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const id = boundedString(value.id, 80);
|
|
131
|
+
const label = boundedString(value.label, 160);
|
|
132
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
133
|
+
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(
|
|
134
|
+
(path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
|
|
135
|
+
)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
id,
|
|
140
|
+
label,
|
|
141
|
+
fieldPaths: value.fieldPaths,
|
|
142
|
+
...description ? { description } : {}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function parseAction(value) {
|
|
146
|
+
if (!isRecord(value)) return null;
|
|
147
|
+
if (!hasOnlyKeys(value, ["id", "label"])) return null;
|
|
148
|
+
const label = boundedString(value.label, 80);
|
|
149
|
+
if (value.id !== "submit" && value.id !== "back" && value.id !== "next" && value.id !== "reset" || !label) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
id: value.id,
|
|
154
|
+
label
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function parseAgentToolUiSurface(value) {
|
|
158
|
+
if (!isRecord(value) || value.schemaVersion !== AGENT_TOOL_UI_SCHEMA_VERSION)
|
|
159
|
+
return null;
|
|
160
|
+
if (!hasOnlyKeys(value, [
|
|
161
|
+
"actions",
|
|
162
|
+
"description",
|
|
163
|
+
"fields",
|
|
164
|
+
"id",
|
|
165
|
+
"operationId",
|
|
166
|
+
"requestId",
|
|
167
|
+
"schemaVersion",
|
|
168
|
+
"steps",
|
|
169
|
+
"submitLabel",
|
|
170
|
+
"title",
|
|
171
|
+
"toolSlug",
|
|
172
|
+
"values"
|
|
173
|
+
])) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
const id = boundedString(value.id, 200);
|
|
177
|
+
const title = boundedString(value.title, 200);
|
|
178
|
+
const toolSlug = boundedString(value.toolSlug, 200);
|
|
179
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
180
|
+
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
181
|
+
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
182
|
+
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
183
|
+
if (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const fields = value.fields.map(parseField);
|
|
187
|
+
if (fields.some((field) => field === null)) return null;
|
|
188
|
+
const steps = value.steps === void 0 ? void 0 : Array.isArray(value.steps) && value.steps.length <= 8 ? value.steps.map(parseStep) : null;
|
|
189
|
+
if (steps?.some((step) => step === null)) return null;
|
|
190
|
+
const actions = value.actions === void 0 ? void 0 : Array.isArray(value.actions) && value.actions.length <= 8 ? value.actions.map(parseAction) : null;
|
|
191
|
+
if (actions?.some((action) => action === null)) return null;
|
|
192
|
+
if (value.description !== void 0 && !description) return null;
|
|
193
|
+
if (value.operationId !== void 0 && !operationId) return null;
|
|
194
|
+
if (value.requestId !== void 0 && !requestId) return null;
|
|
195
|
+
if (value.submitLabel !== void 0 && !submitLabel) return null;
|
|
196
|
+
const values = value.values !== void 0 && isRecord(value.values) ? value.values : void 0;
|
|
197
|
+
if (value.values !== void 0) {
|
|
198
|
+
if (!values || !Object.values(values).every((item) => isJsonValue(item)))
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
203
|
+
id,
|
|
204
|
+
title,
|
|
205
|
+
toolSlug,
|
|
206
|
+
fields,
|
|
207
|
+
...actions ? { actions } : {},
|
|
208
|
+
...description ? { description } : {},
|
|
209
|
+
...operationId ? { operationId } : {},
|
|
210
|
+
...requestId ? { requestId } : {},
|
|
211
|
+
...submitLabel ? { submitLabel } : {},
|
|
212
|
+
...steps ? { steps } : {},
|
|
213
|
+
...values ? { values } : {}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function hasOnlyKeys(value, allowed) {
|
|
217
|
+
const allowedKeys = new Set(allowed);
|
|
218
|
+
return Object.keys(value).every((key) => allowedKeys.has(key));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/runtime/tool-result-envelope.ts
|
|
222
|
+
var ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
223
|
+
"schemaVersion",
|
|
224
|
+
"output",
|
|
225
|
+
"presentationKinds",
|
|
226
|
+
"ui"
|
|
227
|
+
]);
|
|
228
|
+
function isRecord2(value) {
|
|
229
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
230
|
+
}
|
|
231
|
+
function isJsonValue2(value, seen = /* @__PURE__ */ new Set()) {
|
|
232
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
236
|
+
if (typeof value !== "object") return false;
|
|
237
|
+
if (seen.has(value)) return false;
|
|
238
|
+
seen.add(value);
|
|
239
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue2(item, seen)) : Object.entries(value).every(
|
|
240
|
+
([key, item]) => typeof key === "string" && isJsonValue2(item, seen)
|
|
241
|
+
);
|
|
242
|
+
seen.delete(value);
|
|
243
|
+
return valid;
|
|
244
|
+
}
|
|
245
|
+
function decodeEnvelope(value) {
|
|
246
|
+
if (typeof value !== "string") return value;
|
|
247
|
+
try {
|
|
248
|
+
return JSON.parse(value);
|
|
249
|
+
} catch {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function parseAgentToolResultEnvelope(value) {
|
|
254
|
+
const decoded = decodeEnvelope(value);
|
|
255
|
+
if (!isRecord2(decoded)) return null;
|
|
256
|
+
const keys = Object.keys(decoded);
|
|
257
|
+
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) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
const presentationKinds = decoded.presentationKinds.flatMap((kind) => {
|
|
261
|
+
if (typeof kind !== "string") return [];
|
|
262
|
+
const normalized = kind.trim();
|
|
263
|
+
return normalized && normalized.length <= 128 ? [normalized] : [];
|
|
264
|
+
});
|
|
265
|
+
if (presentationKinds.length !== decoded.presentationKinds.length) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const ui = decoded.ui === void 0 ? void 0 : parseAgentToolUiSurface(decoded.ui);
|
|
269
|
+
if (decoded.ui !== void 0 && !ui) return null;
|
|
270
|
+
return {
|
|
271
|
+
schemaVersion: "webless.tool-result.v1",
|
|
272
|
+
output: decoded.output,
|
|
273
|
+
presentationKinds,
|
|
274
|
+
...ui ? { ui } : {}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/react/lib/composer-form.ts
|
|
279
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
280
|
+
var MIN_FORM_FIELDS = 2;
|
|
281
|
+
var MAX_FORM_FIELDS = 5;
|
|
282
|
+
var FIELD_LIBRARY = [
|
|
283
|
+
{
|
|
284
|
+
id: "email",
|
|
285
|
+
kind: "email",
|
|
286
|
+
label: "Email",
|
|
287
|
+
placeholder: "email@example.com",
|
|
288
|
+
required: true,
|
|
289
|
+
autocomplete: "email",
|
|
290
|
+
patterns: [/\be-?mails?\b/i]
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
id: "name",
|
|
294
|
+
kind: "text",
|
|
295
|
+
label: "Name",
|
|
296
|
+
placeholder: "Your name",
|
|
297
|
+
required: true,
|
|
298
|
+
autocomplete: "name",
|
|
299
|
+
patterns: [/\b((full|first|last)\s+)?names?\b/i],
|
|
300
|
+
exclude: /\bcompany\s+names?\b/i
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
id: "phone",
|
|
304
|
+
kind: "tel",
|
|
305
|
+
label: "Phone",
|
|
306
|
+
placeholder: "+1 555 0100",
|
|
307
|
+
required: true,
|
|
308
|
+
autocomplete: "tel",
|
|
309
|
+
patterns: [/\b(phone|mobile|cell)\b/i]
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
id: "company",
|
|
313
|
+
kind: "text",
|
|
314
|
+
label: "Company",
|
|
315
|
+
placeholder: "Company name",
|
|
316
|
+
required: true,
|
|
317
|
+
autocomplete: "organization",
|
|
318
|
+
patterns: [/\bcompan(y|ies)\b/i]
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
id: "message",
|
|
322
|
+
kind: "textarea",
|
|
323
|
+
label: "Message",
|
|
324
|
+
placeholder: "Message\u2026",
|
|
325
|
+
required: true,
|
|
326
|
+
patterns: [/\b(your message|a message|the message|inquiry|enquiry)\b/i],
|
|
327
|
+
exclude: /\bin one message\b/i
|
|
328
|
+
}
|
|
329
|
+
];
|
|
330
|
+
function asRecord(value) {
|
|
331
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
332
|
+
}
|
|
333
|
+
function asString(value) {
|
|
334
|
+
return typeof value === "string" ? value.trim() : "";
|
|
335
|
+
}
|
|
336
|
+
function isFieldKind2(value) {
|
|
337
|
+
return value === "text" || value === "email" || value === "tel" || value === "textarea";
|
|
338
|
+
}
|
|
339
|
+
function parseVisitorFormFields(value) {
|
|
340
|
+
if (!Array.isArray(value)) return [];
|
|
341
|
+
const fields = [];
|
|
342
|
+
const seen = /* @__PURE__ */ new Set();
|
|
343
|
+
for (const item of value) {
|
|
344
|
+
const record = asRecord(item);
|
|
345
|
+
const id = asString(record?.id).toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
346
|
+
const kind = asString(record?.kind);
|
|
347
|
+
if (!record || !id || seen.has(id) || !isFieldKind2(kind)) continue;
|
|
348
|
+
seen.add(id);
|
|
349
|
+
const label = asString(record.label) || id;
|
|
350
|
+
fields.push({
|
|
351
|
+
id,
|
|
352
|
+
kind,
|
|
353
|
+
label,
|
|
354
|
+
placeholder: asString(record.placeholder) || label,
|
|
355
|
+
required: record.required !== false,
|
|
356
|
+
...asString(record.autocomplete) ? { autocomplete: asString(record.autocomplete) } : {}
|
|
357
|
+
});
|
|
358
|
+
if (fields.length >= MAX_FORM_FIELDS) break;
|
|
359
|
+
}
|
|
360
|
+
return fields;
|
|
361
|
+
}
|
|
362
|
+
function readComposerControlValue(event) {
|
|
363
|
+
const node = event.target ?? event.currentTarget ?? null;
|
|
364
|
+
if (node && typeof node === "object" && "value" in node && typeof node.value === "string") {
|
|
365
|
+
return node.value;
|
|
366
|
+
}
|
|
367
|
+
return "";
|
|
368
|
+
}
|
|
369
|
+
function isValidComposerFieldValue(field, value) {
|
|
370
|
+
const trimmed = value.trim();
|
|
371
|
+
if (!trimmed) return !field.required;
|
|
372
|
+
if (field.kind === "email") return EMAIL_PATTERN.test(trimmed);
|
|
373
|
+
if (field.kind === "tel") return trimmed.replace(/\D/g, "").length >= 7;
|
|
374
|
+
return trimmed.length > 0;
|
|
375
|
+
}
|
|
376
|
+
function isComposerFormComplete(form, values) {
|
|
377
|
+
return form.fields.every(
|
|
378
|
+
(field) => isValidComposerFieldValue(field, values[field.id] ?? "")
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
function formatComposerFormMessage(form, values) {
|
|
382
|
+
return form.fields.map((field) => {
|
|
383
|
+
const value = (values[field.id] ?? "").trim();
|
|
384
|
+
return value ? `${field.label}: ${value}` : "";
|
|
385
|
+
}).filter(Boolean).join("\n");
|
|
386
|
+
}
|
|
387
|
+
function looksLikeFieldCollection(text) {
|
|
388
|
+
return /\b(please|need|send|share|enter|provide|add|collect|what|which|use)\b/i.test(
|
|
389
|
+
text
|
|
390
|
+
) || /:\s*$/m.test(text) || /^[-*•]\s+/m.test(text);
|
|
391
|
+
}
|
|
392
|
+
function looksLikeBookingCopy(text) {
|
|
393
|
+
return /book(ing)? (card|a demo|this time)|pick a (date|time)|available (times|slots)|calendly/i.test(
|
|
394
|
+
text
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
function inferComposerForm(text) {
|
|
398
|
+
const cleaned = text.trim();
|
|
399
|
+
if (!cleaned || looksLikeBookingCopy(cleaned) || !looksLikeFieldCollection(cleaned)) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
const fields = FIELD_LIBRARY.flatMap((field) => {
|
|
403
|
+
if (field.exclude?.test(cleaned)) {
|
|
404
|
+
const leftover = cleaned.replace(field.exclude, " ");
|
|
405
|
+
if (!field.patterns.some((pattern) => pattern.test(leftover))) return [];
|
|
406
|
+
} else if (!field.patterns.some((pattern) => pattern.test(cleaned))) {
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
const { patterns: _patterns, exclude: _exclude, ...next } = field;
|
|
410
|
+
return [next];
|
|
411
|
+
}).slice(0, MAX_FORM_FIELDS);
|
|
412
|
+
if (fields.length < MIN_FORM_FIELDS) return null;
|
|
413
|
+
return {
|
|
414
|
+
id: `inferred:${fields.map((field) => field.id).join("+")}`,
|
|
415
|
+
fields
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function resolveComposerForm(input) {
|
|
419
|
+
if (input.enabled === false || input.hasBookingOffer) return null;
|
|
420
|
+
const text = input.agentText.trim();
|
|
421
|
+
if (!text) return null;
|
|
422
|
+
const card = input.cards?.find((item) => item.type === "visitor_form");
|
|
423
|
+
if (card?.fields && card.fields.length >= MIN_FORM_FIELDS) {
|
|
424
|
+
return {
|
|
425
|
+
id: `card:${card.fields.map((field) => field.id).join("+")}`,
|
|
426
|
+
fields: card.fields.slice(0, MAX_FORM_FIELDS)
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return inferComposerForm(text);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/react/lib/tool-card.ts
|
|
433
|
+
function preferBookingOffer(current, next) {
|
|
434
|
+
if (!current) return next;
|
|
435
|
+
if (next.slots.length !== current.slots.length) {
|
|
436
|
+
return next.slots.length > current.slots.length ? next : current;
|
|
437
|
+
}
|
|
438
|
+
if (next.eventTypes.length !== current.eventTypes.length) {
|
|
439
|
+
return next.eventTypes.length > current.eventTypes.length ? next : current;
|
|
440
|
+
}
|
|
441
|
+
return next;
|
|
442
|
+
}
|
|
443
|
+
function looksLikeBookingReady(text) {
|
|
444
|
+
return /demo option|options are ready|pick a (date|time)|available (times|slots)|book(ing)? (card|the demo)|schedule/i.test(
|
|
445
|
+
text
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
function bookingOfferIdentityKey(offer) {
|
|
449
|
+
const eventTypes = offer.eventTypes.map(
|
|
450
|
+
(item) => `${item.uri}:${item.duration ?? ""}:${item.locationKind ?? ""}:${item.location ?? ""}`
|
|
451
|
+
).join("|");
|
|
452
|
+
const slots = offer.slots.map((slot) => `${slot.eventTypeUri ?? ""}:${slot.startTime}`).join("|");
|
|
453
|
+
return `${eventTypes}::${slots}` || "offer";
|
|
454
|
+
}
|
|
455
|
+
var FENCE_PATTERN = /```(?:webless-tool-card|json)\s*([\s\S]*?)```/gi;
|
|
456
|
+
function asRecord2(value) {
|
|
457
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
458
|
+
}
|
|
459
|
+
function asString2(value) {
|
|
460
|
+
return typeof value === "string" ? value.trim() : "";
|
|
461
|
+
}
|
|
462
|
+
function isEventUri(value) {
|
|
463
|
+
return /^https:\/\/api\.calendly\.com\/scheduled_events\/[^/]+$/i.test(value);
|
|
464
|
+
}
|
|
465
|
+
function isEventTypeUri(value) {
|
|
466
|
+
return /^https:\/\/api\.calendly\.com\/event_types\/[^/]+$/i.test(value);
|
|
467
|
+
}
|
|
468
|
+
function parseToolCard(value) {
|
|
469
|
+
const record = asRecord2(value);
|
|
470
|
+
if (!record) return null;
|
|
471
|
+
if (record.booking_offer && asString2(record.type) !== "booking_offer") {
|
|
472
|
+
const nested = parseToolCard(record.booking_offer);
|
|
473
|
+
if (nested) return nested;
|
|
474
|
+
}
|
|
475
|
+
if (record.visitor_booking && asString2(record.type) !== "booking_confirmed") {
|
|
476
|
+
const nested = parseToolCard(record.visitor_booking);
|
|
477
|
+
if (nested) return nested;
|
|
478
|
+
}
|
|
479
|
+
const type = asString2(record.type);
|
|
480
|
+
if (type === "booking_offer") {
|
|
481
|
+
const eventTypes = Array.isArray(record.eventTypes) ? record.eventTypes.flatMap((item) => {
|
|
482
|
+
const entry = asRecord2(item);
|
|
483
|
+
const uri = asString2(entry?.uri);
|
|
484
|
+
if (!entry || !uri) return [];
|
|
485
|
+
const duration = entry.duration;
|
|
486
|
+
const locationKind = asString2(entry.locationKind);
|
|
487
|
+
const location = asString2(entry.location);
|
|
488
|
+
return [
|
|
489
|
+
{
|
|
490
|
+
name: asString2(entry.name) || "Meeting",
|
|
491
|
+
uri,
|
|
492
|
+
...typeof duration === "number" ? { duration } : {},
|
|
493
|
+
...locationKind ? { locationKind } : {},
|
|
494
|
+
...location ? { location } : {}
|
|
495
|
+
}
|
|
496
|
+
];
|
|
497
|
+
}) : [];
|
|
498
|
+
const slots = Array.isArray(record.slots) ? record.slots.flatMap((item) => {
|
|
499
|
+
const entry = asRecord2(item);
|
|
500
|
+
const startTime = asString2(entry?.startTime);
|
|
501
|
+
if (!entry || !startTime) return [];
|
|
502
|
+
const eventTypeUri = asString2(entry.eventTypeUri);
|
|
503
|
+
return [
|
|
504
|
+
{
|
|
505
|
+
startTime,
|
|
506
|
+
...isEventTypeUri(eventTypeUri) ? { eventTypeUri } : {}
|
|
507
|
+
}
|
|
508
|
+
];
|
|
509
|
+
}) : [];
|
|
510
|
+
if (eventTypes.length === 0 && slots.length === 0) return null;
|
|
511
|
+
return { type: "booking_offer", eventTypes, slots };
|
|
512
|
+
}
|
|
513
|
+
if (type === "booking_confirmed") {
|
|
514
|
+
const eventUri = asString2(record.eventUri);
|
|
515
|
+
if (!isEventUri(eventUri)) return null;
|
|
516
|
+
const inviteeUri = asString2(record.inviteeUri);
|
|
517
|
+
const inviteeEmail = asString2(record.inviteeEmail);
|
|
518
|
+
const startTime = asString2(record.startTime);
|
|
519
|
+
return {
|
|
520
|
+
type: "booking_confirmed",
|
|
521
|
+
eventUri,
|
|
522
|
+
...inviteeUri ? { inviteeUri } : {},
|
|
523
|
+
...inviteeEmail ? { inviteeEmail } : {},
|
|
524
|
+
...startTime ? { startTime } : {}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
if (type === "booking_canceled") {
|
|
528
|
+
const eventUri = asString2(record.eventUri);
|
|
529
|
+
if (!isEventUri(eventUri)) return null;
|
|
530
|
+
return { type: "booking_canceled", eventUri };
|
|
531
|
+
}
|
|
532
|
+
if (type === "visitor_form") {
|
|
533
|
+
const fields = parseVisitorFormFields(record.fields);
|
|
534
|
+
if (fields.length < 2) return null;
|
|
535
|
+
return { type: "visitor_form", fields };
|
|
536
|
+
}
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
function formatBookingOfferFence(offer) {
|
|
540
|
+
return [
|
|
541
|
+
"```webless-tool-card",
|
|
542
|
+
JSON.stringify({
|
|
543
|
+
type: "booking_offer",
|
|
544
|
+
eventTypes: offer.eventTypes,
|
|
545
|
+
slots: offer.slots
|
|
546
|
+
}),
|
|
547
|
+
"```"
|
|
548
|
+
].join("\n");
|
|
549
|
+
}
|
|
550
|
+
function bookingCardFromActionOutput(output) {
|
|
551
|
+
const record = asRecord2(output);
|
|
552
|
+
const data = asRecord2(record?.data) ?? record;
|
|
553
|
+
return parseToolCard(data);
|
|
554
|
+
}
|
|
555
|
+
function ensureBookingOfferText(text, offer) {
|
|
556
|
+
if (!offer) return text;
|
|
557
|
+
if (extractToolCards(text).some((card) => card.type === "booking_offer")) {
|
|
558
|
+
return text;
|
|
559
|
+
}
|
|
560
|
+
const visible = stripToolCards(text).trim() || text.trim();
|
|
561
|
+
return `${visible}
|
|
562
|
+
|
|
563
|
+
${formatBookingOfferFence(offer)}`;
|
|
564
|
+
}
|
|
565
|
+
function hideToolCardFences(text) {
|
|
566
|
+
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();
|
|
567
|
+
}
|
|
568
|
+
var BOOKING_CARD_FALLBACK = "Pick a date and time that works for you.";
|
|
569
|
+
function looksLikeBookingAvailabilityDump(text) {
|
|
570
|
+
const cleaned = text.trim();
|
|
571
|
+
if (!cleaned) return false;
|
|
572
|
+
const isoCount = (cleaned.match(/\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/g) ?? []).length;
|
|
573
|
+
const utcCount = (cleaned.match(/\bUTC\b/g) ?? []).length;
|
|
574
|
+
return isoCount >= 2 || utcCount >= 2 || /api\.calendly\.com|calendly\.com\//i.test(cleaned) || /webless-tool-card/i.test(cleaned);
|
|
575
|
+
}
|
|
576
|
+
function sanitizeBookingOfferCopy(text) {
|
|
577
|
+
const cleaned = hideToolCardFences(text);
|
|
578
|
+
if (!cleaned || looksLikeBookingAvailabilityDump(cleaned)) {
|
|
579
|
+
return BOOKING_CARD_FALLBACK;
|
|
580
|
+
}
|
|
581
|
+
return cleaned;
|
|
582
|
+
}
|
|
583
|
+
function visitorTimeZone() {
|
|
584
|
+
try {
|
|
585
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
586
|
+
} catch {
|
|
587
|
+
return "UTC";
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function extractToolCards(text) {
|
|
591
|
+
const cards = [];
|
|
592
|
+
for (const match of text.matchAll(FENCE_PATTERN)) {
|
|
593
|
+
try {
|
|
594
|
+
const card = parseToolCard(JSON.parse(match[1] ?? ""));
|
|
595
|
+
if (card) cards.push(card);
|
|
596
|
+
} catch {
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return cards;
|
|
600
|
+
}
|
|
601
|
+
function stripToolCards(text) {
|
|
602
|
+
return text.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
603
|
+
}
|
|
604
|
+
function localDateKey(date) {
|
|
605
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
606
|
+
return [
|
|
607
|
+
date.getFullYear(),
|
|
608
|
+
String(date.getMonth() + 1).padStart(2, "0"),
|
|
609
|
+
String(date.getDate()).padStart(2, "0")
|
|
610
|
+
].join("-");
|
|
611
|
+
}
|
|
612
|
+
function slotDateKey(startTime) {
|
|
613
|
+
return localDateKey(new Date(startTime)) || startTime;
|
|
614
|
+
}
|
|
615
|
+
function bookingSlotsForEventType(slots, eventTypeUri) {
|
|
616
|
+
return slots.filter(
|
|
617
|
+
(slot) => !eventTypeUri || !slot.eventTypeUri || slot.eventTypeUri === eventTypeUri
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
function firstAvailableBookingMonth(slots) {
|
|
621
|
+
let earliest;
|
|
622
|
+
for (const slot of slots) {
|
|
623
|
+
const key = slotDateKey(slot.startTime);
|
|
624
|
+
if (!earliest || key < earliest) earliest = key;
|
|
625
|
+
}
|
|
626
|
+
const [year, month] = (earliest ?? slotDateKey((/* @__PURE__ */ new Date()).toISOString())).split("-").map(Number);
|
|
627
|
+
if (!year || !month) {
|
|
628
|
+
const now = /* @__PURE__ */ new Date();
|
|
629
|
+
return { year: now.getFullYear(), month: now.getMonth() };
|
|
630
|
+
}
|
|
631
|
+
return { year, month: month - 1 };
|
|
632
|
+
}
|
|
633
|
+
function formatMonthTitle(year, month) {
|
|
634
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
635
|
+
month: "long",
|
|
636
|
+
year: "numeric"
|
|
637
|
+
}).format(new Date(year, month, 1));
|
|
638
|
+
}
|
|
639
|
+
function formatLongDate(startTime) {
|
|
640
|
+
const date = new Date(startTime);
|
|
641
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
642
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
643
|
+
weekday: "long",
|
|
644
|
+
month: "long",
|
|
645
|
+
day: "numeric"
|
|
646
|
+
}).format(date);
|
|
647
|
+
}
|
|
648
|
+
function weekdayLabels() {
|
|
649
|
+
return Array.from(
|
|
650
|
+
{ length: 7 },
|
|
651
|
+
(_, index) => new Intl.DateTimeFormat(void 0, { weekday: "short" }).format(
|
|
652
|
+
new Date(2026, 7, 3 + index)
|
|
653
|
+
)
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
function formatTimeChip(startTime) {
|
|
657
|
+
const date = new Date(startTime);
|
|
658
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
659
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
660
|
+
hour: "numeric",
|
|
661
|
+
minute: "2-digit"
|
|
662
|
+
}).format(date);
|
|
663
|
+
}
|
|
664
|
+
function formatSlotTimeZone(startTime) {
|
|
665
|
+
const date = new Date(startTime);
|
|
666
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
667
|
+
return new Intl.DateTimeFormat(void 0, { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "";
|
|
668
|
+
}
|
|
669
|
+
function formatSlotLabel(startTime) {
|
|
670
|
+
const date = new Date(startTime);
|
|
671
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
672
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
673
|
+
weekday: "short",
|
|
674
|
+
month: "short",
|
|
675
|
+
day: "numeric",
|
|
676
|
+
hour: "numeric",
|
|
677
|
+
minute: "2-digit",
|
|
678
|
+
timeZoneName: "short"
|
|
679
|
+
}).format(date);
|
|
680
|
+
}
|
|
681
|
+
function formatBookingRequest(input) {
|
|
682
|
+
return [
|
|
683
|
+
"Book this meeting now with CALENDLY_POST_INVITEE.",
|
|
684
|
+
"Execute CALENDLY_POST_INVITEE in this turn with the fields below.",
|
|
685
|
+
"Do not open a Calendly URL and do not list other scheduled events.",
|
|
686
|
+
"Do not invent a location kind. Use only the location fields below.",
|
|
687
|
+
`event_type: ${input.eventTypeUri}`,
|
|
688
|
+
`start_time: ${input.startTime}`,
|
|
689
|
+
`invitee.name: ${input.inviteeName}`,
|
|
690
|
+
`invitee.email: ${input.inviteeEmail}`,
|
|
691
|
+
`invitee.timezone: ${input.timezone}`,
|
|
692
|
+
...input.locationKind ? [
|
|
693
|
+
`location.kind: ${input.locationKind}`,
|
|
694
|
+
...input.location ? [`location.location: ${input.location}`] : []
|
|
695
|
+
] : ["Do not send a location field."],
|
|
696
|
+
"After it succeeds, reply with one short confirmation and a webless-tool-card booking_confirmed block using visitor_booking."
|
|
697
|
+
].join("\n");
|
|
698
|
+
}
|
|
699
|
+
function visitorBookingPrefix(booking) {
|
|
700
|
+
return [
|
|
701
|
+
"This visitor already booked a meeting. Use only this meeting:",
|
|
702
|
+
`- scheduled event URI: ${booking.eventUri}`,
|
|
703
|
+
...booking.inviteeUri ? [`- invitee URI: ${booking.inviteeUri}`] : [],
|
|
704
|
+
...booking.inviteeEmail ? [`- invitee email: ${booking.inviteeEmail}`] : [],
|
|
705
|
+
"For details call CALENDLY_GET_EVENT or CALENDLY_GET_EVENT_INVITEE with those URIs.",
|
|
706
|
+
"If you must list events, pass this invitee_email. Never describe any other scheduled event.",
|
|
707
|
+
"start_time values from Calendly are UTC."
|
|
708
|
+
].join("\n");
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/react/lib/tool-result.ts
|
|
712
|
+
var MAX_TEXT_LENGTH = 240;
|
|
713
|
+
var MAX_DETAILS = 6;
|
|
714
|
+
var MAX_LINKS = 4;
|
|
715
|
+
var MAX_COLLECTION_ITEMS = 8;
|
|
716
|
+
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;
|
|
717
|
+
function safeText(value) {
|
|
718
|
+
if (typeof value !== "string") return "";
|
|
719
|
+
return value.trim().slice(0, MAX_TEXT_LENGTH);
|
|
720
|
+
}
|
|
721
|
+
function safeHref(value) {
|
|
722
|
+
const text = safeText(value);
|
|
723
|
+
if (!text) return "";
|
|
724
|
+
try {
|
|
725
|
+
const url = new URL(text);
|
|
726
|
+
return url.protocol === "https:" ? url.toString() : "";
|
|
727
|
+
} catch {
|
|
728
|
+
return "";
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function fallbackTitle(result) {
|
|
732
|
+
if (result.status === "failed") return "Couldn\u2019t complete this action";
|
|
733
|
+
if (result.status === "rejected") return "Action not approved";
|
|
734
|
+
return "Action completed";
|
|
735
|
+
}
|
|
736
|
+
function bookingPresenter(kind) {
|
|
737
|
+
return {
|
|
738
|
+
kind,
|
|
739
|
+
present: ({ envelope }) => {
|
|
740
|
+
const card = bookingCardFromActionOutput(envelope.output);
|
|
741
|
+
return card && card.type === kind ? { kind: "booking", card } : null;
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function asRecord3(value) {
|
|
746
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
747
|
+
}
|
|
748
|
+
function parseFacts(value) {
|
|
749
|
+
if (!Array.isArray(value)) return [];
|
|
750
|
+
return value.flatMap((item) => {
|
|
751
|
+
const fact = asRecord3(item);
|
|
752
|
+
const label = safeText(fact?.label);
|
|
753
|
+
const factValue = safeText(fact?.value);
|
|
754
|
+
if (!label || !factValue) return [];
|
|
755
|
+
if (INTERNAL_FACT_LABEL.test(label)) return [];
|
|
756
|
+
return [{ label, value: factValue }];
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
function parseLinks(output, defaultLabel) {
|
|
760
|
+
const href = safeHref(output.href);
|
|
761
|
+
const linkLabel = safeText(output.linkLabel) || defaultLabel;
|
|
762
|
+
return href ? [{ label: linkLabel, href }] : [];
|
|
763
|
+
}
|
|
764
|
+
function entityResultPresenter() {
|
|
765
|
+
return {
|
|
766
|
+
kind: "entity_result",
|
|
767
|
+
present: ({ envelope, result }) => {
|
|
768
|
+
const output = asRecord3(envelope.output);
|
|
769
|
+
if (!output) return null;
|
|
770
|
+
const title = safeText(output.title) || fallbackTitle(result);
|
|
771
|
+
const description = safeText(output.description);
|
|
772
|
+
const facts = parseFacts(output.facts).filter((fact) => fact.value !== description && fact.value !== title).slice(0, MAX_DETAILS);
|
|
773
|
+
return {
|
|
774
|
+
kind: "entity",
|
|
775
|
+
title,
|
|
776
|
+
...description && description !== title ? { description } : {},
|
|
777
|
+
...facts.length ? { details: facts } : {},
|
|
778
|
+
...parseLinks(output, "Open record").length ? { links: parseLinks(output, "Open record") } : {}
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
function collectionResultPresenter() {
|
|
784
|
+
return {
|
|
785
|
+
kind: "collection",
|
|
786
|
+
present: ({ envelope, result }) => {
|
|
787
|
+
const output = asRecord3(envelope.output);
|
|
788
|
+
if (!output) return null;
|
|
789
|
+
const title = safeText(output.title) || fallbackTitleFromKind("collection");
|
|
790
|
+
const items = Array.isArray(output.items) ? output.items.slice(0, MAX_COLLECTION_ITEMS).flatMap((item) => {
|
|
791
|
+
const entry = asRecord3(item);
|
|
792
|
+
if (!entry) return [];
|
|
793
|
+
const itemTitle = safeText(entry.title);
|
|
794
|
+
if (!itemTitle) return [];
|
|
795
|
+
const description = safeText(entry.description);
|
|
796
|
+
const details = parseFacts(entry.facts).slice(0, MAX_DETAILS);
|
|
797
|
+
const href = safeHref(entry.href);
|
|
798
|
+
return [
|
|
799
|
+
{
|
|
800
|
+
title: itemTitle,
|
|
801
|
+
...description ? { description } : {},
|
|
802
|
+
...details.length ? { details } : {},
|
|
803
|
+
...href ? { href } : {}
|
|
804
|
+
}
|
|
805
|
+
];
|
|
806
|
+
}) : [];
|
|
807
|
+
return {
|
|
808
|
+
kind: "collection",
|
|
809
|
+
title,
|
|
810
|
+
items
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
function signatureResultPresenter() {
|
|
816
|
+
return {
|
|
817
|
+
kind: "signature",
|
|
818
|
+
present: ({ envelope, result }) => {
|
|
819
|
+
const output = asRecord3(envelope.output);
|
|
820
|
+
if (!output) return null;
|
|
821
|
+
const title = safeText(output.title) || fallbackTitleFromKind("signature");
|
|
822
|
+
const description = safeText(output.description);
|
|
823
|
+
const statusLabel = safeText(output.status);
|
|
824
|
+
return {
|
|
825
|
+
kind: "signature",
|
|
826
|
+
title,
|
|
827
|
+
...description ? { description } : {},
|
|
828
|
+
...statusLabel ? { statusLabel } : {},
|
|
829
|
+
...parseLinks(output, "Sign now").length ? { links: parseLinks(output, "Sign now") } : {}
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
function resultCardPresenter(kind) {
|
|
835
|
+
return {
|
|
836
|
+
kind,
|
|
837
|
+
present: ({ envelope, result }) => {
|
|
838
|
+
const output = asRecord3(envelope.output);
|
|
839
|
+
if (!output) return null;
|
|
840
|
+
const title = safeText(output.title) || fallbackTitleFromKind(kind);
|
|
841
|
+
const description = safeText(output.description);
|
|
842
|
+
const facts = parseFacts(output.facts).slice(0, MAX_DETAILS);
|
|
843
|
+
return {
|
|
844
|
+
kind: "summary",
|
|
845
|
+
title,
|
|
846
|
+
...description && description !== title ? { description } : {},
|
|
847
|
+
...facts.length ? { details: facts } : {},
|
|
848
|
+
...parseLinks(output, "Open").length ? { links: parseLinks(output, "Open") } : {}
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function fallbackTitleFromKind(kind) {
|
|
854
|
+
if (kind === "document") return "Document ready";
|
|
855
|
+
if (kind === "signature") return "Ready to sign";
|
|
856
|
+
if (kind === "payment") return "Payment link";
|
|
857
|
+
if (kind === "collection") return "Results";
|
|
858
|
+
if (kind === "confirmation_result") return "Confirmed";
|
|
859
|
+
return "Saved";
|
|
860
|
+
}
|
|
861
|
+
var builtInVisitorToolResultRegistry = [
|
|
862
|
+
bookingPresenter("booking_offer"),
|
|
863
|
+
bookingPresenter("booking_confirmed"),
|
|
864
|
+
bookingPresenter("booking_canceled"),
|
|
865
|
+
entityResultPresenter(),
|
|
866
|
+
collectionResultPresenter(),
|
|
867
|
+
signatureResultPresenter(),
|
|
868
|
+
resultCardPresenter("document"),
|
|
869
|
+
resultCardPresenter("payment"),
|
|
870
|
+
resultCardPresenter("confirmation_result")
|
|
871
|
+
];
|
|
872
|
+
function resolvePresenterOutput(result, envelope, registry) {
|
|
873
|
+
const presenters = [...registry, ...builtInVisitorToolResultRegistry];
|
|
874
|
+
for (const presentationKind of envelope.presentationKinds) {
|
|
875
|
+
const presenter = presenters.find(
|
|
876
|
+
(candidate) => candidate.kind === presentationKind
|
|
877
|
+
);
|
|
878
|
+
if (!presenter) continue;
|
|
879
|
+
try {
|
|
880
|
+
const presented = presenter.present({
|
|
881
|
+
envelope,
|
|
882
|
+
presentationKind,
|
|
883
|
+
result
|
|
884
|
+
});
|
|
885
|
+
if (presented) return presented;
|
|
886
|
+
} catch {
|
|
887
|
+
return null;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
function finalizeSummaryPresentation(result, proposed) {
|
|
893
|
+
const title = safeText(proposed.title) || fallbackTitle(result);
|
|
894
|
+
const description = safeText(proposed.description);
|
|
895
|
+
const details = proposed.details?.slice(0, MAX_DETAILS).flatMap((detail) => {
|
|
896
|
+
const label = safeText(detail.label);
|
|
897
|
+
const value = safeText(detail.value);
|
|
898
|
+
if (!label || !value) return [];
|
|
899
|
+
if (INTERNAL_FACT_LABEL.test(label)) return [];
|
|
900
|
+
if (value === title || value === description) return [];
|
|
901
|
+
return [{ label, value }];
|
|
902
|
+
});
|
|
903
|
+
const links = proposed.links?.slice(0, MAX_LINKS).flatMap((link) => {
|
|
904
|
+
const label = safeText(link.label);
|
|
905
|
+
const href = safeHref(link.href);
|
|
906
|
+
return label && href ? [{ label, href }] : [];
|
|
907
|
+
});
|
|
908
|
+
return {
|
|
909
|
+
id: result.callId,
|
|
910
|
+
toolName: result.toolName,
|
|
911
|
+
status: result.status,
|
|
912
|
+
kind: "summary",
|
|
913
|
+
title,
|
|
914
|
+
...description && description !== title ? { description } : {},
|
|
915
|
+
...details?.length ? { details } : {},
|
|
916
|
+
...links?.length ? { links } : {}
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
function presentVisitorToolResult(result, registry = []) {
|
|
920
|
+
const envelope = parseAgentToolResultEnvelope(result.output) ?? legacyBookingEnvelope(result.output);
|
|
921
|
+
const proposed = envelope ? resolvePresenterOutput(result, envelope, registry) : null;
|
|
922
|
+
if (proposed?.kind === "booking") {
|
|
923
|
+
return {
|
|
924
|
+
id: result.callId,
|
|
925
|
+
toolName: result.toolName,
|
|
926
|
+
status: result.status,
|
|
927
|
+
kind: "booking",
|
|
928
|
+
card: proposed.card
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
if (envelope?.ui && envelope.presentationKinds.includes("tool_input")) {
|
|
932
|
+
return {
|
|
933
|
+
id: result.callId,
|
|
934
|
+
toolName: result.toolName,
|
|
935
|
+
status: result.status,
|
|
936
|
+
kind: "hidden"
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
if (!proposed) {
|
|
940
|
+
if (result.status === "failed" || result.status === "rejected") {
|
|
941
|
+
return {
|
|
942
|
+
id: result.callId,
|
|
943
|
+
toolName: result.toolName,
|
|
944
|
+
status: result.status,
|
|
945
|
+
kind: "summary",
|
|
946
|
+
title: fallbackTitle(result)
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
id: result.callId,
|
|
951
|
+
toolName: result.toolName,
|
|
952
|
+
status: result.status,
|
|
953
|
+
kind: "hidden"
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
if (proposed.kind === "entity") {
|
|
957
|
+
return {
|
|
958
|
+
id: result.callId,
|
|
959
|
+
toolName: result.toolName,
|
|
960
|
+
status: result.status,
|
|
961
|
+
...proposed
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
if (proposed.kind === "collection") {
|
|
965
|
+
return {
|
|
966
|
+
id: result.callId,
|
|
967
|
+
toolName: result.toolName,
|
|
968
|
+
status: result.status,
|
|
969
|
+
...proposed
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
if (proposed.kind === "signature") {
|
|
973
|
+
return {
|
|
974
|
+
id: result.callId,
|
|
975
|
+
toolName: result.toolName,
|
|
976
|
+
status: result.status,
|
|
977
|
+
...proposed
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
return finalizeSummaryPresentation(result, proposed);
|
|
981
|
+
}
|
|
982
|
+
function legacyBookingEnvelope(output) {
|
|
983
|
+
const card = bookingCardFromActionOutput(output);
|
|
984
|
+
return card ? {
|
|
985
|
+
schemaVersion: "webless.tool-result.v1",
|
|
986
|
+
output: card,
|
|
987
|
+
presentationKinds: [card.type]
|
|
988
|
+
} : null;
|
|
989
|
+
}
|
|
990
|
+
|
|
1
991
|
// src/react/hooks/useAgentChat.ts
|
|
2
992
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
993
|
|
|
@@ -10,11 +1000,31 @@ import {
|
|
|
10
1000
|
// src/runtime/capability.ts
|
|
11
1001
|
import { ClientError } from "eve/client";
|
|
12
1002
|
var MAX_REFRESH_SKEW_MS = 3e4;
|
|
13
|
-
|
|
1003
|
+
var LOCAL_LOOPBACK_ORIGINS = [
|
|
1004
|
+
"http://127.0.0.1:3010",
|
|
1005
|
+
"http://127.0.0.1:3001"
|
|
1006
|
+
];
|
|
1007
|
+
function isLoopbackRuntimeOrigin(origin) {
|
|
1008
|
+
try {
|
|
1009
|
+
const host = new URL(origin).hostname;
|
|
1010
|
+
return host === "127.0.0.1" || host === "localhost";
|
|
1011
|
+
} catch {
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function localBootstrapOrigins(origin) {
|
|
1016
|
+
const normalized = origin.replace(/\/$/, "");
|
|
1017
|
+
if (!isLoopbackRuntimeOrigin(normalized)) return [normalized];
|
|
1018
|
+
return [
|
|
1019
|
+
normalized,
|
|
1020
|
+
...LOCAL_LOOPBACK_ORIGINS.filter((candidate) => candidate !== normalized)
|
|
1021
|
+
];
|
|
1022
|
+
}
|
|
1023
|
+
function isRecord3(value) {
|
|
14
1024
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15
1025
|
}
|
|
16
1026
|
function parseBootstrapResponse(value, indexId, now) {
|
|
17
|
-
if (!
|
|
1027
|
+
if (!isRecord3(value) || value.apiVersion !== "webless.ai/agent-runtime-bootstrap/v1" || typeof value.accessToken !== "string" || !value.accessToken || typeof value.expiresAt !== "string" || !isRecord3(value.identity) || value.identity.indexId !== indexId || typeof value.identity.revision !== "string" || !value.identity.revision || typeof value.identity.tenantId !== "string" || !value.identity.tenantId || typeof value.origin !== "string" || !value.origin || value.tokenType !== "Bearer" || typeof value.visitorSubject !== "string" || !value.visitorSubject) {
|
|
18
1028
|
throw new Error("Agent Runtime returned an invalid access response.");
|
|
19
1029
|
}
|
|
20
1030
|
const expiresAt = Date.parse(value.expiresAt);
|
|
@@ -34,7 +1044,7 @@ async function readBootstrapError(response) {
|
|
|
34
1044
|
const fallback = `Agent Runtime is unavailable (${response.status}).`;
|
|
35
1045
|
try {
|
|
36
1046
|
const value = await response.json();
|
|
37
|
-
return
|
|
1047
|
+
return isRecord3(value) && typeof value.error === "string" && value.error ? value.error : fallback;
|
|
38
1048
|
} catch {
|
|
39
1049
|
return fallback;
|
|
40
1050
|
}
|
|
@@ -56,20 +1066,31 @@ function createAgentRuntimeCapability(options) {
|
|
|
56
1066
|
);
|
|
57
1067
|
}
|
|
58
1068
|
}
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
1069
|
+
const bootstrapBody = JSON.stringify({
|
|
1070
|
+
clientSessionId: options.visitorSessionId,
|
|
1071
|
+
indexId: options.indexId,
|
|
1072
|
+
...previewBuildId ? { previewBuildId } : {},
|
|
1073
|
+
...previewGrant ? { previewGrant } : {},
|
|
1074
|
+
version: options.version
|
|
1075
|
+
});
|
|
1076
|
+
const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
|
|
1077
|
+
body: bootstrapBody,
|
|
1078
|
+
headers: { "content-type": "application/json" },
|
|
1079
|
+
method: "POST"
|
|
1080
|
+
});
|
|
1081
|
+
let response;
|
|
1082
|
+
let lastError;
|
|
1083
|
+
for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
|
|
1084
|
+
try {
|
|
1085
|
+
response = await postBootstrap(origin);
|
|
1086
|
+
break;
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
lastError = error;
|
|
71
1089
|
}
|
|
72
|
-
|
|
1090
|
+
}
|
|
1091
|
+
if (!response) {
|
|
1092
|
+
throw lastError instanceof Error ? lastError : new Error("Agent Runtime is unavailable.");
|
|
1093
|
+
}
|
|
73
1094
|
if (!response.ok) {
|
|
74
1095
|
throw new Error(await readBootstrapError(response));
|
|
75
1096
|
}
|
|
@@ -243,12 +1264,8 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
243
1264
|
if (event.type === "message.completed") {
|
|
244
1265
|
handlers.onComplete?.();
|
|
245
1266
|
}
|
|
246
|
-
if (event.type === "action.result")
|
|
247
|
-
|
|
248
|
-
if (result && typeof result === "object" && "output" in result) {
|
|
249
|
-
handlers.onActionResult?.(result.output);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
1267
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
1268
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
252
1269
|
if (event.type !== "message.appended") return rendered;
|
|
253
1270
|
const { messageDelta, messageSoFar } = event.data;
|
|
254
1271
|
let delta = messageDelta;
|
|
@@ -259,9 +1276,45 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
259
1276
|
} else if (messageDelta) {
|
|
260
1277
|
next += messageDelta;
|
|
261
1278
|
}
|
|
262
|
-
if (delta) handlers.onDelta(delta);
|
|
1279
|
+
if (delta) handlers.onDelta?.(delta);
|
|
263
1280
|
return next;
|
|
264
1281
|
}
|
|
1282
|
+
function emitActionResult(event, handlers) {
|
|
1283
|
+
const result = event.data.result;
|
|
1284
|
+
if (result.kind === "tool-result") {
|
|
1285
|
+
handlers.onToolResult?.({
|
|
1286
|
+
callId: result.callId,
|
|
1287
|
+
toolName: result.toolName,
|
|
1288
|
+
status: event.data.status,
|
|
1289
|
+
output: result.output,
|
|
1290
|
+
...event.data.error ? { error: event.data.error } : {}
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
if ("output" in result) handlers.onActionResult?.(result.output);
|
|
1294
|
+
}
|
|
1295
|
+
function emitInputRequests(event, handlers) {
|
|
1296
|
+
handlers.onInputRequest?.(
|
|
1297
|
+
event.data.requests.map((request) => {
|
|
1298
|
+
const ui = parseAgentToolUiSurface(
|
|
1299
|
+
request.ui
|
|
1300
|
+
);
|
|
1301
|
+
return {
|
|
1302
|
+
requestId: request.requestId,
|
|
1303
|
+
kind: request.kind,
|
|
1304
|
+
prompt: request.prompt,
|
|
1305
|
+
...request.display ? { display: request.display } : {},
|
|
1306
|
+
...request.allowFreeform !== void 0 ? { allowFreeform: request.allowFreeform } : {},
|
|
1307
|
+
...request.options ? { options: request.options } : {},
|
|
1308
|
+
...ui ? { ui } : {},
|
|
1309
|
+
action: {
|
|
1310
|
+
callId: request.action.callId,
|
|
1311
|
+
kind: "tool-call",
|
|
1312
|
+
toolName: request.action.toolName
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
})
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
265
1318
|
function isResumeTurnMessage(received, candidate) {
|
|
266
1319
|
if (received === candidate) return true;
|
|
267
1320
|
return Boolean(candidate) && received.endsWith(`
|
|
@@ -537,9 +1590,11 @@ var AgentSession = class {
|
|
|
537
1590
|
let streamIndex = session?.state.streamIndex ?? 0;
|
|
538
1591
|
let rendered = "";
|
|
539
1592
|
const workItems = /* @__PURE__ */ new Map();
|
|
1593
|
+
let requestedInput = false;
|
|
540
1594
|
try {
|
|
541
1595
|
for await (const event of response) {
|
|
542
1596
|
if (signal.aborted) break;
|
|
1597
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
543
1598
|
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
544
1599
|
streamIndex += 1;
|
|
545
1600
|
if (session) {
|
|
@@ -557,7 +1612,7 @@ var AgentSession = class {
|
|
|
557
1612
|
this.persistSessionCursor(session);
|
|
558
1613
|
}
|
|
559
1614
|
}
|
|
560
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
1615
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
561
1616
|
throw new Error("Empty response from runtime");
|
|
562
1617
|
}
|
|
563
1618
|
return rendered.trim();
|
|
@@ -577,6 +1632,9 @@ var AgentSession = class {
|
|
|
577
1632
|
() => attached.snapshot({ signal })
|
|
578
1633
|
);
|
|
579
1634
|
const turnEvents = latestTurnEvents(snapshot.events);
|
|
1635
|
+
const hasInputRequest = turnEvents.some(
|
|
1636
|
+
(event) => event.type === "input.requested"
|
|
1637
|
+
);
|
|
580
1638
|
const received = turnEvents[0];
|
|
581
1639
|
const lastSent = persisted.lastMessage;
|
|
582
1640
|
const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
|
|
@@ -587,6 +1645,8 @@ var AgentSession = class {
|
|
|
587
1645
|
const workItems = /* @__PURE__ */ new Map();
|
|
588
1646
|
for (const event of turnEvents) {
|
|
589
1647
|
applyWorkEvent(event, handlers, workItems);
|
|
1648
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
1649
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
590
1650
|
}
|
|
591
1651
|
if (rendered.startsWith(initialText)) {
|
|
592
1652
|
const missedText = rendered.slice(initialText.length);
|
|
@@ -616,7 +1676,9 @@ var AgentSession = class {
|
|
|
616
1676
|
);
|
|
617
1677
|
}
|
|
618
1678
|
handlers.onComplete?.();
|
|
619
|
-
if (!rendered.trim()
|
|
1679
|
+
if (!rendered.trim() && !hasInputRequest) {
|
|
1680
|
+
throw new Error("Empty response from runtime");
|
|
1681
|
+
}
|
|
620
1682
|
return rendered.trim();
|
|
621
1683
|
}
|
|
622
1684
|
let streamIndex = snapshot.session.streamIndex;
|
|
@@ -635,7 +1697,55 @@ var AgentSession = class {
|
|
|
635
1697
|
session = client.sessions.attach(session.state.sessionId, { streamIndex });
|
|
636
1698
|
this.session = session;
|
|
637
1699
|
this.persistSessionCursor(session);
|
|
638
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
1700
|
+
if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
|
|
1701
|
+
throw new Error("Empty response from runtime");
|
|
1702
|
+
}
|
|
1703
|
+
return rendered.trim();
|
|
1704
|
+
}
|
|
1705
|
+
async respondTurn(responses, signal, handlers) {
|
|
1706
|
+
const client = this.ensureClient();
|
|
1707
|
+
const session = this.session ?? this.attachPersistedSession(client);
|
|
1708
|
+
if (!session) {
|
|
1709
|
+
throw new Error("No active session is waiting for input.");
|
|
1710
|
+
}
|
|
1711
|
+
this.session = session;
|
|
1712
|
+
const inputResponses = responses.map(
|
|
1713
|
+
({ requestId, optionId, text }) => ({
|
|
1714
|
+
requestId,
|
|
1715
|
+
...optionId ? { optionId } : {},
|
|
1716
|
+
...text ? { text } : {}
|
|
1717
|
+
})
|
|
1718
|
+
);
|
|
1719
|
+
const response = await withCapabilityRefresh(
|
|
1720
|
+
this.capability,
|
|
1721
|
+
() => session.respond(inputResponses, { signal })
|
|
1722
|
+
);
|
|
1723
|
+
this.activeResponse = response;
|
|
1724
|
+
let streamIndex = session.state.streamIndex;
|
|
1725
|
+
let rendered = "";
|
|
1726
|
+
let requestedInput = false;
|
|
1727
|
+
const workItems = /* @__PURE__ */ new Map();
|
|
1728
|
+
try {
|
|
1729
|
+
for await (const event of response) {
|
|
1730
|
+
if (signal.aborted) break;
|
|
1731
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
1732
|
+
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
1733
|
+
streamIndex += 1;
|
|
1734
|
+
savePersistedAgentSession(
|
|
1735
|
+
this.visitorSessionId,
|
|
1736
|
+
session.state.sessionId,
|
|
1737
|
+
streamIndex,
|
|
1738
|
+
this.storeOptions
|
|
1739
|
+
);
|
|
1740
|
+
}
|
|
1741
|
+
} finally {
|
|
1742
|
+
this.activeResponse = void 0;
|
|
1743
|
+
this.session = client.sessions.attach(session.state.sessionId, {
|
|
1744
|
+
streamIndex
|
|
1745
|
+
});
|
|
1746
|
+
this.persistSessionCursor(this.session);
|
|
1747
|
+
}
|
|
1748
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
639
1749
|
throw new Error("Empty response from runtime");
|
|
640
1750
|
}
|
|
641
1751
|
return rendered.trim();
|
|
@@ -695,297 +1805,62 @@ function createAgentClient(options) {
|
|
|
695
1805
|
resumeOptions.handlers,
|
|
696
1806
|
resumeOptions.initialText
|
|
697
1807
|
),
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
|
|
708
|
-
function isTransientRuntimeMessage(message) {
|
|
709
|
-
const normalized = message.trim().toLowerCase();
|
|
710
|
-
return normalized.includes("empty response from runtime") || normalized.includes("failed to fetch") || normalized.includes("networkerror") || normalized.includes("load failed");
|
|
711
|
-
}
|
|
712
|
-
function isPreviewAuthorizationMessage(message) {
|
|
713
|
-
const normalized = message.trim().toLowerCase();
|
|
714
|
-
return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
|
|
715
|
-
}
|
|
716
|
-
function formatAgentError(error) {
|
|
717
|
-
if (error instanceof ClientError3) {
|
|
718
|
-
if (error.status === 401 && error.code === "index_required") {
|
|
719
|
-
return "Missing indexId \u2014 pass a published index id to createAgentClient().";
|
|
720
|
-
}
|
|
721
|
-
if (error.status === 403 && error.code === "agent_unavailable") {
|
|
722
|
-
return "Agent unavailable for this index (disabled or unpublished).";
|
|
723
|
-
}
|
|
724
|
-
if (error.status === 409 && error.code === "session_not_active") {
|
|
725
|
-
return "Session expired \u2014 send a new message to start again.";
|
|
726
|
-
}
|
|
727
|
-
if (error.message && isPreviewAuthorizationMessage(error.message)) {
|
|
728
|
-
return PREVIEW_AUTHORIZATION_ERROR_MESSAGE;
|
|
729
|
-
}
|
|
730
|
-
if (error.status >= 500 || error.message && isTransientRuntimeMessage(error.message)) {
|
|
731
|
-
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
732
|
-
}
|
|
733
|
-
return error.message || "This assistant is unavailable right now.";
|
|
734
|
-
}
|
|
735
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
736
|
-
return "";
|
|
737
|
-
}
|
|
738
|
-
if (error instanceof Error) {
|
|
739
|
-
if (isPreviewAuthorizationMessage(error.message)) {
|
|
740
|
-
return PREVIEW_AUTHORIZATION_ERROR_MESSAGE;
|
|
741
|
-
}
|
|
742
|
-
return isTransientRuntimeMessage(error.message) ? TRANSIENT_AGENT_ERROR_MESSAGE : error.message;
|
|
743
|
-
}
|
|
744
|
-
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
// src/react/lib/tool-card.ts
|
|
748
|
-
function bookingOfferIdentityKey(offer) {
|
|
749
|
-
const eventTypes = offer.eventTypes.map(
|
|
750
|
-
(item) => `${item.uri}:${item.duration ?? ""}:${item.locationKind ?? ""}:${item.location ?? ""}`
|
|
751
|
-
).join("|");
|
|
752
|
-
const slots = offer.slots.map((slot) => `${slot.eventTypeUri ?? ""}:${slot.startTime}`).join("|");
|
|
753
|
-
return `${eventTypes}::${slots}` || "offer";
|
|
754
|
-
}
|
|
755
|
-
var FENCE_PATTERN = /```(?:webless-tool-card|json)\s*([\s\S]*?)```/gi;
|
|
756
|
-
function asRecord(value) {
|
|
757
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
758
|
-
}
|
|
759
|
-
function asString(value) {
|
|
760
|
-
return typeof value === "string" ? value.trim() : "";
|
|
761
|
-
}
|
|
762
|
-
function isEventUri(value) {
|
|
763
|
-
return /^https:\/\/api\.calendly\.com\/scheduled_events\/[^/]+$/i.test(value);
|
|
764
|
-
}
|
|
765
|
-
function isEventTypeUri(value) {
|
|
766
|
-
return /^https:\/\/api\.calendly\.com\/event_types\/[^/]+$/i.test(value);
|
|
767
|
-
}
|
|
768
|
-
function parseToolCard(value) {
|
|
769
|
-
const record = asRecord(value);
|
|
770
|
-
if (!record) return null;
|
|
771
|
-
if (record.booking_offer && asString(record.type) !== "booking_offer") {
|
|
772
|
-
const nested = parseToolCard(record.booking_offer);
|
|
773
|
-
if (nested) return nested;
|
|
774
|
-
}
|
|
775
|
-
const type = asString(record.type);
|
|
776
|
-
if (type === "booking_offer") {
|
|
777
|
-
const eventTypes = Array.isArray(record.eventTypes) ? record.eventTypes.flatMap((item) => {
|
|
778
|
-
const entry = asRecord(item);
|
|
779
|
-
const uri = asString(entry?.uri);
|
|
780
|
-
if (!entry || !isEventTypeUri(uri)) return [];
|
|
781
|
-
const duration = entry.duration;
|
|
782
|
-
const locationKind = asString(entry.locationKind);
|
|
783
|
-
const location = asString(entry.location);
|
|
784
|
-
return [
|
|
785
|
-
{
|
|
786
|
-
name: asString(entry.name) || "Meeting",
|
|
787
|
-
uri,
|
|
788
|
-
...typeof duration === "number" ? { duration } : {},
|
|
789
|
-
...locationKind ? { locationKind } : {},
|
|
790
|
-
...location ? { location } : {}
|
|
791
|
-
}
|
|
792
|
-
];
|
|
793
|
-
}) : [];
|
|
794
|
-
const slots = Array.isArray(record.slots) ? record.slots.flatMap((item) => {
|
|
795
|
-
const entry = asRecord(item);
|
|
796
|
-
const startTime = asString(entry?.startTime);
|
|
797
|
-
if (!entry || !startTime) return [];
|
|
798
|
-
const eventTypeUri = asString(entry.eventTypeUri);
|
|
799
|
-
return [
|
|
800
|
-
{
|
|
801
|
-
startTime,
|
|
802
|
-
...isEventTypeUri(eventTypeUri) ? { eventTypeUri } : {}
|
|
803
|
-
}
|
|
804
|
-
];
|
|
805
|
-
}) : [];
|
|
806
|
-
if (slots.length === 0) return null;
|
|
807
|
-
return { type: "booking_offer", eventTypes, slots };
|
|
808
|
-
}
|
|
809
|
-
if (type === "booking_confirmed") {
|
|
810
|
-
const eventUri = asString(record.eventUri);
|
|
811
|
-
if (!isEventUri(eventUri)) return null;
|
|
812
|
-
const inviteeUri = asString(record.inviteeUri);
|
|
813
|
-
const inviteeEmail = asString(record.inviteeEmail);
|
|
814
|
-
const startTime = asString(record.startTime);
|
|
815
|
-
return {
|
|
816
|
-
type: "booking_confirmed",
|
|
817
|
-
eventUri,
|
|
818
|
-
...inviteeUri ? { inviteeUri } : {},
|
|
819
|
-
...inviteeEmail ? { inviteeEmail } : {},
|
|
820
|
-
...startTime ? { startTime } : {}
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
if (type === "booking_canceled") {
|
|
824
|
-
const eventUri = asString(record.eventUri);
|
|
825
|
-
if (!isEventUri(eventUri)) return null;
|
|
826
|
-
return { type: "booking_canceled", eventUri };
|
|
827
|
-
}
|
|
828
|
-
return null;
|
|
829
|
-
}
|
|
830
|
-
function formatBookingOfferFence(offer) {
|
|
831
|
-
return [
|
|
832
|
-
"```webless-tool-card",
|
|
833
|
-
JSON.stringify({
|
|
834
|
-
type: "booking_offer",
|
|
835
|
-
eventTypes: offer.eventTypes,
|
|
836
|
-
slots: offer.slots
|
|
837
|
-
}),
|
|
838
|
-
"```"
|
|
839
|
-
].join("\n");
|
|
840
|
-
}
|
|
841
|
-
function bookingOfferFromActionOutput(output) {
|
|
842
|
-
const record = asRecord(output);
|
|
843
|
-
const data = asRecord(record?.data) ?? record;
|
|
844
|
-
const card = parseToolCard(data);
|
|
845
|
-
return card?.type === "booking_offer" ? card : null;
|
|
846
|
-
}
|
|
847
|
-
function ensureBookingOfferText(text, offer) {
|
|
848
|
-
if (!offer) return text;
|
|
849
|
-
if (extractToolCards(text).some((card) => card.type === "booking_offer")) {
|
|
850
|
-
return text;
|
|
851
|
-
}
|
|
852
|
-
const visible = stripToolCards(text).trim() || text.trim();
|
|
853
|
-
return `${visible}
|
|
854
|
-
|
|
855
|
-
${formatBookingOfferFence(offer)}`;
|
|
856
|
-
}
|
|
857
|
-
function hideToolCardFences(text) {
|
|
858
|
-
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();
|
|
859
|
-
}
|
|
860
|
-
function visitorTimeZone() {
|
|
861
|
-
try {
|
|
862
|
-
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
863
|
-
} catch {
|
|
864
|
-
return "UTC";
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
function extractToolCards(text) {
|
|
868
|
-
const cards = [];
|
|
869
|
-
for (const match of text.matchAll(FENCE_PATTERN)) {
|
|
870
|
-
try {
|
|
871
|
-
const card = parseToolCard(JSON.parse(match[1] ?? ""));
|
|
872
|
-
if (card) cards.push(card);
|
|
873
|
-
} catch {
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
return cards;
|
|
877
|
-
}
|
|
878
|
-
function stripToolCards(text) {
|
|
879
|
-
return text.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
880
|
-
}
|
|
881
|
-
function localDateKey(date) {
|
|
882
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
883
|
-
return [
|
|
884
|
-
date.getFullYear(),
|
|
885
|
-
String(date.getMonth() + 1).padStart(2, "0"),
|
|
886
|
-
String(date.getDate()).padStart(2, "0")
|
|
887
|
-
].join("-");
|
|
888
|
-
}
|
|
889
|
-
function slotDateKey(startTime) {
|
|
890
|
-
return localDateKey(new Date(startTime)) || startTime;
|
|
891
|
-
}
|
|
892
|
-
function bookingSlotsForEventType(slots, eventTypeUri) {
|
|
893
|
-
return slots.filter(
|
|
894
|
-
(slot) => !eventTypeUri || !slot.eventTypeUri || slot.eventTypeUri === eventTypeUri
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
function firstAvailableBookingMonth(slots) {
|
|
898
|
-
let earliest;
|
|
899
|
-
for (const slot of slots) {
|
|
900
|
-
const key = slotDateKey(slot.startTime);
|
|
901
|
-
if (!earliest || key < earliest) earliest = key;
|
|
902
|
-
}
|
|
903
|
-
const [year, month] = (earliest ?? slotDateKey((/* @__PURE__ */ new Date()).toISOString())).split("-").map(Number);
|
|
904
|
-
if (!year || !month) {
|
|
905
|
-
const now = /* @__PURE__ */ new Date();
|
|
906
|
-
return { year: now.getFullYear(), month: now.getMonth() };
|
|
907
|
-
}
|
|
908
|
-
return { year, month: month - 1 };
|
|
909
|
-
}
|
|
910
|
-
function formatMonthTitle(year, month) {
|
|
911
|
-
return new Intl.DateTimeFormat(void 0, {
|
|
912
|
-
month: "long",
|
|
913
|
-
year: "numeric"
|
|
914
|
-
}).format(new Date(year, month, 1));
|
|
915
|
-
}
|
|
916
|
-
function formatLongDate(startTime) {
|
|
917
|
-
const date = new Date(startTime);
|
|
918
|
-
if (Number.isNaN(date.getTime())) return startTime;
|
|
919
|
-
return new Intl.DateTimeFormat(void 0, {
|
|
920
|
-
weekday: "long",
|
|
921
|
-
month: "long",
|
|
922
|
-
day: "numeric"
|
|
923
|
-
}).format(date);
|
|
924
|
-
}
|
|
925
|
-
function weekdayLabels() {
|
|
926
|
-
return Array.from(
|
|
927
|
-
{ length: 7 },
|
|
928
|
-
(_, index) => new Intl.DateTimeFormat(void 0, { weekday: "short" }).format(
|
|
929
|
-
new Date(2026, 7, 3 + index)
|
|
930
|
-
)
|
|
931
|
-
);
|
|
932
|
-
}
|
|
933
|
-
function formatTimeChip(startTime) {
|
|
934
|
-
const date = new Date(startTime);
|
|
935
|
-
if (Number.isNaN(date.getTime())) return startTime;
|
|
936
|
-
return new Intl.DateTimeFormat(void 0, {
|
|
937
|
-
hour: "numeric",
|
|
938
|
-
minute: "2-digit"
|
|
939
|
-
}).format(date);
|
|
940
|
-
}
|
|
941
|
-
function formatSlotTimeZone(startTime) {
|
|
942
|
-
const date = new Date(startTime);
|
|
943
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
944
|
-
return new Intl.DateTimeFormat(void 0, { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "";
|
|
945
|
-
}
|
|
946
|
-
function formatSlotLabel(startTime) {
|
|
947
|
-
const date = new Date(startTime);
|
|
948
|
-
if (Number.isNaN(date.getTime())) return startTime;
|
|
949
|
-
return new Intl.DateTimeFormat(void 0, {
|
|
950
|
-
weekday: "short",
|
|
951
|
-
month: "short",
|
|
952
|
-
day: "numeric",
|
|
953
|
-
hour: "numeric",
|
|
954
|
-
minute: "2-digit",
|
|
955
|
-
timeZoneName: "short"
|
|
956
|
-
}).format(date);
|
|
957
|
-
}
|
|
958
|
-
function formatBookingRequest(input) {
|
|
959
|
-
return [
|
|
960
|
-
"Book this meeting now with CALENDLY_POST_INVITEE.",
|
|
961
|
-
"Do not open a Calendly URL and do not list other scheduled events.",
|
|
962
|
-
"Do not invent a location kind. Use only the location fields below.",
|
|
963
|
-
`event_type: ${input.eventTypeUri}`,
|
|
964
|
-
`start_time: ${input.startTime}`,
|
|
965
|
-
`invitee.name: ${input.inviteeName}`,
|
|
966
|
-
`invitee.email: ${input.inviteeEmail}`,
|
|
967
|
-
`invitee.timezone: ${input.timezone}`,
|
|
968
|
-
...input.locationKind ? [
|
|
969
|
-
`location.kind: ${input.locationKind}`,
|
|
970
|
-
...input.location ? [`location.location: ${input.location}`] : []
|
|
971
|
-
] : ["Do not send a location field."],
|
|
972
|
-
"After it succeeds, reply with one short confirmation and a webless-tool-card booking_confirmed block using visitor_booking."
|
|
973
|
-
].join("\n");
|
|
1808
|
+
respondTurn: (respondOptions) => session.respondTurn(
|
|
1809
|
+
respondOptions.responses,
|
|
1810
|
+
respondOptions.signal ?? new AbortController().signal,
|
|
1811
|
+
respondOptions.handlers
|
|
1812
|
+
),
|
|
1813
|
+
reset: () => session.reset(),
|
|
1814
|
+
cancelActive: () => session.cancelActive(),
|
|
1815
|
+
getActiveSessionId: () => session.getActiveSessionId()
|
|
1816
|
+
};
|
|
974
1817
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1818
|
+
|
|
1819
|
+
// src/runtime/errors.ts
|
|
1820
|
+
import { ClientError as ClientError3 } from "eve/client";
|
|
1821
|
+
var TRANSIENT_AGENT_ERROR_MESSAGE = "I couldn\u2019t finish that answer. Please try again.";
|
|
1822
|
+
var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
|
|
1823
|
+
function isTransientRuntimeMessage(message) {
|
|
1824
|
+
const normalized = message.trim().toLowerCase();
|
|
1825
|
+
return normalized.includes("empty response from runtime") || normalized.includes("failed to fetch") || normalized.includes("networkerror") || normalized.includes("load failed");
|
|
1826
|
+
}
|
|
1827
|
+
function isPreviewAuthorizationMessage(message) {
|
|
1828
|
+
const normalized = message.trim().toLowerCase();
|
|
1829
|
+
return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
|
|
1830
|
+
}
|
|
1831
|
+
function formatAgentError(error) {
|
|
1832
|
+
if (error instanceof ClientError3) {
|
|
1833
|
+
if (error.status === 401 && error.code === "index_required") {
|
|
1834
|
+
return "Missing indexId \u2014 pass a published index id to createAgentClient().";
|
|
1835
|
+
}
|
|
1836
|
+
if (error.status === 403 && error.code === "agent_unavailable") {
|
|
1837
|
+
return "Agent unavailable for this index (disabled or unpublished).";
|
|
1838
|
+
}
|
|
1839
|
+
if (error.status === 409 && error.code === "session_not_active") {
|
|
1840
|
+
return "Session expired \u2014 send a new message to start again.";
|
|
1841
|
+
}
|
|
1842
|
+
if (error.message && isPreviewAuthorizationMessage(error.message)) {
|
|
1843
|
+
return PREVIEW_AUTHORIZATION_ERROR_MESSAGE;
|
|
1844
|
+
}
|
|
1845
|
+
if (error.status >= 500 || error.message && isTransientRuntimeMessage(error.message)) {
|
|
1846
|
+
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
1847
|
+
}
|
|
1848
|
+
return error.message || "This assistant is unavailable right now.";
|
|
1849
|
+
}
|
|
1850
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1851
|
+
return "";
|
|
1852
|
+
}
|
|
1853
|
+
if (error instanceof Error) {
|
|
1854
|
+
if (isPreviewAuthorizationMessage(error.message)) {
|
|
1855
|
+
return PREVIEW_AUTHORIZATION_ERROR_MESSAGE;
|
|
1856
|
+
}
|
|
1857
|
+
return isTransientRuntimeMessage(error.message) ? TRANSIENT_AGENT_ERROR_MESSAGE : error.message;
|
|
1858
|
+
}
|
|
1859
|
+
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
985
1860
|
}
|
|
986
1861
|
|
|
987
1862
|
// src/react/persisted-conversation.ts
|
|
988
|
-
var CONVERSATION_VERSION =
|
|
1863
|
+
var CONVERSATION_VERSION = 3;
|
|
989
1864
|
function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
990
1865
|
return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
|
|
991
1866
|
}
|
|
@@ -1025,30 +1900,171 @@ function parseToolStep(value) {
|
|
|
1025
1900
|
...typeof record.detail === "string" && record.detail ? { detail: record.detail } : {}
|
|
1026
1901
|
};
|
|
1027
1902
|
}
|
|
1903
|
+
function parseInputOption(value) {
|
|
1904
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1905
|
+
const record = value;
|
|
1906
|
+
if (typeof record.id !== "string" || typeof record.label !== "string") {
|
|
1907
|
+
return null;
|
|
1908
|
+
}
|
|
1909
|
+
return {
|
|
1910
|
+
id: record.id,
|
|
1911
|
+
label: record.label,
|
|
1912
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
1913
|
+
...record.style === "default" || record.style === "primary" || record.style === "danger" ? { style: record.style } : {}
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
function parseInputRequest(value) {
|
|
1917
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1918
|
+
const record = value;
|
|
1919
|
+
if (typeof record.requestId !== "string" || record.kind !== "question" && record.kind !== "session-limit" && record.kind !== "tool-approval" || typeof record.prompt !== "string") {
|
|
1920
|
+
return null;
|
|
1921
|
+
}
|
|
1922
|
+
const action = record.action;
|
|
1923
|
+
if (typeof action !== "object" || action === null) return null;
|
|
1924
|
+
const actionRecord = action;
|
|
1925
|
+
if (typeof actionRecord.callId !== "string" || actionRecord.kind !== "tool-call" || typeof actionRecord.toolName !== "string") {
|
|
1926
|
+
return null;
|
|
1927
|
+
}
|
|
1928
|
+
const options = Array.isArray(record.options) ? record.options.map(parseInputOption) : void 0;
|
|
1929
|
+
if (Array.isArray(record.options) && options?.some((option) => option === null)) {
|
|
1930
|
+
return null;
|
|
1931
|
+
}
|
|
1932
|
+
const ui = record.ui ? parseAgentToolUiSurface(record.ui) : void 0;
|
|
1933
|
+
if (record.ui && !ui) return null;
|
|
1934
|
+
return {
|
|
1935
|
+
requestId: record.requestId,
|
|
1936
|
+
kind: record.kind,
|
|
1937
|
+
prompt: record.prompt,
|
|
1938
|
+
action: {
|
|
1939
|
+
callId: actionRecord.callId,
|
|
1940
|
+
kind: "tool-call",
|
|
1941
|
+
toolName: actionRecord.toolName
|
|
1942
|
+
},
|
|
1943
|
+
...record.display === "confirmation" || record.display === "select" || record.display === "text" ? { display: record.display } : {},
|
|
1944
|
+
...record.allowFreeform === true ? { allowFreeform: true } : {},
|
|
1945
|
+
...options && options.length > 0 ? {
|
|
1946
|
+
options: options.filter(
|
|
1947
|
+
(option) => option !== null
|
|
1948
|
+
)
|
|
1949
|
+
} : {},
|
|
1950
|
+
...ui ? { ui } : {}
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
function parseToolResult(value) {
|
|
1954
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1955
|
+
const record = value;
|
|
1956
|
+
if (typeof record.id !== "string" || typeof record.toolName !== "string" || record.status !== "completed" && record.status !== "failed" && record.status !== "rejected") {
|
|
1957
|
+
return null;
|
|
1958
|
+
}
|
|
1959
|
+
if (record.kind === "input") {
|
|
1960
|
+
const surface = parseAgentToolUiSurface(
|
|
1961
|
+
record.surface
|
|
1962
|
+
);
|
|
1963
|
+
return surface ? {
|
|
1964
|
+
id: record.id,
|
|
1965
|
+
toolName: record.toolName,
|
|
1966
|
+
status: record.status,
|
|
1967
|
+
kind: "input",
|
|
1968
|
+
surface
|
|
1969
|
+
} : null;
|
|
1970
|
+
}
|
|
1971
|
+
if (record.kind === "entity" && typeof record.title === "string") {
|
|
1972
|
+
return {
|
|
1973
|
+
id: record.id,
|
|
1974
|
+
toolName: record.toolName,
|
|
1975
|
+
status: record.status,
|
|
1976
|
+
kind: "entity",
|
|
1977
|
+
title: record.title,
|
|
1978
|
+
...typeof record.description === "string" ? { description: record.description } : {}
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
if (record.kind === "collection" && typeof record.title === "string" && Array.isArray(record.items)) {
|
|
1982
|
+
return {
|
|
1983
|
+
id: record.id,
|
|
1984
|
+
toolName: record.toolName,
|
|
1985
|
+
status: record.status,
|
|
1986
|
+
kind: "collection",
|
|
1987
|
+
title: record.title,
|
|
1988
|
+
items: record.items.flatMap((item) => {
|
|
1989
|
+
if (typeof item !== "object" || item === null) return [];
|
|
1990
|
+
const entry = item;
|
|
1991
|
+
if (typeof entry.title !== "string") return [];
|
|
1992
|
+
return [
|
|
1993
|
+
{
|
|
1994
|
+
title: entry.title,
|
|
1995
|
+
...typeof entry.description === "string" ? { description: entry.description } : {},
|
|
1996
|
+
...typeof entry.href === "string" ? { href: entry.href } : {}
|
|
1997
|
+
}
|
|
1998
|
+
];
|
|
1999
|
+
})
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
if (record.kind === "signature" && typeof record.title === "string") {
|
|
2003
|
+
return {
|
|
2004
|
+
id: record.id,
|
|
2005
|
+
toolName: record.toolName,
|
|
2006
|
+
status: record.status,
|
|
2007
|
+
kind: "signature",
|
|
2008
|
+
title: record.title,
|
|
2009
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
2010
|
+
...typeof record.statusLabel === "string" ? { statusLabel: record.statusLabel } : {}
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
if (record.kind !== "summary" || typeof record.title !== "string")
|
|
2014
|
+
return null;
|
|
2015
|
+
return {
|
|
2016
|
+
id: record.id,
|
|
2017
|
+
toolName: record.toolName,
|
|
2018
|
+
status: record.status,
|
|
2019
|
+
kind: "summary",
|
|
2020
|
+
title: record.title,
|
|
2021
|
+
...typeof record.description === "string" ? { description: record.description } : {}
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
1028
2024
|
function visitorTurnText(message) {
|
|
1029
2025
|
return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
|
|
1030
2026
|
}
|
|
1031
2027
|
function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
1032
2028
|
if (typeof sessionStorage === "undefined") return null;
|
|
1033
|
-
const raw = sessionStorage.getItem(
|
|
2029
|
+
const raw = sessionStorage.getItem(
|
|
2030
|
+
conversationKey(storageKeyPrefix, visitorSessionId)
|
|
2031
|
+
);
|
|
1034
2032
|
if (!raw) return null;
|
|
1035
2033
|
try {
|
|
1036
2034
|
const value = JSON.parse(raw);
|
|
1037
2035
|
if (typeof value !== "object" || value === null) return null;
|
|
1038
2036
|
const record = value;
|
|
1039
|
-
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)) {
|
|
2037
|
+
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)) {
|
|
1040
2038
|
return null;
|
|
1041
2039
|
}
|
|
1042
2040
|
const messages = record.messages.map(parseMessage);
|
|
1043
2041
|
if (messages.some((message) => message === null)) return null;
|
|
1044
|
-
const storedToolSteps = record.version === CONVERSATION_VERSION && Array.isArray(record.toolSteps) ? record.toolSteps : [];
|
|
2042
|
+
const storedToolSteps = (record.version === 2 || record.version === CONVERSATION_VERSION) && Array.isArray(record.toolSteps) ? record.toolSteps : [];
|
|
1045
2043
|
const toolSteps = storedToolSteps.map(parseToolStep);
|
|
1046
2044
|
if (toolSteps.some((step) => step === null)) return null;
|
|
2045
|
+
const storedToolResults = record.version === CONVERSATION_VERSION && Array.isArray(record.toolResults) ? record.toolResults : [];
|
|
2046
|
+
const toolResults = storedToolResults.map(parseToolResult);
|
|
2047
|
+
if (toolResults.some((result) => result === null)) return null;
|
|
2048
|
+
const storedPendingInputs = Array.isArray(record.pendingInputs) ? record.pendingInputs : [];
|
|
2049
|
+
const pendingInputs = storedPendingInputs.map(parseInputRequest);
|
|
2050
|
+
if (pendingInputs.some((request) => request === null)) return null;
|
|
1047
2051
|
return {
|
|
1048
|
-
messages: messages.filter(
|
|
2052
|
+
messages: messages.filter(
|
|
2053
|
+
(message) => message !== null
|
|
2054
|
+
),
|
|
1049
2055
|
pending: record.pending,
|
|
1050
2056
|
streamingText: record.streamingText,
|
|
1051
|
-
toolSteps: toolSteps.filter((step) => step !== null)
|
|
2057
|
+
toolSteps: toolSteps.filter((step) => step !== null),
|
|
2058
|
+
...Array.isArray(record.toolResults) ? {
|
|
2059
|
+
toolResults: toolResults.filter(
|
|
2060
|
+
(result) => result !== null
|
|
2061
|
+
)
|
|
2062
|
+
} : {},
|
|
2063
|
+
...pendingInputs.length > 0 ? {
|
|
2064
|
+
pendingInputs: pendingInputs.filter(
|
|
2065
|
+
(request) => request !== null
|
|
2066
|
+
)
|
|
2067
|
+
} : {}
|
|
1052
2068
|
};
|
|
1053
2069
|
} catch {
|
|
1054
2070
|
return null;
|
|
@@ -1063,7 +2079,9 @@ function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conv
|
|
|
1063
2079
|
}
|
|
1064
2080
|
function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
1065
2081
|
if (typeof sessionStorage === "undefined") return;
|
|
1066
|
-
sessionStorage.removeItem(
|
|
2082
|
+
sessionStorage.removeItem(
|
|
2083
|
+
conversationKey(storageKeyPrefix, visitorSessionId)
|
|
2084
|
+
);
|
|
1067
2085
|
clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId);
|
|
1068
2086
|
}
|
|
1069
2087
|
function pendingBookingKey(storageKeyPrefix, visitorSessionId) {
|
|
@@ -1099,7 +2117,76 @@ function savePendingWidgetBooking(storageKeyPrefix, visitorSessionId, booking) {
|
|
|
1099
2117
|
}
|
|
1100
2118
|
function clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
|
|
1101
2119
|
if (typeof sessionStorage === "undefined") return;
|
|
1102
|
-
sessionStorage.removeItem(
|
|
2120
|
+
sessionStorage.removeItem(
|
|
2121
|
+
pendingBookingKey(storageKeyPrefix, visitorSessionId)
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
// src/react/lib/visitor-input.ts
|
|
2126
|
+
function shouldRenderVisitorInputCard(request) {
|
|
2127
|
+
if (request.kind === "tool-approval" || request.kind === "session-limit") {
|
|
2128
|
+
return true;
|
|
2129
|
+
}
|
|
2130
|
+
if (request.kind === "question") {
|
|
2131
|
+
return (request.options?.length ?? 0) > 0;
|
|
2132
|
+
}
|
|
2133
|
+
return false;
|
|
2134
|
+
}
|
|
2135
|
+
function isChatCollectibleInputRequest(request) {
|
|
2136
|
+
return request.kind === "question" && (request.options?.length ?? 0) === 0;
|
|
2137
|
+
}
|
|
2138
|
+
function appendChatCollectiblePrompts(messages, requests) {
|
|
2139
|
+
const next = [...messages];
|
|
2140
|
+
for (const request of requests.filter(isChatCollectibleInputRequest)) {
|
|
2141
|
+
const prompt = request.prompt.trim();
|
|
2142
|
+
if (!prompt) continue;
|
|
2143
|
+
const last = next.at(-1);
|
|
2144
|
+
if (last?.role === "agent" && last.text.trim() === prompt) continue;
|
|
2145
|
+
next.push({
|
|
2146
|
+
id: `agent-input-${request.requestId}`,
|
|
2147
|
+
role: "agent",
|
|
2148
|
+
text: prompt,
|
|
2149
|
+
createdAt: Date.now()
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
return next;
|
|
2153
|
+
}
|
|
2154
|
+
function chatInputResponseForText(requests, text) {
|
|
2155
|
+
const trimmed = text.trim();
|
|
2156
|
+
if (!trimmed) return null;
|
|
2157
|
+
const pending = requests.find(isChatCollectibleInputRequest);
|
|
2158
|
+
if (!pending) return null;
|
|
2159
|
+
return { requestId: pending.requestId, text: trimmed };
|
|
2160
|
+
}
|
|
2161
|
+
function normalizeAssistantDedupeKey(text) {
|
|
2162
|
+
return text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
2163
|
+
}
|
|
2164
|
+
function isNearDuplicateAssistantText(left, right) {
|
|
2165
|
+
const a = normalizeAssistantDedupeKey(left);
|
|
2166
|
+
const b = normalizeAssistantDedupeKey(right);
|
|
2167
|
+
if (!a || !b) return false;
|
|
2168
|
+
if (a === b) return true;
|
|
2169
|
+
const shorter = a.length <= b.length ? a : b;
|
|
2170
|
+
const longer = a.length <= b.length ? b : a;
|
|
2171
|
+
if (shorter.length < 40) return false;
|
|
2172
|
+
return longer.startsWith(
|
|
2173
|
+
shorter.slice(0, Math.floor(shorter.length * 0.85))
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
function appendAgentTurnMessage(messages, displayText) {
|
|
2177
|
+
const trimmed = displayText.trim();
|
|
2178
|
+
if (!trimmed) return [...messages];
|
|
2179
|
+
const agentMessage = {
|
|
2180
|
+
id: `agent-${Date.now()}`,
|
|
2181
|
+
role: "agent",
|
|
2182
|
+
text: trimmed,
|
|
2183
|
+
createdAt: Date.now()
|
|
2184
|
+
};
|
|
2185
|
+
const last = messages.at(-1);
|
|
2186
|
+
if (last?.role === "agent" && (last.id.startsWith("agent-input-") || isNearDuplicateAssistantText(last.text, trimmed))) {
|
|
2187
|
+
return [...messages.slice(0, -1), agentMessage];
|
|
2188
|
+
}
|
|
2189
|
+
return [...messages, agentMessage];
|
|
1103
2190
|
}
|
|
1104
2191
|
|
|
1105
2192
|
// src/react/hooks/useAgentChat.ts
|
|
@@ -1120,17 +2207,23 @@ function createInitialState(greeting = DEFAULT_GREETING) {
|
|
|
1120
2207
|
followUps: [],
|
|
1121
2208
|
streamingText: "",
|
|
1122
2209
|
pendingOffer: null,
|
|
2210
|
+
pendingInputs: [],
|
|
2211
|
+
toolResults: [],
|
|
1123
2212
|
error: null
|
|
1124
2213
|
};
|
|
1125
2214
|
}
|
|
1126
2215
|
function stateFromConversation(conversation, initialState) {
|
|
1127
2216
|
if (!conversation || conversation.messages.length === 0) return initialState;
|
|
2217
|
+
const pendingInputs = conversation.pendingInputs ?? [];
|
|
2218
|
+
const hasWaitingInput = pendingInputs.length > 0;
|
|
1128
2219
|
return {
|
|
1129
2220
|
...initialState,
|
|
1130
2221
|
messages: conversation.messages,
|
|
1131
|
-
phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
|
|
2222
|
+
phase: hasWaitingInput ? "waiting-input" : conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
|
|
1132
2223
|
streamingText: conversation.streamingText,
|
|
1133
|
-
toolSteps: conversation.toolSteps
|
|
2224
|
+
toolSteps: conversation.toolSteps,
|
|
2225
|
+
toolResults: conversation.toolResults,
|
|
2226
|
+
...hasWaitingInput ? { pendingInputs } : {}
|
|
1134
2227
|
};
|
|
1135
2228
|
}
|
|
1136
2229
|
function upsertToolStep(steps, item) {
|
|
@@ -1145,6 +2238,24 @@ function upsertToolStep(steps, item) {
|
|
|
1145
2238
|
if (index < 0) return [...steps, next];
|
|
1146
2239
|
return steps.map((step, stepIndex) => stepIndex === index ? next : step);
|
|
1147
2240
|
}
|
|
2241
|
+
function applyBookingOffer(prev, card) {
|
|
2242
|
+
const pendingOffer = preferBookingOffer(prev.pendingOffer, card);
|
|
2243
|
+
const last = prev.messages.at(-1);
|
|
2244
|
+
if (last?.role === "agent" && prev.phase === "complete") {
|
|
2245
|
+
return {
|
|
2246
|
+
...prev,
|
|
2247
|
+
pendingOffer,
|
|
2248
|
+
messages: [
|
|
2249
|
+
...prev.messages.slice(0, -1),
|
|
2250
|
+
{
|
|
2251
|
+
...last,
|
|
2252
|
+
text: ensureBookingOfferText(last.text, pendingOffer)
|
|
2253
|
+
}
|
|
2254
|
+
]
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
return { ...prev, pendingOffer };
|
|
2258
|
+
}
|
|
1148
2259
|
function completeActivePlanning(steps) {
|
|
1149
2260
|
return steps.map(
|
|
1150
2261
|
(step) => step.kind === "planning" && step.state === "active" ? {
|
|
@@ -1154,6 +2265,9 @@ function completeActivePlanning(steps) {
|
|
|
1154
2265
|
} : step
|
|
1155
2266
|
);
|
|
1156
2267
|
}
|
|
2268
|
+
function isJsonRecord(value) {
|
|
2269
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2270
|
+
}
|
|
1157
2271
|
function useAgentChat({
|
|
1158
2272
|
customerId,
|
|
1159
2273
|
getUnpublishedPreviewGrant,
|
|
@@ -1163,7 +2277,8 @@ function useAgentChat({
|
|
|
1163
2277
|
runtimeOrigin,
|
|
1164
2278
|
visitorSessionId,
|
|
1165
2279
|
storageKeyPrefix,
|
|
1166
|
-
greeting
|
|
2280
|
+
greeting,
|
|
2281
|
+
toolResultRegistry
|
|
1167
2282
|
}) {
|
|
1168
2283
|
const initialState = useMemo(
|
|
1169
2284
|
() => createInitialState(greeting?.trim() || DEFAULT_GREETING),
|
|
@@ -1171,6 +2286,8 @@ function useAgentChat({
|
|
|
1171
2286
|
);
|
|
1172
2287
|
const previewGrantProviderRef = useRef(getUnpublishedPreviewGrant);
|
|
1173
2288
|
previewGrantProviderRef.current = getUnpublishedPreviewGrant;
|
|
2289
|
+
const toolResultRegistryRef = useRef(toolResultRegistry);
|
|
2290
|
+
toolResultRegistryRef.current = toolResultRegistry;
|
|
1174
2291
|
const resolveUnpublishedPreviewGrant = () => {
|
|
1175
2292
|
const provider = previewGrantProviderRef.current;
|
|
1176
2293
|
if (!provider) {
|
|
@@ -1265,14 +2382,18 @@ function useAgentChat({
|
|
|
1265
2382
|
messages: state.messages,
|
|
1266
2383
|
pending: isAgentBusy(state.phase),
|
|
1267
2384
|
streamingText: state.streamingText,
|
|
1268
|
-
toolSteps: state.toolSteps
|
|
2385
|
+
toolSteps: state.toolSteps,
|
|
2386
|
+
toolResults: state.toolResults ?? [],
|
|
2387
|
+
...state.pendingInputs && state.pendingInputs.length > 0 ? { pendingInputs: state.pendingInputs } : {}
|
|
1269
2388
|
});
|
|
1270
2389
|
}, [
|
|
1271
2390
|
resolvedStorageKeyPrefix,
|
|
1272
2391
|
state.messages,
|
|
2392
|
+
state.pendingInputs,
|
|
1273
2393
|
state.phase,
|
|
1274
2394
|
state.streamingText,
|
|
1275
2395
|
state.toolSteps,
|
|
2396
|
+
state.toolResults,
|
|
1276
2397
|
visitorId
|
|
1277
2398
|
]);
|
|
1278
2399
|
const reset = useCallback(() => {
|
|
@@ -1285,13 +2406,20 @@ function useAgentChat({
|
|
|
1285
2406
|
}, [initialState, resolvedStorageKeyPrefix, visitorId]);
|
|
1286
2407
|
const runTurn = useCallback(
|
|
1287
2408
|
async (input) => {
|
|
1288
|
-
const {
|
|
2409
|
+
const {
|
|
2410
|
+
controller,
|
|
2411
|
+
initialText = "",
|
|
2412
|
+
responses,
|
|
2413
|
+
resume,
|
|
2414
|
+
visitorText
|
|
2415
|
+
} = input;
|
|
1289
2416
|
const { signal } = controller;
|
|
1290
2417
|
const isActiveRun = () => runRef.current === controller && !signal.aborted;
|
|
1291
2418
|
try {
|
|
1292
2419
|
let streamStarted = Boolean(initialText);
|
|
1293
2420
|
let streamed = initialText;
|
|
1294
2421
|
const capturedOffers = [];
|
|
2422
|
+
let capturedInputCount = 0;
|
|
1295
2423
|
const handlers = {
|
|
1296
2424
|
onWork: (item) => {
|
|
1297
2425
|
if (!isActiveRun()) return;
|
|
@@ -1301,11 +2429,62 @@ function useAgentChat({
|
|
|
1301
2429
|
toolSteps: upsertToolStep(prev.toolSteps, item)
|
|
1302
2430
|
}));
|
|
1303
2431
|
},
|
|
1304
|
-
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
2432
|
+
onToolResult: (result) => {
|
|
2433
|
+
const presentation = presentVisitorToolResult(
|
|
2434
|
+
result,
|
|
2435
|
+
toolResultRegistryRef.current
|
|
2436
|
+
);
|
|
2437
|
+
if (presentation.kind === "booking") {
|
|
2438
|
+
const card = presentation.card;
|
|
2439
|
+
if (card.type === "booking_offer") {
|
|
2440
|
+
capturedOffers.push(card);
|
|
2441
|
+
setState((prev) => applyBookingOffer(prev, card));
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
if (!isActiveRun()) return;
|
|
2445
|
+
if (card.type === "booking_confirmed") {
|
|
2446
|
+
pendingBookingRef.current = card;
|
|
2447
|
+
savePendingWidgetBooking(
|
|
2448
|
+
resolvedStorageKeyPrefix,
|
|
2449
|
+
visitorId,
|
|
2450
|
+
card
|
|
2451
|
+
);
|
|
2452
|
+
} else if (card.type === "booking_canceled" && pendingBookingRef.current?.eventUri === card.eventUri) {
|
|
2453
|
+
pendingBookingRef.current = null;
|
|
2454
|
+
clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
|
|
2455
|
+
}
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
if (!isActiveRun()) return;
|
|
2459
|
+
if (presentation.kind === "hidden") return;
|
|
2460
|
+
if (presentation.kind === "input") return;
|
|
2461
|
+
setState((prev) => ({
|
|
2462
|
+
...prev,
|
|
2463
|
+
toolResults: [
|
|
2464
|
+
...(prev.toolResults ?? []).filter(
|
|
2465
|
+
(item) => item.id !== presentation.id
|
|
2466
|
+
),
|
|
2467
|
+
presentation
|
|
2468
|
+
]
|
|
2469
|
+
}));
|
|
2470
|
+
},
|
|
2471
|
+
onInputRequest: (requests) => {
|
|
2472
|
+
if (!isActiveRun()) return;
|
|
2473
|
+
capturedInputCount += requests.length;
|
|
2474
|
+
const cardRequests = requests.filter(shouldRenderVisitorInputCard);
|
|
2475
|
+
const chatRequests = requests.filter(isChatCollectibleInputRequest);
|
|
2476
|
+
setState((prev) => ({
|
|
2477
|
+
...prev,
|
|
2478
|
+
phase: cardRequests.length > 0 ? "waiting-input" : "complete",
|
|
2479
|
+
pendingInputs: [...requests],
|
|
2480
|
+
...chatRequests.length > 0 ? {
|
|
2481
|
+
messages: appendChatCollectiblePrompts(
|
|
2482
|
+
prev.messages,
|
|
2483
|
+
chatRequests
|
|
2484
|
+
),
|
|
2485
|
+
toolSteps: completeActivePlanning(prev.toolSteps)
|
|
2486
|
+
} : {}
|
|
2487
|
+
}));
|
|
1309
2488
|
},
|
|
1310
2489
|
onDelta: (delta) => {
|
|
1311
2490
|
if (!isActiveRun()) return;
|
|
@@ -1331,7 +2510,11 @@ function useAgentChat({
|
|
|
1331
2510
|
streamStarted = true;
|
|
1332
2511
|
}
|
|
1333
2512
|
};
|
|
1334
|
-
let finalText =
|
|
2513
|
+
let finalText = responses ? await clientRef.current.respondTurn({
|
|
2514
|
+
handlers,
|
|
2515
|
+
responses,
|
|
2516
|
+
signal
|
|
2517
|
+
}) : resume ? await clientRef.current.resumeTurn({
|
|
1335
2518
|
handlers,
|
|
1336
2519
|
initialText,
|
|
1337
2520
|
message: visitorText,
|
|
@@ -1343,17 +2526,15 @@ function useAgentChat({
|
|
|
1343
2526
|
signal
|
|
1344
2527
|
});
|
|
1345
2528
|
}
|
|
1346
|
-
if (!isActiveRun() || finalText === null) return;
|
|
2529
|
+
if (!isActiveRun() || finalText === null) return null;
|
|
2530
|
+
if (!finalText.trim() && capturedInputCount > 0) {
|
|
2531
|
+
runRef.current = null;
|
|
2532
|
+
return null;
|
|
2533
|
+
}
|
|
1347
2534
|
const displayText = ensureBookingOfferText(
|
|
1348
2535
|
finalText,
|
|
1349
2536
|
capturedOffers.at(-1) ?? null
|
|
1350
2537
|
);
|
|
1351
|
-
const agentMessage = {
|
|
1352
|
-
id: `agent-${Date.now()}`,
|
|
1353
|
-
role: "agent",
|
|
1354
|
-
text: displayText,
|
|
1355
|
-
createdAt: Date.now()
|
|
1356
|
-
};
|
|
1357
2538
|
const parsedCards = extractToolCards(displayText);
|
|
1358
2539
|
for (const card of parsedCards) {
|
|
1359
2540
|
if (card.type === "booking_confirmed") {
|
|
@@ -1367,21 +2548,25 @@ function useAgentChat({
|
|
|
1367
2548
|
}
|
|
1368
2549
|
setState((prev) => ({
|
|
1369
2550
|
...prev,
|
|
1370
|
-
phase: "complete",
|
|
1371
|
-
messages:
|
|
2551
|
+
phase: (prev.pendingInputs ?? []).some(shouldRenderVisitorInputCard) ? "waiting-input" : "complete",
|
|
2552
|
+
messages: appendAgentTurnMessage(prev.messages, displayText),
|
|
1372
2553
|
toolSteps: completeActivePlanning(prev.toolSteps),
|
|
1373
2554
|
streamingText: "",
|
|
1374
|
-
pendingOffer:
|
|
2555
|
+
pendingOffer: capturedOffers.at(-1) ?? prev.pendingOffer,
|
|
2556
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2557
|
+
isChatCollectibleInputRequest
|
|
2558
|
+
),
|
|
1375
2559
|
followUps: [],
|
|
1376
2560
|
journey: null
|
|
1377
2561
|
}));
|
|
1378
2562
|
runRef.current = null;
|
|
2563
|
+
return displayText;
|
|
1379
2564
|
} catch (error) {
|
|
1380
2565
|
if (error instanceof DOMException && error.name === "AbortError")
|
|
1381
|
-
return;
|
|
1382
|
-
if (!isActiveRun()) return;
|
|
2566
|
+
return null;
|
|
2567
|
+
if (!isActiveRun()) return null;
|
|
1383
2568
|
const message = formatAgentError(error);
|
|
1384
|
-
if (!message) return;
|
|
2569
|
+
if (!message) return null;
|
|
1385
2570
|
setState((prev) => ({
|
|
1386
2571
|
...prev,
|
|
1387
2572
|
phase: "error",
|
|
@@ -1397,6 +2582,7 @@ function useAgentChat({
|
|
|
1397
2582
|
error: message
|
|
1398
2583
|
}));
|
|
1399
2584
|
runRef.current = null;
|
|
2585
|
+
return null;
|
|
1400
2586
|
}
|
|
1401
2587
|
},
|
|
1402
2588
|
[resolvedStorageKeyPrefix, visitorId]
|
|
@@ -1424,6 +2610,49 @@ function useAgentChat({
|
|
|
1424
2610
|
);
|
|
1425
2611
|
const submit = useCallback(
|
|
1426
2612
|
async (visitorText, options) => {
|
|
2613
|
+
const trimmed = visitorText.trim();
|
|
2614
|
+
if (!trimmed) return null;
|
|
2615
|
+
const chatResponse = chatInputResponseForText(
|
|
2616
|
+
state.pendingInputs ?? [],
|
|
2617
|
+
trimmed
|
|
2618
|
+
);
|
|
2619
|
+
if (chatResponse) {
|
|
2620
|
+
const visitorMessage2 = {
|
|
2621
|
+
id: `visitor-${Date.now()}`,
|
|
2622
|
+
role: "visitor",
|
|
2623
|
+
text: trimmed,
|
|
2624
|
+
createdAt: Date.now()
|
|
2625
|
+
};
|
|
2626
|
+
if (runRef.current) {
|
|
2627
|
+
runRef.current.abort();
|
|
2628
|
+
clientRef.current.cancelActive();
|
|
2629
|
+
}
|
|
2630
|
+
const controller2 = new AbortController();
|
|
2631
|
+
runRef.current = controller2;
|
|
2632
|
+
setState((prev) => ({
|
|
2633
|
+
...prev,
|
|
2634
|
+
phase: "running-tools",
|
|
2635
|
+
messages: [...prev.messages, visitorMessage2],
|
|
2636
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2637
|
+
(request) => request.requestId !== chatResponse.requestId
|
|
2638
|
+
),
|
|
2639
|
+
toolSteps: [
|
|
2640
|
+
{
|
|
2641
|
+
id: "planning",
|
|
2642
|
+
kind: "planning",
|
|
2643
|
+
label: "Understanding your question",
|
|
2644
|
+
state: "active"
|
|
2645
|
+
}
|
|
2646
|
+
],
|
|
2647
|
+
error: null
|
|
2648
|
+
}));
|
|
2649
|
+
return await runTurn({
|
|
2650
|
+
controller: controller2,
|
|
2651
|
+
responses: [chatResponse],
|
|
2652
|
+
resume: false,
|
|
2653
|
+
visitorText: ""
|
|
2654
|
+
});
|
|
2655
|
+
}
|
|
1427
2656
|
if (runRef.current) {
|
|
1428
2657
|
runRef.current.abort();
|
|
1429
2658
|
clientRef.current.cancelActive();
|
|
@@ -1458,11 +2687,17 @@ ${outgoing}` : outgoing;
|
|
|
1458
2687
|
followUps: [],
|
|
1459
2688
|
streamingText: "",
|
|
1460
2689
|
pendingOffer: null,
|
|
2690
|
+
pendingInputs: [],
|
|
2691
|
+
toolResults: [],
|
|
1461
2692
|
error: null
|
|
1462
2693
|
}));
|
|
1463
|
-
await runTurn({
|
|
2694
|
+
return await runTurn({
|
|
2695
|
+
controller,
|
|
2696
|
+
resume: false,
|
|
2697
|
+
visitorText: runtimeText
|
|
2698
|
+
});
|
|
1464
2699
|
},
|
|
1465
|
-
[runTurn]
|
|
2700
|
+
[runTurn, state.pendingInputs]
|
|
1466
2701
|
);
|
|
1467
2702
|
const retry = useCallback(async () => {
|
|
1468
2703
|
const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
|
|
@@ -1488,6 +2723,8 @@ ${outgoing}` : outgoing;
|
|
|
1488
2723
|
followUps: [],
|
|
1489
2724
|
streamingText: "",
|
|
1490
2725
|
pendingOffer: null,
|
|
2726
|
+
pendingInputs: [],
|
|
2727
|
+
toolResults: [],
|
|
1491
2728
|
error: null
|
|
1492
2729
|
}));
|
|
1493
2730
|
await runTurn({
|
|
@@ -1496,6 +2733,50 @@ ${outgoing}` : outgoing;
|
|
|
1496
2733
|
visitorText: visitorTurnText(visitorMessage)
|
|
1497
2734
|
});
|
|
1498
2735
|
}, [runTurn, state.messages]);
|
|
2736
|
+
const respondToToolInput = useCallback(
|
|
2737
|
+
async (surface, values) => {
|
|
2738
|
+
const details = JSON.stringify(values);
|
|
2739
|
+
await submit(`${surface.title} details provided`, {
|
|
2740
|
+
runtimeText: [
|
|
2741
|
+
`Structured input provided for ${surface.toolSlug}.`,
|
|
2742
|
+
surface.operationId ? `Operation: ${surface.operationId}.` : "",
|
|
2743
|
+
`Use these exact values and retry the action: ${details}`,
|
|
2744
|
+
"Do not invent or alter any values."
|
|
2745
|
+
].filter(Boolean).join("\n")
|
|
2746
|
+
});
|
|
2747
|
+
},
|
|
2748
|
+
[submit]
|
|
2749
|
+
);
|
|
2750
|
+
const respondToInput = useCallback(
|
|
2751
|
+
async (response) => {
|
|
2752
|
+
if (runRef.current) return;
|
|
2753
|
+
const pending = state.pendingInputs?.find(
|
|
2754
|
+
(request) => request.requestId === response.requestId
|
|
2755
|
+
);
|
|
2756
|
+
if (!pending) return;
|
|
2757
|
+
if (pending.ui && isJsonRecord(response.value)) {
|
|
2758
|
+
await respondToToolInput(pending.ui, response.value);
|
|
2759
|
+
return;
|
|
2760
|
+
}
|
|
2761
|
+
const controller = new AbortController();
|
|
2762
|
+
runRef.current = controller;
|
|
2763
|
+
setState((prev) => ({
|
|
2764
|
+
...prev,
|
|
2765
|
+
phase: "running-tools",
|
|
2766
|
+
pendingInputs: (prev.pendingInputs ?? []).filter(
|
|
2767
|
+
(request) => request.requestId !== response.requestId
|
|
2768
|
+
),
|
|
2769
|
+
error: null
|
|
2770
|
+
}));
|
|
2771
|
+
await runTurn({
|
|
2772
|
+
controller,
|
|
2773
|
+
responses: [response],
|
|
2774
|
+
resume: false,
|
|
2775
|
+
visitorText: ""
|
|
2776
|
+
});
|
|
2777
|
+
},
|
|
2778
|
+
[respondToToolInput, runTurn, state.pendingInputs]
|
|
2779
|
+
);
|
|
1499
2780
|
useEffect(() => {
|
|
1500
2781
|
const conversation = loadPersistedAgentConversation(
|
|
1501
2782
|
resolvedStorageKeyPrefix,
|
|
@@ -1529,6 +2810,8 @@ ${outgoing}` : outgoing;
|
|
|
1529
2810
|
state,
|
|
1530
2811
|
reset,
|
|
1531
2812
|
retry,
|
|
2813
|
+
respondToInput,
|
|
2814
|
+
respondToToolInput,
|
|
1532
2815
|
submit,
|
|
1533
2816
|
rememberBooking,
|
|
1534
2817
|
forgetBooking,
|
|
@@ -1567,6 +2850,33 @@ function normalizeAgentPlacement(placement) {
|
|
|
1567
2850
|
};
|
|
1568
2851
|
}
|
|
1569
2852
|
|
|
2853
|
+
// src/react/panel-controller.ts
|
|
2854
|
+
var controllers = /* @__PURE__ */ new Map();
|
|
2855
|
+
function registerAgentPanelController(customerId, controller) {
|
|
2856
|
+
controllers.set(customerId, controller);
|
|
2857
|
+
}
|
|
2858
|
+
function unregisterAgentPanelController(customerId) {
|
|
2859
|
+
controllers.delete(customerId);
|
|
2860
|
+
}
|
|
2861
|
+
function openAgentPanel(customerId) {
|
|
2862
|
+
controllers.get(customerId)?.open();
|
|
2863
|
+
}
|
|
2864
|
+
function closeAgentPanel(customerId) {
|
|
2865
|
+
controllers.get(customerId)?.close();
|
|
2866
|
+
}
|
|
2867
|
+
function resetAgentPanel(customerId) {
|
|
2868
|
+
controllers.get(customerId)?.reset();
|
|
2869
|
+
}
|
|
2870
|
+
function submitAgentPanel(customerId, message, options) {
|
|
2871
|
+
const controller = controllers.get(customerId);
|
|
2872
|
+
if (!controller) {
|
|
2873
|
+
return Promise.reject(
|
|
2874
|
+
new Error(`Agent panel "${customerId}" is not mounted.`)
|
|
2875
|
+
);
|
|
2876
|
+
}
|
|
2877
|
+
return controller.submit(message, options);
|
|
2878
|
+
}
|
|
2879
|
+
|
|
1570
2880
|
// src/react/types/conversation.ts
|
|
1571
2881
|
var defaultAgentRailTheme = {
|
|
1572
2882
|
railMaxWidth: "450px",
|
|
@@ -1579,8 +2889,8 @@ var defaultAgentRailTheme = {
|
|
|
1579
2889
|
textMuted: "#5a6378",
|
|
1580
2890
|
textSubtle: "#8a94a8",
|
|
1581
2891
|
border: "rgb(42 51 70 / 0.1)",
|
|
1582
|
-
visitorBubble: "#
|
|
1583
|
-
visitorText: "#
|
|
2892
|
+
visitorBubble: "#f3edff",
|
|
2893
|
+
visitorText: "#171b2a",
|
|
1584
2894
|
success: "#18794e",
|
|
1585
2895
|
danger: "#c94b63",
|
|
1586
2896
|
fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
|
|
@@ -1597,13 +2907,14 @@ var defaultDarkAgentRailTheme = {
|
|
|
1597
2907
|
textMuted: "#b6bfce",
|
|
1598
2908
|
textSubtle: "#919cad",
|
|
1599
2909
|
border: "rgb(226 232 240 / 0.16)",
|
|
1600
|
-
visitorBubble: "#
|
|
2910
|
+
visitorBubble: "#2b2140",
|
|
2911
|
+
visitorText: "#f5f7fb",
|
|
1601
2912
|
success: "#55cf91",
|
|
1602
2913
|
danger: "#ff8da1"
|
|
1603
2914
|
};
|
|
1604
2915
|
|
|
1605
2916
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
1606
|
-
import { useEffect as
|
|
2917
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
|
|
1607
2918
|
|
|
1608
2919
|
// src/react/hooks/useAgentColorScheme.ts
|
|
1609
2920
|
import { useSyncExternalStore } from "react";
|
|
@@ -1637,11 +2948,21 @@ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
|
|
|
1637
2948
|
|
|
1638
2949
|
// src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
|
|
1639
2950
|
import { useState as useState2 } from "react";
|
|
1640
|
-
import {
|
|
2951
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2952
|
+
function joinLabels(labels) {
|
|
2953
|
+
if (labels.length <= 1) return labels[0] ?? "";
|
|
2954
|
+
if (labels.length === 2) return `${labels[0]} and ${labels[1]}`;
|
|
2955
|
+
return `${labels.slice(0, -1).join(", ")} and ${labels.at(-1) ?? ""}`;
|
|
2956
|
+
}
|
|
1641
2957
|
function workSummary(steps, failed, brandLabel) {
|
|
2958
|
+
const activeSpecialists = steps.filter(
|
|
2959
|
+
(step) => step.kind === "specialist" && step.state === "active"
|
|
2960
|
+
);
|
|
2961
|
+
if (activeSpecialists.length > 0)
|
|
2962
|
+
return `Working with ${joinLabels(
|
|
2963
|
+
activeSpecialists.map((step) => step.label)
|
|
2964
|
+
)}`;
|
|
1642
2965
|
const active = [...steps].reverse().find((step) => step.state === "active");
|
|
1643
|
-
if (active?.kind === "specialist")
|
|
1644
|
-
return `${active.label} is reviewing your question`;
|
|
1645
2966
|
if (active?.kind === "search") return "Searching this site";
|
|
1646
2967
|
if (active)
|
|
1647
2968
|
return brandLabel ? `${brandLabel} is choosing the best way to help` : "Choosing the best way to help";
|
|
@@ -1659,7 +2980,7 @@ function workSummary(steps, failed, brandLabel) {
|
|
|
1659
2980
|
if (specialists.length === 1)
|
|
1660
2981
|
return `Brought in ${specialists[0]?.label}`;
|
|
1661
2982
|
if (searched) return "Searched this site";
|
|
1662
|
-
return "Answer ready";
|
|
2983
|
+
return brandLabel ? `Answered with ${brandLabel}` : "Answer ready";
|
|
1663
2984
|
}
|
|
1664
2985
|
function stepLabel(step, brandLabel) {
|
|
1665
2986
|
return step.kind === "planning" ? brandLabel : step.label;
|
|
@@ -1677,36 +2998,11 @@ function stepDetail(step, steps) {
|
|
|
1677
2998
|
return "Searched this site";
|
|
1678
2999
|
return step.detail;
|
|
1679
3000
|
}
|
|
1680
|
-
function SearchIcon() {
|
|
1681
|
-
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
|
|
1682
|
-
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "3.75", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1683
|
-
/* @__PURE__ */ jsx(
|
|
1684
|
-
"path",
|
|
1685
|
-
{
|
|
1686
|
-
d: "m10 10 3 3",
|
|
1687
|
-
stroke: "currentColor",
|
|
1688
|
-
strokeWidth: "1.4",
|
|
1689
|
-
strokeLinecap: "round"
|
|
1690
|
-
}
|
|
1691
|
-
)
|
|
1692
|
-
] });
|
|
1693
|
-
}
|
|
1694
|
-
function PlanningIcon() {
|
|
1695
|
-
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
1696
|
-
"path",
|
|
1697
|
-
{
|
|
1698
|
-
d: "M4 4.5h8M4 8h5.5M4 11.5h7",
|
|
1699
|
-
stroke: "currentColor",
|
|
1700
|
-
strokeWidth: "1.4",
|
|
1701
|
-
strokeLinecap: "round"
|
|
1702
|
-
}
|
|
1703
|
-
) });
|
|
1704
|
-
}
|
|
1705
3001
|
function AgentActivityBubble({
|
|
1706
3002
|
brandLabel = "",
|
|
1707
|
-
brandLogoUrl,
|
|
1708
3003
|
failed = false,
|
|
1709
|
-
steps
|
|
3004
|
+
steps,
|
|
3005
|
+
onRetryStep
|
|
1710
3006
|
}) {
|
|
1711
3007
|
const active = steps.some((step) => step.state === "active");
|
|
1712
3008
|
const receiptId = steps.map((step) => step.id).join(":");
|
|
@@ -1714,6 +3010,10 @@ function AgentActivityBubble({
|
|
|
1714
3010
|
null
|
|
1715
3011
|
);
|
|
1716
3012
|
const detailsOpen = active || expandedReceiptId === receiptId;
|
|
3013
|
+
const delegationCount = steps.filter(
|
|
3014
|
+
(step) => step.kind === "specialist"
|
|
3015
|
+
).length;
|
|
3016
|
+
const delegated = delegationCount > 0 && steps.some((step) => step.kind === "planning");
|
|
1717
3017
|
const visibleSteps = steps.filter(
|
|
1718
3018
|
(step) => step.kind !== "planning" || Boolean(brandLabel)
|
|
1719
3019
|
);
|
|
@@ -1729,7 +3029,7 @@ function AgentActivityBubble({
|
|
|
1729
3029
|
workSummary(steps, failed, brandLabel)
|
|
1730
3030
|
] }),
|
|
1731
3031
|
/* @__PURE__ */ jsxs("div", { className: "agent-activity-bubble__details", children: [
|
|
1732
|
-
/* @__PURE__ */
|
|
3032
|
+
/* @__PURE__ */ jsxs(
|
|
1733
3033
|
"button",
|
|
1734
3034
|
{
|
|
1735
3035
|
type: "button",
|
|
@@ -1741,15 +3041,19 @@ function AgentActivityBubble({
|
|
|
1741
3041
|
(current) => current === receiptId ? null : receiptId
|
|
1742
3042
|
);
|
|
1743
3043
|
},
|
|
1744
|
-
children:
|
|
3044
|
+
children: [
|
|
3045
|
+
/* @__PURE__ */ jsx("span", { className: "agent-activity-bubble__summary-title", children: "How this answer was made" }),
|
|
3046
|
+
/* @__PURE__ */ jsx("span", { className: "agent-activity-bubble__summary-toggle", children: detailsOpen ? "Hide work" : "Show work" })
|
|
3047
|
+
]
|
|
1745
3048
|
}
|
|
1746
3049
|
),
|
|
1747
3050
|
detailsOpen ? /* @__PURE__ */ jsx("ol", { className: "agent-activity-bubble__steps", children: visibleSteps.map((step) => {
|
|
1748
3051
|
const detail = stepDetail(step, steps);
|
|
3052
|
+
const child = delegated && step.kind === "specialist";
|
|
1749
3053
|
return /* @__PURE__ */ jsxs(
|
|
1750
3054
|
"li",
|
|
1751
3055
|
{
|
|
1752
|
-
className: "agent-activity-bubble__step"
|
|
3056
|
+
className: `agent-activity-bubble__step${child ? " agent-activity-bubble__step--child" : ""}`,
|
|
1753
3057
|
"data-kind": step.kind,
|
|
1754
3058
|
"data-state": step.state,
|
|
1755
3059
|
children: [
|
|
@@ -1757,25 +3061,27 @@ function AgentActivityBubble({
|
|
|
1757
3061
|
"span",
|
|
1758
3062
|
{
|
|
1759
3063
|
className: "agent-activity-bubble__step-icon",
|
|
1760
|
-
"aria-hidden": "true"
|
|
1761
|
-
children: step.kind === "planning" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1762
|
-
/* @__PURE__ */ jsx(PlanningIcon, {}),
|
|
1763
|
-
brandLogoUrl ? /* @__PURE__ */ jsx(
|
|
1764
|
-
"img",
|
|
1765
|
-
{
|
|
1766
|
-
src: brandLogoUrl,
|
|
1767
|
-
alt: "",
|
|
1768
|
-
onError: (event) => {
|
|
1769
|
-
event.currentTarget.hidden = true;
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
) : null
|
|
1773
|
-
] }) : step.kind === "search" ? /* @__PURE__ */ jsx(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
|
|
3064
|
+
"aria-hidden": "true"
|
|
1774
3065
|
}
|
|
1775
3066
|
),
|
|
1776
3067
|
/* @__PURE__ */ jsxs("span", { className: "agent-activity-bubble__step-copy", children: [
|
|
1777
3068
|
/* @__PURE__ */ jsx("span", { className: "agent-activity-bubble__step-heading", children: /* @__PURE__ */ jsx("strong", { children: stepLabel(step, brandLabel) }) }),
|
|
1778
|
-
detail ? /* @__PURE__ */ jsx("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
|
|
3069
|
+
detail ? /* @__PURE__ */ jsx("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null,
|
|
3070
|
+
delegated && step.kind === "planning" ? /* @__PURE__ */ jsxs("span", { className: "agent-activity-bubble__delegation", children: [
|
|
3071
|
+
"Delegated ",
|
|
3072
|
+
delegationCount,
|
|
3073
|
+
" ",
|
|
3074
|
+
delegationCount === 1 ? "task" : "tasks"
|
|
3075
|
+
] }) : null,
|
|
3076
|
+
step.state === "error" && onRetryStep ? /* @__PURE__ */ jsx(
|
|
3077
|
+
"button",
|
|
3078
|
+
{
|
|
3079
|
+
type: "button",
|
|
3080
|
+
className: "agent-activity-bubble__step-retry",
|
|
3081
|
+
onClick: () => onRetryStep(step),
|
|
3082
|
+
children: "Retry"
|
|
3083
|
+
}
|
|
3084
|
+
) : null
|
|
1779
3085
|
] })
|
|
1780
3086
|
]
|
|
1781
3087
|
},
|
|
@@ -1787,62 +3093,198 @@ function AgentActivityBubble({
|
|
|
1787
3093
|
}
|
|
1788
3094
|
|
|
1789
3095
|
// src/react/components/Composer/Composer.tsx
|
|
1790
|
-
import {
|
|
3096
|
+
import {
|
|
3097
|
+
useEffect as useEffect2,
|
|
3098
|
+
useId,
|
|
3099
|
+
useRef as useRef2,
|
|
3100
|
+
useState as useState3
|
|
3101
|
+
} from "react";
|
|
1791
3102
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1792
3103
|
function SendIcon() {
|
|
1793
|
-
return /* @__PURE__ */ jsx2("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(
|
|
3104
|
+
return /* @__PURE__ */ jsx2("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(
|
|
3105
|
+
"path",
|
|
3106
|
+
{
|
|
3107
|
+
d: "M8 12V4M8 4l-3 3M8 4l3 3",
|
|
3108
|
+
stroke: "currentColor",
|
|
3109
|
+
strokeWidth: "1.5",
|
|
3110
|
+
strokeLinecap: "round",
|
|
3111
|
+
strokeLinejoin: "round"
|
|
3112
|
+
}
|
|
3113
|
+
) });
|
|
3114
|
+
}
|
|
3115
|
+
function emptyValues(form) {
|
|
3116
|
+
const values = {};
|
|
3117
|
+
for (const field of form?.fields ?? []) values[field.id] = "";
|
|
3118
|
+
return values;
|
|
1794
3119
|
}
|
|
1795
3120
|
function Composer({
|
|
1796
3121
|
disabled = false,
|
|
1797
3122
|
placeholder = "Ask anything\u2026",
|
|
1798
3123
|
variant = "default",
|
|
3124
|
+
form = null,
|
|
1799
3125
|
onSubmit
|
|
1800
3126
|
}) {
|
|
1801
3127
|
const [value, setValue] = useState3("");
|
|
3128
|
+
const [values, setValues] = useState3(
|
|
3129
|
+
() => emptyValues(form)
|
|
3130
|
+
);
|
|
3131
|
+
const [blurred, setBlurred] = useState3({});
|
|
1802
3132
|
const inputRef = useRef2(null);
|
|
1803
|
-
|
|
3133
|
+
const firstFieldRef = useRef2(null);
|
|
3134
|
+
const formId = useId();
|
|
3135
|
+
const activeForm = form;
|
|
3136
|
+
const canSendForm = activeForm ? isComposerFormComplete(activeForm, values) : Boolean(value.trim());
|
|
3137
|
+
useEffect2(() => {
|
|
3138
|
+
setValues(emptyValues(form));
|
|
3139
|
+
setBlurred({});
|
|
3140
|
+
}, [form?.id]);
|
|
3141
|
+
useEffect2(() => {
|
|
3142
|
+
if (activeForm) firstFieldRef.current?.focus();
|
|
3143
|
+
}, [activeForm?.id]);
|
|
3144
|
+
function submitChat() {
|
|
1804
3145
|
const trimmed = value.trim();
|
|
1805
3146
|
if (!trimmed || disabled) return;
|
|
1806
3147
|
onSubmit?.(trimmed);
|
|
1807
3148
|
setValue("");
|
|
1808
3149
|
inputRef.current?.focus();
|
|
1809
3150
|
}
|
|
3151
|
+
function submitForm() {
|
|
3152
|
+
if (!activeForm || disabled || !canSendForm) return;
|
|
3153
|
+
onSubmit?.(formatComposerFormMessage(activeForm, values));
|
|
3154
|
+
setValues(emptyValues(activeForm));
|
|
3155
|
+
setBlurred({});
|
|
3156
|
+
}
|
|
1810
3157
|
function handleSubmit(event) {
|
|
1811
3158
|
event.preventDefault();
|
|
1812
|
-
|
|
3159
|
+
if (activeForm) submitForm();
|
|
3160
|
+
else submitChat();
|
|
1813
3161
|
}
|
|
1814
|
-
function
|
|
3162
|
+
function handleChatKeyDown(event) {
|
|
1815
3163
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
1816
3164
|
event.preventDefault();
|
|
1817
|
-
|
|
3165
|
+
submitChat();
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
function handleFormKeyDown(event) {
|
|
3169
|
+
const target = event.target;
|
|
3170
|
+
const isTextarea = Boolean(target && "tagName" in target && target.tagName === "TEXTAREA");
|
|
3171
|
+
if (event.key === "Enter" && !event.shiftKey && !isTextarea) {
|
|
3172
|
+
event.preventDefault();
|
|
3173
|
+
submitForm();
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
return /* @__PURE__ */ jsx2(
|
|
3177
|
+
"form",
|
|
3178
|
+
{
|
|
3179
|
+
className: [
|
|
3180
|
+
"composer",
|
|
3181
|
+
variant === "dock" ? "composer--dock" : "",
|
|
3182
|
+
activeForm ? "composer--form" : ""
|
|
3183
|
+
].filter(Boolean).join(" "),
|
|
3184
|
+
onSubmit: handleSubmit,
|
|
3185
|
+
children: activeForm ? /* @__PURE__ */ jsxs2("div", { className: "composer__sheet", role: "group", "aria-label": "Required details", children: [
|
|
3186
|
+
activeForm.fields.map((field, index) => {
|
|
3187
|
+
const fieldId = `${formId}-${field.id}`;
|
|
3188
|
+
const invalid = Boolean(blurred[field.id]) && !isValidComposerFieldValue(field, values[field.id] ?? "");
|
|
3189
|
+
const controlProps = {
|
|
3190
|
+
id: fieldId,
|
|
3191
|
+
name: field.id,
|
|
3192
|
+
disabled,
|
|
3193
|
+
required: field.required,
|
|
3194
|
+
autoComplete: field.autocomplete,
|
|
3195
|
+
placeholder: field.placeholder,
|
|
3196
|
+
spellCheck: false,
|
|
3197
|
+
value: values[field.id] ?? "",
|
|
3198
|
+
"aria-invalid": invalid || void 0,
|
|
3199
|
+
"aria-describedby": invalid ? `${fieldId}-error` : void 0,
|
|
3200
|
+
onBlur: () => setBlurred((current) => ({ ...current, [field.id]: true })),
|
|
3201
|
+
onChange: (event) => {
|
|
3202
|
+
const next = readComposerControlValue(event);
|
|
3203
|
+
setValues((current) => ({
|
|
3204
|
+
...current,
|
|
3205
|
+
[field.id]: next
|
|
3206
|
+
}));
|
|
3207
|
+
},
|
|
3208
|
+
onKeyDown: handleFormKeyDown
|
|
3209
|
+
};
|
|
3210
|
+
return /* @__PURE__ */ jsxs2(
|
|
3211
|
+
"div",
|
|
3212
|
+
{
|
|
3213
|
+
className: [
|
|
3214
|
+
"composer__row",
|
|
3215
|
+
field.kind === "textarea" ? "composer__row--grow" : ""
|
|
3216
|
+
].filter(Boolean).join(" "),
|
|
3217
|
+
children: [
|
|
3218
|
+
/* @__PURE__ */ jsxs2("label", { className: "composer__label", htmlFor: fieldId, children: [
|
|
3219
|
+
/* @__PURE__ */ jsx2("span", { className: "composer__sr-only", children: field.label }),
|
|
3220
|
+
field.kind === "textarea" ? /* @__PURE__ */ jsx2(
|
|
3221
|
+
"textarea",
|
|
3222
|
+
{
|
|
3223
|
+
...controlProps,
|
|
3224
|
+
ref: index === 0 ? (node) => {
|
|
3225
|
+
firstFieldRef.current = node;
|
|
3226
|
+
} : void 0,
|
|
3227
|
+
className: "composer__control composer__control--area",
|
|
3228
|
+
rows: 3
|
|
3229
|
+
}
|
|
3230
|
+
) : /* @__PURE__ */ jsx2(
|
|
3231
|
+
"input",
|
|
3232
|
+
{
|
|
3233
|
+
...controlProps,
|
|
3234
|
+
ref: index === 0 ? (node) => {
|
|
3235
|
+
firstFieldRef.current = node;
|
|
3236
|
+
} : void 0,
|
|
3237
|
+
className: "composer__control",
|
|
3238
|
+
type: field.kind,
|
|
3239
|
+
inputMode: field.kind === "tel" ? "tel" : void 0
|
|
3240
|
+
}
|
|
3241
|
+
)
|
|
3242
|
+
] }),
|
|
3243
|
+
invalid ? /* @__PURE__ */ jsx2("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
|
|
3244
|
+
]
|
|
3245
|
+
},
|
|
3246
|
+
field.id
|
|
3247
|
+
);
|
|
3248
|
+
}),
|
|
3249
|
+
/* @__PURE__ */ jsx2("div", { className: "composer__toolbar", children: /* @__PURE__ */ jsx2(
|
|
3250
|
+
"button",
|
|
3251
|
+
{
|
|
3252
|
+
type: "submit",
|
|
3253
|
+
className: "composer__send",
|
|
3254
|
+
disabled: disabled || !canSendForm,
|
|
3255
|
+
"aria-label": "Send details",
|
|
3256
|
+
children: /* @__PURE__ */ jsx2(SendIcon, {})
|
|
3257
|
+
}
|
|
3258
|
+
) })
|
|
3259
|
+
] }) : /* @__PURE__ */ jsxs2("div", { className: "composer__field", children: [
|
|
3260
|
+
/* @__PURE__ */ jsx2(
|
|
3261
|
+
"textarea",
|
|
3262
|
+
{
|
|
3263
|
+
ref: inputRef,
|
|
3264
|
+
className: "composer__input",
|
|
3265
|
+
rows: 1,
|
|
3266
|
+
value,
|
|
3267
|
+
placeholder,
|
|
3268
|
+
disabled,
|
|
3269
|
+
spellCheck: false,
|
|
3270
|
+
"aria-label": "Message",
|
|
3271
|
+
onChange: (event) => setValue(event.target.value),
|
|
3272
|
+
onKeyDown: handleChatKeyDown
|
|
3273
|
+
}
|
|
3274
|
+
),
|
|
3275
|
+
/* @__PURE__ */ jsx2(
|
|
3276
|
+
"button",
|
|
3277
|
+
{
|
|
3278
|
+
type: "submit",
|
|
3279
|
+
className: "composer__send",
|
|
3280
|
+
disabled: disabled || !value.trim(),
|
|
3281
|
+
"aria-label": "Send message",
|
|
3282
|
+
children: /* @__PURE__ */ jsx2(SendIcon, {})
|
|
3283
|
+
}
|
|
3284
|
+
)
|
|
3285
|
+
] })
|
|
1818
3286
|
}
|
|
1819
|
-
|
|
1820
|
-
return /* @__PURE__ */ jsx2("form", { className: `composer${variant === "dock" ? " composer--dock" : ""}`, onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs2("div", { className: "composer__field", children: [
|
|
1821
|
-
/* @__PURE__ */ jsx2(
|
|
1822
|
-
"textarea",
|
|
1823
|
-
{
|
|
1824
|
-
ref: inputRef,
|
|
1825
|
-
className: "composer__input",
|
|
1826
|
-
rows: 1,
|
|
1827
|
-
value,
|
|
1828
|
-
placeholder,
|
|
1829
|
-
disabled,
|
|
1830
|
-
"aria-label": "Message",
|
|
1831
|
-
onChange: (event) => setValue(event.target.value),
|
|
1832
|
-
onKeyDown: handleKeyDown
|
|
1833
|
-
}
|
|
1834
|
-
),
|
|
1835
|
-
/* @__PURE__ */ jsx2(
|
|
1836
|
-
"button",
|
|
1837
|
-
{
|
|
1838
|
-
type: "submit",
|
|
1839
|
-
className: "composer__send",
|
|
1840
|
-
disabled: disabled || !value.trim(),
|
|
1841
|
-
"aria-label": "Send message",
|
|
1842
|
-
children: /* @__PURE__ */ jsx2(SendIcon, {})
|
|
1843
|
-
}
|
|
1844
|
-
)
|
|
1845
|
-
] }) });
|
|
3287
|
+
);
|
|
1846
3288
|
}
|
|
1847
3289
|
|
|
1848
3290
|
// src/react/components/FollowUpChips/FollowUpChips.tsx
|
|
@@ -1888,11 +3330,17 @@ function FollowUpChips({
|
|
|
1888
3330
|
import { useState as useState5 } from "react";
|
|
1889
3331
|
|
|
1890
3332
|
// src/react/components/BookingCard/BookingCard.tsx
|
|
1891
|
-
import { useId, useMemo as useMemo2, useState as useState4 } from "react";
|
|
3333
|
+
import { useId as useId2, useMemo as useMemo2, useState as useState4 } from "react";
|
|
1892
3334
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3335
|
+
var BOOKING_STEPS = [
|
|
3336
|
+
{ id: "date", label: "Date" },
|
|
3337
|
+
{ id: "time", label: "Time" },
|
|
3338
|
+
{ id: "details", label: "Details" }
|
|
3339
|
+
];
|
|
1893
3340
|
function monthFromKey(key) {
|
|
1894
3341
|
const [year, month] = key.split("-").map(Number);
|
|
1895
|
-
if (!year || !month)
|
|
3342
|
+
if (!year || !month)
|
|
3343
|
+
return { year: (/* @__PURE__ */ new Date()).getFullYear(), month: (/* @__PURE__ */ new Date()).getMonth() };
|
|
1896
3344
|
return { year, month: month - 1 };
|
|
1897
3345
|
}
|
|
1898
3346
|
function dateKeyFromParts(year, month, day) {
|
|
@@ -1917,7 +3365,7 @@ function BookingCard({
|
|
|
1917
3365
|
offer,
|
|
1918
3366
|
onBook
|
|
1919
3367
|
}) {
|
|
1920
|
-
const fieldId =
|
|
3368
|
+
const fieldId = useId2();
|
|
1921
3369
|
const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
|
|
1922
3370
|
const [step, setStep] = useState4("date");
|
|
1923
3371
|
const [eventTypeUri, setEventTypeUri] = useState4(defaultType);
|
|
@@ -1925,6 +3373,7 @@ function BookingCard({
|
|
|
1925
3373
|
const [startTime, setStartTime] = useState4("");
|
|
1926
3374
|
const [name, setName] = useState4("");
|
|
1927
3375
|
const [email, setEmail] = useState4("");
|
|
3376
|
+
const stepIndex = BOOKING_STEPS.findIndex((item) => item.id === step);
|
|
1928
3377
|
const slots = useMemo2(
|
|
1929
3378
|
() => bookingSlotsForEventType(offer.slots, eventTypeUri),
|
|
1930
3379
|
[eventTypeUri, offer.slots]
|
|
@@ -1945,14 +3394,18 @@ function BookingCard({
|
|
|
1945
3394
|
setSelectedDate("");
|
|
1946
3395
|
setStartTime("");
|
|
1947
3396
|
setVisibleMonth(
|
|
1948
|
-
firstAvailableBookingMonth(
|
|
3397
|
+
firstAvailableBookingMonth(
|
|
3398
|
+
bookingSlotsForEventType(offer.slots, nextType)
|
|
3399
|
+
)
|
|
1949
3400
|
);
|
|
1950
3401
|
}
|
|
1951
3402
|
const daySlots = useMemo2(
|
|
1952
3403
|
() => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
|
|
1953
3404
|
[selectedDate, slots]
|
|
1954
3405
|
);
|
|
1955
|
-
const selectedType = offer.eventTypes.find(
|
|
3406
|
+
const selectedType = offer.eventTypes.find(
|
|
3407
|
+
(item) => item.uri === eventTypeUri
|
|
3408
|
+
);
|
|
1956
3409
|
const selectedSample = availableByDate.get(selectedDate) ?? startTime;
|
|
1957
3410
|
const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
|
|
1958
3411
|
const weekdays = useMemo2(() => weekdayLabels(), []);
|
|
@@ -1998,24 +3451,47 @@ function BookingCard({
|
|
|
1998
3451
|
});
|
|
1999
3452
|
}
|
|
2000
3453
|
return /* @__PURE__ */ jsx4("section", { className: "booking-card", "aria-label": "Book a meeting", children: /* @__PURE__ */ jsxs4("form", { className: "booking-card__form", onSubmit: handleSubmit, children: [
|
|
3454
|
+
/* @__PURE__ */ jsx4("ol", { className: "booking-card__steps", "aria-label": "Booking steps", children: BOOKING_STEPS.map((item, index) => /* @__PURE__ */ jsxs4(
|
|
3455
|
+
"li",
|
|
3456
|
+
{
|
|
3457
|
+
className: [
|
|
3458
|
+
"booking-card__step-indicator",
|
|
3459
|
+
index === stepIndex ? "booking-card__step-indicator--active" : "",
|
|
3460
|
+
index < stepIndex ? "booking-card__step-indicator--complete" : ""
|
|
3461
|
+
].filter(Boolean).join(" "),
|
|
3462
|
+
"aria-current": index === stepIndex ? "step" : void 0,
|
|
3463
|
+
children: [
|
|
3464
|
+
/* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: index + 1 }),
|
|
3465
|
+
/* @__PURE__ */ jsx4("span", { children: item.label })
|
|
3466
|
+
]
|
|
3467
|
+
},
|
|
3468
|
+
item.id
|
|
3469
|
+
)) }),
|
|
2001
3470
|
step === "date" ? /* @__PURE__ */ jsxs4("div", { className: "booking-card__step", children: [
|
|
2002
3471
|
/* @__PURE__ */ jsx4("p", { className: "booking-card__title", children: selectedType?.name || "Pick a date" }),
|
|
2003
3472
|
timeZone ? /* @__PURE__ */ jsxs4("p", { className: "booking-card__tz", children: [
|
|
2004
3473
|
"Times in ",
|
|
2005
3474
|
timeZone
|
|
2006
3475
|
] }) : null,
|
|
2007
|
-
offer.eventTypes.length > 1 ? /* @__PURE__ */ jsxs4(
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
"
|
|
2011
|
-
{
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
3476
|
+
offer.eventTypes.length > 1 ? /* @__PURE__ */ jsxs4(
|
|
3477
|
+
"label",
|
|
3478
|
+
{
|
|
3479
|
+
className: "booking-card__field",
|
|
3480
|
+
htmlFor: `${fieldId}-type`,
|
|
3481
|
+
children: [
|
|
3482
|
+
/* @__PURE__ */ jsx4("span", { children: "Meeting" }),
|
|
3483
|
+
/* @__PURE__ */ jsx4(
|
|
3484
|
+
"select",
|
|
3485
|
+
{
|
|
3486
|
+
id: `${fieldId}-type`,
|
|
3487
|
+
value: eventTypeUri,
|
|
3488
|
+
onChange: (event) => selectEventType(event.target.value),
|
|
3489
|
+
children: offer.eventTypes.map((item) => /* @__PURE__ */ jsx4("option", { value: item.uri, children: item.name }, item.uri))
|
|
3490
|
+
}
|
|
3491
|
+
)
|
|
3492
|
+
]
|
|
3493
|
+
}
|
|
3494
|
+
) : null,
|
|
2019
3495
|
/* @__PURE__ */ jsxs4("div", { className: "booking-card__month", children: [
|
|
2020
3496
|
/* @__PURE__ */ jsx4(
|
|
2021
3497
|
"button",
|
|
@@ -2042,29 +3518,44 @@ function BookingCard({
|
|
|
2042
3518
|
)
|
|
2043
3519
|
] }),
|
|
2044
3520
|
/* @__PURE__ */ jsx4("div", { className: "booking-card__weekdays", children: weekdays.map((label) => /* @__PURE__ */ jsx4("span", { children: label }, label)) }),
|
|
2045
|
-
/* @__PURE__ */ jsx4("
|
|
2046
|
-
|
|
2047
|
-
|
|
3521
|
+
offer.slots.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "booking-card__loading", role: "status", children: "Finding available times\u2026" }) : null,
|
|
3522
|
+
/* @__PURE__ */ jsx4(
|
|
3523
|
+
"div",
|
|
3524
|
+
{
|
|
3525
|
+
className: "booking-card__calendar",
|
|
3526
|
+
role: "grid",
|
|
3527
|
+
"aria-label": "Available dates",
|
|
3528
|
+
children: cells.map((cell, index) => {
|
|
3529
|
+
if (!cell) {
|
|
3530
|
+
return /* @__PURE__ */ jsx4(
|
|
3531
|
+
"span",
|
|
3532
|
+
{
|
|
3533
|
+
className: "booking-card__day"
|
|
3534
|
+
},
|
|
3535
|
+
`empty-${index}`
|
|
3536
|
+
);
|
|
3537
|
+
}
|
|
3538
|
+
const available = availableByDate.has(cell.key);
|
|
3539
|
+
const selected = cell.key === selectedDate;
|
|
3540
|
+
return /* @__PURE__ */ jsx4(
|
|
3541
|
+
"button",
|
|
3542
|
+
{
|
|
3543
|
+
type: "button",
|
|
3544
|
+
className: [
|
|
3545
|
+
"booking-card__day",
|
|
3546
|
+
available ? "booking-card__day--available" : "",
|
|
3547
|
+
selected ? "booking-card__day--selected" : ""
|
|
3548
|
+
].filter(Boolean).join(" "),
|
|
3549
|
+
disabled: !available,
|
|
3550
|
+
"aria-pressed": selected,
|
|
3551
|
+
onClick: () => selectDate(cell.key),
|
|
3552
|
+
children: cell.day
|
|
3553
|
+
},
|
|
3554
|
+
cell.key
|
|
3555
|
+
);
|
|
3556
|
+
})
|
|
2048
3557
|
}
|
|
2049
|
-
|
|
2050
|
-
const selected = cell.key === selectedDate;
|
|
2051
|
-
return /* @__PURE__ */ jsx4(
|
|
2052
|
-
"button",
|
|
2053
|
-
{
|
|
2054
|
-
type: "button",
|
|
2055
|
-
className: [
|
|
2056
|
-
"booking-card__day",
|
|
2057
|
-
available ? "booking-card__day--available" : "",
|
|
2058
|
-
selected ? "booking-card__day--selected" : ""
|
|
2059
|
-
].filter(Boolean).join(" "),
|
|
2060
|
-
disabled: !available,
|
|
2061
|
-
"aria-pressed": selected,
|
|
2062
|
-
onClick: () => selectDate(cell.key),
|
|
2063
|
-
children: cell.day
|
|
2064
|
-
},
|
|
2065
|
-
cell.key
|
|
2066
|
-
);
|
|
2067
|
-
}) })
|
|
3558
|
+
)
|
|
2068
3559
|
] }, "date") : null,
|
|
2069
3560
|
step === "time" ? /* @__PURE__ */ jsxs4("div", { className: "booking-card__step", children: [
|
|
2070
3561
|
/* @__PURE__ */ jsxs4("div", { className: "booking-card__step-bar", children: [
|
|
@@ -2116,35 +3607,57 @@ function BookingCard({
|
|
|
2116
3607
|
] })
|
|
2117
3608
|
] }),
|
|
2118
3609
|
/* @__PURE__ */ jsxs4("div", { className: "booking-card__identity", children: [
|
|
2119
|
-
/* @__PURE__ */ jsxs4(
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
"
|
|
2123
|
-
{
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
}
|
|
2144
|
-
|
|
2145
|
-
|
|
3610
|
+
/* @__PURE__ */ jsxs4(
|
|
3611
|
+
"label",
|
|
3612
|
+
{
|
|
3613
|
+
className: "booking-card__field",
|
|
3614
|
+
htmlFor: `${fieldId}-name`,
|
|
3615
|
+
children: [
|
|
3616
|
+
/* @__PURE__ */ jsx4("span", { children: "Name" }),
|
|
3617
|
+
/* @__PURE__ */ jsx4(
|
|
3618
|
+
"input",
|
|
3619
|
+
{
|
|
3620
|
+
id: `${fieldId}-name`,
|
|
3621
|
+
autoComplete: "name",
|
|
3622
|
+
value: name,
|
|
3623
|
+
onChange: (event) => setName(event.target.value),
|
|
3624
|
+
required: true
|
|
3625
|
+
}
|
|
3626
|
+
)
|
|
3627
|
+
]
|
|
3628
|
+
}
|
|
3629
|
+
),
|
|
3630
|
+
/* @__PURE__ */ jsxs4(
|
|
3631
|
+
"label",
|
|
3632
|
+
{
|
|
3633
|
+
className: "booking-card__field",
|
|
3634
|
+
htmlFor: `${fieldId}-email`,
|
|
3635
|
+
children: [
|
|
3636
|
+
/* @__PURE__ */ jsx4("span", { children: "Email" }),
|
|
3637
|
+
/* @__PURE__ */ jsx4(
|
|
3638
|
+
"input",
|
|
3639
|
+
{
|
|
3640
|
+
id: `${fieldId}-email`,
|
|
3641
|
+
type: "email",
|
|
3642
|
+
autoComplete: "email",
|
|
3643
|
+
value: email,
|
|
3644
|
+
onChange: (event) => setEmail(event.target.value),
|
|
3645
|
+
required: true
|
|
3646
|
+
}
|
|
3647
|
+
)
|
|
3648
|
+
]
|
|
3649
|
+
}
|
|
3650
|
+
)
|
|
2146
3651
|
] }),
|
|
2147
|
-
/* @__PURE__ */ jsx4(
|
|
3652
|
+
/* @__PURE__ */ jsx4(
|
|
3653
|
+
"button",
|
|
3654
|
+
{
|
|
3655
|
+
type: "submit",
|
|
3656
|
+
className: "booking-card__submit",
|
|
3657
|
+
disabled: !name.trim() || !email.trim(),
|
|
3658
|
+
children: "Book this time"
|
|
3659
|
+
}
|
|
3660
|
+
)
|
|
2148
3661
|
] }, "details") : null
|
|
2149
3662
|
] }) });
|
|
2150
3663
|
}
|
|
@@ -2153,6 +3666,43 @@ function BookingCard({
|
|
|
2153
3666
|
import { Streamdown } from "streamdown";
|
|
2154
3667
|
import "streamdown/styles.css";
|
|
2155
3668
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3669
|
+
function normalizeDedupeText(text) {
|
|
3670
|
+
return text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
3671
|
+
}
|
|
3672
|
+
function paragraphsAreNearDuplicates(first, second) {
|
|
3673
|
+
const left = normalizeDedupeText(first);
|
|
3674
|
+
const right = normalizeDedupeText(second);
|
|
3675
|
+
if (left.length < 40 || right.length < 40) return false;
|
|
3676
|
+
if (left === right) return true;
|
|
3677
|
+
const shorter = left.length <= right.length ? left : right;
|
|
3678
|
+
const longer = left.length <= right.length ? right : left;
|
|
3679
|
+
return longer.startsWith(
|
|
3680
|
+
shorter.slice(0, Math.floor(shorter.length * 0.85))
|
|
3681
|
+
);
|
|
3682
|
+
}
|
|
3683
|
+
function paragraphsShareOpening(first, second) {
|
|
3684
|
+
const opening = first.split("\n")[0]?.trim();
|
|
3685
|
+
if (!opening || opening.length < 20) return false;
|
|
3686
|
+
return second.trim().startsWith(opening);
|
|
3687
|
+
}
|
|
3688
|
+
function collapseRepeatedText(text) {
|
|
3689
|
+
const trimmed = text.trim();
|
|
3690
|
+
if (trimmed.length < 40) return trimmed;
|
|
3691
|
+
const paragraphs = trimmed.split(/\n{2,}/u).map((part) => part.trim()).filter(Boolean);
|
|
3692
|
+
if (paragraphs.length === 2 && (paragraphsAreNearDuplicates(paragraphs[0], paragraphs[1]) || paragraphsShareOpening(paragraphs[0], paragraphs[1]))) {
|
|
3693
|
+
return paragraphs[0];
|
|
3694
|
+
}
|
|
3695
|
+
if (paragraphs.length >= 2 && paragraphs.length % 2 === 0) {
|
|
3696
|
+
const mid2 = paragraphs.length / 2;
|
|
3697
|
+
const first = paragraphs.slice(0, mid2).join("\n\n");
|
|
3698
|
+
const second = paragraphs.slice(mid2).join("\n\n");
|
|
3699
|
+
if (first === second) return first;
|
|
3700
|
+
}
|
|
3701
|
+
const mid = Math.floor(trimmed.length / 2);
|
|
3702
|
+
const left = trimmed.slice(0, mid).trim();
|
|
3703
|
+
const right = trimmed.slice(mid).trim();
|
|
3704
|
+
return left.length >= 20 && left === right ? left : trimmed;
|
|
3705
|
+
}
|
|
2156
3706
|
function MessageBubble({
|
|
2157
3707
|
message,
|
|
2158
3708
|
brandLogoUrl,
|
|
@@ -2169,24 +3719,41 @@ function MessageBubble({
|
|
|
2169
3719
|
const offers = offer ? [offer] : extractedOffers;
|
|
2170
3720
|
const visibleText = hideToolCardFences(message.text);
|
|
2171
3721
|
const isStreaming = message.role === "agent" && Boolean(message.streaming);
|
|
2172
|
-
const displayText =
|
|
3722
|
+
const displayText = collapseRepeatedText(
|
|
3723
|
+
offers.length > 0 ? sanitizeBookingOfferCopy(visibleText) : looksLikeBookingAvailabilityDump(visibleText) ? sanitizeBookingOfferCopy(visibleText) : visibleText || (isStreaming ? "" : message.text)
|
|
3724
|
+
);
|
|
2173
3725
|
if (message.role === "visitor") {
|
|
2174
3726
|
return /* @__PURE__ */ jsx5("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ jsx5("p", { className: "message-bubble__text", children: message.text }) });
|
|
2175
3727
|
}
|
|
2176
|
-
const
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
3728
|
+
const citations = message.citations ?? [];
|
|
3729
|
+
const agentText = /* @__PURE__ */ jsxs5("div", { className: "message-bubble__text", children: [
|
|
3730
|
+
/* @__PURE__ */ jsx5(
|
|
3731
|
+
Streamdown,
|
|
3732
|
+
{
|
|
3733
|
+
animated: isStreaming,
|
|
3734
|
+
caret: "circle",
|
|
3735
|
+
className: "message-bubble__markdown",
|
|
3736
|
+
controls: false,
|
|
3737
|
+
isAnimating: isStreaming,
|
|
3738
|
+
linkSafety: { enabled: false },
|
|
3739
|
+
mode: isStreaming ? "streaming" : "static",
|
|
3740
|
+
skipHtml: true,
|
|
3741
|
+
children: displayText
|
|
3742
|
+
}
|
|
3743
|
+
),
|
|
3744
|
+
citations.length > 0 ? /* @__PURE__ */ jsx5("ul", { className: "message-bubble__sources", "aria-label": "Sources", children: citations.map((citation) => /* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsxs5(
|
|
3745
|
+
"a",
|
|
3746
|
+
{
|
|
3747
|
+
href: citation.url,
|
|
3748
|
+
target: "_blank",
|
|
3749
|
+
rel: "noreferrer",
|
|
3750
|
+
children: [
|
|
3751
|
+
/* @__PURE__ */ jsx5("span", { className: "message-bubble__source-icon", "aria-hidden": "true", children: "\u25A6" }),
|
|
3752
|
+
/* @__PURE__ */ jsx5("span", { children: citation.label })
|
|
3753
|
+
]
|
|
3754
|
+
}
|
|
3755
|
+
) }, citation.id)) }) : null
|
|
3756
|
+
] });
|
|
2190
3757
|
return /* @__PURE__ */ jsxs5("article", { className: "message-bubble message-bubble--agent", children: [
|
|
2191
3758
|
displayText ? showBrandLogo ? /* @__PURE__ */ jsxs5("div", { className: "message-bubble__agent-row", children: [
|
|
2192
3759
|
/* @__PURE__ */ jsx5("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
|
|
@@ -2212,10 +3779,262 @@ function MessageBubble({
|
|
|
2212
3779
|
] });
|
|
2213
3780
|
}
|
|
2214
3781
|
|
|
2215
|
-
// src/react/components/
|
|
3782
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3783
|
+
import { useState as useState6 } from "react";
|
|
3784
|
+
|
|
3785
|
+
// src/react/components/ConfirmationCard/ConfirmationCard.tsx
|
|
2216
3786
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3787
|
+
function ConfirmationCard({
|
|
3788
|
+
disabled = false,
|
|
3789
|
+
request,
|
|
3790
|
+
onRespond
|
|
3791
|
+
}) {
|
|
3792
|
+
const options = request.options ?? [];
|
|
3793
|
+
const heading = request.kind === "tool-approval" ? "Confirm this action" : request.prompt;
|
|
3794
|
+
const prompt = request.kind === "tool-approval" ? request.prompt : void 0;
|
|
3795
|
+
return /* @__PURE__ */ jsxs6(
|
|
3796
|
+
"section",
|
|
3797
|
+
{
|
|
3798
|
+
className: "confirmation-card",
|
|
3799
|
+
"aria-labelledby": `confirmation-${request.requestId}`,
|
|
3800
|
+
children: [
|
|
3801
|
+
/* @__PURE__ */ jsxs6("div", { className: "confirmation-card__heading", children: [
|
|
3802
|
+
/* @__PURE__ */ jsx6("strong", { id: `confirmation-${request.requestId}`, children: heading }),
|
|
3803
|
+
prompt ? /* @__PURE__ */ jsx6("p", { children: prompt }) : null
|
|
3804
|
+
] }),
|
|
3805
|
+
/* @__PURE__ */ jsx6("div", { className: "confirmation-card__actions", children: options.map((option) => /* @__PURE__ */ jsx6(
|
|
3806
|
+
"button",
|
|
3807
|
+
{
|
|
3808
|
+
type: "button",
|
|
3809
|
+
className: `confirmation-card__action confirmation-card__action--${option.style ?? "default"}`,
|
|
3810
|
+
disabled,
|
|
3811
|
+
onClick: () => onRespond?.({
|
|
3812
|
+
requestId: request.requestId,
|
|
3813
|
+
optionId: option.id
|
|
3814
|
+
}),
|
|
3815
|
+
children: option.label
|
|
3816
|
+
},
|
|
3817
|
+
option.id
|
|
3818
|
+
)) })
|
|
3819
|
+
]
|
|
3820
|
+
}
|
|
3821
|
+
);
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3824
|
+
// src/react/components/HumanInputCard/HumanInputCard.tsx
|
|
3825
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3826
|
+
function HumanInputCard({
|
|
3827
|
+
disabled = false,
|
|
3828
|
+
request,
|
|
3829
|
+
onRespond
|
|
3830
|
+
}) {
|
|
3831
|
+
const [text, setText] = useState6("");
|
|
3832
|
+
const options = request.options ?? [];
|
|
3833
|
+
const showText = request.display === "text" || request.allowFreeform && options.length === 0;
|
|
3834
|
+
if (options.length > 0) {
|
|
3835
|
+
return /* @__PURE__ */ jsx7(
|
|
3836
|
+
ConfirmationCard,
|
|
3837
|
+
{
|
|
3838
|
+
disabled,
|
|
3839
|
+
request,
|
|
3840
|
+
onRespond
|
|
3841
|
+
}
|
|
3842
|
+
);
|
|
3843
|
+
}
|
|
3844
|
+
function submitText(event) {
|
|
3845
|
+
event.preventDefault();
|
|
3846
|
+
const value = text.trim();
|
|
3847
|
+
if (!value || disabled) return;
|
|
3848
|
+
onRespond?.({ requestId: request.requestId, text: value });
|
|
3849
|
+
}
|
|
3850
|
+
return /* @__PURE__ */ jsxs7(
|
|
3851
|
+
"section",
|
|
3852
|
+
{
|
|
3853
|
+
className: "human-input-card",
|
|
3854
|
+
"aria-labelledby": `human-input-${request.requestId}`,
|
|
3855
|
+
children: [
|
|
3856
|
+
/* @__PURE__ */ jsx7("div", { className: "human-input-card__heading", children: /* @__PURE__ */ jsx7("strong", { id: `human-input-${request.requestId}`, children: request.prompt }) }),
|
|
3857
|
+
showText ? /* @__PURE__ */ jsxs7("form", { onSubmit: submitText, children: [
|
|
3858
|
+
/* @__PURE__ */ jsx7("label", { htmlFor: `human-input-text-${request.requestId}`, children: "Response" }),
|
|
3859
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
3860
|
+
/* @__PURE__ */ jsx7(
|
|
3861
|
+
"input",
|
|
3862
|
+
{
|
|
3863
|
+
id: `human-input-text-${request.requestId}`,
|
|
3864
|
+
value: text,
|
|
3865
|
+
disabled,
|
|
3866
|
+
onChange: (event) => setText(event.target.value)
|
|
3867
|
+
}
|
|
3868
|
+
),
|
|
3869
|
+
/* @__PURE__ */ jsx7("button", { type: "submit", disabled: disabled || !text.trim(), children: "Send" })
|
|
3870
|
+
] })
|
|
3871
|
+
] }) : null,
|
|
3872
|
+
!showText && options.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "human-input-card__unavailable", role: "status", children: "This request can\u2019t be answered here." }) : null
|
|
3873
|
+
]
|
|
3874
|
+
}
|
|
3875
|
+
);
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
// src/react/components/CollectionResultCard/CollectionResultCard.tsx
|
|
3879
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3880
|
+
function CollectionResultCard({
|
|
3881
|
+
result
|
|
3882
|
+
}) {
|
|
3883
|
+
const empty = result.items.length === 0;
|
|
3884
|
+
return /* @__PURE__ */ jsxs8(
|
|
3885
|
+
"section",
|
|
3886
|
+
{
|
|
3887
|
+
className: `collection-result-card tool-result-card tool-result-card--${result.status}`,
|
|
3888
|
+
"aria-label": result.title,
|
|
3889
|
+
children: [
|
|
3890
|
+
/* @__PURE__ */ jsxs8("div", { className: "tool-result-card__heading", children: [
|
|
3891
|
+
/* @__PURE__ */ jsx8("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
3892
|
+
/* @__PURE__ */ jsx8("strong", { children: result.title })
|
|
3893
|
+
] }),
|
|
3894
|
+
empty ? /* @__PURE__ */ jsx8("p", { className: "collection-result-card__empty", children: "No matching record for the email you shared." }) : /* @__PURE__ */ jsx8("ul", { className: "collection-result-card__list", children: result.items.map((item) => /* @__PURE__ */ jsxs8("li", { children: [
|
|
3895
|
+
/* @__PURE__ */ jsx8("div", { className: "collection-result-card__item-title", children: item.title }),
|
|
3896
|
+
item.description ? /* @__PURE__ */ jsx8("p", { children: item.description }) : null,
|
|
3897
|
+
item.details?.length ? /* @__PURE__ */ jsx8("dl", { children: item.details.map((detail) => /* @__PURE__ */ jsxs8("div", { children: [
|
|
3898
|
+
/* @__PURE__ */ jsx8("dt", { children: detail.label }),
|
|
3899
|
+
/* @__PURE__ */ jsx8("dd", { children: detail.value })
|
|
3900
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
3901
|
+
item.href ? /* @__PURE__ */ jsx8("a", { href: item.href, target: "_blank", rel: "noreferrer", children: "Open record" }) : null
|
|
3902
|
+
] }, item.title)) })
|
|
3903
|
+
]
|
|
3904
|
+
}
|
|
3905
|
+
);
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3908
|
+
// src/react/components/EntityResultCard/EntityResultCard.tsx
|
|
3909
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3910
|
+
function EntityResultCard({
|
|
3911
|
+
result
|
|
3912
|
+
}) {
|
|
3913
|
+
return /* @__PURE__ */ jsxs9(
|
|
3914
|
+
"section",
|
|
3915
|
+
{
|
|
3916
|
+
className: `entity-result-card tool-result-card tool-result-card--${result.status}`,
|
|
3917
|
+
"aria-label": result.title,
|
|
3918
|
+
children: [
|
|
3919
|
+
/* @__PURE__ */ jsxs9("div", { className: "tool-result-card__heading", children: [
|
|
3920
|
+
/* @__PURE__ */ jsx9("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
3921
|
+
/* @__PURE__ */ jsx9("strong", { children: result.title })
|
|
3922
|
+
] }),
|
|
3923
|
+
result.description ? /* @__PURE__ */ jsx9("p", { children: result.description }) : null,
|
|
3924
|
+
result.details?.length ? /* @__PURE__ */ jsx9("dl", { children: result.details.map((detail) => /* @__PURE__ */ jsxs9("div", { children: [
|
|
3925
|
+
/* @__PURE__ */ jsx9("dt", { children: detail.label }),
|
|
3926
|
+
/* @__PURE__ */ jsx9("dd", { children: detail.value })
|
|
3927
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
3928
|
+
result.links?.length ? /* @__PURE__ */ jsx9("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ jsx9(
|
|
3929
|
+
"a",
|
|
3930
|
+
{
|
|
3931
|
+
href: link.href,
|
|
3932
|
+
target: "_blank",
|
|
3933
|
+
rel: "noreferrer",
|
|
3934
|
+
children: link.label
|
|
3935
|
+
},
|
|
3936
|
+
link.href
|
|
3937
|
+
)) }) : null
|
|
3938
|
+
]
|
|
3939
|
+
}
|
|
3940
|
+
);
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
// src/react/components/SignatureResultCard/SignatureResultCard.tsx
|
|
3944
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3945
|
+
function SignatureResultCard({
|
|
3946
|
+
result
|
|
3947
|
+
}) {
|
|
3948
|
+
const primaryLink = result.links?.[0];
|
|
3949
|
+
return /* @__PURE__ */ jsxs10(
|
|
3950
|
+
"section",
|
|
3951
|
+
{
|
|
3952
|
+
className: `signature-result-card tool-result-card tool-result-card--${result.status}`,
|
|
3953
|
+
"aria-label": result.title,
|
|
3954
|
+
children: [
|
|
3955
|
+
/* @__PURE__ */ jsxs10("div", { className: "tool-result-card__heading", children: [
|
|
3956
|
+
/* @__PURE__ */ jsx10("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
3957
|
+
/* @__PURE__ */ jsx10("strong", { children: result.title })
|
|
3958
|
+
] }),
|
|
3959
|
+
result.statusLabel ? /* @__PURE__ */ jsx10("span", { className: "signature-result-card__badge", children: result.statusLabel }) : null,
|
|
3960
|
+
result.description ? /* @__PURE__ */ jsx10("p", { children: result.description }) : null,
|
|
3961
|
+
primaryLink ? /* @__PURE__ */ jsx10(
|
|
3962
|
+
"a",
|
|
3963
|
+
{
|
|
3964
|
+
className: "signature-result-card__cta",
|
|
3965
|
+
href: primaryLink.href,
|
|
3966
|
+
target: "_blank",
|
|
3967
|
+
rel: "noreferrer",
|
|
3968
|
+
children: primaryLink.label
|
|
3969
|
+
}
|
|
3970
|
+
) : null
|
|
3971
|
+
]
|
|
3972
|
+
}
|
|
3973
|
+
);
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
// src/react/components/ToolResultCard/ToolResultCard.tsx
|
|
3977
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3978
|
+
function ToolResultCard({
|
|
3979
|
+
result
|
|
3980
|
+
}) {
|
|
3981
|
+
return /* @__PURE__ */ jsxs11(
|
|
3982
|
+
"section",
|
|
3983
|
+
{
|
|
3984
|
+
className: `tool-result-card tool-result-card--${result.status}`,
|
|
3985
|
+
"aria-label": result.title,
|
|
3986
|
+
children: [
|
|
3987
|
+
/* @__PURE__ */ jsxs11("div", { className: "tool-result-card__heading", children: [
|
|
3988
|
+
/* @__PURE__ */ jsx11("span", { className: "tool-result-card__status", "aria-hidden": "true" }),
|
|
3989
|
+
/* @__PURE__ */ jsx11("strong", { children: result.title })
|
|
3990
|
+
] }),
|
|
3991
|
+
result.description ? /* @__PURE__ */ jsx11("p", { children: result.description }) : null,
|
|
3992
|
+
result.details?.length ? /* @__PURE__ */ jsx11("dl", { children: result.details.map((detail) => /* @__PURE__ */ jsxs11("div", { children: [
|
|
3993
|
+
/* @__PURE__ */ jsx11("dt", { children: detail.label }),
|
|
3994
|
+
/* @__PURE__ */ jsx11("dd", { children: detail.value })
|
|
3995
|
+
] }, `${detail.label}:${detail.value}`)) }) : null,
|
|
3996
|
+
result.links?.length ? /* @__PURE__ */ jsx11("div", { className: "tool-result-card__links", children: result.links.map((link) => /* @__PURE__ */ jsx11(
|
|
3997
|
+
"a",
|
|
3998
|
+
{
|
|
3999
|
+
href: link.href,
|
|
4000
|
+
target: "_blank",
|
|
4001
|
+
rel: "noreferrer",
|
|
4002
|
+
children: link.label
|
|
4003
|
+
},
|
|
4004
|
+
link.href
|
|
4005
|
+
)) }) : null
|
|
4006
|
+
]
|
|
4007
|
+
}
|
|
4008
|
+
);
|
|
4009
|
+
}
|
|
4010
|
+
|
|
4011
|
+
// src/react/components/VisitorToolResultView/VisitorToolResultView.tsx
|
|
4012
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
4013
|
+
function VisitorToolResultView({
|
|
4014
|
+
result
|
|
4015
|
+
}) {
|
|
4016
|
+
if (result.kind === "entity") {
|
|
4017
|
+
return /* @__PURE__ */ jsx12(EntityResultCard, { result });
|
|
4018
|
+
}
|
|
4019
|
+
if (result.kind === "collection") {
|
|
4020
|
+
return /* @__PURE__ */ jsx12(CollectionResultCard, { result });
|
|
4021
|
+
}
|
|
4022
|
+
if (result.kind === "signature") {
|
|
4023
|
+
return /* @__PURE__ */ jsx12(SignatureResultCard, { result });
|
|
4024
|
+
}
|
|
4025
|
+
if (result.kind === "summary") {
|
|
4026
|
+
return /* @__PURE__ */ jsx12(ToolResultCard, { result });
|
|
4027
|
+
}
|
|
4028
|
+
return null;
|
|
4029
|
+
}
|
|
4030
|
+
function isRenderableVisitorToolResult(result) {
|
|
4031
|
+
return result.kind === "summary" || result.kind === "entity" || result.kind === "collection" || result.kind === "signature";
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
// src/react/components/AgentRail/AgentRail.tsx
|
|
4035
|
+
import { Fragment, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2217
4036
|
function MinimizeIcon() {
|
|
2218
|
-
return /* @__PURE__ */
|
|
4037
|
+
return /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(
|
|
2219
4038
|
"path",
|
|
2220
4039
|
{
|
|
2221
4040
|
d: "M3.5 8h9",
|
|
@@ -2226,7 +4045,7 @@ function MinimizeIcon() {
|
|
|
2226
4045
|
) });
|
|
2227
4046
|
}
|
|
2228
4047
|
function CloseIcon() {
|
|
2229
|
-
return /* @__PURE__ */
|
|
4048
|
+
return /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(
|
|
2230
4049
|
"path",
|
|
2231
4050
|
{
|
|
2232
4051
|
d: "M4 4l8 8M12 4l-8 8",
|
|
@@ -2237,7 +4056,7 @@ function CloseIcon() {
|
|
|
2237
4056
|
) });
|
|
2238
4057
|
}
|
|
2239
4058
|
function NewChatIcon() {
|
|
2240
|
-
return /* @__PURE__ */
|
|
4059
|
+
return /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(
|
|
2241
4060
|
"path",
|
|
2242
4061
|
{
|
|
2243
4062
|
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",
|
|
@@ -2249,7 +4068,7 @@ function NewChatIcon() {
|
|
|
2249
4068
|
) });
|
|
2250
4069
|
}
|
|
2251
4070
|
function ExpandIcon() {
|
|
2252
|
-
return /* @__PURE__ */
|
|
4071
|
+
return /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(
|
|
2253
4072
|
"path",
|
|
2254
4073
|
{
|
|
2255
4074
|
d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
|
|
@@ -2261,7 +4080,7 @@ function ExpandIcon() {
|
|
|
2261
4080
|
) });
|
|
2262
4081
|
}
|
|
2263
4082
|
function RestoreIcon() {
|
|
2264
|
-
return /* @__PURE__ */
|
|
4083
|
+
return /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(
|
|
2265
4084
|
"path",
|
|
2266
4085
|
{
|
|
2267
4086
|
d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
|
|
@@ -2289,28 +4108,29 @@ function AgentRail({
|
|
|
2289
4108
|
onRetry,
|
|
2290
4109
|
onSubmit,
|
|
2291
4110
|
onFollowUpSelect,
|
|
2292
|
-
onBook
|
|
4111
|
+
onBook,
|
|
4112
|
+
onInputResponse
|
|
2293
4113
|
}) {
|
|
2294
4114
|
const transcriptRef = useRef3(null);
|
|
2295
4115
|
const resolvedBrandLabel = brandLabel.trim();
|
|
2296
4116
|
const resolvedBrandLogoUrl = brandLogoUrl?.trim();
|
|
2297
|
-
const [failedLogoUrl, setFailedLogoUrl] =
|
|
4117
|
+
const [failedLogoUrl, setFailedLogoUrl] = useState7(null);
|
|
2298
4118
|
const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
|
|
2299
4119
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2300
4120
|
const brandedTheme = { ...defaultAgentRailTheme, ...theme };
|
|
2301
4121
|
const resolvedTheme = resolvedColorScheme === "dark" ? {
|
|
2302
4122
|
...brandedTheme,
|
|
2303
4123
|
brand: theme?.brand ?? defaultDarkAgentRailTheme.brand,
|
|
2304
|
-
brandDeep: defaultDarkAgentRailTheme.brandDeep,
|
|
2305
|
-
brandSoft: `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
|
|
2306
|
-
border: defaultDarkAgentRailTheme.border,
|
|
2307
|
-
danger: defaultDarkAgentRailTheme.danger,
|
|
2308
|
-
success: defaultDarkAgentRailTheme.success,
|
|
2309
|
-
surface: defaultDarkAgentRailTheme.surface,
|
|
2310
|
-
surfaceMuted: defaultDarkAgentRailTheme.surfaceMuted,
|
|
2311
|
-
text: defaultDarkAgentRailTheme.text,
|
|
2312
|
-
textMuted: defaultDarkAgentRailTheme.textMuted,
|
|
2313
|
-
textSubtle: defaultDarkAgentRailTheme.textSubtle,
|
|
4124
|
+
brandDeep: theme?.brandDeep ?? defaultDarkAgentRailTheme.brandDeep,
|
|
4125
|
+
brandSoft: theme?.brandSoft ?? `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
|
|
4126
|
+
border: theme?.border ?? defaultDarkAgentRailTheme.border,
|
|
4127
|
+
danger: theme?.danger ?? defaultDarkAgentRailTheme.danger,
|
|
4128
|
+
success: theme?.success ?? defaultDarkAgentRailTheme.success,
|
|
4129
|
+
surface: theme?.surface ?? defaultDarkAgentRailTheme.surface,
|
|
4130
|
+
surfaceMuted: theme?.surfaceMuted ?? defaultDarkAgentRailTheme.surfaceMuted,
|
|
4131
|
+
text: theme?.text ?? defaultDarkAgentRailTheme.text,
|
|
4132
|
+
textMuted: theme?.textMuted ?? defaultDarkAgentRailTheme.textMuted,
|
|
4133
|
+
textSubtle: theme?.textSubtle ?? defaultDarkAgentRailTheme.textSubtle,
|
|
2314
4134
|
visitorBubble: theme?.visitorBubble ?? theme?.brand ?? defaultDarkAgentRailTheme.visitorBubble
|
|
2315
4135
|
} : brandedTheme;
|
|
2316
4136
|
const railStyle = {
|
|
@@ -2333,8 +4153,15 @@ function AgentRail({
|
|
|
2333
4153
|
"--as-font-display": resolvedTheme.fontDisplay,
|
|
2334
4154
|
colorScheme: resolvedColorScheme
|
|
2335
4155
|
};
|
|
2336
|
-
const
|
|
4156
|
+
const pendingInputRequests = (state.pendingInputs ?? []).filter(
|
|
4157
|
+
(request) => shouldRenderVisitorInputCard(request) && (!state.pendingOffer || request.kind === "tool-approval")
|
|
4158
|
+
);
|
|
4159
|
+
const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming" || state.phase === "waiting-input" && pendingInputRequests.length > 0;
|
|
4160
|
+
const semanticSurfaceDisabled = isBusy && state.phase !== "waiting-input";
|
|
2337
4161
|
const showActivity = state.toolSteps.length > 0;
|
|
4162
|
+
const visitorToolResults = (state.toolResults ?? []).filter(
|
|
4163
|
+
isRenderableVisitorToolResult
|
|
4164
|
+
);
|
|
2338
4165
|
const hasVisitorMessages2 = state.messages.some(
|
|
2339
4166
|
(message) => message.role === "visitor"
|
|
2340
4167
|
);
|
|
@@ -2344,22 +4171,44 @@ function AgentRail({
|
|
|
2344
4171
|
);
|
|
2345
4172
|
const visibleMessages = hasVisitorMessages2 ? state.messages : [];
|
|
2346
4173
|
const lastMessage = visibleMessages.at(-1);
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
4174
|
+
let lastAgentIndex = -1;
|
|
4175
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4176
|
+
if (visibleMessages[index]?.role === "agent") {
|
|
4177
|
+
lastAgentIndex = index;
|
|
4178
|
+
break;
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
let lastVisitorIndex = -1;
|
|
4182
|
+
for (let index = visibleMessages.length - 1; index >= 0; index -= 1) {
|
|
4183
|
+
if (visibleMessages[index]?.role === "visitor") {
|
|
4184
|
+
lastVisitorIndex = index;
|
|
4185
|
+
break;
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
const lastIsAgent = lastMessage?.role === "agent";
|
|
4189
|
+
const streamingMessage = state.phase === "streaming" && state.streamingText && !lastIsAgent ? {
|
|
2350
4190
|
createdAt: 0,
|
|
2351
4191
|
id: "streaming-response",
|
|
2352
4192
|
role: "agent",
|
|
2353
4193
|
streaming: true,
|
|
2354
4194
|
text: state.streamingText
|
|
2355
|
-
} : state.pendingOffer ? {
|
|
4195
|
+
} : state.pendingOffer && !lastIsAgent ? {
|
|
2356
4196
|
createdAt: 0,
|
|
2357
4197
|
id: "pending-booking",
|
|
2358
4198
|
role: "agent",
|
|
2359
4199
|
streaming: false,
|
|
2360
4200
|
text: "Pick a date and time that works for you."
|
|
2361
4201
|
} : null;
|
|
2362
|
-
|
|
4202
|
+
const bookingReadyText = state.streamingText || (lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "");
|
|
4203
|
+
const waitingForBooking = !state.pendingOffer && looksLikeBookingReady(bookingReadyText) && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
|
|
4204
|
+
const lastAgentText = lastIsAgent && lastMessage?.role === "agent" ? lastMessage.text : "";
|
|
4205
|
+
const composerForm = resolveComposerForm({
|
|
4206
|
+
agentText: lastAgentText,
|
|
4207
|
+
cards: extractToolCards(lastAgentText),
|
|
4208
|
+
hasBookingOffer: Boolean(state.pendingOffer) || waitingForBooking,
|
|
4209
|
+
enabled: lastIsAgent && !isBusy
|
|
4210
|
+
});
|
|
4211
|
+
useEffect3(() => {
|
|
2363
4212
|
const node = transcriptRef.current;
|
|
2364
4213
|
if (!node) return;
|
|
2365
4214
|
node.scrollTop = node.scrollHeight;
|
|
@@ -2370,11 +4219,13 @@ function AgentRail({
|
|
|
2370
4219
|
state.followUps,
|
|
2371
4220
|
state.journey
|
|
2372
4221
|
]);
|
|
2373
|
-
return /* @__PURE__ */
|
|
4222
|
+
return /* @__PURE__ */ jsxs12(
|
|
2374
4223
|
"aside",
|
|
2375
4224
|
{
|
|
2376
|
-
className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4225
|
+
className: `agent-rail not-typeset${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
4226
|
+
"data-not-typeset": "",
|
|
2377
4227
|
"data-color-scheme": resolvedColorScheme,
|
|
4228
|
+
spellCheck: false,
|
|
2378
4229
|
style: railStyle,
|
|
2379
4230
|
"aria-label": "Agent conversation",
|
|
2380
4231
|
"aria-modal": mobileFullscreen || expanded ? true : void 0,
|
|
@@ -2382,28 +4233,28 @@ function AgentRail({
|
|
|
2382
4233
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
2383
4234
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
2384
4235
|
children: [
|
|
2385
|
-
/* @__PURE__ */
|
|
2386
|
-
onCollapse ? /* @__PURE__ */
|
|
4236
|
+
/* @__PURE__ */ jsx13("header", { className: "agent-rail__header", children: /* @__PURE__ */ jsxs12("div", { className: "agent-rail__brand-row", children: [
|
|
4237
|
+
onCollapse ? /* @__PURE__ */ jsx13(
|
|
2387
4238
|
"button",
|
|
2388
4239
|
{
|
|
2389
4240
|
type: "button",
|
|
2390
4241
|
className: "agent-rail__collapse",
|
|
2391
4242
|
"aria-label": "Collapse assist",
|
|
2392
4243
|
onClick: onCollapse,
|
|
2393
|
-
children: /* @__PURE__ */
|
|
4244
|
+
children: /* @__PURE__ */ jsx13(MinimizeIcon, {})
|
|
2394
4245
|
}
|
|
2395
|
-
) : onClose ? /* @__PURE__ */
|
|
4246
|
+
) : onClose ? /* @__PURE__ */ jsx13(
|
|
2396
4247
|
"button",
|
|
2397
4248
|
{
|
|
2398
4249
|
type: "button",
|
|
2399
4250
|
className: "agent-rail__close",
|
|
2400
4251
|
"aria-label": "Close agent",
|
|
2401
4252
|
onClick: onClose,
|
|
2402
|
-
children: /* @__PURE__ */
|
|
4253
|
+
children: /* @__PURE__ */ jsx13(CloseIcon, {})
|
|
2403
4254
|
}
|
|
2404
|
-
) : /* @__PURE__ */
|
|
2405
|
-
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */
|
|
2406
|
-
showBrandLogo ? /* @__PURE__ */
|
|
4255
|
+
) : /* @__PURE__ */ jsx13("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
|
|
4256
|
+
resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ jsxs12("span", { className: "agent-rail__identity", children: [
|
|
4257
|
+
showBrandLogo ? /* @__PURE__ */ jsx13("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(
|
|
2407
4258
|
"img",
|
|
2408
4259
|
{
|
|
2409
4260
|
className: "agent-rail__brand-logo",
|
|
@@ -2414,10 +4265,10 @@ function AgentRail({
|
|
|
2414
4265
|
}
|
|
2415
4266
|
}
|
|
2416
4267
|
) }) : null,
|
|
2417
|
-
resolvedBrandLabel ? /* @__PURE__ */
|
|
4268
|
+
resolvedBrandLabel ? /* @__PURE__ */ jsx13("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
|
|
2418
4269
|
] }) : null,
|
|
2419
|
-
/* @__PURE__ */
|
|
2420
|
-
onReset ? /* @__PURE__ */
|
|
4270
|
+
/* @__PURE__ */ jsxs12("span", { className: "agent-rail__actions", children: [
|
|
4271
|
+
onReset ? /* @__PURE__ */ jsx13(
|
|
2421
4272
|
"button",
|
|
2422
4273
|
{
|
|
2423
4274
|
type: "button",
|
|
@@ -2425,24 +4276,24 @@ function AgentRail({
|
|
|
2425
4276
|
"aria-label": "Start a new conversation",
|
|
2426
4277
|
disabled: !hasVisitorMessages2,
|
|
2427
4278
|
onClick: onReset,
|
|
2428
|
-
children: /* @__PURE__ */
|
|
4279
|
+
children: /* @__PURE__ */ jsx13(NewChatIcon, {})
|
|
2429
4280
|
}
|
|
2430
4281
|
) : null,
|
|
2431
|
-
onExpandToggle ? /* @__PURE__ */
|
|
4282
|
+
onExpandToggle ? /* @__PURE__ */ jsx13(
|
|
2432
4283
|
"button",
|
|
2433
4284
|
{
|
|
2434
4285
|
type: "button",
|
|
2435
4286
|
className: "agent-rail__expand",
|
|
2436
4287
|
"aria-label": expanded ? "Exit full screen" : "Open full screen",
|
|
2437
4288
|
onClick: onExpandToggle,
|
|
2438
|
-
children: expanded ? /* @__PURE__ */
|
|
4289
|
+
children: expanded ? /* @__PURE__ */ jsx13(RestoreIcon, {}) : /* @__PURE__ */ jsx13(ExpandIcon, {})
|
|
2439
4290
|
}
|
|
2440
4291
|
) : null
|
|
2441
4292
|
] })
|
|
2442
4293
|
] }) }),
|
|
2443
|
-
/* @__PURE__ */
|
|
2444
|
-
!hasVisitorMessages2 ? /* @__PURE__ */
|
|
2445
|
-
greeting?.role === "agent" ? /* @__PURE__ */
|
|
4294
|
+
/* @__PURE__ */ jsx13("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ jsxs12("div", { className: "agent-rail__thread", children: [
|
|
4295
|
+
!hasVisitorMessages2 ? /* @__PURE__ */ jsxs12("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
4296
|
+
greeting?.role === "agent" ? /* @__PURE__ */ jsx13(
|
|
2446
4297
|
MessageBubble,
|
|
2447
4298
|
{
|
|
2448
4299
|
message: greeting,
|
|
@@ -2450,7 +4301,7 @@ function AgentRail({
|
|
|
2450
4301
|
onBook
|
|
2451
4302
|
}
|
|
2452
4303
|
) : null,
|
|
2453
|
-
showIdleFollowUps ? /* @__PURE__ */
|
|
4304
|
+
showIdleFollowUps ? /* @__PURE__ */ jsx13("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ jsx13(
|
|
2454
4305
|
FollowUpChips,
|
|
2455
4306
|
{
|
|
2456
4307
|
suggestions: state.followUps,
|
|
@@ -2458,64 +4309,94 @@ function AgentRail({
|
|
|
2458
4309
|
label: "Start here",
|
|
2459
4310
|
onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
|
|
2460
4311
|
}
|
|
2461
|
-
) }) : null
|
|
4312
|
+
) }) : null,
|
|
4313
|
+
showActivity ? /* @__PURE__ */ jsx13(
|
|
4314
|
+
AgentActivityBubble,
|
|
4315
|
+
{
|
|
4316
|
+
brandLabel: resolvedBrandLabel,
|
|
4317
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4318
|
+
failed: state.phase === "error",
|
|
4319
|
+
steps: state.toolSteps
|
|
4320
|
+
}
|
|
4321
|
+
) : null,
|
|
4322
|
+
visitorToolResults.map((result) => /* @__PURE__ */ jsx13(VisitorToolResultView, { result }, result.id)),
|
|
4323
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ jsx13(
|
|
4324
|
+
HumanInputCard,
|
|
4325
|
+
{
|
|
4326
|
+
request,
|
|
4327
|
+
onRespond: onInputResponse
|
|
4328
|
+
},
|
|
4329
|
+
request.requestId
|
|
4330
|
+
))
|
|
2462
4331
|
] }) : null,
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
4332
|
+
visibleMessages.map((message, index) => /* @__PURE__ */ jsxs12("div", { className: "agent-rail__turn-block", children: [
|
|
4333
|
+
/* @__PURE__ */ jsx13(
|
|
4334
|
+
MessageBubble,
|
|
4335
|
+
{
|
|
4336
|
+
message,
|
|
4337
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4338
|
+
offer: index === lastAgentIndex ? state.pendingOffer : void 0,
|
|
4339
|
+
onBook
|
|
4340
|
+
}
|
|
4341
|
+
),
|
|
4342
|
+
index === lastVisitorIndex ? /* @__PURE__ */ jsxs12(Fragment, { children: [
|
|
4343
|
+
showActivity ? /* @__PURE__ */ jsx13(
|
|
4344
|
+
AgentActivityBubble,
|
|
4345
|
+
{
|
|
4346
|
+
brandLabel: resolvedBrandLabel,
|
|
4347
|
+
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4348
|
+
failed: state.phase === "error",
|
|
4349
|
+
steps: state.toolSteps
|
|
4350
|
+
}
|
|
4351
|
+
) : null,
|
|
4352
|
+
visitorToolResults.map((result) => /* @__PURE__ */ jsx13(VisitorToolResultView, { result }, result.id)),
|
|
4353
|
+
pendingInputRequests.map((request) => /* @__PURE__ */ jsx13(
|
|
4354
|
+
HumanInputCard,
|
|
4355
|
+
{
|
|
4356
|
+
request,
|
|
4357
|
+
onRespond: onInputResponse
|
|
4358
|
+
},
|
|
4359
|
+
request.requestId
|
|
4360
|
+
))
|
|
4361
|
+
] }) : null
|
|
4362
|
+
] }, message.id)),
|
|
4363
|
+
streamingMessage ? /* @__PURE__ */ jsx13(
|
|
2482
4364
|
MessageBubble,
|
|
2483
4365
|
{
|
|
2484
|
-
message:
|
|
4366
|
+
message: streamingMessage,
|
|
2485
4367
|
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
4368
|
+
offer: state.pendingOffer,
|
|
2486
4369
|
onBook
|
|
2487
4370
|
}
|
|
2488
4371
|
) : null,
|
|
2489
|
-
|
|
2490
|
-
|
|
4372
|
+
waitingForBooking ? /* @__PURE__ */ jsx13(
|
|
4373
|
+
BookingCard,
|
|
2491
4374
|
{
|
|
2492
|
-
|
|
2493
|
-
brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
|
|
2494
|
-
offer: state.pendingOffer,
|
|
2495
|
-
onBook
|
|
4375
|
+
offer: { type: "booking_offer", eventTypes: [], slots: [] }
|
|
2496
4376
|
}
|
|
2497
4377
|
) : null,
|
|
2498
|
-
state.error ? /* @__PURE__ */
|
|
2499
|
-
/* @__PURE__ */
|
|
2500
|
-
/* @__PURE__ */
|
|
2501
|
-
/* @__PURE__ */
|
|
4378
|
+
state.error ? /* @__PURE__ */ jsxs12("section", { className: "agent-rail__error", role: "alert", children: [
|
|
4379
|
+
/* @__PURE__ */ jsxs12("div", { children: [
|
|
4380
|
+
/* @__PURE__ */ jsx13("strong", { children: "Something went wrong" }),
|
|
4381
|
+
/* @__PURE__ */ jsx13("p", { children: state.error })
|
|
2502
4382
|
] }),
|
|
2503
|
-
onRetry ? /* @__PURE__ */
|
|
4383
|
+
onRetry ? /* @__PURE__ */ jsx13("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
2504
4384
|
] }) : null
|
|
2505
4385
|
] }) }),
|
|
2506
|
-
/* @__PURE__ */
|
|
2507
|
-
/* @__PURE__ */
|
|
4386
|
+
/* @__PURE__ */ jsxs12("div", { className: "agent-rail__composer-wrap", children: [
|
|
4387
|
+
/* @__PURE__ */ jsx13(
|
|
2508
4388
|
Composer,
|
|
2509
4389
|
{
|
|
2510
4390
|
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
2511
4391
|
disabled: isBusy,
|
|
4392
|
+
form: composerForm,
|
|
2512
4393
|
placeholder: composerPlaceholder,
|
|
2513
4394
|
onSubmit
|
|
2514
4395
|
}
|
|
2515
4396
|
),
|
|
2516
|
-
/* @__PURE__ */
|
|
2517
|
-
/* @__PURE__ */
|
|
2518
|
-
/* @__PURE__ */
|
|
4397
|
+
/* @__PURE__ */ jsx13("div", { className: "agent-rail__footer", children: /* @__PURE__ */ jsxs12("p", { children: [
|
|
4398
|
+
/* @__PURE__ */ jsx13("span", { children: "AI can make mistakes. Check important info." }),
|
|
4399
|
+
/* @__PURE__ */ jsx13("span", { children: poweredByLabel })
|
|
2519
4400
|
] }) })
|
|
2520
4401
|
] })
|
|
2521
4402
|
]
|
|
@@ -2524,9 +4405,9 @@ function AgentRail({
|
|
|
2524
4405
|
}
|
|
2525
4406
|
|
|
2526
4407
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
2527
|
-
import { Fragment as Fragment2, jsx as
|
|
4408
|
+
import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2528
4409
|
function SparklesIcon() {
|
|
2529
|
-
return /* @__PURE__ */
|
|
4410
|
+
return /* @__PURE__ */ jsxs13(
|
|
2530
4411
|
"svg",
|
|
2531
4412
|
{
|
|
2532
4413
|
className: "assist-edge-tab__sparkles",
|
|
@@ -2534,21 +4415,21 @@ function SparklesIcon() {
|
|
|
2534
4415
|
fill: "none",
|
|
2535
4416
|
"aria-hidden": "true",
|
|
2536
4417
|
children: [
|
|
2537
|
-
/* @__PURE__ */
|
|
4418
|
+
/* @__PURE__ */ jsx14(
|
|
2538
4419
|
"path",
|
|
2539
4420
|
{
|
|
2540
4421
|
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",
|
|
2541
4422
|
fill: "currentColor"
|
|
2542
4423
|
}
|
|
2543
4424
|
),
|
|
2544
|
-
/* @__PURE__ */
|
|
4425
|
+
/* @__PURE__ */ jsx14(
|
|
2545
4426
|
"path",
|
|
2546
4427
|
{
|
|
2547
4428
|
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",
|
|
2548
4429
|
fill: "currentColor"
|
|
2549
4430
|
}
|
|
2550
4431
|
),
|
|
2551
|
-
/* @__PURE__ */
|
|
4432
|
+
/* @__PURE__ */ jsx14(
|
|
2552
4433
|
"path",
|
|
2553
4434
|
{
|
|
2554
4435
|
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",
|
|
@@ -2562,7 +4443,7 @@ function SparklesIcon() {
|
|
|
2562
4443
|
function TabMarkIcon({ customIconUrl }) {
|
|
2563
4444
|
const url = customIconUrl?.trim();
|
|
2564
4445
|
if (url) {
|
|
2565
|
-
return /* @__PURE__ */
|
|
4446
|
+
return /* @__PURE__ */ jsx14(
|
|
2566
4447
|
"img",
|
|
2567
4448
|
{
|
|
2568
4449
|
alt: "",
|
|
@@ -2572,10 +4453,10 @@ function TabMarkIcon({ customIconUrl }) {
|
|
|
2572
4453
|
}
|
|
2573
4454
|
);
|
|
2574
4455
|
}
|
|
2575
|
-
return /* @__PURE__ */
|
|
4456
|
+
return /* @__PURE__ */ jsx14(SparklesIcon, {});
|
|
2576
4457
|
}
|
|
2577
4458
|
function ChevronLeftIcon() {
|
|
2578
|
-
return /* @__PURE__ */
|
|
4459
|
+
return /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx14(
|
|
2579
4460
|
"path",
|
|
2580
4461
|
{
|
|
2581
4462
|
d: "M10 4L6 8l4 4",
|
|
@@ -2587,7 +4468,7 @@ function ChevronLeftIcon() {
|
|
|
2587
4468
|
) });
|
|
2588
4469
|
}
|
|
2589
4470
|
function ChevronDownIcon() {
|
|
2590
|
-
return /* @__PURE__ */
|
|
4471
|
+
return /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx14(
|
|
2591
4472
|
"path",
|
|
2592
4473
|
{
|
|
2593
4474
|
d: "M4 6l4 4 4-4",
|
|
@@ -2599,7 +4480,7 @@ function ChevronDownIcon() {
|
|
|
2599
4480
|
) });
|
|
2600
4481
|
}
|
|
2601
4482
|
function DragDots() {
|
|
2602
|
-
return /* @__PURE__ */
|
|
4483
|
+
return /* @__PURE__ */ jsx14("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ jsx14("i", {}, index)) });
|
|
2603
4484
|
}
|
|
2604
4485
|
var VARIANT_COPY = {
|
|
2605
4486
|
outline: { label: "Ask anything", aria: "Ask anything" },
|
|
@@ -2628,6 +4509,7 @@ function AssistEdgeTab({
|
|
|
2628
4509
|
const resolvedColorScheme = useAgentColorScheme(colorScheme);
|
|
2629
4510
|
const copy = VARIANT_COPY[variant];
|
|
2630
4511
|
const visibleLabel = label?.trim() || copy.label;
|
|
4512
|
+
const alignment = along < 50 ? "start" : along > 50 ? "end" : "center";
|
|
2631
4513
|
const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
|
|
2632
4514
|
const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
|
|
2633
4515
|
const resolvedBorderColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.border : borderColor;
|
|
@@ -2644,11 +4526,11 @@ function AssistEdgeTab({
|
|
|
2644
4526
|
...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
|
|
2645
4527
|
colorScheme: resolvedColorScheme
|
|
2646
4528
|
};
|
|
2647
|
-
return /* @__PURE__ */
|
|
4529
|
+
return /* @__PURE__ */ jsxs13(
|
|
2648
4530
|
"button",
|
|
2649
4531
|
{
|
|
2650
4532
|
type: "button",
|
|
2651
|
-
className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
|
|
4533
|
+
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" : ""}`,
|
|
2652
4534
|
"data-color-scheme": resolvedColorScheme,
|
|
2653
4535
|
style,
|
|
2654
4536
|
"aria-label": `Open ${visibleLabel}`,
|
|
@@ -2656,15 +4538,15 @@ function AssistEdgeTab({
|
|
|
2656
4538
|
tabIndex: visible ? 0 : -1,
|
|
2657
4539
|
onClick: onOpen,
|
|
2658
4540
|
children: [
|
|
2659
|
-
mobile ? /* @__PURE__ */
|
|
2660
|
-
/* @__PURE__ */
|
|
4541
|
+
mobile ? /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
4542
|
+
/* @__PURE__ */ jsxs13(
|
|
2661
4543
|
"span",
|
|
2662
4544
|
{
|
|
2663
4545
|
className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
|
|
2664
4546
|
"aria-hidden": "true",
|
|
2665
4547
|
children: [
|
|
2666
|
-
/* @__PURE__ */
|
|
2667
|
-
showLogo ? /* @__PURE__ */
|
|
4548
|
+
/* @__PURE__ */ jsx14(TabMarkIcon, { customIconUrl }),
|
|
4549
|
+
showLogo ? /* @__PURE__ */ jsx14(
|
|
2668
4550
|
"img",
|
|
2669
4551
|
{
|
|
2670
4552
|
className: "assist-edge-tab__logo",
|
|
@@ -2678,11 +4560,11 @@ function AssistEdgeTab({
|
|
|
2678
4560
|
]
|
|
2679
4561
|
}
|
|
2680
4562
|
),
|
|
2681
|
-
/* @__PURE__ */
|
|
2682
|
-
] }) : variant === "outline" ? /* @__PURE__ */
|
|
2683
|
-
/* @__PURE__ */
|
|
2684
|
-
/* @__PURE__ */
|
|
2685
|
-
showLogo ? /* @__PURE__ */
|
|
4563
|
+
/* @__PURE__ */ jsx14("span", { className: "assist-edge-tab__label", children: visibleLabel })
|
|
4564
|
+
] }) : variant === "outline" ? /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
4565
|
+
/* @__PURE__ */ jsxs13("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4566
|
+
/* @__PURE__ */ jsx14(TabMarkIcon, { customIconUrl }),
|
|
4567
|
+
showLogo ? /* @__PURE__ */ jsx14(
|
|
2686
4568
|
"img",
|
|
2687
4569
|
{
|
|
2688
4570
|
className: "assist-edge-tab__logo",
|
|
@@ -2694,18 +4576,18 @@ function AssistEdgeTab({
|
|
|
2694
4576
|
}
|
|
2695
4577
|
) : null
|
|
2696
4578
|
] }),
|
|
2697
|
-
/* @__PURE__ */
|
|
2698
|
-
/* @__PURE__ */
|
|
4579
|
+
/* @__PURE__ */ jsx14("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4580
|
+
/* @__PURE__ */ jsx14(ChevronDownIcon, {})
|
|
2699
4581
|
] }) : null,
|
|
2700
|
-
variant === "ask" ? /* @__PURE__ */
|
|
2701
|
-
/* @__PURE__ */
|
|
2702
|
-
/* @__PURE__ */
|
|
2703
|
-
/* @__PURE__ */
|
|
4582
|
+
variant === "ask" ? /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
4583
|
+
/* @__PURE__ */ jsx14(ChevronLeftIcon, {}),
|
|
4584
|
+
/* @__PURE__ */ jsx14("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4585
|
+
/* @__PURE__ */ jsx14(DragDots, {})
|
|
2704
4586
|
] }) : null,
|
|
2705
|
-
variant === "fill" ? /* @__PURE__ */
|
|
2706
|
-
/* @__PURE__ */
|
|
2707
|
-
/* @__PURE__ */
|
|
2708
|
-
showLogo ? /* @__PURE__ */
|
|
4587
|
+
variant === "fill" ? /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
4588
|
+
/* @__PURE__ */ jsxs13("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
4589
|
+
/* @__PURE__ */ jsx14(TabMarkIcon, { customIconUrl }),
|
|
4590
|
+
showLogo ? /* @__PURE__ */ jsx14(
|
|
2709
4591
|
"img",
|
|
2710
4592
|
{
|
|
2711
4593
|
className: "assist-edge-tab__logo",
|
|
@@ -2717,8 +4599,8 @@ function AssistEdgeTab({
|
|
|
2717
4599
|
}
|
|
2718
4600
|
) : null
|
|
2719
4601
|
] }),
|
|
2720
|
-
/* @__PURE__ */
|
|
2721
|
-
/* @__PURE__ */
|
|
4602
|
+
/* @__PURE__ */ jsx14("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
4603
|
+
/* @__PURE__ */ jsx14(ChevronLeftIcon, {})
|
|
2722
4604
|
] }) : null
|
|
2723
4605
|
]
|
|
2724
4606
|
}
|
|
@@ -2726,10 +4608,10 @@ function AssistEdgeTab({
|
|
|
2726
4608
|
}
|
|
2727
4609
|
|
|
2728
4610
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
2729
|
-
import { useEffect as
|
|
4611
|
+
import { useEffect as useEffect6, useRef as useRef4, useState as useState9 } from "react";
|
|
2730
4612
|
|
|
2731
4613
|
// src/react/page-shift.ts
|
|
2732
|
-
import { useEffect as
|
|
4614
|
+
import { useEffect as useEffect4 } from "react";
|
|
2733
4615
|
var PAGE_SHIFT_CLASS = "webless-agent-page-shift";
|
|
2734
4616
|
var DEFAULT_RAIL_WIDTH_PX = 450;
|
|
2735
4617
|
function shouldApplyPageShift(input) {
|
|
@@ -2781,7 +4663,7 @@ function clearPageMargin() {
|
|
|
2781
4663
|
}
|
|
2782
4664
|
function usePageShift(input) {
|
|
2783
4665
|
const { active, railSlotRef } = input;
|
|
2784
|
-
|
|
4666
|
+
useEffect4(() => {
|
|
2785
4667
|
if (typeof document === "undefined") {
|
|
2786
4668
|
return;
|
|
2787
4669
|
}
|
|
@@ -2809,12 +4691,12 @@ function usePageShift(input) {
|
|
|
2809
4691
|
}
|
|
2810
4692
|
|
|
2811
4693
|
// src/react/hooks/useIsMobile.ts
|
|
2812
|
-
import { useEffect as
|
|
4694
|
+
import { useEffect as useEffect5, useState as useState8 } from "react";
|
|
2813
4695
|
function useIsMobile(breakpoint = 767) {
|
|
2814
|
-
const [isMobile, setIsMobile] =
|
|
4696
|
+
const [isMobile, setIsMobile] = useState8(
|
|
2815
4697
|
() => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
|
|
2816
4698
|
);
|
|
2817
|
-
|
|
4699
|
+
useEffect5(() => {
|
|
2818
4700
|
const media = window.matchMedia(`(max-width: ${breakpoint}px)`);
|
|
2819
4701
|
const onChange = () => setIsMobile(media.matches);
|
|
2820
4702
|
onChange();
|
|
@@ -2824,23 +4706,8 @@ function useIsMobile(breakpoint = 767) {
|
|
|
2824
4706
|
return isMobile;
|
|
2825
4707
|
}
|
|
2826
4708
|
|
|
2827
|
-
// src/react/panel-controller.ts
|
|
2828
|
-
var controllers = /* @__PURE__ */ new Map();
|
|
2829
|
-
function registerAgentPanelController(customerId, controller) {
|
|
2830
|
-
controllers.set(customerId, controller);
|
|
2831
|
-
}
|
|
2832
|
-
function unregisterAgentPanelController(customerId) {
|
|
2833
|
-
controllers.delete(customerId);
|
|
2834
|
-
}
|
|
2835
|
-
function openAgentPanel(customerId) {
|
|
2836
|
-
controllers.get(customerId)?.open();
|
|
2837
|
-
}
|
|
2838
|
-
function closeAgentPanel(customerId) {
|
|
2839
|
-
controllers.get(customerId)?.close();
|
|
2840
|
-
}
|
|
2841
|
-
|
|
2842
4709
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
2843
|
-
import { jsx as
|
|
4710
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2844
4711
|
function AgentWidget({
|
|
2845
4712
|
indexId,
|
|
2846
4713
|
customerId,
|
|
@@ -2853,13 +4720,14 @@ function AgentWidget({
|
|
|
2853
4720
|
pageShift = true,
|
|
2854
4721
|
registerPanelController = false,
|
|
2855
4722
|
colorScheme = "auto",
|
|
2856
|
-
branding
|
|
4723
|
+
branding,
|
|
4724
|
+
toolResultRegistry
|
|
2857
4725
|
}) {
|
|
2858
4726
|
const isMobile = useIsMobile();
|
|
2859
4727
|
const placement = normalizeAgentPlacement(placementInput);
|
|
2860
4728
|
const railSlotRef = useRef4(null);
|
|
2861
|
-
const [railCollapsed, setRailCollapsed] =
|
|
2862
|
-
const [railExpanded, setRailExpanded] =
|
|
4729
|
+
const [railCollapsed, setRailCollapsed] = useState9(defaultCollapsed);
|
|
4730
|
+
const [railExpanded, setRailExpanded] = useState9(false);
|
|
2863
4731
|
const pageShiftActive = shouldApplyPageShift({
|
|
2864
4732
|
pageShift,
|
|
2865
4733
|
isMobile,
|
|
@@ -2870,14 +4738,15 @@ function AgentWidget({
|
|
|
2870
4738
|
active: pageShiftActive,
|
|
2871
4739
|
railSlotRef
|
|
2872
4740
|
});
|
|
2873
|
-
const { state, reset, retry, submit } = useAgentChat({
|
|
4741
|
+
const { state, reset, retry, respondToInput, respondToToolInput, submit } = useAgentChat({
|
|
2874
4742
|
customerId,
|
|
2875
4743
|
getUnpublishedPreviewGrant,
|
|
2876
4744
|
indexId,
|
|
2877
4745
|
previewBuildId,
|
|
2878
4746
|
version,
|
|
2879
4747
|
runtimeOrigin,
|
|
2880
|
-
greeting: branding?.greeting
|
|
4748
|
+
greeting: branding?.greeting,
|
|
4749
|
+
toolResultRegistry
|
|
2881
4750
|
});
|
|
2882
4751
|
const agentName = branding?.agentName ?? "";
|
|
2883
4752
|
const tabLabel = branding?.tabLabel ?? agentName;
|
|
@@ -2898,22 +4767,24 @@ function AgentWidget({
|
|
|
2898
4767
|
} : {},
|
|
2899
4768
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
2900
4769
|
};
|
|
2901
|
-
|
|
4770
|
+
useEffect6(() => {
|
|
2902
4771
|
if (!registerPanelController) return;
|
|
2903
4772
|
registerAgentPanelController(customerId, {
|
|
2904
4773
|
open: () => setRailCollapsed(false),
|
|
2905
4774
|
close: () => {
|
|
2906
4775
|
setRailCollapsed(true);
|
|
2907
4776
|
setRailExpanded(false);
|
|
2908
|
-
}
|
|
4777
|
+
},
|
|
4778
|
+
reset,
|
|
4779
|
+
submit
|
|
2909
4780
|
});
|
|
2910
4781
|
return () => unregisterAgentPanelController(customerId);
|
|
2911
|
-
}, [customerId, registerPanelController]);
|
|
4782
|
+
}, [customerId, registerPanelController, reset, submit]);
|
|
2912
4783
|
async function handleSubmit(message) {
|
|
2913
4784
|
if (isMobile) setRailCollapsed(false);
|
|
2914
4785
|
await submit(message);
|
|
2915
4786
|
}
|
|
2916
|
-
|
|
4787
|
+
useEffect6(() => {
|
|
2917
4788
|
if (railCollapsed) return;
|
|
2918
4789
|
const handleKeyDown = (event) => {
|
|
2919
4790
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -2947,19 +4818,19 @@ function AgentWidget({
|
|
|
2947
4818
|
window.addEventListener("keydown", handleKeyDown);
|
|
2948
4819
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2949
4820
|
}, [isMobile, railCollapsed, railExpanded]);
|
|
2950
|
-
return /* @__PURE__ */
|
|
2951
|
-
/* @__PURE__ */
|
|
4821
|
+
return /* @__PURE__ */ jsxs14("div", { className: "webless-agent-root", children: [
|
|
4822
|
+
/* @__PURE__ */ jsx15(
|
|
2952
4823
|
"div",
|
|
2953
4824
|
{
|
|
2954
4825
|
className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
|
|
2955
|
-
children: /* @__PURE__ */
|
|
4826
|
+
children: /* @__PURE__ */ jsx15(
|
|
2956
4827
|
"div",
|
|
2957
4828
|
{
|
|
2958
4829
|
ref: railSlotRef,
|
|
2959
4830
|
className: "webless-agent-root__rail-slot",
|
|
2960
4831
|
inert: railCollapsed || void 0,
|
|
2961
4832
|
"aria-hidden": railCollapsed,
|
|
2962
|
-
children: /* @__PURE__ */
|
|
4833
|
+
children: /* @__PURE__ */ jsx15(
|
|
2963
4834
|
AgentRail,
|
|
2964
4835
|
{
|
|
2965
4836
|
theme,
|
|
@@ -2977,6 +4848,8 @@ function AgentWidget({
|
|
|
2977
4848
|
onSubmit: handleSubmit,
|
|
2978
4849
|
onReset: reset,
|
|
2979
4850
|
onRetry: () => void retry(),
|
|
4851
|
+
onInputResponse: (response) => void respondToInput(response),
|
|
4852
|
+
onToolInput: (surface, values) => void respondToToolInput(surface, values),
|
|
2980
4853
|
onFollowUpSelect: (label) => void handleSubmit(label),
|
|
2981
4854
|
onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
|
|
2982
4855
|
}
|
|
@@ -2985,7 +4858,7 @@ function AgentWidget({
|
|
|
2985
4858
|
)
|
|
2986
4859
|
}
|
|
2987
4860
|
),
|
|
2988
|
-
railCollapsed ? /* @__PURE__ */
|
|
4861
|
+
railCollapsed ? /* @__PURE__ */ jsx15(
|
|
2989
4862
|
AssistEdgeTab,
|
|
2990
4863
|
{
|
|
2991
4864
|
variant: placement.variant,
|
|
@@ -3013,6 +4886,8 @@ function AgentWidget({
|
|
|
3013
4886
|
export {
|
|
3014
4887
|
DEFAULT_RUNTIME_ORIGIN,
|
|
3015
4888
|
resolveAgentRuntimeConfig,
|
|
4889
|
+
builtInVisitorToolResultRegistry,
|
|
4890
|
+
presentVisitorToolResult,
|
|
3016
4891
|
useAgentChat,
|
|
3017
4892
|
hasVisitorMessages,
|
|
3018
4893
|
createIdleSuggestions,
|
|
@@ -3021,10 +4896,12 @@ export {
|
|
|
3021
4896
|
normalizeAgentPlacement,
|
|
3022
4897
|
openAgentPanel,
|
|
3023
4898
|
closeAgentPanel,
|
|
4899
|
+
resetAgentPanel,
|
|
4900
|
+
submitAgentPanel,
|
|
3024
4901
|
defaultAgentRailTheme,
|
|
3025
4902
|
defaultDarkAgentRailTheme,
|
|
3026
4903
|
AgentRail,
|
|
3027
4904
|
AssistEdgeTab,
|
|
3028
4905
|
AgentWidget
|
|
3029
4906
|
};
|
|
3030
|
-
//# sourceMappingURL=chunk-
|
|
4907
|
+
//# sourceMappingURL=chunk-Y4DCEJDC.js.map
|