@webless/agent 0.9.2 → 0.10.0

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/react.cjs CHANGED
@@ -1653,6 +1653,24 @@ function formatAgentError(error) {
1653
1653
  }
1654
1654
 
1655
1655
  // src/react/lib/composer-form.ts
1656
+ var COMPOSER_FORM_SKIP_DISMISSAL = "I'd like to keep chatting without sharing the requested details for now. Please don't ask for them again unless I choose to proceed with something that needs them.";
1657
+ function visitorMessageDisplayText(message) {
1658
+ if (message.role !== "visitor") return message.text;
1659
+ const text2 = message.text.trim();
1660
+ if (!text2) return message.text;
1661
+ if (text2.startsWith(COMPOSER_FORM_SKIP_DISMISSAL)) {
1662
+ const afterDismissal = text2.slice(COMPOSER_FORM_SKIP_DISMISSAL.length).trim().replace(/^\n+/, "").trim();
1663
+ if (afterDismissal) return afterDismissal;
1664
+ }
1665
+ const runtime = message.runtimeText?.trim();
1666
+ if (runtime && text2 === runtime && runtime.includes(COMPOSER_FORM_SKIP_DISMISSAL)) {
1667
+ const afterDismissal = runtime.slice(
1668
+ runtime.indexOf(COMPOSER_FORM_SKIP_DISMISSAL) + COMPOSER_FORM_SKIP_DISMISSAL.length
1669
+ ).trim().replace(/^\n+/, "").trim();
1670
+ if (afterDismissal) return afterDismissal;
1671
+ }
1672
+ return message.text;
1673
+ }
1656
1674
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1657
1675
  var MIN_FORM_FIELDS = 2;
1658
1676
  var MAX_FORM_FIELDS = 5;
@@ -2906,7 +2924,8 @@ function createInitialState(greeting = DEFAULT_GREETING) {
2906
2924
  pendingOffer: null,
2907
2925
  pendingInputs: [],
2908
2926
  toolResults: [],
2909
- error: null
2927
+ error: null,
2928
+ declinedComposerFormIds: []
2910
2929
  };
2911
2930
  }
2912
2931
  function stateFromConversation(conversation, initialState) {
@@ -3326,6 +3345,7 @@ function useAgentChat({
3326
3345
  const trimmed = visitorText.trim();
3327
3346
  const outgoing = options?.runtimeText ?? visitorText;
3328
3347
  if (!outgoing.trim()) return null;
3348
+ const declinedComposerFormId = options?.declinedComposerFormId?.trim();
3329
3349
  const chatResponse = chatInputResponseForText(
3330
3350
  state.pendingInputs ?? [],
3331
3351
  outgoing.trim()
@@ -3359,7 +3379,15 @@ function useAgentChat({
3359
3379
  state: "active"
3360
3380
  }
3361
3381
  ],
3362
- error: null
3382
+ error: null,
3383
+ ...declinedComposerFormId ? {
3384
+ declinedComposerFormIds: [
3385
+ .../* @__PURE__ */ new Set([
3386
+ ...prev.declinedComposerFormIds ?? [],
3387
+ declinedComposerFormId
3388
+ ])
3389
+ ]
3390
+ } : {}
3363
3391
  }));
3364
3392
  return await runTurn({
3365
3393
  controller: controller2,
@@ -3381,9 +3409,9 @@ ${outgoing}` : outgoing;
3381
3409
  const visitorMessage = {
3382
3410
  id: `visitor-${Date.now()}`,
3383
3411
  role: "visitor",
3384
- text: visitorText,
3412
+ text: trimmed,
3385
3413
  createdAt: Date.now(),
3386
- ...runtimeText !== visitorText ? { runtimeText } : {}
3414
+ ...runtimeText.trim() !== trimmed ? { runtimeText } : {}
3387
3415
  };
3388
3416
  setState((prev) => ({
3389
3417
  ...prev,
@@ -3403,7 +3431,15 @@ ${outgoing}` : outgoing;
3403
3431
  pendingOffer: options?.preservePendingOffer ? prev.pendingOffer : null,
3404
3432
  pendingInputs: [],
3405
3433
  toolResults: [],
3406
- error: null
3434
+ error: null,
3435
+ ...declinedComposerFormId ? {
3436
+ declinedComposerFormIds: [
3437
+ .../* @__PURE__ */ new Set([
3438
+ ...prev.declinedComposerFormIds ?? [],
3439
+ declinedComposerFormId
3440
+ ])
3441
+ ]
3442
+ } : {}
3407
3443
  }));
