@kivox/client 0.1.0-beta.16 → 0.1.0-beta.17

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 CHANGED
@@ -1,10 +1,10 @@
1
- # KIVOX JavaScript client
1
+ # Kivox JavaScript client
2
2
 
3
- `@kivox/client` is the official JavaScript/TypeScript client for interacting with KIVOX. It provides the necessary abstractions and bindings to handle sessions, real-time events, audio, and communication with agents.
3
+ `@kivox/client` is the official JavaScript/TypeScript client for interacting with Kivox. It provides the necessary abstractions and bindings to handle sessions, real-time events, audio, and communication with agents.
4
4
 
5
5
  ## Features
6
6
 
7
- - Unified client for the KIVOX API
7
+ - Unified client for the Kivox API
8
8
  - Conversation and voice session management
9
9
  - WebSockets, events, and real-time state
10
10
  - Audio recording and playback
package/dist/index.d.ts CHANGED
@@ -68,8 +68,8 @@ type Agent = {
68
68
  id: string
69
69
  template_id: string
70
70
  config: AgentConfig
71
- blueprint: AgentBlueprint
72
71
  variables: AgentVariable[]
72
+ ork_source: string
73
73
  status: AgentStatus
74
74
  created_at: Date
75
75
  updated_at: Date
@@ -83,13 +83,6 @@ type AgentConfig = {
83
83
  max_silence_timeout_seconds: number
84
84
  allow_interruptions: boolean
85
85
  };
86
- type AgentBlueprint<
87
- NodeType = unknown,
88
- EdgeType = unknown
89
- > = {
90
- nodes: NodeType[]
91
- edges: EdgeType[]
92
- };
93
86
  type AgentVariable = {
94
87
  key: string
95
88
  value: string
@@ -97,8 +90,12 @@ type AgentVariable = {
97
90
  is_secret: boolean
98
91
  description: string
99
92
  };
100
- type AgentCreate = Partial<Pick<Agent, "config" | "blueprint" | "variables" | "status" | "template_id">>;
101
- type AgentUpdate = Partial<Pick<AgentCreate, "config" | "blueprint" | "variables">>;
93
+ type AgentCreate = Partial<Pick<Agent, "config" | "variables" | "ork_source" | "status" | "template_id">>;
94
+ type AgentUpdate = Partial<Pick<AgentCreate, "config" | "variables" | "ork_source">>;
95
+ type AgentCompilationDiagnostic = {
96
+ errors: string[]
97
+ warnings: string[]
98
+ };
102
99
  /**
103
100
  * Configuration for listing agents.
104
101
  */
@@ -111,7 +108,7 @@ type AgentListParams = {
111
108
  search?: string
112
109
  };
113
110
  /**
114
- * Client for interacting with KIVOX agents.
111
+ * Client for interacting with Kivox agents.
115
112
  *
116
113
  * @example
117
114
  * ```ts
@@ -200,6 +197,14 @@ declare class AgentClient {
200
197
  */
201
198
  delete(id: string): Promise<Agent>;
202
199
  /**
200
+ * Compiles the Ork source code of an agent.
201
+ *
202
+ * @param id Agent ID
203
+ * @param orkSource Updated Ork source code. If not provided, the current Ork source code is used.
204
+ * @returns Compiled agent
205
+ */
206
+ compile(id: string, orkSource?: string): Promise<AgentCompilationDiagnostic>;
207
+ /**
203
208
  * Parses a list of agents with stringified config and blueprint fields.
204
209
  *
205
210
  * @returns List of parsed agents.
@@ -325,6 +330,7 @@ type ServerAudioDelta = {
325
330
  */
326
331
  type ServerTurnComplete = {
327
332
  type: "turn_complete"
333
+ stats: UsageStats
328
334
  };
329
335
  /**
330
336
  * Signals that the conversation flow reached its exit node.
@@ -357,7 +363,19 @@ type ServerTick = {
357
363
  */
358
364
  type ServerSessionClosed = {
359
365
  type: "session_closed"
360
- reason: "flow_complete" | "ended_by_agent" | "ended_by_user" | "timeout" | "connection_closed" | "error"
366
+ reason: string
367
+ stats: UsageStats
368
+ };
369
+ /**
370
+ * Snapshot of runtime resource consumption.
371
+ */
372
+ type UsageStats = {
373
+ http_calls: number
374
+ http_limit: number
375
+ llm_calls: number
376
+ llm_limit: number
377
+ steps: number
378
+ steps_limit: number
361
379
  };
362
380
  /**
363
381
  * Union type for all messages sent from the server to the client.
@@ -458,7 +476,7 @@ type ConversationSessionConfig = {
458
476
  onEvent?: (event: ServerSessionEvent) => void
459
477
  };
460
478
  /**
461
- * Represents an active, real-time conversation session with a KIVOX agent.
479
+ * Represents an active, real-time conversation session with a Kivox agent.
462
480
  * Provides methods to dispatch user inputs and lifecycle commands, as well as tracking basic session state.
463
481
  */
464
482
  declare class ConversationSession {
@@ -567,7 +585,7 @@ type ConversationConnectParams = ConversationSessionConfig & {
567
585
  *
568
586
  * @example
569
587
  * ```ts
570
- * // Connect to a KIVOX agent
588
+ * // Connect to a Kivox agent
571
589
  * const session = await kivox.conversations.connect({
572
590
  * agentId: 'agent-123',
573
591
  * onEvent: (event) => {
@@ -666,11 +684,11 @@ type Template = {
666
684
  id: string
667
685
  name: string
668
686
  description?: string
669
- blueprint: AgentBlueprint
687
+ ork_source: string
670
688
  created_at: Date
671
689
  updated_at: Date
672
690
  };
673
- type TemplateCreate = Pick<Template, "name" | "description" | "blueprint">;
691
+ type TemplateCreate = Pick<Template, "name" | "description" | "ork_source">;
674
692
  type TemplateUpdate = Partial<TemplateCreate>;
675
693
  /**
676
694
  * Configuration for listing templates.
@@ -682,7 +700,7 @@ type TemplateListParams = {
682
700
  page?: number
683
701
  };
684
702
  /**
685
- * Client for interacting with KIVOX templates.
703
+ * Client for interacting with Kivox templates.
686
704
  *
687
705
  * @example
688
706
  * ```ts
@@ -730,11 +748,11 @@ declare class TemplateClient {
730
748
  delete(id: string): Promise<void>;
731
749
  }
732
750
  /**
733
- * Configuration for the KIVOX client.
751
+ * Configuration for the Kivox client.
734
752
  */
735
753
  type KivoxConfig = {
736
754
  /**
737
- * Base URL of your self-hosted KIVOX instance.
755
+ * Base URL of your self-hosted Kivox instance.
738
756
  * @example 'http://localhost:8787'
739
757
  * @example 'https://api.kivox.io'
740
758
  */
@@ -746,8 +764,8 @@ type KivoxConfig = {
746
764
  headers?: Record<string, string>
747
765
  };
748
766
  /**
749
- * Main KIVOX client.
750
- * Provides unified access to all KIVOX resources.
767
+ * Main Kivox client.
768
+ * Provides unified access to all Kivox resources.
751
769
  *
752
770
  * @example
753
771
  * ```ts
@@ -780,18 +798,17 @@ declare class KivoxClient {
780
798
  readonly messages: MessageClient;
781
799
  constructor(config: KivoxConfig);
782
800
  }
783
- import { AgentBlueprint as AgentBlueprint2, AgentConfig as AgentConfig2, AgentVariable as AgentVariable2 } from "@kivox/client";
784
801
  declare const REGEX_VARIABLE: RegExp;
785
802
  /**
786
803
  * Extracts all variable references from config and blueprint.
787
804
  */
788
- declare function extractAllVariables(config: AgentConfig2, blueprint: AgentBlueprint2<Record<string, unknown>, Record<string, unknown>>): string[];
805
+ declare function extractAllVariables(config: AgentConfig): string[];
789
806
  /**
790
807
  * Replaces variable references with their values
791
808
  */
792
- declare function interpolateVariables(text: string, variables: AgentVariable2[]): string;
809
+ declare function interpolateVariables(text: string, variables: AgentVariable[]): string;
793
810
  /**
794
811
  * Checks if a string contains any variable references.
795
812
  */
796
813
  declare function hasVariables(text: string): boolean;
797
- export { interpolateVariables, hasVariables, extractAllVariables, TemplateUpdate, TemplateCreate, Template, SessionConnectionLostEvent, ServerTurnComplete, ServerTick, ServerTextFull, ServerTextDelta, ServerSessionEvent, ServerSessionClosed, ServerMessage, ServerHandshakeOK, ServerHandshakeError, ServerFlowComplete, ServerError, ServerConnectionMessage, ServerAudioDelta, REGEX_VARIABLE, Paginated, MessageRole, Message, KivoxConfig, KivoxClient, HttpTransportError, ConversationTransportError, ConversationSessionConfig, ConversationSession, Conversation, ClientMessage, ClientInputText, ClientInputAudioStream, ClientInputAudio, ClientHandshake, ClientEndSession, ClientCancel, AgentVariable, AgentUpdate, AgentStatus, AgentCreate, AgentConfig, AgentBlueprint, Agent };
814
+ export { interpolateVariables, hasVariables, extractAllVariables, UsageStats, TemplateUpdate, TemplateCreate, Template, SessionConnectionLostEvent, ServerTurnComplete, ServerTick, ServerTextFull, ServerTextDelta, ServerSessionEvent, ServerSessionClosed, ServerMessage, ServerHandshakeOK, ServerHandshakeError, ServerFlowComplete, ServerError, ServerConnectionMessage, ServerAudioDelta, REGEX_VARIABLE, Paginated, MessageRole, Message, KivoxConfig, KivoxClient, HttpTransportError, ConversationTransportError, ConversationSessionConfig, ConversationSession, Conversation, ClientMessage, ClientInputText, ClientInputAudioStream, ClientInputAudio, ClientHandshake, ClientEndSession, ClientCancel, AgentVariable, AgentUpdate, AgentStatus, AgentCreate, AgentConfig, AgentCompilationDiagnostic, Agent };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- class Z{http;constructor(K){this.http=K}async list(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};if(K.search)J.search=K.search;let Q=await this.http.get("/agents",J);return{...Q,items:Z.parseAgents(Q.items)}}async listLive(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};if(K.search)J.search=K.search;let Q=await this.http.get("/agents/live",J);return{...Q,items:Z.parseAgents(Q.items)}}async listDraft(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};if(K.search)J.search=K.search;let Q=await this.http.get("/agents/draft",J);return{...Q,items:Z.parseAgents(Q.items)}}async listArchived(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};if(K.search)J.search=K.search;let Q=await this.http.get("/agents/archived",J);return{...Q,items:Z.parseAgents(Q.items)}}async get(K){let J=await this.http.get(`/agents/${K}`);return Z.parseAgent(J)}async create(K){let J=await this.http.post("/agents",K);return Z.parseAgent(J)}async update(K,J){let Q=await this.http.put(`/agents/${K}`,J);return Z.parseAgent(Q)}async markAsLive(K){let J=await this.http.patch(`/agents/${K}/live`);return Z.parseAgent(J)}async markAsDraft(K){let J=await this.http.patch(`/agents/${K}/draft`);return Z.parseAgent(J)}async markAsArchived(K){let J=await this.http.patch(`/agents/${K}/archived`);return Z.parseAgent(J)}async delete(K){let J=await this.http.delete(`/agents/${K}`);return Z.parseAgent(J)}static parseAgents(K){return K.map(Z.parseAgent)}static parseAgent(K){return{...K,config:JSON.parse(K.config),blueprint:JSON.parse(K.blueprint),variables:JSON.parse(K.variables)}}}class O{_transport;_conversationId;_closed=!1;_onEvent;_pendingAudioMetadata=null;_remainingMs=0;_elapsedMs=0;get conversationId(){return this._conversationId}get remainingMs(){return this._remainingMs}get elapsedMs(){return this._elapsedMs}get closed(){return this._closed}constructor(K,J,Q){this._transport=K,this._conversationId=J,this._onEvent=Q.onEvent,this.#K()}sendText(K){if(this._closed){console.warn("Cannot send text: session is closed");return}this._transport.send({type:"input_text",text:K})}sendAudio(K){if(this._closed){console.warn("Cannot send audio: session is closed");return}this._transport.send({type:"input_audio"}),this._transport.sendBinary(K)}streamAudio(K){if(this._closed){console.warn("Cannot stream audio: session is closed");return}this._transport.send({type:"input_audio_stream"}),this._transport.sendBinary(K)}cancelRequest(){if(this._closed){console.warn("Cannot cancel request: session is closed");return}this._transport.send({type:"cancel"})}end(){if(this._closed){console.warn("Cannot end session: session is closed");return}this._transport.send({type:"end"})}close(){if(this._closed)return;this._closed=!0,this._transport.close()}#K(){this._transport.onMessage((K)=>{let J=K;if(J.type==="audio_delta"){this._pendingAudioMetadata={size:J.size};return}if(J.type==="tick")this._remainingMs=J.remaining_ms,this._elapsedMs=J.elapsed_ms;if(J.type==="session_closed")this._closed=!0;this._onEvent?.(J)}),this._transport.onBinary((K)=>{if(this._pendingAudioMetadata){let J={type:"audio_delta",size:this._pendingAudioMetadata.size,audio:K};this._pendingAudioMetadata=null,this._onEvent?.(J)}}),this._transport.onClose(()=>{if(this._closed)return;this._closed=!0,this._onEvent?.({type:"connection_lost",reason:"socket_closed"})}),this._transport.onError(()=>{if(this._closed)return;this._closed=!0,this._onEvent?.({type:"connection_lost",reason:"socket_error"})})}}class z extends Error{code;constructor(K,J="TRANSPORT_ERROR"){super(K);this.code=J;this.name="ConversationTransportError"}}class X{_ws;_closed=!1;_onMessage=null;_onBinary=null;_onError=null;_onClose=null;constructor(K){this._ws=K}async connect(){if(this._ws.readyState===WebSocket.OPEN)throw new z("Already connected","ALREADY_CONNECTED");if(this._ws.readyState===WebSocket.CLOSING||this._ws.readyState===WebSocket.CLOSED)throw new z("Socket is closing or closed","SOCKET_CLOSED");return new Promise((K,J)=>{let Q=()=>{this._ws.removeEventListener("open",Q),this._ws.removeEventListener("error",Y),this.#K(),K()},Y=()=>{this._ws.removeEventListener("open",Q),this._ws.removeEventListener("error",Y),J(new z("Connection failed","CONNECTION_FAILED"))};this._ws.addEventListener("open",Q),this._ws.addEventListener("error",Y)})}send(K){if(this._ws.readyState!==WebSocket.OPEN)throw new z("Not connected","NOT_CONNECTED");this._ws.send(JSON.stringify(K))}async sendBinary(K){if(this._ws.readyState!==WebSocket.OPEN)throw new z("Not connected","NOT_CONNECTED");let J=await K.arrayBuffer();this._ws.send(J)}onMessage(K){this._onMessage=K}onBinary(K){this._onBinary=K}onError(K){this._onError=K}onClose(K){this._onClose=K}close(){if(this._closed)return;if(this._closed=!0,this._ws.readyState===WebSocket.OPEN||this._ws.readyState===WebSocket.CONNECTING)this._ws.close();this._onClose?.()}#K(){this._ws.addEventListener("message",(K)=>{if(K.data instanceof Blob)this._onBinary?.(K.data);else if(K.data instanceof ArrayBuffer)this._onBinary?.(new Blob([K.data]));else if(typeof K.data==="string")try{let J=JSON.parse(K.data);this._onMessage?.(J)}catch(J){console.warn("Failed to parse JSON message:",K.data,J)}}),this._ws.addEventListener("error",()=>{if(this._closed)return;this._onError?.(new z("WebSocket error")),this.close()}),this._ws.addEventListener("close",()=>{if(this._closed)return;this.close()})}}class _{http;ws;constructor(K,J){this.http=K;this.ws=J}async list(K){let J={limit:(K.limit??20).toString(),page:(K.page??1).toString()};return this.http.get(`/agents/${K.agentId}/conversations`,J)}async get(K){return this.http.get(`/conversations/${K}`)}async connect(K){let J=this.ws.connect("/conversations/websocket"),Q=new X(J);return await Q.connect(),Q.send({type:"handshake",agent_id:K.agentId}),new Promise((Y,$)=>{let B=!1;Q.onMessage((N)=>{switch(N.type){case"handshake_ok":B=!0,K.onConnection?.(N),Y(new O(Q,N.conversation_id,K));break;case"handshake_error":B=!0,K.onConnection?.(N),Q.close(),$(Error(`Handshake rejected: ${N.reason}`));break;default:break}}),Q.onError((N)=>{if(!B)$(N)}),Q.onClose(()=>{if(!B)$(Error("WebSocket connection lost during handshake"))})})}}function M(K){let J=new URL(K),Q=J.protocol==="https:"?"wss:":"ws:";return{rest:`${J.origin}/v1`,ws:`${Q}//${J.host}/v1`}}class x extends Error{status;statusText;body;constructor(K,J,Q,Y){super(K);this.status=J;this.statusText=Q;this.body=Y;this.name="HttpTransportError"}}class D{config;constructor(K){this.config=K}async request(K,J){let Q=`${this.config.baseUrl}${K}`,Y=await fetch(Q,{...J,headers:{"Content-Type":"application/json",...this.config.headers,...J?.headers}});if(!Y.ok){let $=await Y.text();throw new x(`HTTP ${Y.status}: ${Y.statusText}`,Y.status,Y.statusText,$)}return Y.json()}async get(K,J){let Q=new URL(`${this.config.baseUrl}${K}`);if(J){let Y=Object.entries(J);for(let[$,B]of Y)Q.searchParams.set($,B)}return this.request(K+Q.search,{method:"GET"})}async post(K,J){return this.request(K,{method:"POST",body:J?JSON.stringify(J):void 0})}async put(K,J){return this.request(K,{method:"PUT",body:J?JSON.stringify(J):void 0})}async patch(K,J){return this.request(K,{method:"PATCH",body:J?JSON.stringify(J):void 0})}async delete(K){return this.request(K,{method:"DELETE"})}}class H{config;constructor(K){this.config=K}connect(K){let J=`${this.config.baseUrl}${K}`;return new WebSocket(J)}}class U{http;constructor(K){this.http=K}async list(K={}){let J={limit:(K.limit??50).toString(),page:(K.page??1).toString()};if(K.conversationId)J.conversation_id=K.conversationId;return this.http.get("/messages",J)}async get(K){return this.http.get(`/messages/${K}`)}}class F{http;constructor(K){this.http=K}async list(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};return this.http.get("/templates",J)}async get(K){return this.http.get(`/templates/${K}`)}async create(K){return this.http.post("/templates",K)}async update(K,J){return this.http.put(`/templates/${K}`,J)}async delete(K){return this.http.delete(`/templates/${K}`)}}class W{agents;conversations;templates;messages;constructor(K){let J=M(K.baseUrl),Q=new D({baseUrl:J.rest,headers:K.headers}),Y=new H({baseUrl:J.ws,headers:K.headers});this.agents=new Z(Q),this.templates=new F(Q),this.conversations=new _(Q,Y),this.messages=new U(Q)}}var L=/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g;function j(K){let J=new Set,Q;while((Q=L.exec(K))!==null){let Y=Q[1];if(!Y)continue;J.add(Y)}return J}function G(K,J){if(typeof K==="string"){let Q=j(K);for(let Y of Q)J.add(Y)}else if(Array.isArray(K))for(let Q of K)G(Q,J);else if(typeof K==="object"&&K!==null){let Q=Object.values(K);for(let Y of Q)G(Y,J)}}function v(K,J){let Q=new Set;G(K,Q);for(let Y of J.nodes)if(Y.data)G(Y.data,Q);for(let Y of J.edges)if(Y.data)G(Y.data,Q);return Array.from(Q).toSorted()}function d(K,J){let Q=new Map(J.map((Y)=>[Y.key,Y.value]));return K.replace(L,(Y,$)=>{return Q.get($)??Y})}function m(K){return L.test(K)}export{d as interpolateVariables,m as hasVariables,v as extractAllVariables,L as REGEX_VARIABLE,W as KivoxClient,x as HttpTransportError,z as ConversationTransportError,O as ConversationSession};
1
+ class Z{http;constructor(K){this.http=K}async list(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};if(K.search)J.search=K.search;let Q=await this.http.get("/agents",J);return{...Q,items:Z.parseAgents(Q.items)}}async listLive(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};if(K.search)J.search=K.search;let Q=await this.http.get("/agents/live",J);return{...Q,items:Z.parseAgents(Q.items)}}async listDraft(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};if(K.search)J.search=K.search;let Q=await this.http.get("/agents/draft",J);return{...Q,items:Z.parseAgents(Q.items)}}async listArchived(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};if(K.search)J.search=K.search;let Q=await this.http.get("/agents/archived",J);return{...Q,items:Z.parseAgents(Q.items)}}async get(K){let J=await this.http.get(`/agents/${K}`);return Z.parseAgent(J)}async create(K){let J=await this.http.post("/agents",K);return Z.parseAgent(J)}async update(K,J){let Q=await this.http.put(`/agents/${K}`,J);return Z.parseAgent(Q)}async markAsLive(K){let J=await this.http.patch(`/agents/${K}/live`);return Z.parseAgent(J)}async markAsDraft(K){let J=await this.http.patch(`/agents/${K}/draft`);return Z.parseAgent(J)}async markAsArchived(K){let J=await this.http.patch(`/agents/${K}/archived`);return Z.parseAgent(J)}async delete(K){let J=await this.http.delete(`/agents/${K}`);return Z.parseAgent(J)}async compile(K,J){return await this.http.post(`/agents/${K}/compile`,{ork_source:J})}static parseAgents(K){return K.map(Z.parseAgent)}static parseAgent(K){return{...K,config:JSON.parse(K.config),variables:JSON.parse(K.variables)}}}class O{_transport;_conversationId;_closed=!1;_onEvent;_pendingAudioMetadata=null;_remainingMs=0;_elapsedMs=0;get conversationId(){return this._conversationId}get remainingMs(){return this._remainingMs}get elapsedMs(){return this._elapsedMs}get closed(){return this._closed}constructor(K,J,Q){this._transport=K,this._conversationId=J,this._onEvent=Q.onEvent,this.#K()}sendText(K){if(this._closed){console.warn("Cannot send text: session is closed");return}this._transport.send({type:"input_text",text:K})}sendAudio(K){if(this._closed){console.warn("Cannot send audio: session is closed");return}this._transport.send({type:"input_audio"}),this._transport.sendBinary(K)}streamAudio(K){if(this._closed){console.warn("Cannot stream audio: session is closed");return}this._transport.send({type:"input_audio_stream"}),this._transport.sendBinary(K)}cancelRequest(){if(this._closed){console.warn("Cannot cancel request: session is closed");return}this._transport.send({type:"cancel"})}end(){if(this._closed){console.warn("Cannot end session: session is closed");return}this._transport.send({type:"end"})}close(){if(this._closed)return;this._closed=!0,this._transport.close()}#K(){this._transport.onMessage((K)=>{let J=K;if(J.type==="audio_delta"){this._pendingAudioMetadata={size:J.size};return}if(J.type==="tick")this._remainingMs=J.remaining_ms,this._elapsedMs=J.elapsed_ms;if(J.type==="session_closed")this._closed=!0;this._onEvent?.(J)}),this._transport.onBinary((K)=>{if(this._pendingAudioMetadata){let J={type:"audio_delta",size:this._pendingAudioMetadata.size,audio:K};this._pendingAudioMetadata=null,this._onEvent?.(J)}}),this._transport.onClose(()=>{if(this._closed)return;this._closed=!0,this._onEvent?.({type:"connection_lost",reason:"socket_closed"})}),this._transport.onError(()=>{if(this._closed)return;this._closed=!0,this._onEvent?.({type:"connection_lost",reason:"socket_error"})})}}class z extends Error{code;constructor(K,J="TRANSPORT_ERROR"){super(K);this.code=J;this.name="ConversationTransportError"}}class X{_ws;_closed=!1;_onMessage=null;_onBinary=null;_onError=null;_onClose=null;constructor(K){this._ws=K}async connect(){if(this._ws.readyState===WebSocket.OPEN)throw new z("Already connected","ALREADY_CONNECTED");if(this._ws.readyState===WebSocket.CLOSING||this._ws.readyState===WebSocket.CLOSED)throw new z("Socket is closing or closed","SOCKET_CLOSED");return new Promise((K,J)=>{let Q=()=>{this._ws.removeEventListener("open",Q),this._ws.removeEventListener("error",Y),this.#K(),K()},Y=()=>{this._ws.removeEventListener("open",Q),this._ws.removeEventListener("error",Y),J(new z("Connection failed","CONNECTION_FAILED"))};this._ws.addEventListener("open",Q),this._ws.addEventListener("error",Y)})}send(K){if(this._ws.readyState!==WebSocket.OPEN)throw new z("Not connected","NOT_CONNECTED");this._ws.send(JSON.stringify(K))}async sendBinary(K){if(this._ws.readyState!==WebSocket.OPEN)throw new z("Not connected","NOT_CONNECTED");let J=await K.arrayBuffer();this._ws.send(J)}onMessage(K){this._onMessage=K}onBinary(K){this._onBinary=K}onError(K){this._onError=K}onClose(K){this._onClose=K}close(){if(this._closed)return;if(this._closed=!0,this._ws.readyState===WebSocket.OPEN||this._ws.readyState===WebSocket.CONNECTING)this._ws.close();this._onClose?.()}#K(){this._ws.addEventListener("message",(K)=>{if(K.data instanceof Blob)this._onBinary?.(K.data);else if(K.data instanceof ArrayBuffer)this._onBinary?.(new Blob([K.data]));else if(typeof K.data==="string")try{let J=JSON.parse(K.data);this._onMessage?.(J)}catch(J){console.warn("Failed to parse JSON message:",K.data,J)}}),this._ws.addEventListener("error",()=>{if(this._closed)return;this._onError?.(new z("WebSocket error")),this.close()}),this._ws.addEventListener("close",()=>{if(this._closed)return;this.close()})}}class x{http;ws;constructor(K,J){this.http=K;this.ws=J}async list(K){let J={limit:(K.limit??20).toString(),page:(K.page??1).toString()};return this.http.get(`/agents/${K.agentId}/conversations`,J)}async get(K){return this.http.get(`/conversations/${K}`)}async connect(K){let J=this.ws.connect("/conversations/websocket"),Q=new X(J);return await Q.connect(),Q.send({type:"handshake",agent_id:K.agentId}),new Promise((Y,$)=>{let B=!1;Q.onMessage((N)=>{switch(N.type){case"handshake_ok":B=!0,K.onConnection?.(N),Y(new O(Q,N.conversation_id,K));break;case"handshake_error":B=!0,K.onConnection?.(N),Q.close(),$(Error(`Handshake rejected: ${N.reason}`));break;default:break}}),Q.onError((N)=>{if(!B)$(N)}),Q.onClose(()=>{if(!B)$(Error("WebSocket connection lost during handshake"))})})}}function M(K){let J=new URL(K),Q=J.protocol==="https:"?"wss:":"ws:";return{rest:`${J.origin}/v1`,ws:`${Q}//${J.host}/v1`}}class F extends Error{status;statusText;body;constructor(K,J,Q,Y){super(K);this.status=J;this.statusText=Q;this.body=Y;this.name="HttpTransportError"}}class G{config;constructor(K){this.config=K}async request(K,J){let Q=`${this.config.baseUrl}${K}`,Y=await fetch(Q,{...J,headers:{"Content-Type":"application/json",...this.config.headers,...J?.headers}});if(!Y.ok){let $=await Y.text();throw new F(`HTTP ${Y.status}: ${Y.statusText}`,Y.status,Y.statusText,$)}return Y.json()}async get(K,J){let Q=new URL(`${this.config.baseUrl}${K}`);if(J){let Y=Object.entries(J);for(let[$,B]of Y)Q.searchParams.set($,B)}return this.request(K+Q.search,{method:"GET"})}async post(K,J){return this.request(K,{method:"POST",body:J?JSON.stringify(J):void 0})}async put(K,J){return this.request(K,{method:"PUT",body:J?JSON.stringify(J):void 0})}async patch(K,J){return this.request(K,{method:"PATCH",body:J?JSON.stringify(J):void 0})}async delete(K){return this.request(K,{method:"DELETE"})}}class _{config;constructor(K){this.config=K}connect(K){let J=`${this.config.baseUrl}${K}`;return new WebSocket(J)}}class D{http;constructor(K){this.http=K}async list(K={}){let J={limit:(K.limit??50).toString(),page:(K.page??1).toString()};if(K.conversationId)J.conversation_id=K.conversationId;return this.http.get("/messages",J)}async get(K){return this.http.get(`/messages/${K}`)}}class H{http;constructor(K){this.http=K}async list(K={}){let J={limit:(K.limit??10).toString(),page:(K.page??1).toString()};return this.http.get("/templates",J)}async get(K){return this.http.get(`/templates/${K}`)}async create(K){return this.http.post("/templates",K)}async update(K,J){return this.http.put(`/templates/${K}`,J)}async delete(K){return this.http.delete(`/templates/${K}`)}}class W{agents;conversations;templates;messages;constructor(K){let J=M(K.baseUrl),Q=new G({baseUrl:J.rest,headers:K.headers}),Y=new _({baseUrl:J.ws,headers:K.headers});this.agents=new Z(Q),this.templates=new H(Q),this.conversations=new x(Q,Y),this.messages=new D(Q)}}var L=/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g;function j(K){let J=new Set,Q;while((Q=L.exec(K))!==null){let Y=Q[1];if(!Y)continue;J.add(Y)}return J}function U(K,J){if(typeof K==="string"){let Q=j(K);for(let Y of Q)J.add(Y)}else if(Array.isArray(K))for(let Q of K)U(Q,J);else if(typeof K==="object"&&K!==null){let Q=Object.values(K);for(let Y of Q)U(Y,J)}}function v(K){let J=new Set;return U(K,J),Array.from(J).toSorted()}function d(K,J){let Q=new Map(J.map((Y)=>[Y.key,Y.value]));return K.replace(L,(Y,$)=>{return Q.get($)??Y})}function m(K){return L.test(K)}export{d as interpolateVariables,m as hasVariables,v as extractAllVariables,L as REGEX_VARIABLE,W as KivoxClient,F as HttpTransportError,z as ConversationTransportError,O as ConversationSession};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kivox/client",
3
- "version": "0.1.0-beta.16",
4
- "description": "JavaScript/TypeScript client for KIVOX",
3
+ "version": "0.1.0-beta.17",
4
+ "description": "JavaScript/TypeScript client for Kivox",
5
5
  "homepage": "https://github.com/ekisa-team/kivox#readme",
6
6
  "bugs": {
7
7
  "url": "https://github.com/ekisa-team/kivox/issues"