@helpai/elements 0.24.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",
@@ -1704,7 +1710,7 @@
1704
1710
  "description": "UI layout that renders this tab: `chat` (the conversation), `home`, `help`, or `news`."
1705
1711
  },
1706
1712
  "contentTags": {
1707
- "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.",
1708
1714
  "maxItems": 20,
1709
1715
  "type": "array",
1710
1716
  "items": {
@@ -1859,7 +1865,7 @@
1859
1865
  "type": "boolean"
1860
1866
  },
1861
1867
  "endpoint": {
1862
- "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.",
1863
1869
  "type": "string",
1864
1870
  "maxLength": 2048,
1865
1871
  "format": "uri"
@@ -3086,368 +3092,6 @@
3086
3092
  }
3087
3093
  ]
3088
3094
  },
3089
- "forms": {
3090
- "maxItems": 20,
3091
- "type": "array",
3092
- "items": {
3093
- "type": "object",
3094
- "properties": {
3095
- "id": {
3096
- "type": "string",
3097
- "minLength": 1,
3098
- "maxLength": 80,
3099
- "description": "Stable id — persistence dedupe + `manual` openForm(id)."
3100
- },
3101
- "on": {
3102
- "anyOf": [
3103
- {
3104
- "type": "string",
3105
- "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
3106
- "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
3107
- },
3108
- {
3109
- "minItems": 1,
3110
- "maxItems": 8,
3111
- "type": "array",
3112
- "items": {
3113
- "type": "string",
3114
- "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
3115
- "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
3116
- }
3117
- }
3118
- ],
3119
- "description": "Trigger(s) that surface this form."
3120
- },
3121
- "when": {
3122
- "description": "Extra eligibility predicate evaluated when a trigger fires.",
3123
- "type": "object",
3124
- "properties": {
3125
- "missingContext": {
3126
- "description": "Only show if `userContext` lacks ALL of these keys (progressive profiling).",
3127
- "maxItems": 20,
3128
- "type": "array",
3129
- "items": {
3130
- "type": "string",
3131
- "maxLength": 80
3132
- }
3133
- },
3134
- "pageArea": {
3135
- "description": "Only on these `pageContext.area` values.",
3136
- "anyOf": [
3137
- {
3138
- "type": "string",
3139
- "maxLength": 64
3140
- },
3141
- {
3142
- "maxItems": 20,
3143
- "type": "array",
3144
- "items": {
3145
- "type": "string",
3146
- "maxLength": 64
3147
- }
3148
- }
3149
- ]
3150
- },
3151
- "minMessages": {
3152
- "type": "integer",
3153
- "minimum": 0,
3154
- "maximum": 1000
3155
- },
3156
- "maxMessages": {
3157
- "type": "integer",
3158
- "minimum": 0,
3159
- "maximum": 1000
3160
- }
3161
- },
3162
- "additionalProperties": {}
3163
- },
3164
- "fields": {
3165
- "default": [],
3166
- "description": "The questions, in order. ≥1 required to show.",
3167
- "maxItems": 20,
3168
- "type": "array",
3169
- "items": {
3170
- "type": "object",
3171
- "properties": {
3172
- "name": {
3173
- "type": "string",
3174
- "minLength": 1,
3175
- "maxLength": 80,
3176
- "description": "Stable key the answer is recorded under (e.g. `email`)."
3177
- },
3178
- "label": {
3179
- "type": "string",
3180
- "minLength": 1,
3181
- "maxLength": 200,
3182
- "description": "Visible field label."
3183
- },
3184
- "type": {
3185
- "type": "string",
3186
- "enum": [
3187
- "text",
3188
- "email",
3189
- "tel",
3190
- "url",
3191
- "number",
3192
- "date",
3193
- "time",
3194
- "textarea",
3195
- "select",
3196
- "radio",
3197
- "checkbox",
3198
- "multiselect"
3199
- ],
3200
- "description": "Field renderer + built-in validation (email/tel/url/number/date/time checked)."
3201
- },
3202
- "placeholder": {
3203
- "type": "string",
3204
- "maxLength": 200
3205
- },
3206
- "required": {
3207
- "default": true,
3208
- "description": "Must be answered to submit. `false` allows skipping.",
3209
- "type": "boolean"
3210
- },
3211
- "defaultValue": {
3212
- "description": "Pre-filled value (comma-join for `multiselect`).",
3213
- "type": "string",
3214
- "maxLength": 2000
3215
- },
3216
- "options": {
3217
- "description": "Choices for `select` / `radio` / `multiselect`.",
3218
- "maxItems": 50,
3219
- "type": "array",
3220
- "items": {
3221
- "type": "object",
3222
- "properties": {
3223
- "value": {
3224
- "type": "string",
3225
- "minLength": 1,
3226
- "maxLength": 200,
3227
- "description": "The value recorded when this choice is picked."
3228
- },
3229
- "label": {
3230
- "description": "Display text; falls back to `value`.",
3231
- "type": "string",
3232
- "minLength": 1,
3233
- "maxLength": 200
3234
- },
3235
- "description": {
3236
- "description": "Optional sub-label under the choice.",
3237
- "type": "string",
3238
- "maxLength": 280
3239
- }
3240
- },
3241
- "required": [
3242
- "value"
3243
- ],
3244
- "additionalProperties": {}
3245
- }
3246
- },
3247
- "validation": {
3248
- "type": "object",
3249
- "properties": {
3250
- "pattern": {
3251
- "description": "Regex source string — text-like fields (e.g. `^\\d{5}$`).",
3252
- "type": "string",
3253
- "maxLength": 500
3254
- },
3255
- "minLength": {
3256
- "type": "integer",
3257
- "minimum": 0,
3258
- "maximum": 10000
3259
- },
3260
- "maxLength": {
3261
- "type": "integer",
3262
- "minimum": 1,
3263
- "maximum": 10000
3264
- },
3265
- "min": {
3266
- "description": "Minimum — `number` fields.",
3267
- "type": "number"
3268
- },
3269
- "max": {
3270
- "description": "Maximum — `number` fields.",
3271
- "type": "number"
3272
- },
3273
- "minSelections": {
3274
- "description": "Minimum picks — `multiselect`.",
3275
- "type": "integer",
3276
- "minimum": 0,
3277
- "maximum": 50
3278
- },
3279
- "maxSelections": {
3280
- "description": "Maximum picks — `multiselect`.",
3281
- "type": "integer",
3282
- "minimum": 1,
3283
- "maximum": 50
3284
- }
3285
- },
3286
- "additionalProperties": {}
3287
- },
3288
- "allowOther": {
3289
- "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
3290
- "type": "boolean"
3291
- },
3292
- "validationHint": {
3293
- "description": "Small helper line under the field.",
3294
- "type": "string",
3295
- "maxLength": 280
3296
- }
3297
- },
3298
- "required": [
3299
- "name",
3300
- "label",
3301
- "type",
3302
- "required"
3303
- ],
3304
- "additionalProperties": {}
3305
- }
3306
- },
3307
- "title": {
3308
- "type": "string",
3309
- "maxLength": 120
3310
- },
3311
- "description": {
3312
- "type": "string",
3313
- "maxLength": 500
3314
- },
3315
- "submitLabel": {
3316
- "type": "string",
3317
- "maxLength": 60
3318
- },
3319
- "skippable": {
3320
- "default": false,
3321
- "description": "Show a Skip control so the visitor can dismiss the form.",
3322
- "type": "boolean"
3323
- },
3324
- "frequency": {
3325
- "default": "once",
3326
- "description": "`once` (per device, persisted), `conversation` (re-eligible each new conversation), or `always`.",
3327
- "type": "string",
3328
- "enum": [
3329
- "once",
3330
- "conversation",
3331
- "always"
3332
- ]
3333
- },
3334
- "mirrorToContext": {
3335
- "default": true,
3336
- "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.",
3337
- "type": "boolean"
3338
- },
3339
- "reopenable": {
3340
- "default": true,
3341
- "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).",
3342
- "type": "boolean"
3343
- },
3344
- "reviewable": {
3345
- "default": true,
3346
- "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.",
3347
- "type": "boolean"
3348
- }
3349
- },
3350
- "required": [
3351
- "id",
3352
- "on",
3353
- "fields",
3354
- "skippable",
3355
- "frequency",
3356
- "mirrorToContext",
3357
- "reopenable",
3358
- "reviewable"
3359
- ],
3360
- "additionalProperties": {}
3361
- },
3362
- "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.",
3363
- "examples": [
3364
- [
3365
- {
3366
- "id": "intake",
3367
- "on": "pre-chat",
3368
- "fields": [
3369
- {
3370
- "name": "email",
3371
- "label": "Email",
3372
- "type": "email"
3373
- }
3374
- ]
3375
- }
3376
- ],
3377
- [
3378
- {
3379
- "id": "lead",
3380
- "on": "pre-chat",
3381
- "title": "Before we start",
3382
- "fields": [
3383
- {
3384
- "name": "name",
3385
- "label": "Your name",
3386
- "type": "text"
3387
- },
3388
- {
3389
- "name": "email",
3390
- "label": "Work email",
3391
- "type": "email"
3392
- }
3393
- ]
3394
- },
3395
- {
3396
- "id": "csat",
3397
- "on": "conversation-closed",
3398
- "frequency": "always",
3399
- "fields": [
3400
- {
3401
- "name": "rating",
3402
- "label": "How did we do?",
3403
- "type": "radio",
3404
- "options": [
3405
- {
3406
- "value": "good"
3407
- },
3408
- {
3409
- "value": "bad"
3410
- }
3411
- ]
3412
- }
3413
- ]
3414
- },
3415
- {
3416
- "id": "appointment",
3417
- "on": [
3418
- "page-area:booking",
3419
- "manual"
3420
- ],
3421
- "mirrorToContext": false,
3422
- "fields": [
3423
- {
3424
- "name": "department",
3425
- "label": "Department",
3426
- "type": "select",
3427
- "options": [
3428
- {
3429
- "value": "dermatology"
3430
- },
3431
- {
3432
- "value": "cardiology"
3433
- }
3434
- ]
3435
- },
3436
- {
3437
- "name": "date",
3438
- "label": "Preferred date",
3439
- "type": "date"
3440
- },
3441
- {
3442
- "name": "consent",
3443
- "label": "I consent to be contacted",
3444
- "type": "checkbox"
3445
- }
3446
- ]
3447
- }
3448
- ]
3449
- ]
3450
- },
3451
3095
  "modules": {
3452
3096
  "maxItems": 4,
3453
3097
  "type": "array",
@@ -3471,7 +3115,7 @@
3471
3115
  "description": "UI layout that renders this tab: `chat` (the conversation), `home`, `help`, or `news`."
3472
3116
  },
