@syntrologie/adapt-chatbot 2.53.0 → 2.55.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/dist/AdaptiveChatBar.d.ts +15 -3
- package/dist/AdaptiveChatBar.d.ts.map +1 -1
- package/dist/AdaptiveChatBar.js +2 -2
- package/dist/AdaptiveChatBarMountable.d.ts.map +1 -1
- package/dist/AdaptiveChatTrail.d.ts +15 -0
- package/dist/AdaptiveChatTrail.d.ts.map +1 -1
- package/dist/AdaptiveChatTrail.follow-bottom.test.d.ts +2 -0
- package/dist/AdaptiveChatTrail.follow-bottom.test.d.ts.map +1 -0
- package/dist/AdaptiveChatTrail.js +1 -1
- package/dist/AdaptiveChipsStrip.js +2 -2
- package/dist/ChatAssistantLit.js +2 -2
- package/dist/ChatSession.d.ts +14 -1
- package/dist/ChatSession.d.ts.map +1 -1
- package/dist/ChatTransport.d.ts +27 -0
- package/dist/ChatTransport.d.ts.map +1 -1
- package/dist/LeaveAMessageLit.d.ts +37 -6
- package/dist/LeaveAMessageLit.d.ts.map +1 -1
- package/dist/{chunk-IALBNWHO.js → chunk-57MPLQOL.js} +2 -2
- package/dist/{chunk-A36JVPEF.js → chunk-ETDHHQS3.js} +2 -2
- package/dist/{chunk-M7FVDW3M.js → chunk-LJEPZI6Y.js} +18 -2
- package/dist/chunk-LJEPZI6Y.js.map +7 -0
- package/dist/{chunk-2OS5KLNX.js → chunk-NGV35N4T.js} +99 -1
- package/dist/{chunk-2OS5KLNX.js.map → chunk-NGV35N4T.js.map} +4 -4
- package/dist/{chunk-74CVNFXS.js → chunk-VFWA5W56.js} +21 -28
- package/dist/{chunk-74CVNFXS.js.map → chunk-VFWA5W56.js.map} +2 -2
- package/dist/findSmartCanvas.d.ts +19 -0
- package/dist/findSmartCanvas.d.ts.map +1 -0
- package/dist/runtime.js +758 -465
- package/dist/runtime.js.map +4 -4
- package/dist/schema.d.ts +72 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +19 -2
- package/dist/schema.js.map +2 -2
- package/dist/support-chat/boot.d.ts +35 -3
- package/dist/support-chat/boot.d.ts.map +1 -1
- package/dist/support-chat/inbox.d.ts +57 -0
- package/dist/support-chat/inbox.d.ts.map +1 -0
- package/dist/support-chat/inbox.test.d.ts +2 -0
- package/dist/support-chat/inbox.test.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-M7FVDW3M.js.map +0 -7
- /package/dist/{chunk-IALBNWHO.js.map → chunk-57MPLQOL.js.map} +0 -0
- /package/dist/{chunk-A36JVPEF.js.map → chunk-ETDHHQS3.js.map} +0 -0
package/dist/runtime.js
CHANGED
|
@@ -9,24 +9,25 @@ import {
|
|
|
9
9
|
decodeMutationEnvelope,
|
|
10
10
|
fetchMountedElements,
|
|
11
11
|
renderFallbackHtml
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-57MPLQOL.js";
|
|
13
13
|
import {
|
|
14
14
|
pickPersona
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-VFWA5W56.js";
|
|
16
16
|
import {
|
|
17
17
|
getElementInstanceStore,
|
|
18
18
|
setElementInstanceStore
|
|
19
19
|
} from "./chunk-EWPPVPJ4.js";
|
|
20
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-LJEPZI6Y.js";
|
|
21
21
|
import "./chunk-TZA7572J.js";
|
|
22
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-ETDHHQS3.js";
|
|
23
23
|
import {
|
|
24
24
|
DIVE_DEEPER_EVENT,
|
|
25
|
+
TELEMETRY_DOM_EVENT,
|
|
25
26
|
TILE_MOUNTED_EVENT,
|
|
26
27
|
TILE_UNMOUNTED_EVENT,
|
|
27
28
|
isCanonicalBusEvent,
|
|
28
29
|
stripMountPlumbing
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-NGV35N4T.js";
|
|
30
31
|
import "./chunk-UVKRO5ER.js";
|
|
31
32
|
|
|
32
33
|
// src/ChatSession.ts
|
|
@@ -41,6 +42,7 @@ var ChatSession = class {
|
|
|
41
42
|
this._runActive = false;
|
|
42
43
|
this._thinkingText = "";
|
|
43
44
|
this._nextId = 1;
|
|
45
|
+
this._turn = 0;
|
|
44
46
|
this.subscribers = /* @__PURE__ */ new Set();
|
|
45
47
|
this.sendListeners = /* @__PURE__ */ new Set();
|
|
46
48
|
this.interruptListeners = /* @__PURE__ */ new Set();
|
|
@@ -51,9 +53,14 @@ var ChatSession = class {
|
|
|
51
53
|
return {
|
|
52
54
|
messages: [...this._messages],
|
|
53
55
|
inFlight: this._inFlight || this._runActive,
|
|
54
|
-
thinkingText: this._thinkingText
|
|
56
|
+
thinkingText: this._thinkingText,
|
|
57
|
+
turn: this._turn
|
|
55
58
|
};
|
|
56
59
|
}
|
|
60
|
+
/** User sends so far in this session; the single turn counter. */
|
|
61
|
+
get turn() {
|
|
62
|
+
return this._turn;
|
|
63
|
+
}
|
|
57
64
|
/**
|
|
58
65
|
* Append a delta to the active thinking turn's narration buffer.
|
|
59
66
|
* Forwarded from the AG-UI THINKING_TEXT_MESSAGE_CONTENT event so
|
|
@@ -116,8 +123,9 @@ var ChatSession = class {
|
|
|
116
123
|
const id = generateUserMessageId();
|
|
117
124
|
this._messages.push({ id, role: "user", text: trimmed });
|
|
118
125
|
this._inFlight = true;
|
|
126
|
+
this._turn += 1;
|
|
119
127
|
this.notify();
|
|
120
|
-
const event = { text: trimmed, id };
|
|
128
|
+
const event = { text: trimmed, id, turn: this._turn, via: opts.via };
|
|
121
129
|
if (opts?.activeLidSlot) event.activeLidSlot = opts.activeLidSlot;
|
|
122
130
|
if (opts?.forwardedProps) event.forwardedProps = opts.forwardedProps;
|
|
123
131
|
for (const listener of this.sendListeners) listener(event);
|
|
@@ -217,6 +225,7 @@ var ChatSession = class {
|
|
|
217
225
|
this._runActive = false;
|
|
218
226
|
this._thinkingText = "";
|
|
219
227
|
this._nextId = 1;
|
|
228
|
+
this._turn = 0;
|
|
220
229
|
this.notify();
|
|
221
230
|
}
|
|
222
231
|
/** Register a transport's send listener. Returns unsubscribe. */
|
|
@@ -456,7 +465,7 @@ function mergeForwardedProps(prev, next) {
|
|
|
456
465
|
return { ...next, forwardedProps: prev.forwardedProps };
|
|
457
466
|
}
|
|
458
467
|
var ChatTransport = class {
|
|
459
|
-
constructor() {
|
|
468
|
+
constructor(opts = {}) {
|
|
460
469
|
this._config = null;
|
|
461
470
|
this._status = "idle";
|
|
462
471
|
this._agui = null;
|
|
@@ -525,6 +534,26 @@ var ChatTransport = class {
|
|
|
525
534
|
*/
|
|
526
535
|
this._transportId = `tx_${Math.random().toString(36).slice(2, 8)}`;
|
|
527
536
|
this._configuredAt = 0;
|
|
537
|
+
/** Per-run telemetry state (FEAT-1789150184). The chat session owns the turn; this mirrors it per run. */
|
|
538
|
+
this._runTurn = 0;
|
|
539
|
+
this._runStartedAt = 0;
|
|
540
|
+
this._firstDeltaAt = null;
|
|
541
|
+
this._runMounts = 0;
|
|
542
|
+
this._runCards = 0;
|
|
543
|
+
/**
|
|
544
|
+
* Count of RUN_FINISHED events the server still owes us that must be
|
|
545
|
+
* swallowed, not reported — one per interrupt. NOT a boolean: an
|
|
546
|
+
* interrupt latches this debt against whichever run's RUN_FINISHED
|
|
547
|
+
* arrives next, and a visitor can interrupt then immediately send again
|
|
548
|
+
* before the first run's late RUN_FINISHED shows up. Clearing this in
|
|
549
|
+
* `onSend` (as a boolean would need to, to arm for the NEXT run) would
|
|
550
|
+
* let that late RUN_FINISHED slip through and get misattributed to the
|
|
551
|
+
* new run — a phantom `reply_received` at turn N+1 with near-zero
|
|
552
|
+
* ttft/total. The counter survives across sends and is only decremented
|
|
553
|
+
* by an actual RUN_FINISHED, so debt from run N is always paid by run
|
|
554
|
+
* N's own (possibly late) finish, never by run N+1's.
|
|
555
|
+
*/
|
|
556
|
+
this._pendingSuppressedFinishes = 0;
|
|
528
557
|
// -------------------------------------------------------------------------
|
|
529
558
|
// Session stream — server-initiated events (proactive nudges)
|
|
530
559
|
// -------------------------------------------------------------------------
|
|
@@ -533,6 +562,18 @@ var ChatTransport = class {
|
|
|
533
562
|
// (read-deadline constant lives at module scope: SESSION_READ_DEADLINE_MS)
|
|
534
563
|
/** Capped reconnect backoff for the session stream. */
|
|
535
564
|
this._sessionStreamBackoffMs = 2e3;
|
|
565
|
+
this._now = opts.now ?? (() => Date.now());
|
|
566
|
+
}
|
|
567
|
+
/** The transport is not an element: atoms go straight to document, where TelemetryCore listens. */
|
|
568
|
+
_reportAtom(name, props) {
|
|
569
|
+
if (typeof document === "undefined") return;
|
|
570
|
+
document.dispatchEvent(
|
|
571
|
+
new CustomEvent(TELEMETRY_DOM_EVENT, {
|
|
572
|
+
detail: { name, props },
|
|
573
|
+
bubbles: true,
|
|
574
|
+
composed: true
|
|
575
|
+
})
|
|
576
|
+
);
|
|
536
577
|
}
|
|
537
578
|
/**
|
|
538
579
|
* Configure the transport. Idempotent — calling again with the same
|
|
@@ -677,6 +718,12 @@ var ChatTransport = class {
|
|
|
677
718
|
this._fallbackListeners.clear();
|
|
678
719
|
this._dispatchedNudgeIntentIds.clear();
|
|
679
720
|
this.stopSessionStream();
|
|
721
|
+
this._runTurn = 0;
|
|
722
|
+
this._runStartedAt = 0;
|
|
723
|
+
this._firstDeltaAt = null;
|
|
724
|
+
this._runMounts = 0;
|
|
725
|
+
this._runCards = 0;
|
|
726
|
+
this._pendingSuppressedFinishes = 0;
|
|
680
727
|
}
|
|
681
728
|
/**
|
|
682
729
|
* Open the persistent `/api/adaptive/session` SSE and feed its frames
|
|
@@ -931,13 +978,28 @@ var ChatTransport = class {
|
|
|
931
978
|
return { ...base ?? {}, ...pending };
|
|
932
979
|
}
|
|
933
980
|
_wireSession() {
|
|
934
|
-
this._sessionUnsubSend = chatSession.onSend(
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
981
|
+
this._sessionUnsubSend = chatSession.onSend(
|
|
982
|
+
({ text, id, turn, via, activeLidSlot, forwardedProps }) => {
|
|
983
|
+
this._pendingLidSlot = typeof activeLidSlot === "string" && activeLidSlot.length > 0 ? activeLidSlot : null;
|
|
984
|
+
this._pendingForwardedProps = forwardedProps ?? null;
|
|
985
|
+
this._runTurn = turn;
|
|
986
|
+
this._runStartedAt = this._now();
|
|
987
|
+
this._firstDeltaAt = null;
|
|
988
|
+
this._runMounts = 0;
|
|
989
|
+
this._runCards = 0;
|
|
990
|
+
this._reportAtom("syntro_chat_message_sent", { turn, chars: text.length, via });
|
|
991
|
+
void this._forwardUserMessage(text, id);
|
|
992
|
+
}
|
|
993
|
+
);
|
|
939
994
|
this._sessionUnsubInterrupt = chatSession.onInterrupt(() => {
|
|
940
995
|
this._agui?.send({ type: "stop-generation" });
|
|
996
|
+
if (this._runStartedAt) {
|
|
997
|
+
this._pendingSuppressedFinishes += 1;
|
|
998
|
+
this._reportAtom("syntro_chat_interrupted", {
|
|
999
|
+
turn: this._runTurn,
|
|
1000
|
+
after_ms: this._now() - this._runStartedAt
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
941
1003
|
});
|
|
942
1004
|
this._sessionUnsubToolResult = chatSession.onToolResult(({ toolCallId, result, approved }) => {
|
|
943
1005
|
this._agui?.send({ type: "tool-result", toolCallId, result, approved });
|
|
@@ -1181,6 +1243,7 @@ var ChatTransport = class {
|
|
|
1181
1243
|
return;
|
|
1182
1244
|
}
|
|
1183
1245
|
case "message-delta": {
|
|
1246
|
+
if (this._firstDeltaAt === null) this._firstDeltaAt = this._now();
|
|
1184
1247
|
const id = event.messageId ?? this._currentAssistantMessageId;
|
|
1185
1248
|
if (!id || !event.delta) return;
|
|
1186
1249
|
chatSession.receiveDelta(id, event.delta);
|
|
@@ -1243,6 +1306,10 @@ var ChatTransport = class {
|
|
|
1243
1306
|
}
|
|
1244
1307
|
case "tool-call": {
|
|
1245
1308
|
const existing = this._findToolCallStatus(event.toolCall.id);
|
|
1309
|
+
if (!existing && event.toolCall.name.startsWith("mount_")) {
|
|
1310
|
+
this._runMounts += 1;
|
|
1311
|
+
if (/card/i.test(event.toolCall.name)) this._runCards += 1;
|
|
1312
|
+
}
|
|
1246
1313
|
if (!existing) {
|
|
1247
1314
|
this._startThinkingCycle(toolNarrationVariants(event.toolCall.name));
|
|
1248
1315
|
}
|
|
@@ -1309,21 +1376,42 @@ var ChatTransport = class {
|
|
|
1309
1376
|
return;
|
|
1310
1377
|
}
|
|
1311
1378
|
// ── Deliberate no-ops, made EXPLICIT so the exhaustiveness check below
|
|
1312
|
-
// can do its job. All
|
|
1379
|
+
// can do its job. All three were already falling through to nothing; the
|
|
1313
1380
|
// `never` guard is what surfaced that they were never written down.
|
|
1314
1381
|
//
|
|
1315
|
-
//
|
|
1382
|
+
// These tool-call events belong to a deferred-tool approval flow
|
|
1316
1383
|
// that exists on <syntro-chat> (the editor / assistant surface), not
|
|
1317
1384
|
// here: the ambient visitor trail renders no approve/deny affordance, so
|
|
1318
1385
|
// there is nothing for an interrupt, a cross-run result, or a
|
|
1319
|
-
// cancellation to update.
|
|
1320
|
-
// messages the run left streaming in <syntro-chat>; this surface tracks
|
|
1321
|
-
// run liveness through `typing`, which the transport emits alongside it.
|
|
1386
|
+
// cancellation to update.
|
|
1322
1387
|
case "tool-call-deferred":
|
|
1323
1388
|
case "tool-call-result":
|
|
1324
1389
|
case "tool-call-cancelled":
|
|
1325
|
-
case "run-finished":
|
|
1326
1390
|
return;
|
|
1391
|
+
// `run-finished` (BUG-1787292410) settles messages the run left
|
|
1392
|
+
// streaming in <syntro-chat>; this surface tracks run liveness through
|
|
1393
|
+
// `typing`, which the transport emits alongside it. It's also the one
|
|
1394
|
+
// telemetry-relevant signal: the reply_received atom fires here,
|
|
1395
|
+
// unless this run was interrupted — the server still emits
|
|
1396
|
+
// RUN_FINISHED after stop-generation, and that must not double-report.
|
|
1397
|
+
case "run-finished": {
|
|
1398
|
+
if (this._pendingSuppressedFinishes > 0) {
|
|
1399
|
+
this._pendingSuppressedFinishes -= 1;
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
if (this._runStartedAt) {
|
|
1403
|
+
const end = this._now();
|
|
1404
|
+
this._reportAtom("syntro_chat_reply_received", {
|
|
1405
|
+
turn: this._runTurn,
|
|
1406
|
+
ttft_ms: (this._firstDeltaAt ?? end) - this._runStartedAt,
|
|
1407
|
+
total_ms: end - this._runStartedAt,
|
|
1408
|
+
mounts: this._runMounts,
|
|
1409
|
+
cards: this._runCards
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
this._runStartedAt = 0;
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1327
1415
|
default: {
|
|
1328
1416
|
const _exhaustive = event;
|
|
1329
1417
|
void _exhaustive;
|
|
@@ -1332,7 +1420,25 @@ var ChatTransport = class {
|
|
|
1332
1420
|
}
|
|
1333
1421
|
}
|
|
1334
1422
|
};
|
|
1335
|
-
var chatTransport = new ChatTransport();
|
|
1423
|
+
var chatTransport = new ChatTransport({ now: () => Date.now() });
|
|
1424
|
+
|
|
1425
|
+
// src/findSmartCanvas.ts
|
|
1426
|
+
function findSmartCanvas() {
|
|
1427
|
+
if (typeof document === "undefined") return null;
|
|
1428
|
+
const find = (root) => {
|
|
1429
|
+
const direct = root.querySelector("smart-canvas");
|
|
1430
|
+
if (direct) return direct;
|
|
1431
|
+
for (const el of root.querySelectorAll("*")) {
|
|
1432
|
+
const sr = el.shadowRoot;
|
|
1433
|
+
if (sr) {
|
|
1434
|
+
const found = find(sr);
|
|
1435
|
+
if (found) return found;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
return null;
|
|
1439
|
+
};
|
|
1440
|
+
return find(document);
|
|
1441
|
+
}
|
|
1336
1442
|
|
|
1337
1443
|
// src/observer/allowlist.ts
|
|
1338
1444
|
var MAX_TEXT_LEN = 200;
|
|
@@ -2113,6 +2219,47 @@ function registerLeadConnectorBridge() {
|
|
|
2113
2219
|
registerLeadConnectorBridge();
|
|
2114
2220
|
|
|
2115
2221
|
// src/support-chat/boot.ts
|
|
2222
|
+
var SUPPORT_CHAT_WATCH_TICK_MS = 500;
|
|
2223
|
+
var SUPPORT_CHAT_WATCH_MAX_MS = 30 * 60 * 1e3;
|
|
2224
|
+
var watches = /* @__PURE__ */ new Map();
|
|
2225
|
+
function settleWatch(bridge, watch) {
|
|
2226
|
+
clearInterval(watch.timer);
|
|
2227
|
+
watches.delete(bridge);
|
|
2228
|
+
if (watch.suppressNative) bridge.hideNative();
|
|
2229
|
+
for (const cb of watch.listeners) cb();
|
|
2230
|
+
watch.listeners.clear();
|
|
2231
|
+
}
|
|
2232
|
+
function ensureWatch(bridge, suppressNative) {
|
|
2233
|
+
const existing = watches.get(bridge);
|
|
2234
|
+
if (existing) {
|
|
2235
|
+
existing.suppressNative = existing.suppressNative || suppressNative;
|
|
2236
|
+
return existing;
|
|
2237
|
+
}
|
|
2238
|
+
console.info(
|
|
2239
|
+
`supportChat.bridge: vendor '${bridge.vendor}' is not on the page yet \u2014 watching for it (delayed-script hosts load it on the first interaction)`
|
|
2240
|
+
);
|
|
2241
|
+
const watch = {
|
|
2242
|
+
startedAt: Date.now(),
|
|
2243
|
+
suppressNative,
|
|
2244
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
2245
|
+
timer: setInterval(() => {
|
|
2246
|
+
if (bridge.detect()) {
|
|
2247
|
+
settleWatch(bridge, watch);
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
if (Date.now() - watch.startedAt >= SUPPORT_CHAT_WATCH_MAX_MS) {
|
|
2251
|
+
clearInterval(watch.timer);
|
|
2252
|
+
watches.delete(bridge);
|
|
2253
|
+
watch.listeners.clear();
|
|
2254
|
+
console.warn(
|
|
2255
|
+
`supportChat.bridge: vendor '${bridge.vendor}' never appeared within ${SUPPORT_CHAT_WATCH_MAX_MS / 6e4} minutes \u2014 handoff disabled`
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
}, SUPPORT_CHAT_WATCH_TICK_MS)
|
|
2259
|
+
};
|
|
2260
|
+
watches.set(bridge, watch);
|
|
2261
|
+
return watch;
|
|
2262
|
+
}
|
|
2116
2263
|
function bootSupportChat(cfg2) {
|
|
2117
2264
|
if (!cfg2?.vendor) return void 0;
|
|
2118
2265
|
registerCrispBridge();
|
|
@@ -2127,442 +2274,29 @@ function bootSupportChat(cfg2) {
|
|
|
2127
2274
|
leadConnectorBridge.configure({ widgetId: cfg2.widgetId });
|
|
2128
2275
|
}
|
|
2129
2276
|
const bridge = getSupportChatBridge(cfg2.vendor);
|
|
2130
|
-
if (!bridge
|
|
2131
|
-
console.warn(
|
|
2132
|
-
`supportChat.bridge: vendor '${cfg2.vendor}' not detected on this page \u2014 handoff disabled`
|
|
2133
|
-
);
|
|
2277
|
+
if (!bridge) {
|
|
2278
|
+
console.warn(`supportChat.bridge: no bridge for vendor '${cfg2.vendor}' \u2014 handoff disabled`);
|
|
2134
2279
|
return void 0;
|
|
2135
2280
|
}
|
|
2136
|
-
if (
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
// src/AdaptiveChatBarMountable.ts
|
|
2141
|
-
var STATE_KEY = "__syntroChatBarMount";
|
|
2142
|
-
function getState(container) {
|
|
2143
|
-
return container[STATE_KEY] ?? null;
|
|
2144
|
-
}
|
|
2145
|
-
function setState(container, state) {
|
|
2146
|
-
container[STATE_KEY] = state;
|
|
2147
|
-
}
|
|
2148
|
-
function applyPlaceholder(bar, cfg2) {
|
|
2149
|
-
if (cfg2.placeholder !== void 0) bar.placeholder = cfg2.placeholder;
|
|
2150
|
-
if (cfg2.assistantName !== void 0) bar.assistantName = cfg2.assistantName;
|
|
2151
|
-
if (cfg2.personas !== void 0) bar.personas = cfg2.personas;
|
|
2152
|
-
if (cfg2.greeting !== void 0) bar.greeting = cfg2.greeting;
|
|
2153
|
-
if (cfg2.voiceInput !== void 0) bar.voiceInput = cfg2.voiceInput;
|
|
2154
|
-
bar.introSuggestion = cfg2.introSuggestion;
|
|
2155
|
-
bar.forceExpanded = cfg2.forceExpanded === true;
|
|
2156
|
-
if (typeof cfg2.maximized === "boolean") {
|
|
2157
|
-
bar.maximized = cfg2.maximized;
|
|
2281
|
+
if (bridge.detect()) {
|
|
2282
|
+
if (cfg2.suppressNative) bridge.hideNative();
|
|
2283
|
+
return bridge;
|
|
2158
2284
|
}
|
|
2285
|
+
ensureWatch(bridge, cfg2.suppressNative === true);
|
|
2286
|
+
return bridge;
|
|
2159
2287
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
}
|
|
2166
|
-
function refreshTemplateWidgetMap(uiTemplates) {
|
|
2167
|
-
_templateWidgetMap.clear();
|
|
2168
|
-
for (const tag of SERVER_MOUNTED_TILE_WIDGETS) {
|
|
2169
|
-
_templateWidgetMap.set(tag, tag);
|
|
2170
|
-
}
|
|
2171
|
-
if (!uiTemplates) return;
|
|
2172
|
-
const tiles = uiTemplates.tiles;
|
|
2173
|
-
if (!Array.isArray(tiles)) return;
|
|
2174
|
-
for (const t of tiles) {
|
|
2175
|
-
const id = typeof t?.id === "string" ? t.id : null;
|
|
2176
|
-
const widget = typeof t?.widget === "string" ? t.widget : null;
|
|
2177
|
-
if (id && widget) _templateWidgetMap.set(id, widget);
|
|
2288
|
+
function onSupportChatReady(bridge, cb) {
|
|
2289
|
+
if (bridge.detect()) {
|
|
2290
|
+
cb();
|
|
2291
|
+
return () => {
|
|
2292
|
+
};
|
|
2178
2293
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
actions: runtime2.actions,
|
|
2184
|
-
// Pass the runtime's event bus so ItemHandler can broadcast
|
|
2185
|
-
// `element.compositional_append` / `_patch` / `_remove` events
|
|
2186
|
-
// to container widgets (chips strip, FAQ accordion, nav tips).
|
|
2187
|
-
events: {
|
|
2188
|
-
publish: runtime2.events.publish.bind(runtime2.events),
|
|
2189
|
-
subscribe: runtime2.events.subscribe?.bind(runtime2.events)
|
|
2190
|
-
},
|
|
2191
|
-
handlers: [new TileHandler(resolveTileWidget), new ActionHandler(), new ItemHandler()],
|
|
2192
|
-
resolveTileWidget
|
|
2193
|
-
});
|
|
2194
|
-
setElementInstanceStore(_elementStore);
|
|
2195
|
-
return _elementStore;
|
|
2196
|
-
}
|
|
2197
|
-
var _hydrationStarted = false;
|
|
2198
|
-
function hydrateOnce(runtime2, backendUrl) {
|
|
2199
|
-
if (_hydrationStarted) return;
|
|
2200
|
-
_hydrationStarted = true;
|
|
2201
|
-
const store = getOrCreateElementStore(runtime2);
|
|
2202
|
-
const trimmed = backendUrl.replace(/\/$/, "");
|
|
2203
|
-
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
2204
|
-
fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
2205
|
-
if (!response) return;
|
|
2206
|
-
void store.hydrate(response.mounted_elements);
|
|
2207
|
-
});
|
|
2208
|
-
}
|
|
2209
|
-
var PAGE_LEVEL_KINDS = /* @__PURE__ */ new Set(["view", "scroll", "idle", "hesitation", "hover", "nav"]);
|
|
2210
|
-
function buildObserverEnrich(runtimeRef) {
|
|
2211
|
-
return (ev, raw) => {
|
|
2212
|
-
const extract = runtimeRef.getProductIdentity;
|
|
2213
|
-
if (typeof extract !== "function") return ev;
|
|
2214
|
-
if (ev.kind === "click") {
|
|
2215
|
-
const chain = raw.properties.$elements;
|
|
2216
|
-
if (!Array.isArray(chain)) return ev;
|
|
2217
|
-
const id = extract(chain);
|
|
2218
|
-
if (!id) return ev;
|
|
2219
|
-
return {
|
|
2220
|
-
...ev,
|
|
2221
|
-
product: {
|
|
2222
|
-
handle: id.productHandle,
|
|
2223
|
-
url: id.productUrl,
|
|
2224
|
-
...id.productTitle ? { title: id.productTitle } : {}
|
|
2225
|
-
}
|
|
2226
|
-
};
|
|
2227
|
-
}
|
|
2228
|
-
if (!ev.product && PAGE_LEVEL_KINDS.has(ev.kind) && typeof location !== "undefined") {
|
|
2229
|
-
const id = extract([{ href: location.pathname }]);
|
|
2230
|
-
if (!id) return ev;
|
|
2231
|
-
return {
|
|
2232
|
-
...ev,
|
|
2233
|
-
product: {
|
|
2234
|
-
handle: id.productHandle,
|
|
2235
|
-
url: id.productUrl,
|
|
2236
|
-
...id.productTitle ? { title: id.productTitle } : {}
|
|
2237
|
-
}
|
|
2238
|
-
};
|
|
2239
|
-
}
|
|
2240
|
-
return ev;
|
|
2241
|
-
};
|
|
2242
|
-
}
|
|
2243
|
-
var _nudgeSurfaceWired = false;
|
|
2244
|
-
var _nudgeListener = null;
|
|
2245
|
-
function wireNudgeSurface(runtime2, backendUrl) {
|
|
2246
|
-
if (_nudgeSurfaceWired || typeof document === "undefined") return;
|
|
2247
|
-
_nudgeSurfaceWired = true;
|
|
2248
|
-
_nudgeListener = (e) => {
|
|
2249
|
-
try {
|
|
2250
|
-
const detail = e.detail;
|
|
2251
|
-
const isIntent = detail?.phase === "intent";
|
|
2252
|
-
if (!isIntent) {
|
|
2253
|
-
const store = getOrCreateElementStore(runtime2);
|
|
2254
|
-
const trimmed = backendUrl.replace(/\/$/, "");
|
|
2255
|
-
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
2256
|
-
void fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
2257
|
-
if (!response) return;
|
|
2258
|
-
void store.hydrate(response.mounted_elements);
|
|
2259
|
-
});
|
|
2260
|
-
}
|
|
2261
|
-
if (detail?.suppressToast === true) return;
|
|
2262
|
-
const findCanvas = (root) => {
|
|
2263
|
-
const direct = root.querySelector("smart-canvas");
|
|
2264
|
-
if (direct) return direct;
|
|
2265
|
-
for (const el of root.querySelectorAll("*")) {
|
|
2266
|
-
const sr = el.shadowRoot;
|
|
2267
|
-
if (sr) {
|
|
2268
|
-
const found = findCanvas(sr);
|
|
2269
|
-
if (found) return found;
|
|
2270
|
-
}
|
|
2271
|
-
}
|
|
2272
|
-
return null;
|
|
2273
|
-
};
|
|
2274
|
-
const canvas = findCanvas(document);
|
|
2275
|
-
const messageId = e.detail?.messageId;
|
|
2276
|
-
const nudgeText = messageId ? (chatSession.getState().messages.find((m) => m.id === messageId)?.text ?? "").trim() : "";
|
|
2277
|
-
const title = nudgeText ? nudgeText.length > 140 ? `${nudgeText.slice(0, 137)}\u2026` : nudgeText : "I put something together for you";
|
|
2278
|
-
canvas?.previewNotification?.({
|
|
2279
|
-
title,
|
|
2280
|
-
body: "Tap to take a look"
|
|
2281
|
-
});
|
|
2282
|
-
} catch (err) {
|
|
2283
|
-
console.warn("[adaptive-chatbot] nudge surface reaction failed", err);
|
|
2284
|
-
}
|
|
2294
|
+
const watch = ensureWatch(bridge, false);
|
|
2295
|
+
watch.listeners.add(cb);
|
|
2296
|
+
return () => {
|
|
2297
|
+
watch.listeners.delete(cb);
|
|
2285
2298
|
};
|
|
2286
|
-
document.addEventListener("syntro:nudge", _nudgeListener);
|
|
2287
|
-
}
|
|
2288
|
-
function configureTransportIfPossible(cfg2) {
|
|
2289
|
-
if (!cfg2.runtime) return;
|
|
2290
|
-
const backendUrl = cfg2.runtime.backendUrl;
|
|
2291
|
-
if (!backendUrl) return;
|
|
2292
|
-
const elementsActive = cfg2.elementsEnabled === true && cfg2.uiTemplates != null;
|
|
2293
|
-
refreshTemplateWidgetMap(elementsActive ? cfg2.uiTemplates : void 0);
|
|
2294
|
-
const forwardedProps = elementsActive ? { elementsEnabled: true, uiTemplates: cfg2.uiTemplates } : void 0;
|
|
2295
|
-
if (elementsActive) publishTemplatesIfPossible(backendUrl, cfg2.uiTemplates, cfg2.runtime?.token);
|
|
2296
|
-
const runtime2 = cfg2.runtime;
|
|
2297
|
-
const onElementMutation = elementsActive ? (mutations) => {
|
|
2298
|
-
void getOrCreateElementStore(runtime2).apply(mutations);
|
|
2299
|
-
} : void 0;
|
|
2300
|
-
if (elementsActive) {
|
|
2301
|
-
hydrateOnce(runtime2, backendUrl);
|
|
2302
|
-
}
|
|
2303
|
-
const persona = pickPersona(cfg2.personas ?? null);
|
|
2304
|
-
chatTransport.configure({
|
|
2305
|
-
...cfg2,
|
|
2306
|
-
backendUrl,
|
|
2307
|
-
forwardedProps,
|
|
2308
|
-
onElementMutation,
|
|
2309
|
-
activeLidSlot: cfg2._syntroSlotName,
|
|
2310
|
-
personaId: persona?.id,
|
|
2311
|
-
personaName: persona?.name,
|
|
2312
|
-
personaRole: persona?.role_title
|
|
2313
|
-
});
|
|
2314
|
-
void chatTransport.startSessionStream();
|
|
2315
|
-
wireNudgeSurface(runtime2, backendUrl);
|
|
2316
|
-
startObserverIfPossible(cfg2);
|
|
2317
|
-
}
|
|
2318
|
-
var _templatesPublished = false;
|
|
2319
|
-
function publishTemplatesIfPossible(backendUrl, uiTemplates, token) {
|
|
2320
|
-
if (_templatesPublished || typeof window === "undefined") return;
|
|
2321
|
-
if (!uiTemplates || typeof uiTemplates !== "object") return;
|
|
2322
|
-
_templatesPublished = true;
|
|
2323
|
-
const headers2 = { "Content-Type": "application/json" };
|
|
2324
|
-
if (token) headers2.Authorization = `Bearer ${token}`;
|
|
2325
|
-
const editorToken = readEditorToken();
|
|
2326
|
-
if (editorToken) headers2["X-Syntro-Editor-Token"] = editorToken;
|
|
2327
|
-
const trimmed = backendUrl.replace(/\/$/, "");
|
|
2328
|
-
void fetch(`${trimmed}/api/adaptive/templates`, {
|
|
2329
|
-
method: "POST",
|
|
2330
|
-
headers: headers2,
|
|
2331
|
-
credentials: "include",
|
|
2332
|
-
body: JSON.stringify({ ui_templates: uiTemplates })
|
|
2333
|
-
}).catch(() => {
|
|
2334
|
-
});
|
|
2335
|
-
}
|
|
2336
|
-
var _diveDeeperWired = false;
|
|
2337
|
-
function wireDiveDeeperListener() {
|
|
2338
|
-
if (_diveDeeperWired || typeof window === "undefined") return;
|
|
2339
|
-
_diveDeeperWired = true;
|
|
2340
|
-
window.addEventListener(DIVE_DEEPER_EVENT, (e) => {
|
|
2341
|
-
const detail = e.detail ?? {};
|
|
2342
|
-
const prompt = typeof detail.prompt === "string" && detail.prompt.trim() ? detail.prompt : detail.title ? "I'd like to dive deeper on this, I didn't see what I needed." : "";
|
|
2343
|
-
if (!prompt) return;
|
|
2344
|
-
const origin = { kind: "dive-deeper" };
|
|
2345
|
-
if (detail.title) origin.title = detail.title;
|
|
2346
|
-
if (detail.context) origin.context = detail.context;
|
|
2347
|
-
chatSession.send(prompt, { forwardedProps: { origin } });
|
|
2348
|
-
if (!chatSession.hasTransport()) {
|
|
2349
|
-
chatSession.error("Chat backend not configured \u2014 set backendUrl in the canvas config.");
|
|
2350
|
-
}
|
|
2351
|
-
});
|
|
2352
2299
|
}
|
|
2353
|
-
var _observerStarted = false;
|
|
2354
|
-
var _observerRuntime = null;
|
|
2355
|
-
var _observerHandle = null;
|
|
2356
|
-
var _observerBusUnsubscribe = null;
|
|
2357
|
-
function startObserverIfPossible(cfg2) {
|
|
2358
|
-
if (typeof window === "undefined") return;
|
|
2359
|
-
const runtimeRef = cfg2.runtime;
|
|
2360
|
-
if (!runtimeRef?.events?.subscribe) return;
|
|
2361
|
-
if (_observerStarted && _observerRuntime === runtimeRef) return;
|
|
2362
|
-
if (_observerStarted) {
|
|
2363
|
-
_observerBusUnsubscribe?.();
|
|
2364
|
-
_observerHandle?.stop();
|
|
2365
|
-
_observerBusUnsubscribe = null;
|
|
2366
|
-
_observerHandle = null;
|
|
2367
|
-
_observerStarted = false;
|
|
2368
|
-
}
|
|
2369
|
-
const trimmed = (runtimeRef.backendUrl ?? "").replace(/\/$/, "");
|
|
2370
|
-
const url = trimmed ? `${trimmed}/api/adaptive/observation` : "/api/adaptive/observation";
|
|
2371
|
-
const runtimeId = (method) => {
|
|
2372
|
-
try {
|
|
2373
|
-
const v = runtimeRef.telemetry?.[method]?.();
|
|
2374
|
-
return typeof v === "string" && v.length > 0 ? v : null;
|
|
2375
|
-
} catch {
|
|
2376
|
-
return null;
|
|
2377
|
-
}
|
|
2378
|
-
};
|
|
2379
|
-
const getDistinctId = () => runtimeId("getDistinctId");
|
|
2380
|
-
const getSessionId = () => runtimeId("getSessionId");
|
|
2381
|
-
const token = () => {
|
|
2382
|
-
const t = runtimeRef.token;
|
|
2383
|
-
return typeof t === "string" ? t : "";
|
|
2384
|
-
};
|
|
2385
|
-
const enrich = buildObserverEnrich(runtimeRef);
|
|
2386
|
-
const handle = startObserver({ url, token, getDistinctId, getSessionId, enrich });
|
|
2387
|
-
window.__syntroObserverStats = () => handle.stats();
|
|
2388
|
-
const unsubscribe = runtimeRef.events.subscribe({}, (evt) => {
|
|
2389
|
-
const raw = busEventToRawEvent(evt);
|
|
2390
|
-
if (raw) handle.ingest(raw);
|
|
2391
|
-
});
|
|
2392
|
-
_observerRuntime = runtimeRef;
|
|
2393
|
-
_observerHandle = handle;
|
|
2394
|
-
_observerBusUnsubscribe = unsubscribe;
|
|
2395
|
-
_observerStarted = true;
|
|
2396
|
-
}
|
|
2397
|
-
function wireListeners(bar, state) {
|
|
2398
|
-
const onMessageSent = (e) => {
|
|
2399
|
-
const text = e.detail.text;
|
|
2400
|
-
chatSession.send(text, { activeLidSlot: state.cfg._syntroSlotName });
|
|
2401
|
-
if (!chatSession.hasTransport()) {
|
|
2402
|
-
chatSession.error("Chat backend not configured \u2014 set backendUrl in the canvas config.");
|
|
2403
|
-
}
|
|
2404
|
-
};
|
|
2405
|
-
const onInterrupt = () => {
|
|
2406
|
-
chatSession.interrupt();
|
|
2407
|
-
};
|
|
2408
|
-
const onToolCallApproved = (e) => {
|
|
2409
|
-
const detail = e.detail;
|
|
2410
|
-
chatSession.resolveToolCall(detail.toolCallId, {}, detail.approved);
|
|
2411
|
-
};
|
|
2412
|
-
const onClose = () => {
|
|
2413
|
-
state.cfg.onClose?.();
|
|
2414
|
-
};
|
|
2415
|
-
bar.addEventListener("chat-message-sent", onMessageSent);
|
|
2416
|
-
bar.addEventListener("chat-interrupt", onInterrupt);
|
|
2417
|
-
bar.addEventListener("canvas-close", onClose);
|
|
2418
|
-
bar.addEventListener("trail-toolcall-approved", onToolCallApproved);
|
|
2419
|
-
return () => {
|
|
2420
|
-
bar.removeEventListener("chat-message-sent", onMessageSent);
|
|
2421
|
-
bar.removeEventListener("chat-interrupt", onInterrupt);
|
|
2422
|
-
bar.removeEventListener("canvas-close", onClose);
|
|
2423
|
-
bar.removeEventListener("trail-toolcall-approved", onToolCallApproved);
|
|
2424
|
-
};
|
|
2425
|
-
}
|
|
2426
|
-
var AdaptiveChatBarMountable = {
|
|
2427
|
-
mount(container, mountConfig) {
|
|
2428
|
-
const cfg2 = mountConfig ?? {};
|
|
2429
|
-
configureTransportIfPossible(cfg2);
|
|
2430
|
-
bootSupportChat(cfg2.supportChat);
|
|
2431
|
-
wireDiveDeeperListener();
|
|
2432
|
-
const bar = document.createElement("adaptive-chat-bar");
|
|
2433
|
-
applyPlaceholder(bar, cfg2);
|
|
2434
|
-
const widgets = cfg2.runtime?.widgets;
|
|
2435
|
-
if (widgets) {
|
|
2436
|
-
bar.mountInlineWidget = (widgetId, container2, props) => {
|
|
2437
|
-
if (!widgets.has(widgetId)) return null;
|
|
2438
|
-
const handle = widgets.mount(widgetId, container2, props);
|
|
2439
|
-
const element = container2.firstElementChild;
|
|
2440
|
-
if (!element) {
|
|
2441
|
-
handle.unmount?.();
|
|
2442
|
-
return null;
|
|
2443
|
-
}
|
|
2444
|
-
return { element, cleanup: () => handle.unmount?.() };
|
|
2445
|
-
};
|
|
2446
|
-
}
|
|
2447
|
-
const unsubSession = chatSession.subscribe((s) => {
|
|
2448
|
-
bar.messages = [...s.messages];
|
|
2449
|
-
bar.inFlight = s.inFlight;
|
|
2450
|
-
bar.thinkingText = s.thinkingText;
|
|
2451
|
-
});
|
|
2452
|
-
const runtimeEvents = cfg2.runtime ? cfg2.runtime.events : void 0;
|
|
2453
|
-
const refreshReceipts = () => {
|
|
2454
|
-
const store = _elementStore;
|
|
2455
|
-
if (store) {
|
|
2456
|
-
bar.tileReceipts = store.listTileReceipts();
|
|
2457
|
-
bar.inlineWidgets = store.listInlineWidgets();
|
|
2458
|
-
}
|
|
2459
|
-
};
|
|
2460
|
-
let unsubReceipts;
|
|
2461
|
-
if (runtimeEvents?.subscribe) {
|
|
2462
|
-
unsubReceipts = runtimeEvents.subscribe(
|
|
2463
|
-
{ names: [TILE_MOUNTED_EVENT, TILE_UNMOUNTED_EVENT] },
|
|
2464
|
-
refreshReceipts
|
|
2465
|
-
);
|
|
2466
|
-
}
|
|
2467
|
-
refreshReceipts();
|
|
2468
|
-
const state = { bar, cleanup: () => {
|
|
2469
|
-
}, cfg: cfg2 };
|
|
2470
|
-
const unwireListeners = wireListeners(bar, state);
|
|
2471
|
-
container.appendChild(bar);
|
|
2472
|
-
const unsubFallback = chatTransport.onFallback(() => {
|
|
2473
|
-
bar.remove();
|
|
2474
|
-
container.innerHTML = renderFallbackHtml(state.cfg.fallback);
|
|
2475
|
-
});
|
|
2476
|
-
state.cleanup = () => {
|
|
2477
|
-
unsubSession();
|
|
2478
|
-
unsubFallback();
|
|
2479
|
-
unsubReceipts?.();
|
|
2480
|
-
unwireListeners();
|
|
2481
|
-
bar.remove();
|
|
2482
|
-
setState(container, null);
|
|
2483
|
-
};
|
|
2484
|
-
setState(container, state);
|
|
2485
|
-
return state.cleanup;
|
|
2486
|
-
},
|
|
2487
|
-
update(container, mountConfig) {
|
|
2488
|
-
const state = getState(container);
|
|
2489
|
-
if (!state) return;
|
|
2490
|
-
const cfg2 = mountConfig ?? {};
|
|
2491
|
-
configureTransportIfPossible(cfg2);
|
|
2492
|
-
applyPlaceholder(state.bar, cfg2);
|
|
2493
|
-
state.cfg = cfg2;
|
|
2494
|
-
}
|
|
2495
|
-
};
|
|
2496
|
-
|
|
2497
|
-
// src/AdaptiveChipsStripMountable.ts
|
|
2498
|
-
var STATE_KEY2 = "__syntroChipsStripMount";
|
|
2499
|
-
function getState2(container) {
|
|
2500
|
-
return container[STATE_KEY2] ?? null;
|
|
2501
|
-
}
|
|
2502
|
-
function setState2(container, state) {
|
|
2503
|
-
container[STATE_KEY2] = state;
|
|
2504
|
-
}
|
|
2505
|
-
function applyProps(strip, cfg2) {
|
|
2506
|
-
if (cfg2.chips !== void 0) strip.chips = cfg2.chips;
|
|
2507
|
-
if (cfg2.runtime !== void 0) {
|
|
2508
|
-
strip.runtimeRef = cfg2.runtime;
|
|
2509
|
-
}
|
|
2510
|
-
if (cfg2.chromeless !== void 0) strip.chromeless = cfg2.chromeless;
|
|
2511
|
-
}
|
|
2512
|
-
var AdaptiveChipsStripMountable = {
|
|
2513
|
-
mount(container, mountConfig) {
|
|
2514
|
-
const cfg2 = mountConfig ?? {};
|
|
2515
|
-
const strip = document.createElement("adaptive-chips-strip");
|
|
2516
|
-
applyProps(strip, cfg2);
|
|
2517
|
-
const state = {
|
|
2518
|
-
strip,
|
|
2519
|
-
listeners: {
|
|
2520
|
-
revealed: (e) => {
|
|
2521
|
-
const cb = cfg2.onChipRevealed;
|
|
2522
|
-
if (cb) {
|
|
2523
|
-
cb(
|
|
2524
|
-
e.detail
|
|
2525
|
-
);
|
|
2526
|
-
}
|
|
2527
|
-
},
|
|
2528
|
-
dismissed: (e) => {
|
|
2529
|
-
const cb = cfg2.onChipDismissed;
|
|
2530
|
-
if (cb) cb(e.detail);
|
|
2531
|
-
}
|
|
2532
|
-
}
|
|
2533
|
-
};
|
|
2534
|
-
strip.addEventListener("chip-revealed", state.listeners.revealed);
|
|
2535
|
-
strip.addEventListener("chip-dismissed", state.listeners.dismissed);
|
|
2536
|
-
container.appendChild(strip);
|
|
2537
|
-
setState2(container, state);
|
|
2538
|
-
return () => {
|
|
2539
|
-
strip.removeEventListener("chip-revealed", state.listeners.revealed);
|
|
2540
|
-
strip.removeEventListener("chip-dismissed", state.listeners.dismissed);
|
|
2541
|
-
strip.remove();
|
|
2542
|
-
setState2(container, null);
|
|
2543
|
-
};
|
|
2544
|
-
},
|
|
2545
|
-
update(container, mountConfig) {
|
|
2546
|
-
const state = getState2(container);
|
|
2547
|
-
if (!state) return;
|
|
2548
|
-
const cfg2 = mountConfig ?? {};
|
|
2549
|
-
applyProps(state.strip, cfg2);
|
|
2550
|
-
state.strip.removeEventListener("chip-revealed", state.listeners.revealed);
|
|
2551
|
-
state.strip.removeEventListener("chip-dismissed", state.listeners.dismissed);
|
|
2552
|
-
state.listeners.revealed = (e) => {
|
|
2553
|
-
const cb = cfg2.onChipRevealed;
|
|
2554
|
-
if (cb) {
|
|
2555
|
-
cb(e.detail);
|
|
2556
|
-
}
|
|
2557
|
-
};
|
|
2558
|
-
state.listeners.dismissed = (e) => {
|
|
2559
|
-
const cb = cfg2.onChipDismissed;
|
|
2560
|
-
if (cb) cb(e.detail);
|
|
2561
|
-
};
|
|
2562
|
-
state.strip.addEventListener("chip-revealed", state.listeners.revealed);
|
|
2563
|
-
state.strip.addEventListener("chip-dismissed", state.listeners.dismissed);
|
|
2564
|
-
}
|
|
2565
|
-
};
|
|
2566
2300
|
|
|
2567
2301
|
// src/LeaveAMessageLit.ts
|
|
2568
2302
|
import { html, LitElement, nothing } from "lit";
|
|
@@ -2707,8 +2441,14 @@ var trailHostStyles = {
|
|
|
2707
2441
|
width: "100%",
|
|
2708
2442
|
boxSizing: "border-box"
|
|
2709
2443
|
};
|
|
2710
|
-
var
|
|
2711
|
-
var
|
|
2444
|
+
var LEAVE_A_MESSAGE_PERSIST_NAMESPACE = "supportchat";
|
|
2445
|
+
var LEAVE_A_MESSAGE_PERSIST_KEY = "leave-a-message";
|
|
2446
|
+
var PERSIST_NAMESPACE = LEAVE_A_MESSAGE_PERSIST_NAMESPACE;
|
|
2447
|
+
var PERSIST_KEY = LEAVE_A_MESSAGE_PERSIST_KEY;
|
|
2448
|
+
var liveCards = /* @__PURE__ */ new Set();
|
|
2449
|
+
function liveLeaveAMessageCards() {
|
|
2450
|
+
return liveCards;
|
|
2451
|
+
}
|
|
2712
2452
|
var lamUidCounter = 0;
|
|
2713
2453
|
var LeaveAMessageLit = class extends LitElement {
|
|
2714
2454
|
constructor() {
|
|
@@ -2878,6 +2618,10 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2878
2618
|
super.connectedCallback();
|
|
2879
2619
|
this._loadPersisted();
|
|
2880
2620
|
this.addEventListener("keydown", this._onKeyDown);
|
|
2621
|
+
liveCards.add(this);
|
|
2622
|
+
if (this.bridge) {
|
|
2623
|
+
this._readyUnsub = onSupportChatReady(this.bridge, () => this.requestUpdate());
|
|
2624
|
+
}
|
|
2881
2625
|
}
|
|
2882
2626
|
updated() {
|
|
2883
2627
|
if (this.state !== "form" && this.bridge && this.bridge.detect() && !this._unsubscribe) {
|
|
@@ -2886,15 +2630,36 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2886
2630
|
}
|
|
2887
2631
|
disconnectedCallback() {
|
|
2888
2632
|
this.removeEventListener("keydown", this._onKeyDown);
|
|
2633
|
+
liveCards.delete(this);
|
|
2634
|
+
this._readyUnsub?.();
|
|
2635
|
+
this._readyUnsub = void 0;
|
|
2889
2636
|
this._unsubscribe?.();
|
|
2890
2637
|
this._unsubscribe = void 0;
|
|
2891
2638
|
this._noticeHandle?.dismiss();
|
|
2892
2639
|
this._noticeHandle = void 0;
|
|
2893
2640
|
super.disconnectedCallback();
|
|
2894
2641
|
}
|
|
2642
|
+
/** Is this card's own vendor subscription delivering replies? While it
|
|
2643
|
+
* is, the page-level inbox leaves every message to the card. */
|
|
2644
|
+
get isListening() {
|
|
2645
|
+
return this._unsubscribe !== void 0;
|
|
2646
|
+
}
|
|
2647
|
+
/** Hand the card a team message that arrived through the page-level
|
|
2648
|
+
* inbox (support-chat/inbox.ts) while the card was mounted but not yet
|
|
2649
|
+
* listening — a compose-step card the concierge put up moments before
|
|
2650
|
+
* the team's own message landed. */
|
|
2651
|
+
receiveOperatorMessage(text) {
|
|
2652
|
+
this._onReply(text);
|
|
2653
|
+
}
|
|
2654
|
+
/** Open the thread face — what tapping the mail face or an announcement
|
|
2655
|
+
* does. Used by the page-level inbox when its announcement is tapped
|
|
2656
|
+
* after a card has already mounted and hydrated the thread. */
|
|
2657
|
+
showThread() {
|
|
2658
|
+
this._flip();
|
|
2659
|
+
}
|
|
2895
2660
|
/** Read the injected persistence store, if any (I2). */
|
|
2896
2661
|
_persistStore() {
|
|
2897
|
-
return this.runtime?.state?.
|
|
2662
|
+
return this.runtime?.state?.ns?.(PERSIST_NAMESPACE);
|
|
2898
2663
|
}
|
|
2899
2664
|
/** Hydrate {state, transcript, email} from a prior page's mount. */
|
|
2900
2665
|
_loadPersisted() {
|
|
@@ -2968,20 +2733,8 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2968
2733
|
if (typeof document === "undefined") return;
|
|
2969
2734
|
const title = this.replyToast?.title?.trim();
|
|
2970
2735
|
if (!title) return;
|
|
2971
|
-
const find = (root) => {
|
|
2972
|
-
const direct = root.querySelector("smart-canvas");
|
|
2973
|
-
if (direct) return direct;
|
|
2974
|
-
for (const el of root.querySelectorAll("*")) {
|
|
2975
|
-
const sr = el.shadowRoot;
|
|
2976
|
-
if (sr) {
|
|
2977
|
-
const found = find(sr);
|
|
2978
|
-
if (found) return found;
|
|
2979
|
-
}
|
|
2980
|
-
}
|
|
2981
|
-
return null;
|
|
2982
|
-
};
|
|
2983
2736
|
try {
|
|
2984
|
-
const canvas =
|
|
2737
|
+
const canvas = findSmartCanvas();
|
|
2985
2738
|
this._noticeHandle?.dismiss();
|
|
2986
2739
|
this._noticeHandle = void 0;
|
|
2987
2740
|
const handle = canvas?.previewNotification?.({
|
|
@@ -3126,6 +2879,7 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
3126
2879
|
.messages=${this._trailMessages()}
|
|
3127
2880
|
.forceExpanded=${true}
|
|
3128
2881
|
.hideTopBorder=${true}
|
|
2882
|
+
.followBottom=${true}
|
|
3129
2883
|
style=${styleMap(trailHostStyles)}
|
|
3130
2884
|
></adaptive-chat-trail>
|
|
3131
2885
|
${copy?.replyLabel ? html`<label data-lam-reply-label for=${replyId} style=${styleMap(srOnlyStyles)}
|
|
@@ -3179,6 +2933,545 @@ function registerLeaveAMessageLit() {
|
|
|
3179
2933
|
}
|
|
3180
2934
|
registerLeaveAMessageLit();
|
|
3181
2935
|
|
|
2936
|
+
// src/support-chat/inbox.ts
|
|
2937
|
+
var LEAVE_A_MESSAGE_WIDGET_ID = "adaptive-chatbot:leave-a-message";
|
|
2938
|
+
var SUPPORT_CHAT_INBOX_INSTANCE_ID = "supportchat-inbox";
|
|
2939
|
+
function findLeaveAMessageTemplate(uiTemplates) {
|
|
2940
|
+
const tiles = uiTemplates?.tiles;
|
|
2941
|
+
if (!Array.isArray(tiles)) return void 0;
|
|
2942
|
+
const t = tiles.find((x) => x?.widget === LEAVE_A_MESSAGE_WIDGET_ID);
|
|
2943
|
+
if (!t) return void 0;
|
|
2944
|
+
const slot = typeof t.default_slot === "string" && t.default_slot ? t.default_slot : "drawer";
|
|
2945
|
+
const props = t.default_widget_props && typeof t.default_widget_props === "object" ? t.default_widget_props : {};
|
|
2946
|
+
return { slot, props };
|
|
2947
|
+
}
|
|
2948
|
+
var _wired;
|
|
2949
|
+
function wireSupportChatInbox(opts) {
|
|
2950
|
+
const { bridge, inbox, runtime: runtime2, uiTemplates } = opts;
|
|
2951
|
+
const store = runtime2?.state?.ns?.(LEAVE_A_MESSAGE_PERSIST_NAMESPACE);
|
|
2952
|
+
let replyUnsub;
|
|
2953
|
+
let mountUnsub;
|
|
2954
|
+
let unmountUnsub;
|
|
2955
|
+
let notice;
|
|
2956
|
+
let localSlot;
|
|
2957
|
+
const readThread = () => store?.get(LEAVE_A_MESSAGE_PERSIST_KEY) ?? {};
|
|
2958
|
+
const openThread = () => {
|
|
2959
|
+
const thread = readThread();
|
|
2960
|
+
store?.set(LEAVE_A_MESSAGE_PERSIST_KEY, { ...thread, state: "flipped" });
|
|
2961
|
+
notice = void 0;
|
|
2962
|
+
const live = liveLeaveAMessageCards().values().next().value;
|
|
2963
|
+
if (live) {
|
|
2964
|
+
live.showThread();
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
const template = findLeaveAMessageTemplate(uiTemplates);
|
|
2968
|
+
if (!template || !inbox || !runtime2) {
|
|
2969
|
+
console.warn(
|
|
2970
|
+
`supportChat.inbox: cannot open the thread \u2014 uiTemplates declares no ${LEAVE_A_MESSAGE_WIDGET_ID} tile for this workspace`
|
|
2971
|
+
);
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
const props = {
|
|
2975
|
+
slot: template.slot,
|
|
2976
|
+
instance_id: SUPPORT_CHAT_INBOX_INSTANCE_ID,
|
|
2977
|
+
widget: LEAVE_A_MESSAGE_WIDGET_ID,
|
|
2978
|
+
props: { ...template.props, title: inbox.title, message: "", context_summary: "" }
|
|
2979
|
+
};
|
|
2980
|
+
localSlot = template.slot;
|
|
2981
|
+
runtime2.events.publish(TILE_MOUNTED_EVENT, props);
|
|
2982
|
+
};
|
|
2983
|
+
const onMessage = (text) => {
|
|
2984
|
+
const card = liveLeaveAMessageCards().values().next().value;
|
|
2985
|
+
if (card) {
|
|
2986
|
+
if (!card.isListening) card.receiveOperatorMessage(text);
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2989
|
+
if (!inbox) {
|
|
2990
|
+
console.warn(
|
|
2991
|
+
`supportChat.inbox: a '${bridge.vendor}' team message arrived with no handoff card mounted and no supportChat.inbox authored \u2014 dropped`
|
|
2992
|
+
);
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
const thread = readThread();
|
|
2996
|
+
store?.set(LEAVE_A_MESSAGE_PERSIST_KEY, {
|
|
2997
|
+
...thread,
|
|
2998
|
+
state: thread.state === "flipped" ? "flipped" : "mail",
|
|
2999
|
+
transcript: [...thread.transcript ?? [], { role: "operator", text }]
|
|
3000
|
+
});
|
|
3001
|
+
const canvas = findSmartCanvas();
|
|
3002
|
+
notice?.dismiss();
|
|
3003
|
+
notice = void 0;
|
|
3004
|
+
const handle = canvas?.previewNotification?.({
|
|
3005
|
+
title: inbox.toastTitle,
|
|
3006
|
+
body: text,
|
|
3007
|
+
persistent: true,
|
|
3008
|
+
onTap: openThread
|
|
3009
|
+
});
|
|
3010
|
+
if (handle && typeof handle.dismiss === "function") notice = handle;
|
|
3011
|
+
};
|
|
3012
|
+
const readyUnsub = onSupportChatReady(bridge, () => {
|
|
3013
|
+
replyUnsub = bridge.onOperatorReply(onMessage);
|
|
3014
|
+
});
|
|
3015
|
+
if (runtime2?.events.subscribe) {
|
|
3016
|
+
mountUnsub = runtime2.events.subscribe({ names: [TILE_MOUNTED_EVENT] }, (e) => {
|
|
3017
|
+
const p = e.props;
|
|
3018
|
+
if (!localSlot || p?.widget !== LEAVE_A_MESSAGE_WIDGET_ID) return;
|
|
3019
|
+
if (p.instance_id === SUPPORT_CHAT_INBOX_INSTANCE_ID) return;
|
|
3020
|
+
const unmount = {
|
|
3021
|
+
slot: localSlot,
|
|
3022
|
+
instance_id: SUPPORT_CHAT_INBOX_INSTANCE_ID
|
|
3023
|
+
};
|
|
3024
|
+
localSlot = void 0;
|
|
3025
|
+
runtime2.events.publish(TILE_UNMOUNTED_EVENT, unmount);
|
|
3026
|
+
});
|
|
3027
|
+
unmountUnsub = runtime2.events.subscribe({ names: [TILE_UNMOUNTED_EVENT] }, (e) => {
|
|
3028
|
+
const p = e.props;
|
|
3029
|
+
if (p?.instance_id === SUPPORT_CHAT_INBOX_INSTANCE_ID) localSlot = void 0;
|
|
3030
|
+
});
|
|
3031
|
+
}
|
|
3032
|
+
const cleanup = () => {
|
|
3033
|
+
readyUnsub();
|
|
3034
|
+
replyUnsub?.();
|
|
3035
|
+
replyUnsub = void 0;
|
|
3036
|
+
mountUnsub?.();
|
|
3037
|
+
mountUnsub = void 0;
|
|
3038
|
+
unmountUnsub?.();
|
|
3039
|
+
unmountUnsub = void 0;
|
|
3040
|
+
notice?.dismiss();
|
|
3041
|
+
notice = void 0;
|
|
3042
|
+
if (_wired === cleanup) _wired = void 0;
|
|
3043
|
+
};
|
|
3044
|
+
_wired = cleanup;
|
|
3045
|
+
return cleanup;
|
|
3046
|
+
}
|
|
3047
|
+
function isSupportChatInboxWired() {
|
|
3048
|
+
return _wired !== void 0;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
// src/AdaptiveChatBarMountable.ts
|
|
3052
|
+
var STATE_KEY = "__syntroChatBarMount";
|
|
3053
|
+
function getState(container) {
|
|
3054
|
+
return container[STATE_KEY] ?? null;
|
|
3055
|
+
}
|
|
3056
|
+
function setState(container, state) {
|
|
3057
|
+
container[STATE_KEY] = state;
|
|
3058
|
+
}
|
|
3059
|
+
function applyPlaceholder(bar, cfg2) {
|
|
3060
|
+
if (cfg2.placeholder !== void 0) bar.placeholder = cfg2.placeholder;
|
|
3061
|
+
if (cfg2.assistantName !== void 0) bar.assistantName = cfg2.assistantName;
|
|
3062
|
+
if (cfg2.personas !== void 0) bar.personas = cfg2.personas;
|
|
3063
|
+
if (cfg2.greeting !== void 0) bar.greeting = cfg2.greeting;
|
|
3064
|
+
if (cfg2.voiceInput !== void 0) bar.voiceInput = cfg2.voiceInput;
|
|
3065
|
+
bar.introSuggestion = cfg2.introSuggestion;
|
|
3066
|
+
bar.forceExpanded = cfg2.forceExpanded === true;
|
|
3067
|
+
if (typeof cfg2.maximized === "boolean") {
|
|
3068
|
+
bar.maximized = cfg2.maximized;
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
var _elementStore = null;
|
|
3072
|
+
var _templateWidgetMap = /* @__PURE__ */ new Map();
|
|
3073
|
+
var SERVER_MOUNTED_TILE_WIDGETS = ["syntro-product-reco-carousel"];
|
|
3074
|
+
function resolveTileWidget(templateId) {
|
|
3075
|
+
return _templateWidgetMap.get(templateId);
|
|
3076
|
+
}
|
|
3077
|
+
function refreshTemplateWidgetMap(uiTemplates) {
|
|
3078
|
+
_templateWidgetMap.clear();
|
|
3079
|
+
for (const tag of SERVER_MOUNTED_TILE_WIDGETS) {
|
|
3080
|
+
_templateWidgetMap.set(tag, tag);
|
|
3081
|
+
}
|
|
3082
|
+
if (!uiTemplates) return;
|
|
3083
|
+
const tiles = uiTemplates.tiles;
|
|
3084
|
+
if (!Array.isArray(tiles)) return;
|
|
3085
|
+
for (const t of tiles) {
|
|
3086
|
+
const id = typeof t?.id === "string" ? t.id : null;
|
|
3087
|
+
const widget = typeof t?.widget === "string" ? t.widget : null;
|
|
3088
|
+
if (id && widget) _templateWidgetMap.set(id, widget);
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
function getOrCreateElementStore(runtime2) {
|
|
3092
|
+
if (_elementStore) return _elementStore;
|
|
3093
|
+
_elementStore = new ElementInstanceStore({
|
|
3094
|
+
actions: runtime2.actions,
|
|
3095
|
+
// Pass the runtime's event bus so ItemHandler can broadcast
|
|
3096
|
+
// `element.compositional_append` / `_patch` / `_remove` events
|
|
3097
|
+
// to container widgets (chips strip, FAQ accordion, nav tips).
|
|
3098
|
+
events: {
|
|
3099
|
+
publish: runtime2.events.publish.bind(runtime2.events),
|
|
3100
|
+
subscribe: runtime2.events.subscribe?.bind(runtime2.events)
|
|
3101
|
+
},
|
|
3102
|
+
handlers: [new TileHandler(resolveTileWidget), new ActionHandler(), new ItemHandler()],
|
|
3103
|
+
resolveTileWidget
|
|
3104
|
+
});
|
|
3105
|
+
setElementInstanceStore(_elementStore);
|
|
3106
|
+
return _elementStore;
|
|
3107
|
+
}
|
|
3108
|
+
var _hydrationStarted = false;
|
|
3109
|
+
function hydrateOnce(runtime2, backendUrl) {
|
|
3110
|
+
if (_hydrationStarted) return;
|
|
3111
|
+
_hydrationStarted = true;
|
|
3112
|
+
const store = getOrCreateElementStore(runtime2);
|
|
3113
|
+
const trimmed = backendUrl.replace(/\/$/, "");
|
|
3114
|
+
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
3115
|
+
fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
3116
|
+
if (!response) return;
|
|
3117
|
+
void store.hydrate(response.mounted_elements);
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
3120
|
+
var PAGE_LEVEL_KINDS = /* @__PURE__ */ new Set(["view", "scroll", "idle", "hesitation", "hover", "nav"]);
|
|
3121
|
+
function buildObserverEnrich(runtimeRef) {
|
|
3122
|
+
return (ev, raw) => {
|
|
3123
|
+
const extract = runtimeRef.getProductIdentity;
|
|
3124
|
+
if (typeof extract !== "function") return ev;
|
|
3125
|
+
if (ev.kind === "click") {
|
|
3126
|
+
const chain = raw.properties.$elements;
|
|
3127
|
+
if (!Array.isArray(chain)) return ev;
|
|
3128
|
+
const id = extract(chain);
|
|
3129
|
+
if (!id) return ev;
|
|
3130
|
+
return {
|
|
3131
|
+
...ev,
|
|
3132
|
+
product: {
|
|
3133
|
+
handle: id.productHandle,
|
|
3134
|
+
url: id.productUrl,
|
|
3135
|
+
...id.productTitle ? { title: id.productTitle } : {}
|
|
3136
|
+
}
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
if (!ev.product && PAGE_LEVEL_KINDS.has(ev.kind) && typeof location !== "undefined") {
|
|
3140
|
+
const id = extract([{ href: location.pathname }]);
|
|
3141
|
+
if (!id) return ev;
|
|
3142
|
+
return {
|
|
3143
|
+
...ev,
|
|
3144
|
+
product: {
|
|
3145
|
+
handle: id.productHandle,
|
|
3146
|
+
url: id.productUrl,
|
|
3147
|
+
...id.productTitle ? { title: id.productTitle } : {}
|
|
3148
|
+
}
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
3151
|
+
return ev;
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
var _nudgeSurfaceWired = false;
|
|
3155
|
+
var _nudgeListener = null;
|
|
3156
|
+
function wireNudgeSurface(runtime2, backendUrl) {
|
|
3157
|
+
if (_nudgeSurfaceWired || typeof document === "undefined") return;
|
|
3158
|
+
_nudgeSurfaceWired = true;
|
|
3159
|
+
_nudgeListener = (e) => {
|
|
3160
|
+
try {
|
|
3161
|
+
const detail = e.detail;
|
|
3162
|
+
const isIntent = detail?.phase === "intent";
|
|
3163
|
+
if (!isIntent) {
|
|
3164
|
+
const store = getOrCreateElementStore(runtime2);
|
|
3165
|
+
const trimmed = backendUrl.replace(/\/$/, "");
|
|
3166
|
+
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
3167
|
+
void fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
3168
|
+
if (!response) return;
|
|
3169
|
+
void store.hydrate(response.mounted_elements);
|
|
3170
|
+
});
|
|
3171
|
+
}
|
|
3172
|
+
if (detail?.suppressToast === true) return;
|
|
3173
|
+
const canvas = findSmartCanvas();
|
|
3174
|
+
const messageId = e.detail?.messageId;
|
|
3175
|
+
const nudgeText = messageId ? (chatSession.getState().messages.find((m) => m.id === messageId)?.text ?? "").trim() : "";
|
|
3176
|
+
const title = nudgeText ? nudgeText.length > 140 ? `${nudgeText.slice(0, 137)}\u2026` : nudgeText : "I put something together for you";
|
|
3177
|
+
canvas?.previewNotification?.({
|
|
3178
|
+
title,
|
|
3179
|
+
body: "Tap to take a look"
|
|
3180
|
+
});
|
|
3181
|
+
} catch (err) {
|
|
3182
|
+
console.warn("[adaptive-chatbot] nudge surface reaction failed", err);
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3185
|
+
document.addEventListener("syntro:nudge", _nudgeListener);
|
|
3186
|
+
}
|
|
3187
|
+
function configureTransportIfPossible(cfg2) {
|
|
3188
|
+
if (!cfg2.runtime) return;
|
|
3189
|
+
const backendUrl = cfg2.runtime.backendUrl;
|
|
3190
|
+
if (!backendUrl) return;
|
|
3191
|
+
const elementsActive = cfg2.elementsEnabled === true && cfg2.uiTemplates != null;
|
|
3192
|
+
refreshTemplateWidgetMap(elementsActive ? cfg2.uiTemplates : void 0);
|
|
3193
|
+
const forwardedProps = elementsActive ? { elementsEnabled: true, uiTemplates: cfg2.uiTemplates } : void 0;
|
|
3194
|
+
if (elementsActive) publishTemplatesIfPossible(backendUrl, cfg2.uiTemplates, cfg2.runtime?.token);
|
|
3195
|
+
const runtime2 = cfg2.runtime;
|
|
3196
|
+
const onElementMutation = elementsActive ? (mutations) => {
|
|
3197
|
+
void getOrCreateElementStore(runtime2).apply(mutations);
|
|
3198
|
+
} : void 0;
|
|
3199
|
+
if (elementsActive) {
|
|
3200
|
+
hydrateOnce(runtime2, backendUrl);
|
|
3201
|
+
}
|
|
3202
|
+
const persona = pickPersona(cfg2.personas ?? null);
|
|
3203
|
+
chatTransport.configure({
|
|
3204
|
+
...cfg2,
|
|
3205
|
+
backendUrl,
|
|
3206
|
+
forwardedProps,
|
|
3207
|
+
onElementMutation,
|
|
3208
|
+
activeLidSlot: cfg2._syntroSlotName,
|
|
3209
|
+
personaId: persona?.id,
|
|
3210
|
+
personaName: persona?.name,
|
|
3211
|
+
personaRole: persona?.role_title
|
|
3212
|
+
});
|
|
3213
|
+
void chatTransport.startSessionStream();
|
|
3214
|
+
wireNudgeSurface(runtime2, backendUrl);
|
|
3215
|
+
startObserverIfPossible(cfg2);
|
|
3216
|
+
}
|
|
3217
|
+
var _templatesPublished = false;
|
|
3218
|
+
function publishTemplatesIfPossible(backendUrl, uiTemplates, token) {
|
|
3219
|
+
if (_templatesPublished || typeof window === "undefined") return;
|
|
3220
|
+
if (!uiTemplates || typeof uiTemplates !== "object") return;
|
|
3221
|
+
_templatesPublished = true;
|
|
3222
|
+
const headers2 = { "Content-Type": "application/json" };
|
|
3223
|
+
if (token) headers2.Authorization = `Bearer ${token}`;
|
|
3224
|
+
const editorToken = readEditorToken();
|
|
3225
|
+
if (editorToken) headers2["X-Syntro-Editor-Token"] = editorToken;
|
|
3226
|
+
const trimmed = backendUrl.replace(/\/$/, "");
|
|
3227
|
+
void fetch(`${trimmed}/api/adaptive/templates`, {
|
|
3228
|
+
method: "POST",
|
|
3229
|
+
headers: headers2,
|
|
3230
|
+
credentials: "include",
|
|
3231
|
+
body: JSON.stringify({ ui_templates: uiTemplates })
|
|
3232
|
+
}).catch(() => {
|
|
3233
|
+
});
|
|
3234
|
+
}
|
|
3235
|
+
var _diveDeeperWired = false;
|
|
3236
|
+
function wireDiveDeeperListener() {
|
|
3237
|
+
if (_diveDeeperWired || typeof window === "undefined") return;
|
|
3238
|
+
_diveDeeperWired = true;
|
|
3239
|
+
window.addEventListener(DIVE_DEEPER_EVENT, (e) => {
|
|
3240
|
+
const detail = e.detail ?? {};
|
|
3241
|
+
const prompt = typeof detail.prompt === "string" && detail.prompt.trim() ? detail.prompt : detail.title ? "I'd like to dive deeper on this, I didn't see what I needed." : "";
|
|
3242
|
+
if (!prompt) return;
|
|
3243
|
+
const origin = { kind: "dive-deeper" };
|
|
3244
|
+
if (detail.title) origin.title = detail.title;
|
|
3245
|
+
if (detail.context) origin.context = detail.context;
|
|
3246
|
+
chatSession.send(prompt, { forwardedProps: { origin }, via: "dive_deeper" });
|
|
3247
|
+
if (!chatSession.hasTransport()) {
|
|
3248
|
+
chatSession.error("Chat backend not configured \u2014 set backendUrl in the canvas config.");
|
|
3249
|
+
}
|
|
3250
|
+
});
|
|
3251
|
+
}
|
|
3252
|
+
var _observerStarted = false;
|
|
3253
|
+
var _observerRuntime = null;
|
|
3254
|
+
var _observerHandle = null;
|
|
3255
|
+
var _observerBusUnsubscribe = null;
|
|
3256
|
+
function startObserverIfPossible(cfg2) {
|
|
3257
|
+
if (typeof window === "undefined") return;
|
|
3258
|
+
const runtimeRef = cfg2.runtime;
|
|
3259
|
+
if (!runtimeRef?.events?.subscribe) return;
|
|
3260
|
+
if (_observerStarted && _observerRuntime === runtimeRef) return;
|
|
3261
|
+
if (_observerStarted) {
|
|
3262
|
+
_observerBusUnsubscribe?.();
|
|
3263
|
+
_observerHandle?.stop();
|
|
3264
|
+
_observerBusUnsubscribe = null;
|
|
3265
|
+
_observerHandle = null;
|
|
3266
|
+
_observerStarted = false;
|
|
3267
|
+
}
|
|
3268
|
+
const trimmed = (runtimeRef.backendUrl ?? "").replace(/\/$/, "");
|
|
3269
|
+
const url = trimmed ? `${trimmed}/api/adaptive/observation` : "/api/adaptive/observation";
|
|
3270
|
+
const runtimeId = (method) => {
|
|
3271
|
+
try {
|
|
3272
|
+
const v = runtimeRef.telemetry?.[method]?.();
|
|
3273
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
3274
|
+
} catch {
|
|
3275
|
+
return null;
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3278
|
+
const getDistinctId = () => runtimeId("getDistinctId");
|
|
3279
|
+
const getSessionId = () => runtimeId("getSessionId");
|
|
3280
|
+
const token = () => {
|
|
3281
|
+
const t = runtimeRef.token;
|
|
3282
|
+
return typeof t === "string" ? t : "";
|
|
3283
|
+
};
|
|
3284
|
+
const enrich = buildObserverEnrich(runtimeRef);
|
|
3285
|
+
const handle = startObserver({ url, token, getDistinctId, getSessionId, enrich });
|
|
3286
|
+
window.__syntroObserverStats = () => handle.stats();
|
|
3287
|
+
const unsubscribe = runtimeRef.events.subscribe({}, (evt) => {
|
|
3288
|
+
const raw = busEventToRawEvent(evt);
|
|
3289
|
+
if (raw) handle.ingest(raw);
|
|
3290
|
+
});
|
|
3291
|
+
_observerRuntime = runtimeRef;
|
|
3292
|
+
_observerHandle = handle;
|
|
3293
|
+
_observerBusUnsubscribe = unsubscribe;
|
|
3294
|
+
_observerStarted = true;
|
|
3295
|
+
}
|
|
3296
|
+
function wireListeners(bar, state) {
|
|
3297
|
+
const onMessageSent = (e) => {
|
|
3298
|
+
const { text, via } = e.detail;
|
|
3299
|
+
chatSession.send(text, { activeLidSlot: state.cfg._syntroSlotName, via });
|
|
3300
|
+
if (!chatSession.hasTransport()) {
|
|
3301
|
+
chatSession.error("Chat backend not configured \u2014 set backendUrl in the canvas config.");
|
|
3302
|
+
}
|
|
3303
|
+
};
|
|
3304
|
+
const onInterrupt = () => {
|
|
3305
|
+
chatSession.interrupt();
|
|
3306
|
+
};
|
|
3307
|
+
const onToolCallApproved = (e) => {
|
|
3308
|
+
const detail = e.detail;
|
|
3309
|
+
chatSession.resolveToolCall(detail.toolCallId, {}, detail.approved);
|
|
3310
|
+
};
|
|
3311
|
+
const onClose = () => {
|
|
3312
|
+
state.cfg.onClose?.();
|
|
3313
|
+
};
|
|
3314
|
+
bar.addEventListener("chat-message-sent", onMessageSent);
|
|
3315
|
+
bar.addEventListener("chat-interrupt", onInterrupt);
|
|
3316
|
+
bar.addEventListener("canvas-close", onClose);
|
|
3317
|
+
bar.addEventListener("trail-toolcall-approved", onToolCallApproved);
|
|
3318
|
+
return () => {
|
|
3319
|
+
bar.removeEventListener("chat-message-sent", onMessageSent);
|
|
3320
|
+
bar.removeEventListener("chat-interrupt", onInterrupt);
|
|
3321
|
+
bar.removeEventListener("canvas-close", onClose);
|
|
3322
|
+
bar.removeEventListener("trail-toolcall-approved", onToolCallApproved);
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
var AdaptiveChatBarMountable = {
|
|
3326
|
+
mount(container, mountConfig) {
|
|
3327
|
+
const cfg2 = mountConfig ?? {};
|
|
3328
|
+
configureTransportIfPossible(cfg2);
|
|
3329
|
+
const bridge = bootSupportChat(cfg2.supportChat);
|
|
3330
|
+
if (bridge && !isSupportChatInboxWired()) {
|
|
3331
|
+
wireSupportChatInbox({
|
|
3332
|
+
bridge,
|
|
3333
|
+
inbox: cfg2.supportChat?.inbox,
|
|
3334
|
+
runtime: cfg2.runtime,
|
|
3335
|
+
uiTemplates: cfg2.uiTemplates
|
|
3336
|
+
});
|
|
3337
|
+
}
|
|
3338
|
+
wireDiveDeeperListener();
|
|
3339
|
+
const bar = document.createElement("adaptive-chat-bar");
|
|
3340
|
+
applyPlaceholder(bar, cfg2);
|
|
3341
|
+
const widgets = cfg2.runtime?.widgets;
|
|
3342
|
+
if (widgets) {
|
|
3343
|
+
bar.mountInlineWidget = (widgetId, container2, props) => {
|
|
3344
|
+
if (!widgets.has(widgetId)) return null;
|
|
3345
|
+
const handle = widgets.mount(widgetId, container2, props);
|
|
3346
|
+
const element = container2.firstElementChild;
|
|
3347
|
+
if (!element) {
|
|
3348
|
+
handle.unmount?.();
|
|
3349
|
+
return null;
|
|
3350
|
+
}
|
|
3351
|
+
return { element, cleanup: () => handle.unmount?.() };
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3354
|
+
const unsubSession = chatSession.subscribe((s) => {
|
|
3355
|
+
bar.messages = [...s.messages];
|
|
3356
|
+
bar.inFlight = s.inFlight;
|
|
3357
|
+
bar.turn = s.turn;
|
|
3358
|
+
bar.thinkingText = s.thinkingText;
|
|
3359
|
+
});
|
|
3360
|
+
const runtimeEvents = cfg2.runtime ? cfg2.runtime.events : void 0;
|
|
3361
|
+
const refreshReceipts = () => {
|
|
3362
|
+
const store = _elementStore;
|
|
3363
|
+
if (store) {
|
|
3364
|
+
bar.tileReceipts = store.listTileReceipts();
|
|
3365
|
+
bar.inlineWidgets = store.listInlineWidgets();
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
let unsubReceipts;
|
|
3369
|
+
if (runtimeEvents?.subscribe) {
|
|
3370
|
+
unsubReceipts = runtimeEvents.subscribe(
|
|
3371
|
+
{ names: [TILE_MOUNTED_EVENT, TILE_UNMOUNTED_EVENT] },
|
|
3372
|
+
refreshReceipts
|
|
3373
|
+
);
|
|
3374
|
+
}
|
|
3375
|
+
refreshReceipts();
|
|
3376
|
+
const state = { bar, cleanup: () => {
|
|
3377
|
+
}, cfg: cfg2 };
|
|
3378
|
+
const unwireListeners = wireListeners(bar, state);
|
|
3379
|
+
container.appendChild(bar);
|
|
3380
|
+
const unsubFallback = chatTransport.onFallback(() => {
|
|
3381
|
+
bar.remove();
|
|
3382
|
+
container.innerHTML = renderFallbackHtml(state.cfg.fallback);
|
|
3383
|
+
});
|
|
3384
|
+
state.cleanup = () => {
|
|
3385
|
+
unsubSession();
|
|
3386
|
+
unsubFallback();
|
|
3387
|
+
unsubReceipts?.();
|
|
3388
|
+
unwireListeners();
|
|
3389
|
+
bar.remove();
|
|
3390
|
+
setState(container, null);
|
|
3391
|
+
};
|
|
3392
|
+
setState(container, state);
|
|
3393
|
+
return state.cleanup;
|
|
3394
|
+
},
|
|
3395
|
+
update(container, mountConfig) {
|
|
3396
|
+
const state = getState(container);
|
|
3397
|
+
if (!state) return;
|
|
3398
|
+
const cfg2 = mountConfig ?? {};
|
|
3399
|
+
configureTransportIfPossible(cfg2);
|
|
3400
|
+
applyPlaceholder(state.bar, cfg2);
|
|
3401
|
+
state.cfg = cfg2;
|
|
3402
|
+
}
|
|
3403
|
+
};
|
|
3404
|
+
|
|
3405
|
+
// src/AdaptiveChipsStripMountable.ts
|
|
3406
|
+
var STATE_KEY2 = "__syntroChipsStripMount";
|
|
3407
|
+
function getState2(container) {
|
|
3408
|
+
return container[STATE_KEY2] ?? null;
|
|
3409
|
+
}
|
|
3410
|
+
function setState2(container, state) {
|
|
3411
|
+
container[STATE_KEY2] = state;
|
|
3412
|
+
}
|
|
3413
|
+
function applyProps(strip, cfg2) {
|
|
3414
|
+
if (cfg2.chips !== void 0) strip.chips = cfg2.chips;
|
|
3415
|
+
if (cfg2.runtime !== void 0) {
|
|
3416
|
+
strip.runtimeRef = cfg2.runtime;
|
|
3417
|
+
}
|
|
3418
|
+
if (cfg2.chromeless !== void 0) strip.chromeless = cfg2.chromeless;
|
|
3419
|
+
}
|
|
3420
|
+
var AdaptiveChipsStripMountable = {
|
|
3421
|
+
mount(container, mountConfig) {
|
|
3422
|
+
const cfg2 = mountConfig ?? {};
|
|
3423
|
+
const strip = document.createElement("adaptive-chips-strip");
|
|
3424
|
+
applyProps(strip, cfg2);
|
|
3425
|
+
const state = {
|
|
3426
|
+
strip,
|
|
3427
|
+
listeners: {
|
|
3428
|
+
revealed: (e) => {
|
|
3429
|
+
const cb = cfg2.onChipRevealed;
|
|
3430
|
+
if (cb) {
|
|
3431
|
+
cb(
|
|
3432
|
+
e.detail
|
|
3433
|
+
);
|
|
3434
|
+
}
|
|
3435
|
+
},
|
|
3436
|
+
dismissed: (e) => {
|
|
3437
|
+
const cb = cfg2.onChipDismissed;
|
|
3438
|
+
if (cb) cb(e.detail);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
};
|
|
3442
|
+
strip.addEventListener("chip-revealed", state.listeners.revealed);
|
|
3443
|
+
strip.addEventListener("chip-dismissed", state.listeners.dismissed);
|
|
3444
|
+
container.appendChild(strip);
|
|
3445
|
+
setState2(container, state);
|
|
3446
|
+
return () => {
|
|
3447
|
+
strip.removeEventListener("chip-revealed", state.listeners.revealed);
|
|
3448
|
+
strip.removeEventListener("chip-dismissed", state.listeners.dismissed);
|
|
3449
|
+
strip.remove();
|
|
3450
|
+
setState2(container, null);
|
|
3451
|
+
};
|
|
3452
|
+
},
|
|
3453
|
+
update(container, mountConfig) {
|
|
3454
|
+
const state = getState2(container);
|
|
3455
|
+
if (!state) return;
|
|
3456
|
+
const cfg2 = mountConfig ?? {};
|
|
3457
|
+
applyProps(state.strip, cfg2);
|
|
3458
|
+
state.strip.removeEventListener("chip-revealed", state.listeners.revealed);
|
|
3459
|
+
state.strip.removeEventListener("chip-dismissed", state.listeners.dismissed);
|
|
3460
|
+
state.listeners.revealed = (e) => {
|
|
3461
|
+
const cb = cfg2.onChipRevealed;
|
|
3462
|
+
if (cb) {
|
|
3463
|
+
cb(e.detail);
|
|
3464
|
+
}
|
|
3465
|
+
};
|
|
3466
|
+
state.listeners.dismissed = (e) => {
|
|
3467
|
+
const cb = cfg2.onChipDismissed;
|
|
3468
|
+
if (cb) cb(e.detail);
|
|
3469
|
+
};
|
|
3470
|
+
state.strip.addEventListener("chip-revealed", state.listeners.revealed);
|
|
3471
|
+
state.strip.addEventListener("chip-dismissed", state.listeners.dismissed);
|
|
3472
|
+
}
|
|
3473
|
+
};
|
|
3474
|
+
|
|
3182
3475
|
// src/NavLinkMountable.ts
|
|
3183
3476
|
var STATE_KEY3 = "__syntroNavLinkMount";
|
|
3184
3477
|
var SAFE_NAVIGATION_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|