@helpai/elements 0.26.0 → 0.28.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.mjs CHANGED
@@ -569,9 +569,11 @@ var modulesSchema = z14.array(moduleSchema).max(4).describe(
569
569
  // src/schema/widget/tracking.ts
570
570
  import { z as z15 } from "zod";
571
571
  var trackingSchema = z15.object({
572
- enabled: z15.boolean().optional().describe("Master switch. Default `false` \u2014 no hits are sent until the deployment turns tracking on."),
572
+ enabled: z15.boolean().optional().describe(
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
+ ),
573
575
  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."
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."
575
577
  ),
576
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`."),
577
579
  token: z15.string().max(512).optional().describe(
@@ -585,16 +587,16 @@ var trackingSchema = z15.object({
585
587
 
586
588
  // src/schema/widget.ts
587
589
  var endpointsSchema = z16.object({
588
- upload: z16.string().min(1).max(2048).default("/ai-agent/upload-file"),
589
- transcribe: z16.string().min(1).max(2048).default("/ai-agent/transcribe-audio"),
590
- submitForm: z16.string().min(1).max(2048).default("/submit-form").describe(
590
+ upload: z16.string().min(1).max(2048).default("/pai/upload-file"),
591
+ transcribe: z16.string().min(1).max(2048).default("/pai/transcribe-audio"),
592
+ submitForm: z16.string().min(1).max(2048).default("/pai/submit-form").describe(
591
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."
592
594
  )
593
595
  }).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."
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."
595
597
  ).meta({
596
598
  examples: [
597
- { upload: "/ai-agent/upload-file", transcribe: "/ai-agent/transcribe-audio" },
599
+ { upload: "/pai/upload-file", transcribe: "/pai/transcribe-audio" },
598
600
  { upload: "https://help.ai/uploads/presign" },
599
601
  { submitForm: "https://crm.acme.com/leads" }
600
602
  ]
@@ -643,11 +645,11 @@ var connectionConfigSchema = z16.object({
643
645
  baseUrl: z16.string().min(1).max(2048).optional().describe(
644
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."
645
647
  ),
646
- agentBaseUrl: z16.string().min(1).max(2048).optional().describe(
647
- "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: `/ai-agent/handshake`). Precedence: this option > derived from the main origin (`${baseUrl}/api/agent`). Not server-pushable."
648
+ agentApiBaseUrl: z16.string().min(1).max(2048).optional().describe(
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."
648
650
  ),
649
- dataBaseUrl: z16.string().min(1).max(2048).optional().describe(
650
- "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`)."
651
653
  ),
652
654
  userContext: userContextSchema.optional(),
653
655
  pageContext: pageContextSchema.optional(),
