@helpai/elements 0.27.0 → 0.28.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, H as HandshakeResponse, L as Link, S as ServerConfig, b as SiteConfig, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial } from './deployment-CcW_mx_i.js';
1
+ export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, H as HandshakeResponse, L as Link, S as ServerConfig, b as SiteConfig, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial } from './deployment-E-Jyc0nF.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
@@ -896,7 +896,7 @@ interface PageContext {
896
896
  /**
897
897
  * Override the HTTP endpoints the widget calls. `upload` / `transcribe`
898
898
  * paths are appended to the agent base (`${baseUrl}/api/agent`);
899
- * `submitForm` is appended to the data base (`dataBaseUrl`). Absolute URLs
899
+ * `submitForm` is appended to the data base (`dataApiBaseUrl`). Absolute URLs
900
900
  * also work (useful when the upload endpoint lives on a different origin
901
901
  * like a CDN presign service).
902
902
  */
@@ -939,7 +939,7 @@ interface TrackingOptions {
939
939
  */
940
940
  enabled?: boolean;
941
941
  /**
942
- * Pixel collector URL. Default: `${dataBaseUrl}/pai/elements-px.gif`
942
+ * Pixel collector URL. Default: `${dataApiBaseUrl}/pai/elements-px.gif`
943
943
  * (e.g. `https://help.ai/api/data/pai/elements-px.gif`).
944
944
  */
945
945
  endpoint?: string;
@@ -1016,22 +1016,22 @@ interface ChatWidgetOptions {
1016
1016
  * Agent-API base (no trailing slash) — set it when the
1017
1017
  * `module-help-ai-agent-api` service is reached DIRECTLY (a standalone
1018
1018
  * local module, e.g. `http://localhost:3087`) instead of through the
1019
- * edge. Routes attach as-is (`${agentBaseUrl}/pai/handshake`).
1019
+ * edge. Routes attach as-is (`${agentApiBaseUrl}/pai/handshake`).
1020
1020
  * Precedence: this option > derived from the main origin
1021
1021
  * (`${baseUrl}/api/agent`). Not server-pushable — the handshake already
1022
1022
  * travelled over it.
1023
1023
  */
1024
- agentBaseUrl?: string;
1024
+ agentApiBaseUrl?: string;
1025
1025
  /**
1026
1026
  * Data-API base (no trailing slash) — the `module-help-ai-data-api`
1027
1027
  * service serving content, form definitions, and form submissions at
1028
1028
  * root-level paths (`/content`, `/forms`, `/activity`, `/submit-form`).
1029
1029
  * Set it for a standalone local module (e.g. `http://localhost:3106`).
1030
- * Precedence: this option > server-pushed `config.dataBaseUrl` > derived
1030
+ * Precedence: this option > server-pushed `config.dataApiBaseUrl` > derived
1031
1031
  * from the main origin (`${baseUrl}/api/data`, e.g.
1032
1032
  * `https://help.ai/api/data`).
1033
1033
  */
1034
- dataBaseUrl?: string;
1034
+ dataApiBaseUrl?: string;
1035
1035
  /** Host-asserted, informational end-user context (untrusted; not auth). */
1036
1036
  userContext?: UserContext;
1037
1037
  /** Host-asserted, informational page/visit context (untrusted; `area`, url, …). */
@@ -1086,10 +1086,10 @@ interface ResolvedOptions {
1086
1086
  publicKey?: string;
1087
1087
  aiAgentDeploymentId?: string;
1088
1088
  baseUrl: string;
1089
- /** Resolved agent-API base — see {@link ChatWidgetOptions.agentBaseUrl}. */
1090
- agentBaseUrl: string;
1091
- /** Resolved data-API base — see {@link ChatWidgetOptions.dataBaseUrl}. */
1092
- dataBaseUrl: string;
1089
+ /** Resolved agent-API base — see {@link ChatWidgetOptions.agentApiBaseUrl}. */
1090
+ agentApiBaseUrl: string;
1091
+ /** Resolved data-API base — see {@link ChatWidgetOptions.dataApiBaseUrl}. */
1092
+ dataApiBaseUrl: string;
1093
1093
  userContext?: UserContext;
1094
1094
  pageContext?: PageContext;
1095
1095
  position: Position;
@@ -1188,7 +1188,7 @@ interface ResolvedOptions {
1188
1188
  */
1189
1189
  interface ServerConfig {
1190
1190
  /** Data-API base override — adopted only when the host didn't set one. */
1191
- dataBaseUrl?: string;
1191
+ dataApiBaseUrl?: string;
1192
1192
  presentation?: PresentationOptions;
1193
1193
  behavior?: BehaviorOptions;
1194
1194
  theme?: ThemePreference | ThemeOverrides;
package/index.mjs CHANGED
@@ -525,15 +525,15 @@ function resolveOptions(rawOpts) {
525
525
  const locale = i18n.locale ?? resolveDefaultLocale(i18n.defaultLocale);
526
526
  const availableLocales = i18n.availableLocales ?? [locale];
527
527
  const baseUrl = (opts.baseUrl ?? BRAND.defaultBaseUrl).replace(/\/$/, "");
528
- const agentBaseUrl = (opts.agentBaseUrl ?? `${baseUrl}/api/agent`).replace(/\/$/, "");
529
- const dataBaseUrl = (opts.dataBaseUrl ?? `${baseUrl}/api/data`).replace(/\/$/, "");
528
+ const agentApiBaseUrl = (opts.agentApiBaseUrl ?? `${baseUrl}/api/agent`).replace(/\/$/, "");
529
+ const dataApiBaseUrl = (opts.dataApiBaseUrl ?? `${baseUrl}/api/data`).replace(/\/$/, "");
530
530
  return {
531
531
  widgetId: opts.widgetId ?? "default",
532
532
  publicKey: opts.publicKey,
533
533
  aiAgentDeploymentId: opts.aiAgentDeploymentId,
534
534
  baseUrl,
535
- agentBaseUrl,
536
- dataBaseUrl,
535
+ agentApiBaseUrl,
536
+ dataApiBaseUrl,
537
537
  userContext: sanitizeUserContext(opts.userContext),
538
538
  pageContext: sanitizePageContext(opts.pageContext),
539
539
  position: presentation.position ?? "bottom-right",
@@ -578,7 +578,7 @@ function resolveOptions(rawOpts) {
578
578
  poweredBy: resolvePoweredBy(footer.poweredBy),
579
579
  composer: resolveComposer(opts.composer),
580
580
  modules: resolveModules(opts.modules),
581
- tracking: resolveTracking(opts.tracking, dataBaseUrl),
581
+ tracking: resolveTracking(opts.tracking, dataApiBaseUrl),
582
582
  storage: opts.storage ?? defaultStorage,
583
583
  onMessage: opts.onMessage,
584
584
  onOpen: opts.onOpen,
@@ -611,13 +611,13 @@ function resolveHaptics(overrides) {
611
611
  events: overrides?.events
612
612
  };
613
613
  }
614
- function resolveTracking(overrides, dataBaseUrl) {
614
+ function resolveTracking(overrides, dataApiBaseUrl) {
615
615
  const sampleRate = overrides?.sampleRate ?? DEFAULT_TRACKING.sampleRate;
616
616
  return {
617
617
  enabled: overrides?.enabled ?? DEFAULT_TRACKING.enabled,
618
- // The collector rides the data module — `${dataBaseUrl}/pai/elements-px.gif`
618
+ // The collector rides the data module — `${dataApiBaseUrl}/pai/elements-px.gif`
619
619
  // (e.g. `https://help.ai/api/data/pai/elements-px.gif`).
620
- endpoint: overrides?.endpoint ?? `${dataBaseUrl}/pai/elements-px.gif`,
620
+ endpoint: overrides?.endpoint ?? `${dataApiBaseUrl}/pai/elements-px.gif`,
621
621
  // Clamp instead of reject — a malformed rate must never turn a
622
622
  // disabled tracker on or crash resolve (no Zod on the IIFE path).
623
623
  sampleRate: Math.min(1, Math.max(0, Number.isFinite(sampleRate) ? sampleRate : 1)),
@@ -786,7 +786,8 @@ function mergeServerConfig(user, server) {
786
786
  out[key] = mergeLeaves(sv, uv);
787
787
  }
788
788
  if (server.modules !== void 0 && user.modules === void 0) out.modules = server.modules;
789
- if (server.dataBaseUrl !== void 0 && user.dataBaseUrl === void 0) out.dataBaseUrl = server.dataBaseUrl;
789
+ if (server.dataApiBaseUrl !== void 0 && user.dataApiBaseUrl === void 0)
790
+ out.dataApiBaseUrl = server.dataApiBaseUrl;
790
791
  const userI18n = user.i18n;
791
792
  const serverI18n = server.i18n;
792
793
  if (userI18n || serverI18n) {
@@ -846,8 +847,8 @@ var EMBED_SCALAR_ATTRS = [
846
847
  ["public-key", "publicKey"],
847
848
  ["ai-agent-deployment-id", "aiAgentDeploymentId"],
848
849
  ["base-url", "baseUrl"],
849
- ["agent-base-url", "agentBaseUrl"],
850
- ["data-base-url", "dataBaseUrl"],
850
+ ["agent-api-base-url", "agentApiBaseUrl"],
851
+ ["data-api-base-url", "dataApiBaseUrl"],
851
852
  ["theme", "theme", (v) => v]
852
853
  ];
853
854
  var PRESENTATION_ATTRS = [
@@ -1283,7 +1284,7 @@ var DEFAULT_PATHS = {
1283
1284
  * on the client; a failure just leaves the badge until the next sync.
1284
1285
  */
1285
1286
  markRead: "/pai/mark-read",
1286
- // ── Data API (module-help-ai-data-api, via `dataBaseUrl`) ─────────
1287
+ // ── Data API (module-help-ai-data-api, via `dataApiBaseUrl`) ─────────
1287
1288
  /**
1288
1289
  * The data module's one ACTIVATION read. `GET
1289
1290
  * /pai/bootstrap?visitorId=…&conversationId=…` →
@@ -1380,6 +1381,7 @@ function messageToWireParts(m) {
1380
1381
  var log4 = logger.scope("transport");
1381
1382
  var MAX_RESUME_ATTEMPTS = 3;
1382
1383
  var RESUME_BACKOFF_MS = 400;
1384
+ var CONTENT_CACHE_TTL_MS = 6e4;
1383
1385
  var MAX_REQUEST_RETRIES = 3;
1384
1386
  var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
1385
1387
  function backoffMs(attempt) {
@@ -1418,6 +1420,20 @@ var AgentTransport = class {
1418
1420
  // always sees the visitor's latest context as they navigate.
1419
1421
  __publicField(this, "userContext");
1420
1422
  __publicField(this, "pageContext");
1423
+ // ---- Data API (content / forms — module-help-ai-data-api) --------------
1424
+ //
1425
+ // The DATA surfaces live on `dataApiBaseUrl` (default `${baseUrl}/api/data`)
1426
+ // under the module's `/pai/*` group — same auth headers + envelope, same
1427
+ // retry behavior. One endpoint for every content surface (Home / Help /
1428
+ // News / docs); callers pass filters; a thrown StreamError (e.g. 404 on an
1429
+ // older backend) is treated as "no content" by the module and the tab
1430
+ // degrades gracefully.
1431
+ // Per-query content cache: published content barely changes mid-visit, but
1432
+ // every tab switch remounts its module and re-asks. Entries hold the
1433
+ // REQUEST PROMISE (concurrent identical queries share one network call)
1434
+ // and serve repeats for CONTENT_CACHE_TTL_MS; failures are evicted so the
1435
+ // next ask retries.
1436
+ __publicField(this, "contentCache", /* @__PURE__ */ new Map());
1421
1437
  }
1422
1438
  // ---- Public API -------------------------------------------------------
1423
1439
  /**
@@ -1574,16 +1590,22 @@ var AgentTransport = class {
1574
1590
  log4.debug("markRead failed (non-fatal)", { err });
1575
1591
  }
1576
1592
  }
1577
- // ---- Data API (content / forms module-help-ai-data-api) --------------
1578
- //
1579
- // The DATA surfaces live on `dataBaseUrl` (default `${baseUrl}/api/data`)
1580
- // with ROOT-level paths — same auth headers + envelope, same retry
1581
- // behavior. One endpoint for every content surface (Home / Help / News /
1582
- // docs); callers pass filters; a thrown StreamError (e.g. 404 on an older
1583
- // backend) is treated as "no content" by the module and the tab degrades
1584
- // gracefully.
1585
- /** Fetch content rows by filter. `GET /content` (data-API). */
1586
- async listContent(query = {}) {
1593
+ /** Fetch content rows by filter. `GET /pai/content` (data-API), TTL-cached. */
1594
+ listContent(query = {}) {
1595
+ const key = JSON.stringify(query, Object.keys(query).toSorted());
1596
+ const cached = this.contentCache.get(key);
1597
+ if (cached && Date.now() - cached.at < CONTENT_CACHE_TTL_MS) {
1598
+ log4.debug("listContent \u2192 cache", query);
1599
+ return cached.result;
1600
+ }
1601
+ const result = this.fetchContent(query).catch((err) => {
1602
+ this.contentCache.delete(key);
1603
+ throw err;
1604
+ });
1605
+ this.contentCache.set(key, { at: Date.now(), result });
1606
+ return result;
1607
+ }
1608
+ async fetchContent(query) {
1587
1609
  const url = new URL(this.dataUrl(DEFAULT_PATHS.content));
1588
1610
  for (const [key, value] of Object.entries(query)) {
1589
1611
  if (value === void 0) continue;
@@ -1889,7 +1911,7 @@ var AgentTransport = class {
1889
1911
  */
1890
1912
  url(pathOrUrl) {
1891
1913
  if (/^https?:\/\//i.test(pathOrUrl)) return pathOrUrl;
1892
- return `${this.opts.agentBaseUrl}${pathOrUrl}`;
1914
+ return `${this.opts.agentApiBaseUrl}${pathOrUrl}`;
1893
1915
  }
1894
1916
  /**
1895
1917
  * Data-API variant of {@link url} — resolves a root-level path (content /
@@ -1899,7 +1921,7 @@ var AgentTransport = class {
1899
1921
  */
1900
1922
  dataUrl(pathOrUrl) {
1901
1923
  if (/^https?:\/\//i.test(pathOrUrl)) return pathOrUrl;
1902
- return `${this.opts.dataBaseUrl}${pathOrUrl}`;
1924
+ return `${this.opts.dataApiBaseUrl}${pathOrUrl}`;
1903
1925
  }
1904
1926
  get fetchImpl() {
1905
1927
  return this.opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
@@ -6465,8 +6487,8 @@ function App({ options, hostElement, bus }) {
6465
6487
  const [formContext, setFormContext] = useState13({});
6466
6488
  const [transport] = useState13(
6467
6489
  () => new AgentTransport({
6468
- agentBaseUrl: options.agentBaseUrl,
6469
- dataBaseUrl: options.dataBaseUrl,
6490
+ agentApiBaseUrl: options.agentApiBaseUrl,
6491
+ dataApiBaseUrl: options.dataApiBaseUrl,
6470
6492
  auth: createAuth({
6471
6493
  publicKey: options.publicKey,
6472
6494
  aiAgentDeploymentId: options.aiAgentDeploymentId,
package/package.json CHANGED
@@ -80,5 +80,5 @@
80
80
  ],
81
81
  "type": "module",
82
82
  "types": "./index.d.ts",
83
- "version": "0.27.0"
83
+ "version": "0.28.1"
84
84
  }
package/schema.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, E as Endpoints, H as HandshakeResponse, L as Link, P as PAGE_AREA_SUGGESTIONS, f as PageContext, S as ServerConfig, b as SiteConfig, U as UserContext, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial, g as assetSchema, h as blocksConfigSchema, i as connectionConfigPartialSchema, j as connectionConfigSchema, k as cssColorSchema, l as cssLengthSchema, m as endpointsSchema, n as handshakeResponseSchema, o as linkSchema, p as localeSchema, q as pageContextSchema, s as serverConfigSchema, r as siteConfigSchema, u as userContextSchema, t as uuid7Schema, w as widgetConfigPartialSchema, v as widgetConfigSchema, x as widgetSettingsPartialSchema, y as widgetSettingsSchema } from './deployment-CcW_mx_i.js';
1
+ export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, E as Endpoints, H as HandshakeResponse, L as Link, P as PAGE_AREA_SUGGESTIONS, f as PageContext, S as ServerConfig, b as SiteConfig, U as UserContext, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial, g as assetSchema, h as blocksConfigSchema, i as connectionConfigPartialSchema, j as connectionConfigSchema, k as cssColorSchema, l as cssLengthSchema, m as endpointsSchema, n as handshakeResponseSchema, o as linkSchema, p as localeSchema, q as pageContextSchema, s as serverConfigSchema, r as siteConfigSchema, u as userContextSchema, t as uuid7Schema, w as widgetConfigPartialSchema, v as widgetConfigSchema, x as widgetSettingsPartialSchema, y as widgetSettingsSchema } from './deployment-E-Jyc0nF.js';
2
2
  import { z } from 'zod';
3
3
 
4
4
  /**
package/schema.json CHANGED
@@ -32,14 +32,14 @@
32
32
  "minLength": 1,
33
33
  "maxLength": 2048
34
34
  },
35
- "agentBaseUrl": {
35
+ "agentApiBaseUrl": {
36
36
  "description": "Full agent-API base — set it to reach a STANDALONE `module-help-ai-agent-api` directly (local module on its own port, e.g. `http://localhost:3087`; routes attach as-is: `/pai/handshake`). Precedence: this option > derived from the main origin (`${baseUrl}/api/agent`). Not server-pushable.",
37
37
  "type": "string",
38
38
  "minLength": 1,
39
39
  "maxLength": 2048
40
40
  },
41
- "dataBaseUrl": {
42
- "description": "Full data-API base (e.g. `https://help.ai/api/data`, or a standalone local module like `http://localhost:3106`) — 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`).",
41
+ "dataApiBaseUrl": {
42
+ "description": "Full data-API base (e.g. `https://help.ai/api/data`, or a standalone local module like `http://localhost:3106`) — the service serving content, form definitions (`GET /forms`), and form submissions at root-level paths. Precedence: this option > server-pushed `config.dataApiBaseUrl` > derived from the main origin (`${baseUrl}/api/data`).",
43
43
  "type": "string",
44
44
  "minLength": 1,
45
45
  "maxLength": 2048
@@ -191,7 +191,7 @@
191
191
  "submitForm"
192
192
  ],
193
193
  "additionalProperties": {},
194
- "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.",
194
+ "description": "HTTP endpoint overrides. `upload` / `transcribe` paths are appended to the agent base (`${baseUrl}/api/agent`), `submitForm` to the data base (`dataApiBaseUrl`); absolute URLs are accepted too.",
195
195
  "examples": [
196
196
  {
197
197
  "upload": "/pai/upload-file",
@@ -1871,7 +1871,7 @@
1871
1871
  "type": "boolean"
1872
1872
  },
1873
1873
  "endpoint": {
1874
- "description": "Pixel collector URL. Default: `${dataBaseUrl}/pai/elements-px.gif` (e.g. `https://help.ai/api/data/pai/elements-px.gif`). Must be a full URL.",
1874
+ "description": "Pixel collector URL. Default: `${dataApiBaseUrl}/pai/elements-px.gif` (e.g. `https://help.ai/api/data/pai/elements-px.gif`). Must be a full URL.",
1875
1875
  "type": "string",
1876
1876
  "maxLength": 2048,
1877
1877
  "format": "uri"
@@ -3276,7 +3276,7 @@
3276
3276
  "type": "boolean"
3277
3277
  },
3278
3278
  "endpoint": {
3279
- "description": "Pixel collector URL. Default: `${dataBaseUrl}/pai/elements-px.gif` (e.g. `https://help.ai/api/data/pai/elements-px.gif`). Must be a full URL.",
3279
+ "description": "Pixel collector URL. Default: `${dataApiBaseUrl}/pai/elements-px.gif` (e.g. `https://help.ai/api/data/pai/elements-px.gif`). Must be a full URL.",
3280
3280
  "type": "string",
3281
3281
  "maxLength": 2048,
3282
3282
  "format": "uri"
@@ -3305,7 +3305,7 @@
3305
3305
  }
3306
3306
  ]
3307
3307
  },
3308
- "dataBaseUrl": {
3308
+ "dataApiBaseUrl": {
3309
3309
  "type": "string",
3310
3310
  "minLength": 1,
3311
3311
  "maxLength": 2048
package/schema.mjs CHANGED
@@ -573,7 +573,7 @@ var trackingSchema = z15.object({
573
573
  "Master switch. Default `true` \u2014 the widget always fires; the data module gates ingestion per deployment server-side. Set `false` to silence the client entirely."
574
574
  ),
575
575
  endpoint: z15.url().max(2048).optional().describe(
576
- "Pixel collector URL. Default: `${dataBaseUrl}/pai/elements-px.gif` (e.g. `https://help.ai/api/data/pai/elements-px.gif`). Must be a full URL."
576
+ "Pixel collector URL. Default: `${dataApiBaseUrl}/pai/elements-px.gif` (e.g. `https://help.ai/api/data/pai/elements-px.gif`). Must be a full URL."
577
577
  ),
578
578
  sampleRate: z15.number().min(0).max(1).optional().describe("Per-visitor sampling 0..1 (decided once per page load, keeping funnels intact). Default `1`."),
579
579
  token: z15.string().max(512).optional().describe(
@@ -593,7 +593,7 @@ var endpointsSchema = z16.object({
593
593
  "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."
594
594
  )
595
595
  }).loose().describe(
596
- "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."
596
+ "HTTP endpoint overrides. `upload` / `transcribe` paths are appended to the agent base (`${baseUrl}/api/agent`), `submitForm` to the data base (`dataApiBaseUrl`); absolute URLs are accepted too."
597
597
  ).meta({
598
598
  examples: [
599
599
  { upload: "/pai/upload-file", transcribe: "/pai/transcribe-audio" },
@@ -645,11 +645,11 @@ var connectionConfigSchema = z16.object({
645
645
  baseUrl: z16.string().min(1).max(2048).optional().describe(
646
646
  "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."
647
647
  ),
648
- agentBaseUrl: z16.string().min(1).max(2048).optional().describe(
648
+ agentApiBaseUrl: z16.string().min(1).max(2048).optional().describe(
649
649
  "Full agent-API base \u2014 set it to reach a STANDALONE `module-help-ai-agent-api` directly (local module on its own port, e.g. `http://localhost:3087`; routes attach as-is: `/pai/handshake`). Precedence: this option > derived from the main origin (`${baseUrl}/api/agent`). Not server-pushable."
650
650
  ),
651
- dataBaseUrl: z16.string().min(1).max(2048).optional().describe(
652
- "Full data-API base (e.g. `https://help.ai/api/data`, or a standalone local module like `http://localhost:3106`) \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`)."
651
+ dataApiBaseUrl: z16.string().min(1).max(2048).optional().describe(
652
+ "Full data-API base (e.g. `https://help.ai/api/data`, or a standalone local module like `http://localhost:3106`) \u2014 the service serving content, form definitions (`GET /forms`), and form submissions at root-level paths. Precedence: this option > server-pushed `config.dataApiBaseUrl` > derived from the main origin (`${baseUrl}/api/data`)."
653
653
  ),
654
654
  userContext: userContextSchema.optional(),
655
655
  pageContext: pageContextSchema.optional(),
@@ -733,7 +733,7 @@ var connectionConfigPartialSchema = connectionConfigSchema.partial();
733
733
  // src/schema/deployment.ts
734
734
  import { z as z17 } from "zod";
735
735
  var serverConfigSchema = widgetSettingsSchema.partial().omit({ forms: true }).extend({
736
- dataBaseUrl: z17.string().min(1).max(2048).optional()
736
+ dataApiBaseUrl: z17.string().min(1).max(2048).optional()
737
737
  });
738
738
  var siteConfigSchema = z17.object({
739
739
  title: z17.string().min(1).max(120).optional().describe("Brand / site name. Used as the logo's alt-text fallback."),
package/web-component.mjs CHANGED
@@ -510,15 +510,15 @@ function resolveOptions(rawOpts) {
510
510
  const locale = i18n.locale ?? resolveDefaultLocale(i18n.defaultLocale);
511
511
  const availableLocales = i18n.availableLocales ?? [locale];
512
512
  const baseUrl = (opts.baseUrl ?? BRAND.defaultBaseUrl).replace(/\/$/, "");
513
- const agentBaseUrl = (opts.agentBaseUrl ?? `${baseUrl}/api/agent`).replace(/\/$/, "");
514
- const dataBaseUrl = (opts.dataBaseUrl ?? `${baseUrl}/api/data`).replace(/\/$/, "");
513
+ const agentApiBaseUrl = (opts.agentApiBaseUrl ?? `${baseUrl}/api/agent`).replace(/\/$/, "");
514
+ const dataApiBaseUrl = (opts.dataApiBaseUrl ?? `${baseUrl}/api/data`).replace(/\/$/, "");
515
515
  return {
516
516
  widgetId: opts.widgetId ?? "default",
517
517
  publicKey: opts.publicKey,
518
518
  aiAgentDeploymentId: opts.aiAgentDeploymentId,
519
519
  baseUrl,
520
- agentBaseUrl,
521
- dataBaseUrl,
520
+ agentApiBaseUrl,
521
+ dataApiBaseUrl,
522
522
  userContext: sanitizeUserContext(opts.userContext),
523
523
  pageContext: sanitizePageContext(opts.pageContext),
524
524
  position: presentation.position ?? "bottom-right",
@@ -563,7 +563,7 @@ function resolveOptions(rawOpts) {
563
563
  poweredBy: resolvePoweredBy(footer.poweredBy),
564
564
  composer: resolveComposer(opts.composer),
565
565
  modules: resolveModules(opts.modules),
566
- tracking: resolveTracking(opts.tracking, dataBaseUrl),
566
+ tracking: resolveTracking(opts.tracking, dataApiBaseUrl),
567
567
  storage: opts.storage ?? defaultStorage,
568
568
  onMessage: opts.onMessage,
569
569
  onOpen: opts.onOpen,
@@ -596,13 +596,13 @@ function resolveHaptics(overrides) {
596
596
  events: overrides?.events
597
597
  };
598
598
  }
599
- function resolveTracking(overrides, dataBaseUrl) {
599
+ function resolveTracking(overrides, dataApiBaseUrl) {
600
600
  const sampleRate = overrides?.sampleRate ?? DEFAULT_TRACKING.sampleRate;
601
601
  return {
602
602
  enabled: overrides?.enabled ?? DEFAULT_TRACKING.enabled,
603
- // The collector rides the data module — `${dataBaseUrl}/pai/elements-px.gif`
603
+ // The collector rides the data module — `${dataApiBaseUrl}/pai/elements-px.gif`
604
604
  // (e.g. `https://help.ai/api/data/pai/elements-px.gif`).
605
- endpoint: overrides?.endpoint ?? `${dataBaseUrl}/pai/elements-px.gif`,
605
+ endpoint: overrides?.endpoint ?? `${dataApiBaseUrl}/pai/elements-px.gif`,
606
606
  // Clamp instead of reject — a malformed rate must never turn a
607
607
  // disabled tracker on or crash resolve (no Zod on the IIFE path).
608
608
  sampleRate: Math.min(1, Math.max(0, Number.isFinite(sampleRate) ? sampleRate : 1)),
@@ -771,7 +771,8 @@ function mergeServerConfig(user, server) {
771
771
  out[key] = mergeLeaves(sv, uv);
772
772
  }
773
773
  if (server.modules !== void 0 && user.modules === void 0) out.modules = server.modules;
774
- if (server.dataBaseUrl !== void 0 && user.dataBaseUrl === void 0) out.dataBaseUrl = server.dataBaseUrl;
774
+ if (server.dataApiBaseUrl !== void 0 && user.dataApiBaseUrl === void 0)
775
+ out.dataApiBaseUrl = server.dataApiBaseUrl;
775
776
  const userI18n = user.i18n;
776
777
  const serverI18n = server.i18n;
777
778
  if (userI18n || serverI18n) {
@@ -831,8 +832,8 @@ var EMBED_SCALAR_ATTRS = [
831
832
  ["public-key", "publicKey"],
832
833
  ["ai-agent-deployment-id", "aiAgentDeploymentId"],
833
834
  ["base-url", "baseUrl"],
834
- ["agent-base-url", "agentBaseUrl"],
835
- ["data-base-url", "dataBaseUrl"],
835
+ ["agent-api-base-url", "agentApiBaseUrl"],
836
+ ["data-api-base-url", "dataApiBaseUrl"],
836
837
  ["theme", "theme", (v) => v]
837
838
  ];
838
839
  var PRESENTATION_ATTRS = [
@@ -1338,7 +1339,7 @@ var DEFAULT_PATHS = {
1338
1339
  * on the client; a failure just leaves the badge until the next sync.
1339
1340
  */
1340
1341
  markRead: "/pai/mark-read",
1341
- // ── Data API (module-help-ai-data-api, via `dataBaseUrl`) ─────────
1342
+ // ── Data API (module-help-ai-data-api, via `dataApiBaseUrl`) ─────────
1342
1343
  /**
1343
1344
  * The data module's one ACTIVATION read. `GET
1344
1345
  * /pai/bootstrap?visitorId=…&conversationId=…` →
@@ -1435,6 +1436,7 @@ function messageToWireParts(m) {
1435
1436
  var log4 = logger.scope("transport");
1436
1437
  var MAX_RESUME_ATTEMPTS = 3;
1437
1438
  var RESUME_BACKOFF_MS = 400;
1439
+ var CONTENT_CACHE_TTL_MS = 6e4;
1438
1440
  var MAX_REQUEST_RETRIES = 3;
1439
1441
  var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
1440
1442
  function backoffMs(attempt) {
@@ -1473,6 +1475,20 @@ var AgentTransport = class {
1473
1475
  // always sees the visitor's latest context as they navigate.
1474
1476
  __publicField(this, "userContext");
1475
1477
  __publicField(this, "pageContext");
1478
+ // ---- Data API (content / forms — module-help-ai-data-api) --------------
1479
+ //
1480
+ // The DATA surfaces live on `dataApiBaseUrl` (default `${baseUrl}/api/data`)
1481
+ // under the module's `/pai/*` group — same auth headers + envelope, same
1482
+ // retry behavior. One endpoint for every content surface (Home / Help /
1483
+ // News / docs); callers pass filters; a thrown StreamError (e.g. 404 on an
1484
+ // older backend) is treated as "no content" by the module and the tab
1485
+ // degrades gracefully.
1486
+ // Per-query content cache: published content barely changes mid-visit, but
1487
+ // every tab switch remounts its module and re-asks. Entries hold the
1488
+ // REQUEST PROMISE (concurrent identical queries share one network call)
1489
+ // and serve repeats for CONTENT_CACHE_TTL_MS; failures are evicted so the
1490
+ // next ask retries.
1491
+ __publicField(this, "contentCache", /* @__PURE__ */ new Map());
1476
1492
  }
1477
1493
  // ---- Public API -------------------------------------------------------
1478
1494
  /**
@@ -1629,16 +1645,22 @@ var AgentTransport = class {
1629
1645
  log4.debug("markRead failed (non-fatal)", { err });
1630
1646
  }
1631
1647
  }
1632
- // ---- Data API (content / forms module-help-ai-data-api) --------------
1633
- //
1634
- // The DATA surfaces live on `dataBaseUrl` (default `${baseUrl}/api/data`)
1635
- // with ROOT-level paths — same auth headers + envelope, same retry
1636
- // behavior. One endpoint for every content surface (Home / Help / News /
1637
- // docs); callers pass filters; a thrown StreamError (e.g. 404 on an older
1638
- // backend) is treated as "no content" by the module and the tab degrades
1639
- // gracefully.
1640
- /** Fetch content rows by filter. `GET /content` (data-API). */
1641
- async listContent(query = {}) {
1648
+ /** Fetch content rows by filter. `GET /pai/content` (data-API), TTL-cached. */
1649
+ listContent(query = {}) {
1650
+ const key = JSON.stringify(query, Object.keys(query).toSorted());
1651
+ const cached = this.contentCache.get(key);
1652
+ if (cached && Date.now() - cached.at < CONTENT_CACHE_TTL_MS) {
1653
+ log4.debug("listContent \u2192 cache", query);
1654
+ return cached.result;
1655
+ }
1656
+ const result = this.fetchContent(query).catch((err) => {
1657
+ this.contentCache.delete(key);
1658
+ throw err;
1659
+ });
1660
+ this.contentCache.set(key, { at: Date.now(), result });
1661
+ return result;
1662
+ }
1663
+ async fetchContent(query) {
1642
1664
  const url = new URL(this.dataUrl(DEFAULT_PATHS.content));
1643
1665
  for (const [key, value] of Object.entries(query)) {
1644
1666
  if (value === void 0) continue;
@@ -1944,7 +1966,7 @@ var AgentTransport = class {
1944
1966
  */
1945
1967
  url(pathOrUrl) {
1946
1968
  if (/^https?:\/\//i.test(pathOrUrl)) return pathOrUrl;
1947
- return `${this.opts.agentBaseUrl}${pathOrUrl}`;
1969
+ return `${this.opts.agentApiBaseUrl}${pathOrUrl}`;
1948
1970
  }
1949
1971
  /**
1950
1972
  * Data-API variant of {@link url} — resolves a root-level path (content /
@@ -1954,7 +1976,7 @@ var AgentTransport = class {
1954
1976
  */
1955
1977
  dataUrl(pathOrUrl) {
1956
1978
  if (/^https?:\/\//i.test(pathOrUrl)) return pathOrUrl;
1957
- return `${this.opts.dataBaseUrl}${pathOrUrl}`;
1979
+ return `${this.opts.dataApiBaseUrl}${pathOrUrl}`;
1958
1980
  }
1959
1981
  get fetchImpl() {
1960
1982
  return this.opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
@@ -6520,8 +6542,8 @@ function App({ options, hostElement, bus }) {
6520
6542
  const [formContext, setFormContext] = useState13({});
6521
6543
  const [transport] = useState13(
6522
6544
  () => new AgentTransport({
6523
- agentBaseUrl: options.agentBaseUrl,
6524
- dataBaseUrl: options.dataBaseUrl,
6545
+ agentApiBaseUrl: options.agentApiBaseUrl,
6546
+ dataApiBaseUrl: options.dataApiBaseUrl,
6525
6547
  auth: createAuth({
6526
6548
  publicKey: options.publicKey,
6527
6549
  aiAgentDeploymentId: options.aiAgentDeploymentId,