@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.d.ts
ADDED
|
@@ -0,0 +1,964 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme types for the widget.
|
|
3
|
+
* Covers both widget-level theming and per-component theming.
|
|
4
|
+
*/
|
|
5
|
+
type ButtonStyle = "rounded" | "pill" | "square";
|
|
6
|
+
type ButtonSize = "sm" | "md" | "lg";
|
|
7
|
+
/**
|
|
8
|
+
* Widget-level theme — applies to the entire widget chrome
|
|
9
|
+
* (header, messages, buttons, trigger FAB, etc.)
|
|
10
|
+
*/
|
|
11
|
+
interface WidgetTheme {
|
|
12
|
+
/** Main accent color (buttons, selected states). Supports CSS gradients. */
|
|
13
|
+
primaryColor?: string;
|
|
14
|
+
/** Text color on primary-colored elements */
|
|
15
|
+
primaryTextColor?: string;
|
|
16
|
+
/** Widget background */
|
|
17
|
+
backgroundColor?: string;
|
|
18
|
+
/** Cards, inputs background */
|
|
19
|
+
surfaceColor?: string;
|
|
20
|
+
/** Agent message bubble background — empty string renders agent messages bubble-less. Supports CSS gradients. */
|
|
21
|
+
agentBubbleColor?: string;
|
|
22
|
+
/** Main text color */
|
|
23
|
+
textColor?: string;
|
|
24
|
+
/** Secondary/muted text color */
|
|
25
|
+
mutedTextColor?: string;
|
|
26
|
+
/** Border color */
|
|
27
|
+
borderColor?: string;
|
|
28
|
+
/** Error state color */
|
|
29
|
+
errorColor?: string;
|
|
30
|
+
/** Success state color */
|
|
31
|
+
successColor?: string;
|
|
32
|
+
/** Button corner style */
|
|
33
|
+
buttonStyle?: ButtonStyle;
|
|
34
|
+
/** Global border radius (e.g., "12px", "20px") */
|
|
35
|
+
borderRadius?: string;
|
|
36
|
+
/** Font family */
|
|
37
|
+
fontFamily?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Component-level theme — applies to interactive components
|
|
41
|
+
* (calendar, form, buttons, confirmation). Extends widget theme with
|
|
42
|
+
* component-specific properties; component values win over widget values.
|
|
43
|
+
*/
|
|
44
|
+
interface ComponentTheme {
|
|
45
|
+
primaryColor?: string;
|
|
46
|
+
primaryTextColor?: string;
|
|
47
|
+
backgroundColor?: string;
|
|
48
|
+
surfaceColor?: string;
|
|
49
|
+
textColor?: string;
|
|
50
|
+
mutedTextColor?: string;
|
|
51
|
+
borderColor?: string;
|
|
52
|
+
errorColor?: string;
|
|
53
|
+
successColor?: string;
|
|
54
|
+
fontFamily?: string;
|
|
55
|
+
fontSize?: string;
|
|
56
|
+
borderRadius?: string;
|
|
57
|
+
padding?: string;
|
|
58
|
+
buttonStyle?: ButtonStyle;
|
|
59
|
+
buttonSize?: ButtonSize;
|
|
60
|
+
}
|
|
61
|
+
/** Fully resolved widget theme — all fields guaranteed present */
|
|
62
|
+
type ResolvedWidgetTheme = Required<WidgetTheme>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Slash command types — commands available during text chat.
|
|
66
|
+
*
|
|
67
|
+
* `tool_type` is an open union: the known values get literal types, but the
|
|
68
|
+
* backend can introduce new tool types without a core release breaking on
|
|
69
|
+
* them. Domain-specific tools (e.g. client-pack widgets) register handlers
|
|
70
|
+
* via the ComponentStore registry rather than adding literals here.
|
|
71
|
+
*/
|
|
72
|
+
type SlashCommandToolType = "CALLBACK_SCHEDULE_TOOL" | "WIDGET_TOOL" | (string & {});
|
|
73
|
+
interface SlashCommand {
|
|
74
|
+
tool_type: SlashCommandToolType;
|
|
75
|
+
command: string;
|
|
76
|
+
display_name: string;
|
|
77
|
+
description: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Widget configuration types.
|
|
82
|
+
* These define the public API surface for consumers initializing the widget.
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
type MediaType = "audio" | "text";
|
|
86
|
+
type EnvironmentMode = "dev" | "stage" | "prod";
|
|
87
|
+
type TriggerPlacement = "fixed" | "absolute";
|
|
88
|
+
type TriggerAlignment = "center" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
89
|
+
type WidgetAlignment = "bottom-right" | "bottom-center" | "bottom-left";
|
|
90
|
+
interface WidgetPositionConfig {
|
|
91
|
+
triggerPlacement?: TriggerPlacement;
|
|
92
|
+
triggerAlignment?: TriggerAlignment;
|
|
93
|
+
hideTriggerOnExpand?: boolean;
|
|
94
|
+
widgetAlignment?: WidgetAlignment;
|
|
95
|
+
}
|
|
96
|
+
interface LegalDisclaimerConfig {
|
|
97
|
+
text: string;
|
|
98
|
+
links?: Record<string, string>;
|
|
99
|
+
}
|
|
100
|
+
/** Styles that are safe to pass cross-platform (subset of CSS properties that map to RN) */
|
|
101
|
+
interface PortableStyles {
|
|
102
|
+
height?: string | number;
|
|
103
|
+
width?: string | number;
|
|
104
|
+
backgroundColor?: string;
|
|
105
|
+
padding?: string | number;
|
|
106
|
+
borderRadius?: string | number;
|
|
107
|
+
}
|
|
108
|
+
interface ButtonIconConfig {
|
|
109
|
+
size?: string | number;
|
|
110
|
+
url?: string;
|
|
111
|
+
}
|
|
112
|
+
interface ButtonsConfig {
|
|
113
|
+
mic?: {
|
|
114
|
+
styles?: PortableStyles;
|
|
115
|
+
};
|
|
116
|
+
modalTrigger?: {
|
|
117
|
+
styles?: PortableStyles;
|
|
118
|
+
icon?: ButtonIconConfig;
|
|
119
|
+
};
|
|
120
|
+
text?: {
|
|
121
|
+
textBeforeCall?: string;
|
|
122
|
+
textDuringCall?: string;
|
|
123
|
+
styles?: PortableStyles;
|
|
124
|
+
};
|
|
125
|
+
call?: {
|
|
126
|
+
textBeforeCall?: string;
|
|
127
|
+
textDuringCall?: string;
|
|
128
|
+
styles?: PortableStyles;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
interface FeedbackScreenConfig {
|
|
132
|
+
title?: string;
|
|
133
|
+
description?: string;
|
|
134
|
+
starsCount?: number;
|
|
135
|
+
starsStyles?: {
|
|
136
|
+
filledColor?: string;
|
|
137
|
+
emptyColor?: string;
|
|
138
|
+
};
|
|
139
|
+
placeholder?: string;
|
|
140
|
+
submitBtnCTA?: string;
|
|
141
|
+
submitBtnStyles?: PortableStyles;
|
|
142
|
+
}
|
|
143
|
+
interface InnerWindowConfig {
|
|
144
|
+
width?: string | number;
|
|
145
|
+
height?: string | number;
|
|
146
|
+
borderRadius?: string | number;
|
|
147
|
+
}
|
|
148
|
+
/** Voice-call view options (audio mode only). */
|
|
149
|
+
interface VoiceCallConfig {
|
|
150
|
+
showAnimation?: boolean;
|
|
151
|
+
showTranscript?: boolean;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Inline chat pills for DOM action / system events.
|
|
155
|
+
* Off by default; set `enabled: true` to show pills in the chat thread.
|
|
156
|
+
* `showIds: true` appends the action id alongside the meta.
|
|
157
|
+
*/
|
|
158
|
+
interface EventLogsConfig {
|
|
159
|
+
enabled?: boolean;
|
|
160
|
+
showIds?: boolean;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Custom variables passed to the agent. Values are forwarded verbatim as
|
|
164
|
+
* `custom_args_values`, so the full JSON value space the backend accepts is
|
|
165
|
+
* allowed (source contract: agents-cdn global.d.ts).
|
|
166
|
+
*/
|
|
167
|
+
type WidgetVariables = Record<string, string | number | boolean | object | Array<unknown>>;
|
|
168
|
+
/**
|
|
169
|
+
* The main configuration object consumers pass to initialize the widget.
|
|
170
|
+
* Used by both web (<ringg-widget> / loadAgent) and React Native
|
|
171
|
+
* (<RinggWidget />) entry points.
|
|
172
|
+
*/
|
|
173
|
+
interface RinggWidgetConfig {
|
|
174
|
+
/** Agent identifier — required */
|
|
175
|
+
agentId: string;
|
|
176
|
+
/** API key for authentication (X-API-KEY header) */
|
|
177
|
+
xApiKey?: string;
|
|
178
|
+
/** JWT for authentication (Authorization header) — wins over xApiKey when both are set */
|
|
179
|
+
authorization?: string;
|
|
180
|
+
/** Custom variables passed to the agent (e.g., user name, role) */
|
|
181
|
+
variables?: WidgetVariables;
|
|
182
|
+
/** Environment mode — selects which UrlResolver entry to use */
|
|
183
|
+
mode?: EnvironmentMode;
|
|
184
|
+
/** Widget title displayed in the header */
|
|
185
|
+
title?: string;
|
|
186
|
+
/** Widget description displayed in the header */
|
|
187
|
+
description?: string;
|
|
188
|
+
/** Default communication tab */
|
|
189
|
+
defaultTab?: MediaType;
|
|
190
|
+
/** Whether to hide the audio/text tab selector */
|
|
191
|
+
hideTabSelector?: boolean;
|
|
192
|
+
/** Start expanded instead of as a trigger button */
|
|
193
|
+
defaultExpanded?: boolean;
|
|
194
|
+
/** Skip the start screen — trigger click starts the call directly */
|
|
195
|
+
bypassStartScreen?: boolean;
|
|
196
|
+
/** Skip the post-call feedback screen */
|
|
197
|
+
bypassFeedbackScreen?: boolean;
|
|
198
|
+
/** Custom logo URL for the widget header */
|
|
199
|
+
logoUrl?: string;
|
|
200
|
+
/** Styles for the logo element */
|
|
201
|
+
logoStyles?: PortableStyles;
|
|
202
|
+
/** Slash commands available from config (merged with runtime ones) */
|
|
203
|
+
enabledSlashCommands?: SlashCommand[];
|
|
204
|
+
/** App-wide theming */
|
|
205
|
+
theme?: WidgetTheme;
|
|
206
|
+
/** Widget positioning (web-only, ignored on RN) */
|
|
207
|
+
widgetPosition?: WidgetPositionConfig;
|
|
208
|
+
/** Legal disclaimer shown before call start */
|
|
209
|
+
legalDisclaimer?: LegalDisclaimerConfig;
|
|
210
|
+
/** Button customizations */
|
|
211
|
+
buttons?: ButtonsConfig;
|
|
212
|
+
/** Post-call feedback screen customizations */
|
|
213
|
+
feedbackScreen?: FeedbackScreenConfig;
|
|
214
|
+
/** Widget window dimensions (web-only) */
|
|
215
|
+
innerWindowProps?: InnerWindowConfig;
|
|
216
|
+
/** Voice-call view options */
|
|
217
|
+
voiceCall?: VoiceCallConfig;
|
|
218
|
+
/** URL for the notification sound */
|
|
219
|
+
notificationTuneUrl?: string;
|
|
220
|
+
/** Words rotated inside the typing indicator */
|
|
221
|
+
typingWords?: string[];
|
|
222
|
+
/** Inline chat pills for DOM action / system events (off by default) */
|
|
223
|
+
eventLogs?: EventLogsConfig;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Interactive component types pushed from the backend during a call.
|
|
228
|
+
* These define the data contracts for calendar, form, buttons, confirmation,
|
|
229
|
+
* flows, and Block Kit. Domain-specific components (e.g. policy finder) are
|
|
230
|
+
* NOT defined here — client packs register them via the component registry.
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
interface BaseComponent {
|
|
234
|
+
component_type: string;
|
|
235
|
+
component_id: string;
|
|
236
|
+
data: unknown;
|
|
237
|
+
theme?: ComponentTheme;
|
|
238
|
+
}
|
|
239
|
+
interface CalendarSlot {
|
|
240
|
+
id: string;
|
|
241
|
+
datetime: string;
|
|
242
|
+
}
|
|
243
|
+
interface CalendarBookingData {
|
|
244
|
+
available_slots: CalendarSlot[];
|
|
245
|
+
timezone: string;
|
|
246
|
+
title: string;
|
|
247
|
+
}
|
|
248
|
+
interface CalendarBookingPayload extends BaseComponent {
|
|
249
|
+
component_type: "calendar_booking";
|
|
250
|
+
data: CalendarBookingData;
|
|
251
|
+
}
|
|
252
|
+
type FormFieldType = "text" | "email" | "tel" | "number" | "select" | "multiselect" | "textarea" | "date" | "boolean";
|
|
253
|
+
interface FormFieldOption {
|
|
254
|
+
value: string;
|
|
255
|
+
label: string;
|
|
256
|
+
}
|
|
257
|
+
interface FormField {
|
|
258
|
+
name: string;
|
|
259
|
+
type: FormFieldType;
|
|
260
|
+
label: string;
|
|
261
|
+
placeholder?: string;
|
|
262
|
+
required?: boolean;
|
|
263
|
+
options?: string[] | FormFieldOption[];
|
|
264
|
+
validation?: {
|
|
265
|
+
pattern?: string;
|
|
266
|
+
minLength?: number;
|
|
267
|
+
maxLength?: number;
|
|
268
|
+
message?: string;
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
interface FormData {
|
|
272
|
+
title: string;
|
|
273
|
+
description?: string;
|
|
274
|
+
fields: FormField[];
|
|
275
|
+
submit_label?: string;
|
|
276
|
+
}
|
|
277
|
+
interface FormPayload extends BaseComponent {
|
|
278
|
+
component_type: "form";
|
|
279
|
+
data: FormData;
|
|
280
|
+
}
|
|
281
|
+
interface ButtonAction {
|
|
282
|
+
type: "navigate" | "trigger_component" | "api_call";
|
|
283
|
+
url?: string;
|
|
284
|
+
component_type?: string;
|
|
285
|
+
method?: "POST" | "GET";
|
|
286
|
+
endpoint?: string;
|
|
287
|
+
payload?: Record<string, string>;
|
|
288
|
+
}
|
|
289
|
+
interface ButtonItem {
|
|
290
|
+
id: string;
|
|
291
|
+
label: string;
|
|
292
|
+
style?: "primary" | "secondary" | "outline" | "destructive";
|
|
293
|
+
action: ButtonAction;
|
|
294
|
+
}
|
|
295
|
+
interface ButtonsData {
|
|
296
|
+
title?: string;
|
|
297
|
+
/** "free" renders bare chips attached to the previous agent message (quick replies); default is a boxed card. */
|
|
298
|
+
presentation?: "boxed" | "free";
|
|
299
|
+
/** What a completed pick leaves behind: a confirmation card (default) or the selection echoed as a user bubble. */
|
|
300
|
+
completionDisplay?: "confirmation" | "selected_item";
|
|
301
|
+
buttons: ButtonItem[];
|
|
302
|
+
}
|
|
303
|
+
interface ButtonsPayload extends BaseComponent {
|
|
304
|
+
component_type: "buttons";
|
|
305
|
+
data: ButtonsData;
|
|
306
|
+
}
|
|
307
|
+
interface ConfirmationData {
|
|
308
|
+
title: string;
|
|
309
|
+
message: string;
|
|
310
|
+
icon?: "success" | "info" | "warning" | "error";
|
|
311
|
+
details?: Array<{
|
|
312
|
+
label: string;
|
|
313
|
+
value: string;
|
|
314
|
+
}>;
|
|
315
|
+
}
|
|
316
|
+
interface ConfirmationPayload extends BaseComponent {
|
|
317
|
+
component_type: "confirmation";
|
|
318
|
+
data: ConfirmationData;
|
|
319
|
+
}
|
|
320
|
+
interface FlowStep {
|
|
321
|
+
id: string;
|
|
322
|
+
title: string;
|
|
323
|
+
component: SimpleComponentPayload;
|
|
324
|
+
}
|
|
325
|
+
interface InteractiveFlowData {
|
|
326
|
+
title?: string;
|
|
327
|
+
steps: FlowStep[];
|
|
328
|
+
on_complete: {
|
|
329
|
+
method: "POST" | "GET";
|
|
330
|
+
endpoint: string;
|
|
331
|
+
payload: Record<string, string>;
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
interface InteractiveFlowPayload extends BaseComponent {
|
|
335
|
+
component_type: "interactive_flow";
|
|
336
|
+
data: InteractiveFlowData;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Block Kit arrives over the `ringg.blocks` text stream (chunked, so no RPC
|
|
340
|
+
* size cap). The block tree itself is rendered by the platform UI; core
|
|
341
|
+
* treats the blocks as opaque and only carries the envelope.
|
|
342
|
+
*/
|
|
343
|
+
interface BlocksData {
|
|
344
|
+
blocks: unknown[];
|
|
345
|
+
tool_id?: string;
|
|
346
|
+
[key: string]: unknown;
|
|
347
|
+
}
|
|
348
|
+
interface BlocksPayload extends BaseComponent {
|
|
349
|
+
component_type: "blocks";
|
|
350
|
+
data: BlocksData;
|
|
351
|
+
}
|
|
352
|
+
type SimpleComponentPayload = CalendarBookingPayload | FormPayload | ButtonsPayload | ConfirmationPayload;
|
|
353
|
+
type ComponentPayload = SimpleComponentPayload | InteractiveFlowPayload | BlocksPayload;
|
|
354
|
+
/**
|
|
355
|
+
* Client-pack components core doesn't model (e.g. `disease_data`). Packs
|
|
356
|
+
* commit them to the timeline via `controller.addLocalComponent`; the platform
|
|
357
|
+
* UI owns their rendering.
|
|
358
|
+
*/
|
|
359
|
+
interface CustomComponentPayload {
|
|
360
|
+
component_type: string;
|
|
361
|
+
component_id: string;
|
|
362
|
+
[key: string]: unknown;
|
|
363
|
+
}
|
|
364
|
+
interface ComponentActionResponse {
|
|
365
|
+
success: boolean;
|
|
366
|
+
message: string;
|
|
367
|
+
next_step?: SimpleComponentPayload;
|
|
368
|
+
confirmation?: ConfirmationData;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Chat message types — the normalized message format used across platforms.
|
|
373
|
+
*/
|
|
374
|
+
|
|
375
|
+
interface ChatMessage {
|
|
376
|
+
name: string;
|
|
377
|
+
message: string;
|
|
378
|
+
isSelf: boolean;
|
|
379
|
+
/** Epoch ms; may carry a sub-ms fraction used only for stable ordering */
|
|
380
|
+
timestamp: number;
|
|
381
|
+
/** Reference URL attached to an agent reply (RAG source link) */
|
|
382
|
+
sourceUrl?: string;
|
|
383
|
+
/** When present, this message renders a component instead of text */
|
|
384
|
+
componentType?: string;
|
|
385
|
+
componentData?: ComponentPayload | CustomComponentPayload;
|
|
386
|
+
/** "system" renders an inline event pill (DOM action fired, etc.) instead of a bubble */
|
|
387
|
+
kind?: "system";
|
|
388
|
+
/** Secondary text shown next to a system pill's label (e.g. the event name) */
|
|
389
|
+
systemMeta?: string;
|
|
390
|
+
systemLevel?: "info" | "error";
|
|
391
|
+
}
|
|
392
|
+
interface ErrorState {
|
|
393
|
+
hasError: boolean;
|
|
394
|
+
message: string;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Widget event types — dispatched to the host application.
|
|
399
|
+
* These provide lifecycle hooks for the embedding app to react to widget
|
|
400
|
+
* state changes. Wire names are the `ringg:` prefixed kebab/snake names
|
|
401
|
+
* that existing integrations already listen for.
|
|
402
|
+
*/
|
|
403
|
+
|
|
404
|
+
type WidgetEventName = "ringg:widget_status" | "ringg:conversation_status" | "ringg:feedback_status" | "ringg:calendar_booking" | "ringg:component_acknowledgement";
|
|
405
|
+
interface WidgetStatusPayload {
|
|
406
|
+
status: "maximised" | "minimised";
|
|
407
|
+
mode: MediaType;
|
|
408
|
+
}
|
|
409
|
+
interface ConversationStatusPayload {
|
|
410
|
+
status: "started" | "ended";
|
|
411
|
+
mode: MediaType;
|
|
412
|
+
callId: string;
|
|
413
|
+
}
|
|
414
|
+
interface FeedbackStatusPayload {
|
|
415
|
+
status: "submitted" | "skipped";
|
|
416
|
+
callId: string;
|
|
417
|
+
rating?: number;
|
|
418
|
+
}
|
|
419
|
+
interface CalendarBookingEventPayload {
|
|
420
|
+
status: "shown" | "confirmed" | "failed";
|
|
421
|
+
componentId: string;
|
|
422
|
+
slotId?: string;
|
|
423
|
+
message?: string;
|
|
424
|
+
}
|
|
425
|
+
interface ComponentAcknowledgementPayload {
|
|
426
|
+
componentName: string;
|
|
427
|
+
componentId: string;
|
|
428
|
+
status: string;
|
|
429
|
+
}
|
|
430
|
+
interface WidgetEventMap {
|
|
431
|
+
"ringg:widget_status": WidgetStatusPayload;
|
|
432
|
+
"ringg:conversation_status": ConversationStatusPayload;
|
|
433
|
+
"ringg:feedback_status": FeedbackStatusPayload;
|
|
434
|
+
"ringg:calendar_booking": CalendarBookingEventPayload;
|
|
435
|
+
"ringg:component_acknowledgement": ComponentAcknowledgementPayload;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Host-page DOM action contract (`execute_dom_action` RPC payloads).
|
|
440
|
+
*
|
|
441
|
+
* Core normalizes the backend wire shape ({ action_id, event_name, detail } or
|
|
442
|
+
* the internal { id, kind, event_name, default_payload }) into `DomAction` and
|
|
443
|
+
* hands it to the platform's `onDomAction` port together with a logger that
|
|
444
|
+
* feeds the eventLogs chat pills. Only web can actually execute these — other
|
|
445
|
+
* platforms leave the port unset and the action is acked and dropped.
|
|
446
|
+
*/
|
|
447
|
+
type DomActionKind = "trigger_event";
|
|
448
|
+
interface DomAction {
|
|
449
|
+
id: string;
|
|
450
|
+
kind: DomActionKind;
|
|
451
|
+
description?: string;
|
|
452
|
+
event_name?: string;
|
|
453
|
+
default_payload?: Record<string, unknown>;
|
|
454
|
+
}
|
|
455
|
+
interface DomActionLogEntry {
|
|
456
|
+
id?: string;
|
|
457
|
+
label: string;
|
|
458
|
+
meta?: string;
|
|
459
|
+
level: "info" | "error";
|
|
460
|
+
}
|
|
461
|
+
type DomActionLogger = (entry: DomActionLogEntry) => void;
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Transport port — the surface core needs from a realtime SDK.
|
|
465
|
+
*
|
|
466
|
+
* Web injects an adapter over `livekit-client`; React Native injects one over
|
|
467
|
+
* `@livekit/react-native`. Core NEVER imports a LiveKit SDK directly — this
|
|
468
|
+
* interface is the only coupling point, mirroring the room APIs the widget
|
|
469
|
+
* actually uses (connect/prewarm, mic, RPC methods, text streams,
|
|
470
|
+
* transcription, connection state).
|
|
471
|
+
*/
|
|
472
|
+
type ConnectionState = "disconnected" | "connecting" | "connected" | "reconnecting";
|
|
473
|
+
interface TranscriptionSegment {
|
|
474
|
+
/** Stable segment id — updates stream in for the same id as STT refines */
|
|
475
|
+
id: string;
|
|
476
|
+
text: string;
|
|
477
|
+
final: boolean;
|
|
478
|
+
/** True when the segment belongs to the local participant */
|
|
479
|
+
isLocal: boolean;
|
|
480
|
+
/** Display name of the speaking participant, when known */
|
|
481
|
+
participantName?: string;
|
|
482
|
+
/** Epoch ms the segment was first received */
|
|
483
|
+
receivedAt: number;
|
|
484
|
+
}
|
|
485
|
+
interface IncomingChatMessage {
|
|
486
|
+
message: string;
|
|
487
|
+
/** True when sent by the local participant */
|
|
488
|
+
isSelf: boolean;
|
|
489
|
+
/** True when sent by the agent participant */
|
|
490
|
+
isAgent: boolean;
|
|
491
|
+
senderName?: string;
|
|
492
|
+
timestamp: number;
|
|
493
|
+
}
|
|
494
|
+
interface RpcInvocation {
|
|
495
|
+
method: string;
|
|
496
|
+
payload: string;
|
|
497
|
+
callerIdentity: string;
|
|
498
|
+
}
|
|
499
|
+
/** Incoming text stream (e.g. `lk.transcription` replies, `ringg.blocks`) */
|
|
500
|
+
interface IncomingTextStream {
|
|
501
|
+
/** Resolves with the full text once the stream completes */
|
|
502
|
+
readAll(): Promise<string>;
|
|
503
|
+
participantIdentity: string;
|
|
504
|
+
/** True when the stream was opened by the agent participant */
|
|
505
|
+
isAgent: boolean;
|
|
506
|
+
}
|
|
507
|
+
interface TransportAdapter {
|
|
508
|
+
connect(url: string, token: string): Promise<void>;
|
|
509
|
+
disconnect(): Promise<void>;
|
|
510
|
+
/** Best-effort prewarm (DNS/TLS/region resolution); failures must not throw */
|
|
511
|
+
prepareConnection(url: string): Promise<void>;
|
|
512
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
513
|
+
/** Send a chat message to the room. Core commits the local echo itself, so
|
|
514
|
+
* adapters must NOT surface locally-sent messages via onChatMessage. */
|
|
515
|
+
sendChatMessage(text: string): Promise<void>;
|
|
516
|
+
/** Invoke an RPC on the agent participant. Rejects if no agent is present. */
|
|
517
|
+
performRpcToAgent(method: string, payload: string): Promise<string>;
|
|
518
|
+
/** Handler return value is sent back as the RPC response payload. */
|
|
519
|
+
registerRpcMethod(method: string, handler: (invocation: RpcInvocation) => Promise<string>): void;
|
|
520
|
+
unregisterRpcMethod(method: string): void;
|
|
521
|
+
registerTextStreamHandler(topic: string, handler: (stream: IncomingTextStream) => void): void;
|
|
522
|
+
unregisterTextStreamHandler(topic: string): void;
|
|
523
|
+
onConnectionStateChange(handler: (state: ConnectionState) => void): () => void;
|
|
524
|
+
/** Fires when the room disconnects or the agent participant leaves */
|
|
525
|
+
onSessionEnded(handler: () => void): () => void;
|
|
526
|
+
onTranscription(handler: (segment: TranscriptionSegment) => void): () => void;
|
|
527
|
+
onChatMessage(handler: (message: IncomingChatMessage) => void): () => void;
|
|
528
|
+
/**
|
|
529
|
+
* Fires when acquiring a media device fails after connect (LiveKit's
|
|
530
|
+
* `MediaDevicesError`) — e.g. the mic permission is revoked mid-call. Core
|
|
531
|
+
* surfaces the production error message on it.
|
|
532
|
+
*/
|
|
533
|
+
onMediaDevicesError(handler: () => void): () => void;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Event bus port — abstraction over platform-specific host-event dispatch.
|
|
538
|
+
*
|
|
539
|
+
* Web: DOM CustomEvents on `window` (existing integrations listen for these)
|
|
540
|
+
* React Native: callback props / in-memory listeners
|
|
541
|
+
*/
|
|
542
|
+
|
|
543
|
+
interface EventBus {
|
|
544
|
+
emit<K extends WidgetEventName>(event: K, payload: WidgetEventMap[K]): void;
|
|
545
|
+
/** Subscribe to a typed event. Returns an unsubscribe function. */
|
|
546
|
+
on<K extends WidgetEventName>(event: K, handler: (payload: WidgetEventMap[K]) => void): () => void;
|
|
547
|
+
/** Remove all listeners for one event, or all events if omitted. */
|
|
548
|
+
off(event?: WidgetEventName): void;
|
|
549
|
+
}
|
|
550
|
+
/** In-memory event bus — for React Native or testing. */
|
|
551
|
+
declare function createCallbackEventBus(): EventBus;
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Clock port — injectable time and timers.
|
|
555
|
+
*
|
|
556
|
+
* The widget's UX-critical logic is timing logic (typing-indicator minimum
|
|
557
|
+
* duration, chat-widget grace buffering). Routing all time access through
|
|
558
|
+
* this port makes that logic deterministic under test.
|
|
559
|
+
*/
|
|
560
|
+
type TimerHandle = ReturnType<typeof setTimeout>;
|
|
561
|
+
interface Clock {
|
|
562
|
+
/** Monotonic-enough milliseconds (wall clock is fine outside tests) */
|
|
563
|
+
now(): number;
|
|
564
|
+
setTimeout(fn: () => void, ms: number): TimerHandle;
|
|
565
|
+
clearTimeout(handle: TimerHandle): void;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Small platform ports — capabilities core needs but cannot implement
|
|
570
|
+
* portably. Web and React Native each provide implementations.
|
|
571
|
+
*/
|
|
572
|
+
/** Microphone permission — navigator.permissions on web, RN permissions API on native */
|
|
573
|
+
interface MicPermissionPort {
|
|
574
|
+
isGranted(): Promise<boolean>;
|
|
575
|
+
/** Prompt the user; resolves with the final grant state */
|
|
576
|
+
request(): Promise<boolean>;
|
|
577
|
+
}
|
|
578
|
+
/** New-message notification sound — HTMLAudioElement on web, a sound lib on RN */
|
|
579
|
+
interface NotificationPlayer {
|
|
580
|
+
/** Best-effort playback; must never throw (autoplay policies etc.) */
|
|
581
|
+
play(): void;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* API client for the Ringg backend.
|
|
586
|
+
* Pure fetch-based — works in any JS runtime (browser, Node, React Native).
|
|
587
|
+
* URLs are always injected by the platform entry point via a UrlResolver;
|
|
588
|
+
* core embeds no endpoints.
|
|
589
|
+
*/
|
|
590
|
+
|
|
591
|
+
interface EnvironmentUrls {
|
|
592
|
+
backendUrl: string;
|
|
593
|
+
livekitUrl: string;
|
|
594
|
+
}
|
|
595
|
+
interface UrlResolver {
|
|
596
|
+
resolve(mode: EnvironmentMode): EnvironmentUrls;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* RPC message formatting — transforms between backend RPC format and internal
|
|
601
|
+
* component format.
|
|
602
|
+
*
|
|
603
|
+
* The backend sends `send_dynamic_data` RPCs like:
|
|
604
|
+
* { component_type: "render_component_calendar", component_config: { ... } }
|
|
605
|
+
* The widget needs them in the internal format:
|
|
606
|
+
* { component_type: "calendar_booking", component_id: "calendar_1234", data: { ... } }
|
|
607
|
+
*
|
|
608
|
+
* This module handles both directions.
|
|
609
|
+
*/
|
|
610
|
+
|
|
611
|
+
/** A Block Kit action as it goes on the wire (the UI-only `label` never does). */
|
|
612
|
+
interface BlocksActionWire {
|
|
613
|
+
action_id: string;
|
|
614
|
+
value?: unknown;
|
|
615
|
+
values: Record<string, unknown>;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Base store — the one state-sharing primitive in core.
|
|
620
|
+
*
|
|
621
|
+
* Platform bindings stay thin because they all consume the same shape:
|
|
622
|
+
* web (Lit): subscribe(() => this.host.requestUpdate())
|
|
623
|
+
* RN (React): useSyncExternalStore(store.subscribe, store.getSnapshot)
|
|
624
|
+
*
|
|
625
|
+
* Snapshots must be immutable values — stores rebuild them on change so
|
|
626
|
+
* reference equality is a valid dirty check. `getSnapshot`/`subscribe` are
|
|
627
|
+
* closures, so they stay bound when bindings pass them unbound (React:
|
|
628
|
+
* `useSyncExternalStore(store.subscribe, store.getSnapshot)`).
|
|
629
|
+
*/
|
|
630
|
+
interface Store<T> {
|
|
631
|
+
getSnapshot(): T;
|
|
632
|
+
subscribe(listener: (snapshot: T) => void): () => void;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Shell store — widget open/close/feedback lifecycle and the active call id.
|
|
637
|
+
* Ported from the alpha WidgetStateController; emits `ringg:widget_status`
|
|
638
|
+
* host events on visibility transitions.
|
|
639
|
+
*/
|
|
640
|
+
|
|
641
|
+
type WidgetViewState = "closed" | "open" | "feedback";
|
|
642
|
+
interface ShellSnapshot {
|
|
643
|
+
viewState: WidgetViewState;
|
|
644
|
+
currentCallId: string | null;
|
|
645
|
+
callMode: MediaType;
|
|
646
|
+
}
|
|
647
|
+
interface ShellStore extends Store<ShellSnapshot> {
|
|
648
|
+
readonly isOpen: boolean;
|
|
649
|
+
/**
|
|
650
|
+
* Feedback is pending (post-call screen not yet submitted/skipped). Mirrors
|
|
651
|
+
* the production hook's independent `showFeedback` flag: stays true across
|
|
652
|
+
* minimize so reopening restores the feedback view. The derived snapshot
|
|
653
|
+
* `viewState` only reads "feedback" while the panel is actually open.
|
|
654
|
+
*/
|
|
655
|
+
readonly isShowingFeedback: boolean;
|
|
656
|
+
open(): void;
|
|
657
|
+
minimize(): void;
|
|
658
|
+
/** Forget the call, clear feedback and minimize — a conversation fully ended. */
|
|
659
|
+
close(): void;
|
|
660
|
+
toggle(): void;
|
|
661
|
+
showFeedback(): void;
|
|
662
|
+
setCallMode(mode: MediaType): void;
|
|
663
|
+
setCurrentCallId(callId: string | null): void;
|
|
664
|
+
dispose(): void;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Session store — the call lifecycle state machine.
|
|
669
|
+
*
|
|
670
|
+
* idle → starting → live-optimistic → connected → idle
|
|
671
|
+
*
|
|
672
|
+
* "live-optimistic" is load-bearing UX (lifted from production): the flag
|
|
673
|
+
* flips the moment /calling/webcall succeeds — before transport.connect()
|
|
674
|
+
* resolves. The call exists server-side at that point, so the calling layout
|
|
675
|
+
* mounts one HTTP round trip after Start instead of waiting out the LiveKit
|
|
676
|
+
* handshake. A connect failure rolls it back so the start screen (with the
|
|
677
|
+
* error) returns instead of a dead calling layout.
|
|
678
|
+
*/
|
|
679
|
+
|
|
680
|
+
type SessionPhase = "idle" | "starting" | "live-optimistic" | "connected";
|
|
681
|
+
interface SessionSnapshot {
|
|
682
|
+
phase: SessionPhase;
|
|
683
|
+
/** True while a start/end request is in flight */
|
|
684
|
+
isLoading: boolean;
|
|
685
|
+
/** UI gate for the calling layout — optimistic OR actually connected */
|
|
686
|
+
isSessionLive: boolean;
|
|
687
|
+
connectionState: ConnectionState;
|
|
688
|
+
error: ErrorState;
|
|
689
|
+
}
|
|
690
|
+
interface SessionStartResult {
|
|
691
|
+
callId: string;
|
|
692
|
+
slashCommands: SlashCommand[];
|
|
693
|
+
}
|
|
694
|
+
interface SessionStore extends Store<SessionSnapshot> {
|
|
695
|
+
readonly isSessionLive: boolean;
|
|
696
|
+
/** Warm DNS/TLS/region on widget open — best-effort, never blocks connect. */
|
|
697
|
+
prewarm(): void;
|
|
698
|
+
/**
|
|
699
|
+
* Start a call. Resolves with the call id + runtime slash commands on
|
|
700
|
+
* success; resolves null when blocked (mic denied) or failed (error is in
|
|
701
|
+
* the snapshot). The caller (controller) resets conversation stores first.
|
|
702
|
+
*/
|
|
703
|
+
start(params: {
|
|
704
|
+
agentId: string;
|
|
705
|
+
variables: WidgetVariables;
|
|
706
|
+
mediaType: MediaType;
|
|
707
|
+
}): Promise<SessionStartResult | null>;
|
|
708
|
+
/** Disconnect the transport and return to idle. Never throws. */
|
|
709
|
+
end(): Promise<void>;
|
|
710
|
+
clearError(): void;
|
|
711
|
+
dispose(): void;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Message store — the single ordered conversation timeline.
|
|
716
|
+
*
|
|
717
|
+
* Three sources feed it and must interleave correctly:
|
|
718
|
+
* - chat messages (text mode, incl. the local optimistic echo)
|
|
719
|
+
* - transcription segments (audio mode; segments UPDATE in place by id as STT
|
|
720
|
+
* refines, keeping their original position)
|
|
721
|
+
* - component messages (RPC widgets, Block Kit — commit-time stamped so a
|
|
722
|
+
* buffered widget sorts strictly after the agent message that released it)
|
|
723
|
+
*
|
|
724
|
+
* Ordering: entries sort by timestamp. The store hands out strictly
|
|
725
|
+
* monotonically increasing timestamps (sub-ms bumped when the clock hasn't
|
|
726
|
+
* advanced), replacing the production widget's `Date.now() + Math.random()`
|
|
727
|
+
* collision-avoidance hacks with a deterministic policy.
|
|
728
|
+
*/
|
|
729
|
+
|
|
730
|
+
interface MessageSnapshot {
|
|
731
|
+
messages: readonly ChatMessage[];
|
|
732
|
+
}
|
|
733
|
+
interface MessageStore extends Store<MessageSnapshot> {
|
|
734
|
+
/** Next timestamp, strictly greater than any previously issued one. */
|
|
735
|
+
nextTimestamp(): number;
|
|
736
|
+
/** Commit a chat message. Agent messages absorb any pending source URL. */
|
|
737
|
+
addChatMessage(params: {
|
|
738
|
+
message: string;
|
|
739
|
+
isSelf: boolean;
|
|
740
|
+
name?: string;
|
|
741
|
+
timestamp?: number;
|
|
742
|
+
}): ChatMessage;
|
|
743
|
+
/** Commit a component message (commit-time stamped — see module docs). */
|
|
744
|
+
addComponent(component: ComponentPayload | CustomComponentPayload): ChatMessage;
|
|
745
|
+
/** Commit an inline system-event pill (DOM action fired, etc.). */
|
|
746
|
+
addSystemLog(params: {
|
|
747
|
+
label: string;
|
|
748
|
+
meta?: string;
|
|
749
|
+
level: "info" | "error";
|
|
750
|
+
}): void;
|
|
751
|
+
/**
|
|
752
|
+
* Render the user's quick-reply selection as a chat bubble anchored just
|
|
753
|
+
* after the prompting component — even if a follow-up agent message has
|
|
754
|
+
* already arrived.
|
|
755
|
+
*/
|
|
756
|
+
addLocalSelection(componentId: string, label: string): void;
|
|
757
|
+
/**
|
|
758
|
+
* Upsert a transcription segment (audio mode). Position is stable by id.
|
|
759
|
+
* When a source URL is pending, it attaches to the newest transcription if
|
|
760
|
+
* that is an agent utterance without one; otherwise it is dropped
|
|
761
|
+
* (production clears the ref on any update that doesn't append).
|
|
762
|
+
*/
|
|
763
|
+
upsertTranscription(segment: TranscriptionSegment): void;
|
|
764
|
+
/** Stash a RAG source URL; the next agent reply carries it. */
|
|
765
|
+
setPendingSourceUrl(url: string): void;
|
|
766
|
+
/** Drop a stashed source URL without attaching it. */
|
|
767
|
+
clearPendingSourceUrl(): void;
|
|
768
|
+
reset(): void;
|
|
769
|
+
dispose(): void;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Typing store — drives the "agent is typing" indicator (text mode).
|
|
774
|
+
*
|
|
775
|
+
* Behavior lifted from the production widget:
|
|
776
|
+
* - Dots appear the moment the user sends (we always expect a reply); the
|
|
777
|
+
* min-duration timer anchors only when they first appear, so back-to-back
|
|
778
|
+
* sends keep one continuous indicator instead of resetting it.
|
|
779
|
+
* - Any landed backend response — text or RPC — answers the pending turn and
|
|
780
|
+
* drops the dots, unconditionally.
|
|
781
|
+
* - Replies that land faster than MIN_TYPING_INDICATOR_MS wait out the
|
|
782
|
+
* remainder before committing, otherwise the indicator just flashes.
|
|
783
|
+
*/
|
|
784
|
+
|
|
785
|
+
interface TypingSnapshot {
|
|
786
|
+
isTyping: boolean;
|
|
787
|
+
}
|
|
788
|
+
interface TypingStore extends Store<TypingSnapshot> {
|
|
789
|
+
/** User sent a message — show the dots (idempotent while already shown). */
|
|
790
|
+
showOnUserSend(): void;
|
|
791
|
+
/** A backend response landed (or the turn errored) — drop the dots now. */
|
|
792
|
+
clear(): void;
|
|
793
|
+
/**
|
|
794
|
+
* Resolve once the indicator has been visible for MIN_TYPING_INDICATOR_MS.
|
|
795
|
+
* Callers await this before committing a fast agent reply.
|
|
796
|
+
* Resolves immediately when the dots aren't showing.
|
|
797
|
+
*/
|
|
798
|
+
waitMinDuration(): Promise<void>;
|
|
799
|
+
dispose(): void;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Component store — interactive components pushed by the backend mid-call.
|
|
804
|
+
*
|
|
805
|
+
* Chat-mode anchoring (lifted from production): widget RPCs land instantly
|
|
806
|
+
* while the agent's text streams a beat later. In text mode we buffer the
|
|
807
|
+
* widget until the accompanying message lands, then commit it right after so
|
|
808
|
+
* it sorts below. If no message arrives within CHAT_WIDGET_GRACE_MS, flush
|
|
809
|
+
* anyway. Audio mode commits immediately.
|
|
810
|
+
*
|
|
811
|
+
* Also owns the response side: which components still await a user response
|
|
812
|
+
* (and the original config needed to format it), completed flows, and the
|
|
813
|
+
* extension registry that lets client packs handle domain-specific
|
|
814
|
+
* `send_dynamic_data` component types without polluting core.
|
|
815
|
+
*/
|
|
816
|
+
|
|
817
|
+
interface ComponentSnapshot {
|
|
818
|
+
completedFlowIds: ReadonlySet<string>;
|
|
819
|
+
}
|
|
820
|
+
interface PendingResponse {
|
|
821
|
+
name: string;
|
|
822
|
+
originalConfig: unknown;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Extension hook for domain-specific dynamic-data payloads (client packs).
|
|
826
|
+
* Return true when the payload was handled; false lets core continue with
|
|
827
|
+
* its built-in handling.
|
|
828
|
+
*/
|
|
829
|
+
type DynamicDataExtension = (componentName: string, parsed: Record<string, unknown>) => boolean;
|
|
830
|
+
interface ComponentStore extends Store<ComponentSnapshot> {
|
|
831
|
+
/** Queue or commit a component depending on call mode (see module docs). */
|
|
832
|
+
add(component: ComponentPayload, mode: MediaType): void;
|
|
833
|
+
/** The agent's text landed — release buffered widgets right after it. */
|
|
834
|
+
onAgentTextLanded(): void;
|
|
835
|
+
flush(): void;
|
|
836
|
+
trackPendingResponse(componentId: string, pending: PendingResponse): void;
|
|
837
|
+
/** Consume the pending-response record for a component (one-shot). */
|
|
838
|
+
takePendingResponse(componentId: string): PendingResponse | undefined;
|
|
839
|
+
markFlowComplete(componentId: string): void;
|
|
840
|
+
/** Register a client-pack handler. Returns an unregister function. */
|
|
841
|
+
registerExtension(extension: DynamicDataExtension): () => void;
|
|
842
|
+
/** Offer a payload to extensions; true when one handled it. */
|
|
843
|
+
runExtensions(componentName: string, parsed: Record<string, unknown>): boolean;
|
|
844
|
+
/** Drop buffered widgets + timers so nothing fires after the call ended. */
|
|
845
|
+
reset(): void;
|
|
846
|
+
dispose(): void;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Slash command store — merges commands declared in config with commands the
|
|
851
|
+
* backend enables at webcall time. Config commands win on collisions.
|
|
852
|
+
*/
|
|
853
|
+
|
|
854
|
+
interface SlashCommandSnapshot {
|
|
855
|
+
commands: readonly SlashCommand[];
|
|
856
|
+
}
|
|
857
|
+
interface SlashCommandStore extends Store<SlashCommandSnapshot> {
|
|
858
|
+
/** Commands from the webcall response */
|
|
859
|
+
setRuntimeCommands(commands: SlashCommand[]): void;
|
|
860
|
+
reset(): void;
|
|
861
|
+
dispose(): void;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* RinggWidgetController — the composition root of the widget brain.
|
|
866
|
+
*
|
|
867
|
+
* Owns every store, wires transport events into them, and exposes the
|
|
868
|
+
* imperative API the platform UIs call. This is the headless replacement for
|
|
869
|
+
* the production widget's App.tsx orchestration: platform bindings render
|
|
870
|
+
* snapshots and forward user intents; ALL behavior lives here or in the
|
|
871
|
+
* stores.
|
|
872
|
+
*/
|
|
873
|
+
|
|
874
|
+
interface ControllerPorts {
|
|
875
|
+
transport: TransportAdapter;
|
|
876
|
+
urlResolver: UrlResolver;
|
|
877
|
+
eventBus?: EventBus;
|
|
878
|
+
clock?: Clock;
|
|
879
|
+
micPermission?: MicPermissionPort;
|
|
880
|
+
notification?: NotificationPlayer;
|
|
881
|
+
/**
|
|
882
|
+
* Host-page action executor (web-only: `execute_dom_action` payloads).
|
|
883
|
+
* Core normalizes the wire payload into a `DomAction` and provides the
|
|
884
|
+
* eventLogs-gated system-log sink. Platforms that can't execute host
|
|
885
|
+
* actions leave this unset; payloads are acked and dropped.
|
|
886
|
+
*/
|
|
887
|
+
onDomAction?: (action: DomAction, log: DomActionLogger) => void;
|
|
888
|
+
}
|
|
889
|
+
interface RinggWidgetController {
|
|
890
|
+
readonly config: RinggWidgetConfig;
|
|
891
|
+
readonly theme: ResolvedWidgetTheme;
|
|
892
|
+
readonly eventBus: EventBus;
|
|
893
|
+
readonly shell: ShellStore;
|
|
894
|
+
readonly session: SessionStore;
|
|
895
|
+
readonly messages: MessageStore;
|
|
896
|
+
readonly typing: TypingStore;
|
|
897
|
+
readonly components: ComponentStore;
|
|
898
|
+
readonly slashCommands: SlashCommandStore;
|
|
899
|
+
openWidget(): void;
|
|
900
|
+
minimizeWidget(): void;
|
|
901
|
+
/** Trigger click: toggles, or starts directly with bypassStartScreen. */
|
|
902
|
+
handleTriggerClick(): void;
|
|
903
|
+
startCall(mediaType: MediaType): Promise<void>;
|
|
904
|
+
endCall(): Promise<void>;
|
|
905
|
+
sendMessage(text: string): Promise<void>;
|
|
906
|
+
/** Send the user's component response (slot pick, form submit, button tap). */
|
|
907
|
+
sendComponentResponse(componentId: string, responseData: Record<string, unknown>): Promise<void>;
|
|
908
|
+
/**
|
|
909
|
+
* Block Kit action → agent (`receive_component_blocks` envelope over
|
|
910
|
+
* `receive_dynamic_data`). Resolves only after the RPC round-trips — the UI
|
|
911
|
+
* ties its in-flight spinner to this Promise. Transport failures are
|
|
912
|
+
* swallowed (production logs and moves on), so it never rejects.
|
|
913
|
+
*/
|
|
914
|
+
sendBlocksAction(toolId: string | undefined, componentId: string, action: BlocksActionWire): Promise<void>;
|
|
915
|
+
/**
|
|
916
|
+
* Component API for interactive-flow `on_complete` / `api_call` button
|
|
917
|
+
* actions. Relative endpoints resolve against the backend URL; auth headers
|
|
918
|
+
* match the widget's other backend calls.
|
|
919
|
+
*/
|
|
920
|
+
callComponentApi(method: "POST" | "GET", endpoint: string, payload: Record<string, string>): Promise<ComponentActionResponse>;
|
|
921
|
+
/**
|
|
922
|
+
* Raw dynamic-data RPC for client packs (e.g. disease search/submit) — the
|
|
923
|
+
* payload carries its own `component_type`. Rejects on transport failure so
|
|
924
|
+
* packs can implement their own fallbacks.
|
|
925
|
+
*/
|
|
926
|
+
sendDynamicData(payload: Record<string, unknown>): Promise<void>;
|
|
927
|
+
/**
|
|
928
|
+
* Commit a pack-owned component to the timeline immediately (parity with
|
|
929
|
+
* production's local widget messages — no buffering, no shell side effects).
|
|
930
|
+
*/
|
|
931
|
+
addLocalComponent(component: CustomComponentPayload): void;
|
|
932
|
+
/** Show the user's quick-reply selection as a chat bubble. */
|
|
933
|
+
displaySelection(componentId: string, label: string): void;
|
|
934
|
+
markFlowComplete(componentId: string): void;
|
|
935
|
+
/** Client-pack hook for domain-specific dynamic-data payloads. */
|
|
936
|
+
registerDynamicDataExtension(extension: DynamicDataExtension): () => void;
|
|
937
|
+
sendSlashCommand(command: SlashCommand): Promise<void>;
|
|
938
|
+
submitFeedback(rating: number, comment: string): Promise<void>;
|
|
939
|
+
skipFeedback(): void;
|
|
940
|
+
destroy(): void;
|
|
941
|
+
}
|
|
942
|
+
declare function createRinggWidgetController(config: RinggWidgetConfig, ports: ControllerPorts): RinggWidgetController;
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* React bindings — shared by @ringg/web (React DOM) and @ringg/react-native.
|
|
946
|
+
*
|
|
947
|
+
* Every hook is a useSyncExternalStore call over a core store; there is no
|
|
948
|
+
* behavior here and there must never be. A fix to typing timing, message
|
|
949
|
+
* ordering, or session lifecycle lands in the stores once and both platforms
|
|
950
|
+
* pick it up.
|
|
951
|
+
*
|
|
952
|
+
* Imported via the "@ringg/core/react" subpath so non-React consumers of
|
|
953
|
+
* core never touch the react dependency (it's an optional peer).
|
|
954
|
+
*/
|
|
955
|
+
|
|
956
|
+
declare function useStoreSnapshot<T>(store: Store<T>): T;
|
|
957
|
+
declare const useRinggMessages: (controller: RinggWidgetController) => MessageSnapshot;
|
|
958
|
+
declare const useRinggTyping: (controller: RinggWidgetController) => TypingSnapshot;
|
|
959
|
+
declare const useRinggSession: (controller: RinggWidgetController) => SessionSnapshot;
|
|
960
|
+
declare const useRinggShell: (controller: RinggWidgetController) => ShellSnapshot;
|
|
961
|
+
declare const useRinggComponents: (controller: RinggWidgetController) => ComponentSnapshot;
|
|
962
|
+
declare const useRinggSlashCommands: (controller: RinggWidgetController) => SlashCommandSnapshot;
|
|
963
|
+
|
|
964
|
+
export { type ControllerPorts, type RinggWidgetConfig, type RinggWidgetController, type TransportAdapter, createCallbackEventBus, createRinggWidgetController, useRinggComponents, useRinggMessages, useRinggSession, useRinggShell, useRinggSlashCommands, useRinggTyping, useStoreSnapshot };
|