@@ -687,7 +689,7 @@ var widgetSettingsSchema = z16.object({
687
689
  modules: modulesSchema.optional(),
688
690
  tracking: trackingSchema.optional()
689
691
  }).loose().describe(
690
- "Portal-configurable widget settings \u2014 fourteen sections, one per dashboard tab. The backend pushes this same shape on `/ai-agent/handshake` under `config`."
692
+ "Portal-configurable widget settings \u2014 fourteen sections, one per dashboard tab. The backend pushes this same shape on `/pai/handshake` under `config`."
691
693
  ).meta({
692
694
  examples: [
693
695
  { presentation: { mode: "floating", position: "bottom-right" }, theme: "auto" },
@@ -731,7 +733,7 @@ var connectionConfigPartialSchema = connectionConfigSchema.partial();
731
733
  // src/schema/deployment.ts
732
734
  import { z as z17 } from "zod";
733
735
  var serverConfigSchema = widgetSettingsSchema.partial().omit({ forms: true }).extend({
734
- dataBaseUrl: z17.string().min(1).max(2048).optional()
736
+ dataApiBaseUrl: z17.string().min(1).max(2048).optional()
735
737
  });
736
738
  var siteConfigSchema = z17.object({
737
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
@@ -461,13 +461,13 @@ var DEFAULT_FEATURES = {
461
461
  };
462
462
  var DEFAULT_FORMS = { list: [], byTrigger: {} };
463
463
  var DEFAULT_TRACKING = {
464
- enabled: false,
464
+ enabled: true,
465
465
  sampleRate: 1
466
466
  };
467
467
  var DEFAULT_ENDPOINTS = {
468
- upload: "/ai-agent/upload-file",
469
- transcribe: "/ai-agent/transcribe-audio",
470
- submitForm: "/submit-form"
468
+ upload: "/pai/upload-file",
469
+ transcribe: "/pai/transcribe-audio",
470
+ submitForm: "/pai/submit-form"
471
471
  };
472
472
  var DEFAULT_LAUNCHER = {
473
473
  variant: "circle",
@@ -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}/elements/px.gif`
604
- // (e.g. `https://help.ai/api/data/elements/px.gif`).
605
- endpoint: overrides?.endpoint ?? `${dataBaseUrl}/elements/px.gif`,
603
+ // The collector rides the data module — `${dataApiBaseUrl}/pai/elements-px.gif`
604
+ // (e.g. `https://help.ai/api/data/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 = [
@@ -1308,63 +1309,61 @@ function parseWireDate(value) {
1308
1309
  }
1309
1310
  var DEFAULT_PATHS = {
1310
1311
  /** Conversation bootstrap. `POST` → deployment config + per-visitor state. */
1311
- handshake: "/ai-agent/handshake",
1312
+ handshake: "/pai/handshake",
1312
1313
  /** Send a message + stream the reply (SSE). `POST`. */
1313
- streamMessage: "/ai-agent/stream-message",
1314
+ streamMessage: "/pai/stream-message",
1314
1315
  /**
1315
1316
  * Resume an interrupted reply. `GET` (envelope rides as query params).
1316
1317
  * Stateless: the server re-streams the whole reply SSE again from the start —
1317
1318
  * there is NO `Last-Event-ID`. The client reconciles by id (`seenIds`), so
1318
1319
  * replayed events are skipped and only the continuation surfaces.
1319
1320
  */
1320
- streamResume: "/ai-agent/stream-resume",
1321
+ streamResume: "/pai/stream-resume",
1321
1322
  /** Abort the in-flight reply stream. `POST`. */
1322
- cancelStream: "/ai-agent/cancel-stream",
1323
+ cancelStream: "/pai/cancel-stream",
1323
1324
  /** List the visitor's conversations. `GET`. */
1324
- listConversations: "/ai-agent/list-conversations",
1325
+ listConversations: "/pai/list-conversations",
1325
1326
  /** Load one conversation's thread (messages + canContinue + suggestions). `GET ?conversationId=…`. */
1326
- listMessages: "/ai-agent/list-messages",
1327
+ listMessages: "/pai/list-messages",
1327
1328
  /**
1328
1329
  * Persist a user-driven settings change. POST `{ visitorId, userPrefs }` →
1329
1330
  * server persists, returns the merged authoritative copy. Fire-and-forget on
1330
1331
  * the client — a failure leaves the local cache valid; the next
1331
1332
  * `handshake()` reconciles.
1332
1333
  */
1333
- updateSettings: "/ai-agent/update-settings",
1334
+ updateSettings: "/pai/update-settings",
1334
1335
  /**
1335
1336
  * Mark a conversation read. POST `{ visitorId, conversationId }` → the server
1336
1337
  * records `lastReadAt = now` for that (visitor, conversation) and recomputes
1337
1338
  * `unreadCount` (returned on `/list-conversations`) accordingly. Fire-and-forget
1338
1339
  * on the client; a failure just leaves the badge until the next sync.
1339
1340
  */
1340
- markRead: "/ai-agent/mark-read",
1341
- // ── Data API (module-help-ai-data-api, via `dataBaseUrl`) ─────────
1342
- /** All widget content via one filtered endpoint. `GET /content?tags=…`. */
1343
- content: "/content",
1341
+ markRead: "/pai/mark-read",
1342
+ // ── Data API (module-help-ai-data-api, via `dataApiBaseUrl`) ─────────
1343
+ /**
1344
+ * The data module's one ACTIVATION read. `GET
1345
+ * /pai/bootstrap?visitorId=…&conversationId=…` →
1346
+ * `{ forms, formSubmissions }`: the deployment's resolved form
1347
+ * definitions plus the visitor's recorded submissions for the envelope's
1348
+ * conversation (chronological — they rebuild the timeline markers).
1349
+ * Also re-read with an explicit `conversationId` selector on a
1350
+ * history-pane thread switch. A failure is treated as "no forms, no
1351
+ * records" (non-fatal) — the chat works regardless.
1352
+ */
1353
+ bootstrap: "/pai/bootstrap",
1344
1354
  /**
1345
- * Resolved form definitions for the deployment. `GET /forms`
1346
- * `{ forms: FormDef[] }`. Fetched once at boot (in parallel with
1347
- * handshake); form gating waits for it. A failure is treated as
1348
- * "no forms" (non-fatal).
1355
+ * All widget content via one filtered endpoint content stays OUT of the
1356
+ * bootstrap because its reads are query-driven (`tags` / `categories` /
1357
+ * `ids` / `search` + pagination). `GET /pai/content?tags=…`.
1349
1358
  */
1350
- forms: "/forms",
1359
+ content: "/pai/content",
1351
1360
  /**
1352
1361
  * Form submission (the event-driven forms engine). POST `{ visitorId,
1353
1362
  * conversationId, formId, trigger, values, skipped? }` → record the form's answers
1354
1363
  * immediately, keyed by the same `visitorId` + `conversationId` the chat uses.
1355
1364
  * Overridable via `endpoints.submitForm`. Fire-and-forget; 404 is ignored.
1356
1365
  */
1357
- submitForm: "/submit-form",
1358
- /**
1359
- * Visitor interaction records for a conversation. `GET
1360
- * /activity?visitorId=…&conversationId=…` →
1361
- * `{ formSubmissions: FormSubmissionRecord[] }` (chronological). Replaces
1362
- * the former `formSubmissions` echo on the handshake / list-messages;
1363
- * deliberately generic — future visitor interaction records ride the same
1364
- * endpoint. A failure (e.g. 404 `AI_AGENT_PUBLIC_ACCESS_NOT_FOUND` when the
1365
- * deployment doesn't resolve) is treated as "no records" (non-fatal).
1366
- */
1367
- activity: "/activity"
1366
+ submitForm: "/pai/submit-form"
1368
1367
  };
1369
1368
  var CONTEXT_PARAM = "context";
1370
1369
  function buildSendMessageRequest(params) {
@@ -1633,7 +1632,7 @@ var AgentTransport = class {
1633
1632
  }
1634
1633
  // ---- Data API (content / forms — module-help-ai-data-api) --------------
1635
1634
  //
1636
- // The DATA surfaces live on `dataBaseUrl` (default `${baseUrl}/api/data`)
1635
+ // The DATA surfaces live on `dataApiBaseUrl` (default `${baseUrl}/api/data`)
1637
1636
  // with ROOT-level paths — same auth headers + envelope, same retry
1638
1637
  // behavior. One endpoint for every content surface (Home / Help / News /
1639
1638
  // docs); callers pass filters; a thrown StreamError (e.g. 404 on an older
@@ -1653,38 +1652,26 @@ var AgentTransport = class {
1653
1652
  return { ...res, items };
1654
1653
  }
1655
1654
  /**
1656
- * Fetch the deployment's resolved form definitions. `GET /forms`
1657
- * (data-API). Replaces the former `config.forms` push on the handshake
1658
- * called once at boot, in parallel with `handshake`; form gating
1659
- * (pre-chat etc.) waits for it. Callers treat a thrown StreamError as
1660
- * "no forms" (non-fatal).
1661
- */
1662
- async listForms() {
1663
- log4.debug("listForms \u2192");
1664
- const res = await this.getJson(this.dataUrl(DEFAULT_PATHS.forms), "listForms");
1665
- const forms = res.forms ?? [];
1666
- log4.debug("listForms \u2190", { count: forms.length });
1667
- return { forms };
1668
- }
1669
- /**
1670
- * Fetch the visitor's recorded activity for a conversation — currently the
1671
- * form submissions that rebuild the collapsed "form submitted / skipped"
1672
- * timeline markers on resume. `GET /activity` (data-API); the
1673
- * envelope carries `visitorId` / `conversationId` as query params.
1674
- * `conversationId` here is an optional resource selector (a thread that may
1675
- * differ from the active conversation — the history-pane switch); when
1676
- * omitted, the envelope's active conversation applies. Callers treat a
1677
- * thrown StreamError (e.g. 404 on a deployment that doesn't resolve) as
1678
- * "no activity" (non-fatal).
1655
+ * The data module's ONE activation read. `GET /pai/bootstrap` (data-API)
1656
+ * the deployment's resolved form definitions + the visitor's recorded
1657
+ * form submissions for the envelope's conversation. The handshake
1658
+ * (agent-API) is completely independent of these surfaces.
1659
+ *
1660
+ * `conversationId` is an optional resource selector (a thread that may
1661
+ * differ from the active conversation — the history-pane switch re-reads
1662
+ * that thread's submissions); when omitted, the envelope's active
1663
+ * conversation applies. Callers treat a thrown StreamError (e.g. 404 on a
1664
+ * deployment that doesn't resolve) as "no forms, no records" (non-fatal).
1679
1665
  */
1680
- async getActivity(conversationId) {
1681
- const url = new URL(this.dataUrl(DEFAULT_PATHS.activity));
1666
+ async bootstrap(conversationId) {
1667
+ const url = new URL(this.dataUrl(DEFAULT_PATHS.bootstrap));
1682
1668
  if (conversationId) url.searchParams.set("conversationId", conversationId);
1683
- log4.debug("getActivity \u2192", { conversationId: conversationId ?? this.conversationId });
1684
- const res = await this.getJson(url.toString(), "getActivity");
1669
+ log4.debug("bootstrap \u2192", { conversationId: conversationId ?? this.conversationId });
1670
+ const res = await this.getJson(url.toString(), "bootstrap");
1671
+ const forms = res.forms ?? [];
1685
1672
  const formSubmissions = res.formSubmissions ?? [];
1686
- log4.debug("getActivity \u2190", { count: formSubmissions.length });
1687
- return { formSubmissions };
1673
+ log4.debug("bootstrap \u2190", { forms: forms.length, formSubmissions: formSubmissions.length });
1674
+ return { forms, formSubmissions };
1688
1675
  }
1689
1676
  async saveUserPrefs(userPrefs) {
1690
1677
  log4.debug("saveUserPrefs \u2192", {
@@ -1697,10 +1684,10 @@ var AgentTransport = class {
1697
1684
  return res;
1698
1685
  }
1699
1686
  get uploadPath() {
1700
- return this.opts.endpoints?.upload ?? "/ai-agent/upload-file";
1687
+ return this.opts.endpoints?.upload ?? "/pai/upload-file";
1701
1688
  }
1702
1689
  get transcribePath() {
1703
- return this.opts.endpoints?.transcribe ?? "/ai-agent/transcribe-audio";
1690
+ return this.opts.endpoints?.transcribe ?? "/pai/transcribe-audio";
1704
1691
  }
1705
1692
  get submitFormPath() {
1706
1693
  return this.opts.endpoints?.submitForm ?? DEFAULT_PATHS.submitForm;
@@ -1843,7 +1830,7 @@ var AgentTransport = class {
1843
1830
  }
1844
1831
  return false;
1845
1832
  }
1846
- /** Abort + fire-and-forget POST to `/ai-agent/cancel-stream`. Idempotent. */
1833
+ /** Abort + fire-and-forget POST to `/pai/cancel-stream`. Idempotent. */
1847
1834
  cancelStream(ctrl, conversationId) {
1848
1835
  if (ctrl.signal.aborted) return;
1849
1836
  log4.debug("cancel stream", { conversationId, visitorId: this.visitorId });
@@ -1953,12 +1940,12 @@ var AgentTransport = class {
1953
1940
  return await res.json();
1954
1941
  }
1955
1942
  /**
1956
- * Agent-API URL — resolves a `/ai-agent/*` path against the resolved
1943
+ * Agent-API URL — resolves a `/pai/*` path against the resolved
1957
1944
  * agent module base. Absolute URLs pass through unchanged.
1958
1945
  */
1959
1946
  url(pathOrUrl) {
1960
1947
  if (/^https?:\/\//i.test(pathOrUrl)) return pathOrUrl;
1961
- return `${this.opts.agentBaseUrl}${pathOrUrl}`;
1948
+ return `${this.opts.agentApiBaseUrl}${pathOrUrl}`;
1962
1949
  }
1963
1950
  /**
1964
1951
  * Data-API variant of {@link url} — resolves a root-level path (content /
@@ -1968,7 +1955,7 @@ var AgentTransport = class {
1968
1955
  */
1969
1956
  dataUrl(pathOrUrl) {
1970
1957
  if (/^https?:\/\//i.test(pathOrUrl)) return pathOrUrl;
1971
- return `${this.opts.dataBaseUrl}${pathOrUrl}`;
1958
+ return `${this.opts.dataApiBaseUrl}${pathOrUrl}`;
1972
1959
  }
1973
1960
  get fetchImpl() {
1974
1961
  return this.opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
@@ -6534,8 +6521,8 @@ function App({ options, hostElement, bus }) {
6534
6521
  const [formContext, setFormContext] = useState13({});
6535
6522
  const [transport] = useState13(
6536
6523
  () => new AgentTransport({
6537
- agentBaseUrl: options.agentBaseUrl,
6538
- dataBaseUrl: options.dataBaseUrl,
6524
+ agentApiBaseUrl: options.agentApiBaseUrl,
6525
+ dataApiBaseUrl: options.dataApiBaseUrl,
6539
6526
  auth: createAuth({
6540
6527
  publicKey: options.publicKey,
6541
6528
  aiAgentDeploymentId: options.aiAgentDeploymentId,
@@ -6616,10 +6603,11 @@ function App({ options, hostElement, bus }) {
6616
6603
  },
6617
6604
  [messagesSig]
6618
6605
  );
6606
+ const dataBootRef = useRef9(null);
6619
6607
  const fetchFormMarkers = useCallback6(
6620
6608
  async (conversationId) => {
6621
6609
  try {
6622
- const { formSubmissions: subs } = await transport.getActivity(conversationId);
6610
+ const { formSubmissions: subs } = await (conversationId ? transport.bootstrap(conversationId) : dataBootRef.current ?? transport.bootstrap());
6623
6611
  const lastIndexByForm = /* @__PURE__ */ new Map();
6624
6612
  subs.forEach((rec, i) => lastIndexByForm.set(rec.formId, i));
6625
6613
  return subs.filter((rec, i) => !rec.skipped || lastIndexByForm.get(rec.formId) === i).map((rec, i) => ({
@@ -6630,7 +6618,7 @@ function App({ options, hostElement, bus }) {
6630
6618
  values: rec.values
6631
6619
  }));
6632
6620
  } catch (err) {
6633
- log16.debug("getActivity failed \u2014 no form markers (non-fatal)", { err });
6621
+ log16.debug("bootstrap failed \u2014 no form markers (non-fatal)", { err });
6634
6622
  return [];
6635
6623
  }
6636
6624
  },
@@ -6819,12 +6807,13 @@ function App({ options, hostElement, bus }) {
6819
6807
  }, []);
6820
6808
  useEffect17(() => {
6821
6809
  if (!activated) return;
6810
+ dataBootRef.current = transport.bootstrap();
6822
6811
  void (async () => {
6823
6812
  try {
6824
- const res = await transport.listForms();
6825
- setRemoteForms(resolveForms(res.forms));
6813
+ const res = await dataBootRef.current;
6814
+ setRemoteForms(resolveForms((res ?? { forms: [] }).forms));
6826
6815
  } catch (err) {
6827
- log16.debug("listForms failed \u2014 treating as no forms", { err });
6816
+ log16.debug("bootstrap failed \u2014 treating as no forms", { err });
6828
6817
  } finally {
6829
6818
  setFormsReady(true);
6830
6819
  }
@@ -7209,6 +7198,20 @@ function App({ options, hostElement, bus }) {
7209
7198
  },
7210
7199
  [transport, messagesSig, conversationIdSig, bus, options, persistence, refreshUnread, fetchFormMarkers]
7211
7200
  );
7201
+ const pendingOpenFormRef = useRef9(null);
7202
+ const openForm = useCallback6(
7203
+ (id) => {
7204
+ if (typeof id !== "string") return;
7205
+ if (formsReady) forms.fire("manual", id);
7206
+ else pendingOpenFormRef.current = id;
7207
+ },
7208
+ [formsReady, forms]
7209
+ );
7210
+ useEffect17(() => {
7211
+ if (!formsReady || !pendingOpenFormRef.current) return;
7212
+ forms.fire("manual", pendingOpenFormRef.current);
7213
+ pendingOpenFormRef.current = null;
7214
+ }, [formsReady, forms]);
7212
7215
  useEffect17(() => {
7213
7216
  const unsub = bindHostCommands(hostElement, {
7214
7217
  open: handleOpen,
@@ -7216,10 +7219,10 @@ function App({ options, hostElement, bus }) {
7216
7219
  expand: handleExpand,
7217
7220
  fullscreen: handleFullscreen,
7218
7221
  popout: handlePopOut,
7219
- openForm: (id) => forms.fire("manual", typeof id === "string" ? id : void 0)
7222
+ openForm
7220
7223
  });
7221
7224
  return unsub;
7222
- }, [hostElement, handleOpen, handleClose, handleExpand, handleFullscreen, handlePopOut, forms]);
7225
+ }, [hostElement, handleOpen, handleClose, handleExpand, handleFullscreen, handlePopOut, openForm]);
7223
7226
  const effectiveOptions = useMemo3(
7224
7227
  () => applyOptionOverrides(options, activeLocale, activeThemeMode, activeTextSize),
7225
7228
  [options, activeLocale, activeThemeMode, activeTextSize]