@helpai/elements 0.23.0 → 0.25.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/schema.json CHANGED
@@ -27,7 +27,13 @@
27
27
  "maxLength": 200
28
28
  },
29
29
  "baseUrl": {
30
- "description": "Full backend origin (e.g. `https://help.ai/api`). Set this to point at your staging / self-hosted / custom backend. Default: the brand's `defaultBaseUrl` baked at build time.",
30
+ "description": "MAIN site origin (e.g. `https://help.ai`) — the API modules derive from it: agent API at `${baseUrl}/api/agent`, data API at `${baseUrl}/api/data`. Set this to point at your staging / self-hosted / custom backend. Default: the brand's `defaultBaseUrl` baked at build time.",
31
+ "type": "string",
32
+ "minLength": 1,
33
+ "maxLength": 2048
34
+ },
35
+ "dataBaseUrl": {
36
+ "description": "Full data-API base (e.g. `https://help.ai/api/data`) — the service serving content, form definitions (`GET /forms`), and form submissions at root-level paths. Precedence: this option > server-pushed `config.dataBaseUrl` > derived from the main origin (`${baseUrl}/api/data`).",
31
37
  "type": "string",
32
38
  "minLength": 1,
33
39
  "maxLength": 2048
@@ -166,7 +172,7 @@
166
172
  "maxLength": 2048
167
173
  },
168
174
  "submitForm": {
169
- "default": "/ai/agent/submit-form",
175
+ "default": "/submit-form",
170
176
  "description": "Form submission endpoint. The widget POSTs each completed form's values here (fire-and-forget), keyed by the same visitorId + conversationId as the conversation; the payload carries `formId` + `trigger` so the backend can route. Optional — a 404 is ignored.",
171
177
  "type": "string",
172
178
  "minLength": 1,
@@ -179,7 +185,7 @@
179
185
  "submitForm"
180
186
  ],
181
187
  "additionalProperties": {},
182
- "description": "HTTP endpoint overrides. Paths are appended to baseUrl; absolute URLs are accepted too.",
188
+ "description": "HTTP endpoint overrides. `upload` / `transcribe` paths are appended to the agent base (`${baseUrl}/api/agent`), `submitForm` to the data base (`dataBaseUrl`); absolute URLs are accepted too.",
183
189
  "examples": [
184
190
  {
185
191
  "upload": "/ai/agent/upload-file",
@@ -1568,6 +1574,16 @@
1568
1574
  "default": true,
1569
1575
  "description": "Mirror the answers into `userContext` so they ride every request. Set `false` for PII (DOB, policy #, symptoms) — those then go only to the submit endpoint.",
1570
1576
  "type": "boolean"
1577
+ },
1578
+ "reopenable": {
1579
+ "default": true,
1580
+ "description": "Skipped marker offers a \"Fill out\" button that reopens the form. Set `false` when a skip must be final (e.g. a consent ask that should not be re-surfaced).",
1581
+ "type": "boolean"
1582
+ },
1583
+ "reviewable": {
1584
+ "default": true,
1585
+ "description": "Submitted marker expands in place to a read-only list of the visitor's answers. Set `false` for sensitive answers that shouldn't linger on screen.",
1586
+ "type": "boolean"
1571
1587
  }
1572
1588
  },
1573
1589
  "required": [
@@ -1576,7 +1592,9 @@
1576
1592
  "fields",
1577
1593
  "skippable",
1578
1594
  "frequency",
1579
- "mirrorToContext"
1595
+ "mirrorToContext",
1596
+ "reopenable",
1597
+ "reviewable"
1580
1598
  ],
1581
1599
  "additionalProperties": {}
1582
1600
  },
@@ -1692,7 +1710,7 @@
1692
1710
  "description": "UI layout that renders this tab: `chat` (the conversation), `home`, `help`, or `news`."
1693
1711
  },