3473
3117
  "contentTags": {
3474
- "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.",
3475
3119
  "maxItems": 20,
3476
3120
  "type": "array",
3477
3121
  "items": {
@@ -3626,7 +3270,7 @@
3626
3270
  "type": "boolean"
3627
3271
  },
3628
3272
  "endpoint": {
3629
- "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.",
3630
3274
  "type": "string",
3631
3275
  "maxLength": 2048,
3632
3276
  "format": "uri"
@@ -3654,6 +3298,11 @@
3654
3298
  "sampleRate": 0.5
3655
3299
  }
3656
3300
  ]
3301
+ },
3302
+ "dataBaseUrl": {
3303
+ "type": "string",
3304
+ "minLength": 1,
3305
+ "maxLength": 2048
3657
3306
  }
3658
3307
  },
3659
3308
  "additionalProperties": {}
@@ -3878,33 +3527,6 @@
3878
3527
  "type": "array",
3879
3528
  "items": {}
3880
3529
  },
3881
- "formSubmissions": {
3882
- "type": "array",
3883
- "items": {
3884
- "type": "object",
3885
- "properties": {
3886
- "formId": {
3887
- "type": "string"
3888
- },
3889
- "trigger": {
3890
- "type": "string"
3891
- },
3892
- "skipped": {
3893
- "type": "boolean"
3894
- },
3895
- "createdAt": {
3896
- "type": "string",
3897
- "format": "date-time",
3898
- "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))$"
3899
- }
3900
- },
3901
- "required": [
3902
- "formId",
3903
- "trigger"
3904
- ],
3905
- "additionalProperties": {}
3906
- }
3907
- },
3908
3530
  "userPrefs": {
3909
3531
  "type": "object",
3910
3532
  "propertyNames": {
package/schema.mjs CHANGED
@@ -517,7 +517,7 @@ var moduleSchema = z14.object({
517
517
  "UI layout that renders this tab: `chat` (the conversation), `home`, `help`, or `news`."
518
518
  ),
519
519
  contentTags: z14.array(z14.string().max(60)).max(20).optional().describe(
520
- "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."
521
521
  ),
522
522
  id: z14.string().max(60).optional().describe(
523
523
  "Stable id for navigation + last-tab persistence. Defaults to the first `contentTags` entry, else `layout`."
@@ -570,7 +570,9 @@ var modulesSchema = z14.array(moduleSchema).max(4).describe(
570
570
  import { z as z15 } from "zod";
571
571
  var trackingSchema = z15.object({
572
572
  enabled: z15.boolean().optional().describe("Master switch. Default `false` \u2014 no hits are sent until the deployment turns tracking on."),
573
- 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
+ ),
574
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`."),
575
577
  token: z15.string().max(512).optional().describe(
576
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`)."
@@ -585,10 +587,12 @@ var trackingSchema = z15.object({
585
587
  var endpointsSchema = z16.object({
586
588
  upload: z16.string().min(1).max(2048).default("/ai/agent/upload-file"),
587
589
  transcribe: z16.string().min(1).max(2048).default("/ai/agent/transcribe-audio"),
588
- 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(
589
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."
590
592
  )
591
- }).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({
592
596
  examples: [
593
597
  { upload: "/ai/agent/upload-file", transcribe: "/ai/agent/transcribe-audio" },
594
598
  { upload: "https://help.ai/uploads/presign" },
@@ -637,7 +641,10 @@ var connectionConfigSchema = z16.object({
637
641
  "Deployment UUID from the dashboard. Picks which agent / settings bundle answers this mount. Sent as `x-ai-agent-deployment-id`."
638
642
  ),
639
643
  baseUrl: z16.string().min(1).max(2048).optional().describe(
640
- "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`)."
641
648
  ),
642
649
  userContext: userContextSchema.optional(),
643
650
  pageContext: pageContextSchema.optional(),
@@ -722,7 +729,9 @@ var connectionConfigPartialSchema = connectionConfigSchema.partial();
722
729
 
723
730
  // src/schema/deployment.ts
724
731
  import { z as z17 } from "zod";
725
- var serverConfigSchema = widgetSettingsSchema.partial();
732
+ var serverConfigSchema = widgetSettingsSchema.partial().omit({ forms: true }).extend({
733
+ dataBaseUrl: z17.string().min(1).max(2048).optional()
734
+ });
726
735
  var siteConfigSchema = z17.object({
727
736
  title: z17.string().min(1).max(120).optional().describe("Brand / site name. Used as the logo's alt-text fallback."),
728
737
  logo: assetSchema.optional().describe("Brand logo shown at the top of the page-mode sidebar."),
@@ -781,20 +790,6 @@ var startConversationResponseSchema = z17.object({
781
790
  conversationId: z17.string(),
782
791
  canContinue: z17.boolean(),
783
792
  messages: z17.array(z17.unknown()),
784
- /**
785
- * Form submissions recorded for this conversation — echoed like
786
- * `messages` so the widget reconstructs the collapsed "form submitted"
787
- * timeline markers on resume. `createdAt` is ISO-8601 (a serialized
788
- * Mongo ISODate — same format as message `createdAt`).
789
- */
790
- formSubmissions: z17.array(
791
- z17.object({
792
- formId: z17.string(),
793
- trigger: z17.string(),
794
- skipped: z17.boolean().optional(),
795
- createdAt: z17.iso.datetime().optional()
796
- }).loose()
797
- ).optional(),
798
793
  /**
799
794
  * Per-visitor preferences (locale picker, theme picker, sound
800
795
  * mute, dragged panel size). Distinct from `config` — this is