3408
3444
  return await runTurn({
3409
3445
  controller,
@@ -4030,6 +4066,7 @@ function AnswerReceiptDialog({
4030
4066
  // src/react/components/Composer/Composer.tsx
4031
4067
  var import_react8 = require("react");
4032
4068
  var import_jsx_runtime3 = require("react/jsx-runtime");
4069
+ var FORM_SUBMITTED_MESSAGE = "Shared my details";
4033
4070
  function SendIcon() {
4034
4071
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4035
4072
  "path",
@@ -4042,6 +4079,30 @@ function SendIcon() {
4042
4079
  }
4043
4080
  ) });
4044
4081
  }
4082
+ function CheckIcon() {
4083
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4084
+ "path",
4085
+ {
4086
+ d: "M2.5 6.2l2.3 2.3 4.7-5",
4087
+ stroke: "currentColor",
4088
+ strokeWidth: "1.6",
4089
+ strokeLinecap: "round",
4090
+ strokeLinejoin: "round"
4091
+ }
4092
+ ) });
4093
+ }
4094
+ function CollapseIcon() {
4095
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4096
+ "path",
4097
+ {
4098
+ d: "M4 10l4-4 4 4",
4099
+ stroke: "currentColor",
4100
+ strokeWidth: "1.6",
4101
+ strokeLinecap: "round",
4102
+ strokeLinejoin: "round"
4103
+ }
4104
+ ) });
4105
+ }
4045
4106
  function emptyValues(form) {
4046
4107
  const values = {};
4047
4108
  for (const field of form?.fields ?? []) values[field.id] = "";
@@ -4075,26 +4136,31 @@ function Composer({
4075
4136
  const savedForm = allowFormResume && !draft.submitted ? draft.form : null;
4076
4137
  const activeForm = !disabled && draft.expanded ? savedForm : null;
4077
4138
  const hasDraft = Object.values(draft.values).some((value2) => value2.trim());
4139
+ const canSend = !disabled && Boolean(value.trim() || activeForm);
4078
4140
  (0, import_react8.useEffect)(() => {
4079
4141
  if (activeForm) firstFieldRef.current?.focus();
4080
4142
  else if ((savedForm || draft.submitted) && !disabled)
4081
4143
  inputRef.current?.focus();
4082
4144
  }, [activeForm?.id, disabled, savedForm?.id, draft.submitted]);
4145
+ function collapseForm() {
4146
+ setDraft((current) => ({ ...current, expanded: false }));
4147
+ }
4083
4148
  function submitChat() {
4084
4149
  const trimmed = value.trim();
4085
4150
  if (!trimmed || disabled) return;
4086
4151
  const shareDismissal = savedForm && !draft.dismissalSent;
4087
4152
  if (shareDismissal) {
4088
4153
  onSubmit?.(trimmed, {
4089
- runtimeText: `I'd like to keep chatting without sharing the requested details for now. Please don't ask for them again unless I choose to proceed with something that needs them.
4090
-
4091
- ${trimmed}`
4154
+ declinedComposerFormId: savedForm.id
4092
4155
  });
4093
4156
  } else {
4094
4157
  onSubmit?.(trimmed);
4095
4158
  }
4096
- if (shareDismissal)
4097
- setDraft((current) => ({ ...current, dismissalSent: true }));
4159
+ setDraft((current) => ({
4160
+ ...current,
4161
+ expanded: false,
4162
+ dismissalSent: shareDismissal ? true : current.dismissalSent
4163
+ }));
4098
4164
  setValue("");
4099
4165
  inputRef.current?.focus();
4100
4166
  }
@@ -4109,7 +4175,7 @@ ${trimmed}`
4109
4175
  if (control instanceof HTMLElement) control.focus();
4110
4176
  return;
4111
4177
  }
4112
- onSubmit?.("", {
4178
+ onSubmit?.(FORM_SUBMITTED_MESSAGE, {
4113
4179
  runtimeText: formatComposerFormMessage(activeForm, draft.values)
4114
4180
  });
4115
4181
  setDraft((current) => ({
@@ -4120,10 +4186,16 @@ ${trimmed}`
4120
4186
  attempted: false
4121
4187
  }));
