@qualified-salesforce/web 0.0.1-oidc-bootstrap → 0.1.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/CAPABILITY-REFERENCE.md +44 -0
- package/CHANGELOG.md +75 -0
- package/constants.generated.js +30 -2
- package/css/widget/multimodal_v2/index.split-css.panda.css +265 -47
- package/package.json +17 -3
- package/packs/widget/api/docking/viewport_lock.js +93 -2
- package/packs/widget/esm/index.d.ts +134 -0
- package/packs/widget/esm/index.js +18 -2
- package/packs/widget/esm/qualified.css +265 -47
- package/src/graphql-types.js +74 -1
- package/src/url.js +4 -1
- package/vendor/actioncable/connection.js +40 -3
- package/vendor/actioncable/connection_monitor.js +8 -0
- package/vendor/actioncable/consumer.js +7 -1
- package/vendor/actioncable/index.js +5 -2
- package/vendor/actioncable/sse_socket.js +262 -0
- package/widget/analytics.js +8 -0
- package/widget/api_handler/base_api_handler.js +24 -0
- package/widget/booker_outcome_tracker.js +58 -20
- package/widget/capabilities.js +190 -0
- package/widget/channel.js +38 -6
- package/widget/constants.generated.js +2 -1
- package/widget/multimodal_v2/components/AppLayout.js +1 -1
- package/widget/multimodal_v2/components/app/Foreground.js +10 -9
- package/widget/multimodal_v2/components/app/Input.js +8 -3
- package/widget/multimodal_v2/components/app/LightboxExperience.js +17 -3
- package/widget/multimodal_v2/components/app/Main.js +87 -31
- package/widget/multimodal_v2/components/app/MessengerButton.js +18 -1
- package/widget/multimodal_v2/components/chat/ChatInputForm.js +8 -6
- package/widget/multimodal_v2/components/chat/timeline/CalendarEventPickRenderer.js +2 -2
- package/widget/multimodal_v2/components/chat/timeline/PresentDeckRenderer.js +346 -170
- package/widget/multimodal_v2/components/chat/timeline/SystemRenderer.js +4 -7
- package/widget/multimodal_v2/components/decks/index.js +506 -52
- package/widget/multimodal_v2/components/helpers/content_click_payloads.js +67 -1
- package/widget/multimodal_v2/components/lightbox_gather/index.js +4 -4
- package/widget/multimodal_v2/components/meetings/MeetingBookerEntry.js +8 -1
- package/widget/multimodal_v2/components/meetings/MeetingOfferEmailStep.js +1 -1
- package/widget/multimodal_v2/components/meetings/MeetingSummaryRail.js +1 -1
- package/widget/multimodal_v2/components/meetings/MeetingTimeSelector.js +1 -1
- package/widget/multimodal_v2/components/multimodal/Controls.js +11 -3
- package/widget/multimodal_v2/components/multimodal/useMultimodal.js +19 -7
- package/widget/multimodal_v2/components/presentations/index.js +61 -0
- package/widget/multimodal_v2/constants.js +1 -1
- package/widget/multimodal_v2/images/icons.js +17 -1
- package/widget/multimodal_v2/index.panda.js +2 -2
- package/widget/multimodal_v2/locales/cs.js +8 -1
- package/widget/multimodal_v2/locales/da.js +8 -1
- package/widget/multimodal_v2/locales/de.js +8 -1
- package/widget/multimodal_v2/locales/en.js +8 -1
- package/widget/multimodal_v2/locales/en_AU.js +8 -1
- package/widget/multimodal_v2/locales/en_GB.js +8 -1
- package/widget/multimodal_v2/locales/es.js +8 -1
- package/widget/multimodal_v2/locales/es_LA.js +8 -1
- package/widget/multimodal_v2/locales/fi.js +8 -1
- package/widget/multimodal_v2/locales/fr.js +8 -1
- package/widget/multimodal_v2/locales/fr_CA.js +8 -1
- package/widget/multimodal_v2/locales/id.js +8 -1
- package/widget/multimodal_v2/locales/it.js +8 -1
- package/widget/multimodal_v2/locales/ja.js +8 -1
- package/widget/multimodal_v2/locales/ko.js +8 -1
- package/widget/multimodal_v2/locales/nb.js +8 -1
- package/widget/multimodal_v2/locales/nl.js +8 -1
- package/widget/multimodal_v2/locales/pl.js +8 -1
- package/widget/multimodal_v2/locales/pt_BR.js +8 -1
- package/widget/multimodal_v2/locales/ru.js +8 -1
- package/widget/multimodal_v2/locales/sv.js +8 -1
- package/widget/multimodal_v2/locales/th.js +8 -1
- package/widget/multimodal_v2/locales/tr.js +8 -1
- package/widget/multimodal_v2/locales/vi.js +8 -1
- package/widget/multimodal_v2/locales/zh_CN.js +8 -1
- package/widget/multimodal_v2/locales/zh_TW.js +8 -1
- package/widget/multimodal_v2/multimodal.js +5 -1
- package/widget/multimodal_v2/multimodal_channel.js +13 -8
- package/widget/multimodal_v2/signals/messages_signal.js +4 -0
- package/widget/multimodal_v2/stores/ai_conversation_store.js +6 -2
- package/widget/multimodal_v2/stores/artifact_store.js +352 -27
- package/widget/multimodal_v2/stores/call_store.js +40 -1
- package/widget/multimodal_v2/stores/call_ui_adapter.js +1 -1
- package/widget/multimodal_v2/stores/decks_store.js +405 -39
- package/widget/multimodal_v2/stores/meeting_store.js +15 -10
- package/widget/multimodal_v2/stores/presentation_ga_adapter.js +64 -0
- package/widget/multimodal_v2/stores/presentation_telemetry_store.js +556 -0
- package/widget/multimodal_v2/stores/presentations_store.js +1 -0
- package/widget/multimodal_v2/stores/root_store.js +157 -268
- package/widget/multimodal_v2/stores/screen_share_store.js +41 -21
- package/widget/multimodal_v2/stores/translation_store.js +9 -0
- package/widget/multimodal_v2/stores/ui_store.js +7 -7
- package/widget/multimodal_v2/stores/website_navigation_store.js +3 -1
- package/widget/multimodal_v2/text_turn_response_tracker.js +419 -0
- package/widget/multimodal_v2/types.js +21 -1
- package/widget/multimodal_v2/utils/locale.js +2 -1
- package/widget/multimodal_v2/utils/signals.js +35 -4
- package/widget/pardot_visitor.js +7 -1
- package/widget/server-logger.js +97 -2
- package/widget/tracker.js +69 -21
- package/widget/widget/page_mirror_factory.js +9 -1
- package/widget/widget/widget.js +29 -4
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { __webpack_require__ } from "../../../js/runtime.js";
|
|
2
|
-
import {
|
|
2
|
+
import { signals_core_module_d, signals_core_module_w, signals_core_module_E, signals_core_module_r } from "../../../js/multimodal_v2.js";
|
|
3
|
+
import { getSentryClient } from "../../widget/sentry.js";
|
|
3
4
|
import { BaseStore } from "./base_store.js";
|
|
4
5
|
import "../../utils/native_globals.js";
|
|
5
|
-
import { MaximizedState as types_MaximizedState } from "../types.js";
|
|
6
|
+
import { MaximizedState as types_MaximizedState, StartCallTrigger as types_StartCallTrigger } from "../types.js";
|
|
6
7
|
|
|
7
8
|
/* provided dependency */ var artifact_store_setTimeout = __webpack_require__(68034).pS;
|
|
9
|
+
/* provided dependency */ var artifact_store_clearTimeout = __webpack_require__(68034).Kj;
|
|
10
|
+
function _extends() {
|
|
11
|
+
_extends = Object.assign || function(target) {
|
|
12
|
+
for(var i = 1; i < arguments.length; i++){
|
|
13
|
+
var source = arguments[i];
|
|
14
|
+
for(var key in source){
|
|
15
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
16
|
+
target[key] = source[key];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return target;
|
|
21
|
+
};
|
|
22
|
+
return _extends.apply(this, arguments);
|
|
23
|
+
}
|
|
24
|
+
|
|
8
25
|
|
|
9
26
|
|
|
10
27
|
|
|
28
|
+
// Expiry closes the overlay rather than giving the deck back: handing it back
|
|
29
|
+
// paints the self-guided viewer while Piper's share may still be seconds away.
|
|
30
|
+
const DECK_PRESENTATION_START_TIMEOUT_MS = 30000;
|
|
11
31
|
/**
|
|
12
32
|
* Owns artifact orchestration: presenting slides / presentation videos / the
|
|
13
33
|
* meeting booker in the maximized ARTIFACT overlay, navigating the host page
|
|
@@ -48,7 +68,17 @@ import { MaximizedState as types_MaximizedState } from "../types.js";
|
|
|
48
68
|
this.markPending(message.id);
|
|
49
69
|
try {
|
|
50
70
|
if (delayMs > 0) await new Promise((resolve)=>artifact_store_setTimeout(resolve, delayMs));
|
|
51
|
-
await this.presentArtifactMessage(message
|
|
71
|
+
await this.presentArtifactMessage(message, {
|
|
72
|
+
origin: "incoming"
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
var _getSentryClient;
|
|
76
|
+
(_getSentryClient = getSentryClient({
|
|
77
|
+
project: "multimodal"
|
|
78
|
+
})) == null ? void 0 : _getSentryClient.captureException(error);
|
|
79
|
+
if (message.type === "calendar_event_offer") {
|
|
80
|
+
this.rootStore.reportBookerLoadFailure(message, "chat");
|
|
81
|
+
}
|
|
52
82
|
} finally{
|
|
53
83
|
// Safe to release the slot now: on the way through here the artifact id has
|
|
54
84
|
// been set, so the renderer is already withholding it for that reason
|
|
@@ -64,19 +94,20 @@ import { MaximizedState as types_MaximizedState } from "../types.js";
|
|
|
64
94
|
* Unlike {@link queueArtifactMessage} this leaves the timeline alone, for the
|
|
65
95
|
* visitor asking to (re)open an artifact from a card that is already on screen:
|
|
66
96
|
* hiding it mid-load would blank the spot they just clicked.
|
|
67
|
-
*/ this.presentArtifactMessage = async (message)=>{
|
|
97
|
+
*/ this.presentArtifactMessage = async (message, param)=>{
|
|
98
|
+
let { origin = "revisit" } = param === void 0 ? {} : param;
|
|
68
99
|
switch(message.type){
|
|
69
100
|
case "present_content":
|
|
70
101
|
await this.rootStore.slidesStore.load();
|
|
71
102
|
// Mirror the on-screen slide into the voice agent's perception loop
|
|
72
103
|
// (ScreenShareStore ships it via the update_perception channel action).
|
|
73
104
|
this.loadSlidePerception();
|
|
74
|
-
this.showArtifact(message);
|
|
105
|
+
this.showArtifact(message, origin);
|
|
75
106
|
break;
|
|
76
107
|
case "present_video":
|
|
77
108
|
if (this.rootStore.callInProgress.value) {
|
|
78
109
|
await this.rootStore.presentationsStore.load();
|
|
79
|
-
if (!this.hiddenArtifactIds.has(message.id)) this.showArtifact(message);
|
|
110
|
+
if (!this.hiddenArtifactIds.has(message.id)) this.showArtifact(message, origin);
|
|
80
111
|
}
|
|
81
112
|
break;
|
|
82
113
|
case "present_deck":
|
|
@@ -98,7 +129,7 @@ import { MaximizedState as types_MaximizedState } from "../types.js";
|
|
|
98
129
|
// (ScreenShareStore ships it via the update_perception channel action).
|
|
99
130
|
// Gated with the present so we only mirror a live, on-screen slide.
|
|
100
131
|
this.loadSlidePerception();
|
|
101
|
-
this.showArtifact(message);
|
|
132
|
+
this.showArtifact(message, origin);
|
|
102
133
|
}
|
|
103
134
|
}
|
|
104
135
|
break;
|
|
@@ -107,7 +138,7 @@ import { MaximizedState as types_MaximizedState } from "../types.js";
|
|
|
107
138
|
// element-conversation lightbox is a separate flow; see
|
|
108
139
|
// RootStore#elementConversationOffer.)
|
|
109
140
|
await this.rootStore.meetingStore.load();
|
|
110
|
-
this.showArtifact(message);
|
|
141
|
+
this.showArtifact(message, origin);
|
|
111
142
|
break;
|
|
112
143
|
case "navigate_content":
|
|
113
144
|
// The one kind that never reaches the overlay: it drives the host page
|
|
@@ -124,7 +155,108 @@ import { MaximizedState as types_MaximizedState } from "../types.js";
|
|
|
124
155
|
// is the last share, whose `slides` may not include this slide, so the store
|
|
125
156
|
// can't look the title up from it.
|
|
126
157
|
store.prepareRevisit(message.id, slideIndex, pageTitle);
|
|
127
|
-
this.showArtifact(message);
|
|
158
|
+
this.showArtifact(message, "revisit");
|
|
159
|
+
}, // card. Like revisitDeckSlide this bypasses the call gate above — the visitor
|
|
160
|
+
// asked for it, so it is legal off-call — and goes straight to showArtifact so
|
|
161
|
+
// a deck they dismissed earlier can be reopened.
|
|
162
|
+
//
|
|
163
|
+
// Nothing is reported back: no channel action for the agent and no analytics
|
|
164
|
+
// event, so a visitor reading the deck on their own is invisible to Piper and
|
|
165
|
+
// does not count as a presentation. Known gap, not yet a product decision.
|
|
166
|
+
this.presentSelfGuidedDeck = async (message)=>{
|
|
167
|
+
const store = await this.rootStore.decksStore.load();
|
|
168
|
+
store.prepareSelfGuidedPlayback(message.id);
|
|
169
|
+
this.showArtifact(message, "revisit");
|
|
170
|
+
}, /**
|
|
171
|
+
* Puts the deck the visitor is handing over into its starting state: their
|
|
172
|
+
* slide held on screen, dimmed, with the viewer's controls dropped, while the
|
|
173
|
+
* call that will present it starts (CurrentDeck#starting).
|
|
174
|
+
*
|
|
175
|
+
* Called from RootStore#startCall, which is where both ways into a deck
|
|
176
|
+
* presentation meet — the deck's timeline card and the self-guided viewer's
|
|
177
|
+
* switch to voice both reach it through the call UI adapter — so neither has to
|
|
178
|
+
* know this exists. Nothing happens for any other trigger, `StartPresentation`
|
|
179
|
+
* included: a product demo starts from a card, not from a frame of video the
|
|
180
|
+
* visitor was already watching, so there is nothing of theirs to hold.
|
|
181
|
+
*
|
|
182
|
+
* Routed through `presentSelfGuidedDeck`'s door rather than
|
|
183
|
+
* `presentArtifactMessage`'s, and that is the point: the call does not exist
|
|
184
|
+
* yet, so this is an off-call view of a deck, which is exactly what the
|
|
185
|
+
* self-guided route is for. The guided route's `callInProgress` gate stays as
|
|
186
|
+
* it is — it guards Piper presenting, and she is not presenting yet.
|
|
187
|
+
*
|
|
188
|
+
* Awaited by its caller, so it is written to be safe to wait on: see
|
|
189
|
+
* `loadDecksForHandOff`. It resolves either way and never rejects.
|
|
190
|
+
*/ this.startDeckPresentation = async (trigger)=>{
|
|
191
|
+
var _ref, _ref1;
|
|
192
|
+
if ((trigger == null ? void 0 : trigger.type) !== types_StartCallTrigger.StartDeck) return;
|
|
193
|
+
const store = await this.loadDecksForHandOff();
|
|
194
|
+
if (!store) return;
|
|
195
|
+
// The self-guided viewer is the only thing that puts a deck slide in front of
|
|
196
|
+
// the visitor off-call, so whether it is open is the whole of the question
|
|
197
|
+
// "which slide were they on". Open: the one it is showing, read from the
|
|
198
|
+
// viewer rather than from the trigger so this says what it means. Closed:
|
|
199
|
+
// they were looking at the deck's timeline card, whose preview is the deck's
|
|
200
|
+
// cover, so the cover is where the deck opens.
|
|
201
|
+
const viewer = this.selfGuidedDeckOnScreen(trigger.deckId);
|
|
202
|
+
const message = (_ref = viewer == null ? void 0 : viewer.message) != null ? _ref : this.latestDeckShare(trigger.deckId);
|
|
203
|
+
if (!message) return;
|
|
204
|
+
store.prepareStartingPresentation(message.id, (_ref1 = viewer == null ? void 0 : viewer.currentDeck.currentIndex) != null ? _ref1 : 0);
|
|
205
|
+
// Already the artifact in the switch-to-voice case, where the visitor is
|
|
206
|
+
// looking at this very share: the starting state is then a state change on
|
|
207
|
+
// what is on screen, with nothing to open and nothing to swap. Skipped rather
|
|
208
|
+
// than re-shown so it does not read as a revisit of itself, which is a
|
|
209
|
+
// lifecycle event (showArtifact) and not what happened.
|
|
210
|
+
if (this.current.messageId.peek() !== message.id) {
|
|
211
|
+
this.showArtifact(message, "revisit");
|
|
212
|
+
}
|
|
213
|
+
this.watchDeckPresentation();
|
|
214
|
+
}, /**
|
|
215
|
+
* The decks chunk on the only terms a call start can afford to wait on: a fetch
|
|
216
|
+
* that rejects, or that is still in flight when the hand-off it feeds would have
|
|
217
|
+
* expired anyway, gives up and lets the call start without the overlay.
|
|
218
|
+
*
|
|
219
|
+
* The visitor asked for a call, and the held slide is a courtesy on top of it.
|
|
220
|
+
* Bounded by the hand-off's own deadline rather than a second number, because
|
|
221
|
+
* that is the same wait: a chunk that has not landed by then has no hand-off
|
|
222
|
+
* left to set up.
|
|
223
|
+
*/ this.loadDecksForHandOff = async ()=>{
|
|
224
|
+
let abandon;
|
|
225
|
+
const bound = new Promise((resolve)=>{
|
|
226
|
+
abandon = artifact_store_setTimeout(()=>resolve(null), DECK_PRESENTATION_START_TIMEOUT_MS);
|
|
227
|
+
});
|
|
228
|
+
try {
|
|
229
|
+
return await Promise.race([
|
|
230
|
+
this.rootStore.decksStore.load().catch(()=>null),
|
|
231
|
+
bound
|
|
232
|
+
]);
|
|
233
|
+
} finally{
|
|
234
|
+
artifact_store_clearTimeout(abandon);
|
|
235
|
+
}
|
|
236
|
+
}, /**
|
|
237
|
+
* Gives the deck back to the visitor: the viewer returns, at the slide they
|
|
238
|
+
* handed over, and its controls come back with it.
|
|
239
|
+
*
|
|
240
|
+
* The whole of giving up, because nothing was taken apart to get here — the
|
|
241
|
+
* starting state is a flag over a viewer that stayed exactly where it was. From
|
|
242
|
+
* the timeline card that leaves them in the deck they asked to be shown, able
|
|
243
|
+
* to read it themselves, which is the same place the card's own "view deck"
|
|
244
|
+
* affordance would have put them and a better answer than closing the overlay
|
|
245
|
+
* on a deck they just asked for. Both entry points therefore land in one state,
|
|
246
|
+
* and the messenger behind it holds the "voice and video are temporarily
|
|
247
|
+
* unavailable" notice that explains why nobody is presenting
|
|
248
|
+
* (AiConversationStore#failoverToText).
|
|
249
|
+
*
|
|
250
|
+
* Idempotent, and the only way out that has to be: the other two — Piper's
|
|
251
|
+
* share arriving, the overlay closing — clear the visitor's playback state
|
|
252
|
+
* wholesale and take this with it (DecksStore#clearSelfGuided).
|
|
253
|
+
*
|
|
254
|
+
* For a start that failed, and only that: the wait running out closes the
|
|
255
|
+
* overlay instead, and deliberately does not come through here.
|
|
256
|
+
*/ this.endDeckPresentation = ()=>{
|
|
257
|
+
var _this_rootStore_decksStore_value;
|
|
258
|
+
this.stopWatchingDeckPresentation();
|
|
259
|
+
(_this_rootStore_decksStore_value = this.rootStore.decksStore.value) == null ? void 0 : _this_rootStore_decksStore_value.cancelStartingPresentation();
|
|
128
260
|
}, /**
|
|
129
261
|
* Tears down the overlay for whatever artifact is in it. Deliberately says
|
|
130
262
|
* nothing about the meeting booker: clearing the artifact message is the whole
|
|
@@ -132,37 +264,89 @@ import { MaximizedState as types_MaximizedState } from "../types.js";
|
|
|
132
264
|
* has. Meeting-aware teardown — which also has the element conversation to let
|
|
133
265
|
* go of — belongs to `closeMeetingBooker` and `clearMeetingArtifact`.
|
|
134
266
|
*/ this.clearArtifact = ()=>{
|
|
267
|
+
var _this_rootStore_decksStore_value, _this_rootStore_decksStore_value1;
|
|
135
268
|
const artifactMessage = this.current.message.value;
|
|
136
269
|
const closedArtifact = this.rootStore.maximizedState.value === types_MaximizedState.ARTIFACT && ((artifactMessage == null ? void 0 : artifactMessage.type) === "present_video" || (artifactMessage == null ? void 0 : artifactMessage.type) === "present_deck") ? artifactMessage : null;
|
|
270
|
+
// A deck the visitor was driving themselves is theirs to close. Piper isn't
|
|
271
|
+
// presenting it, so the agent must not hear about it below and apologise for
|
|
272
|
+
// a presentation it never gave.
|
|
273
|
+
const closedSelfGuided = (closedArtifact == null ? void 0 : closedArtifact.type) === "present_deck" && Boolean((_this_rootStore_decksStore_value = this.rootStore.decksStore.value) == null ? void 0 : _this_rootStore_decksStore_value.isSelfGuided(closedArtifact.id));
|
|
274
|
+
// A single slide the visitor pulled back up from the transcript is a review of
|
|
275
|
+
// something already shown, so it reports no progress: the deck message it
|
|
276
|
+
// targets is the latest share, which may already hold the whole run's
|
|
277
|
+
// progress, and a review of slide 2 would overwrite that with slides 0-2.
|
|
278
|
+
const closedRevisit = (closedArtifact == null ? void 0 : closedArtifact.type) === "present_deck" && Boolean((_this_rootStore_decksStore_value1 = this.rootStore.decksStore.value) == null ? void 0 : _this_rootStore_decksStore_value1.isRevisiting(closedArtifact.id));
|
|
279
|
+
// How far the presentation got, read before the teardown below: that clears
|
|
280
|
+
// the overlay's message id, and `currentDeck` is derived from it.
|
|
281
|
+
const closedDeckProgress = (closedArtifact == null ? void 0 : closedArtifact.type) === "present_deck" && !closedSelfGuided && !closedRevisit ? this.deckProgress(closedArtifact) : null;
|
|
137
282
|
if (closedArtifact) {
|
|
138
283
|
this.hiddenArtifactIds.add(closedArtifact.id);
|
|
139
284
|
}
|
|
285
|
+
const presentationMessage = (artifactMessage == null ? void 0 : artifactMessage.type) === "present_content" || (artifactMessage == null ? void 0 : artifactMessage.type) === "present_video" || (artifactMessage == null ? void 0 : artifactMessage.type) === "present_deck" ? artifactMessage : null;
|
|
286
|
+
if (presentationMessage) {
|
|
287
|
+
this.rootStore.presentationTelemetryStore.onVisitorDismiss(presentationMessage.id);
|
|
288
|
+
}
|
|
140
289
|
this.rootStore.clearMaximizedState({
|
|
141
290
|
when: types_MaximizedState.ARTIFACT
|
|
142
291
|
});
|
|
143
292
|
// Closing the presentation overlay mid-call: let the agent know so it stops
|
|
144
293
|
// narrating and acknowledges that the visitor dismissed the presentation.
|
|
145
|
-
if (closedArtifact && this.rootStore.callInProgress.value) {
|
|
146
|
-
this.channel.perform("notify_artifact_closed", {
|
|
294
|
+
if (closedArtifact && !closedSelfGuided && this.rootStore.callInProgress.value) {
|
|
295
|
+
this.channel.perform("notify_artifact_closed", _extends({
|
|
147
296
|
artifact_type: closedArtifact.type === "present_video" ? "presentation" : "deck",
|
|
148
297
|
artifact_message_id: closedArtifact.id
|
|
149
|
-
});
|
|
298
|
+
}, closedDeckProgress != null ? closedDeckProgress : {}));
|
|
150
299
|
}
|
|
151
|
-
}, /**
|
|
300
|
+
}, /**
|
|
301
|
+
* How far a guided deck had got when the visitor closed it: the slide on screen
|
|
302
|
+
* and the sequence it belongs to, reported exactly as a barge-in reports them
|
|
303
|
+
* (DecksStore#notifyInterruption). The server splits the sequence at the
|
|
304
|
+
* on-screen slide to record what was really presented, and clears the deck's
|
|
305
|
+
* `awaiting_narration` in the same write.
|
|
306
|
+
*
|
|
307
|
+
* Deliberately without the barge-in guards (auto-advancing, more than one
|
|
308
|
+
* slide, not already on the last one). Those exist because resuming is all a
|
|
309
|
+
* barge-in reports for, and there is nothing to resume in those cases. A close
|
|
310
|
+
* also has to clear `awaiting_narration`, and a deck left carrying it absorbs
|
|
311
|
+
* the next assistant transcript into itself, marking its whole payload
|
|
312
|
+
* presented (Internal::PyperxController#absorb_deck_narration!) — which is
|
|
313
|
+
* exactly what a single-slide deck or a close on the last slide would hit. So
|
|
314
|
+
* every close reports.
|
|
315
|
+
*/ this.deckProgress = (message)=>{
|
|
316
|
+
var _message_slides;
|
|
152
317
|
var _this_rootStore_decksStore_value;
|
|
318
|
+
const current = (_this_rootStore_decksStore_value = this.rootStore.decksStore.value) == null ? void 0 : _this_rootStore_decksStore_value.currentDeck.value;
|
|
319
|
+
if (!current) return null;
|
|
320
|
+
const sequence = ((_message_slides = message.slides) != null ? _message_slides : []).map((slide)=>slide.slideIndex);
|
|
321
|
+
return {
|
|
322
|
+
deck_id: message.deckId,
|
|
323
|
+
current_slide_index: current.currentIndex,
|
|
324
|
+
// A legacy share carries no `slides`, but it still put one slide on screen.
|
|
325
|
+
// Standing in for the sequence keeps it non-empty, which is what the server
|
|
326
|
+
// needs before it will record anything at all.
|
|
327
|
+
sequence_slide_indexes: sequence.length > 0 ? sequence : [
|
|
328
|
+
current.currentIndex
|
|
329
|
+
]
|
|
330
|
+
};
|
|
331
|
+
}, /** Silently hides the live deck/demo targeted by Piper's hide event. */ this.hideArtifact = (artifactMessageId)=>{
|
|
332
|
+
var _this_rootStore_decksStore_value, _this_rootStore_decksStore_value1;
|
|
153
333
|
// Record the hide before checking the current overlay. The matching artifact
|
|
154
334
|
// may still be waiting on a lazy store; when that wait resolves,
|
|
155
335
|
// presentArtifactMessage checks this set and does not open stale content.
|
|
156
336
|
this.hiddenArtifactIds.add(artifactMessageId);
|
|
157
337
|
const artifactMessage = this.current.message.value;
|
|
338
|
+
// The visitor's own view of the deck — a single revisited slide, or the deck
|
|
339
|
+
// they are stepping through — belongs to them, not to Piper's presentation.
|
|
340
|
+
const isMatchingRevisit = (artifactMessage == null ? void 0 : artifactMessage.id) === artifactMessageId && artifactMessage.type === "present_deck" && Boolean(((_this_rootStore_decksStore_value = this.rootStore.decksStore.value) == null ? void 0 : _this_rootStore_decksStore_value.isRevisiting(artifactMessageId)) || ((_this_rootStore_decksStore_value1 = this.rootStore.decksStore.value) == null ? void 0 : _this_rootStore_decksStore_value1.isSelfGuided(artifactMessageId)));
|
|
341
|
+
if (!isMatchingRevisit) {
|
|
342
|
+
this.rootStore.presentationTelemetryStore.onAgentHide(artifactMessageId);
|
|
343
|
+
}
|
|
158
344
|
if (this.rootStore.maximizedState.value !== types_MaximizedState.ARTIFACT) return;
|
|
159
345
|
if ((artifactMessage == null ? void 0 : artifactMessage.id) !== artifactMessageId) return;
|
|
160
346
|
if (artifactMessage.type !== "present_video" && artifactMessage.type !== "present_deck") return;
|
|
161
347
|
// A visitor may reopen an old deck slide from the timeline after Piper has
|
|
162
348
|
// finished. A late/replayed completion event must not close that review.
|
|
163
|
-
if (
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
349
|
+
if (isMatchingRevisit) return;
|
|
166
350
|
// Do not call clearArtifact: that path represents a visitor dismissal and
|
|
167
351
|
// notifies Piper when a video is closed mid-presentation.
|
|
168
352
|
this.rootStore.clearMaximizedState({
|
|
@@ -184,20 +368,161 @@ import { MaximizedState as types_MaximizedState } from "../types.js";
|
|
|
184
368
|
const pending = new Set(this.pending.peek());
|
|
185
369
|
if (!pending.delete(messageId)) return;
|
|
186
370
|
this.pending.value = pending;
|
|
187
|
-
}, this.
|
|
371
|
+
}, this.deckPresentationWatch = null, this.deckPresentationTimer = null, /** Starts the wait's clock once per hand-off. Not a signal: nothing renders it. */ this.deckPresentationDeadlineArmed = false, /**
|
|
372
|
+
* Whether the deck's starting state is still waiting on the call, as opposed to
|
|
373
|
+
* on the presentation — the boundary between its two messages
|
|
374
|
+
* (components/decks/index.tsx reads this for its text).
|
|
375
|
+
*
|
|
376
|
+
* `isVideoLoading` because that is the signal the call UI's own connecting state
|
|
377
|
+
* is drawn from: useMultimodal's `isConnecting` is exactly this, and it is what
|
|
378
|
+
* decides whether "Connecting you to {name}" — the same string this overlay
|
|
379
|
+
* borrows — is on screen in the multimodal component. Bound to the same signal
|
|
380
|
+
* rather than to one that merely correlates with it, so the two surfaces cannot
|
|
381
|
+
* disagree about whether the call is still coming up. They did: `isAudioConnected`
|
|
382
|
+
* (coffee's `is_audio_ready`, which is a VAD signal) lags the moment the call UI
|
|
383
|
+
* stops saying connecting, and Piper could be heard talking over an overlay that
|
|
384
|
+
* still read "connecting".
|
|
385
|
+
*
|
|
386
|
+
* The second term is the window before there is a call at all, which
|
|
387
|
+
* `isVideoLoading` does not cover on its own: RootStore#startCall raises it in
|
|
388
|
+
* the same tick it opens this overlay, so today no frame ever renders with a
|
|
389
|
+
* call-less deck — but stated rather than relied on, because "no call yet" being
|
|
390
|
+
* part of connecting is a property of this state and not of the order of two
|
|
391
|
+
* statements in another file. It cannot disagree with the call UI either: with no
|
|
392
|
+
* call there is nothing there to call ready. Once `isVideoLoading` has gone false
|
|
393
|
+
* the call object exists by construction (CallStore raises it on coffee's own
|
|
394
|
+
* status), so this term never fires again afterwards.
|
|
395
|
+
*/ this.deckPresentationConnecting = signals_core_module_w(()=>{
|
|
396
|
+
const { state } = this.rootStore.getCallUiAdapter();
|
|
397
|
+
return state.isVideoLoading.value || !state.isInCall.value;
|
|
398
|
+
}), /**
|
|
399
|
+
* The starting state's life is the call start it is waiting on, so that is what
|
|
400
|
+
* is watched rather than any one failure. `isVideoLoading` is the widget's
|
|
401
|
+
* existing "a call is starting" flag (RootStore, raised by
|
|
402
|
+
* AiConversationStore#startConversation) and `isInCall` is the call itself, so
|
|
403
|
+
* neither being true means the attempt is over and no call came out of it —
|
|
404
|
+
* which is every way a start can fail at once, the room-creation failover behind
|
|
405
|
+
* `failoverToText` included, rather than a list of them that the next failure
|
|
406
|
+
* path would not be on.
|
|
407
|
+
*
|
|
408
|
+
* The same watch starts the wait's clock, off the label's own condition
|
|
409
|
+
* (`deckPresentationConnecting`): the label follows it live, the clock once.
|
|
410
|
+
*/ this.watchDeckPresentation = ()=>{
|
|
411
|
+
this.stopWatchingDeckPresentation();
|
|
412
|
+
// Neither flag is up yet on this first run: startCall opens the overlay before
|
|
413
|
+
// it reaches the store that starts connecting. So the attempt has to be seen
|
|
414
|
+
// to begin before its absence can mean it ended.
|
|
415
|
+
let startSeen = false;
|
|
416
|
+
this.deckPresentationWatch = signals_core_module_E(()=>{
|
|
417
|
+
var _this_rootStore_decksStore_value_currentDeck_value, _this_rootStore_decksStore_value;
|
|
418
|
+
// Ended by something that had no reason to tell this watch: Piper's own
|
|
419
|
+
// share replacing the message, or the overlay closing. Both clear the
|
|
420
|
+
// visitor's playback state and the flag with it
|
|
421
|
+
// (DecksStore#clearSelfGuided), leaving nothing to wait for.
|
|
422
|
+
if (!((_this_rootStore_decksStore_value = this.rootStore.decksStore.value) == null ? void 0 : (_this_rootStore_decksStore_value_currentDeck_value = _this_rootStore_decksStore_value.currentDeck.value) == null ? void 0 : _this_rootStore_decksStore_value_currentDeck_value.starting)) {
|
|
423
|
+
void Promise.resolve().then(this.stopWatchingDeckPresentation);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
// Read through the adapter, which is what already derives these for the call
|
|
427
|
+
// UI, rather than re-deriving them from the call store. Fetched inside the
|
|
428
|
+
// effect because the adapter is built per call and swaps under debug mode;
|
|
429
|
+
// its signals are the stores' own either way.
|
|
430
|
+
const { state } = this.rootStore.getCallUiAdapter();
|
|
431
|
+
const starting = state.isVideoLoading.value;
|
|
432
|
+
const inCall = state.isInCall.value;
|
|
433
|
+
// Once, on the first time the call reads as connected: a reconnect renews
|
|
434
|
+
// neither the promise of a presentation nor the deadline on it.
|
|
435
|
+
if (!this.deckPresentationConnecting.value && !this.deckPresentationDeadlineArmed) {
|
|
436
|
+
this.deckPresentationDeadlineArmed = true;
|
|
437
|
+
this.armDeckPresentationTimeout();
|
|
438
|
+
}
|
|
439
|
+
if (starting || inCall) {
|
|
440
|
+
startSeen = true;
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (!startSeen) return;
|
|
444
|
+
// Off the effect deliberately: ending the starting state disposes this very
|
|
445
|
+
// effect, and disposing an effect from inside its own callback is not
|
|
446
|
+
// something @preact/signals promises to survive.
|
|
447
|
+
void Promise.resolve().then(this.endDeckPresentation);
|
|
448
|
+
});
|
|
449
|
+
}, this.armDeckPresentationTimeout = ()=>{
|
|
450
|
+
if (this.deckPresentationTimer !== null) artifact_store_clearTimeout(this.deckPresentationTimer);
|
|
451
|
+
this.deckPresentationTimer = artifact_store_setTimeout(()=>{
|
|
452
|
+
var _decksStore_currentDeck_peek;
|
|
453
|
+
this.deckPresentationTimer = null;
|
|
454
|
+
// The watch clears this timer a microtask after the state it reads changes,
|
|
455
|
+
// so a fire that beat the clear must not close an arrived presentation.
|
|
456
|
+
const decksStore = this.rootStore.decksStore.peek();
|
|
457
|
+
if (!(decksStore == null ? void 0 : (_decksStore_currentDeck_peek = decksStore.currentDeck.peek()) == null ? void 0 : _decksStore_currentDeck_peek.starting)) return;
|
|
458
|
+
this.stopWatchingDeckPresentation();
|
|
459
|
+
// The message goes unconditionally, not as the close's side effect: it is
|
|
460
|
+
// what clears the flag, through the deck store's own subscription.
|
|
461
|
+
signals_core_module_r(()=>{
|
|
462
|
+
this.rootStore.clearMaximizedState({
|
|
463
|
+
when: types_MaximizedState.ARTIFACT
|
|
464
|
+
});
|
|
465
|
+
this.rootStore.setUIState({
|
|
466
|
+
artifactMessageId: null
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
}, DECK_PRESENTATION_START_TIMEOUT_MS);
|
|
470
|
+
}, /**
|
|
471
|
+
* Stands the watch and its clock down and forgets the phase, without touching
|
|
472
|
+
* the deck. Separate from `endDeckPresentation` because the starting state can
|
|
473
|
+
* end without this store deciding it should — see the effect above.
|
|
474
|
+
*/ this.stopWatchingDeckPresentation = ()=>{
|
|
475
|
+
this.deckPresentationWatch == null ? void 0 : this.deckPresentationWatch.call(this);
|
|
476
|
+
this.deckPresentationWatch = null;
|
|
477
|
+
// Every route out of the starting state passes through here, so the next
|
|
478
|
+
// hand-off cannot inherit a deadline the last one already spent.
|
|
479
|
+
this.deckPresentationDeadlineArmed = false;
|
|
480
|
+
if (this.deckPresentationTimer !== null) {
|
|
481
|
+
artifact_store_clearTimeout(this.deckPresentationTimer);
|
|
482
|
+
this.deckPresentationTimer = null;
|
|
483
|
+
}
|
|
484
|
+
}, /** The latest share of a deck that has a card of its own, as its card picks one. */ this.latestDeckShare = (deckId)=>{
|
|
485
|
+
const messages = this.rootStore.messages.peek();
|
|
486
|
+
for(let i = messages.length - 1; i >= 0; i--){
|
|
487
|
+
const message = messages[i];
|
|
488
|
+
if ((message == null ? void 0 : message.type) === "present_deck" && message.deckId === deckId && !message.navigationOnly) {
|
|
489
|
+
return message;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return null;
|
|
493
|
+
}, /** The deck the visitor is stepping through themselves, if that is what is on screen. */ this.selfGuidedDeckOnScreen = (deckId)=>{
|
|
494
|
+
if (this.rootStore.maximizedState.peek() !== types_MaximizedState.ARTIFACT) return null;
|
|
495
|
+
const message = this.current.message.peek();
|
|
496
|
+
if ((message == null ? void 0 : message.type) !== "present_deck" || message.deckId !== deckId) return null;
|
|
497
|
+
const decksStore = this.rootStore.decksStore.value;
|
|
498
|
+
if (!(decksStore == null ? void 0 : decksStore.isSelfGuided(message.id))) return null;
|
|
499
|
+
const currentDeck = decksStore.currentDeck.peek();
|
|
500
|
+
return currentDeck ? {
|
|
501
|
+
message,
|
|
502
|
+
currentDeck
|
|
503
|
+
} : null;
|
|
504
|
+
}, this.showArtifact = (message, origin)=>{
|
|
505
|
+
const replacedMessage = this.current.message.peek();
|
|
506
|
+
const incomingPresentation = origin === "incoming" && (message.type === "present_content" || message.type === "present_video" || message.type === "present_deck");
|
|
507
|
+
if (((replacedMessage == null ? void 0 : replacedMessage.id) !== message.id || origin === "revisit") && !incomingPresentation && ((replacedMessage == null ? void 0 : replacedMessage.type) === "present_content" || (replacedMessage == null ? void 0 : replacedMessage.type) === "present_video" || (replacedMessage == null ? void 0 : replacedMessage.type) === "present_deck")) {
|
|
508
|
+
this.rootStore.presentationTelemetryStore.onArtifactHidden(replacedMessage.id, "replaced");
|
|
509
|
+
}
|
|
188
510
|
if (this.current.messageId.value === message.id) {
|
|
189
511
|
this.rootStore.setMaximizedState(types_MaximizedState.ARTIFACT);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
512
|
+
} else {
|
|
513
|
+
signals_core_module_r(()=>{
|
|
514
|
+
this.rootStore.setUIState({
|
|
515
|
+
artifactMessageId: message.id
|
|
516
|
+
});
|
|
517
|
+
this.rootStore.setMaximizedState(types_MaximizedState.ARTIFACT);
|
|
518
|
+
if (this.rootStore.displayMode.minimized.value && !this.rootStore.conversationMode.video.value) {
|
|
519
|
+
this.rootStore.setOpenedDisplayMode();
|
|
520
|
+
}
|
|
195
521
|
});
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
});
|
|
522
|
+
}
|
|
523
|
+
if (origin === "incoming" && (message.type === "present_content" || message.type === "present_video" || message.type === "present_deck")) {
|
|
524
|
+
this.rootStore.presentationTelemetryStore.onLiveArtifactVisible(message);
|
|
525
|
+
}
|
|
201
526
|
};
|
|
202
527
|
}
|
|
203
528
|
}
|
|
@@ -155,6 +155,14 @@ class CallStore {
|
|
|
155
155
|
if (dailyWsProxyUrl) {
|
|
156
156
|
dailyConfig.proxyUrl = dailyWsProxyUrl;
|
|
157
157
|
}
|
|
158
|
+
// QAI-5864 (deferred): this is where the future "voice" capability guard belongs — an
|
|
159
|
+
// `isCapabilityAllowed("voice")` check gating `createCallObject`/`join` is what stops
|
|
160
|
+
// daily-js from contacting its `c.daily.co` fallback origin. It is a no-op for today's
|
|
161
|
+
// text-only npm package: `setup()` never defines the call-machine URL there, so the
|
|
162
|
+
// `selfHosted && !localBundleUrl` throw above already fires before this line and no Daily
|
|
163
|
+
// connection is established. The guard (plus a "voice" entry in CAPABILITY_KEYS /
|
|
164
|
+
// CAPABILITY_CONFIG_ENABLERS with piperXAgentVoice/piperXAgentAvatar) lands with the voice
|
|
165
|
+
// build, per capabilities.ts.
|
|
158
166
|
this.persistentCallObject = Ha.createCallObject({
|
|
159
167
|
dailyConfig
|
|
160
168
|
});
|
|
@@ -260,6 +268,11 @@ class CallStore {
|
|
|
260
268
|
this.isSilentRestart = signals_core_module_d(false);
|
|
261
269
|
this.voiceGreetingStartTimestamp = null;
|
|
262
270
|
this.isVisitorSpeaking = signals_core_module_d(false);
|
|
271
|
+
this.isBotSpeaking = signals_core_module_d(false);
|
|
272
|
+
// Monotonic Coffee marker emitted on the first audio byte of each distinct
|
|
273
|
+
// realtime response. Unlike isBotSpeaking, this advances even when a tool
|
|
274
|
+
// preamble and its continuation play without an audible silence gap.
|
|
275
|
+
this.voiceAnswerSequence = signals_core_module_d(null);
|
|
263
276
|
this.audioAnalyser = signals_core_module_d(null);
|
|
264
277
|
this.audioContext = null;
|
|
265
278
|
this.currentAudioTrackId = null;
|
|
@@ -382,6 +395,10 @@ class CallStore {
|
|
|
382
395
|
status
|
|
383
396
|
});
|
|
384
397
|
};
|
|
398
|
+
this.endCall = async ()=>{
|
|
399
|
+
this.endPresentationTelemetry();
|
|
400
|
+
await this.aiConversationStore.endConversation();
|
|
401
|
+
};
|
|
385
402
|
this.leaveCall = async ()=>{
|
|
386
403
|
const call = this.dailyCall.value;
|
|
387
404
|
if (!call) return;
|
|
@@ -446,6 +463,7 @@ class CallStore {
|
|
|
446
463
|
this.handleCoffeeStateUpdate = (state)=>{
|
|
447
464
|
this.logCoffeeTimingMetrics(state);
|
|
448
465
|
signals_core_module_r(()=>{
|
|
466
|
+
var _state_voice_answer_seq;
|
|
449
467
|
var _this_perceivedTracker, _this_perceivedTracker1, _this_perceivedTracker2, _this_perceivedTracker3;
|
|
450
468
|
// Participant IDs
|
|
451
469
|
if (state.agent_participant_id) {
|
|
@@ -486,6 +504,8 @@ class CallStore {
|
|
|
486
504
|
}
|
|
487
505
|
// Speaking state
|
|
488
506
|
this.rootStore.setTypingIndicator(state.is_bot_speaking);
|
|
507
|
+
this.isBotSpeaking.value = state.is_bot_speaking;
|
|
508
|
+
this.voiceAnswerSequence.value = (_state_voice_answer_seq = state.voice_answer_seq) != null ? _state_voice_answer_seq : null;
|
|
489
509
|
this.isVisitorSpeaking.value = state.is_user_speaking;
|
|
490
510
|
(_this_perceivedTracker = this.perceivedTracker) == null ? void 0 : _this_perceivedTracker.setUserSpeaking(state.is_user_speaking);
|
|
491
511
|
(_this_perceivedTracker1 = this.perceivedTracker) == null ? void 0 : _this_perceivedTracker1.setBotSpeaking(state.is_bot_speaking);
|
|
@@ -494,6 +514,7 @@ class CallStore {
|
|
|
494
514
|
});
|
|
495
515
|
// Terminal states (outside batch since they're async)
|
|
496
516
|
if (state.is_rep_joined && state.call_status === "ended") {
|
|
517
|
+
this.endPresentationTelemetry();
|
|
497
518
|
this.handleSwitchToText();
|
|
498
519
|
} else if (state.call_status === "restarting") {
|
|
499
520
|
if (this.rootStore.isVideoLoading.value || this.dailyCall.value) {
|
|
@@ -502,11 +523,20 @@ class CallStore {
|
|
|
502
523
|
}
|
|
503
524
|
};
|
|
504
525
|
this.handleCallEnded = async ()=>{
|
|
526
|
+
this.endPresentationTelemetry();
|
|
505
527
|
this.rootStore.setTypingIndicator(false);
|
|
506
528
|
this.teardownAudioAnalyser();
|
|
529
|
+
this.resetRealtimeSpeechState();
|
|
507
530
|
this.teardownPerceivedVoiceMetric();
|
|
508
531
|
await this.aiConversationStore.handleEndResponse();
|
|
509
532
|
};
|
|
533
|
+
this.endPresentationTelemetry = ()=>{
|
|
534
|
+
try {
|
|
535
|
+
this.rootStore.presentationTelemetryStore.onCallEnded();
|
|
536
|
+
} catch (unused) {
|
|
537
|
+
// Presentation telemetry must never prevent call teardown.
|
|
538
|
+
}
|
|
539
|
+
};
|
|
510
540
|
this.setupAudioAnalyser = (call)=>{
|
|
511
541
|
try {
|
|
512
542
|
var _call_participants, _localParticipant_tracks_audio, _localParticipant_tracks;
|
|
@@ -543,6 +573,14 @@ class CallStore {
|
|
|
543
573
|
this.currentAudioTrackId = null;
|
|
544
574
|
this.isVisitorSpeaking.value = false;
|
|
545
575
|
};
|
|
576
|
+
// Coffee owns these signals, not the local microphone analyser. Replacing a
|
|
577
|
+
// Daily audio track tears down and rebuilds the analyser during a live call;
|
|
578
|
+
// clearing Coffee state there would fabricate a silence edge and discard the
|
|
579
|
+
// monotonic response boundary used to synchronize deck narration.
|
|
580
|
+
this.resetRealtimeSpeechState = ()=>{
|
|
581
|
+
this.isBotSpeaking.value = false;
|
|
582
|
+
this.voiceAnswerSequence.value = null;
|
|
583
|
+
};
|
|
546
584
|
this.setupPerceivedVoiceMetric = ()=>{
|
|
547
585
|
if (!this.perceivedMetricEnabled || this.perceivedTracker || this.perceivedMetricUnavailable) return;
|
|
548
586
|
try {
|
|
@@ -699,7 +737,7 @@ class CallStore {
|
|
|
699
737
|
}
|
|
700
738
|
this.isHandlingFatalCallEnd = true;
|
|
701
739
|
try {
|
|
702
|
-
await this.
|
|
740
|
+
await this.endCall();
|
|
703
741
|
} catch (error) {
|
|
704
742
|
var _getSentryClient;
|
|
705
743
|
(_getSentryClient = getSentryClient({
|
|
@@ -726,6 +764,7 @@ class CallStore {
|
|
|
726
764
|
call.off("error", this.handleError);
|
|
727
765
|
this.voiceGreetingStartTimestamp = null;
|
|
728
766
|
this.teardownAudioAnalyser();
|
|
767
|
+
this.resetRealtimeSpeechState();
|
|
729
768
|
this.teardownPerceivedVoiceMetric();
|
|
730
769
|
this.userStoppedSpeakingTimestamp = null;
|
|
731
770
|
this._callConnectedEmitted = false;
|
|
@@ -86,7 +86,7 @@ import { signals_core_module_w } from "../../../js/multimodal_v2.js";
|
|
|
86
86
|
},
|
|
87
87
|
endCall: ()=>{
|
|
88
88
|
var _callStore_value;
|
|
89
|
-
return (_callStore_value = callStore.value) == null ? void 0 : _callStore_value.
|
|
89
|
+
return (_callStore_value = callStore.value) == null ? void 0 : _callStore_value.endCall();
|
|
90
90
|
},
|
|
91
91
|
selectMic: (deviceId)=>{
|
|
92
92
|
var _deviceStore_value;
|