@helpai/elements 0.13.1 → 0.14.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/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-Rtrqo8-b.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" | "formDismiss" | "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,20 @@ 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), shown blocking (composer gate) or inline (in the transcript), and
463
+ * its answers are recorded via one shared submit endpoint. Generic across
464
+ * verticals there is NO industry logic; a clinic intake, an insurance claim
465
+ * form, a CSAT survey, and a sales lead capture are all just different config.
466
+ *
467
+ * The same {@link FormField} shape powers the AI-driven ask-input tool.
466
468
  */
467
469
  /** A renderable field type. `multiselect` collects an array of values. */
468
- type FieldType = "text" | "email" | "tel" | "url" | "number" | "textarea" | "select" | "radio" | "checkbox" | "multiselect";
470
+ type FieldType = "text" | "email" | "tel" | "url" | "number" | "date" | "time" | "textarea" | "select" | "radio" | "checkbox" | "multiselect";
469
471
  /** One choice for `select` / `radio` / `multiselect` fields. */
470
472
  interface FieldOption {
471
473
  value: string;
@@ -486,7 +488,7 @@ interface FieldValidation {
486
488
  minSelections?: number;
487
489
  maxSelections?: number;
488
490
  }
489
- /** One field in a form (intake or ask-input). */
491
+ /** One field in a form. */
490
492
  interface FormField {
491
493
  /** Stable key the value is recorded under (e.g. `email`). */
492
494
  name: string;
@@ -505,29 +507,79 @@ interface FormField {
505
507
  validationHint?: string;
506
508
  }
507
509
  /**
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.
510
+ * Trigger that surfaces a form. Terse string with an optional `:param` so it's
511
+ * serializable + server-pushable. `after-messages:3`, `idle:30`,
512
+ * `page-area:pricing`.
512
513
  */
513
- interface IntakeOptions {
514
- enabled?: boolean;
514
+ type FormTrigger = "pre-chat" | `after-messages:${number}` | "conversation-closed" | "panel-close" | `idle:${number}` | `page-area:${string}` | "manual";
515
+ /** The trigger kinds, parsed out from the {@link FormTrigger} string. */
516
+ type TriggerKind = "pre-chat" | "after-messages" | "conversation-closed" | "panel-close" | "idle" | "page-area" | "manual";
517
+ /** A {@link FormTrigger} parsed into kind + optional param. */
518
+ interface ParsedTrigger {
519
+ kind: TriggerKind;
520
+ /** Numeric for `after-messages` / `idle`; string area for `page-area`. */
521
+ param?: number | string;
522
+ }
523
+ /**
524
+ * Extra eligibility predicate, evaluated when a trigger fires. Small and
525
+ * declarative on purpose — richer routing belongs to the agent / backend.
526
+ */
527
+ interface FormCondition {
528
+ /** Only show if `userContext` lacks ALL of these keys (progressive profiling). */
529
+ missingContext?: string[];
530
+ /** Only on these page areas (`pageContext.area`). */
531
+ pageArea?: string | string[];
532
+ minMessages?: number;
533
+ maxMessages?: number;
534
+ }
535
+ /** How often a form may show. */
536
+ type FormFrequency = "once" | "session" | "always";
537
+ /** One form as authored in config (raw input). */
538
+ interface FormDef {
539
+ /** Stable id — persistence dedupe + `manual` `openForm(id)`. */
540
+ id: string;
541
+ on: FormTrigger | FormTrigger[];
542
+ when?: FormCondition;
543
+ fields: FormField[];
515
544
  title?: string;
516
545
  description?: string;
517
- fields?: FormField[];
518
546
  submitLabel?: string;
519
- /** Allow dismissing the whole form without answering. Default `false`. */
520
547
  skippable?: boolean;
548
+ /** Gate the composer. Honored only for `pre-chat` / `after-messages`. */
549
+ blocking?: boolean;
550
+ /** Default `once`. */
551
+ frequency?: FormFrequency;
552
+ /**
553
+ * Mirror the submitted answers into `userContext` so they ride every
554
+ * subsequent request (the agent sees them in-conversation). Default `true`.
555
+ * Set `false` for PII (DOB, policy #, symptoms) — those then go ONLY to the
556
+ * submit endpoint, never onto the always-on context envelope.
557
+ */
558
+ mirrorToContext?: boolean;
521
559
  }
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;
560
+ /** The `forms` block is an ordered list (max 20); order = priority. */
561
+ type FormsOptions = FormDef[];
562
+ /** A fully-resolved form exposed on {@link ResolvedOptions}. */
563
+ interface ResolvedForm {
564
+ id: string;
565
+ triggers: ParsedTrigger[];
566
+ when?: FormCondition;
567
+ fields: FormField[];
526
568
  title?: string;
527
569
  description?: string;
528
- fields: FormField[];
529
570
  submitLabel?: string;
530
571
  skippable: boolean;
572
+ blocking: boolean;
573
+ frequency: FormFrequency;
574
+ /** Mirror answers into `userContext` (false → PII, endpoint only). */
575
+ mirrorToContext: boolean;
576
+ }
577
+ /** Fully-resolved forms block: ordered list + an event→forms index. */
578
+ interface ResolvedForms {
579
+ /** Forms in declared order (= priority). */
580
+ list: ResolvedForm[];
581
+ /** Forms keyed by trigger kind, for fast event lookup. */
582
+ byTrigger: Partial<Record<TriggerKind, ResolvedForm[]>>;
531
583
  }
532
584
 
533
585
  /**
@@ -900,13 +952,14 @@ interface Endpoints {
900
952
  /** Voice transcription endpoint. Default: `'/ai/agent/transcribe-audio'`. */
901
953
  transcribe?: string;
902
954
  /**
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.
955
+ * Form submission endpoint. Default: `'/ai/agent/submit-form'`. When any form
956
+ * (a blocking gate, an inline CSAT, etc.) is completed, the widget POSTs the
957
+ * captured values here (fire-and-forget), keyed by the same `visitorId` +
958
+ * `sessionId` the conversation uses; the payload carries `formId` + `trigger`
959
+ * so the backend can route. Optional: a backend that doesn't implement it just
960
+ * 404s and the submission is ignored.
908
961
  */
909
- intake?: string;
962
+ submitForm?: string;
910
963
  }
911
964
 
912
965
  /**
@@ -977,8 +1030,8 @@ interface ChatWidgetOptions {
977
1030
  footer?: FooterOptions;
978
1031
  /** Capability flags (file upload, voice mode, tools). Server-pushable. */
979
1032
  features?: FeatureFlags;
980
- /** Pre-chat intake form (lead/sales capture). Blocking gate. Server-pushable. */
981
- intake?: IntakeOptions;
1033
+ /** Event-driven forms (lead capture, CSAT, intake, consent…). Server-pushable. */
1034
+ forms?: FormsOptions;
982
1035
  /** Messenger modules (Chat / Home / Messages / Help / News tabs). Server-pushable. */
983
1036
  modules?: ModulesOptions;
984
1037
  /**
@@ -1026,8 +1079,8 @@ interface ResolvedOptions {
1026
1079
  features: Required<Omit<FeatureFlags, "tools">> & {
1027
1080
  tools?: Array<string | ToolRef>;
1028
1081
  };
1029
- /** Resolved pre-chat intake form `enabled` only when it has ≥1 valid field. */
1030
- intake: ResolvedIntake;
1082
+ /** Resolved event-driven formsordered list + trigger index. */
1083
+ forms: ResolvedForms;
1031
1084
  endpoints: Required<Endpoints>;
1032
1085
  attachments: Required<Pick<AttachmentLimits, "maxBytes" | "maxCount">> & {
1033
1086
  accept: string;
@@ -1105,7 +1158,7 @@ interface ServerConfig {
1105
1158
  i18n?: I18nOptions;
1106
1159
  footer?: FooterOptions;
1107
1160
  features?: FeatureFlags;
1108
- intake?: IntakeOptions;
1161
+ forms?: FormsOptions;
1109
1162
  modules?: ModulesOptions;
1110
1163
  endpoints?: Endpoints;
1111
1164
  }
@@ -1205,8 +1258,12 @@ interface EventMap {
1205
1258
  toggleHistory: {
1206
1259
  view: "chat" | "history";
1207
1260
  };
1208
- /** Pre-chat intake form completed (or skipped → empty values). */
1209
- intakeSubmit: Record<string, string>;
1261
+ /** A form (intake, CSAT, …) was completed or skipped. */
1262
+ formSubmit: {
1263
+ formId: string;
1264
+ values: Record<string, string>;
1265
+ skipped: boolean;
1266
+ };
1210
1267
  /** User answered an ask-input tool request (re-POST follows). */
1211
1268
  toolResult: {
1212
1269
  toolCallId: string;
@@ -1396,6 +1453,12 @@ interface ChatWidgetInstance {
1396
1453
  * URL is empty.
1397
1454
  */
1398
1455
  popOut: () => void;
1456
+ /**
1457
+ * Surface a configured form by id (a form with a `manual` trigger). Lets the
1458
+ * host open, say, an appointment or feedback form from its own button. No-op
1459
+ * if no form with that id + a `manual` trigger exists.
1460
+ */
1461
+ openForm: (id: string) => void;
1399
1462
  /**
1400
1463
  * Subscribe to a lifecycle event. Returns an `off` function. See
1401
1464
  * {@link EventMap} for the full set + payload shapes — currently