4122
4188
  }
4189
+ function handleSend() {
4190
+ if (value.trim()) {
4191
+ submitChat();
4192
+ return;
4193
+ }
4194
+ if (activeForm) submitForm();
4195
+ }
4123
4196
  function handleSubmit(event) {
4124
4197
  event.preventDefault();
4125
- if (activeForm) submitForm();
4126
- else submitChat();
4198
+ handleSend();
4127
4199
  }
4128
4200
  function handleChatKeyDown(event) {
4129
4201
  if (event.key === "Enter" && !event.shiftKey) {
@@ -4139,7 +4211,34 @@ ${trimmed}`
4139
4211
  submitForm();
4140
4212
  }
4141
4213
  }
4142
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4214
+ const chatField = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
4215
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4216
+ "textarea",
4217
+ {
4218
+ ref: inputRef,
4219
+ className: "composer__input",
4220
+ rows: 1,
4221
+ value,
4222
+ placeholder,
4223
+ disabled,
4224
+ spellCheck: false,
4225
+ "aria-label": "Message",
4226
+ onChange: (event) => setValue(event.target.value),
4227
+ onKeyDown: handleChatKeyDown
4228
+ }
4229
+ ),
4230
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4231
+ "button",
4232
+ {
4233
+ type: "submit",
4234
+ className: "composer__send",
4235
+ disabled: !canSend,
4236
+ "aria-label": "Send message",
4237
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
4238
+ }
4239
+ )
4240
+ ] });
4241
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4143
4242
  "form",
4144
4243
  {
4145
4244
  ref: formRef,
@@ -4150,107 +4249,10 @@ ${trimmed}`
4150
4249
  activeForm ? "composer--form" : ""
4151
4250
  ].filter(Boolean).join(" "),
4152
4251
  onSubmit: handleSubmit,
4153
- children: activeForm ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4154
- "div",
4155
- {
4156
- className: "composer__sheet",
4157
- role: "group",
4158
- "aria-label": "Contact details",
4159
- children: [
4160
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__form-heading", children: [
4161
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "Share your details" }),
4162
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Optional" }),
4163
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: "You can also keep chatting." })
4164
- ] }),
4165
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "composer__fields", children: activeForm.fields.map((field, index) => {
4166
- const fieldId = `${formId}-${field.id}`;
4167
- const invalid = draft.attempted && !isValidComposerFieldValue(field, draft.values[field.id] ?? "");
4168
- const controlProps = {
4169
- id: fieldId,
4170
- name: field.id,
4171
- disabled,
4172
- required: field.required,
4173
- autoComplete: field.autocomplete,
4174
- placeholder: field.placeholder,
4175
- spellCheck: false,
4176
- value: draft.values[field.id] ?? "",
4177
- "aria-invalid": invalid || void 0,
4178
- "aria-describedby": invalid ? `${fieldId}-error` : void 0,
4179
- onChange: (event) => {
4180
- const next = readComposerControlValue(event);
4181
- setDraft((current) => ({
4182
- ...current,
4183
- values: { ...current.values, [field.id]: next }
4184
- }));
4185
- },
4186
- onKeyDown: handleFormKeyDown
4187
- };
4188
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4189
- "div",
4190
- {
4191
- className: [
4192
- "composer__row",
4193
- field.kind === "textarea" ? "composer__row--grow" : ""
4194
- ].filter(Boolean).join(" "),
4195
- children: [
4196
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "composer__label", htmlFor: fieldId, children: [
4197
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "composer__field-label", children: field.label }),
4198
- field.kind === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4199
- "textarea",
4200
- {
4201
- ...controlProps,
4202
- ref: index === 0 ? (node) => {
4203
- firstFieldRef.current = node;
4204
- } : void 0,
4205
- className: "composer__control composer__control--area",
4206
- rows: 3
4207
- }
4208
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4209
- "input",
4210
- {
4211
- ...controlProps,
4212
- ref: index === 0 ? (node) => {
4213
- firstFieldRef.current = node;
4214
- } : void 0,
4215
- className: "composer__control",
4216
- type: field.kind,
4217
- inputMode: field.kind === "tel" ? "tel" : void 0
4218
- }
4219
- )
4220
- ] }),
4221
- invalid ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "composer__error", id: `${fieldId}-error`, children: field.kind === "email" ? "Enter a valid email to share your details." : `Add your ${field.label.toLowerCase()} to share your details.` }) : null
4222
- ]
4223
- },
4224
- field.id
4225
- );
4226
- }) }),
4227
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__toolbar", children: [
4228
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4229
- "button",
4230
- {
4231
- type: "submit",
4232
- className: "composer__action composer__action--primary",
4233
- disabled,
4234
- children: "Share details"
4235
- }
4236
- ),
4237
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4238
- "button",
4239
- {
4240
- type: "button",
4241
- className: "composer__action",
4242
- disabled,
4243
- onClick: () => setDraft((current) => ({ ...current, expanded: false })),
4244
- children: "Keep chatting"
4245
- }
4246
- )
4247
- ] })
4248
- ]
4249
- }
4250
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
4252
+ children: [
4251
4253
  savedForm ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__resume", children: [
4252
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: hasDraft ? "Your details aren\u2019t shared yet" : "Contact details are optional" }),
4253
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4254
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: hasDraft ? "Your details aren\u2019t shared yet" : "Share your details so we can follow up" }),
4255
+ !activeForm ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4254
4256
  "button",
