@helpai/elements 0.26.0 → 0.27.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 +10 -8
- package/elements-web-component.esm.js +18 -18
- package/elements-web-component.esm.js.map +3 -3
- package/elements.cjs.js +23 -23
- package/elements.cjs.js.map +3 -3
- package/elements.esm.js +23 -23
- package/elements.esm.js.map +3 -3
- package/elements.js +18 -18
- package/elements.js.map +3 -3
- package/index.d.ts +10 -9
- package/index.mjs +75 -73
- package/package.json +1 -1
- package/schema.d.ts +1 -1
- package/schema.json +10 -10
- package/schema.mjs +10 -8
- package/web-component.mjs +75 -73
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",
|
|
@@ -600,9 +600,9 @@ function resolveTracking(overrides, dataBaseUrl) {
|
|
|
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
|
|
604
|
-
// (e.g. `https://help.ai/api/data/elements
|
|
605
|
-
endpoint: overrides?.endpoint ?? `${dataBaseUrl}/elements
|
|
603
|
+
// The collector rides the data module — `${dataBaseUrl}/pai/elements-px.gif`
|
|
604
|
+
// (e.g. `https://help.ai/api/data/pai/elements-px.gif`).
|
|
605
|
+
endpoint: overrides?.endpoint ?? `${dataBaseUrl}/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)),
|
|
@@ -1308,63 +1308,61 @@ function parseWireDate(value) {
|
|
|
1308
1308
|
}
|
|
1309
1309
|
var DEFAULT_PATHS = {
|
|
1310
1310
|
/** Conversation bootstrap. `POST` → deployment config + per-visitor state. */
|
|
1311
|
-
handshake: "/
|
|
1311
|
+
handshake: "/pai/handshake",
|
|
1312
1312
|
/** Send a message + stream the reply (SSE). `POST`. */
|
|
1313
|
-
streamMessage: "/
|
|
1313
|
+
streamMessage: "/pai/stream-message",
|
|
1314
1314
|
/**
|
|
1315
1315
|
* Resume an interrupted reply. `GET` (envelope rides as query params).
|
|
1316
1316
|
* Stateless: the server re-streams the whole reply SSE again from the start —
|
|
1317
1317
|
* there is NO `Last-Event-ID`. The client reconciles by id (`seenIds`), so
|
|
1318
1318
|
* replayed events are skipped and only the continuation surfaces.
|
|
1319
1319
|
*/
|
|
1320
|
-
streamResume: "/
|
|
1320
|
+
streamResume: "/pai/stream-resume",
|
|
1321
1321
|
/** Abort the in-flight reply stream. `POST`. */
|
|
1322
|
-
cancelStream: "/
|
|
1322
|
+
cancelStream: "/pai/cancel-stream",
|
|
1323
1323
|
/** List the visitor's conversations. `GET`. */
|
|
1324
|
-
listConversations: "/
|
|
1324
|
+
listConversations: "/pai/list-conversations",
|
|
1325
1325
|
/** Load one conversation's thread (messages + canContinue + suggestions). `GET ?conversationId=…`. */
|
|
1326
|
-
listMessages: "/
|
|
1326
|
+
listMessages: "/pai/list-messages",
|
|
1327
1327
|
/**
|
|
1328
1328
|
* Persist a user-driven settings change. POST `{ visitorId, userPrefs }` →
|
|
1329
1329
|
* server persists, returns the merged authoritative copy. Fire-and-forget on
|
|
1330
1330
|
* the client — a failure leaves the local cache valid; the next
|
|
1331
1331
|
* `handshake()` reconciles.
|
|
1332
1332
|
*/
|
|
1333
|
-
updateSettings: "/
|
|
1333
|
+
updateSettings: "/pai/update-settings",
|
|
1334
1334
|
/**
|
|
1335
1335
|
* Mark a conversation read. POST `{ visitorId, conversationId }` → the server
|
|
1336
1336
|
* records `lastReadAt = now` for that (visitor, conversation) and recomputes
|
|
1337
1337
|
* `unreadCount` (returned on `/list-conversations`) accordingly. Fire-and-forget
|
|
1338
1338
|
* on the client; a failure just leaves the badge until the next sync.
|
|
1339
1339
|
*/
|
|
1340
|
-
markRead: "/
|
|
1340
|
+
markRead: "/pai/mark-read",
|
|
1341
1341
|
// ── Data API (module-help-ai-data-api, via `dataBaseUrl`) ─────────
|
|
1342
|
-
/** All widget content via one filtered endpoint. `GET /content?tags=…`. */
|
|
1343
|
-
content: "/content",
|
|
1344
1342
|
/**
|
|
1345
|
-
*
|
|
1346
|
-
*
|
|
1347
|
-
*
|
|
1348
|
-
*
|
|
1343
|
+
* The data module's one ACTIVATION read. `GET
|
|
1344
|
+
* /pai/bootstrap?visitorId=…&conversationId=…` →
|
|
1345
|
+
* `{ forms, formSubmissions }`: the deployment's resolved form
|
|
1346
|
+
* definitions plus the visitor's recorded submissions for the envelope's
|
|
1347
|
+
* conversation (chronological — they rebuild the timeline markers).
|
|
1348
|
+
* Also re-read with an explicit `conversationId` selector on a
|
|
1349
|
+
* history-pane thread switch. A failure is treated as "no forms, no
|
|
1350
|
+
* records" (non-fatal) — the chat works regardless.
|
|
1351
|
+
*/
|
|
1352
|
+
bootstrap: "/pai/bootstrap",
|
|
1353
|
+
/**
|
|
1354
|
+
* All widget content via one filtered endpoint — content stays OUT of the
|
|
1355
|
+
* bootstrap because its reads are query-driven (`tags` / `categories` /
|
|
1356
|
+
* `ids` / `search` + pagination). `GET /pai/content?tags=…`.
|
|
1349
1357
|
*/
|
|
1350
|
-
|
|
1358
|
+
content: "/pai/content",
|
|
1351
1359
|
/**
|
|
1352
1360
|
* Form submission (the event-driven forms engine). POST `{ visitorId,
|
|
1353
1361
|
* conversationId, formId, trigger, values, skipped? }` → record the form's answers
|
|
1354
1362
|
* immediately, keyed by the same `visitorId` + `conversationId` the chat uses.
|
|
1355
1363
|
* Overridable via `endpoints.submitForm`. Fire-and-forget; 404 is ignored.
|
|
1356
1364
|
*/
|
|
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"
|
|
1365
|
+
submitForm: "/pai/submit-form"
|
|
1368
1366
|
};
|
|
1369
1367
|
var CONTEXT_PARAM = "context";
|
|
1370
1368
|
function buildSendMessageRequest(params) {
|
|
@@ -1653,38 +1651,26 @@ var AgentTransport = class {
|
|
|
1653
1651
|
return { ...res, items };
|
|
1654
1652
|
}
|
|
1655
1653
|
/**
|
|
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).
|
|
1654
|
+
* The data module's ONE activation read. `GET /pai/bootstrap` (data-API)
|
|
1655
|
+
* → the deployment's resolved form definitions + the visitor's recorded
|
|
1656
|
+
* form submissions for the envelope's conversation. The handshake
|
|
1657
|
+
* (agent-API) is completely independent of these surfaces.
|
|
1658
|
+
*
|
|
1659
|
+
* `conversationId` is an optional resource selector (a thread that may
|
|
1660
|
+
* differ from the active conversation — the history-pane switch re-reads
|
|
1661
|
+
* that thread's submissions); when omitted, the envelope's active
|
|
1662
|
+
* conversation applies. Callers treat a thrown StreamError (e.g. 404 on a
|
|
1663
|
+
* deployment that doesn't resolve) as "no forms, no records" (non-fatal).
|
|
1679
1664
|
*/
|
|
1680
|
-
async
|
|
1681
|
-
const url = new URL(this.dataUrl(DEFAULT_PATHS.
|
|
1665
|
+
async bootstrap(conversationId) {
|
|
1666
|
+
const url = new URL(this.dataUrl(DEFAULT_PATHS.bootstrap));
|
|
1682
1667
|
if (conversationId) url.searchParams.set("conversationId", conversationId);
|
|
1683
|
-
log4.debug("
|
|
1684
|
-
const res = await this.getJson(url.toString(), "
|
|
1668
|
+
log4.debug("bootstrap \u2192", { conversationId: conversationId ?? this.conversationId });
|
|
1669
|
+
const res = await this.getJson(url.toString(), "bootstrap");
|
|
1670
|
+
const forms = res.forms ?? [];
|
|
1685
1671
|
const formSubmissions = res.formSubmissions ?? [];
|
|
1686
|
-
log4.debug("
|
|
1687
|
-
return { formSubmissions };
|
|
1672
|
+
log4.debug("bootstrap \u2190", { forms: forms.length, formSubmissions: formSubmissions.length });
|
|
1673
|
+
return { forms, formSubmissions };
|
|
1688
1674
|
}
|
|
1689
1675
|
async saveUserPrefs(userPrefs) {
|
|
1690
1676
|
log4.debug("saveUserPrefs \u2192", {
|
|
@@ -1697,10 +1683,10 @@ var AgentTransport = class {
|
|
|
1697
1683
|
return res;
|
|
1698
1684
|
}
|
|
1699
1685
|
get uploadPath() {
|
|
1700
|
-
return this.opts.endpoints?.upload ?? "/
|
|
1686
|
+
return this.opts.endpoints?.upload ?? "/pai/upload-file";
|
|
1701
1687
|
}
|
|
1702
1688
|
get transcribePath() {
|
|
1703
|
-
return this.opts.endpoints?.transcribe ?? "/
|
|
1689
|
+
return this.opts.endpoints?.transcribe ?? "/pai/transcribe-audio";
|
|
1704
1690
|
}
|
|
1705
1691
|
get submitFormPath() {
|
|
1706
1692
|
return this.opts.endpoints?.submitForm ?? DEFAULT_PATHS.submitForm;
|
|
@@ -1843,7 +1829,7 @@ var AgentTransport = class {
|
|
|
1843
1829
|
}
|
|
1844
1830
|
return false;
|
|
1845
1831
|
}
|
|
1846
|
-
/** Abort + fire-and-forget POST to `/
|
|
1832
|
+
/** Abort + fire-and-forget POST to `/pai/cancel-stream`. Idempotent. */
|
|
1847
1833
|
cancelStream(ctrl, conversationId) {
|
|
1848
1834
|
if (ctrl.signal.aborted) return;
|
|
1849
1835
|
log4.debug("cancel stream", { conversationId, visitorId: this.visitorId });
|
|
@@ -1953,7 +1939,7 @@ var AgentTransport = class {
|
|
|
1953
1939
|
return await res.json();
|
|
1954
1940
|
}
|
|
1955
1941
|
/**
|
|
1956
|
-
* Agent-API URL — resolves a `/
|
|
1942
|
+
* Agent-API URL — resolves a `/pai/*` path against the resolved
|
|
1957
1943
|
* agent module base. Absolute URLs pass through unchanged.
|
|
1958
1944
|
*/
|
|
1959
1945
|
url(pathOrUrl) {
|
|
@@ -6616,10 +6602,11 @@ function App({ options, hostElement, bus }) {
|
|
|
6616
6602
|
},
|
|
6617
6603
|
[messagesSig]
|
|
6618
6604
|
);
|
|
6605
|
+
const dataBootRef = useRef9(null);
|
|
6619
6606
|
const fetchFormMarkers = useCallback6(
|
|
6620
6607
|
async (conversationId) => {
|
|
6621
6608
|
try {
|
|
6622
|
-
const { formSubmissions: subs } = await transport.
|
|
6609
|
+
const { formSubmissions: subs } = await (conversationId ? transport.bootstrap(conversationId) : dataBootRef.current ?? transport.bootstrap());
|
|
6623
6610
|
const lastIndexByForm = /* @__PURE__ */ new Map();
|
|
6624
6611
|
subs.forEach((rec, i) => lastIndexByForm.set(rec.formId, i));
|
|
6625
6612
|
return subs.filter((rec, i) => !rec.skipped || lastIndexByForm.get(rec.formId) === i).map((rec, i) => ({
|
|
@@ -6630,7 +6617,7 @@ function App({ options, hostElement, bus }) {
|
|
|
6630
6617
|
values: rec.values
|
|
6631
6618
|
}));
|
|
6632
6619
|
} catch (err) {
|
|
6633
|
-
log16.debug("
|
|
6620
|
+
log16.debug("bootstrap failed \u2014 no form markers (non-fatal)", { err });
|
|
6634
6621
|
return [];
|
|
6635
6622
|
}
|
|
6636
6623
|
},
|
|
@@ -6819,12 +6806,13 @@ function App({ options, hostElement, bus }) {
|
|
|
6819
6806
|
}, []);
|
|
6820
6807
|
useEffect17(() => {
|
|
6821
6808
|
if (!activated) return;
|
|
6809
|
+
dataBootRef.current = transport.bootstrap();
|
|
6822
6810
|
void (async () => {
|
|
6823
6811
|
try {
|
|
6824
|
-
const res = await
|
|
6825
|
-
setRemoteForms(resolveForms(res.forms));
|
|
6812
|
+
const res = await dataBootRef.current;
|
|
6813
|
+
setRemoteForms(resolveForms((res ?? { forms: [] }).forms));
|
|
6826
6814
|
} catch (err) {
|
|
6827
|
-
log16.debug("
|
|
6815
|
+
log16.debug("bootstrap failed \u2014 treating as no forms", { err });
|
|
6828
6816
|
} finally {
|
|
6829
6817
|
setFormsReady(true);
|
|
6830
6818
|
}
|
|
@@ -7209,6 +7197,20 @@ function App({ options, hostElement, bus }) {
|
|
|
7209
7197
|
},
|
|
7210
7198
|
[transport, messagesSig, conversationIdSig, bus, options, persistence, refreshUnread, fetchFormMarkers]
|
|
7211
7199
|
);
|
|
7200
|
+
const pendingOpenFormRef = useRef9(null);
|
|
7201
|
+
const openForm = useCallback6(
|
|
7202
|
+
(id) => {
|
|
7203
|
+
if (typeof id !== "string") return;
|
|
7204
|
+
if (formsReady) forms.fire("manual", id);
|
|
7205
|
+
else pendingOpenFormRef.current = id;
|
|
7206
|
+
},
|
|
7207
|
+
[formsReady, forms]
|
|
7208
|
+
);
|
|
7209
|
+
useEffect17(() => {
|
|
7210
|
+
if (!formsReady || !pendingOpenFormRef.current) return;
|
|
7211
|
+
forms.fire("manual", pendingOpenFormRef.current);
|
|
7212
|
+
pendingOpenFormRef.current = null;
|
|
7213
|
+
}, [formsReady, forms]);
|
|
7212
7214
|
useEffect17(() => {
|
|
7213
7215
|
const unsub = bindHostCommands(hostElement, {
|
|
7214
7216
|
open: handleOpen,
|
|
@@ -7216,10 +7218,10 @@ function App({ options, hostElement, bus }) {
|
|
|
7216
7218
|
expand: handleExpand,
|
|
7217
7219
|
fullscreen: handleFullscreen,
|
|
7218
7220
|
popout: handlePopOut,
|
|
7219
|
-
openForm
|
|
7221
|
+
openForm
|
|
7220
7222
|
});
|
|
7221
7223
|
return unsub;
|
|
7222
|
-
}, [hostElement, handleOpen, handleClose, handleExpand, handleFullscreen, handlePopOut,
|
|
7224
|
+
}, [hostElement, handleOpen, handleClose, handleExpand, handleFullscreen, handlePopOut, openForm]);
|
|
7223
7225
|
const effectiveOptions = useMemo3(
|
|
7224
7226
|
() => applyOptionOverrides(options, activeLocale, activeThemeMode, activeTextSize),
|
|
7225
7227
|
[options, activeLocale, activeThemeMode, activeTextSize]
|