@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/configurator.mjs +15 -13
- package/elements-web-component.esm.js +18 -18
- package/elements-web-component.esm.js.map +3 -3
- package/elements.cjs.js +18 -18
- package/elements.cjs.js.map +3 -3
- package/elements.esm.js +18 -18
- package/elements.esm.js.map +3 -3
- package/elements.js +18 -18
- package/elements.js.map +3 -3
- package/index.d.ts +19 -18
- package/index.mjs +91 -88
- package/package.json +1 -1
- package/schema.d.ts +1 -1
- package/schema.json +15 -15
- package/schema.mjs +15 -13
- package/web-component.mjs +91 -88
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(
|
|
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: `${
|
|
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("/
|
|
589
|
-
transcribe: z16.string().min(1).max(2048).default("/
|
|
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 (`
|
|
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: "/
|
|
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
|
-
|
|
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: `/
|
|
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
|
-
|
|
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.
|
|
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 `/
|
|
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
|
-
|
|
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:
|
|
464
|
+
enabled: true,
|
|
465
465
|
sampleRate: 1
|
|
466
466
|
};
|
|
467
467
|
var DEFAULT_ENDPOINTS = {
|
|
468
|
-
upload: "/
|
|
469
|
-
transcribe: "/
|
|
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
|
|
514
|
-
const
|
|
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
|
-
|
|
521
|
-
|
|
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,
|
|
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,
|
|
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 — `${
|
|
604
|
-
// (e.g. `https://help.ai/api/data/elements
|
|
605
|
-
endpoint: overrides?.endpoint ?? `${
|
|
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.
|
|
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", "
|
|
835
|
-
["data-base-url", "
|
|
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: "/
|
|
1312
|
+
handshake: "/pai/handshake",
|
|
1312
1313
|
/** Send a message + stream the reply (SSE). `POST`. */
|
|
1313
|
-
streamMessage: "/
|
|
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: "/
|
|
1321
|
+
streamResume: "/pai/stream-resume",
|
|
1321
1322
|
/** Abort the in-flight reply stream. `POST`. */
|
|
1322
|
-
cancelStream: "/
|
|
1323
|
+
cancelStream: "/pai/cancel-stream",
|
|
1323
1324
|
/** List the visitor's conversations. `GET`. */
|
|
1324
|
-
listConversations: "/
|
|
1325
|
+
listConversations: "/pai/list-conversations",
|
|
1325
1326
|
/** Load one conversation's thread (messages + canContinue + suggestions). `GET ?conversationId=…`. */
|
|
1326
|
-
listMessages: "/
|
|
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: "/
|
|
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: "/
|
|
1341
|
-
// ── Data API (module-help-ai-data-api, via `
|
|
1342
|
-
/**
|
|
1343
|
-
|
|
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
|
-
*
|
|
1346
|
-
*
|
|
1347
|
-
*
|
|
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
|
-
|
|
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 `
|
|
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
|
-
*
|
|
1657
|
-
*
|
|
1658
|
-
*
|
|
1659
|
-
* (
|
|
1660
|
-
*
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
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
|
|
1681
|
-
const url = new URL(this.dataUrl(DEFAULT_PATHS.
|
|
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("
|
|
1684
|
-
const res = await this.getJson(url.toString(), "
|
|
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("
|
|
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 ?? "/
|
|
1687
|
+
return this.opts.endpoints?.upload ?? "/pai/upload-file";
|
|
1701
1688
|
}
|
|
1702
1689
|
get transcribePath() {
|
|
1703
|
-
return this.opts.endpoints?.transcribe ?? "/
|
|
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 `/
|
|
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 `/
|
|
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.
|
|
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.
|
|
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
|
-
|
|
6538
|
-
|
|
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.
|
|
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("
|
|
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
|
|
6825
|
-
setRemoteForms(resolveForms(res.forms));
|
|
6813
|
+
const res = await dataBootRef.current;
|
|
6814
|
+
setRemoteForms(resolveForms((res ?? { forms: [] }).forms));
|
|
6826
6815
|
} catch (err) {
|
|
6827
|
-
log16.debug("
|
|
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
|
|
7222
|
+
openForm
|
|
7220
7223
|
});
|
|
7221
7224
|
return unsub;
|
|
7222
|
-
}, [hostElement, handleOpen, handleClose, handleExpand, handleFullscreen, handlePopOut,
|
|
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]
|