@alxxsck/ai-assistant 1.0.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.
Files changed (56) hide show
  1. package/README.md +345 -0
  2. package/dist/App.d.ts +26 -0
  3. package/dist/Avatar-XISRbl7X.js +29627 -0
  4. package/dist/OrbitControls-XelJ40xM.js +458 -0
  5. package/dist/api/messages.api.d.ts +20 -0
  6. package/dist/api/messages.api.types.d.ts +88 -0
  7. package/dist/api/messages.api.utils.d.ts +2 -0
  8. package/dist/api/voice.api.d.ts +65 -0
  9. package/dist/api/voice.api.types.d.ts +37 -0
  10. package/dist/avatar/AnimationManager.d.ts +39 -0
  11. package/dist/avatar/Avatar.d.ts +69 -0
  12. package/dist/avatar/BlendShapeScenario.d.ts +19 -0
  13. package/dist/bridge-ai-chat-widget.es.js +10194 -0
  14. package/dist/constants.d.ts +2 -0
  15. package/dist/context/ChatWidgetContext.d.ts +32 -0
  16. package/dist/domain/command/CommandRuntime.d.ts +50 -0
  17. package/dist/domain/command/command.types.d.ts +70 -0
  18. package/dist/domain/message/cable/AnyCable.service.d.ts +34 -0
  19. package/dist/domain/message/cable/CableContext.d.ts +17 -0
  20. package/dist/domain/message/cable/CableContext.types.d.ts +19 -0
  21. package/dist/domain/message/cable/WidgetChat.channel.d.ts +4 -0
  22. package/dist/domain/message/cable/widget-message.d.ts +3 -0
  23. package/dist/domain/message/message.store.d.ts +51 -0
  24. package/dist/domain/scenario/scenario-choice.d.ts +17 -0
  25. package/dist/domain/ui/ui.service.d.ts +7 -0
  26. package/dist/domain/voice/voice.store.d.ts +69 -0
  27. package/dist/hooks/useDeviceManager.d.ts +25 -0
  28. package/dist/hooks/useSessionExpiryChecks.d.ts +3 -0
  29. package/dist/index.d.ts +41 -0
  30. package/dist/index.types.d.ts +50 -0
  31. package/dist/lib/isSessionExpired.d.ts +2 -0
  32. package/dist/lib/logger.d.ts +12 -0
  33. package/dist/lib/retrieveContent.d.ts +6 -0
  34. package/dist/lib/utils.d.ts +2 -0
  35. package/dist/lib//321/201ontentRenderer.d.ts +6 -0
  36. package/dist/lil-gui.esm-BicvE3D7.js +1263 -0
  37. package/dist/react/mount.d.ts +3 -0
  38. package/dist/types/index.d.ts +9 -0
  39. package/dist/ui/chat-routes.d.ts +11 -0
  40. package/dist/ui/components/ChatFooter.d.ts +2 -0
  41. package/dist/ui/components/ChatListItem.d.ts +10 -0
  42. package/dist/ui/components/ChatListPage.d.ts +1 -0
  43. package/dist/ui/components/ChatMessage.d.ts +8 -0
  44. package/dist/ui/components/ChatTextMode.d.ts +1 -0
  45. package/dist/ui/components/ChatTopControls.d.ts +1 -0
  46. package/dist/ui/components/ChatTypingIndicator.d.ts +4 -0
  47. package/dist/ui/components/ChatUnreadIndicator.d.ts +1 -0
  48. package/dist/ui/components/ChatVoiceMode.d.ts +1 -0
  49. package/dist/ui/components/ChatWidget.d.ts +4 -0
  50. package/dist/ui/components/ErrorMessage.d.ts +7 -0
  51. package/dist/ui/components/VoiceModeMessage.d.ts +12 -0
  52. package/dist/ui/components/overlays/ChatBlurOverlayBottom.d.ts +1 -0
  53. package/dist/ui/components/overlays/ChatBlurOverlayTop.d.ts +1 -0
  54. package/dist/ui/components/overlays/ChatDarkOverlay.d.ts +1 -0
  55. package/dist/ui/error-messages.d.ts +7 -0
  56. package/package.json +102 -0