1694
1712
  "contentTags": {
1695
- "description": "Content tags this tab scopes to — the `tags` filter on `GET /ai/agent/content` (matches content tagged with ANY of them). Omit for the `chat` layout.",
1713
+ "description": "Content tags this tab scopes to — the `tags` filter on `GET /content` (matches content tagged with ANY of them). Omit for the `chat` layout.",
1696
1714
  "maxItems": 20,
1697
1715
  "type": "array",
1698
1716
  "items": {
@@ -1847,7 +1865,7 @@
1847
1865
  "type": "boolean"
1848
1866
  },
1849
1867
  "endpoint": {
1850
- "description": "Pixel collector URL. Default: `https://t.<brand domain>/api/ai-elements/px.gif`. Must be a full URL.",
1868
+ "description": "Pixel collector URL. Default: `${dataBaseUrl}/elements/px.gif` (e.g. `https://help.ai/api/data/elements/px.gif`). Must be a full URL.",
1851
1869
  "type": "string",
1852
1870
  "maxLength": 2048,
1853
1871
  "format": "uri"
@@ -3074,356 +3092,6 @@
3074
3092
  }
3075
3093
  ]
3076
3094
  },
3077
- "forms": {
3078
- "maxItems": 20,
3079
- "type": "array",
3080
- "items": {
3081
- "type": "object",
3082
- "properties": {
3083
- "id": {
3084
- "type": "string",
3085
- "minLength": 1,
3086
- "maxLength": 80,
3087
- "description": "Stable id — persistence dedupe + `manual` openForm(id)."
3088
- },
3089
- "on": {
3090
- "anyOf": [
3091
- {
3092
- "type": "string",
3093
- "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
3094
- "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
3095
- },
3096
- {
3097
- "minItems": 1,
3098
- "maxItems": 8,
3099
- "type": "array",
3100
- "items": {
3101
- "type": "string",
3102
- "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
3103
- "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
3104
- }
3105
- }
3106
- ],
3107
- "description": "Trigger(s) that surface this form."
3108
- },
3109
- "when": {
3110
- "description": "Extra eligibility predicate evaluated when a trigger fires.",
3111
- "type": "object",
3112
- "properties": {
3113
- "missingContext": {
3114
- "description": "Only show if `userContext` lacks ALL of these keys (progressive profiling).",
3115
- "maxItems": 20,
3116
- "type": "array",
3117
- "items": {
3118
- "type": "string",
3119
- "maxLength": 80
3120
- }
3121
- },
3122
- "pageArea": {
3123
- "description": "Only on these `pageContext.area` values.",
3124
- "anyOf": [
3125
- {
3126
- "type": "string",
3127
- "maxLength": 64
3128
- },
3129
- {
3130
- "maxItems": 20,
3131
- "type": "array",
3132
- "items": {
3133
- "type": "string",
3134
- "maxLength": 64
3135
- }
3136
- }
3137
- ]
3138
- },
3139
- "minMessages": {
3140
- "type": "integer",
3141
- "minimum": 0,
3142
- "maximum": 1000
3143
- },
3144
- "maxMessages": {
3145
- "type": "integer",
3146
- "minimum": 0,
3147
- "maximum": 1000
3148
- }
3149
- },
3150
- "additionalProperties": {}
3151
- },
3152
- "fields": {
3153
- "default": [],
3154
- "description": "The questions, in order. ≥1 required to show.",
3155
- "maxItems": 20,
3156
- "type": "array",
3157
- "items": {
3158
- "type": "object",
3159
- "properties": {
3160
- "name": {
3161
- "type": "string",
3162
- "minLength": 1,
3163
- "maxLength": 80,
3164
- "description": "Stable key the answer is recorded under (e.g. `email`)."
3165
- },
3166
- "label": {
3167
- "type": "string",
3168
- "minLength": 1,
3169
- "maxLength": 200,
3170
- "description": "Visible field label."
3171
- },
3172
- "type": {
3173
- "type": "string",
3174
- "enum": [
3175
- "text",
3176
- "email",
3177
- "tel",
3178
- "url",
3179
- "number",
3180
- "date",
3181
- "time",
3182
- "textarea",
3183
- "select",
3184
- "radio",
3185
- "checkbox",
3186
- "multiselect"
3187
- ],
3188
- "description": "Field renderer + built-in validation (email/tel/url/number/date/time checked)."
3189
- },
3190
- "placeholder": {
3191
- "type": "string",
3192
- "maxLength": 200
3193
- },
3194
- "required": {
3195
- "default": true,
3196
- "description": "Must be answered to submit. `false` allows skipping.",
3197
- "type": "boolean"
3198
- },
3199
- "defaultValue": {
3200
- "description": "Pre-filled value (comma-join for `multiselect`).",
3201
- "type": "string",
3202
- "maxLength": 2000
3203
- },
3204
- "options": {
3205
- "description": "Choices for `select` / `radio` / `multiselect`.",
3206
- "maxItems": 50,
3207
- "type": "array",
3208
- "items": {
3209
- "type": "object",
3210
- "properties": {
3211
- "value": {
3212
- "type": "string",
3213
- "minLength": 1,
3214
- "maxLength": 200,
3215
- "description": "The value recorded when this choice is picked."
3216
- },
3217
- "label": {
3218
- "description": "Display text; falls back to `value`.",
3219
- "type": "string",
3220
- "minLength": 1,
3221
- "maxLength": 200
3222
- },
3223
- "description": {
3224
- "description": "Optional sub-label under the choice.",
3225
- "type": "string",
3226
- "maxLength": 280
3227
- }
3228
- },
3229
- "required": [
3230
- "value"
3231
- ],
3232
- "additionalProperties": {}
3233
- }
3234
- },
3235
- "validation": {
3236
- "type": "object",
3237
- "properties": {
3238
- "pattern": {
3239
- "description": "Regex source string — text-like fields (e.g. `^\\d{5}$`).",
3240
- "type": "string",
3241
- "maxLength": 500
3242
- },
3243
- "minLength": {
3244
- "type": "integer",
3245
- "minimum": 0,
3246
- "maximum": 10000
3247
- },
3248
- "maxLength": {
3249
- "type": "integer",
3250
- "minimum": 1,
3251
- "maximum": 10000
3252
- },
3253
- "min": {
3254
- "description": "Minimum — `number` fields.",
3255
- "type": "number"
3256
- },
3257
- "max": {
3258
- "description": "Maximum — `number` fields.",
3259
- "type": "number"
3260
- },
3261
- "minSelections": {
3262
- "description": "Minimum picks — `multiselect`.",
3263
- "type": "integer",
3264
- "minimum": 0,
3265
- "maximum": 50
3266
- },
3267
- "maxSelections": {
3268
- "description": "Maximum picks — `multiselect`.",
3269
- "type": "integer",
3270
- "minimum": 1,
3271
- "maximum": 50
3272
- }
3273
- },
3274
- "additionalProperties": {}
3275
- },
3276
- "allowOther": {
3277
- "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
3278
- "type": "boolean"
3279
- },
3280
- "validationHint": {
3281
- "description": "Small helper line under the field.",
3282
- "type": "string",
3283
- "maxLength": 280
3284
- }
3285
- },
3286
- "required": [
3287
- "name",
3288
- "label",
3289
- "type",
3290
- "required"
3291
- ],
3292
- "additionalProperties": {}
3293
- }
3294
- },
3295
- "title": {
3296
- "type": "string",
3297
- "maxLength": 120
3298
- },
3299
- "description": {
3300
- "type": "string",
3301
- "maxLength": 500
3302
- },
3303
- "submitLabel": {
3304
- "type": "string",
3305
- "maxLength": 60
3306
- },
3307
- "skippable": {
3308
- "default": false,
3309
- "description": "Show a Skip control so the visitor can dismiss the form.",
3310
- "type": "boolean"
3311
- },
3312
- "frequency": {
3313
- "default": "once",
3314
- "description": "`once` (per device, persisted), `conversation` (re-eligible each new conversation), or `always`.",
3315
- "type": "string",
3316
- "enum": [
3317
- "once",
3318
- "conversation",
3319
- "always"
3320
- ]
3321
- },
3322
- "mirrorToContext": {
3323
- "default": true,
3324
- "description": "Mirror the answers into `userContext` so they ride every request. Set `false` for PII (DOB, policy #, symptoms) — those then go only to the submit endpoint.",
3325
- "type": "boolean"
3326
- }
3327
- },
3328
- "required": [
3329
- "id",
3330
- "on",
3331
- "fields",
3332
- "skippable",
3333
- "frequency",
3334
- "mirrorToContext"
3335
- ],
3336
- "additionalProperties": {}
3337
- },
3338
- "description": "Event-driven forms — 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.",
3339
- "examples": [
3340
- [
3341
- {
3342
- "id": "intake",
3343
- "on": "pre-chat",
3344
- "fields": [
3345
- {
3346
- "name": "email",
3347
- "label": "Email",
3348
- "type": "email"
3349
- }
3350
- ]
3351
- }
3352
- ],
3353
- [
3354
- {
3355
- "id": "lead",
3356
- "on": "pre-chat",
3357
- "title": "Before we start",
3358
- "fields": [
3359
- {
3360
- "name": "name",
3361
- "label": "Your name",
3362
- "type": "text"
3363
- },
3364
- {
3365
- "name": "email",
3366
- "label": "Work email",
3367
- "type": "email"
3368
- }
3369
- ]
3370
- },
3371
- {
3372
- "id": "csat",
3373
- "on": "conversation-closed",
3374
- "frequency": "always",
3375
- "fields": [
3376
- {
3377
- "name": "rating",
3378
- "label": "How did we do?",
3379
- "type": "radio",
3380
- "options": [
3381
- {
3382
- "value": "good"
3383
- },
3384
- {
3385
- "value": "bad"
3386
- }
3387
- ]
3388
- }
3389
- ]
3390
- },
3391
- {
3392
- "id": "appointment",
3393
- "on": [
3394
- "page-area:booking",
3395
- "manual"
3396
- ],
3397
- "mirrorToContext": false,
3398
- "fields": [
3399
- {
3400
- "name": "department",
3401
- "label": "Department",
3402
- "type": "select",
3403
- "options": [
3404
- {
3405
- "value": "dermatology"
3406
- },
3407
- {
3408
- "value": "cardiology"
3409
- }
3410
- ]
3411
- },
3412
- {
3413
- "name": "date",
3414
- "label": "Preferred date",
3415
- "type": "date"
3416
- },
3417
- {
3418
- "name": "consent",
3419
- "label": "I consent to be contacted",
3420
- "type": "checkbox"
3421
- }
3422
- ]
3423
- }
3424
- ]
3425
- ]
3426
- },
3427
3095
  "modules": {
3428
3096
  "maxItems": 4,
3429
3097
  "type": "array",
@@ -3447,7 +3115,7 @@
3447
3115
  "description": "UI layout that renders this tab: `chat` (the conversation), `home`, `help`, or `news`."
3448
3116
  },
