@ringg/react-native 0.0.1-alpha.9 → 0.2.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/index.d.mts +964 -0
- package/dist/index.d.ts +964 -0
- package/dist/index.js +1164 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1129 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +24 -66
- package/LICENSE +0 -21
- package/README.md +0 -250
- package/app.plugin.js +0 -51
- package/lib/module/adapters/index.js +0 -4
- package/lib/module/adapters/index.js.map +0 -1
- package/lib/module/adapters/livekit-rn-adapter.js +0 -216
- package/lib/module/adapters/livekit-rn-adapter.js.map +0 -1
- package/lib/module/components/RinggWidget.js +0 -554
- package/lib/module/components/RinggWidget.js.map +0 -1
- package/lib/module/components/steps/ButtonsStep.js +0 -146
- package/lib/module/components/steps/ButtonsStep.js.map +0 -1
- package/lib/module/components/steps/CalendarStep.js +0 -190
- package/lib/module/components/steps/CalendarStep.js.map +0 -1
- package/lib/module/components/steps/ConfirmationStep.js +0 -175
- package/lib/module/components/steps/ConfirmationStep.js.map +0 -1
- package/lib/module/components/steps/FormStep.js +0 -346
- package/lib/module/components/steps/FormStep.js.map +0 -1
- package/lib/module/components/steps/InteractiveFlow.js +0 -249
- package/lib/module/components/steps/InteractiveFlow.js.map +0 -1
- package/lib/module/components/ui/ActionButton.js +0 -92
- package/lib/module/components/ui/ActionButton.js.map +0 -1
- package/lib/module/components/ui/CallControls.js +0 -168
- package/lib/module/components/ui/CallControls.js.map +0 -1
- package/lib/module/components/ui/FeedbackScreen.js +0 -180
- package/lib/module/components/ui/FeedbackScreen.js.map +0 -1
- package/lib/module/components/ui/Icons.js +0 -294
- package/lib/module/components/ui/Icons.js.map +0 -1
- package/lib/module/components/ui/MessageBubble.js +0 -80
- package/lib/module/components/ui/MessageBubble.js.map +0 -1
- package/lib/module/components/ui/TabSelector.js +0 -80
- package/lib/module/components/ui/TabSelector.js.map +0 -1
- package/lib/module/components/ui/WidgetHeader.js +0 -168
- package/lib/module/components/ui/WidgetHeader.js.map +0 -1
- package/lib/module/context/RinggProvider.js +0 -134
- package/lib/module/context/RinggProvider.js.map +0 -1
- package/lib/module/context/index.js +0 -4
- package/lib/module/context/index.js.map +0 -1
- package/lib/module/globals.d.js +0 -2
- package/lib/module/globals.d.js.map +0 -1
- package/lib/module/hooks/index.js +0 -10
- package/lib/module/hooks/index.js.map +0 -1
- package/lib/module/hooks/useRinggCall.js +0 -259
- package/lib/module/hooks/useRinggCall.js.map +0 -1
- package/lib/module/hooks/useRinggChat.js +0 -141
- package/lib/module/hooks/useRinggChat.js.map +0 -1
- package/lib/module/hooks/useRinggComponents.js +0 -236
- package/lib/module/hooks/useRinggComponents.js.map +0 -1
- package/lib/module/hooks/useRinggFeedback.js +0 -66
- package/lib/module/hooks/useRinggFeedback.js.map +0 -1
- package/lib/module/hooks/useRinggTheme.js +0 -39
- package/lib/module/hooks/useRinggTheme.js.map +0 -1
- package/lib/module/hooks/useRinggTranscripts.js +0 -70
- package/lib/module/hooks/useRinggTranscripts.js.map +0 -1
- package/lib/module/hooks/useRinggWidgetState.js +0 -51
- package/lib/module/hooks/useRinggWidgetState.js.map +0 -1
- package/lib/module/index.js +0 -56
- package/lib/module/index.js.map +0 -1
- package/lib/module/package.json +0 -1
- package/lib/typescript/adapters/index.d.ts +0 -2
- package/lib/typescript/adapters/index.d.ts.map +0 -1
- package/lib/typescript/adapters/livekit-rn-adapter.d.ts +0 -38
- package/lib/typescript/adapters/livekit-rn-adapter.d.ts.map +0 -1
- package/lib/typescript/components/RinggWidget.d.ts +0 -44
- package/lib/typescript/components/RinggWidget.d.ts.map +0 -1
- package/lib/typescript/components/steps/ButtonsStep.d.ts +0 -17
- package/lib/typescript/components/steps/ButtonsStep.d.ts.map +0 -1
- package/lib/typescript/components/steps/CalendarStep.d.ts +0 -16
- package/lib/typescript/components/steps/CalendarStep.d.ts.map +0 -1
- package/lib/typescript/components/steps/ConfirmationStep.d.ts +0 -16
- package/lib/typescript/components/steps/ConfirmationStep.d.ts.map +0 -1
- package/lib/typescript/components/steps/FormStep.d.ts +0 -17
- package/lib/typescript/components/steps/FormStep.d.ts.map +0 -1
- package/lib/typescript/components/steps/InteractiveFlow.d.ts +0 -23
- package/lib/typescript/components/steps/InteractiveFlow.d.ts.map +0 -1
- package/lib/typescript/components/ui/ActionButton.d.ts +0 -22
- package/lib/typescript/components/ui/ActionButton.d.ts.map +0 -1
- package/lib/typescript/components/ui/CallControls.d.ts +0 -22
- package/lib/typescript/components/ui/CallControls.d.ts.map +0 -1
- package/lib/typescript/components/ui/FeedbackScreen.d.ts +0 -24
- package/lib/typescript/components/ui/FeedbackScreen.d.ts.map +0 -1
- package/lib/typescript/components/ui/Icons.d.ts +0 -28
- package/lib/typescript/components/ui/Icons.d.ts.map +0 -1
- package/lib/typescript/components/ui/MessageBubble.d.ts +0 -17
- package/lib/typescript/components/ui/MessageBubble.d.ts.map +0 -1
- package/lib/typescript/components/ui/TabSelector.d.ts +0 -21
- package/lib/typescript/components/ui/TabSelector.d.ts.map +0 -1
- package/lib/typescript/components/ui/WidgetHeader.d.ts +0 -20
- package/lib/typescript/components/ui/WidgetHeader.d.ts.map +0 -1
- package/lib/typescript/context/RinggProvider.d.ts +0 -53
- package/lib/typescript/context/RinggProvider.d.ts.map +0 -1
- package/lib/typescript/context/index.d.ts +0 -3
- package/lib/typescript/context/index.d.ts.map +0 -1
- package/lib/typescript/hooks/index.d.ts +0 -15
- package/lib/typescript/hooks/index.d.ts.map +0 -1
- package/lib/typescript/hooks/useRinggCall.d.ts +0 -41
- package/lib/typescript/hooks/useRinggCall.d.ts.map +0 -1
- package/lib/typescript/hooks/useRinggChat.d.ts +0 -29
- package/lib/typescript/hooks/useRinggChat.d.ts.map +0 -1
- package/lib/typescript/hooks/useRinggComponents.d.ts +0 -52
- package/lib/typescript/hooks/useRinggComponents.d.ts.map +0 -1
- package/lib/typescript/hooks/useRinggFeedback.d.ts +0 -23
- package/lib/typescript/hooks/useRinggFeedback.d.ts.map +0 -1
- package/lib/typescript/hooks/useRinggTheme.d.ts +0 -40
- package/lib/typescript/hooks/useRinggTheme.d.ts.map +0 -1
- package/lib/typescript/hooks/useRinggTranscripts.d.ts +0 -30
- package/lib/typescript/hooks/useRinggTranscripts.d.ts.map +0 -1
- package/lib/typescript/hooks/useRinggWidgetState.d.ts +0 -38
- package/lib/typescript/hooks/useRinggWidgetState.d.ts.map +0 -1
- package/lib/typescript/index.d.ts +0 -44
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/package.json +0 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,1164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
createCallbackEventBus: () => createCallbackEventBus,
|
|
24
|
+
createRinggWidgetController: () => createRinggWidgetController,
|
|
25
|
+
useRinggComponents: () => useRinggComponents,
|
|
26
|
+
useRinggMessages: () => useRinggMessages,
|
|
27
|
+
useRinggSession: () => useRinggSession,
|
|
28
|
+
useRinggShell: () => useRinggShell,
|
|
29
|
+
useRinggSlashCommands: () => useRinggSlashCommands,
|
|
30
|
+
useRinggTyping: () => useRinggTyping,
|
|
31
|
+
useStoreSnapshot: () => useStoreSnapshot
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(src_exports);
|
|
34
|
+
|
|
35
|
+
// ../core/dist/generated/tokens.js
|
|
36
|
+
var DEFAULT_WIDGET_THEME = {
|
|
37
|
+
"primaryColor": "#0a0a0b",
|
|
38
|
+
"primaryTextColor": "#ffffff",
|
|
39
|
+
"backgroundColor": "#ffffff",
|
|
40
|
+
"surfaceColor": "#f3f4f6",
|
|
41
|
+
"agentBubbleColor": "",
|
|
42
|
+
"textColor": "#0a0a0b",
|
|
43
|
+
"mutedTextColor": "#6b7280",
|
|
44
|
+
"borderColor": "#e5e7eb",
|
|
45
|
+
"errorColor": "#ef4444",
|
|
46
|
+
"successColor": "#16a34a",
|
|
47
|
+
"buttonStyle": "pill",
|
|
48
|
+
"borderRadius": "16px",
|
|
49
|
+
"fontFamily": "inherit"
|
|
50
|
+
};
|
|
51
|
+
var TIMING = {
|
|
52
|
+
"chatWidgetGraceMs": 800,
|
|
53
|
+
"chatWidgetAfterTextMs": 120,
|
|
54
|
+
"minTypingIndicatorMs": 500
|
|
55
|
+
};
|
|
56
|
+
var WIDGET_DEFAULTS = {
|
|
57
|
+
"mode": "prod",
|
|
58
|
+
"title": "Ringg AI Support",
|
|
59
|
+
"description": "Ringg AI offers 24/7 voice support to handle your business calls efficiently and professionally",
|
|
60
|
+
"defaultTab": "audio",
|
|
61
|
+
"hideTabSelector": false,
|
|
62
|
+
"defaultExpanded": false,
|
|
63
|
+
"bypassStartScreen": false,
|
|
64
|
+
"bypassFeedbackScreen": false,
|
|
65
|
+
"voiceCallShowAnimation": false,
|
|
66
|
+
"voiceCallShowTranscript": true,
|
|
67
|
+
"notificationTuneUrl": "https://assets.ringg.ai/audios/misc/widget_notification.mp3"
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// ../core/dist/ports/event-bus.js
|
|
71
|
+
function createCallbackEventBus() {
|
|
72
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
73
|
+
return {
|
|
74
|
+
emit(event, payload) {
|
|
75
|
+
listeners.get(event)?.forEach((handler) => handler(payload));
|
|
76
|
+
},
|
|
77
|
+
on(event, handler) {
|
|
78
|
+
let set = listeners.get(event);
|
|
79
|
+
if (!set) {
|
|
80
|
+
set = /* @__PURE__ */ new Set();
|
|
81
|
+
listeners.set(event, set);
|
|
82
|
+
}
|
|
83
|
+
const typed = handler;
|
|
84
|
+
set.add(typed);
|
|
85
|
+
return () => {
|
|
86
|
+
listeners.get(event)?.delete(typed);
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
off(event) {
|
|
90
|
+
if (event) {
|
|
91
|
+
listeners.delete(event);
|
|
92
|
+
} else {
|
|
93
|
+
listeners.clear();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ../core/dist/ports/clock.js
|
|
100
|
+
function createSystemClock() {
|
|
101
|
+
return {
|
|
102
|
+
now() {
|
|
103
|
+
return Date.now();
|
|
104
|
+
},
|
|
105
|
+
setTimeout(fn, ms) {
|
|
106
|
+
return setTimeout(fn, ms);
|
|
107
|
+
},
|
|
108
|
+
clearTimeout(handle) {
|
|
109
|
+
clearTimeout(handle);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ../core/dist/ports/platform.js
|
|
115
|
+
var grantedMicPermission = {
|
|
116
|
+
isGranted: async () => true,
|
|
117
|
+
request: async () => true
|
|
118
|
+
};
|
|
119
|
+
var silentNotificationPlayer = {
|
|
120
|
+
play: () => {
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// ../core/dist/api/client.js
|
|
125
|
+
function createRinggApiClient(config) {
|
|
126
|
+
const { backendUrl, livekitUrl } = config.urlResolver.resolve(config.mode);
|
|
127
|
+
const authHeaders = config.authorization ? { Authorization: config.authorization } : config.xApiKey ? { "X-API-KEY": config.xApiKey } : {};
|
|
128
|
+
const headers = () => ({ "Content-Type": "application/json", ...authHeaders });
|
|
129
|
+
return {
|
|
130
|
+
backendUrl,
|
|
131
|
+
livekitUrl,
|
|
132
|
+
async startWebcall(params) {
|
|
133
|
+
const response = await fetch(`${backendUrl}/calling/webcall`, {
|
|
134
|
+
method: "POST",
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
agent_id: params.agentId,
|
|
137
|
+
custom_args_values: params.variables,
|
|
138
|
+
media_type: params.mediaType,
|
|
139
|
+
// Marks this call as coming from the v2 embed so the backend can use
|
|
140
|
+
// the newer endpoint; absent on older embeds, which fall back.
|
|
141
|
+
embed_version: "v2"
|
|
142
|
+
}),
|
|
143
|
+
headers: headers()
|
|
144
|
+
});
|
|
145
|
+
const responseText = await response.text();
|
|
146
|
+
let data;
|
|
147
|
+
try {
|
|
148
|
+
data = JSON.parse(responseText);
|
|
149
|
+
} catch {
|
|
150
|
+
throw new Error(responseText.slice(0, 200) || `Request failed with status ${response.status}`);
|
|
151
|
+
}
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
throw new Error(data?.message || "Failed to make call");
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
call_id: data.call_id,
|
|
157
|
+
user_token: data.user_token,
|
|
158
|
+
enabled_slash_commands: data.enabled_slash_commands
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
async submitFeedback(callId, feedback) {
|
|
162
|
+
await fetch(`${backendUrl}/calling/${callId}/feedback`, {
|
|
163
|
+
method: "POST",
|
|
164
|
+
body: JSON.stringify(feedback),
|
|
165
|
+
headers: headers()
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
async callComponentApi(method, endpoint, payload) {
|
|
169
|
+
const url = endpoint.startsWith("http") ? endpoint : `${backendUrl}${endpoint}`;
|
|
170
|
+
const response = await fetch(url, {
|
|
171
|
+
method,
|
|
172
|
+
body: method === "GET" ? void 0 : JSON.stringify(payload),
|
|
173
|
+
headers: headers()
|
|
174
|
+
});
|
|
175
|
+
return await response.json();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ../core/dist/theme/theme-engine.js
|
|
181
|
+
function mergeWidgetTheme(theme) {
|
|
182
|
+
return { ...DEFAULT_WIDGET_THEME, ...theme };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ../core/dist/rpc/formatter.js
|
|
186
|
+
function transformRpcToComponent(componentName, rpcPayload) {
|
|
187
|
+
const componentId = `${componentName}_${Date.now()}`;
|
|
188
|
+
const config = rpcPayload.component_config ?? rpcPayload.component_data;
|
|
189
|
+
switch (componentName) {
|
|
190
|
+
case "calendar":
|
|
191
|
+
return transformCalendar(componentId, config, rpcPayload);
|
|
192
|
+
case "form":
|
|
193
|
+
return transformForm(componentId, config);
|
|
194
|
+
case "buttons":
|
|
195
|
+
return transformButtons(componentId, config, rpcPayload);
|
|
196
|
+
case "quick_replay":
|
|
197
|
+
return transformQuickReplay(componentId, config);
|
|
198
|
+
case "confirmation":
|
|
199
|
+
return transformConfirmation(componentId, rpcPayload);
|
|
200
|
+
default:
|
|
201
|
+
if (rpcPayload.component_type && rpcPayload.component_id) {
|
|
202
|
+
return rpcPayload;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function transformCalendar(componentId, config, rpcPayload) {
|
|
208
|
+
const configObj = config;
|
|
209
|
+
const slots = configObj?.slots || [];
|
|
210
|
+
const timezone = configObj?.timezone || "UTC";
|
|
211
|
+
return {
|
|
212
|
+
component_type: "calendar_booking",
|
|
213
|
+
component_id: componentId,
|
|
214
|
+
data: {
|
|
215
|
+
available_slots: slots.map((slot) => ({ id: slot.id, datetime: slot.datetime })),
|
|
216
|
+
timezone,
|
|
217
|
+
title: rpcPayload.title || "Select a time slot"
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function transformForm(componentId, config) {
|
|
222
|
+
const configObj = config;
|
|
223
|
+
const transformedFields = (configObj?.fields || []).map((field) => ({
|
|
224
|
+
name: field.key || field.name || "",
|
|
225
|
+
type: mapFieldType(field.type),
|
|
226
|
+
label: field.description || field.label || field.key || field.name || "",
|
|
227
|
+
required: field.required,
|
|
228
|
+
placeholder: field.placeholder,
|
|
229
|
+
options: field.options
|
|
230
|
+
}));
|
|
231
|
+
return {
|
|
232
|
+
component_type: "form",
|
|
233
|
+
component_id: componentId,
|
|
234
|
+
data: {
|
|
235
|
+
title: configObj?.title || "Form",
|
|
236
|
+
description: configObj?.description,
|
|
237
|
+
fields: transformedFields,
|
|
238
|
+
submit_label: configObj?.cta || configObj?.submit_label || "Submit"
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function transformButtons(componentId, config, rpcPayload) {
|
|
243
|
+
return {
|
|
244
|
+
component_type: "buttons",
|
|
245
|
+
component_id: componentId,
|
|
246
|
+
data: {
|
|
247
|
+
title: rpcPayload.title,
|
|
248
|
+
buttons: Array.isArray(config) ? config : []
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function transformQuickReplay(componentId, config) {
|
|
253
|
+
const configObj = config;
|
|
254
|
+
const options = Array.isArray(configObj?.options) ? configObj.options.filter((option) => typeof option === "string" && option.trim().length > 0) : [];
|
|
255
|
+
return {
|
|
256
|
+
component_type: "buttons",
|
|
257
|
+
component_id: componentId,
|
|
258
|
+
data: {
|
|
259
|
+
presentation: "free",
|
|
260
|
+
completionDisplay: "selected_item",
|
|
261
|
+
buttons: options.map((option) => ({
|
|
262
|
+
id: option,
|
|
263
|
+
label: option,
|
|
264
|
+
action: { type: "trigger_component" }
|
|
265
|
+
}))
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function transformConfirmation(componentId, rpcPayload) {
|
|
270
|
+
return {
|
|
271
|
+
component_type: "confirmation",
|
|
272
|
+
component_id: componentId,
|
|
273
|
+
data: {
|
|
274
|
+
title: rpcPayload.title || "Confirmed",
|
|
275
|
+
message: rpcPayload.message || "",
|
|
276
|
+
icon: rpcPayload.icon || "success",
|
|
277
|
+
details: rpcPayload.details
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function mapFieldType(backendType) {
|
|
282
|
+
const typeMap = {
|
|
283
|
+
text: "text",
|
|
284
|
+
string: "text",
|
|
285
|
+
number: "number",
|
|
286
|
+
email: "email",
|
|
287
|
+
phone_number: "tel",
|
|
288
|
+
single_select: "select",
|
|
289
|
+
multiselect: "multiselect",
|
|
290
|
+
checkbox: "boolean",
|
|
291
|
+
date: "date",
|
|
292
|
+
textarea: "textarea"
|
|
293
|
+
};
|
|
294
|
+
return typeMap[backendType] || "text";
|
|
295
|
+
}
|
|
296
|
+
var RECEIVE_RPC_METHOD = "receive_dynamic_data";
|
|
297
|
+
function formatComponentResponse(componentName, componentId, responseData, originalConfig) {
|
|
298
|
+
let componentData;
|
|
299
|
+
const stepData = responseData.step_data;
|
|
300
|
+
if (componentName === "calendar") {
|
|
301
|
+
const slotId = stepData?.slot_id;
|
|
302
|
+
const configObj = originalConfig;
|
|
303
|
+
const slots = configObj?.slots || [];
|
|
304
|
+
const timezone = configObj?.timezone || "UTC";
|
|
305
|
+
const selectedSlot = slots.find((slot) => slot.id === slotId);
|
|
306
|
+
componentData = {
|
|
307
|
+
id: selectedSlot?.id || slotId,
|
|
308
|
+
datetime: selectedSlot?.datetime || stepData?.slot_datetime,
|
|
309
|
+
timezone,
|
|
310
|
+
slot_time: stepData?.slot_time,
|
|
311
|
+
slot_date: stepData?.slot_date
|
|
312
|
+
};
|
|
313
|
+
} else if (componentName === "form") {
|
|
314
|
+
componentData = responseData.step_data || responseData.all_data;
|
|
315
|
+
} else if (componentName === "buttons") {
|
|
316
|
+
const buttonId = stepData?.selected_button;
|
|
317
|
+
const buttons = Array.isArray(originalConfig) ? originalConfig : [];
|
|
318
|
+
const selectedButton = buttons.find((btn) => btn.id === buttonId);
|
|
319
|
+
componentData = selectedButton || { id: buttonId };
|
|
320
|
+
} else if (componentName === "quick_replay") {
|
|
321
|
+
componentData = { text: stepData?.selected_button };
|
|
322
|
+
} else {
|
|
323
|
+
componentData = responseData;
|
|
324
|
+
}
|
|
325
|
+
const toolId = originalConfig?.tool_id;
|
|
326
|
+
return {
|
|
327
|
+
method: RECEIVE_RPC_METHOD,
|
|
328
|
+
payload: JSON.stringify({
|
|
329
|
+
component_type: `receive_component_${componentName}`,
|
|
330
|
+
component_id: componentId,
|
|
331
|
+
component_data: componentData,
|
|
332
|
+
...toolId && { tool_id: toolId }
|
|
333
|
+
})
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function formatSlashCommand(command) {
|
|
337
|
+
return {
|
|
338
|
+
method: RECEIVE_RPC_METHOD,
|
|
339
|
+
payload: JSON.stringify({
|
|
340
|
+
component_type: "receive_component_slash_command",
|
|
341
|
+
command: command.replace(/^\//, "")
|
|
342
|
+
})
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function formatBlocksAction(toolId, componentId, action) {
|
|
346
|
+
return {
|
|
347
|
+
method: RECEIVE_RPC_METHOD,
|
|
348
|
+
payload: JSON.stringify({
|
|
349
|
+
component_type: "receive_component_blocks",
|
|
350
|
+
component_id: componentId,
|
|
351
|
+
...toolId && { tool_id: toolId },
|
|
352
|
+
component_data: {
|
|
353
|
+
action_id: action.action_id,
|
|
354
|
+
...action.value !== void 0 && { value: action.value },
|
|
355
|
+
values: action.values
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function formatDynamicData(payload) {
|
|
361
|
+
return {
|
|
362
|
+
method: RECEIVE_RPC_METHOD,
|
|
363
|
+
payload: JSON.stringify(payload)
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ../core/dist/stores/store.js
|
|
368
|
+
function createStore(build) {
|
|
369
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
370
|
+
let snapshot = build();
|
|
371
|
+
return {
|
|
372
|
+
getSnapshot: () => snapshot,
|
|
373
|
+
subscribe(listener) {
|
|
374
|
+
listeners.add(listener);
|
|
375
|
+
return () => {
|
|
376
|
+
listeners.delete(listener);
|
|
377
|
+
};
|
|
378
|
+
},
|
|
379
|
+
commit() {
|
|
380
|
+
snapshot = build();
|
|
381
|
+
listeners.forEach((listener) => listener(snapshot));
|
|
382
|
+
},
|
|
383
|
+
dispose() {
|
|
384
|
+
listeners.clear();
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ../core/dist/stores/shell-store.js
|
|
390
|
+
function createShellStore(eventBus, initialCallMode = "audio", defaultExpanded = false) {
|
|
391
|
+
let isOpen = defaultExpanded;
|
|
392
|
+
let feedbackPending = false;
|
|
393
|
+
let currentCallId = null;
|
|
394
|
+
let callMode = initialCallMode;
|
|
395
|
+
const buildViewState = () => {
|
|
396
|
+
if (!isOpen)
|
|
397
|
+
return "closed";
|
|
398
|
+
return feedbackPending ? "feedback" : "open";
|
|
399
|
+
};
|
|
400
|
+
const build = () => ({ viewState: buildViewState(), currentCallId, callMode });
|
|
401
|
+
const handle = createStore(build);
|
|
402
|
+
const store = {
|
|
403
|
+
getSnapshot: handle.getSnapshot,
|
|
404
|
+
subscribe: handle.subscribe,
|
|
405
|
+
dispose: handle.dispose,
|
|
406
|
+
get isOpen() {
|
|
407
|
+
return isOpen;
|
|
408
|
+
},
|
|
409
|
+
get isShowingFeedback() {
|
|
410
|
+
return feedbackPending;
|
|
411
|
+
},
|
|
412
|
+
open() {
|
|
413
|
+
if (isOpen)
|
|
414
|
+
return;
|
|
415
|
+
isOpen = true;
|
|
416
|
+
eventBus.emit("ringg:widget_status", { status: "maximised", mode: callMode });
|
|
417
|
+
handle.commit();
|
|
418
|
+
},
|
|
419
|
+
minimize() {
|
|
420
|
+
if (defaultExpanded)
|
|
421
|
+
return;
|
|
422
|
+
if (!isOpen)
|
|
423
|
+
return;
|
|
424
|
+
isOpen = false;
|
|
425
|
+
eventBus.emit("ringg:widget_status", { status: "minimised", mode: callMode });
|
|
426
|
+
handle.commit();
|
|
427
|
+
},
|
|
428
|
+
close() {
|
|
429
|
+
feedbackPending = false;
|
|
430
|
+
currentCallId = null;
|
|
431
|
+
if (!defaultExpanded) {
|
|
432
|
+
isOpen = false;
|
|
433
|
+
eventBus.emit("ringg:widget_status", { status: "minimised", mode: callMode });
|
|
434
|
+
}
|
|
435
|
+
handle.commit();
|
|
436
|
+
},
|
|
437
|
+
toggle() {
|
|
438
|
+
if (isOpen) {
|
|
439
|
+
store.minimize();
|
|
440
|
+
} else {
|
|
441
|
+
store.open();
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
showFeedback() {
|
|
445
|
+
feedbackPending = true;
|
|
446
|
+
handle.commit();
|
|
447
|
+
},
|
|
448
|
+
setCallMode(mode) {
|
|
449
|
+
if (callMode === mode)
|
|
450
|
+
return;
|
|
451
|
+
callMode = mode;
|
|
452
|
+
handle.commit();
|
|
453
|
+
},
|
|
454
|
+
setCurrentCallId(callId) {
|
|
455
|
+
currentCallId = callId;
|
|
456
|
+
handle.commit();
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
return store;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ../core/dist/stores/session-store.js
|
|
463
|
+
var NO_ERROR = { hasError: false, message: "" };
|
|
464
|
+
var MIC_DENIED_MESSAGE = "Microphone access was denied. Please enable it in your browser settings.";
|
|
465
|
+
var MEDIA_DEVICES_ERROR_MESSAGE = "Error acquiring microphone permission. Please make sure you grant the necessary permissions in your browser and reload the tab";
|
|
466
|
+
function createSessionStore(api, transport, mic) {
|
|
467
|
+
let phase = "idle";
|
|
468
|
+
let isLoading = false;
|
|
469
|
+
let connectionState = "disconnected";
|
|
470
|
+
let error = NO_ERROR;
|
|
471
|
+
const computeIsSessionLive = () => phase === "live-optimistic" || phase === "connected";
|
|
472
|
+
const build = () => ({
|
|
473
|
+
phase,
|
|
474
|
+
isLoading,
|
|
475
|
+
isSessionLive: computeIsSessionLive(),
|
|
476
|
+
connectionState,
|
|
477
|
+
error
|
|
478
|
+
});
|
|
479
|
+
const handle = createStore(build);
|
|
480
|
+
transport.onConnectionStateChange((state) => {
|
|
481
|
+
connectionState = state;
|
|
482
|
+
if (state === "connected" && phase !== "idle")
|
|
483
|
+
phase = "connected";
|
|
484
|
+
handle.commit();
|
|
485
|
+
});
|
|
486
|
+
transport.onMediaDevicesError(() => {
|
|
487
|
+
error = { hasError: true, message: MEDIA_DEVICES_ERROR_MESSAGE };
|
|
488
|
+
handle.commit();
|
|
489
|
+
});
|
|
490
|
+
return {
|
|
491
|
+
getSnapshot: handle.getSnapshot,
|
|
492
|
+
subscribe: handle.subscribe,
|
|
493
|
+
dispose: handle.dispose,
|
|
494
|
+
get isSessionLive() {
|
|
495
|
+
return computeIsSessionLive();
|
|
496
|
+
},
|
|
497
|
+
prewarm() {
|
|
498
|
+
void transport.prepareConnection(api.livekitUrl).catch(() => {
|
|
499
|
+
});
|
|
500
|
+
},
|
|
501
|
+
async start(params) {
|
|
502
|
+
if (params.mediaType === "audio" && !await mic.isGranted()) {
|
|
503
|
+
if (!await mic.request()) {
|
|
504
|
+
error = { hasError: true, message: MIC_DENIED_MESSAGE };
|
|
505
|
+
handle.commit();
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
isLoading = true;
|
|
510
|
+
phase = "starting";
|
|
511
|
+
handle.commit();
|
|
512
|
+
try {
|
|
513
|
+
const response = await api.startWebcall({
|
|
514
|
+
agentId: params.agentId,
|
|
515
|
+
variables: params.variables,
|
|
516
|
+
mediaType: params.mediaType
|
|
517
|
+
});
|
|
518
|
+
phase = "live-optimistic";
|
|
519
|
+
error = NO_ERROR;
|
|
520
|
+
handle.commit();
|
|
521
|
+
await transport.connect(api.livekitUrl, response.user_token);
|
|
522
|
+
if (params.mediaType === "audio")
|
|
523
|
+
await transport.setMicrophoneEnabled(true);
|
|
524
|
+
return {
|
|
525
|
+
callId: response.call_id,
|
|
526
|
+
slashCommands: response.enabled_slash_commands ?? []
|
|
527
|
+
};
|
|
528
|
+
} catch (err) {
|
|
529
|
+
phase = "idle";
|
|
530
|
+
error = { hasError: true, message: err.message };
|
|
531
|
+
handle.commit();
|
|
532
|
+
return null;
|
|
533
|
+
} finally {
|
|
534
|
+
isLoading = false;
|
|
535
|
+
handle.commit();
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
async end() {
|
|
539
|
+
isLoading = true;
|
|
540
|
+
handle.commit();
|
|
541
|
+
try {
|
|
542
|
+
await transport.disconnect();
|
|
543
|
+
} catch {
|
|
544
|
+
} finally {
|
|
545
|
+
phase = "idle";
|
|
546
|
+
isLoading = false;
|
|
547
|
+
handle.commit();
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
clearError() {
|
|
551
|
+
error = NO_ERROR;
|
|
552
|
+
handle.commit();
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// ../core/dist/stores/message-store.js
|
|
558
|
+
var MONOTONIC_BUMP = 1e-3;
|
|
559
|
+
function createMessageStore(clock) {
|
|
560
|
+
let entries = [];
|
|
561
|
+
const transcripts = /* @__PURE__ */ new Map();
|
|
562
|
+
let pendingSourceUrl = "";
|
|
563
|
+
let lastTimestamp = 0;
|
|
564
|
+
const build = () => {
|
|
565
|
+
const merged = [...transcripts.values(), ...entries].sort((a, b) => a.timestamp - b.timestamp);
|
|
566
|
+
return { messages: merged };
|
|
567
|
+
};
|
|
568
|
+
const handle = createStore(build);
|
|
569
|
+
const store = {
|
|
570
|
+
getSnapshot: handle.getSnapshot,
|
|
571
|
+
subscribe: handle.subscribe,
|
|
572
|
+
dispose: handle.dispose,
|
|
573
|
+
nextTimestamp() {
|
|
574
|
+
const now = clock.now();
|
|
575
|
+
lastTimestamp = now > lastTimestamp ? now : lastTimestamp + MONOTONIC_BUMP;
|
|
576
|
+
return lastTimestamp;
|
|
577
|
+
},
|
|
578
|
+
addChatMessage(params) {
|
|
579
|
+
const sourceUrl = !params.isSelf && pendingSourceUrl ? pendingSourceUrl : void 0;
|
|
580
|
+
if (sourceUrl)
|
|
581
|
+
pendingSourceUrl = "";
|
|
582
|
+
const msg = {
|
|
583
|
+
name: params.name ?? (params.isSelf ? "You" : "Agent"),
|
|
584
|
+
message: params.message,
|
|
585
|
+
isSelf: params.isSelf,
|
|
586
|
+
timestamp: params.timestamp ?? store.nextTimestamp(),
|
|
587
|
+
...sourceUrl && { sourceUrl }
|
|
588
|
+
};
|
|
589
|
+
entries.push(msg);
|
|
590
|
+
handle.commit();
|
|
591
|
+
return msg;
|
|
592
|
+
},
|
|
593
|
+
addComponent(component) {
|
|
594
|
+
const msg = {
|
|
595
|
+
name: "Agent",
|
|
596
|
+
message: "",
|
|
597
|
+
isSelf: false,
|
|
598
|
+
timestamp: store.nextTimestamp(),
|
|
599
|
+
componentType: component.component_type,
|
|
600
|
+
componentData: component
|
|
601
|
+
};
|
|
602
|
+
entries.push(msg);
|
|
603
|
+
handle.commit();
|
|
604
|
+
return msg;
|
|
605
|
+
},
|
|
606
|
+
addSystemLog(params) {
|
|
607
|
+
entries.push({
|
|
608
|
+
name: "system",
|
|
609
|
+
message: params.label,
|
|
610
|
+
isSelf: false,
|
|
611
|
+
timestamp: store.nextTimestamp(),
|
|
612
|
+
kind: "system",
|
|
613
|
+
...params.meta && { systemMeta: params.meta },
|
|
614
|
+
systemLevel: params.level
|
|
615
|
+
});
|
|
616
|
+
handle.commit();
|
|
617
|
+
},
|
|
618
|
+
addLocalSelection(componentId, label) {
|
|
619
|
+
const prompt = entries.find((m) => m.componentData?.component_id === componentId);
|
|
620
|
+
const anchor = prompt ? prompt.timestamp + MONOTONIC_BUMP / 2 : store.nextTimestamp();
|
|
621
|
+
entries.push({ name: "You", isSelf: true, message: label, timestamp: anchor });
|
|
622
|
+
handle.commit();
|
|
623
|
+
},
|
|
624
|
+
upsertTranscription(segment) {
|
|
625
|
+
const existing = transcripts.get(segment.id);
|
|
626
|
+
transcripts.set(segment.id, {
|
|
627
|
+
name: segment.participantName ?? (segment.isLocal ? "You" : "Agent"),
|
|
628
|
+
message: segment.text,
|
|
629
|
+
isSelf: segment.isLocal,
|
|
630
|
+
timestamp: existing?.timestamp ?? store.nextTimestamp(),
|
|
631
|
+
...existing?.sourceUrl && { sourceUrl: existing.sourceUrl }
|
|
632
|
+
});
|
|
633
|
+
if (pendingSourceUrl) {
|
|
634
|
+
let newestId;
|
|
635
|
+
let newest;
|
|
636
|
+
for (const [id, msg] of transcripts) {
|
|
637
|
+
if (!newest || msg.timestamp >= newest.timestamp) {
|
|
638
|
+
newest = msg;
|
|
639
|
+
newestId = id;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (newest && newestId !== void 0 && !newest.isSelf && !newest.sourceUrl) {
|
|
643
|
+
transcripts.set(newestId, { ...newest, sourceUrl: pendingSourceUrl });
|
|
644
|
+
}
|
|
645
|
+
pendingSourceUrl = "";
|
|
646
|
+
}
|
|
647
|
+
handle.commit();
|
|
648
|
+
},
|
|
649
|
+
setPendingSourceUrl(url) {
|
|
650
|
+
pendingSourceUrl = url;
|
|
651
|
+
},
|
|
652
|
+
clearPendingSourceUrl() {
|
|
653
|
+
pendingSourceUrl = "";
|
|
654
|
+
},
|
|
655
|
+
reset() {
|
|
656
|
+
entries = [];
|
|
657
|
+
transcripts.clear();
|
|
658
|
+
pendingSourceUrl = "";
|
|
659
|
+
handle.commit();
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
return store;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// ../core/dist/constants.js
|
|
666
|
+
var CHAT_WIDGET_GRACE_MS = TIMING.chatWidgetGraceMs;
|
|
667
|
+
var CHAT_WIDGET_AFTER_TEXT_MS = TIMING.chatWidgetAfterTextMs;
|
|
668
|
+
var MIN_TYPING_INDICATOR_MS = TIMING.minTypingIndicatorMs;
|
|
669
|
+
|
|
670
|
+
// ../core/dist/stores/typing-store.js
|
|
671
|
+
function createTypingStore(clock) {
|
|
672
|
+
let shownAt = null;
|
|
673
|
+
let isTyping = false;
|
|
674
|
+
const build = () => ({ isTyping });
|
|
675
|
+
const handle = createStore(build);
|
|
676
|
+
const set = (next) => {
|
|
677
|
+
if (isTyping === next)
|
|
678
|
+
return;
|
|
679
|
+
isTyping = next;
|
|
680
|
+
handle.commit();
|
|
681
|
+
};
|
|
682
|
+
return {
|
|
683
|
+
getSnapshot: handle.getSnapshot,
|
|
684
|
+
subscribe: handle.subscribe,
|
|
685
|
+
dispose: handle.dispose,
|
|
686
|
+
showOnUserSend() {
|
|
687
|
+
if (shownAt === null)
|
|
688
|
+
shownAt = clock.now();
|
|
689
|
+
set(true);
|
|
690
|
+
},
|
|
691
|
+
clear() {
|
|
692
|
+
shownAt = null;
|
|
693
|
+
set(false);
|
|
694
|
+
},
|
|
695
|
+
waitMinDuration() {
|
|
696
|
+
if (shownAt === null)
|
|
697
|
+
return Promise.resolve();
|
|
698
|
+
const remaining = MIN_TYPING_INDICATOR_MS - (clock.now() - shownAt);
|
|
699
|
+
if (remaining <= 0)
|
|
700
|
+
return Promise.resolve();
|
|
701
|
+
return new Promise((resolve) => clock.setTimeout(resolve, remaining));
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// ../core/dist/stores/component-store.js
|
|
707
|
+
function createComponentStore(clock, commitComponent) {
|
|
708
|
+
let pendingChatWidgets = [];
|
|
709
|
+
let graceTimer = null;
|
|
710
|
+
const pendingResponses = /* @__PURE__ */ new Map();
|
|
711
|
+
const completedFlowIds = /* @__PURE__ */ new Set();
|
|
712
|
+
let extensions = [];
|
|
713
|
+
const build = () => ({ completedFlowIds: new Set(completedFlowIds) });
|
|
714
|
+
const handle = createStore(build);
|
|
715
|
+
const store = {
|
|
716
|
+
getSnapshot: handle.getSnapshot,
|
|
717
|
+
subscribe: handle.subscribe,
|
|
718
|
+
dispose: handle.dispose,
|
|
719
|
+
add(component, mode) {
|
|
720
|
+
if (mode !== "text") {
|
|
721
|
+
commitComponent(component);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
pendingChatWidgets.push(component);
|
|
725
|
+
if (!graceTimer) {
|
|
726
|
+
graceTimer = clock.setTimeout(() => store.flush(), CHAT_WIDGET_GRACE_MS);
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
onAgentTextLanded() {
|
|
730
|
+
if (pendingChatWidgets.length > 0) {
|
|
731
|
+
clock.setTimeout(() => store.flush(), CHAT_WIDGET_AFTER_TEXT_MS);
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
flush() {
|
|
735
|
+
if (graceTimer) {
|
|
736
|
+
clock.clearTimeout(graceTimer);
|
|
737
|
+
graceTimer = null;
|
|
738
|
+
}
|
|
739
|
+
const queued = pendingChatWidgets;
|
|
740
|
+
pendingChatWidgets = [];
|
|
741
|
+
queued.forEach((component) => commitComponent(component));
|
|
742
|
+
},
|
|
743
|
+
// ─── Response tracking ─────────────────────────────────────────────────────
|
|
744
|
+
trackPendingResponse(componentId, pending) {
|
|
745
|
+
pendingResponses.set(componentId, pending);
|
|
746
|
+
},
|
|
747
|
+
takePendingResponse(componentId) {
|
|
748
|
+
const pending = pendingResponses.get(componentId);
|
|
749
|
+
if (pending)
|
|
750
|
+
pendingResponses.delete(componentId);
|
|
751
|
+
return pending;
|
|
752
|
+
},
|
|
753
|
+
markFlowComplete(componentId) {
|
|
754
|
+
completedFlowIds.add(componentId);
|
|
755
|
+
handle.commit();
|
|
756
|
+
},
|
|
757
|
+
// ─── Extensions ────────────────────────────────────────────────────────────
|
|
758
|
+
registerExtension(extension) {
|
|
759
|
+
extensions.push(extension);
|
|
760
|
+
return () => {
|
|
761
|
+
extensions = extensions.filter((e) => e !== extension);
|
|
762
|
+
};
|
|
763
|
+
},
|
|
764
|
+
runExtensions(componentName, parsed) {
|
|
765
|
+
return extensions.some((extension) => extension(componentName, parsed));
|
|
766
|
+
},
|
|
767
|
+
// ─── Lifecycle ─────────────────────────────────────────────────────────────
|
|
768
|
+
reset() {
|
|
769
|
+
if (graceTimer) {
|
|
770
|
+
clock.clearTimeout(graceTimer);
|
|
771
|
+
graceTimer = null;
|
|
772
|
+
}
|
|
773
|
+
pendingChatWidgets = [];
|
|
774
|
+
pendingResponses.clear();
|
|
775
|
+
completedFlowIds.clear();
|
|
776
|
+
handle.commit();
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
return store;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// ../core/dist/stores/slash-command-store.js
|
|
783
|
+
function createSlashCommandStore(configCommands = []) {
|
|
784
|
+
let runtimeCommands = [];
|
|
785
|
+
const build = () => {
|
|
786
|
+
const seen = new Set(configCommands.map((c) => c.command));
|
|
787
|
+
const merged = [...configCommands];
|
|
788
|
+
for (const cmd of runtimeCommands) {
|
|
789
|
+
if (!seen.has(cmd.command))
|
|
790
|
+
merged.push(cmd);
|
|
791
|
+
}
|
|
792
|
+
return { commands: merged };
|
|
793
|
+
};
|
|
794
|
+
const handle = createStore(build);
|
|
795
|
+
return {
|
|
796
|
+
getSnapshot: handle.getSnapshot,
|
|
797
|
+
subscribe: handle.subscribe,
|
|
798
|
+
dispose: handle.dispose,
|
|
799
|
+
setRuntimeCommands(commands) {
|
|
800
|
+
runtimeCommands = commands;
|
|
801
|
+
handle.commit();
|
|
802
|
+
},
|
|
803
|
+
reset() {
|
|
804
|
+
runtimeCommands = [];
|
|
805
|
+
handle.commit();
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// ../core/dist/controller.js
|
|
811
|
+
function createRinggWidgetController(config, ports) {
|
|
812
|
+
const theme = mergeWidgetTheme(config.theme);
|
|
813
|
+
const eventBus = ports.eventBus ?? createCallbackEventBus();
|
|
814
|
+
const clock = ports.clock ?? createSystemClock();
|
|
815
|
+
const notification = ports.notification ?? silentNotificationPlayer;
|
|
816
|
+
const onDomAction = ports.onDomAction;
|
|
817
|
+
const transport = ports.transport;
|
|
818
|
+
const api = createRinggApiClient({
|
|
819
|
+
urlResolver: ports.urlResolver,
|
|
820
|
+
mode: config.mode ?? WIDGET_DEFAULTS.mode,
|
|
821
|
+
xApiKey: config.xApiKey,
|
|
822
|
+
authorization: config.authorization
|
|
823
|
+
});
|
|
824
|
+
const shell = createShellStore(eventBus, config.defaultTab ?? WIDGET_DEFAULTS.defaultTab, config.defaultExpanded ?? WIDGET_DEFAULTS.defaultExpanded);
|
|
825
|
+
const session = createSessionStore(api, transport, ports.micPermission ?? grantedMicPermission);
|
|
826
|
+
const messages = createMessageStore(clock);
|
|
827
|
+
const typing = createTypingStore(clock);
|
|
828
|
+
const slashCommands = createSlashCommandStore(config.enabledSlashCommands);
|
|
829
|
+
const components = createComponentStore(clock, (component) => commitComponent(component));
|
|
830
|
+
const unsubscribers = [];
|
|
831
|
+
let conversationEndedDispatched = false;
|
|
832
|
+
const eventLogsEnabled = config.eventLogs?.enabled === true;
|
|
833
|
+
const eventLogsShowIds = config.eventLogs?.showIds === true;
|
|
834
|
+
const pushSystemLog = (entry) => {
|
|
835
|
+
if (!eventLogsEnabled)
|
|
836
|
+
return;
|
|
837
|
+
const meta = eventLogsShowIds && entry.id ? entry.meta ? `${entry.id} \xB7 ${entry.meta}` : entry.id : entry.meta;
|
|
838
|
+
messages.addSystemLog({ label: entry.label, meta, level: entry.level });
|
|
839
|
+
};
|
|
840
|
+
function resetConversation() {
|
|
841
|
+
messages.reset();
|
|
842
|
+
components.reset();
|
|
843
|
+
}
|
|
844
|
+
function commitComponent(component) {
|
|
845
|
+
messages.addComponent(component);
|
|
846
|
+
eventBus.emit("ringg:calendar_booking", { status: "shown", componentId: component.component_id });
|
|
847
|
+
typing.clear();
|
|
848
|
+
if (!shell.isOpen)
|
|
849
|
+
notification.play();
|
|
850
|
+
shell.open();
|
|
851
|
+
}
|
|
852
|
+
async function handleAgentTextStream(stream) {
|
|
853
|
+
if (shell.getSnapshot().callMode !== "text" || !session.isSessionLive)
|
|
854
|
+
return;
|
|
855
|
+
try {
|
|
856
|
+
const message = await stream.readAll();
|
|
857
|
+
if (!stream.isAgent) {
|
|
858
|
+
messages.addChatMessage({ message, isSelf: true, name: "You" });
|
|
859
|
+
messages.clearPendingSourceUrl();
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
await typing.waitMinDuration();
|
|
863
|
+
if (!shell.isOpen)
|
|
864
|
+
notification.play();
|
|
865
|
+
shell.open();
|
|
866
|
+
messages.addChatMessage({ message, isSelf: false, name: "Agent" });
|
|
867
|
+
typing.clear();
|
|
868
|
+
components.onAgentTextLanded();
|
|
869
|
+
} catch {
|
|
870
|
+
if (stream.isAgent)
|
|
871
|
+
typing.clear();
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
async function handleBlocksStream(stream) {
|
|
875
|
+
try {
|
|
876
|
+
const raw = await stream.readAll();
|
|
877
|
+
typing.clear();
|
|
878
|
+
const parsed = JSON.parse(raw);
|
|
879
|
+
if (parsed.component_type !== "render_component_blocks" || !Array.isArray(parsed.component_config?.blocks))
|
|
880
|
+
return;
|
|
881
|
+
components.add({
|
|
882
|
+
component_type: "blocks",
|
|
883
|
+
component_id: `blocks_${clock.now()}`,
|
|
884
|
+
data: parsed.component_config
|
|
885
|
+
}, shell.getSnapshot().callMode);
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
async function handleDynamicData(invocation) {
|
|
890
|
+
typing.clear();
|
|
891
|
+
let parsed;
|
|
892
|
+
try {
|
|
893
|
+
parsed = JSON.parse(invocation.payload);
|
|
894
|
+
} catch {
|
|
895
|
+
throw new Error("Invalid send_dynamic_data payload");
|
|
896
|
+
}
|
|
897
|
+
const componentType = parsed.component_type;
|
|
898
|
+
if (!componentType) {
|
|
899
|
+
return JSON.stringify({ status: "error", message: "No component_type provided" });
|
|
900
|
+
}
|
|
901
|
+
if (componentType === "execute_dom_action") {
|
|
902
|
+
const cfg = parsed.component_config ?? parsed.component_data ?? {};
|
|
903
|
+
const action = {
|
|
904
|
+
id: cfg.id ?? cfg.action_id ?? "",
|
|
905
|
+
kind: cfg.kind ?? "trigger_event",
|
|
906
|
+
event_name: cfg.event_name,
|
|
907
|
+
default_payload: cfg.default_payload ?? cfg.detail
|
|
908
|
+
};
|
|
909
|
+
onDomAction?.(action, pushSystemLog);
|
|
910
|
+
return JSON.stringify({ status: "received" });
|
|
911
|
+
}
|
|
912
|
+
const componentName = componentType.replace("render_component_", "");
|
|
913
|
+
if (components.runExtensions(componentName, parsed)) {
|
|
914
|
+
return JSON.stringify({ status: "received" });
|
|
915
|
+
}
|
|
916
|
+
const component = transformRpcToComponent(componentName, parsed);
|
|
917
|
+
if (!component) {
|
|
918
|
+
return JSON.stringify({ status: "error", message: `Unknown component type: ${componentName}` });
|
|
919
|
+
}
|
|
920
|
+
components.trackPendingResponse(component.component_id, {
|
|
921
|
+
name: componentName,
|
|
922
|
+
originalConfig: parsed.component_config ?? parsed.component_data
|
|
923
|
+
});
|
|
924
|
+
components.add(component, shell.getSnapshot().callMode);
|
|
925
|
+
return JSON.stringify({ status: "received", component_id: component.component_id });
|
|
926
|
+
}
|
|
927
|
+
async function handleRenderComponent(invocation) {
|
|
928
|
+
typing.clear();
|
|
929
|
+
let parsed;
|
|
930
|
+
try {
|
|
931
|
+
parsed = JSON.parse(invocation.payload);
|
|
932
|
+
} catch {
|
|
933
|
+
throw new Error("Invalid component payload");
|
|
934
|
+
}
|
|
935
|
+
if (parsed.component_type && parsed.component_id) {
|
|
936
|
+
components.add(parsed, shell.getSnapshot().callMode);
|
|
937
|
+
}
|
|
938
|
+
return JSON.stringify({ status: "received" });
|
|
939
|
+
}
|
|
940
|
+
function wireTransport() {
|
|
941
|
+
transport.registerRpcMethod("send_dynamic_data", (invocation) => handleDynamicData(invocation));
|
|
942
|
+
transport.registerRpcMethod("render_component", (invocation) => handleRenderComponent(invocation));
|
|
943
|
+
transport.registerRpcMethod("send_source_urls", async (invocation) => {
|
|
944
|
+
let parsed;
|
|
945
|
+
try {
|
|
946
|
+
parsed = JSON.parse(invocation.payload);
|
|
947
|
+
} catch {
|
|
948
|
+
throw new Error("Invalid payload for send_source_urls");
|
|
949
|
+
}
|
|
950
|
+
if (Array.isArray(parsed.source_urls))
|
|
951
|
+
messages.setPendingSourceUrl(parsed.source_urls.join(", "));
|
|
952
|
+
else if (parsed.source_url)
|
|
953
|
+
messages.setPendingSourceUrl(String(parsed.source_url));
|
|
954
|
+
return JSON.stringify({ status: "received" });
|
|
955
|
+
});
|
|
956
|
+
transport.registerTextStreamHandler("lk.transcription", (stream) => {
|
|
957
|
+
void handleAgentTextStream(stream);
|
|
958
|
+
});
|
|
959
|
+
transport.registerTextStreamHandler("ringg.blocks", (stream) => {
|
|
960
|
+
void handleBlocksStream(stream);
|
|
961
|
+
});
|
|
962
|
+
unsubscribers.push(transport.onChatMessage((message) => {
|
|
963
|
+
if (message.isSelf)
|
|
964
|
+
return;
|
|
965
|
+
if (shell.getSnapshot().callMode !== "text" || !session.isSessionLive)
|
|
966
|
+
return;
|
|
967
|
+
messages.addChatMessage({ message: message.message, isSelf: false, name: message.senderName });
|
|
968
|
+
if (message.isAgent)
|
|
969
|
+
typing.clear();
|
|
970
|
+
}), transport.onTranscription((segment) => {
|
|
971
|
+
const { callMode } = shell.getSnapshot();
|
|
972
|
+
if (callMode !== "audio" || session.getSnapshot().connectionState !== "connected")
|
|
973
|
+
return;
|
|
974
|
+
messages.upsertTranscription(segment);
|
|
975
|
+
}), transport.onSessionEnded(() => {
|
|
976
|
+
if (session.isSessionLive)
|
|
977
|
+
void controller.endCall();
|
|
978
|
+
}));
|
|
979
|
+
}
|
|
980
|
+
const controller = {
|
|
981
|
+
config,
|
|
982
|
+
theme,
|
|
983
|
+
eventBus,
|
|
984
|
+
shell,
|
|
985
|
+
session,
|
|
986
|
+
messages,
|
|
987
|
+
typing,
|
|
988
|
+
components,
|
|
989
|
+
slashCommands,
|
|
990
|
+
// ─── Widget shell ──────────────────────────────────────────────────────────
|
|
991
|
+
openWidget() {
|
|
992
|
+
shell.open();
|
|
993
|
+
if (!session.isSessionLive)
|
|
994
|
+
session.prewarm();
|
|
995
|
+
},
|
|
996
|
+
minimizeWidget() {
|
|
997
|
+
shell.minimize();
|
|
998
|
+
},
|
|
999
|
+
handleTriggerClick() {
|
|
1000
|
+
if (config.defaultExpanded)
|
|
1001
|
+
return;
|
|
1002
|
+
const { isLoading, connectionState } = session.getSnapshot();
|
|
1003
|
+
if (config.bypassStartScreen && !isLoading && connectionState === "disconnected" && !session.isSessionLive) {
|
|
1004
|
+
controller.openWidget();
|
|
1005
|
+
void controller.startCall(shell.getSnapshot().callMode);
|
|
1006
|
+
} else if (shell.isOpen) {
|
|
1007
|
+
shell.minimize();
|
|
1008
|
+
} else {
|
|
1009
|
+
controller.openWidget();
|
|
1010
|
+
}
|
|
1011
|
+
},
|
|
1012
|
+
// ─── Call lifecycle ────────────────────────────────────────────────────────
|
|
1013
|
+
async startCall(mediaType) {
|
|
1014
|
+
shell.setCallMode(mediaType);
|
|
1015
|
+
resetConversation();
|
|
1016
|
+
const result = await session.start({
|
|
1017
|
+
agentId: config.agentId,
|
|
1018
|
+
variables: config.variables ?? {},
|
|
1019
|
+
mediaType
|
|
1020
|
+
});
|
|
1021
|
+
if (!result) {
|
|
1022
|
+
typing.clear();
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
shell.setCurrentCallId(result.callId);
|
|
1026
|
+
slashCommands.setRuntimeCommands(result.slashCommands);
|
|
1027
|
+
if (mediaType === "text")
|
|
1028
|
+
typing.showOnUserSend();
|
|
1029
|
+
conversationEndedDispatched = false;
|
|
1030
|
+
eventBus.emit("ringg:conversation_status", { status: "started", mode: mediaType, callId: result.callId });
|
|
1031
|
+
},
|
|
1032
|
+
async endCall() {
|
|
1033
|
+
const { currentCallId, callMode } = shell.getSnapshot();
|
|
1034
|
+
if (currentCallId && !conversationEndedDispatched) {
|
|
1035
|
+
conversationEndedDispatched = true;
|
|
1036
|
+
eventBus.emit("ringg:conversation_status", { status: "ended", mode: callMode, callId: currentCallId });
|
|
1037
|
+
}
|
|
1038
|
+
slashCommands.reset();
|
|
1039
|
+
typing.clear();
|
|
1040
|
+
components.reset();
|
|
1041
|
+
await session.end();
|
|
1042
|
+
if (config.bypassFeedbackScreen) {
|
|
1043
|
+
resetConversation();
|
|
1044
|
+
shell.close();
|
|
1045
|
+
} else {
|
|
1046
|
+
shell.showFeedback();
|
|
1047
|
+
shell.open();
|
|
1048
|
+
}
|
|
1049
|
+
},
|
|
1050
|
+
// ─── Chat ──────────────────────────────────────────────────────────────────
|
|
1051
|
+
async sendMessage(text) {
|
|
1052
|
+
messages.addChatMessage({ message: text, isSelf: true });
|
|
1053
|
+
typing.showOnUserSend();
|
|
1054
|
+
await transport.sendChatMessage(text);
|
|
1055
|
+
},
|
|
1056
|
+
// ─── Components ────────────────────────────────────────────────────────────
|
|
1057
|
+
async sendComponentResponse(componentId, responseData) {
|
|
1058
|
+
const pending = components.takePendingResponse(componentId);
|
|
1059
|
+
if (!pending)
|
|
1060
|
+
return;
|
|
1061
|
+
const { method, payload } = formatComponentResponse(pending.name, componentId, responseData, pending.originalConfig);
|
|
1062
|
+
await transport.performRpcToAgent(method, payload);
|
|
1063
|
+
eventBus.emit("ringg:component_acknowledgement", {
|
|
1064
|
+
componentName: pending.name,
|
|
1065
|
+
componentId,
|
|
1066
|
+
status: "responded"
|
|
1067
|
+
});
|
|
1068
|
+
},
|
|
1069
|
+
async sendBlocksAction(toolId, componentId, action) {
|
|
1070
|
+
const { method, payload } = formatBlocksAction(toolId, componentId, action);
|
|
1071
|
+
try {
|
|
1072
|
+
await transport.performRpcToAgent(method, payload);
|
|
1073
|
+
} catch {
|
|
1074
|
+
}
|
|
1075
|
+
},
|
|
1076
|
+
callComponentApi(method, endpoint, payload) {
|
|
1077
|
+
return api.callComponentApi(method, endpoint, payload);
|
|
1078
|
+
},
|
|
1079
|
+
async sendDynamicData(payload) {
|
|
1080
|
+
const { method, payload: rpcPayload } = formatDynamicData(payload);
|
|
1081
|
+
await transport.performRpcToAgent(method, rpcPayload);
|
|
1082
|
+
},
|
|
1083
|
+
addLocalComponent(component) {
|
|
1084
|
+
messages.addComponent(component);
|
|
1085
|
+
},
|
|
1086
|
+
displaySelection(componentId, label) {
|
|
1087
|
+
messages.addLocalSelection(componentId, label);
|
|
1088
|
+
},
|
|
1089
|
+
markFlowComplete(componentId) {
|
|
1090
|
+
components.markFlowComplete(componentId);
|
|
1091
|
+
eventBus.emit("ringg:calendar_booking", { status: "confirmed", componentId, message: "Flow completed" });
|
|
1092
|
+
},
|
|
1093
|
+
registerDynamicDataExtension(extension) {
|
|
1094
|
+
return components.registerExtension(extension);
|
|
1095
|
+
},
|
|
1096
|
+
// ─── Slash commands ────────────────────────────────────────────────────────
|
|
1097
|
+
async sendSlashCommand(command) {
|
|
1098
|
+
if (components.runExtensions("slash_command", { command: command.command, tool_type: command.tool_type }))
|
|
1099
|
+
return;
|
|
1100
|
+
const { method, payload } = formatSlashCommand(command.command);
|
|
1101
|
+
await transport.performRpcToAgent(method, payload);
|
|
1102
|
+
},
|
|
1103
|
+
// ─── Feedback ──────────────────────────────────────────────────────────────
|
|
1104
|
+
async submitFeedback(rating, comment) {
|
|
1105
|
+
const { currentCallId } = shell.getSnapshot();
|
|
1106
|
+
if (currentCallId) {
|
|
1107
|
+
try {
|
|
1108
|
+
await api.submitFeedback(currentCallId, { rating, comment });
|
|
1109
|
+
eventBus.emit("ringg:feedback_status", { status: "submitted", callId: currentCallId, rating });
|
|
1110
|
+
} catch {
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
resetConversation();
|
|
1114
|
+
shell.close();
|
|
1115
|
+
},
|
|
1116
|
+
skipFeedback() {
|
|
1117
|
+
const { currentCallId } = shell.getSnapshot();
|
|
1118
|
+
if (currentCallId) {
|
|
1119
|
+
eventBus.emit("ringg:feedback_status", { status: "skipped", callId: currentCallId });
|
|
1120
|
+
}
|
|
1121
|
+
resetConversation();
|
|
1122
|
+
shell.close();
|
|
1123
|
+
},
|
|
1124
|
+
// ─── Teardown ──────────────────────────────────────────────────────────────
|
|
1125
|
+
destroy() {
|
|
1126
|
+
unsubscribers.forEach((unsubscribe) => unsubscribe());
|
|
1127
|
+
transport.unregisterRpcMethod("send_dynamic_data");
|
|
1128
|
+
transport.unregisterRpcMethod("render_component");
|
|
1129
|
+
transport.unregisterRpcMethod("send_source_urls");
|
|
1130
|
+
transport.unregisterTextStreamHandler("lk.transcription");
|
|
1131
|
+
transport.unregisterTextStreamHandler("ringg.blocks");
|
|
1132
|
+
void transport.disconnect();
|
|
1133
|
+
[shell, session, messages, typing, components, slashCommands].forEach((store) => store.dispose());
|
|
1134
|
+
eventBus.off();
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
wireTransport();
|
|
1138
|
+
return controller;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// ../core/dist/react/hooks.js
|
|
1142
|
+
var import_react = require("react");
|
|
1143
|
+
function useStoreSnapshot(store) {
|
|
1144
|
+
return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
1145
|
+
}
|
|
1146
|
+
var useRinggMessages = (controller) => useStoreSnapshot(controller.messages);
|
|
1147
|
+
var useRinggTyping = (controller) => useStoreSnapshot(controller.typing);
|
|
1148
|
+
var useRinggSession = (controller) => useStoreSnapshot(controller.session);
|
|
1149
|
+
var useRinggShell = (controller) => useStoreSnapshot(controller.shell);
|
|
1150
|
+
var useRinggComponents = (controller) => useStoreSnapshot(controller.components);
|
|
1151
|
+
var useRinggSlashCommands = (controller) => useStoreSnapshot(controller.slashCommands);
|
|
1152
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1153
|
+
0 && (module.exports = {
|
|
1154
|
+
createCallbackEventBus,
|
|
1155
|
+
createRinggWidgetController,
|
|
1156
|
+
useRinggComponents,
|
|
1157
|
+
useRinggMessages,
|
|
1158
|
+
useRinggSession,
|
|
1159
|
+
useRinggShell,
|
|
1160
|
+
useRinggSlashCommands,
|
|
1161
|
+
useRinggTyping,
|
|
1162
|
+
useStoreSnapshot
|
|
1163
|
+
});
|
|
1164
|
+
//# sourceMappingURL=index.js.map
|