@syntrologie/adapt-chatbot 2.55.1 → 2.56.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 +18 -0
- package/dist/AdaptiveChatBar.d.ts.map +1 -1
- package/dist/AdaptiveChatBar.interleave.test.d.ts +9 -0
- package/dist/AdaptiveChatBar.interleave.test.d.ts.map +1 -0
- package/dist/AdaptiveChatBar.js +4 -4
- package/dist/AdaptiveChatBarMountable.d.ts +28 -17
- package/dist/AdaptiveChatBarMountable.d.ts.map +1 -1
- package/dist/AdaptiveChatTrail.js +3 -3
- package/dist/AdaptiveChipsStrip.d.ts +20 -20
- package/dist/AdaptiveChipsStrip.js +3 -3
- package/dist/ChatAssistantLit.d.ts.map +1 -1
- package/dist/ChatAssistantLit.js +5 -4
- package/dist/ChatTransport.d.ts +6 -1
- package/dist/ChatTransport.d.ts.map +1 -1
- package/dist/cdn.d.ts +1 -0
- package/dist/cdn.d.ts.map +1 -1
- package/dist/chunk-23E6WZOO.js +882 -0
- package/dist/chunk-23E6WZOO.js.map +7 -0
- package/dist/{chunk-EXDL3OKE.js → chunk-7QXC475Y.js} +314 -908
- package/dist/{chunk-EXDL3OKE.js.map → chunk-7QXC475Y.js.map} +4 -4
- package/dist/{chunk-TZA7572J.js → chunk-GN7Z5IVP.js} +2 -2
- package/dist/chunk-LY5AGXVD.js +21 -0
- package/dist/{chunk-ETDHHQS3.js → chunk-NMYFYMJI.js} +12 -12
- package/dist/{chunk-ETDHHQS3.js.map → chunk-NMYFYMJI.js.map} +1 -1
- package/dist/{chunk-LJEPZI6Y.js → chunk-WQIDXGGS.js} +2 -2
- package/dist/{chunk-VFWA5W56.js → chunk-XPUHW2YO.js} +44 -7
- package/dist/chunk-XPUHW2YO.js.map +7 -0
- package/dist/chunk-Z3YR4JFE.js +4702 -0
- package/dist/chunk-Z3YR4JFE.js.map +7 -0
- package/dist/editor-lit.js +1 -1
- package/dist/element-session.d.ts +3 -0
- package/dist/element-session.d.ts.map +1 -0
- package/dist/element-session.js +9 -0
- package/dist/element-session.js.map +7 -0
- package/dist/elements/ElementInstanceStore.d.ts +15 -1
- package/dist/elements/ElementInstanceStore.d.ts.map +1 -1
- package/dist/elements/ElementSessionController.d.ts +37 -0
- package/dist/elements/ElementSessionController.d.ts.map +1 -0
- package/dist/elements/ElementSessionController.test.d.ts +2 -0
- package/dist/elements/ElementSessionController.test.d.ts.map +1 -0
- package/dist/elements/TileHandler.d.ts.map +1 -1
- package/dist/elements/createElementStore.d.ts +11 -0
- package/dist/elements/createElementStore.d.ts.map +1 -0
- package/dist/elements/tileLifecycleProps.d.ts +13 -0
- package/dist/elements/tileLifecycleProps.d.ts.map +1 -0
- package/dist/elements/types.d.ts +7 -0
- package/dist/elements/types.d.ts.map +1 -1
- package/dist/hostAgentController.d.ts +65 -0
- package/dist/hostAgentController.d.ts.map +1 -0
- package/dist/hostAgentController.test.d.ts +2 -0
- package/dist/hostAgentController.test.d.ts.map +1 -0
- package/dist/observer/wire.d.ts +54 -0
- package/dist/observer/wire.d.ts.map +1 -0
- package/dist/observer/wire.test.d.ts +2 -0
- package/dist/observer/wire.test.d.ts.map +1 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +824 -163
- package/dist/runtime.js.map +4 -4
- package/dist/schema.d.ts +66 -66
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +75 -76
- package/dist/schema.js.map +3 -3
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -7
- package/dist/chunk-NGV35N4T.js +0 -474
- package/dist/chunk-NGV35N4T.js.map +0 -7
- package/dist/chunk-UVKRO5ER.js +0 -13
- package/dist/chunk-VFWA5W56.js.map +0 -7
- /package/dist/{chunk-TZA7572J.js.map → chunk-GN7Z5IVP.js.map} +0 -0
- /package/dist/{chunk-UVKRO5ER.js.map → chunk-LY5AGXVD.js.map} +0 -0
- /package/dist/{chunk-LJEPZI6Y.js.map → chunk-WQIDXGGS.js.map} +0 -0
package/dist/runtime.js
CHANGED
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ActionHandler,
|
|
3
2
|
AgUiTransport,
|
|
4
3
|
ChatAssistantLitMountable,
|
|
5
|
-
ElementInstanceStore,
|
|
6
|
-
ItemHandler,
|
|
7
|
-
TileHandler,
|
|
8
4
|
acquireBotCheckTokenForSend,
|
|
9
5
|
decodeMutationEnvelope,
|
|
10
6
|
fetchMountedElements,
|
|
11
7
|
renderFallbackHtml
|
|
12
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7QXC475Y.js";
|
|
9
|
+
import {
|
|
10
|
+
ElementInstanceStore,
|
|
11
|
+
TileHandler,
|
|
12
|
+
createElementSessionController,
|
|
13
|
+
createTemplateWidgetResolver
|
|
14
|
+
} from "./chunk-23E6WZOO.js";
|
|
13
15
|
import {
|
|
14
16
|
pickPersona
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-XPUHW2YO.js";
|
|
16
18
|
import {
|
|
17
19
|
getElementInstanceStore,
|
|
18
20
|
setElementInstanceStore
|
|
19
21
|
} from "./chunk-EWPPVPJ4.js";
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-WQIDXGGS.js";
|
|
23
|
+
import "./chunk-GN7Z5IVP.js";
|
|
24
|
+
import "./chunk-NMYFYMJI.js";
|
|
23
25
|
import {
|
|
24
26
|
DIVE_DEEPER_EVENT,
|
|
25
27
|
TELEMETRY_DOM_EVENT,
|
|
26
28
|
TILE_MOUNTED_EVENT,
|
|
27
29
|
TILE_UNMOUNTED_EVENT,
|
|
28
30
|
isCanonicalBusEvent,
|
|
31
|
+
reportCardStage,
|
|
29
32
|
stripMountPlumbing
|
|
30
|
-
} from "./chunk-
|
|
31
|
-
import "./chunk-
|
|
33
|
+
} from "./chunk-Z3YR4JFE.js";
|
|
34
|
+
import "./chunk-LY5AGXVD.js";
|
|
32
35
|
|
|
33
36
|
// src/ChatSession.ts
|
|
34
37
|
var _userIdCounter = 0;
|
|
@@ -540,6 +543,9 @@ var ChatTransport = class {
|
|
|
540
543
|
this._firstDeltaAt = null;
|
|
541
544
|
this._runMounts = 0;
|
|
542
545
|
this._runCards = 0;
|
|
546
|
+
/** The mount_* tool of every mount this run fired, in order — which surfaces
|
|
547
|
+
* the concierge chose (a carousel on one turn, separate cards on another). */
|
|
548
|
+
this._runMountTools = [];
|
|
543
549
|
/**
|
|
544
550
|
* Count of RUN_FINISHED events the server still owes us that must be
|
|
545
551
|
* swallowed, not reported — one per interrupt. NOT a boolean: an
|
|
@@ -723,6 +729,7 @@ var ChatTransport = class {
|
|
|
723
729
|
this._firstDeltaAt = null;
|
|
724
730
|
this._runMounts = 0;
|
|
725
731
|
this._runCards = 0;
|
|
732
|
+
this._runMountTools = [];
|
|
726
733
|
this._pendingSuppressedFinishes = 0;
|
|
727
734
|
}
|
|
728
735
|
/**
|
|
@@ -984,6 +991,7 @@ var ChatTransport = class {
|
|
|
984
991
|
this._firstDeltaAt = null;
|
|
985
992
|
this._runMounts = 0;
|
|
986
993
|
this._runCards = 0;
|
|
994
|
+
this._runMountTools = [];
|
|
987
995
|
this._reportAtom("syntro_chat_message_sent", { turn, chars: text.length, via });
|
|
988
996
|
void this._forwardUserMessage(text, id);
|
|
989
997
|
}
|
|
@@ -1090,6 +1098,23 @@ var ChatTransport = class {
|
|
|
1090
1098
|
if (!runtime2) return;
|
|
1091
1099
|
const mutations = decodeMutationEnvelope(payload);
|
|
1092
1100
|
if (mutations !== null) {
|
|
1101
|
+
const config = this._config;
|
|
1102
|
+
if (config?.onElementEnvelope) {
|
|
1103
|
+
void config.onElementEnvelope({
|
|
1104
|
+
type: "syntro.element.mutation",
|
|
1105
|
+
schema_version: "v0",
|
|
1106
|
+
mutations
|
|
1107
|
+
}).then(() => {
|
|
1108
|
+
if (this._config === config)
|
|
1109
|
+
runtime2.events.publish("chatbot.element_mutation_applied", {
|
|
1110
|
+
source: "chat-transport",
|
|
1111
|
+
count: mutations.length
|
|
1112
|
+
});
|
|
1113
|
+
}).catch(() => {
|
|
1114
|
+
console.error("[chat-transport] element mutation apply failed");
|
|
1115
|
+
});
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1093
1118
|
const handler = this._config?.onElementMutation;
|
|
1094
1119
|
if (handler) {
|
|
1095
1120
|
try {
|
|
@@ -1308,6 +1333,7 @@ var ChatTransport = class {
|
|
|
1308
1333
|
if (!existing && event.toolCall.name.startsWith("mount_")) {
|
|
1309
1334
|
this._runMounts += 1;
|
|
1310
1335
|
if (/card/i.test(event.toolCall.name)) this._runCards += 1;
|
|
1336
|
+
this._runMountTools.push(event.toolCall.name);
|
|
1311
1337
|
}
|
|
1312
1338
|
if (!existing) {
|
|
1313
1339
|
this._startThinkingCycle(toolNarrationVariants(event.toolCall.name));
|
|
@@ -1405,7 +1431,8 @@ var ChatTransport = class {
|
|
|
1405
1431
|
ttft_ms: (this._firstDeltaAt ?? end) - this._runStartedAt,
|
|
1406
1432
|
total_ms: end - this._runStartedAt,
|
|
1407
1433
|
mounts: this._runMounts,
|
|
1408
|
-
cards: this._runCards
|
|
1434
|
+
cards: this._runCards,
|
|
1435
|
+
mount_tools: [...this._runMountTools]
|
|
1409
1436
|
});
|
|
1410
1437
|
}
|
|
1411
1438
|
this._runStartedAt = 0;
|
|
@@ -1439,6 +1466,38 @@ function findSmartCanvas() {
|
|
|
1439
1466
|
return find(document);
|
|
1440
1467
|
}
|
|
1441
1468
|
|
|
1469
|
+
// src/observer/bus-translate.ts
|
|
1470
|
+
function eventTypeFromName(name) {
|
|
1471
|
+
switch (name) {
|
|
1472
|
+
case "ui.click":
|
|
1473
|
+
return "click";
|
|
1474
|
+
case "ui.submit":
|
|
1475
|
+
return "submit";
|
|
1476
|
+
case "ui.change":
|
|
1477
|
+
return "change";
|
|
1478
|
+
default:
|
|
1479
|
+
return void 0;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
function busEventToRawEvent(e) {
|
|
1483
|
+
if (!e?.name) return null;
|
|
1484
|
+
const ts = typeof e.ts === "number" ? e.ts : Date.now();
|
|
1485
|
+
if (e.source === "posthog") {
|
|
1486
|
+
const original = e.props?.originalEvent;
|
|
1487
|
+
if (typeof original !== "string" || original.length === 0) return null;
|
|
1488
|
+
const src = e.props ?? {};
|
|
1489
|
+
const properties = { ...src };
|
|
1490
|
+
if (Array.isArray(src.elements)) properties.$elements = src.elements;
|
|
1491
|
+
if (typeof src.tagName === "string") properties.$element_tag_name = src.tagName;
|
|
1492
|
+
if (typeof src.pathname === "string") properties.$pathname = src.pathname;
|
|
1493
|
+
if (typeof src.url === "string") properties.$current_url = src.url;
|
|
1494
|
+
const eventType = eventTypeFromName(e.name);
|
|
1495
|
+
if (eventType) properties.$event_type = eventType;
|
|
1496
|
+
return { event: original, timestamp: ts, properties };
|
|
1497
|
+
}
|
|
1498
|
+
return { event: e.name, timestamp: ts, properties: { ...e.props ?? {} } };
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1442
1501
|
// src/observer/allowlist.ts
|
|
1443
1502
|
var MAX_TEXT_LEN = 200;
|
|
1444
1503
|
var SIGNIFICANT_CLICK_TAGS = /* @__PURE__ */ new Set(["button", "a"]);
|
|
@@ -1941,36 +2000,74 @@ function _makeHandle(owner) {
|
|
|
1941
2000
|
};
|
|
1942
2001
|
}
|
|
1943
2002
|
|
|
1944
|
-
// src/observer/
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
return
|
|
1953
|
-
|
|
1954
|
-
return
|
|
1955
|
-
|
|
2003
|
+
// src/observer/wire.ts
|
|
2004
|
+
var PAGE_LEVEL_KINDS = /* @__PURE__ */ new Set(["view", "scroll", "idle", "hesitation", "hover", "nav"]);
|
|
2005
|
+
function buildObserverEnrich(runtimeRef) {
|
|
2006
|
+
return (ev, raw) => {
|
|
2007
|
+
const extract = runtimeRef.getProductIdentity;
|
|
2008
|
+
if (typeof extract !== "function") return ev;
|
|
2009
|
+
if (ev.kind === "click") {
|
|
2010
|
+
const chain = raw.properties.$elements;
|
|
2011
|
+
if (!Array.isArray(chain)) return ev;
|
|
2012
|
+
const id = extract(chain);
|
|
2013
|
+
if (!id) return ev;
|
|
2014
|
+
return {
|
|
2015
|
+
...ev,
|
|
2016
|
+
product: {
|
|
2017
|
+
handle: id.productHandle,
|
|
2018
|
+
url: id.productUrl,
|
|
2019
|
+
...id.productTitle ? { title: id.productTitle } : {}
|
|
2020
|
+
}
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
if (!ev.product && PAGE_LEVEL_KINDS.has(ev.kind) && typeof location !== "undefined") {
|
|
2024
|
+
const id = extract([{ href: location.pathname }]);
|
|
2025
|
+
if (!id) return ev;
|
|
2026
|
+
return {
|
|
2027
|
+
...ev,
|
|
2028
|
+
product: {
|
|
2029
|
+
handle: id.productHandle,
|
|
2030
|
+
url: id.productUrl,
|
|
2031
|
+
...id.productTitle ? { title: id.productTitle } : {}
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
return ev;
|
|
2036
|
+
};
|
|
1956
2037
|
}
|
|
1957
|
-
function
|
|
1958
|
-
if (!
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
2038
|
+
function wireObserver(runtime2) {
|
|
2039
|
+
if (typeof window === "undefined" || !runtime2?.events?.subscribe) return () => {
|
|
2040
|
+
};
|
|
2041
|
+
const trimmed = (runtime2.backendUrl ?? "").replace(/\/$/, "");
|
|
2042
|
+
const url = trimmed ? `${trimmed}/api/adaptive/observation` : "/api/adaptive/observation";
|
|
2043
|
+
const runtimeId = (method) => {
|
|
2044
|
+
try {
|
|
2045
|
+
const value = runtime2.telemetry?.[method]?.();
|
|
2046
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
2047
|
+
} catch {
|
|
2048
|
+
return null;
|
|
2049
|
+
}
|
|
2050
|
+
};
|
|
2051
|
+
const token = () => typeof runtime2.token === "string" ? runtime2.token : "";
|
|
2052
|
+
const handle = startObserver({
|
|
2053
|
+
url,
|
|
2054
|
+
token,
|
|
2055
|
+
getDistinctId: () => runtimeId("getDistinctId"),
|
|
2056
|
+
getSessionId: () => runtimeId("getSessionId"),
|
|
2057
|
+
enrich: buildObserverEnrich(runtime2)
|
|
2058
|
+
});
|
|
2059
|
+
window.__syntroObserverStats = () => handle.stats();
|
|
2060
|
+
const unsubscribe = runtime2.events.subscribe({}, (event) => {
|
|
2061
|
+
const raw = busEventToRawEvent(event);
|
|
2062
|
+
if (raw) handle.ingest(raw);
|
|
2063
|
+
});
|
|
2064
|
+
let stopped = false;
|
|
2065
|
+
return () => {
|
|
2066
|
+
if (stopped) return;
|
|
2067
|
+
stopped = true;
|
|
2068
|
+
unsubscribe();
|
|
2069
|
+
handle.stop();
|
|
2070
|
+
};
|
|
1974
2071
|
}
|
|
1975
2072
|
|
|
1976
2073
|
// src/support-chat/registry.ts
|
|
@@ -3066,106 +3163,41 @@ function applyPlaceholder(bar, cfg2) {
|
|
|
3066
3163
|
if (typeof cfg2.maximized === "boolean") {
|
|
3067
3164
|
bar.maximized = cfg2.maximized;
|
|
3068
3165
|
}
|
|
3166
|
+
if (cfg2.chatControl !== void 0) bar.chatControl = cfg2.chatControl;
|
|
3167
|
+
if (typeof cfg2.interleaved === "boolean") bar.interleaved = cfg2.interleaved;
|
|
3069
3168
|
}
|
|
3070
|
-
var
|
|
3071
|
-
var
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
}
|
|
3076
|
-
function
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
if (!uiTemplates) return;
|
|
3082
|
-
const tiles = uiTemplates.tiles;
|
|
3083
|
-
if (!Array.isArray(tiles)) return;
|
|
3084
|
-
for (const t of tiles) {
|
|
3085
|
-
const id = typeof t?.id === "string" ? t.id : null;
|
|
3086
|
-
const widget = typeof t?.widget === "string" ? t.widget : null;
|
|
3087
|
-
if (id && widget) _templateWidgetMap.set(id, widget);
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
function getOrCreateElementStore(runtime2) {
|
|
3091
|
-
if (_elementStore) return _elementStore;
|
|
3092
|
-
_elementStore = new ElementInstanceStore({
|
|
3093
|
-
actions: runtime2.actions,
|
|
3094
|
-
// Pass the runtime's event bus so ItemHandler can broadcast
|
|
3095
|
-
// `element.compositional_append` / `_patch` / `_remove` events
|
|
3096
|
-
// to container widgets (chips strip, FAQ accordion, nav tips).
|
|
3097
|
-
events: {
|
|
3098
|
-
publish: runtime2.events.publish.bind(runtime2.events),
|
|
3099
|
-
subscribe: runtime2.events.subscribe?.bind(runtime2.events)
|
|
3100
|
-
},
|
|
3101
|
-
handlers: [new TileHandler(resolveTileWidget), new ActionHandler(), new ItemHandler()],
|
|
3102
|
-
resolveTileWidget
|
|
3103
|
-
});
|
|
3104
|
-
setElementInstanceStore(_elementStore);
|
|
3105
|
-
return _elementStore;
|
|
3169
|
+
var _elementSession = null;
|
|
3170
|
+
var _pendingTemplates;
|
|
3171
|
+
function refreshTemplateWidgetMap(templates) {
|
|
3172
|
+
_pendingTemplates = templates;
|
|
3173
|
+
_elementSession?.configureTemplates(templates);
|
|
3174
|
+
}
|
|
3175
|
+
function getOrCreateElementSession(runtime2) {
|
|
3176
|
+
if (_elementSession) return _elementSession;
|
|
3177
|
+
_elementSession = createElementSessionController({ ...runtime2, templates: _pendingTemplates });
|
|
3178
|
+
setElementInstanceStore(_elementSession.store);
|
|
3179
|
+
return _elementSession;
|
|
3106
3180
|
}
|
|
3107
|
-
var _hydrationStarted = false;
|
|
3108
3181
|
function hydrateOnce(runtime2, backendUrl) {
|
|
3109
|
-
|
|
3110
|
-
_hydrationStarted = true;
|
|
3111
|
-
const store = getOrCreateElementStore(runtime2);
|
|
3182
|
+
const session = getOrCreateElementSession(runtime2);
|
|
3112
3183
|
const trimmed = backendUrl.replace(/\/$/, "");
|
|
3113
3184
|
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
3114
|
-
fetchMountedElements({ endpoint, token: runtime2.token })
|
|
3115
|
-
if (!response) return;
|
|
3116
|
-
void store.hydrate(response.mounted_elements);
|
|
3117
|
-
});
|
|
3118
|
-
}
|
|
3119
|
-
var PAGE_LEVEL_KINDS = /* @__PURE__ */ new Set(["view", "scroll", "idle", "hesitation", "hover", "nav"]);
|
|
3120
|
-
function buildObserverEnrich(runtimeRef) {
|
|
3121
|
-
return (ev, raw) => {
|
|
3122
|
-
const extract = runtimeRef.getProductIdentity;
|
|
3123
|
-
if (typeof extract !== "function") return ev;
|
|
3124
|
-
if (ev.kind === "click") {
|
|
3125
|
-
const chain = raw.properties.$elements;
|
|
3126
|
-
if (!Array.isArray(chain)) return ev;
|
|
3127
|
-
const id = extract(chain);
|
|
3128
|
-
if (!id) return ev;
|
|
3129
|
-
return {
|
|
3130
|
-
...ev,
|
|
3131
|
-
product: {
|
|
3132
|
-
handle: id.productHandle,
|
|
3133
|
-
url: id.productUrl,
|
|
3134
|
-
...id.productTitle ? { title: id.productTitle } : {}
|
|
3135
|
-
}
|
|
3136
|
-
};
|
|
3137
|
-
}
|
|
3138
|
-
if (!ev.product && PAGE_LEVEL_KINDS.has(ev.kind) && typeof location !== "undefined") {
|
|
3139
|
-
const id = extract([{ href: location.pathname }]);
|
|
3140
|
-
if (!id) return ev;
|
|
3141
|
-
return {
|
|
3142
|
-
...ev,
|
|
3143
|
-
product: {
|
|
3144
|
-
handle: id.productHandle,
|
|
3145
|
-
url: id.productUrl,
|
|
3146
|
-
...id.productTitle ? { title: id.productTitle } : {}
|
|
3147
|
-
}
|
|
3148
|
-
};
|
|
3149
|
-
}
|
|
3150
|
-
return ev;
|
|
3151
|
-
};
|
|
3185
|
+
session.hydrateOnce(() => fetchMountedElements({ endpoint, token: runtime2.token }));
|
|
3152
3186
|
}
|
|
3153
|
-
var
|
|
3154
|
-
var _nudgeListener = null;
|
|
3187
|
+
var _removeNudgeListener = null;
|
|
3155
3188
|
function wireNudgeSurface(runtime2, backendUrl) {
|
|
3156
|
-
if (
|
|
3157
|
-
|
|
3158
|
-
_nudgeListener = (e) => {
|
|
3189
|
+
if (_removeNudgeListener || typeof document === "undefined") return;
|
|
3190
|
+
const listener = (e) => {
|
|
3159
3191
|
try {
|
|
3160
3192
|
const detail = e.detail;
|
|
3161
3193
|
const isIntent = detail?.phase === "intent";
|
|
3162
3194
|
if (!isIntent) {
|
|
3163
|
-
const
|
|
3195
|
+
const session = getOrCreateElementSession(runtime2);
|
|
3164
3196
|
const trimmed = backendUrl.replace(/\/$/, "");
|
|
3165
3197
|
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
3166
3198
|
void fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
3167
3199
|
if (!response) return;
|
|
3168
|
-
void
|
|
3200
|
+
void session.hydrateLegacy(response.mounted_elements, response.session_expires_at);
|
|
3169
3201
|
});
|
|
3170
3202
|
}
|
|
3171
3203
|
if (detail?.suppressToast === true) return;
|
|
@@ -3181,7 +3213,11 @@ function wireNudgeSurface(runtime2, backendUrl) {
|
|
|
3181
3213
|
console.warn("[adaptive-chatbot] nudge surface reaction failed", err);
|
|
3182
3214
|
}
|
|
3183
3215
|
};
|
|
3184
|
-
document.addEventListener("syntro:nudge",
|
|
3216
|
+
document.addEventListener("syntro:nudge", listener);
|
|
3217
|
+
_removeNudgeListener = () => {
|
|
3218
|
+
document.removeEventListener("syntro:nudge", listener);
|
|
3219
|
+
_removeNudgeListener = null;
|
|
3220
|
+
};
|
|
3185
3221
|
}
|
|
3186
3222
|
function configureTransportIfPossible(cfg2) {
|
|
3187
3223
|
if (!cfg2.runtime) return;
|
|
@@ -3192,9 +3228,7 @@ function configureTransportIfPossible(cfg2) {
|
|
|
3192
3228
|
const forwardedProps = elementsActive ? { elementsEnabled: true, uiTemplates: cfg2.uiTemplates } : void 0;
|
|
3193
3229
|
if (elementsActive) publishTemplatesIfPossible(backendUrl, cfg2.uiTemplates, cfg2.runtime?.token);
|
|
3194
3230
|
const runtime2 = cfg2.runtime;
|
|
3195
|
-
const
|
|
3196
|
-
void getOrCreateElementStore(runtime2).apply(mutations);
|
|
3197
|
-
} : void 0;
|
|
3231
|
+
const onElementEnvelope = elementsActive ? (envelope) => getOrCreateElementSession(runtime2).applyLegacyEnvelope(envelope) : void 0;
|
|
3198
3232
|
if (elementsActive) {
|
|
3199
3233
|
hydrateOnce(runtime2, backendUrl);
|
|
3200
3234
|
}
|
|
@@ -3203,7 +3237,7 @@ function configureTransportIfPossible(cfg2) {
|
|
|
3203
3237
|
...cfg2,
|
|
3204
3238
|
backendUrl,
|
|
3205
3239
|
forwardedProps,
|
|
3206
|
-
|
|
3240
|
+
onElementEnvelope,
|
|
3207
3241
|
activeLidSlot: cfg2._syntroSlotName,
|
|
3208
3242
|
personaId: persona?.id,
|
|
3209
3243
|
personaName: persona?.name,
|
|
@@ -3250,46 +3284,19 @@ function wireDiveDeeperListener() {
|
|
|
3250
3284
|
}
|
|
3251
3285
|
var _observerStarted = false;
|
|
3252
3286
|
var _observerRuntime = null;
|
|
3253
|
-
var
|
|
3254
|
-
var _observerBusUnsubscribe = null;
|
|
3287
|
+
var _observerStop = null;
|
|
3255
3288
|
function startObserverIfPossible(cfg2) {
|
|
3256
3289
|
if (typeof window === "undefined") return;
|
|
3257
3290
|
const runtimeRef = cfg2.runtime;
|
|
3258
3291
|
if (!runtimeRef?.events?.subscribe) return;
|
|
3259
3292
|
if (_observerStarted && _observerRuntime === runtimeRef) return;
|
|
3260
3293
|
if (_observerStarted) {
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
_observerBusUnsubscribe = null;
|
|
3264
|
-
_observerHandle = null;
|
|
3294
|
+
_observerStop?.();
|
|
3295
|
+
_observerStop = null;
|
|
3265
3296
|
_observerStarted = false;
|
|
3266
3297
|
}
|
|
3267
|
-
|
|
3268
|
-
const url = trimmed ? `${trimmed}/api/adaptive/observation` : "/api/adaptive/observation";
|
|
3269
|
-
const runtimeId = (method) => {
|
|
3270
|
-
try {
|
|
3271
|
-
const v = runtimeRef.telemetry?.[method]?.();
|
|
3272
|
-
return typeof v === "string" && v.length > 0 ? v : null;
|
|
3273
|
-
} catch {
|
|
3274
|
-
return null;
|
|
3275
|
-
}
|
|
3276
|
-
};
|
|
3277
|
-
const getDistinctId = () => runtimeId("getDistinctId");
|
|
3278
|
-
const getSessionId = () => runtimeId("getSessionId");
|
|
3279
|
-
const token = () => {
|
|
3280
|
-
const t = runtimeRef.token;
|
|
3281
|
-
return typeof t === "string" ? t : "";
|
|
3282
|
-
};
|
|
3283
|
-
const enrich = buildObserverEnrich(runtimeRef);
|
|
3284
|
-
const handle = startObserver({ url, token, getDistinctId, getSessionId, enrich });
|
|
3285
|
-
window.__syntroObserverStats = () => handle.stats();
|
|
3286
|
-
const unsubscribe = runtimeRef.events.subscribe({}, (evt) => {
|
|
3287
|
-
const raw = busEventToRawEvent(evt);
|
|
3288
|
-
if (raw) handle.ingest(raw);
|
|
3289
|
-
});
|
|
3298
|
+
_observerStop = wireObserver(runtimeRef);
|
|
3290
3299
|
_observerRuntime = runtimeRef;
|
|
3291
|
-
_observerHandle = handle;
|
|
3292
|
-
_observerBusUnsubscribe = unsubscribe;
|
|
3293
3300
|
_observerStarted = true;
|
|
3294
3301
|
}
|
|
3295
3302
|
function wireListeners(bar, state) {
|
|
@@ -3358,7 +3365,7 @@ var AdaptiveChatBarMountable = {
|
|
|
3358
3365
|
});
|
|
3359
3366
|
const runtimeEvents = cfg2.runtime ? cfg2.runtime.events : void 0;
|
|
3360
3367
|
const refreshReceipts = () => {
|
|
3361
|
-
const store =
|
|
3368
|
+
const store = _elementSession?.store;
|
|
3362
3369
|
if (store) {
|
|
3363
3370
|
bar.tileReceipts = store.listTileReceipts();
|
|
3364
3371
|
bar.inlineWidgets = store.listInlineWidgets();
|
|
@@ -3471,6 +3478,658 @@ var AdaptiveChipsStripMountable = {
|
|
|
3471
3478
|
}
|
|
3472
3479
|
};
|
|
3473
3480
|
|
|
3481
|
+
// src/hostAgentController.ts
|
|
3482
|
+
var READ_TOOL = "read_brand_view";
|
|
3483
|
+
var MISSING_RESULT_MS = 8e3;
|
|
3484
|
+
var PENDING_MAX_MS = 12e4;
|
|
3485
|
+
var AGENT_TURN_KEY = "syntro/agent-turn";
|
|
3486
|
+
var CARD_HEARTBEAT_PREFIX = "syntro:mcp-card:";
|
|
3487
|
+
var heartbeatPrefix = (sessionRef) => `${CARD_HEARTBEAT_PREFIX}${sessionRef}:`;
|
|
3488
|
+
var CARD_HEARTBEAT_INTERVAL_MS = 2e3;
|
|
3489
|
+
var CARD_SIBLING_WINDOW_MS = 6e3;
|
|
3490
|
+
var CARD_HEARTBEAT_STALE_MS = 6e4;
|
|
3491
|
+
var CARD_SUPERSEDED = "canvas:superseded";
|
|
3492
|
+
var DOCKED_PREFIX = "syntro:mcp-docked:";
|
|
3493
|
+
var DOCKED_FOR_MS = 24 * 60 * 60 * 1e3;
|
|
3494
|
+
function coarsePointer() {
|
|
3495
|
+
try {
|
|
3496
|
+
return typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
3497
|
+
} catch {
|
|
3498
|
+
return false;
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
function cardStorage() {
|
|
3502
|
+
try {
|
|
3503
|
+
return globalThis.localStorage ?? void 0;
|
|
3504
|
+
} catch {
|
|
3505
|
+
return void 0;
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
var STAR_TOOL = "star_card_tile";
|
|
3509
|
+
function decodeStars(value) {
|
|
3510
|
+
if (!record(value)) return void 0;
|
|
3511
|
+
const out = {};
|
|
3512
|
+
for (const [instanceId, star] of Object.entries(value)) {
|
|
3513
|
+
if (nonempty(instanceId) && record(star) && typeof star.starred === "boolean" && typeof star.updatedAt === "string")
|
|
3514
|
+
out[instanceId] = {
|
|
3515
|
+
starred: star.starred,
|
|
3516
|
+
updatedAt: star.updatedAt,
|
|
3517
|
+
...typeof star.changedAt === "string" ? { changedAt: star.changedAt } : {}
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
return out;
|
|
3521
|
+
}
|
|
3522
|
+
function mergeStars(held, incoming) {
|
|
3523
|
+
const out = { ...held ?? {} };
|
|
3524
|
+
for (const [instanceId, next] of Object.entries(incoming)) {
|
|
3525
|
+
const current = out[instanceId];
|
|
3526
|
+
const keepCurrent = current?.changedAt !== void 0 && (next.changedAt === void 0 || current.changedAt > next.changedAt);
|
|
3527
|
+
if (!keepCurrent) out[instanceId] = next;
|
|
3528
|
+
}
|
|
3529
|
+
return out;
|
|
3530
|
+
}
|
|
3531
|
+
var interactionOwners = /* @__PURE__ */ new WeakMap();
|
|
3532
|
+
function hostUiSubscription(value) {
|
|
3533
|
+
return record(value) && typeof value.subscribeHostUi === "function";
|
|
3534
|
+
}
|
|
3535
|
+
function validSession(value) {
|
|
3536
|
+
if (!record(value) || !nonempty(value.session_ref) || !(value.session_expires_at === null || value.session_expires_at === void 0 || typeof value.session_expires_at === "string" && Number.isFinite(Date.parse(value.session_expires_at))) || !(value.verified_templates === null || value.verified_templates === void 0 || record(value.verified_templates)))
|
|
3537
|
+
return false;
|
|
3538
|
+
const tiles = value.verified_templates?.tiles;
|
|
3539
|
+
return tiles === void 0 || tiles === null || Array.isArray(tiles) && tiles.every((tile) => record(tile) && nonempty(tile.id) && nonempty(tile.widget));
|
|
3540
|
+
}
|
|
3541
|
+
function record(value) {
|
|
3542
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3543
|
+
}
|
|
3544
|
+
function nonempty(value) {
|
|
3545
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
3546
|
+
}
|
|
3547
|
+
function expiry(value) {
|
|
3548
|
+
return value === null || typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
3549
|
+
}
|
|
3550
|
+
function decodeAgentTurn(result) {
|
|
3551
|
+
if (!record(result) || !record(result._meta)) return;
|
|
3552
|
+
const payload = result._meta["syntro/agent-turn"];
|
|
3553
|
+
if (!record(payload)) return;
|
|
3554
|
+
if (payload.version !== 1 && payload.version !== 2) return;
|
|
3555
|
+
const sessionRef = payload.sessionRef ?? payload.sessionId;
|
|
3556
|
+
if (!nonempty(sessionRef) || !nonempty(payload.turnId) || !record(payload.uiTemplates) || !record(payload.snapshot))
|
|
3557
|
+
return;
|
|
3558
|
+
const snapshot = payload.snapshot;
|
|
3559
|
+
if (snapshot.schema_version !== "v0" || !Array.isArray(snapshot.mounted_elements) || !expiry(snapshot.session_expires_at))
|
|
3560
|
+
return;
|
|
3561
|
+
const sessionExpiresAt = payload.sessionExpiresAt ?? snapshot.session_expires_at;
|
|
3562
|
+
if (!expiry(sessionExpiresAt)) return;
|
|
3563
|
+
const tiles = payload.uiTemplates.tiles;
|
|
3564
|
+
if (tiles !== void 0 && (!Array.isArray(tiles) || tiles.some(
|
|
3565
|
+
(template) => !record(template) || !nonempty(template.id) || !nonempty(template.widget)
|
|
3566
|
+
)))
|
|
3567
|
+
return;
|
|
3568
|
+
const templates = createTemplateWidgetResolver();
|
|
3569
|
+
templates.refresh(payload.uiTemplates);
|
|
3570
|
+
for (const candidate of snapshot.mounted_elements) {
|
|
3571
|
+
if (record(candidate) && candidate.element_type === "tile" && candidate.state === "active" && (!nonempty(candidate.template_id) || !templates.resolve(candidate.template_id)))
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
const session = {
|
|
3575
|
+
session_ref: sessionRef,
|
|
3576
|
+
session_expires_at: sessionExpiresAt,
|
|
3577
|
+
verified_templates: payload.uiTemplates
|
|
3578
|
+
};
|
|
3579
|
+
const frame = {
|
|
3580
|
+
schema_version: "v1",
|
|
3581
|
+
kind: "snapshot",
|
|
3582
|
+
session_ref: sessionRef,
|
|
3583
|
+
// The turn id IS the dedup id: the runtime commits one snapshot per
|
|
3584
|
+
// accepted request and names it with that request's id.
|
|
3585
|
+
result_id: payload.turnId,
|
|
3586
|
+
session_expires_at: snapshot.session_expires_at,
|
|
3587
|
+
mounted_elements: snapshot.mounted_elements
|
|
3588
|
+
};
|
|
3589
|
+
const issuedAt = typeof payload.issuedAt === "number" && Number.isFinite(payload.issuedAt) ? payload.issuedAt : void 0;
|
|
3590
|
+
const stars = decodeStars(payload.stars);
|
|
3591
|
+
return {
|
|
3592
|
+
session,
|
|
3593
|
+
frame,
|
|
3594
|
+
...issuedAt !== void 0 ? { issuedAt } : {},
|
|
3595
|
+
...stars ? { stars } : {}
|
|
3596
|
+
};
|
|
3597
|
+
}
|
|
3598
|
+
function activateHostAgentController(runtime2, bridge, hostUi, options = {}) {
|
|
3599
|
+
let controller = createElementSessionController(runtime2);
|
|
3600
|
+
let disposed = false;
|
|
3601
|
+
let pending = Promise.resolve();
|
|
3602
|
+
let session;
|
|
3603
|
+
let generation = 0;
|
|
3604
|
+
const retiredScopes = /* @__PURE__ */ new Set();
|
|
3605
|
+
let stopObserver;
|
|
3606
|
+
let readRequested = false;
|
|
3607
|
+
let applied = 0;
|
|
3608
|
+
const cardId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
3609
|
+
let heartbeatKey;
|
|
3610
|
+
let heartbeatTimer;
|
|
3611
|
+
let popOutDecided = false;
|
|
3612
|
+
let born;
|
|
3613
|
+
let showingEarlyRead = false;
|
|
3614
|
+
let answerIssuedAt;
|
|
3615
|
+
let heartbeatSession;
|
|
3616
|
+
let folded = false;
|
|
3617
|
+
function hostSurface() {
|
|
3618
|
+
try {
|
|
3619
|
+
return runtime2.context?.get().surface ?? {};
|
|
3620
|
+
} catch {
|
|
3621
|
+
return {};
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
function readHeartbeat(key) {
|
|
3625
|
+
try {
|
|
3626
|
+
const raw = cardStorage()?.getItem(key);
|
|
3627
|
+
if (!raw) return;
|
|
3628
|
+
const parsed = JSON.parse(raw);
|
|
3629
|
+
if (!record(parsed) || !nonempty(parsed.id) || typeof parsed.ts !== "number") return;
|
|
3630
|
+
return {
|
|
3631
|
+
id: parsed.id,
|
|
3632
|
+
ts: parsed.ts,
|
|
3633
|
+
mode: typeof parsed.mode === "string" ? parsed.mode : void 0,
|
|
3634
|
+
born: typeof parsed.born === "number" ? parsed.born : void 0
|
|
3635
|
+
};
|
|
3636
|
+
} catch {
|
|
3637
|
+
return;
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
function writeHeartbeat() {
|
|
3641
|
+
if (!heartbeatKey) return;
|
|
3642
|
+
try {
|
|
3643
|
+
cardStorage()?.setItem(
|
|
3644
|
+
heartbeatKey,
|
|
3645
|
+
JSON.stringify({ id: cardId, ts: Date.now(), mode: hostSurface().displayMode, born })
|
|
3646
|
+
);
|
|
3647
|
+
} catch {
|
|
3648
|
+
}
|
|
3649
|
+
considerFold();
|
|
3650
|
+
}
|
|
3651
|
+
function considerFold() {
|
|
3652
|
+
if (folded || disposed || !heartbeatSession || born === void 0) return;
|
|
3653
|
+
const surface = hostSurface();
|
|
3654
|
+
if (surface.type !== "mcp-app" || surface.displayMode !== "inline") return;
|
|
3655
|
+
if (!surface.availableDisplayModes || popsOut(surface, heartbeatSession)) return;
|
|
3656
|
+
const mine = born;
|
|
3657
|
+
if (!freshSiblings(heartbeatSession).some((beat) => (beat.born ?? 0) > mine)) return;
|
|
3658
|
+
folded = true;
|
|
3659
|
+
runtime2.events.publish(CARD_SUPERSEDED, { superseded: true });
|
|
3660
|
+
}
|
|
3661
|
+
function pruneHeartbeats() {
|
|
3662
|
+
try {
|
|
3663
|
+
const store = cardStorage();
|
|
3664
|
+
if (!store) return;
|
|
3665
|
+
const stale = [];
|
|
3666
|
+
for (let index = 0; index < store.length; index++) {
|
|
3667
|
+
const key = store.key(index);
|
|
3668
|
+
if (!key?.startsWith(CARD_HEARTBEAT_PREFIX) || key === heartbeatKey) continue;
|
|
3669
|
+
const beat = readHeartbeat(key);
|
|
3670
|
+
if (!beat || Date.now() - beat.ts > CARD_HEARTBEAT_STALE_MS) stale.push(key);
|
|
3671
|
+
}
|
|
3672
|
+
for (const key of stale) store.removeItem(key);
|
|
3673
|
+
} catch {
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
const releaseHeartbeat = () => stopHeartbeat();
|
|
3677
|
+
const onVisibilityChange = () => {
|
|
3678
|
+
if (!heartbeatKey) return;
|
|
3679
|
+
if (document.visibilityState === "hidden") {
|
|
3680
|
+
if (heartbeatTimer !== void 0) {
|
|
3681
|
+
clearInterval(heartbeatTimer);
|
|
3682
|
+
heartbeatTimer = void 0;
|
|
3683
|
+
}
|
|
3684
|
+
return;
|
|
3685
|
+
}
|
|
3686
|
+
if (heartbeatTimer !== void 0) return;
|
|
3687
|
+
writeHeartbeat();
|
|
3688
|
+
heartbeatTimer = setInterval(writeHeartbeat, CARD_HEARTBEAT_INTERVAL_MS);
|
|
3689
|
+
};
|
|
3690
|
+
let unsubscribeModeWatch;
|
|
3691
|
+
function startHeartbeat(sessionRef) {
|
|
3692
|
+
const key = heartbeatPrefix(sessionRef) + cardId;
|
|
3693
|
+
if (heartbeatKey === key) return;
|
|
3694
|
+
if (heartbeatKey) stopHeartbeat();
|
|
3695
|
+
heartbeatKey = key;
|
|
3696
|
+
heartbeatSession = sessionRef;
|
|
3697
|
+
pruneHeartbeats();
|
|
3698
|
+
writeHeartbeat();
|
|
3699
|
+
heartbeatTimer = setInterval(writeHeartbeat, CARD_HEARTBEAT_INTERVAL_MS);
|
|
3700
|
+
let heartbeatMode = hostSurface().displayMode;
|
|
3701
|
+
try {
|
|
3702
|
+
unsubscribeModeWatch = runtime2.context?.subscribe?.(() => {
|
|
3703
|
+
const mode = hostSurface().displayMode;
|
|
3704
|
+
if (mode === heartbeatMode) return;
|
|
3705
|
+
if (heartbeatMode === "pip" && mode === "inline" && heartbeatSession)
|
|
3706
|
+
rememberDocked(heartbeatSession);
|
|
3707
|
+
heartbeatMode = mode;
|
|
3708
|
+
writeHeartbeat();
|
|
3709
|
+
});
|
|
3710
|
+
} catch {
|
|
3711
|
+
}
|
|
3712
|
+
if (typeof window !== "undefined") {
|
|
3713
|
+
window.addEventListener("pagehide", releaseHeartbeat);
|
|
3714
|
+
window.addEventListener("beforeunload", releaseHeartbeat);
|
|
3715
|
+
}
|
|
3716
|
+
if (typeof document !== "undefined")
|
|
3717
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
3718
|
+
}
|
|
3719
|
+
function stopHeartbeat() {
|
|
3720
|
+
if (heartbeatTimer !== void 0) {
|
|
3721
|
+
clearInterval(heartbeatTimer);
|
|
3722
|
+
heartbeatTimer = void 0;
|
|
3723
|
+
}
|
|
3724
|
+
unsubscribeModeWatch?.();
|
|
3725
|
+
unsubscribeModeWatch = void 0;
|
|
3726
|
+
const key = heartbeatKey;
|
|
3727
|
+
heartbeatKey = void 0;
|
|
3728
|
+
if (typeof window !== "undefined") {
|
|
3729
|
+
window.removeEventListener("pagehide", releaseHeartbeat);
|
|
3730
|
+
window.removeEventListener("beforeunload", releaseHeartbeat);
|
|
3731
|
+
}
|
|
3732
|
+
if (typeof document !== "undefined")
|
|
3733
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
3734
|
+
if (!key) return;
|
|
3735
|
+
try {
|
|
3736
|
+
cardStorage()?.removeItem(key);
|
|
3737
|
+
} catch {
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
function freshSiblings(sessionRef) {
|
|
3741
|
+
const siblings = [];
|
|
3742
|
+
try {
|
|
3743
|
+
const store = cardStorage();
|
|
3744
|
+
if (!store) return siblings;
|
|
3745
|
+
const prefix = heartbeatPrefix(sessionRef);
|
|
3746
|
+
for (let index = 0; index < store.length; index++) {
|
|
3747
|
+
const key = store.key(index);
|
|
3748
|
+
if (!key?.startsWith(prefix)) continue;
|
|
3749
|
+
const beat = readHeartbeat(key);
|
|
3750
|
+
if (!beat || beat.id === cardId) continue;
|
|
3751
|
+
if (Date.now() - beat.ts > CARD_SIBLING_WINDOW_MS) continue;
|
|
3752
|
+
siblings.push(beat);
|
|
3753
|
+
}
|
|
3754
|
+
} catch {
|
|
3755
|
+
return siblings;
|
|
3756
|
+
}
|
|
3757
|
+
return siblings;
|
|
3758
|
+
}
|
|
3759
|
+
function considerPopOut(sessionRef) {
|
|
3760
|
+
if (popOutDecided) return;
|
|
3761
|
+
popOutDecided = true;
|
|
3762
|
+
const siblings = freshSiblings(sessionRef);
|
|
3763
|
+
if (siblings.some((beat) => beat.mode !== "inline")) return;
|
|
3764
|
+
const surface = hostSurface();
|
|
3765
|
+
if (surface.type !== "mcp-app" || surface.displayMode !== "inline") return;
|
|
3766
|
+
if (!popsOut(surface, sessionRef)) return;
|
|
3767
|
+
runtime2.events.publish("canvas:request-display-mode", { mode: "pip" });
|
|
3768
|
+
}
|
|
3769
|
+
function popsOut(surface, sessionRef) {
|
|
3770
|
+
if (!surface.availableDisplayModes?.includes("pip")) return false;
|
|
3771
|
+
if (coarsePointer()) return false;
|
|
3772
|
+
return !shopperDocked(sessionRef);
|
|
3773
|
+
}
|
|
3774
|
+
function shopperDocked(sessionRef) {
|
|
3775
|
+
try {
|
|
3776
|
+
const raw = cardStorage()?.getItem(DOCKED_PREFIX + sessionRef);
|
|
3777
|
+
const at = raw ? Number(raw) : Number.NaN;
|
|
3778
|
+
return Number.isFinite(at) && Date.now() - at < DOCKED_FOR_MS;
|
|
3779
|
+
} catch {
|
|
3780
|
+
return false;
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
function rememberDocked(sessionRef) {
|
|
3784
|
+
try {
|
|
3785
|
+
cardStorage()?.setItem(DOCKED_PREFIX + sessionRef, String(Date.now()));
|
|
3786
|
+
} catch {
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
let interactiveState;
|
|
3790
|
+
function interactive(value) {
|
|
3791
|
+
if (disposed || interactiveState === value) return;
|
|
3792
|
+
interactiveState = value;
|
|
3793
|
+
hostUi?.setInteractive?.(value);
|
|
3794
|
+
runtime2.events.publish("host.ui_state", { interactive: value });
|
|
3795
|
+
}
|
|
3796
|
+
function queue(operation) {
|
|
3797
|
+
const expected = generation;
|
|
3798
|
+
pending = pending.then(async () => {
|
|
3799
|
+
if (!disposed && expected === generation) await operation();
|
|
3800
|
+
}).catch(() => {
|
|
3801
|
+
if (!disposed && expected === generation) {
|
|
3802
|
+
interactive(false);
|
|
3803
|
+
runtime2.events.publish("host.ui_error", { reason: "projection_failed" });
|
|
3804
|
+
}
|
|
3805
|
+
});
|
|
3806
|
+
}
|
|
3807
|
+
function scheduleHostRead(sessionRef, why = "reload") {
|
|
3808
|
+
if (disposed || readRequested || typeof bridge.callServerTool !== "function") return;
|
|
3809
|
+
const call = bridge.callServerTool.bind(bridge);
|
|
3810
|
+
readRequested = true;
|
|
3811
|
+
const expected = generation;
|
|
3812
|
+
const readAgain = () => {
|
|
3813
|
+
if (disposed || why !== "turn_started") return;
|
|
3814
|
+
readRequested = false;
|
|
3815
|
+
if (session) scheduleHostRead(session.session_ref);
|
|
3816
|
+
};
|
|
3817
|
+
void Promise.resolve().then(() => {
|
|
3818
|
+
if (disposed) return;
|
|
3819
|
+
if (expected !== generation) return readAgain();
|
|
3820
|
+
queue(async () => {
|
|
3821
|
+
const appliedAtIssue = applied;
|
|
3822
|
+
reportCardStage("read_brand_view_called", { why });
|
|
3823
|
+
void call({ name: READ_TOOL, arguments: { sessionRef } }).then((result) => {
|
|
3824
|
+
reportReadAnswer(why, result);
|
|
3825
|
+
if (disposed) return;
|
|
3826
|
+
if (expected !== generation) return readAgain();
|
|
3827
|
+
if (applied !== appliedAtIssue) {
|
|
3828
|
+
reportCardStage("tool_result_rejected", { reason: "stale" });
|
|
3829
|
+
return;
|
|
3830
|
+
}
|
|
3831
|
+
return consumeToolResult(result, { earlyRead: why === "turn_started" });
|
|
3832
|
+
}).catch((error) => {
|
|
3833
|
+
reportCardStage("read_brand_view_answered", {
|
|
3834
|
+
why,
|
|
3835
|
+
ok: false,
|
|
3836
|
+
withAgentTurn: false,
|
|
3837
|
+
// The host's own words for the refusal: a read that never
|
|
3838
|
+
// reaches the connector leaves no other record of why.
|
|
3839
|
+
reason: readFailureReason(error instanceof Error ? error.message : String(error))
|
|
3840
|
+
});
|
|
3841
|
+
if (why === "turn_started") return readAgain();
|
|
3842
|
+
if (!disposed && expected === generation)
|
|
3843
|
+
runtime2.events.publish("host.ui_error", { reason: "refresh_required" });
|
|
3844
|
+
});
|
|
3845
|
+
});
|
|
3846
|
+
});
|
|
3847
|
+
}
|
|
3848
|
+
function reportReadAnswer(why, result) {
|
|
3849
|
+
const failed = record(result) && result.isError === true;
|
|
3850
|
+
const text = failed ? firstText(result) : void 0;
|
|
3851
|
+
reportCardStage("read_brand_view_answered", {
|
|
3852
|
+
why,
|
|
3853
|
+
ok: !failed,
|
|
3854
|
+
withAgentTurn: decodeAgentTurn(result) !== void 0,
|
|
3855
|
+
...text ? { reason: readFailureReason(text) } : {}
|
|
3856
|
+
});
|
|
3857
|
+
}
|
|
3858
|
+
const missingAfterMs = options.missingResultMs ?? MISSING_RESULT_MS;
|
|
3859
|
+
let missingTimer = setTimeout(
|
|
3860
|
+
reportMissingResult,
|
|
3861
|
+
missingAfterMs
|
|
3862
|
+
);
|
|
3863
|
+
function standDown() {
|
|
3864
|
+
if (missingTimer !== void 0) clearTimeout(missingTimer);
|
|
3865
|
+
missingTimer = void 0;
|
|
3866
|
+
}
|
|
3867
|
+
function reportMissingResult() {
|
|
3868
|
+
missingTimer = void 0;
|
|
3869
|
+
if (disposed) return;
|
|
3870
|
+
reportCardStage("tool_result_missing", { afterMs: missingAfterMs });
|
|
3871
|
+
}
|
|
3872
|
+
function acceptHostUi2(payload) {
|
|
3873
|
+
if (disposed || !record(payload)) return;
|
|
3874
|
+
if (payload.kind === "session") {
|
|
3875
|
+
if (!validSession(payload.session)) {
|
|
3876
|
+
interactive(false);
|
|
3877
|
+
runtime2.events.publish("host.ui_error", { reason: "invalid_session" });
|
|
3878
|
+
return;
|
|
3879
|
+
}
|
|
3880
|
+
const next = payload.session;
|
|
3881
|
+
standDown();
|
|
3882
|
+
if (retiredScopes.has(next.session_ref)) return;
|
|
3883
|
+
if (session && next.session_ref === session.session_ref) {
|
|
3884
|
+
session = next;
|
|
3885
|
+
controller.configureTemplates(next.verified_templates ?? void 0);
|
|
3886
|
+
interactive(true);
|
|
3887
|
+
return;
|
|
3888
|
+
}
|
|
3889
|
+
if (session) retiredScopes.add(session.session_ref);
|
|
3890
|
+
generation++;
|
|
3891
|
+
const shutdown = controller.dispose();
|
|
3892
|
+
controller = createElementSessionController({
|
|
3893
|
+
...runtime2,
|
|
3894
|
+
templates: next.verified_templates ?? void 0,
|
|
3895
|
+
hostScope: { sessionRef: next.session_ref }
|
|
3896
|
+
});
|
|
3897
|
+
pending = shutdown;
|
|
3898
|
+
session = next;
|
|
3899
|
+
stopObserver ?? (stopObserver = wireObserver(runtime2));
|
|
3900
|
+
interactive(true);
|
|
3901
|
+
reportCardStage("session_bound", { sessionRef: next.session_ref });
|
|
3902
|
+
scheduleHostRead(next.session_ref);
|
|
3903
|
+
return;
|
|
3904
|
+
}
|
|
3905
|
+
if (payload.kind !== "frame") return;
|
|
3906
|
+
const active = controller;
|
|
3907
|
+
const frame = payload.frame;
|
|
3908
|
+
queue(async () => {
|
|
3909
|
+
const outcome = await active.acceptHostFrame(payload.frame);
|
|
3910
|
+
if (active !== controller || disposed) return;
|
|
3911
|
+
if (outcome.status === "rejected") {
|
|
3912
|
+
reportCardStage("tool_result_rejected", { reason: outcome.reason });
|
|
3913
|
+
if (outcome.reason === "projection_failed") interactive(false);
|
|
3914
|
+
runtime2.events.publish("host.ui_error", { reason: outcome.reason });
|
|
3915
|
+
} else if (outcome.status === "duplicate") {
|
|
3916
|
+
reportCardStage("tool_result_rejected", {
|
|
3917
|
+
reason: "duplicate",
|
|
3918
|
+
turnId: outcome.resultId
|
|
3919
|
+
});
|
|
3920
|
+
} else if (outcome.status === "applied") {
|
|
3921
|
+
applied++;
|
|
3922
|
+
const tiles = (Array.isArray(frame?.mounted_elements) ? frame.mounted_elements : []).filter(
|
|
3923
|
+
(element) => element.element_type === "tile" && element.state === "active"
|
|
3924
|
+
);
|
|
3925
|
+
reportCardStage("frame_applied", {
|
|
3926
|
+
turnId: outcome.resultId,
|
|
3927
|
+
tiles: tiles.length,
|
|
3928
|
+
templateIds: [...new Set(tiles.map((element) => element.template_id))].filter((id) => typeof id === "string" && id.length > 0 && id.length <= 80).slice(0, 12)
|
|
3929
|
+
});
|
|
3930
|
+
runtime2.events.publish("host.frame_applied", {
|
|
3931
|
+
result_id: outcome.resultId,
|
|
3932
|
+
tiles: tiles.length
|
|
3933
|
+
});
|
|
3934
|
+
if (born === void 0 && tiles.length > 0 && !showingEarlyRead) {
|
|
3935
|
+
born = answerIssuedAt ?? Date.now();
|
|
3936
|
+
writeHeartbeat();
|
|
3937
|
+
}
|
|
3938
|
+
interactive(true);
|
|
3939
|
+
}
|
|
3940
|
+
});
|
|
3941
|
+
}
|
|
3942
|
+
async function consumeToolResult(result, { earlyRead = false } = {}) {
|
|
3943
|
+
if (disposed) return;
|
|
3944
|
+
const turn = decodeAgentTurn(result);
|
|
3945
|
+
if (!turn) {
|
|
3946
|
+
if (record(result) && record(result._meta) && AGENT_TURN_KEY in result._meta)
|
|
3947
|
+
reportCardStage("tool_result_rejected", { reason: "decode_failed" });
|
|
3948
|
+
return;
|
|
3949
|
+
}
|
|
3950
|
+
showingEarlyRead = earlyRead;
|
|
3951
|
+
if (!earlyRead) {
|
|
3952
|
+
answerIssuedAt ?? (answerIssuedAt = turn.issuedAt ?? Date.now());
|
|
3953
|
+
considerPopOut(turn.session.session_ref);
|
|
3954
|
+
}
|
|
3955
|
+
acceptHostUi2({ kind: "session", session: turn.session });
|
|
3956
|
+
acceptHostUi2({ kind: "frame", frame: turn.frame });
|
|
3957
|
+
startHeartbeat(turn.session.session_ref);
|
|
3958
|
+
boundSessionRef = turn.session.session_ref;
|
|
3959
|
+
if (turn.stars) {
|
|
3960
|
+
knownStars = mergeStars(knownStars, turn.stars);
|
|
3961
|
+
publishStars();
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
let boundSessionRef;
|
|
3965
|
+
let knownStars;
|
|
3966
|
+
function publishStars() {
|
|
3967
|
+
if (disposed || !knownStars) return;
|
|
3968
|
+
runtime2.events.publish("host.stars", { stars: { ...knownStars } });
|
|
3969
|
+
}
|
|
3970
|
+
async function requestStar(props) {
|
|
3971
|
+
const instanceId = props?.instanceId;
|
|
3972
|
+
const starred = props?.starred;
|
|
3973
|
+
if (!nonempty(instanceId) || typeof starred !== "boolean") return;
|
|
3974
|
+
const failed = () => {
|
|
3975
|
+
if (!disposed) runtime2.events.publish("host.star_failed", { instanceId });
|
|
3976
|
+
};
|
|
3977
|
+
if (!boundSessionRef || typeof bridge.callServerTool !== "function") return failed();
|
|
3978
|
+
let result;
|
|
3979
|
+
try {
|
|
3980
|
+
result = await bridge.callServerTool({
|
|
3981
|
+
name: STAR_TOOL,
|
|
3982
|
+
arguments: { sessionRef: boundSessionRef, instanceId, starred }
|
|
3983
|
+
});
|
|
3984
|
+
} catch {
|
|
3985
|
+
return failed();
|
|
3986
|
+
}
|
|
3987
|
+
const stored = record(result) && !result.isError ? result.structuredContent : void 0;
|
|
3988
|
+
if (!record(stored) || stored.instanceId !== instanceId || typeof stored.starred !== "boolean" || typeof stored.updatedAt !== "string")
|
|
3989
|
+
return failed();
|
|
3990
|
+
knownStars = mergeStars(knownStars, {
|
|
3991
|
+
[instanceId]: {
|
|
3992
|
+
starred: stored.starred,
|
|
3993
|
+
updatedAt: stored.updatedAt,
|
|
3994
|
+
...typeof stored.changedAt === "string" ? { changedAt: stored.changedAt } : {}
|
|
3995
|
+
}
|
|
3996
|
+
});
|
|
3997
|
+
publishStars();
|
|
3998
|
+
}
|
|
3999
|
+
const stopStarRequests = runtime2.events.subscribe?.(
|
|
4000
|
+
{ names: ["canvas:star-request", "canvas:stars-request"] },
|
|
4001
|
+
(event) => {
|
|
4002
|
+
if (disposed) return;
|
|
4003
|
+
if (event.name === "canvas:stars-request") publishStars();
|
|
4004
|
+
else void requestStar(event.props);
|
|
4005
|
+
}
|
|
4006
|
+
);
|
|
4007
|
+
const deliver = (result) => {
|
|
4008
|
+
standDown();
|
|
4009
|
+
void consumeToolResult(result);
|
|
4010
|
+
};
|
|
4011
|
+
const stopActivity = bridge.subscribeToolActivity?.(standDown);
|
|
4012
|
+
let turnPending;
|
|
4013
|
+
let pendingTimer;
|
|
4014
|
+
function publishTurnPending(value) {
|
|
4015
|
+
if (disposed || turnPending === value) return;
|
|
4016
|
+
turnPending = value;
|
|
4017
|
+
if (pendingTimer !== void 0) clearTimeout(pendingTimer);
|
|
4018
|
+
pendingTimer = value ? setTimeout(() => publishTurnPending(false), PENDING_MAX_MS) : void 0;
|
|
4019
|
+
runtime2.events.publish("host.turn_pending", { pending: value });
|
|
4020
|
+
}
|
|
4021
|
+
const stopPendingRequests = runtime2.events.subscribe?.(
|
|
4022
|
+
{ names: ["canvas:turn-pending-request"] },
|
|
4023
|
+
() => {
|
|
4024
|
+
if (!disposed && turnPending !== void 0)
|
|
4025
|
+
runtime2.events.publish("host.turn_pending", { pending: turnPending });
|
|
4026
|
+
}
|
|
4027
|
+
);
|
|
4028
|
+
const stopToolCall = bridge.subscribeToolCall?.((call) => {
|
|
4029
|
+
if (disposed) return;
|
|
4030
|
+
if (call.phase === "input") {
|
|
4031
|
+
publishTurnPending(true);
|
|
4032
|
+
const sessionRef = call.arguments.sessionRef;
|
|
4033
|
+
if (nonempty(sessionRef) && !session) scheduleHostRead(sessionRef, "turn_started");
|
|
4034
|
+
return;
|
|
4035
|
+
}
|
|
4036
|
+
void Promise.resolve().then(() => queue(async () => publishTurnPending(false)));
|
|
4037
|
+
});
|
|
4038
|
+
const unsubscribe = bridge.subscribeToolResults(deliver, {
|
|
4039
|
+
metadataKey: AGENT_TURN_KEY
|
|
4040
|
+
});
|
|
4041
|
+
return {
|
|
4042
|
+
acceptHostUi: acceptHostUi2,
|
|
4043
|
+
consumeToolResult,
|
|
4044
|
+
whenIdle: async () => {
|
|
4045
|
+
await pending;
|
|
4046
|
+
await controller.whenIdle();
|
|
4047
|
+
},
|
|
4048
|
+
dispose() {
|
|
4049
|
+
if (disposed) return;
|
|
4050
|
+
disposed = true;
|
|
4051
|
+
generation++;
|
|
4052
|
+
if (missingTimer !== void 0) clearTimeout(missingTimer);
|
|
4053
|
+
missingTimer = void 0;
|
|
4054
|
+
stopActivity?.();
|
|
4055
|
+
stopToolCall?.();
|
|
4056
|
+
stopPendingRequests?.();
|
|
4057
|
+
if (pendingTimer !== void 0) clearTimeout(pendingTimer);
|
|
4058
|
+
stopStarRequests?.();
|
|
4059
|
+
stopHeartbeat();
|
|
4060
|
+
stopObserver?.();
|
|
4061
|
+
stopObserver = void 0;
|
|
4062
|
+
unsubscribe();
|
|
4063
|
+
void controller.dispose();
|
|
4064
|
+
}
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4067
|
+
var READ_REASON_MAX = 120;
|
|
4068
|
+
function readFailureReason(text) {
|
|
4069
|
+
const flat = text.replace(/\s+/g, " ").replace(/[A-Za-z0-9_\-+/=]{24,}/g, "[id]").trim();
|
|
4070
|
+
return flat.length > READ_REASON_MAX ? `${flat.slice(0, READ_REASON_MAX - 1)}~` : flat || "unknown";
|
|
4071
|
+
}
|
|
4072
|
+
function firstText(result) {
|
|
4073
|
+
if (!Array.isArray(result.content)) return void 0;
|
|
4074
|
+
for (const block of result.content) {
|
|
4075
|
+
if (record(block) && block.type === "text" && typeof block.text === "string") return block.text;
|
|
4076
|
+
}
|
|
4077
|
+
return void 0;
|
|
4078
|
+
}
|
|
4079
|
+
function activateHostAgent(runtime2) {
|
|
4080
|
+
if (!record(runtime2) || !record(runtime2.context) || typeof runtime2.context.get !== "function")
|
|
4081
|
+
return;
|
|
4082
|
+
const context = runtime2.context.get();
|
|
4083
|
+
if (!record(context) || !record(context.surface) || context.surface.type !== "mcp-app") return;
|
|
4084
|
+
if (!record(runtime2.actions) || typeof runtime2.actions.apply !== "function" || typeof runtime2.actions.applyBatch !== "function" || !record(runtime2.events) || typeof runtime2.events.publish !== "function" || typeof window === "undefined")
|
|
4085
|
+
return;
|
|
4086
|
+
const host = window.MCPApp?.bridge;
|
|
4087
|
+
if (typeof host?.subscribeToolResults !== "function") return;
|
|
4088
|
+
const receiver = hostUiSubscription(runtime2.hostUi) ? runtime2.hostUi : void 0;
|
|
4089
|
+
const interactionOwner = /* @__PURE__ */ Symbol("host-view");
|
|
4090
|
+
let interactionCanvas = null;
|
|
4091
|
+
let previousInert = false;
|
|
4092
|
+
let previousDisabled = null;
|
|
4093
|
+
const controller = activateHostAgentController(
|
|
4094
|
+
runtime2,
|
|
4095
|
+
host,
|
|
4096
|
+
receiver ? {
|
|
4097
|
+
setInteractive: (value) => {
|
|
4098
|
+
const canvas = findSmartCanvas();
|
|
4099
|
+
if (!canvas) return;
|
|
4100
|
+
if (interactionCanvas !== canvas) {
|
|
4101
|
+
interactionCanvas = canvas;
|
|
4102
|
+
const previous = interactionOwners.get(canvas);
|
|
4103
|
+
previousInert = previous ? previous.inert : canvas.inert;
|
|
4104
|
+
previousDisabled = previous ? previous.disabled : canvas.getAttribute("aria-disabled");
|
|
4105
|
+
interactionOwners.set(canvas, {
|
|
4106
|
+
owner: interactionOwner,
|
|
4107
|
+
inert: previousInert,
|
|
4108
|
+
disabled: previousDisabled
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
if (interactionOwners.get(canvas)?.owner !== interactionOwner) return;
|
|
4112
|
+
canvas.inert = !value || previousInert;
|
|
4113
|
+
canvas.setAttribute("aria-disabled", value ? previousDisabled ?? "false" : "true");
|
|
4114
|
+
}
|
|
4115
|
+
} : void 0
|
|
4116
|
+
);
|
|
4117
|
+
const unsubscribe = receiver?.subscribeHostUi(controller.acceptHostUi);
|
|
4118
|
+
return () => {
|
|
4119
|
+
unsubscribe?.();
|
|
4120
|
+
controller.dispose();
|
|
4121
|
+
if (interactionCanvas && interactionOwners.get(interactionCanvas)?.owner === interactionOwner) {
|
|
4122
|
+
interactionCanvas.inert = previousInert;
|
|
4123
|
+
if (previousDisabled === null) interactionCanvas.removeAttribute("aria-disabled");
|
|
4124
|
+
else interactionCanvas.setAttribute("aria-disabled", previousDisabled);
|
|
4125
|
+
interactionOwners.delete(interactionCanvas);
|
|
4126
|
+
}
|
|
4127
|
+
};
|
|
4128
|
+
}
|
|
4129
|
+
function acceptHostUi(runtime2, payload) {
|
|
4130
|
+
runtime2.acceptHostUi?.(payload);
|
|
4131
|
+
}
|
|
4132
|
+
|
|
3474
4133
|
// src/NavLinkMountable.ts
|
|
3475
4134
|
var STATE_KEY3 = "__syntroNavLinkMount";
|
|
3476
4135
|
var SAFE_NAVIGATION_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
@@ -3655,6 +4314,8 @@ var runtime = {
|
|
|
3655
4314
|
name: "Chat Assistant",
|
|
3656
4315
|
description: "AI chat assistant powered by @syntrologie/chat with SSE transport",
|
|
3657
4316
|
executors: [],
|
|
4317
|
+
onActivate: activateHostAgent,
|
|
4318
|
+
acceptHostUi,
|
|
3658
4319
|
widgets: [
|
|
3659
4320
|
{
|
|
3660
4321
|
id: "adaptive-chatbot:assistant",
|