3449
3117
  "contentTags": {
3450
- "description": "Content tags this tab scopes to — the `tags` filter on `GET /ai/agent/content` (matches content tagged with ANY of them). Omit for the `chat` layout.",
3118
+ "description": "Content tags this tab scopes to — the `tags` filter on `GET /content` (matches content tagged with ANY of them). Omit for the `chat` layout.",
3451
3119
  "maxItems": 20,
3452
3120
  "type": "array",
3453
3121
  "items": {
@@ -3602,7 +3270,7 @@
3602
3270
  "type": "boolean"
3603
3271
  },
3604
3272
  "endpoint": {
3605
- "description": "Pixel collector URL. Default: `https://t.<brand domain>/api/ai-elements/px.gif`. Must be a full URL.",
3273
+ "description": "Pixel collector URL. Default: `${dataBaseUrl}/elements/px.gif` (e.g. `https://help.ai/api/data/elements/px.gif`). Must be a full URL.",
3606
3274
  "type": "string",
3607
3275
  "maxLength": 2048,
3608
3276
  "format": "uri"
@@ -3630,6 +3298,11 @@
3630
3298
  "sampleRate": 0.5
3631
3299
  }
3632
3300
  ]
3301
+ },
3302
+ "dataBaseUrl": {
3303
+ "type": "string",
3304
+ "minLength": 1,
3305
+ "maxLength": 2048
3633
3306
  }
