@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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, L as Link, S as ServerConfig, b as SiteConfig, c as StartSessionResponse, W as WidgetConfig, d as WidgetConfigPartial, e as WidgetSettings, f as WidgetSettingsPartial } from './deployment-Big98MYV.js';
1
+ export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, L as Link, S as ServerConfig, b as SiteConfig, c as StartSessionResponse, W as WidgetConfig, d as WidgetConfigPartial, e as WidgetSettings, f as WidgetSettingsPartial } from './deployment-DYIboFNT.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
@@ -40,7 +40,7 @@ interface ClientStorage {
40
40
  * populated {@link Strings} map. UI components then read `strings.send` as a
41
41
  * normal property access — no per-render lookup, no per-render allocation.
42
42
  */
43
- type StringKey = "launcherOpen" | "panelTitle" | "composerPlaceholder" | "send" | "stop" | "attach" | "micStart" | "micStop" | "micUnsupported" | "expand" | "collapse" | "fullscreen" | "exitFullscreen" | "resizeHandle" | "scrollToBottom" | "popOut" | "close" | "moreActions" | "soundOn" | "soundOff" | "language" | "theme" | "themeAuto" | "themeLight" | "themeDark" | "history" | "historyTitle" | "historyEmpty" | "historyLoading" | "historyNewChat" | "historyBack" | "messagesLoading" | "chatClosed" | "startNewConversation" | "dateToday" | "dateYesterday" | "dateLastWeek" | "dateOlder" | "newConversation" | "dropZone" | "attachmentTooLarge" | "attachmentTooMany" | "attachmentMimeRejected" | "errorRetry" | "errorGeneric" | "errorRateLimited" | "loading" | "thinking" | "thoughts" | "usedTool" | "collapseSidebar" | "expandSidebar" | "intakeSubmit" | "intakeSkip" | "formRequired" | "formInvalidEmail" | "formInvalidTel" | "formInvalidUrl" | "formInvalidNumber" | "formTooShort" | "formTooLong" | "formNumberTooSmall" | "formNumberTooLarge" | "formPatternMismatch" | "formChooseAtLeast" | "formChooseAtMost" | "formOther" | "formOtherPlaceholder" | "inputRequired" | "inputSubmit" | "inputSkip" | "inputSubmitted" | "approvalRequired" | "approve" | "reject" | "approved" | "rejected" | "approvalReason" | "stepNoLongerActive" | "tabHome" | "tabMessages" | "tabHelp" | "tabNews" | "modulesEmpty" | "moduleBack" | "contentLoading" | "homeGreeting" | "homeGreetingNamed" | "homeGreetingLead" | "homeSearchPlaceholder" | "homeContentTitle" | "helpTitle" | "helpSearchPlaceholder" | "helpEmpty" | "helpLoading" | "helpSearchEmpty" | "newsTitle" | "newsEmpty" | "newsLoading" | "newsBack" | "newsPublishedAt";
43
+ type StringKey = "launcherOpen" | "panelTitle" | "composerPlaceholder" | "send" | "stop" | "attach" | "micStart" | "micStop" | "micUnsupported" | "expand" | "collapse" | "fullscreen" | "exitFullscreen" | "resizeHandle" | "scrollToBottom" | "popOut" | "close" | "moreActions" | "soundOn" | "soundOff" | "language" | "theme" | "themeAuto" | "themeLight" | "themeDark" | "history" | "historyTitle" | "historyEmpty" | "historyLoading" | "historyNewChat" | "historyBack" | "messagesLoading" | "chatClosed" | "startNewConversation" | "dateToday" | "dateYesterday" | "dateLastWeek" | "dateOlder" | "newConversation" | "dropZone" | "attachmentTooLarge" | "attachmentTooMany" | "attachmentMimeRejected" | "errorRetry" | "errorGeneric" | "errorRateLimited" | "loading" | "thinking" | "thoughts" | "usedTool" | "collapseSidebar" | "expandSidebar" | "formSubmit" | "formSkip" | "formRequired" | "formInvalidEmail" | "formInvalidTel" | "formInvalidUrl" | "formInvalidNumber" | "formTooShort" | "formTooLong" | "formNumberTooSmall" | "formNumberTooLarge" | "formPatternMismatch" | "formChooseAtLeast" | "formChooseAtMost" | "formOther" | "formOtherPlaceholder" | "inputRequired" | "inputSubmit" | "inputSkip" | "inputSubmitted" | "approvalRequired" | "approve" | "reject" | "approved" | "rejected" | "approvalReason" | "stepNoLongerActive" | "tabHome" | "tabMessages" | "tabHelp" | "tabNews" | "modulesEmpty" | "moduleBack" | "contentLoading" | "homeGreeting" | "homeGreetingNamed" | "homeGreetingLead" | "homeSearchPlaceholder" | "homeContentTitle" | "helpTitle" | "helpSearchPlaceholder" | "helpEmpty" | "helpLoading" | "helpSearchEmpty" | "newsTitle" | "newsEmpty" | "newsLoading" | "newsBack" | "newsPublishedAt";
44
44
  /** A partial map for one locale — what overrides look like on the wire. */
45
45
  type LocaleStrings = Partial<Record<StringKey, string>>;
46
46
  /**
@@ -454,18 +454,21 @@ interface FeedbackOptions {
454
454
  }
455
455
 
456
456
  /**
457
- * Intake form + shared form-field runtime config types framework-free
458
- * mirror of `src/schema/widget/intake.ts`. The IIFE runtime path has no
459
- * Zod, so these plain interfaces are what the resolver + form engine consume.
457
+ * Event-driven forms engine framework-free runtime types (mirror of
458
+ * `src/schema/widget/forms.ts`). The IIFE path has no Zod, so these plain
459
+ * interfaces are what the resolver, controller, and form UI consume.
460
460
  *
461
- * The same {@link FormField} shape powers two surfaces:
462
- * 1. the deterministic pre-chat **intake** gate (this section), and
463
- * 2. the AI-driven **ask-input tool** (`src/ui/tool-ask-input.tsx`), which
464
- * maps a wire ask-input request onto `FormField[]` and reuses the same
465
- * `<DynamicForm>` renderer.
461
+ * A deployment declares an array of {@link FormDef}s; each is bound to trigger
462
+ * event(s) and shown as a gate that replaces the composer (locking the chat
463
+ * until submit/skip). Its answers are recorded via one shared submit endpoint.
464
+ * Generic across verticals there is NO industry logic; a clinic intake, an
465
+ * insurance claim form, a CSAT survey, and a sales lead capture are all just
466
+ * different config.
467
+ *
468
+ * The same {@link FormField} shape powers the AI-driven ask-input tool.
466
469
  */
467
470
  /** A renderable field type. `multiselect` collects an array of values. */
468
- type FieldType = "text" | "email" | "tel" | "url" | "number" | "textarea" | "select" | "radio" | "checkbox" | "multiselect";
471
+ type FieldType = "text" | "email" | "tel" | "url" | "number" | "date" | "time" | "textarea" | "select" | "radio" | "checkbox" | "multiselect";
469
472
  /** One choice for `select` / `radio` / `multiselect` fields. */
470
473
  interface FieldOption {
471
474
  value: string;
@@ -486,7 +489,7 @@ interface FieldValidation {
486
489
  minSelections?: number;
487
490
  maxSelections?: number;
488
491
  }
489
- /** One field in a form (intake or ask-input). */
492
+ /** One field in a form. */
490
493
  interface FormField {
491
494
  /** Stable key the value is recorded under (e.g. `email`). */
492
495
  name: string;
@@ -505,29 +508,76 @@ interface FormField {
505
508
  validationHint?: string;
506
509
  }
507
510
  /**
508
- * Section: pre-chat intake form. A **blocking gate** when `enabled` and not
509
- * yet completed, the user must submit (or skip, if `skippable`) before the
510
- * composer unlocks. Captured values are persisted per-device and attached to
511
- * the first request. Off by default — existing deployments are unchanged.
511
+ * Trigger that surfaces a form. Terse string with an optional `:param` so it's
512
+ * serializable + server-pushable. `after-messages:3`, `idle:30`,
513
+ * `page-area:pricing`.
512
514
  */
513
- interface IntakeOptions {
514
- enabled?: boolean;
515
+ type FormTrigger = "pre-chat" | `after-messages:${number}` | "conversation-closed" | "panel-close" | `idle:${number}` | `page-area:${string}` | "manual";
516
+ /** The trigger kinds, parsed out from the {@link FormTrigger} string. */
517
+ type TriggerKind = "pre-chat" | "after-messages" | "conversation-closed" | "panel-close" | "idle" | "page-area" | "manual";
518
+ /** A {@link FormTrigger} parsed into kind + optional param. */
519
+ interface ParsedTrigger {
520
+ kind: TriggerKind;
521
+ /** Numeric for `after-messages` / `idle`; string area for `page-area`. */
522
+ param?: number | string;
523
+ }
524
+ /**
525
+ * Extra eligibility predicate, evaluated when a trigger fires. Small and
526
+ * declarative on purpose — richer routing belongs to the agent / backend.
527
+ */
528
+ interface FormCondition {
529
+ /** Only show if `userContext` lacks ALL of these keys (progressive profiling). */
530
+ missingContext?: string[];
531
+ /** Only on these page areas (`pageContext.area`). */
532
+ pageArea?: string | string[];
533
+ minMessages?: number;
534
+ maxMessages?: number;
535
+ }
536
+ /** How often a form may show. */
537
+ type FormFrequency = "once" | "session" | "always";
538
+ /** One form as authored in config (raw input). */
539
+ interface FormDef {
540
+ /** Stable id — persistence dedupe + `manual` `openForm(id)`. */
541
+ id: string;
542
+ on: FormTrigger | FormTrigger[];
543
+ when?: FormCondition;
544
+ fields: FormField[];
515
545
  title?: string;
516
546
  description?: string;
517
- fields?: FormField[];
518
547
  submitLabel?: string;
519
- /** Allow dismissing the whole form without answering. Default `false`. */
520
548
  skippable?: boolean;
549
+ /** Default `once`. */
550
+ frequency?: FormFrequency;
551
+ /**
552
+ * Mirror the submitted answers into `userContext` so they ride every
553
+ * subsequent request (the agent sees them in-conversation). Default `true`.
554
+ * Set `false` for PII (DOB, policy #, symptoms) — those then go ONLY to the
555
+ * submit endpoint, never onto the always-on context envelope.
556
+ */
557
+ mirrorToContext?: boolean;
521
558
  }
522
- /** Fully-resolved intake config exposed on {@link ResolvedOptions}. */
523
- interface ResolvedIntake {
524
- /** Effective only when there is at least one valid field. */
525
- enabled: boolean;
559
+ /** The `forms` block is an ordered list (max 20); order = priority. */
560
+ type FormsOptions = FormDef[];
561
+ /** A fully-resolved form exposed on {@link ResolvedOptions}. */
562
+ interface ResolvedForm {
563
+ id: string;
564
+ triggers: ParsedTrigger[];
565
+ when?: FormCondition;
566
+ fields: FormField[];
526
567
  title?: string;
527
568
  description?: string;
528
- fields: FormField[];
529
569
  submitLabel?: string;
530
570
  skippable: boolean;
571
+ frequency: FormFrequency;
572
+ /** Mirror answers into `userContext` (false → PII, endpoint only). */
573
+ mirrorToContext: boolean;
574
+ }
575
+ /** Fully-resolved forms block: ordered list + an event→forms index. */
576
+ interface ResolvedForms {
577
+ /** Forms in declared order (= priority). */
578
+ list: ResolvedForm[];
579
+ /** Forms keyed by trigger kind, for fast event lookup. */
580
+ byTrigger: Partial<Record<TriggerKind, ResolvedForm[]>>;
531
581
  }
532
582
 
533
583
  /**
@@ -900,13 +950,14 @@ interface Endpoints {
900
950
  /** Voice transcription endpoint. Default: `'/ai/agent/transcribe-audio'`. */
901
951
  transcribe?: string;
902
952
  /**
903
- * Pre-chat intake submission endpoint. Default: `'/ai/agent/intake'`. When the
904
- * intake gate is completed, the widget POSTs the captured values here
905
- * (fire-and-forget) so the lead is recorded immediately — keyed by the same
906
- * `visitorId` + `sessionId` the conversation will use. Optional: a backend
907
- * that doesn't implement it just 404s and the submission is ignored.
953
+ * Form submission endpoint. Default: `'/ai/agent/submit-form'`. When any form
954
+ * (a pre-chat lead capture, a CSAT survey, etc.) is completed, the widget POSTs
955
+ * the captured values here (fire-and-forget), keyed by the same `visitorId` +
956
+ * `sessionId` the conversation uses; the payload carries `formId` + `trigger`
957
+ * so the backend can route. Optional: a backend that doesn't implement it just
958
+ * 404s and the submission is ignored.
908
959
  */
909
- intake?: string;
960
+ submitForm?: string;
910
961
  }
911
962
 
912
963
  /**
@@ -977,8 +1028,8 @@ interface ChatWidgetOptions {
977
1028
  footer?: FooterOptions;
978
1029
  /** Capability flags (file upload, voice mode, tools). Server-pushable. */
979
1030
  features?: FeatureFlags;
980
- /** Pre-chat intake form (lead/sales capture). Blocking gate. Server-pushable. */
981
- intake?: IntakeOptions;
1031
+ /** Event-driven forms (lead capture, CSAT, intake, consent…). Server-pushable. */
1032
+ forms?: FormsOptions;
982
1033
  /** Messenger modules (Chat / Home / Messages / Help / News tabs). Server-pushable. */
983
1034
  modules?: ModulesOptions;
984
1035
  /**
@@ -1026,8 +1077,8 @@ interface ResolvedOptions {
1026
1077
  features: Required<Omit<FeatureFlags, "tools">> & {
1027
1078
  tools?: Array<string | ToolRef>;
1028
1079
  };
1029
- /** Resolved pre-chat intake form `enabled` only when it has ≥1 valid field. */
1030
- intake: ResolvedIntake;
1080
+ /** Resolved event-driven formsordered list + trigger index. */
1081
+ forms: ResolvedForms;
1031
1082
  endpoints: Required<Endpoints>;
1032
1083
  attachments: Required<Pick<AttachmentLimits, "maxBytes" | "maxCount">> & {
1033
1084
  accept: string;
@@ -1105,7 +1156,7 @@ interface ServerConfig {
1105
1156
  i18n?: I18nOptions;
1106
1157
  footer?: FooterOptions;
1107
1158
  features?: FeatureFlags;
1108
- intake?: IntakeOptions;
1159
+ forms?: FormsOptions;
1109
1160
  modules?: ModulesOptions;
1110
1161
  endpoints?: Endpoints;
1111
1162
  }
@@ -1205,8 +1256,12 @@ interface EventMap {
1205
1256
  toggleHistory: {
1206
1257
  view: "chat" | "history";
1207
1258
  };
1208
- /** Pre-chat intake form completed (or skipped → empty values). */
1209
- intakeSubmit: Record<string, string>;
1259
+ /** A form (intake, CSAT, …) was completed or skipped. */
1260
+ formSubmit: {
1261
+ formId: string;
1262
+ values: Record<string, string>;
1263
+ skipped: boolean;
1264
+ };
1210
1265
  /** User answered an ask-input tool request (re-POST follows). */
1211
1266
  toolResult: {
1212
1267
  toolCallId: string;
@@ -1396,6 +1451,12 @@ interface ChatWidgetInstance {
1396
1451
  * URL is empty.
1397
1452
  */
1398
1453
  popOut: () => void;
1454
+ /**
1455
+ * Surface a configured form by id (a form with a `manual` trigger). Lets the
1456
+ * host open, say, an appointment or feedback form from its own button. No-op
1457
+ * if no form with that id + a `manual` trigger exists.
1458
+ */
1459
+ openForm: (id: string) => void;
1399
1460
  /**
1400
1461
  * Subscribe to a lifecycle event. Returns an `off` function. See
1401
1462
  * {@link EventMap} for the full set + payload shapes — currently