package/README.md ADDED
@@ -0,0 +1,345 @@
1
+ # Bridge AI Chat Widget
2
+
3
+ Embeddable React + TypeScript chat widget for Bridge AI agents, with:
4
+
5
+ - text chat
6
+ - optional real-time voice mode (WebRTC)
7
+ - optional animated 3D avatar (three.js)
8
+ - WebSocket live message updates (AnyCable)
9
+ - Shadow DOM isolation for host-page style safety
10
+
11
+ ## What This Project Is
12
+
13
+ `bridgeapp-ai-chat-widget` is an ESM library bundle that host applications instantiate via:
14
+
15
+ - `new ChatWidgetInstance(config)`
16
+
17
+ The instance mounts a React app into a host element's Shadow DOM, fetches static asset mappings from a remote manifest, initializes chat/voice/avatar modules, and exposes a small integration API (`setOpen`, `destroy`, `setCustomerInteractionSession`, events).
18
+
19
+ ## Tech Stack
20
+
21
+ - React 19
22
+ - TypeScript
23
+ - Vite (dev app + library build)
24
+ - Tailwind CSS 4
25
+ - Zustand (state stores)
26
+ - three.js + FBX assets (avatar rendering/animation)
27
+ - AnyCable Web client (live chat transport)
28
+ - WebRTC + data channels (voice mode)
29
+
30
+ ## Repository Layout
31
+
32
+ High-level structure:
33
+
34
+ - `src/index.ts` - public library entry (`ChatWidgetInstance`)
35
+ - `src/index.types.ts` - public config/types surface
36
+ - `src/App.tsx` - provider composition root
37
+ - `src/react/mount.tsx` - React root mount helper
38
+ - `src/ui/components` - UI components (chat widget, routes, controls)
39
+ - `src/domain/message` - message store + websocket channel integration
40
+ - `src/domain/voice` - voice lifecycle + WebRTC state
41
+ - `src/avatar` - three.js avatar scene, animation manager, blend shapes
42
+ - `src/api` - HTTP clients for messages/voice/session-related calls
43
+ - `src/context/ChatWidgetContext.tsx` - widget-level UI state/context
44
+ - `src/dev` - local/dev/demo stand bootstrap (non-library integration path)
45
+ - `src/assets` - local assets used by the widget and avatar (not used directly, rather this whole folder is getting pushed to assets repo)
46
+ - `scripts` - utility scripts (for example asset sync to remote static repo)
47
+
48
+ ## Runtime Architecture
49
+
50
+ ### 1) Host Integration Layer
51
+
52
+ `ChatWidgetInstance` (`src/index.ts`) is the host-facing API:
53
+
54
+ - validates `mountElementId`
55
+ - creates `shadowRoot` on mount element
56
+ - injects bundled CSS into shadow root
57
+ - fetches `${ASSETS_URL}/manifest.json`
58
+ - resolves hashed asset paths via `resolveAssetPath(path)`
59
+ - optionally merges remote config (`remoteConfig`)
60
+ - mounts React app and wires `AppApi` callbacks/events
61
+
62
+ ### 2) React App + Providers
63
+
64
+ `App` composes:
65
+
66
+ - `ChatWidgetContextProvider` (widget open state, sizing, config, loading progress)
67
+ - `CableProvider` (AnyCable websocket service + network status)
68
+ - `HashRouter` (voice/text/list routes)
69
+
70
+ ### 3) Messaging Domain
71
+
72
+ `useMessageStore` handles:
73
+
74
+ - initial thread bootstrap (`ListMessages`, `ListMessagesForThreads`)
75
+ - optimistic and server-driven message updates
76
+ - thread map and per-thread message derivation
77
+ - message send (`CreateMessage`)
78
+ - agentic steps merge/update and rendering support
79
+
80
+ Live updates arrive via AnyCable channel events and are pushed into the store.
81
+
82
+ ### 4) Voice Domain
83
+
84
+ `useVoiceStore` manages full voice call lifecycle:
85
+
86
+ - starts voice session via API (`StartVoiceSession`)
87
+ - builds RTCPeerConnection using TURN credentials from session payload
88
+ - sends SDP offer / receives answer
89
+ - buffers + sends ICE candidates
90
+ - consumes voice data channels (`voice-events`, `voice-input`)
91
+ - streams remote audio to an `Audio` element
92
+ - handles mic enable/disable, device switching, and teardown (`EndSession` + `EndVoiceSession`)
93
+
94
+ ### 5) Avatar Domain
95
+
96
+ `Avatar` + `AnimationManager`:
97
+
98
+ - initializes three.js renderer/camera/lights/materials
99
+ - loads model + texture + animation assets from resolved remote paths
100
+ - emits `loading_progress`
101
+ - starts animation mixer loops
102
+ - maps viseme timeline to morph target animation during speech
103
+ - exposes external animation actions (`excited`, `fix_hair`, `spin`, `kiss`)
104
+
105
+ When all assets load, widget emits `avatar_ready` through `ChatWidgetInstance`.
106
+
107
+ ## Public API
108
+
109
+ Import:
110
+
111
+ ```ts
112
+ import {
113
+ ChatWidgetInstance,
114
+ type WidgetConfig,
115
+ } from "bridgeapp-ai-chat-widget";
116
+ ```
117
+
118
+ ### `ExternalWidgetConfig`
119
+
120
+ - `mountElementId: string`
121
+ - - `configName: string` - remote config, lives at the same place as other static assets
122
+ - `customerInteractionSession?: { accessToken; chatId; expiresAt }` - if there's no session (separate case for dev/demo mode) - we look for customerId in remote config and create session ourselves.
123
+
124
+ ### `RemoteWidgetConfig`
125
+
126
+ Actual widget config
127
+
128
+ - `agentId: string`
129
+ - `customerId?: string`
130
+ - `wsUrl: string`
131
+ - `apiUrl: string`
132
+ - `AIAvatar?: boolean` - is 3D avatar be used
133
+ - `voiceEnabled?: boolean` - is voice mode enebled
134
+ - `background?: boolean` - clear or visible background
135
+ - `footer?: boolean` - is footer rendered
136
+ - `debug?: boolean` (avatar debug GUI)
137
+ - `manualOpen?: boolean` - is widget trigger rendered or should it be open only programatically
138
+
139
+ ### Instance Methods
140
+
141
+ - `setOpen(open: boolean): void` - open/close widget
142
+ - `setCustomerInteractionSession(session): void` - update session after initialization (on session expire)
143
+ - `playAnimation(name): void`
144
+ - `destroy(): void`
145
+
146
+ ### Events
147
+
148
+ - `avatar_ready` - emitted once avatar assets are fully loaded
149
+ - `renew_session` - emitted when session is expired or near expiry
150
+
151
+ ## Host Usage Example
152
+
153
+ ```ts
154
+ import { ChatWidgetInstance } from "bridgeapp-ai-chat-widget";
155
+
156
+ const widget = new ChatWidgetInstance({
157
+ mountElementId: "chat-widget-root",
158
+ customerInteractionSession: CustomerInteractionSession,
159
+ AIAvatar: true,
160
+ background: false,
161
+ voiceEnabled: true,
162
+ footer: false,
163
+ manualOpen: true,
164
+ });
165
+
166
+ widget.addEventListener("avatar_ready", () => {
167
+ widget.setOpen(true);
168
+ });
169
+
170
+ widget.addEventListener("renew_session", async () => {
171
+ const newSession = await fetchNewSessionSomehow();
172
+ widget.setCustomerInteractionSession(newSession);
173
+ });
174
+ ```
175
+
176
+ Teardown:
177
+
178
+ ```ts
179
+ widget.destroy();
180
+ ```
181
+
182
+ ## Local Development
183
+
184
+ Install:
185
+
186
+ ```bash
187
+ npm install
188
+ ```
189
+
190
+ Start dev server:
191
+
192
+ ```bash
193
+ npm run dev
194
+ ```
195
+
196
+ Notes:
197
+
198
+ - Vite dev server runs on `5179` by default.
199
+ - `src/dev/main.tsx` is the local bootstrap path (creates session and mounts widget directly).
200
+ - `vite.config.ts` is for app/dev runtime.
201
+ - `vite.config.lib.ts` is for distributable library build.
202
+
203
+ ## Build, Publish, and Deployment
204
+
205
+ ### Build local dev app
206
+
207
+ ```bash
208
+ npm run build-dev
209
+ ```
210
+
211
+ ### Build library artifacts
212
+
213
+ ```bash
214
+ npm run build-lib
215
+ ```
216
+
217
+ Outputs include:
218
+
219
+ - `dist/bridge-ai-chat-widget.es.js`
220
+ - `dist/index.types.d.ts`
221
+
222
+ ### Publish flow
223
+
224
+ - `prepublishOnly` runs `build-lib`
225
+ - package exports are defined in `package.json` under `"exports"`
226
+
227
+ ### Other project scripts
228
+
229
+ - `npm run lint`
230
+ - `npm run lint-fix`
231
+ - `npm run format`
232
+ - `npm run format:check`
233
+ - deployment scripts for S3 buckets (project-specific, to be reworked)
234
+ - `npm run push-assets-metamediastatic` (sync `src/assets` into remote static assets repo)
235
+
236
+ ## Session Handling
237
+
238
+ Widget relies on `customerInteractionSession`:
239
+
240
+ - used as Bearer token for HTTP APIs
241
+ - used in websocket query for AnyCable auth
242
+ - periodically checked by `useSessionExpiryChecks`
243
+
244
+ When session is near expiry or invalid, `renew_session` event is emitted; host app must provide a fresh session via `setCustomerInteractionSession`.
245
+
246
+ ## Lola Backend conversations
247
+
248
+ The widget uses Lola's canonical conversation API. Interaction `chatId` remains
249
+ the authenticated session identifier; chat history is keyed independently by
250
+ `conversationId` (`threadId` in realtime compatibility messages).
251
+
252
+ - `GET /chat/conversations` loads the open chat list.
253
+ - `GET /chat/conversations/:conversationId/messages` loads one history.
254
+ - `POST /chat/conversations` creates and selects a new chat.
255
+ - `POST /chat/conversations/:conversationId/select` selects an existing chat
256
+ for the current interaction session.
257
+ - `POST /chat/messages` sends `conversationId` and adopts the canonical
258
+ `conversationId` returned by the backend when a chat is resolved or created.
259
+
260
+ A `409` response means that the selected conversation was closed. It is shown
261
+ as a chat error and the open conversation list is refreshed; only `401`
262
+ requests trigger interaction-session renewal.
263
+
264
+ ## Lola Backend command runtime
265
+
266
+ The widget consumes Lola Backend `command.created` envelopes from the AnyCable
267
+ `commandCreated`/`uiCommand` frame, acknowledges `received` and a final result, and
268
+ deduplicates `commandId` during reconnect replay. Updating the interaction session
269
+ now reconnects REST and AnyCable without remounting the widget.
270
+
271
+ Host-owned pages, modals, and elements are registered explicitly. Backend payloads
272
+ are never evaluated as JavaScript or used as arbitrary selectors:
273
+
274
+ ```ts
275
+ const widget = new ChatWidgetInstance({
276
+ // Existing widget options...
277
+ customerInteractionSession: {
278
+ accessToken,
279
+ chatId: sessionId,
280
+ sessionId,
281
+ expiresAt,
282
+ },
283
+ uiTargets: {
284
+ deposit_button: {
285
+ kind: "button",
286
+ selector: '[data-lola-action="deposit"]',
287
+ },
288
+ deposit_modal: {
289
+ kind: "modal",
290
+ query: { lola_modal: "deposit" },
291
+ },
292
+ account_page: {
293
+ kind: "page",
294
+ route: "/account",
295
+ query: { source: "lola" },
296
+ },
297
+ },
298
+ handlers: {
299
+ openModal: ({ modalCode, payload }) =>
300
+ modalRegistry.open(modalCode!, payload),
301
+ openPage: ({ url }) => router.push(url!),
302
+ },
303
+ });
304
+ ```
305
+
306
+ Targets and handlers can also be registered after construction with
307
+ `registerUiTarget(code, target)` and `registerCommandHandler(name, handler)`.
308
+ Both return an unregister callback.
309
+
310
+ Built-in handlers cover assistant/chat visibility, supported avatar animations,
311
+ CTA rendering inside the text chat, and element highlighting. Highlight targets
312
+ fall back to `[data-lola="code"]` or `[data-lola-action="code"]`. The default
313
+ highlight is a fixed overlay attached to `document.body`, so it is not clipped
314
+ by an element's `overflow` or container bounds and follows resize and scroll. A registered page
315
+ URL fallback uses `history.pushState` and emits `popstate`, so an explicitly
316
+ configured query such as `?lola_modal=deposit` can be handled without reloading the
317
+ host SPA. A registered modal selector can also open a native `HTMLDialogElement`.
318
+ Commands are normalized across the current backend payload (`modalId`, `pageId`,
319
+ `target`) and the canonical Lola contract (`data.target.code`, `modalCode`,
320
+ `pageCode`). If no safe handler/fallback exists, the widget sends
321
+ `unsupported` and emits `command_unsupported`; other lifecycle events are
322
+ `command_received`, `command_succeeded`, `command_failed`, and `command_expired`.
323
+
324
+ ## Asset Pipeline
325
+
326
+ Runtime asset resolution:
327
+
328
+ 1. fetch remote manifest from `https://metamediastatic.com/manifest.json`
329
+ 2. map logical path (for example `animations/idle_1.fbx`) to hashed URL
330
+ 3. load via `resolveAssetPath`
331
+
332
+ This allows cache-friendly hashed asset delivery without changing code references.
333
+
334
+ ## Troubleshooting
335
+
336
+ - Widget does not mount:
337
+ - verify mount element exists and `mountElementId` is correct
338
+ - No messages or send failures:
339
+ - verify `apiUrl`, `accessToken`, and `chatId`
340
+ - No live updates:
341
+ - verify `wsUrl`, websocket reachability, and token validity
342
+ - Voice fails to start:
343
+ - verify mic permission, TURN reachability, and voice API endpoints
344
+ - Avatar never becomes ready:
345
+ - verify manifest URL + asset availability under static host
package/dist/App.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { OnAvatarReady } from "./types";
2
+ import type { ExternallyAvailableActions } from "./avatar/Avatar";
3
+ import type { WidgetConfig } from "./index.types";
4
+ import type { CustomerInteractionSession } from "./types";
5
+ import type { CommandRuntime } from "./domain/command/CommandRuntime";
6
+ export type AppApi = {
7
+ onAvatarReady: OnAvatarReady;
8
+ onSessionExpired: () => void;
9
+ registerSetOpen: (fn: ((open: boolean) => void) | null) => void;
10
+ registerExecuteReaction: (fn: ((params: {
11
+ animation: string;
12
+ voice: string;
13
+ text: string;
14
+ }) => void) | null) => void;
15
+ resolveAssetPath: (path: string) => string;
16
+ registerSendServiceMessage: (fn: ((message: string, animationName: ExternallyAvailableActions) => void) | null) => void;
17
+ registerSetLocale: (fn: ((locale: string) => void) | null) => void;
18
+ registerSetSession: (fn: ((session: CustomerInteractionSession) => void) | null) => void;
19
+ commandRuntime: CommandRuntime;
20
+ };
21
+ type AppProps = {
22
+ config: WidgetConfig;
23
+ api: AppApi;
24
+ };
25
+ export declare function App({ config, api }: AppProps): import("react/jsx-runtime").JSX.Element;
26
+ export default App;