3634
3307
  },
3635
3308
  "additionalProperties": {}
@@ -3854,33 +3527,6 @@
3854
3527
  "type": "array",
3855
3528
  "items": {}
3856
3529
  },
3857
- "formSubmissions": {
3858
- "type": "array",
3859
- "items": {
3860
- "type": "object",
3861
- "properties": {
3862
- "formId": {
3863
- "type": "string"
3864
- },
3865
- "trigger": {
3866
- "type": "string"
3867
- },
3868
- "skipped": {
3869
- "type": "boolean"
3870
- },
3871
- "createdAt": {
3872
- "type": "string",
3873
- "format": "date-time",
3874
- "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
3875
- }
3876
- },
3877
- "required": [
3878
- "formId",
3879
- "trigger"
3880
- ],
3881
- "additionalProperties": {}
3882
- }
3883
- },
3884
3530
  "userPrefs": {
3885
3531
  "type": "object",
3886
3532
  "propertyNames": {
package/schema.mjs CHANGED
@@ -429,6 +429,12 @@ var formDefSchema = z12.object({
429
429
  frequency: z12.enum(["once", "conversation", "always"]).default("once").describe("`once` (per device, persisted), `conversation` (re-eligible each new conversation), or `always`."),
430
430
  mirrorToContext: z12.boolean().default(true).describe(
431
431
  "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."
432
+ ),
433
+ reopenable: z12.boolean().default(true).describe(
434
+ 'Skipped marker offers a "Fill out" button that reopens the form. Set `false` when a skip must be final (e.g. a consent ask that should not be re-surfaced).'
435
+ ),
436
+ reviewable: z12.boolean().default(true).describe(
437
+ "Submitted marker expands in place to a read-only list of the visitor's answers. Set `false` for sensitive answers that shouldn't linger on screen."
432
438
  )
433
439
  }).loose();
434
440
  var formsSchema = z12.array(formDefSchema).max(20).describe(
@@ -511,7 +517,7 @@ var moduleSchema = z14.object({
511
517
  "UI layout that renders this tab: `chat` (the conversation), `home`, `help`, or `news`."
512
518
  ),
513
519
  contentTags: z14.array(z14.string().max(60)).max(20).optional().describe(
514
- "Content tags this tab scopes to \u2014 the `tags` filter on `GET /ai/agent/content` (matches content tagged with ANY of them). Omit for the `chat` layout."
520
+ "Content tags this tab scopes to \u2014 the `tags` filter on `GET /content` (matches content tagged with ANY of them). Omit for the `chat` layout."
515
521
  ),
516
522
  id: z14.string().max(60).optional().describe(
517
523
  "Stable id for navigation + last-tab persistence. Defaults to the first `contentTags` entry, else `layout`."
@@ -564,7 +570,9 @@ var modulesSchema = z14.array(moduleSchema).max(4).describe(
564
570
  import { z as z15 } from "zod";
565
571
  var trackingSchema = z15.object({
566
572
  enabled: z15.boolean().optional().describe("Master switch. Default `false` \u2014 no hits are sent until the deployment turns tracking on."),
567
- endpoint: z15.url().max(2048).optional().describe("Pixel collector URL. Default: `https://t.<brand domain>/api/ai-elements/px.gif`. Must be a full URL."),
573
+ endpoint: z15.url().max(2048).optional().describe(
574
+ "Pixel collector URL. Default: `${dataBaseUrl}/elements/px.gif` (e.g. `https://help.ai/api/data/elements/px.gif`). Must be a full URL."
575
+ ),
568
576
  sampleRate: z15.number().min(0).max(1).optional().describe("Per-visitor sampling 0..1 (decided once per page load, keeping funnels intact). Default `1`."),
569
577
  token: z15.string().max(512).optional().describe(
570
578
  "Anti-forgery token, echoed as `tk=` on every hit. Server-issued in the `/start-conversation` response \u2014 never set by hand (see `docs/api/tracking-collector.md`)."
@@ -579,10 +587,12 @@ var trackingSchema = z15.object({
579
587
  var endpointsSchema = z16.object({
580
588
  upload: z16.string().min(1).max(2048).default("/ai/agent/upload-file"),
581
589
  transcribe: z16.string().min(1).max(2048).default("/ai/agent/transcribe-audio"),
582
- submitForm: z16.string().min(1).max(2048).default("/ai/agent/submit-form").describe(
590
+ submitForm: z16.string().min(1).max(2048).default("/submit-form").describe(
583
591
  "Form submission endpoint. The widget POSTs each completed form's values here (fire-and-forget), keyed by the same visitorId + conversationId as the conversation; the payload carries `formId` + `trigger` so the backend can route. Optional \u2014 a 404 is ignored."
584
592
  )
585
- }).loose().describe("HTTP endpoint overrides. Paths are appended to baseUrl; absolute URLs are accepted too.").meta({
593
+ }).loose().describe(
594
+ "HTTP endpoint overrides. `upload` / `transcribe` paths are appended to the agent base (`${baseUrl}/api/agent`), `submitForm` to the data base (`dataBaseUrl`); absolute URLs are accepted too."
595
+ ).meta({
586
596
  examples: [
587
597
  { upload: "/ai/agent/upload-file", transcribe: "/ai/agent/transcribe-audio" },
588
598
  { upload: "https://help.ai/uploads/presign" },
@@ -631,7 +641,10 @@ var connectionConfigSchema = z16.object({
631
641
  "Deployment UUID from the dashboard. Picks which agent / settings bundle answers this mount. Sent as `x-ai-agent-deployment-id`."
632
642
  ),
633
643
  baseUrl: z16.string().min(1).max(2048).optional().describe(
634
- "Full backend origin (e.g. `https://help.ai/api`). Set this to point at your staging / self-hosted / custom backend. Default: the brand's `defaultBaseUrl` baked at build time."
644
+ "MAIN site origin (e.g. `https://help.ai`) \u2014 the API modules derive from it: agent API at `${baseUrl}/api/agent`, data API at `${baseUrl}/api/data`. Set this to point at your staging / self-hosted / custom backend. Default: the brand's `defaultBaseUrl` baked at build time."
645
+ ),
646
+ dataBaseUrl: z16.string().min(1).max(2048).optional().describe(
647
+ "Full data-API base (e.g. `https://help.ai/api/data`) \u2014 the service serving content, form definitions (`GET /forms`), and form submissions at root-level paths. Precedence: this option > server-pushed `config.dataBaseUrl` > derived from the main origin (`${baseUrl}/api/data`)."
635
648
  ),
636
649
  userContext: userContextSchema.optional(),
637
650
  pageContext: pageContextSchema.optional(),
@@ -716,7 +729,9 @@ var connectionConfigPartialSchema = connectionConfigSchema.partial();
716
729
 
717
730
  // src/schema/deployment.ts
718
731
  import { z as z17 } from "zod";
719
- var serverConfigSchema = widgetSettingsSchema.partial();
732
+ var serverConfigSchema = widgetSettingsSchema.partial().omit({ forms: true }).extend({
733
+ dataBaseUrl: z17.string().min(1).max(2048).optional()
734
+ });
720
735
  var siteConfigSchema = z17.object({
721
736
  title: z17.string().min(1).max(120).optional().describe("Brand / site name. Used as the logo's alt-text fallback."),
722
737
  logo: assetSchema.optional().describe("Brand logo shown at the top of the page-mode sidebar."),
@@ -775,20 +790,6 @@ var startConversationResponseSchema = z17.object({
775
790
  conversationId: z17.string(),
776
791
  canContinue: z17.boolean(),
777
792
  messages: z17.array(z17.unknown()),
778
- /**
779
- * Form submissions recorded for this conversation — echoed like
780
- * `messages` so the widget reconstructs the collapsed "form submitted"
781
- * timeline markers on resume. `createdAt` is ISO-8601 (a serialized
782
- * Mongo ISODate — same format as message `createdAt`).
783
- */
784
- formSubmissions: z17.array(
785
- z17.object({
786
- formId: z17.string(),
787
- trigger: z17.string(),
788
- skipped: z17.boolean().optional(),
789
- createdAt: z17.iso.datetime().optional()
790
- }).loose()
791
- ).optional(),
792
793
  /**
793
794
  * Per-visitor preferences (locale picker, theme picker, sound
794
795
  * mute, dragged panel size). Distinct from `config` — this is