4255
4257
  {
4256
4258
  type: "button",
@@ -4258,36 +4260,100 @@ ${trimmed}`
4258
4260
  onClick: () => setDraft((current) => ({ ...current, expanded: true })),
4259
4261
  children: hasDraft ? "Resume details" : "Add details"
4260
4262
  }
4261
- )
4263
+ ) : null
4262
4264
  ] }) : null,
4263
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__field", children: [
4264
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4265
- "textarea",
4266
- {
4267
- ref: inputRef,
4268
- className: "composer__input",
4269
- rows: 1,
4270
- value,
4271
- placeholder,
4272
- disabled,
4273
- spellCheck: false,
4274
- "aria-label": "Message",
4275
- onChange: (event) => setValue(event.target.value),
4276
- onKeyDown: handleChatKeyDown
4277
- }
4278
- ),
4279
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4280
- "button",
4281
- {
4282
- type: "submit",
4283
- className: "composer__send",
4284
- disabled: disabled || !value.trim(),
4285
- "aria-label": "Send message",
4286
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
4287
- }
4288
- )
4289
- ] })
4290
- ] })
4265
+ activeForm ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4266
+ "div",
4267
+ {
4268
+ className: "composer__sheet",
4269
+ role: "group",
4270
+ "aria-label": "Contact details",
4271
+ children: [
4272
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__form-heading", children: [
4273
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "Share your details" }),
4274
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Optional" }),
4275
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4276
+ "button",
4277
+ {
4278
+ type: "button",
4279
+ className: "composer__collapse",
4280
+ "aria-label": "Collapse form",
4281
+ disabled,
4282
+ onClick: collapseForm,
4283
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CollapseIcon, {})
4284
+ }
4285
+ )
4286
+ ] }),
4287
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "composer__fields", children: activeForm.fields.map((field, index) => {
4288
+ const fieldId = `${formId}-${field.id}`;
4289
+ const fieldValue = draft.values[field.id] ?? "";
4290
+ const invalid = draft.attempted && !isValidComposerFieldValue(field, fieldValue);
4291
+ const complete = fieldValue.trim().length > 0 && isValidComposerFieldValue(field, fieldValue);
4292
+ const controlProps = {
4293
+ id: fieldId,
4294
+ name: field.id,
4295
+ disabled,
4296
+ required: field.required,
4297
+ autoComplete: field.autocomplete,
4298
+ placeholder: field.placeholder,
4299
+ spellCheck: false,
4300
+ value: fieldValue,
4301
+ "aria-invalid": invalid || void 0,
4302
+ "aria-describedby": invalid ? `${fieldId}-error` : void 0,
4303
+ onChange: (event) => {
4304
+ const next = readComposerControlValue(event);
4305
+ setDraft((current) => ({
4306
+ ...current,
4307
+ values: { ...current.values, [field.id]: next }
4308
+ }));
4309
+ },
4310
+ onKeyDown: handleFormKeyDown
4311
+ };
4312
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4313
+ "div",
4314
+ {
4315
+ className: [
4316
+ "composer__row",
4317
+ field.kind === "textarea" ? "composer__row--area" : ""
4318
+ ].filter(Boolean).join(" "),
4319
+ children: [
4320
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__line", children: [
4321
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "composer__label", htmlFor: fieldId, children: field.label }),
4322
+ field.kind === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4323
+ "textarea",
4324
+ {
4325
+ ...controlProps,
4326
+ ref: index === 0 ? (node) => {
4327
+ firstFieldRef.current = node;
4328
+ } : void 0,
4329
+ className: "composer__control composer__control--area",
4330
+ rows: 2
4331
+ }
4332
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4333
+ "input",
4334
+ {
4335
+ ...controlProps,
4336
+ ref: index === 0 ? (node) => {
4337
+ firstFieldRef.current = node;
4338
+ } : void 0,
4339
+ className: "composer__control",
4340
+ type: field.kind,
4341
+ inputMode: field.kind === "tel" ? "tel" : void 0
4342
+ }
4343
+ ),
4344
+ complete && field.kind !== "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "composer__check", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckIcon, {}) }) : null
4345
+ ] }),
4346
+ invalid ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "composer__error", id: `${fieldId}-error`, children: field.kind === "email" ? "Enter a valid email to share your details." : `Add your ${field.label.toLowerCase()} to share your details.` }) : null
4347
+ ]
4348
+ },
4349
+ field.id
4350
+ );
4351
+ }) }),
4352
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "composer__field composer__field--inset", children: chatField })
4353
+ ]
4354
+ }
4355
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "composer__field", children: chatField })
4356
+ ]
4291
4357
  }
4292
4358
  );
4293
4359
  }
@@ -4798,6 +4864,20 @@ function InteractiveBookingCard({
4798
4864
 
4799
4865
  // src/react/components/MessageBubble/MessageBubble.tsx
4800
4866
  var import_streamdown = require("streamdown");
4867
+
4868
+ // src/react/lib/resolve-message-url.ts
4869
+ function resolveMessageUrl(safeUrl, baseUrl) {
4870
+ if (!safeUrl || !baseUrl) return safeUrl;
4871
+ try {
4872
+ const base = new URL(baseUrl);
4873
+ if (base.protocol !== "https:" && base.protocol !== "http:") return "";
4874
+ return new URL(safeUrl, base).href;
4875
+ } catch {
4876
+ return "";
4877
+ }
4878
+ }
4879
+
4880
+ // src/react/components/MessageBubble/MessageBubble.tsx
4801
4881
  var import_styles = require("streamdown/styles.css");
4802
4882
  var import_jsx_runtime7 = require("react/jsx-runtime");
4803
4883
  function normalizeDedupeText(text2) {
@@ -4840,6 +4920,7 @@ function MessageBubble({
4840
4920
  brandLogoUrl,
4841
4921
  bookingDisabled = false,
4842
4922
  bookingReadOnly = false,
4923
+ linkBaseUrl,
4843
4924
  offer,
4844
4925
  onBook
4845
4926
  }) {
@@ -4857,8 +4938,9 @@ function MessageBubble({
4857
4938
  offers.length > 0 ? sanitizeBookingOfferCopy(visibleText) : looksLikeBookingAvailabilityDump(visibleText) ? sanitizeBookingOfferCopy(visibleText) : visibleText || (isStreaming ? "" : message.text)
4858
4939
  );
4859
4940
  if (message.role === "visitor") {
4860
- if (!message.text.trim()) return null;
4861
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "message-bubble__text", children: message.text }) });
4941
+ const visitorText = visitorMessageDisplayText(message).trim();
4942
+ if (!visitorText) return null;
4943
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "message-bubble__text", children: visitorText }) });
4862
4944
  }
4863
4945
  const citations = message.citations ?? [];
4864
4946
  const agentText = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "message-bubble__text", children: [
@@ -4872,6 +4954,10 @@ function MessageBubble({
4872
4954
  isAnimating: isStreaming,
4873
4955
  linkSafety: { enabled: false },
4874
4956
  mode: isStreaming ? "streaming" : "static",
4957
+ urlTransform: (url, key, node) => resolveMessageUrl(
4958
+ (0, import_streamdown.defaultUrlTransform)(url, key, node) ?? "",
4959
+ key === "href" ? linkBaseUrl : void 0
4960
+ ),
4875
4961
  skipHtml: true,
4876
4962
  children: displayText
4877
4963
  }
@@ -4889,7 +4975,8 @@ function MessageBubble({
4889
4975
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: citation.label })
4890
4976
  ] }) }, citation.id)) }) : null
4891
4977
  ] });
4892
- if (!displayText && !message.searchResults?.length && offers.length === 0) return null;
4978
+ if (!displayText && !message.searchResults?.length && offers.length === 0)
4979
+ return null;
4893
4980
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
4894
4981
  displayText || message.searchResults?.length ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "message-bubble__agent-row", children: [
4895
4982
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -5091,7 +5178,7 @@ function CopyIcon() {
5091
5178
  )
5092
5179
  ] });
5093
5180
  }
5094
- function CheckIcon() {
5181
+ function CheckIcon2() {
5095
5182
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5096
5183
  "path",
5097
5184
  {
@@ -5466,7 +5553,7 @@ function MessageActions({
5466
5553
  title: copied ? "Copied" : "Copy answer",
5467
5554
  disabled,
5468
5555
  onClick: handleCopy,
5469
- children: copied ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CopyIcon, {})
5556
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckIcon2, {}) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CopyIcon, {})
5470
5557
  }
5471
5558
  ),
5472
5559
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
@@ -6420,13 +6507,17 @@ function AgentRail({
6420
6507
  const hasPendingConfirmation = pendingInputRequests.some(
6421
6508
  (request) => request.kind === "tool-approval" || request.kind === "question" && (request.options?.length ?? 0) > 0
6422
6509
  );
6423
- const composerForm = resolveComposerForm({
6510
+ const inferredComposerForm = resolveComposerForm({
6424
6511
  agentText: lastAgentText,
6425
6512
  cards: extractToolCards(lastAgentText),
6426
6513
  hasBookingOffer: Boolean(state.pendingOffer) || waitingForBooking,
6427
6514
  hasPendingConfirmation,
6428
6515
  enabled: lastIsAgent && !isBusy
6429
6516
  });
6517
+ const declinedComposerFormIds = state.declinedComposerFormIds ?? [];
6518
+ const composerForm = inferredComposerForm && lastMessage?.role === "agent" && declinedComposerFormIds.includes(
6519
+ `${lastMessage.id}:${inferredComposerForm.id}`
6520
+ ) ? null : inferredComposerForm;
6430
6521
  const latestResultId = [
6431
6522
  ...visibleMessages.flatMap(
6432
6523
  (message) => message.role === "agent" ? message.toolResults ?? [] : []
@@ -6797,36 +6888,75 @@ function AgentRail({
6797
6888
 
6798
6889
  // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
6799
6890
  var import_jsx_runtime18 = require("react/jsx-runtime");
6800
- function SparklesIcon() {
6891
+ function ChatSparkIcon() {
6801
6892
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
6802
6893
  "svg",
6803
6894
  {
6804
- className: "assist-edge-tab__sparkles",
6805
- viewBox: "0 0 18 16",
6895
+ className: "assist-edge-tab__chat-spark",
6896
+ viewBox: "0 0 24 24",
6806
6897
  fill: "none",
6807
6898
  "aria-hidden": "true",
6808
6899
  children: [
6809
6900
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6810
6901
  "path",
6811
6902
  {
6812
- 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",
6903
+ className: "assist-edge-tab__spark assist-edge-tab__spark--a",
6904
+ d: "M19.55 1.55l.78 1.82 1.82.78-1.82.78-.78 1.82-.78-1.82-1.82-.78 1.82-.78.78-1.82z",
6813
6905
  fill: "currentColor"
6814
6906
  }
6815
6907
  ),
6816
6908
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6817
6909
  "path",
6818
6910
  {
6819
- 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",
6911
+ className: "assist-edge-tab__spark assist-edge-tab__spark--b",
6912
+ d: "M4.15 17.35l.48 1.12 1.12.48-1.12.48-.48 1.12-.48-1.12-1.12-.48 1.12-.48.48-1.12z",
6820
6913
  fill: "currentColor"
6821
6914
  }
6822
6915
  ),
6823
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6824
- "path",
6825
- {
6826
- 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",
6827
- fill: "currentColor"
6828
- }
6829
- )
6916
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("g", { className: "assist-edge-tab__bot", children: [
6917
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6918
+ "path",
6919
+ {
6920
+ d: "M11.15 5.2V3.05",
6921
+ stroke: "currentColor",
6922
+ strokeWidth: "1.9",
6923
+ strokeLinecap: "round"
6924
+ }
6925
+ ),
6926
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: "11.15", cy: "2.45", r: "0.85", fill: "currentColor" }),
6927
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6928
+ "rect",
6929
+ {
6930
+ x: "3.55",
6931
+ y: "5.35",
6932
+ width: "15.2",
6933
+ height: "11.15",
6934
+ rx: "3.9",
6935
+ stroke: "currentColor",
6936
+ strokeWidth: "1.9"
6937
+ }
6938
+ ),
6939
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6940
+ "circle",
6941
+ {
6942
+ className: "assist-edge-tab__eye",
6943
+ cx: "8.7",
6944
+ cy: "10.85",
6945
+ r: "1.28",
6946
+ fill: "currentColor"
6947
+ }
6948
+ ),
6949
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6950
+ "circle",
6951
+ {
6952
+ className: "assist-edge-tab__eye assist-edge-tab__eye--r",
6953
+ cx: "14.35",
6954
+ cy: "10.85",
6955
+ r: "1.28",
6956
+ fill: "currentColor"
6957
+ }
6958
+ )
6959
+ ] })
6830
6960
  ]
6831
6961
  }
6832
6962
  );
@@ -6844,7 +6974,7 @@ function TabMarkIcon({ customIconUrl }) {
6844
6974
  }
6845
6975
  );
6846
6976
  }
6847
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SparklesIcon, {});
6977
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatSparkIcon, {});
6848
6978
  }
6849
6979
  function ChevronLeftIcon() {
6850
6980
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
@@ -6885,6 +7015,7 @@ function AssistEdgeTab({
6885
7015
  inset,
6886
7016
  visible,
6887
7017
  label,
7018
+ subLabel,
6888
7019
  customIconUrl,
6889
7020
  logoUrl,
6890
7021
  brandColor,
@@ -6900,6 +7031,9 @@ function AssistEdgeTab({
6900
7031
  const resolvedColorScheme = useAgentColorScheme(colorScheme);
6901
7032
  const copy = VARIANT_COPY[variant];
6902
7033
  const visibleLabel = label?.trim() || copy.label;
7034
+ const visibleSubLabel = subLabel?.trim() || "";
7035
+ const pill = mobile || side === "bottom";
7036
+ const ariaLabel = visibleSubLabel ? `Open ${visibleLabel} \u2014 ${visibleSubLabel}` : `Open ${visibleLabel}`;
6903
7037
  const alignment = along < 50 ? "start" : along > 50 ? "end" : "center";
6904
7038
  const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
6905
7039
  const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
@@ -6924,18 +7058,20 @@ function AssistEdgeTab({
6924
7058
  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" : ""}`,
6925
7059
  "data-color-scheme": resolvedColorScheme,
6926
7060
  style,
6927
- "aria-label": `Open ${visibleLabel}`,
7061
+ "aria-label": ariaLabel,
6928
7062
  "aria-hidden": !visible,
6929
7063
  tabIndex: visible ? 0 : -1,
6930
7064
  onClick: onOpen,
6931
7065
  children: [
6932
- mobile ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
7066
+ pill ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
6933
7067
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
6934
7068
  "span",
6935
7069
  {
6936
- className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
7070
+ className: `assist-edge-tab__mark assist-edge-tab__mark--chip${mobile ? " assist-edge-tab__mark--mobile" : ""}`,
6937
7071
  "aria-hidden": "true",
6938
7072
  children: [
7073
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__wash assist-edge-tab__wash--light" }),
7074
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__wash assist-edge-tab__wash--cool" }),
6939
7075
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TabMarkIcon, { customIconUrl }),
6940
7076
  showLogo ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6941
7077
  "img",
@@ -6951,7 +7087,10 @@ function AssistEdgeTab({
6951
7087
  ]
6952
7088
  }
6953
7089
  ),
6954
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel })
7090
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "assist-edge-tab__text", children: [
7091
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
7092
+ visibleSubLabel ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__sublabel", children: visibleSubLabel }) : null
7093
+ ] })
6955
7094
  ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
