@aikaara/chat-sdk 0.1.4 → 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/README.md +435 -0
- package/dist/{headless-BhsiNVQj.mjs → AikaaraChatClient-C4lWcRsS.mjs} +89 -29
- package/dist/AikaaraChatClient-ChZ2bL9f.cjs +1 -0
- package/dist/cdn/aikaara-chat.iife.js +24 -17
- package/dist/headless.cjs +8 -1
- package/dist/headless.d.ts +250 -2
- package/dist/headless.mjs +10588 -9
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +252 -2
- package/dist/index.mjs +21 -18
- package/dist/ui.cjs +3 -3
- package/dist/ui.d.ts +207 -1
- package/dist/ui.mjs +26 -14
- package/package.json +4 -1
- package/dist/headless-CrgIWcf7.cjs +0 -1
package/dist/ui.d.ts
CHANGED
|
@@ -6,6 +6,50 @@ export declare class AikaaraChatBubble extends HTMLElement {
|
|
|
6
6
|
setIcon(svgOrText: string): void;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
declare class AikaaraChatClient extends EventEmitter<ChatEvents> {
|
|
10
|
+
private connection;
|
|
11
|
+
private api;
|
|
12
|
+
private messageStore;
|
|
13
|
+
private conversationManager;
|
|
14
|
+
private subscription;
|
|
15
|
+
private config;
|
|
16
|
+
constructor(config: ChatClientConfig_2);
|
|
17
|
+
connect(): Promise<void>;
|
|
18
|
+
sendMessage(content: string): Promise<void>;
|
|
19
|
+
sendUserEvent(eventKey: string, value?: object, source?: string): Promise<void>;
|
|
20
|
+
loadHistory(): Promise<Message_2[]>;
|
|
21
|
+
get messages(): Message_2[];
|
|
22
|
+
get conversationId(): string | null;
|
|
23
|
+
get isConnected(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Update the agent's context with information about the host app's current state.
|
|
26
|
+
* Call this on route changes so the agent knows what page/entity the user is viewing.
|
|
27
|
+
*
|
|
28
|
+
* The context is stored in conversation metadata and interpolated into the system prompt.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // On route change
|
|
33
|
+
* client.setContext({
|
|
34
|
+
* currentPage: '/products/42',
|
|
35
|
+
* entityType: 'product',
|
|
36
|
+
* entityId: '42',
|
|
37
|
+
* availableRoutes: { products: '/products', orders: '/orders' },
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
setContext(context: AppContext): Promise<void>;
|
|
42
|
+
disconnect(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Parse structured action results from tool execution output.
|
|
45
|
+
* When the agent calls tools like `edit_current_entity`, `save_current_entity`,
|
|
46
|
+
* `navigate_to`, or `test_tool_by_id`, the result contains an action payload
|
|
47
|
+
* that the SDK emits as a typed event for the host app to handle.
|
|
48
|
+
*/
|
|
49
|
+
private parseActionResult;
|
|
50
|
+
private handleBroadcast;
|
|
51
|
+
}
|
|
52
|
+
|
|
9
53
|
export declare class AikaaraChatHeader extends HTMLElement {
|
|
10
54
|
private shadow;
|
|
11
55
|
static get observedAttributes(): string[];
|
|
@@ -45,6 +89,8 @@ export declare class AikaaraChatWidget extends HTMLElement {
|
|
|
45
89
|
private getConfig;
|
|
46
90
|
private render;
|
|
47
91
|
private initController;
|
|
92
|
+
sendUserEvent(eventKey: string, value?: Record<string, unknown>, source?: string): void;
|
|
93
|
+
getClient(): AikaaraChatClient | null;
|
|
48
94
|
private darkenColor;
|
|
49
95
|
}
|
|
50
96
|
|
|
@@ -104,12 +150,28 @@ export declare class AikaaraTypingIndicator extends HTMLElement {
|
|
|
104
150
|
hide(): void;
|
|
105
151
|
}
|
|
106
152
|
|
|
153
|
+
declare interface AppContext {
|
|
154
|
+
/** Current page/route path in the host app (e.g., '/products/42') */
|
|
155
|
+
currentPage: string;
|
|
156
|
+
/** Entity type on the current page (e.g., 'product', 'agent') */
|
|
157
|
+
entityType?: string;
|
|
158
|
+
/** Entity ID on the current page */
|
|
159
|
+
entityId?: string | number;
|
|
160
|
+
/** Project/workspace ID if applicable */
|
|
161
|
+
projectId?: string | number;
|
|
162
|
+
/** Routes the agent can navigate to — map of label → path */
|
|
163
|
+
availableRoutes?: Record<string, string>;
|
|
164
|
+
/** Additional context for the agent (e.g., form field names, entity schema) */
|
|
165
|
+
custom?: Record<string, unknown>;
|
|
166
|
+
}
|
|
167
|
+
|
|
107
168
|
declare interface ChatClientConfig extends ConnectionConfig {
|
|
108
169
|
apiKey?: string;
|
|
170
|
+
authToken?: string;
|
|
109
171
|
extUid?: string;
|
|
110
172
|
conversationId?: string;
|
|
111
173
|
systemPromptId?: number;
|
|
112
|
-
channel?: 'widget' | 'api';
|
|
174
|
+
channel?: 'widget' | 'api' | 'sidekick';
|
|
113
175
|
onMessage?: (message: Message) => void;
|
|
114
176
|
onStatusChange?: (status: string) => void;
|
|
115
177
|
onError?: (error: Error) => void;
|
|
@@ -117,10 +179,73 @@ declare interface ChatClientConfig extends ConnectionConfig {
|
|
|
117
179
|
onConnectionStateChange?: (state: ConnectionState) => void;
|
|
118
180
|
}
|
|
119
181
|
|
|
182
|
+
declare interface ChatClientConfig_2 extends ConnectionConfig_2 {
|
|
183
|
+
apiKey?: string;
|
|
184
|
+
authToken?: string;
|
|
185
|
+
extUid?: string;
|
|
186
|
+
conversationId?: string;
|
|
187
|
+
systemPromptId?: number;
|
|
188
|
+
channel?: 'widget' | 'api' | 'sidekick';
|
|
189
|
+
onMessage?: (message: Message_2) => void;
|
|
190
|
+
onStatusChange?: (status: string) => void;
|
|
191
|
+
onError?: (error: Error) => void;
|
|
192
|
+
onStreamUpdate?: (delta: string, fullContent: string) => void;
|
|
193
|
+
onConnectionStateChange?: (state: ConnectionState_2) => void;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
declare interface ChatEvents {
|
|
197
|
+
'connection:state': ConnectionState_2;
|
|
198
|
+
'message:received': Message_2;
|
|
199
|
+
'message:updated': Message_2;
|
|
200
|
+
'message:sent': Message_2;
|
|
201
|
+
'stream:start': {
|
|
202
|
+
messageId: string;
|
|
203
|
+
};
|
|
204
|
+
'stream:update': {
|
|
205
|
+
delta: string;
|
|
206
|
+
content: string;
|
|
207
|
+
};
|
|
208
|
+
'stream:end': {
|
|
209
|
+
messageId: string;
|
|
210
|
+
usage?: {
|
|
211
|
+
tokensInput: number;
|
|
212
|
+
tokensOutput: number;
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
'typing:start': void;
|
|
216
|
+
'typing:stop': void;
|
|
217
|
+
'error': Error;
|
|
218
|
+
'status': string;
|
|
219
|
+
'action:edit_entity': EditEntityAction;
|
|
220
|
+
'action:save_entity': SaveEntityAction;
|
|
221
|
+
'action:test_tool': TestToolAction;
|
|
222
|
+
'action:navigate': NavigateAction;
|
|
223
|
+
'tool:start': {
|
|
224
|
+
toolName: string;
|
|
225
|
+
args: Record<string, unknown>;
|
|
226
|
+
};
|
|
227
|
+
'tool:end': {
|
|
228
|
+
toolName: string;
|
|
229
|
+
result: unknown;
|
|
230
|
+
isError: boolean;
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
120
234
|
declare interface ConnectionConfig {
|
|
121
235
|
baseUrl: string;
|
|
122
236
|
wsUrl?: string;
|
|
123
237
|
userToken: string;
|
|
238
|
+
tiledesk?: TiledeskTransportConfig;
|
|
239
|
+
reconnect?: boolean;
|
|
240
|
+
maxReconnectAttempts?: number;
|
|
241
|
+
reconnectInterval?: number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
declare interface ConnectionConfig_2 {
|
|
245
|
+
baseUrl: string;
|
|
246
|
+
wsUrl?: string;
|
|
247
|
+
userToken: string;
|
|
248
|
+
tiledesk?: TiledeskTransportConfig_2;
|
|
124
249
|
reconnect?: boolean;
|
|
125
250
|
maxReconnectAttempts?: number;
|
|
126
251
|
reconnectInterval?: number;
|
|
@@ -128,6 +253,29 @@ declare interface ConnectionConfig {
|
|
|
128
253
|
|
|
129
254
|
declare type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
|
|
130
255
|
|
|
256
|
+
declare type ConnectionState_2 = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
|
|
257
|
+
|
|
258
|
+
declare interface EditEntityAction {
|
|
259
|
+
action: 'edit_entity';
|
|
260
|
+
entity_type: string;
|
|
261
|
+
entity_id: string | number;
|
|
262
|
+
fields: FieldUpdate[];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
declare class EventEmitter<Events extends Record<string, any>> {
|
|
266
|
+
private handlers;
|
|
267
|
+
on<K extends keyof Events & string>(event: K, handler: (data: Events[K]) => void): () => void;
|
|
268
|
+
off<K extends keyof Events & string>(event: K, handler: (data: Events[K]) => void): void;
|
|
269
|
+
emit<K extends keyof Events & string>(event: K, data: Events[K]): void;
|
|
270
|
+
removeAllListeners(): void;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
declare interface FieldUpdate {
|
|
274
|
+
field: string;
|
|
275
|
+
value: unknown;
|
|
276
|
+
previousValue?: unknown;
|
|
277
|
+
}
|
|
278
|
+
|
|
131
279
|
declare interface Message {
|
|
132
280
|
id: string;
|
|
133
281
|
conversationId: string;
|
|
@@ -142,8 +290,52 @@ declare interface Message {
|
|
|
142
290
|
status?: 'sending' | 'sent' | 'streaming' | 'complete' | 'error';
|
|
143
291
|
}
|
|
144
292
|
|
|
293
|
+
declare interface Message_2 {
|
|
294
|
+
id: string;
|
|
295
|
+
conversationId: string;
|
|
296
|
+
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
297
|
+
content: string;
|
|
298
|
+
toolCalls?: ToolCall_2[];
|
|
299
|
+
toolCallResults?: ToolCallResult_2;
|
|
300
|
+
tokensInput?: number;
|
|
301
|
+
tokensOutput?: number;
|
|
302
|
+
metadata?: Record<string, unknown>;
|
|
303
|
+
createdAt: string;
|
|
304
|
+
status?: 'sending' | 'sent' | 'streaming' | 'complete' | 'error';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
declare interface NavigateAction {
|
|
308
|
+
navigate_to: string;
|
|
309
|
+
}
|
|
310
|
+
|
|
145
311
|
export declare function registerComponents(): void;
|
|
146
312
|
|
|
313
|
+
declare interface SaveEntityAction {
|
|
314
|
+
action: 'save_entity';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
declare interface TestToolAction {
|
|
318
|
+
action: 'test_tool';
|
|
319
|
+
tool_id: number;
|
|
320
|
+
parameters: Record<string, unknown>;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
declare interface TiledeskTransportConfig {
|
|
324
|
+
mqttEndpoint: string;
|
|
325
|
+
jwtToken: string;
|
|
326
|
+
userId: string;
|
|
327
|
+
userName?: string;
|
|
328
|
+
projectId: string;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
declare interface TiledeskTransportConfig_2 {
|
|
332
|
+
mqttEndpoint: string;
|
|
333
|
+
jwtToken: string;
|
|
334
|
+
userId: string;
|
|
335
|
+
userName?: string;
|
|
336
|
+
projectId: string;
|
|
337
|
+
}
|
|
338
|
+
|
|
147
339
|
declare interface ToolCall {
|
|
148
340
|
id: string;
|
|
149
341
|
type: 'function';
|
|
@@ -153,11 +345,25 @@ declare interface ToolCall {
|
|
|
153
345
|
};
|
|
154
346
|
}
|
|
155
347
|
|
|
348
|
+
declare interface ToolCall_2 {
|
|
349
|
+
id: string;
|
|
350
|
+
type: 'function';
|
|
351
|
+
function: {
|
|
352
|
+
name: string;
|
|
353
|
+
arguments: string;
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
156
357
|
declare interface ToolCallResult {
|
|
157
358
|
tool_call_id: string;
|
|
158
359
|
content: string;
|
|
159
360
|
}
|
|
160
361
|
|
|
362
|
+
declare interface ToolCallResult_2 {
|
|
363
|
+
tool_call_id: string;
|
|
364
|
+
content: string;
|
|
365
|
+
}
|
|
366
|
+
|
|
161
367
|
declare interface WidgetConfig extends ChatClientConfig {
|
|
162
368
|
position?: 'bottom-right' | 'bottom-left';
|
|
163
369
|
offset?: {
|
package/dist/ui.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as p, D as u, e as d, f as g, g as m, h as f, i as x, j as k, k as c, l as
|
|
2
|
-
class
|
|
1
|
+
import { a as p, D as u, e as d, f as g, g as m, h as f, i as x, j as k, k as c, l as v } from "./AikaaraChatClient-C4lWcRsS.mjs";
|
|
2
|
+
class y {
|
|
3
3
|
client;
|
|
4
4
|
panel;
|
|
5
5
|
bubble;
|
|
@@ -56,6 +56,12 @@ class v {
|
|
|
56
56
|
this.errorBanner.show("Failed to send message", 3e3);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
sendUserEvent(e, t, a) {
|
|
60
|
+
this.client.sendUserEvent(e, t, a);
|
|
61
|
+
}
|
|
62
|
+
getClient() {
|
|
63
|
+
return this.client;
|
|
64
|
+
}
|
|
59
65
|
togglePanel(e) {
|
|
60
66
|
this.isOpen = e !== void 0 ? e : !this.isOpen, this.isOpen ? (this.panel.removeAttribute("hidden"), requestAnimationFrame(() => {
|
|
61
67
|
this.panel.classList.remove("entering"), this.panel.classList.add("visible"), this.input.focus();
|
|
@@ -109,7 +115,7 @@ class w extends HTMLElement {
|
|
|
109
115
|
apiKey: this.getAttribute("api-key") || this._config.apiKey,
|
|
110
116
|
title: this.getAttribute("title") || this._config.title || "Chat",
|
|
111
117
|
subtitle: this.getAttribute("subtitle") || this._config.subtitle,
|
|
112
|
-
theme: this.getAttribute("theme") || this._config.theme ||
|
|
118
|
+
theme: this.getAttribute("theme") || this._config.theme || v,
|
|
113
119
|
primaryColor: this.getAttribute("primary-color") || this._config.primaryColor || c,
|
|
114
120
|
position: this.getAttribute("position") || this._config.position || k,
|
|
115
121
|
width: Number(this.getAttribute("width")) || this._config.width || x,
|
|
@@ -229,7 +235,13 @@ class w extends HTMLElement {
|
|
|
229
235
|
}
|
|
230
236
|
async initController() {
|
|
231
237
|
const e = this.getConfig();
|
|
232
|
-
!e.baseUrl || !e.userToken || (this.controller?.disconnect(), this.controller = new
|
|
238
|
+
!e.baseUrl || !e.userToken || (this.controller?.disconnect(), this.controller = new y(e, this.shadow), await this.controller.connect());
|
|
239
|
+
}
|
|
240
|
+
sendUserEvent(e, t, a) {
|
|
241
|
+
this.controller?.sendUserEvent(e, t, a);
|
|
242
|
+
}
|
|
243
|
+
getClient() {
|
|
244
|
+
return this.controller?.getClient() ?? null;
|
|
233
245
|
}
|
|
234
246
|
darkenColor(e) {
|
|
235
247
|
try {
|
|
@@ -293,7 +305,7 @@ class T extends HTMLElement {
|
|
|
293
305
|
t && (t.innerHTML = e);
|
|
294
306
|
}
|
|
295
307
|
}
|
|
296
|
-
class
|
|
308
|
+
class C extends HTMLElement {
|
|
297
309
|
shadow;
|
|
298
310
|
static get observedAttributes() {
|
|
299
311
|
return ["title", "subtitle", "avatar-url", "status"];
|
|
@@ -415,13 +427,13 @@ class L extends HTMLElement {
|
|
|
415
427
|
}
|
|
416
428
|
}
|
|
417
429
|
function o(i) {
|
|
418
|
-
let e =
|
|
430
|
+
let e = L(i);
|
|
419
431
|
return e = e.replace(/```(\w*)\n([\s\S]*?)```/g, (t, a, s) => `<pre><code>${s.trim()}</code></pre>`), e = e.replace(/`([^`]+)`/g, "<code>$1</code>"), e = e.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), e = e.replace(/\*(.+?)\*/g, "<em>$1</em>"), e = e.replace(
|
|
420
432
|
/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g,
|
|
421
433
|
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
|
|
422
434
|
), e = e.replace(/\n/g, "<br>"), e;
|
|
423
435
|
}
|
|
424
|
-
function
|
|
436
|
+
function L(i) {
|
|
425
437
|
const e = {
|
|
426
438
|
"&": "&",
|
|
427
439
|
"<": "<",
|
|
@@ -451,7 +463,7 @@ const A = /* @__PURE__ */ new Set([
|
|
|
451
463
|
"h6",
|
|
452
464
|
"span",
|
|
453
465
|
"div"
|
|
454
|
-
]),
|
|
466
|
+
]), E = {
|
|
455
467
|
a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
|
|
456
468
|
code: /* @__PURE__ */ new Set(["class"]),
|
|
457
469
|
pre: /* @__PURE__ */ new Set(["class"]),
|
|
@@ -472,7 +484,7 @@ function h(i) {
|
|
|
472
484
|
i.replaceChild(r, t);
|
|
473
485
|
continue;
|
|
474
486
|
}
|
|
475
|
-
const n =
|
|
487
|
+
const n = E[s] || /* @__PURE__ */ new Set(), b = Array.from(a.attributes);
|
|
476
488
|
for (const r of b)
|
|
477
489
|
n.has(r.name) || a.removeAttribute(r.name);
|
|
478
490
|
if (a.hasAttribute("href")) {
|
|
@@ -482,7 +494,7 @@ function h(i) {
|
|
|
482
494
|
h(t);
|
|
483
495
|
}
|
|
484
496
|
}
|
|
485
|
-
class
|
|
497
|
+
class S extends HTMLElement {
|
|
486
498
|
shadow;
|
|
487
499
|
container;
|
|
488
500
|
welcomeMessage = "";
|
|
@@ -974,8 +986,8 @@ function B() {
|
|
|
974
986
|
const i = [
|
|
975
987
|
["aikaara-chat-widget", w],
|
|
976
988
|
["aikaara-chat-bubble", T],
|
|
977
|
-
["aikaara-chat-header",
|
|
978
|
-
["aikaara-message-list",
|
|
989
|
+
["aikaara-chat-header", C],
|
|
990
|
+
["aikaara-message-list", S],
|
|
979
991
|
["aikaara-message-bubble", M],
|
|
980
992
|
["aikaara-chat-input", _],
|
|
981
993
|
["aikaara-typing-indicator", H],
|
|
@@ -988,12 +1000,12 @@ function B() {
|
|
|
988
1000
|
B();
|
|
989
1001
|
export {
|
|
990
1002
|
T as AikaaraChatBubble,
|
|
991
|
-
|
|
1003
|
+
C as AikaaraChatHeader,
|
|
992
1004
|
_ as AikaaraChatInput,
|
|
993
1005
|
w as AikaaraChatWidget,
|
|
994
1006
|
z as AikaaraErrorBanner,
|
|
995
1007
|
M as AikaaraMessageBubble,
|
|
996
|
-
|
|
1008
|
+
S as AikaaraMessageList,
|
|
997
1009
|
$ as AikaaraStreamingMessage,
|
|
998
1010
|
H as AikaaraTypingIndicator,
|
|
999
1011
|
B as registerComponents
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikaara/chat-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Aikaara Chat SDK — embeddable chat widget and headless client",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
"preview": "vite preview",
|
|
35
35
|
"prepare": "git config core.hooksPath .githooks"
|
|
36
36
|
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"mqtt": "^5.10.0"
|
|
39
|
+
},
|
|
37
40
|
"devDependencies": {
|
|
38
41
|
"typescript": "^5.7.0",
|
|
39
42
|
"vite": "^7.3.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";class p{identifier;callbacks={};sendFn;constructor(t,e){this.identifier=t,this.sendFn=e}onReceived(t){return this.callbacks.received=t,this}onConnected(t){return this.callbacks.connected=t,this}onDisconnected(t){return this.callbacks.disconnected=t,this}onRejected(t){return this.callbacks.rejected=t,this}perform(t,e={}){this.sendFn({action:t,...e})}_notifyReceived(t){this.callbacks.received?.(t)}_notifyConnected(){this.callbacks.connected?.()}_notifyDisconnected(){this.callbacks.disconnected?.()}_notifyRejected(){this.callbacks.rejected?.()}}class l{ws=null;url;subscriptions=new Map;welcomePromise=null;pendingSubscriptions=new Map;constructor(t){this.url=t}connect(){return new Promise((t,e)=>{this.welcomePromise={resolve:t,reject:e},this.ws=new WebSocket(this.url),this.ws.onopen=()=>{},this.ws.onmessage=s=>{this.handleMessage(s)},this.ws.onerror=()=>{const s=new Error("WebSocket connection error");this.welcomePromise?.reject(s),this.welcomePromise=null},this.ws.onclose=()=>{this.subscriptions.forEach(s=>s._notifyDisconnected())}})}disconnect(){this.ws&&(this.ws.onclose=null,this.ws.close(),this.ws=null),this.subscriptions.forEach(t=>t._notifyDisconnected()),this.subscriptions.clear()}subscribe(t){const e=JSON.stringify(t),s=new p(e,n=>{this.send({command:"message",identifier:e,data:JSON.stringify(n)})});return this.subscriptions.set(e,s),this.send({command:"subscribe",identifier:e}),s}subscribeAsync(t){const e=this.subscribe(t),s=e.identifier;return new Promise((n,i)=>{this.pendingSubscriptions.set(s,{resolve:()=>n(e),reject:i}),setTimeout(()=>{this.pendingSubscriptions.has(s)&&(this.pendingSubscriptions.delete(s),i(new Error(`Subscription timeout for ${s}`)))},1e4)})}unsubscribe(t){this.send({command:"unsubscribe",identifier:t}),this.subscriptions.delete(t)}perform(t,e,s={}){this.send({command:"message",identifier:t,data:JSON.stringify({action:e,...s})})}get isConnected(){return this.ws?.readyState===WebSocket.OPEN}send(t){this.ws?.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(t))}handleMessage(t){let e;try{e=JSON.parse(t.data)}catch{return}switch(e.type){case"welcome":this.welcomePromise?.resolve(),this.welcomePromise=null;break;case"ping":break;case"confirm_subscription":{const s=e.identifier;this.subscriptions.get(s)?._notifyConnected();const i=this.pendingSubscriptions.get(s);i&&(i.resolve(),this.pendingSubscriptions.delete(s));break}case"reject_subscription":{const s=e.identifier;this.subscriptions.get(s)?._notifyRejected(),this.subscriptions.delete(s);const i=this.pendingSubscriptions.get(s);i&&(i.reject(new Error(`Subscription rejected: ${s}`)),this.pendingSubscriptions.delete(s));break}case"disconnect":this.subscriptions.forEach(s=>s._notifyDisconnected());break;default:{e.identifier&&e.message!==void 0&&this.subscriptions.get(e.identifier)?._notifyReceived(e.message);break}}}}class u{handlers=new Map;on(t,e){return this.handlers.has(t)||this.handlers.set(t,new Set),this.handlers.get(t).add(e),()=>this.off(t,e)}off(t,e){this.handlers.get(t)?.delete(e)}emit(t,e){this.handlers.get(t)?.forEach(s=>{try{s(e)}catch(n){console.error(`Error in event handler for "${t}":`,n)}})}removeAllListeners(){this.handlers.clear()}}const v=1e3,w=10,y=400,E=600,T="#6366f1",I=12,A="system-ui, -apple-system, sans-serif",k="Type a message...",C="bottom-right",M="light",U={x:20,y:20},d="aikaara_conversation_id";class f extends u{client;config;state="disconnected";reconnectAttempt=0;reconnectTimer=null;constructor(t){super(),this.config=t;const e=this.buildWsUrl(t.baseUrl,t.userToken);this.client=new l(e)}async connect(){this.setState("connecting");try{await this.client.connect(),this.setState("connected"),this.reconnectAttempt=0}catch(t){if(this.setState("disconnected"),this.config.reconnect!==!1)this.scheduleReconnect();else throw t}}async disconnect(){this.clearReconnectTimer(),this.client.disconnect(),this.setState("disconnected")}subscribeToConversation(t){return this.client.subscribeAsync({channel:"ConversationChannel",conversation_id:t})}sendMessage(t,e){const s=JSON.stringify({channel:"ConversationChannel",conversation_id:t});this.client.perform(s,"send_message",{content:e})}sendUserEvent(t,e,s,n){const i=JSON.stringify({channel:"ConversationChannel",conversation_id:t});this.client.perform(i,"send_user_event",{event_key:e,...s&&{value:s},...n&&{source:n}})}get connectionState(){return this.state}setState(t){this.state!==t&&(this.state=t,this.emit("connection:state",t))}scheduleReconnect(){const t=this.config.maxReconnectAttempts??w;if(this.reconnectAttempt>=t){this.emit("error",new Error("Max reconnection attempts reached"));return}this.setState("reconnecting");const s=(this.config.reconnectInterval??v)*Math.pow(2,this.reconnectAttempt);this.reconnectAttempt++,this.reconnectTimer=setTimeout(async()=>{try{const n=this.buildWsUrl(this.config.baseUrl,this.config.userToken);this.client=new l(n),await this.connect()}catch{}},s)}clearReconnectTimer(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}buildWsUrl(t,e){if(this.config.wsUrl){const n=this.config.wsUrl.includes("?")?"&":"?";return`${this.config.wsUrl}${n}token=${e}`}return`${t.replace(/^http/,"ws")}/cable?token=${e}`}}class _{baseUrl;apiKey;userToken;constructor(t,e,s){this.baseUrl=t,this.userToken=e,this.apiKey=s}async createConversation(t){const e={conversation:{...t.extUid&&{ext_uid:t.extUid},...t.systemPromptId&&{system_prompt_id:t.systemPromptId},...t.channel&&{channel:t.channel},...t.title&&{title:t.title}}};return this.request("POST","/api/v1/conversations",e)}async getMessages(t){return(await this.request("GET",`/api/v1/conversations/${t}/messages`)).map(this.mapMessage)}mapMessage(t){return{id:String(t.id),conversationId:String(t.conversation_id),role:t.role,content:t.content||"",toolCalls:t.tool_calls?.map(e=>({id:e.id,type:e.type,function:e.function})),toolCallResults:t.tool_call_results,tokensInput:t.tokens_input,tokensOutput:t.tokens_output,metadata:t.metadata,createdAt:t.created_at,status:"complete"}}async request(t,e,s){const n={"Content-Type":"application/json",Accept:"application/json"};this.apiKey&&(n["X-Api-Key"]=this.apiKey);const i=`${this.baseUrl}${e}`,g={method:t,headers:n};s&&(g.body=JSON.stringify(s));const c=await fetch(i,g);if(!c.ok){const a=await c.text();let h;try{const m=JSON.parse(a);h=m.error||m.message||a}catch{h=a}throw new Error(`API error ${c.status}: ${h}`)}const r=await c.json();if(r&&typeof r=="object"&&"success"in r){if(!r.success)throw new Error(`API error: ${r.message||"Request failed"}`);return r.data}return r}}class b{_messages=[];optimisticCounter=0;get messages(){return[...this._messages]}addOptimistic(t,e,s){const n={id:`optimistic_${++this.optimisticCounter}`,conversationId:s,role:t,content:e,createdAt:new Date().toISOString(),status:"sending"};return this._messages.push(n),n}confirmOptimistic(t){const e=this._messages.find(s=>s.id===t);e&&(e.status="sent")}addStreamingMessage(t){const e={id:`streaming_${Date.now()}`,conversationId:t,role:"assistant",content:"",createdAt:new Date().toISOString(),status:"streaming"};return this._messages.push(e),e}updateStreaming(t){const e=this._messages.findLast(s=>s.status==="streaming");e&&(e.content=t)}appendToStreaming(t){const e=this._messages.findLast(s=>s.status==="streaming");e&&(e.content+=t)}get streamingContent(){return this._messages.findLast(e=>e.status==="streaming")?.content||""}finalizeStreaming(t){const e=this._messages.findLast(s=>s.status==="streaming");return e&&(e.status="complete",t&&(e.tokensInput=t.tokensInput,e.tokensOutput=t.tokensOutput)),e}addMessage(t){this._messages.push(t)}setMessages(t){this._messages=[...t]}clear(){this._messages=[]}}class S{_conversationId;persist;constructor(t,e=!0){this.persist=e,this._conversationId=t||this.loadFromStorage()}get conversationId(){return this._conversationId}set conversationId(t){this._conversationId=t,this.persist&&t&&this.saveToStorage(t)}clear(){if(this._conversationId=null,this.persist)try{localStorage.removeItem(d)}catch{}}loadFromStorage(){if(!this.persist)return null;try{return localStorage.getItem(d)}catch{return null}}saveToStorage(t){try{localStorage.setItem(d,t)}catch{}}}class O extends u{connection;api;messageStore;conversationManager;subscription=null;config;constructor(t){super(),this.config=t,this.connection=new f(t),this.api=new _(t.baseUrl,t.userToken,t.apiKey),this.messageStore=new b,this.conversationManager=new S(t.conversationId),this.connection.on("connection:state",e=>{this.emit("connection:state",e),this.config.onConnectionStateChange?.(e)}),this.connection.on("error",e=>{this.emit("error",e),this.config.onError?.(e)})}async connect(){if(await this.connection.connect(),!this.conversationManager.conversationId){const t=await this.api.createConversation({systemPromptId:this.config.systemPromptId,channel:this.config.channel||"widget",extUid:this.config.extUid});this.conversationManager.conversationId=String(t.id)}this.subscription=await this.connection.subscribeToConversation(this.conversationManager.conversationId),this.subscription.onReceived(t=>{this.handleBroadcast(t)}),await this.loadHistory()}async sendMessage(t){const e=this.conversationManager.conversationId;if(!e)throw new Error("No active conversation");const s=this.messageStore.addOptimistic("user",t,e);this.emit("message:sent",s),this.config.onMessage?.(s),this.connection.sendMessage(e,t)}async sendUserEvent(t,e,s){const n=this.conversationManager.conversationId;if(!n)throw new Error("No active conversation");this.connection.sendUserEvent(n,t,e,s)}async loadHistory(){const t=this.conversationManager.conversationId;if(!t)return[];try{const e=await this.api.getMessages(t);return this.messageStore.setMessages(e),e}catch{return[]}}get messages(){return this.messageStore.messages}get conversationId(){return this.conversationManager.conversationId}get isConnected(){return this.connection.connectionState==="connected"}async disconnect(){this.subscription&&(this.subscription=null),await this.connection.disconnect()}handleBroadcast(t){const e=this.conversationManager.conversationId;switch(t.type){case"status":{const s=t.status;this.emit("status",s),this.config.onStatusChange?.(s),s==="processing"&&this.emit("typing:start",void 0);break}case"error":{const s=new Error(t.message||"Unknown error");this.emit("error",s),this.config.onError?.(s);break}case"message_start":{if(t.role==="assistant"){const s=this.messageStore.addStreamingMessage(e);this.emit("stream:start",{messageId:s.id}),this.emit("typing:start",void 0)}break}case"message_update":{const s=t.delta||"",n=t.content||"";n?this.messageStore.updateStreaming(n):s&&this.messageStore.appendToStreaming(s);const i=this.messageStore.streamingContent;this.emit("stream:update",{delta:s,content:i}),this.config.onStreamUpdate?.(s,i);break}case"message_end":{const s=t.usage,n=this.messageStore.finalizeStreaming(s?{tokensInput:s.tokens_input||0,tokensOutput:s.tokens_output||0}:void 0);this.emit("typing:stop",void 0),n&&(this.emit("stream:end",{messageId:n.id,usage:s?{tokensInput:s.tokens_input||0,tokensOutput:s.tokens_output||0}:void 0}),this.emit("message:received",n),this.config.onMessage?.(n));break}case"message_queued":{const s=this.messageStore.messages.findLast(n=>n.status==="sending");s&&this.messageStore.confirmOptimistic(s.id);break}case"tool_execution_start":case"tool_execution_update":case"tool_execution_end":case"agent_start":case"agent_end":case"turn_start":case"turn_end":case"auto_retry_start":case"auto_retry_end":case"cancelled":this.emit("status",t.type);break}}}exports.ActionCableClient=l;exports.AikaaraChatClient=O;exports.ApiClient=_;exports.ChannelSubscription=p;exports.ConnectionManager=f;exports.ConversationManager=S;exports.DEFAULT_BORDER_RADIUS=I;exports.DEFAULT_FONT_FAMILY=A;exports.DEFAULT_OFFSET=U;exports.DEFAULT_PLACEHOLDER=k;exports.DEFAULT_POSITION=C;exports.DEFAULT_PRIMARY_COLOR=T;exports.DEFAULT_THEME=M;exports.DEFAULT_WIDGET_HEIGHT=E;exports.DEFAULT_WIDGET_WIDTH=y;exports.EventEmitter=u;exports.MessageStore=b;
|