@helpai/elements 0.13.1 → 0.14.1

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/configurator.mjs CHANGED
@@ -377,108 +377,142 @@ var footerSchema = z12.object({
377
377
  ]
378
378
  });
379
379
 
380
- // src/schema/widget/i18n.ts
380
+ // src/schema/widget/forms.ts
381
381
  import { z as z13 } from "zod";
382
- var localeStringsSchema = z13.record(z13.string(), z13.string());
383
- var stringsOverrideSchema = z13.union([localeStringsSchema, z13.record(localeSchema, localeStringsSchema)]).describe(
384
- "Per-instance UI string overrides. Flat = active-locale only; nested = multi-locale map. Both forms are partial \u2014 missing keys fall through to built-in defaults."
385
- ).meta({
386
- examples: [
387
- { send: "Click", stop: "Halt", composerPlaceholder: "Type your question\u2026" },
388
- {
389
- en: { send: "Send", composerPlaceholder: "Type a message\u2026" },
390
- fr: { send: "Envoyer", composerPlaceholder: "Tapez un message\u2026" }
391
- }
392
- ]
393
- });
394
- var i18nSchema = z13.object({
395
- locale: localeSchema.optional().describe("BCP-47 locale tag forced for this widget instance (e.g. `fr-CA`). Page-owner explicit override."),
396
- defaultLocale: z13.union([z13.literal("auto"), localeSchema]).default("auto").describe("Initial / fallback locale for users who haven't picked one. `auto` sniffs `navigator.language`."),
397
- availableLocales: z13.array(localeSchema).max(200).optional().describe("BCP-47 locale tags shown in the runtime language switcher. Hidden when fewer than 2 entries."),
398
- strings: stringsOverrideSchema.optional()
399
- }).loose().describe("Locale + i18n overrides.").meta({
400
- examples: [
401
- { defaultLocale: "auto" },
402
- { defaultLocale: "en", availableLocales: ["en", "fr", "de"] },
403
- { locale: "fr-CA", strings: { send: "Envoyer" } }
404
- ]
405
- });
406
-
407
- // src/schema/widget/intake.ts
408
- import { z as z14 } from "zod";
409
- var fieldTypeSchema = z14.enum([
382
+ var fieldTypeSchema = z13.enum([
410
383
  "text",
411
384
  "email",
412
385
  "tel",
413
386
  "url",
414
387
  "number",
388
+ "date",
389
+ "time",
415
390
  "textarea",
416
391
  "select",
417
392
  "radio",
418
393
  "checkbox",
419
394
  "multiselect"
420
395
  ]);
421
- var fieldOptionSchema = z14.object({
422
- value: z14.string().min(1).max(200).describe("The value recorded when this choice is picked."),
423
- label: z14.string().min(1).max(200).optional().describe("Display text; falls back to `value`."),
424
- description: z14.string().max(280).optional().describe("Optional sub-label under the choice.")
396
+ var fieldOptionSchema = z13.object({
397
+ value: z13.string().min(1).max(200).describe("The value recorded when this choice is picked."),
398
+ label: z13.string().min(1).max(200).optional().describe("Display text; falls back to `value`."),
399
+ description: z13.string().max(280).optional().describe("Optional sub-label under the choice.")
425
400
  }).loose();
426
- var fieldValidationSchema = z14.object({
427
- pattern: z14.string().max(500).optional().describe("Regex source string \u2014 text-like fields (e.g. `^\\d{5}$`)."),
428
- minLength: z14.number().int().min(0).max(1e4).optional(),
429
- maxLength: z14.number().int().min(1).max(1e4).optional(),
430
- min: z14.number().optional().describe("Minimum \u2014 `number` fields."),
431
- max: z14.number().optional().describe("Maximum \u2014 `number` fields."),
432
- minSelections: z14.number().int().min(0).max(50).optional().describe("Minimum picks \u2014 `multiselect`."),
433
- maxSelections: z14.number().int().min(1).max(50).optional().describe("Maximum picks \u2014 `multiselect`.")
401
+ var fieldValidationSchema = z13.object({
402
+ pattern: z13.string().max(500).optional().describe("Regex source string \u2014 text-like fields (e.g. `^\\d{5}$`)."),
403
+ minLength: z13.number().int().min(0).max(1e4).optional(),
404
+ maxLength: z13.number().int().min(1).max(1e4).optional(),
405
+ min: z13.number().optional().describe("Minimum \u2014 `number` fields."),
406
+ max: z13.number().optional().describe("Maximum \u2014 `number` fields."),
407
+ minSelections: z13.number().int().min(0).max(50).optional().describe("Minimum picks \u2014 `multiselect`."),
408
+ maxSelections: z13.number().int().min(1).max(50).optional().describe("Maximum picks \u2014 `multiselect`.")
434
409
  }).loose();
435
- var formFieldSchema = z14.object({
436
- name: z14.string().min(1).max(80).describe("Stable key the answer is recorded under (e.g. `email`). Sent to the agent as captured context."),
437
- label: z14.string().min(1).max(200).describe("Visible field label."),
438
- type: fieldTypeSchema.describe("Field renderer + built-in validation (`email`/`tel`/`url`/`number` are checked)."),
439
- placeholder: z14.string().max(200).optional(),
440
- required: z14.boolean().optional().default(true).describe("Must be answered to submit. Set `false` to allow skipping."),
441
- defaultValue: z14.string().max(2e3).optional().describe("Pre-filled value (comma-join for `multiselect`)."),
442
- options: z14.array(fieldOptionSchema).max(50).optional().describe("Choices for `select` / `radio` / `multiselect`."),
410
+ var formFieldSchema = z13.object({
411
+ name: z13.string().min(1).max(80).describe("Stable key the answer is recorded under (e.g. `email`)."),
412
+ label: z13.string().min(1).max(200).describe("Visible field label."),
413
+ type: fieldTypeSchema.describe("Field renderer + built-in validation (email/tel/url/number/date/time checked)."),
414
+ placeholder: z13.string().max(200).optional(),
415
+ required: z13.boolean().optional().default(true).describe("Must be answered to submit. `false` allows skipping."),
416
+ defaultValue: z13.string().max(2e3).optional().describe("Pre-filled value (comma-join for `multiselect`)."),
417
+ options: z13.array(fieldOptionSchema).max(50).optional().describe("Choices for `select` / `radio` / `multiselect`."),
443
418
  validation: fieldValidationSchema.optional(),
444
- allowOther: z14.boolean().optional().describe("`select`/`radio`/`multiselect` \u2014 allow a free-text 'Other' answer."),
445
- validationHint: z14.string().max(280).optional().describe("Small helper line under the field.")
419
+ allowOther: z13.boolean().optional().describe("`select`/`radio`/`multiselect` \u2014 allow a free-text 'Other' answer."),
420
+ validationHint: z13.string().max(280).optional().describe("Small helper line under the field.")
446
421
  }).loose();
447
- var intakeSchema = z14.object({
448
- enabled: z14.boolean().default(false).describe("Turn the pre-chat intake gate on. Off by default."),
449
- title: z14.string().max(120).optional().describe("Form heading (e.g. 'Before we start')."),
450
- description: z14.string().max(500).optional().describe("Sub-heading under the title."),
451
- fields: z14.array(formFieldSchema).max(20).default([]).describe("The questions to ask, in order. The gate is only active when there is \u22651 field."),
452
- submitLabel: z14.string().max(60).optional().describe("Submit button label (default 'Start chat')."),
453
- skippable: z14.boolean().default(false).describe("Show a Skip control to dismiss the whole form.")
454
- }).loose().describe(
455
- "Pre-chat intake form \u2014 a blocking lead/sales-capture gate of typed, validated fields. The same field shape powers the AI ask-input tool. Off by default."
422
+ var formTriggerSchema = z13.string().regex(
423
+ /^(pre-chat|conversation-closed|panel-close|manual|after-messages:\d+|idle:\d+|page-area:.+)$/,
424
+ "Unrecognised form trigger"
425
+ ).describe(
426
+ "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
427
+ );
428
+ var formConditionSchema = z13.object({
429
+ missingContext: z13.array(z13.string().max(80)).max(20).optional().describe("Only show if `userContext` lacks ALL of these keys (progressive profiling)."),
430
+ pageArea: z13.union([z13.string().max(64), z13.array(z13.string().max(64)).max(20)]).optional().describe("Only on these `pageContext.area` values."),
431
+ minMessages: z13.number().int().min(0).max(1e3).optional(),
432
+ maxMessages: z13.number().int().min(0).max(1e3).optional()
433
+ }).loose();
434
+ var formDefSchema = z13.object({
435
+ id: z13.string().min(1).max(80).describe("Stable id \u2014 persistence dedupe + `manual` openForm(id)."),
436
+ on: z13.union([formTriggerSchema, z13.array(formTriggerSchema).min(1).max(8)]).describe("Trigger(s) that surface this form."),
437
+ when: formConditionSchema.optional().describe("Extra eligibility predicate evaluated when a trigger fires."),
438
+ fields: z13.array(formFieldSchema).max(20).default([]).describe("The questions, in order. \u22651 required to show."),
439
+ title: z13.string().max(120).optional(),
440
+ description: z13.string().max(500).optional(),
441
+ submitLabel: z13.string().max(60).optional(),
442
+ skippable: z13.boolean().default(false).describe("Show a Skip control so the visitor can dismiss the form."),
443
+ frequency: z13.enum(["once", "session", "always"]).default("once").describe("`once` (persisted per-device), `session`, or `always`."),
444
+ mirrorToContext: z13.boolean().default(true).describe(
445
+ "Mirror the answers into `userContext` so they ride every request. Set `false` for PII (DOB, policy #, symptoms) \u2014 those then go only to the submit endpoint."
446
+ )
447
+ }).loose();
448
+ var formsSchema = z13.array(formDefSchema).max(20).describe(
449
+ "Event-driven forms \u2014 an ordered list (order = priority). Each binds to trigger event(s), gates the chat until submit/skip, dedupes by frequency, and records via `endpoints.submitForm`. Generic across verticals."
456
450
  ).meta({
457
451
  examples: [
458
- { enabled: false },
452
+ [{ id: "intake", on: "pre-chat", fields: [{ name: "email", label: "Email", type: "email" }] }],
453
+ [
454
+ {
455
+ id: "lead",
456
+ on: "pre-chat",
457
+ title: "Before we start",
458
+ fields: [
459
+ { name: "name", label: "Your name", type: "text" },
460
+ { name: "email", label: "Work email", type: "email" }
461
+ ]
462
+ },
463
+ {
464
+ id: "csat",
465
+ on: "conversation-closed",
466
+ frequency: "always",
467
+ fields: [
468
+ { name: "rating", label: "How did we do?", type: "radio", options: [{ value: "good" }, { value: "bad" }] }
469
+ ]
470
+ },
471
+ {
472
+ id: "appointment",
473
+ on: ["page-area:booking", "manual"],
474
+ mirrorToContext: false,
475
+ fields: [
476
+ {
477
+ name: "department",
478
+ label: "Department",
479
+ type: "select",
480
+ options: [{ value: "dermatology" }, { value: "cardiology" }]
481
+ },
482
+ { name: "date", label: "Preferred date", type: "date" },
483
+ { name: "consent", label: "I consent to be contacted", type: "checkbox" }
484
+ ]
485
+ }
486
+ ]
487
+ ]
488
+ });
489
+
490
+ // src/schema/widget/i18n.ts
491
+ import { z as z14 } from "zod";
492
+ var localeStringsSchema = z14.record(z14.string(), z14.string());
493
+ var stringsOverrideSchema = z14.union([localeStringsSchema, z14.record(localeSchema, localeStringsSchema)]).describe(
494
+ "Per-instance UI string overrides. Flat = active-locale only; nested = multi-locale map. Both forms are partial \u2014 missing keys fall through to built-in defaults."
495
+ ).meta({
496
+ examples: [
497
+ { send: "Click", stop: "Halt", composerPlaceholder: "Type your question\u2026" },
459
498
  {
460
- enabled: true,
461
- title: "Before we start",
462
- description: "Tell us who you are so we can help faster.",
463
- submitLabel: "Start chat",
464
- fields: [
465
- { name: "name", label: "Your name", type: "text", required: true },
466
- { name: "email", label: "Work email", type: "email", required: true },
467
- { name: "phone", label: "Phone", type: "tel", required: false },
468
- {
469
- name: "topic",
470
- label: "What can we help with?",
471
- type: "select",
472
- required: true,
473
- options: [
474
- { value: "sales", label: "Talk to sales" },
475
- { value: "support", label: "Get support" }
476
- ]
477
- }
478
- ]
499
+ en: { send: "Send", composerPlaceholder: "Type a message\u2026" },
500
+ fr: { send: "Envoyer", composerPlaceholder: "Tapez un message\u2026" }
479
501
  }
480
502
  ]
481
503
  });
504
+ var i18nSchema = z14.object({
505
+ locale: localeSchema.optional().describe("BCP-47 locale tag forced for this widget instance (e.g. `fr-CA`). Page-owner explicit override."),
506
+ defaultLocale: z14.union([z14.literal("auto"), localeSchema]).default("auto").describe("Initial / fallback locale for users who haven't picked one. `auto` sniffs `navigator.language`."),
507
+ availableLocales: z14.array(localeSchema).max(200).optional().describe("BCP-47 locale tags shown in the runtime language switcher. Hidden when fewer than 2 entries."),
508
+ strings: stringsOverrideSchema.optional()
509
+ }).loose().describe("Locale + i18n overrides.").meta({
510
+ examples: [
511
+ { defaultLocale: "auto" },
512
+ { defaultLocale: "en", availableLocales: ["en", "fr", "de"] },
513
+ { locale: "fr-CA", strings: { send: "Envoyer" } }
514
+ ]
515
+ });
482
516
 
483
517
  // src/schema/widget/modules.ts
484
518
  import { z as z15 } from "zod";
@@ -544,14 +578,14 @@ var modulesSchema = z15.array(moduleSchema).max(4).describe(
544
578
  var endpointsSchema = z16.object({
545
579
  upload: z16.string().min(1).max(2048).default("/ai/agent/upload-file"),
546
580
  transcribe: z16.string().min(1).max(2048).default("/ai/agent/transcribe-audio"),
547
- intake: z16.string().min(1).max(2048).default("/ai/agent/intake").describe(
548
- "Pre-chat intake submission endpoint. The widget POSTs the captured lead values here on completion (fire-and-forget), keyed by the same visitorId + sessionId as the conversation. Optional \u2014 a 404 is ignored."
581
+ submitForm: z16.string().min(1).max(2048).default("/ai/agent/submit-form").describe(
582
+ "Form submission endpoint. The widget POSTs each completed form's values here (fire-and-forget), keyed by the same visitorId + sessionId as the conversation; the payload carries `formId` + `trigger` so the backend can route. Optional \u2014 a 404 is ignored."
549
583
  )
550
584
  }).loose().describe("HTTP endpoint overrides. Paths are appended to baseUrl; absolute URLs are accepted too.").meta({
551
585
  examples: [
552
586
  { upload: "/ai/agent/upload-file", transcribe: "/ai/agent/transcribe-audio" },
553
587
  { upload: "https://help.ai/uploads/presign" },
554
- { intake: "https://crm.acme.com/leads/intake" }
588
+ { submitForm: "https://crm.acme.com/leads" }
555
589
  ]
556
590
  });
557
591
  var userContextSchema = z16.object({
@@ -633,7 +667,7 @@ var widgetSettingsSchema = z16.object({
633
667
  i18n: i18nSchema.optional(),
634
668
  footer: footerSchema.optional(),
635
669
  features: featureFlagsSchema.optional(),
636
- intake: intakeSchema.optional(),
670
+ forms: formsSchema.optional(),
637
671
  modules: modulesSchema.optional()
638
672
  }).loose().describe(
639
673
  "Portal-configurable widget settings \u2014 thirteen sections, one per dashboard tab. The backend pushes this same shape on `/ai/agent/start-session` under `config`."