6956
7095
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
6957
7096
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TabMarkIcon, { customIconUrl }),
@@ -6970,12 +7109,12 @@ function AssistEdgeTab({
6970
7109
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
6971
7110
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronDownIcon2, {})
6972
7111
  ] }) : null,
6973
- variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
7112
+ !pill && variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
6974
7113
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronLeftIcon, {}),
6975
7114
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
6976
7115
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DragDots, {})
6977
7116
  ] }) : null,
6978
- variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
7117
+ !pill && variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
6979
7118
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
6980
7119
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TabMarkIcon, { customIconUrl }),
6981
7120
  showLogo ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
@@ -7256,6 +7395,7 @@ function AgentWidget({
7256
7395
  inset: placement.inset,
7257
7396
  visible: true,
7258
7397
  label: tabLabel,
7398
+ subLabel: branding?.tabSubLabel,
7259
7399
  customIconUrl: branding?.tabIconUrl,
7260
7400
  logoUrl: branding?.logoUrl,
7261
7401
  brandColor: branding?.colors?.primary,
@@ -7366,6 +7506,7 @@ function AgentTranscript({
7366
7506
  activities = [],
7367
7507
  theme,
7368
7508
  colorScheme,
7509
+ linkBaseUrl,
7369
7510
  agentLabel = "Agent",
7370
7511
  visitorLabel = "Visitor",
7371
7512
  label = "Conversation transcript",
@@ -7405,6 +7546,7 @@ function AgentTranscript({
7405
7546
  MessageBubble,
7406
7547
  {
7407
7548
  message: entry.role === "agent" ? { ...entry, streaming: false } : entry,
7549
+ linkBaseUrl,
7408
7550
  bookingReadOnly: true,
7409
7551
  bookingDisabled: true
7410
7552
  }