@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/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 y } from "./headless-BhsiNVQj.mjs";
2
- class v {
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 || y,
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 v(e, this.shadow), await this.controller.connect());
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 L extends HTMLElement {
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 = C(i);
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 C(i) {
436
+ function L(i) {
425
437
  const e = {
426
438
  "&": "&amp;",
427
439
  "<": "&lt;",
@@ -451,7 +463,7 @@ const A = /* @__PURE__ */ new Set([
451
463
  "h6",
452
464
  "span",
453
465
  "div"
454
- ]), S = {
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 = S[s] || /* @__PURE__ */ new Set(), b = Array.from(a.attributes);
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 E extends HTMLElement {
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", L],
978
- ["aikaara-message-list", E],
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
- L as AikaaraChatHeader,
1003
+ C as AikaaraChatHeader,
992
1004
  _ as AikaaraChatInput,
993
1005
  w as AikaaraChatWidget,
994
1006
  z as AikaaraErrorBanner,
995
1007
  M as AikaaraMessageBubble,
996
- E as AikaaraMessageList,
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.1.4",
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;