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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -69,6 +69,7 @@ type Agent = {
69
69
  template_id: string
70
70
  config: AgentConfig
71
71
  blueprint: AgentBlueprint
72
+ variables: AgentVariable[]
72
73
  status: AgentStatus
73
74
  created_at: Date
74
75
  updated_at: Date
@@ -89,30 +90,15 @@ type AgentBlueprint<
89
90
  nodes: NodeType[]
90
91
  edges: EdgeType[]
91
92
  };
92
- type AgentCreate = Partial<Pick<Agent, "config" | "blueprint" | "status" | "template_id">>;
93
- type AgentUpdate = Partial<Pick<AgentCreate, "config" | "blueprint">>;
94
- type NodeType = "entry" | "thread" | "tool" | "exit";
95
- type Node = {
96
- id: string
97
- type: NodeType
98
- position: NodePosition
99
- measured: NodeDimensions
100
- data: Record<string, unknown>
101
- };
102
- type NodePosition = {
103
- x: number
104
- y: number
105
- };
106
- type NodeDimensions = {
107
- width: number
108
- height: number
109
- };
110
- type Edge = {
111
- id: string
112
- source: string
113
- target: string
114
- label: string
93
+ type AgentVariable = {
94
+ key: string
95
+ value: string
96
+ kind: "static" | "dynamic"
97
+ is_secret: boolean
98
+ description: string
115
99
  };
100
+ type AgentCreate = Partial<Pick<Agent, "config" | "blueprint" | "variables" | "status" | "template_id">>;
101
+ type AgentUpdate = Partial<Pick<AgentCreate, "config" | "blueprint" | "variables">>;
116
102
  /**
117
103
  * Configuration for listing agents.
118
104
  */
@@ -251,197 +237,157 @@ declare class WebSocketTransport {
251
237
  connect(path: string): WebSocket;
252
238
  }
253
239
  /**
254
- * Represents a handshake offer initiated by the client.
240
+ * Initializes a new conversational session.
241
+ * It must be the first message sent after connection establishment.
255
242
  */
256
- type ClientSessionHandshakeOffer = {
257
- type: "session.handshake.offer"
243
+ type ClientHandshake = {
244
+ type: "handshake"
258
245
  agent_id: string
259
246
  };
260
247
  /**
261
- * Signals that the client has ended the session.
262
- */
263
- type ClientSessionEnd = {
264
- type: "session.end"
265
- conversation_id: string
266
- };
267
- /**
268
- * Sends a text input from the client to the server.
248
+ * Carries a single user text message.
269
249
  */
270
250
  type ClientInputText = {
271
- type: "input.text"
272
- conversation_id: string
251
+ type: "input_text"
273
252
  text: string
274
253
  };
275
254
  /**
276
- * Indicates that the client is starting an audio input.
255
+ * Signals that audio input will begin.
256
+ * Typically followed by one or more binary WebSocket frames.
277
257
  */
278
258
  type ClientInputAudio = {
279
- type: "input.audio"
280
- conversation_id: string
259
+ type: "input_audio"
281
260
  };
282
261
  /**
283
- * Represents a continuous stream of audio data from the client.
262
+ * Signals that a streamed audio chunk will follow in the next binary frame.
284
263
  */
285
264
  type ClientInputAudioStream = {
286
- type: "input.audio.stream"
287
- conversation_id: string
288
- };
289
- /**
290
- * Signals that the client has cancelled a request.
291
- */
292
- type ClientRequestCancel = {
293
- type: "request.cancel"
294
- conversation_id: string
295
- };
296
- /**
297
- * Union type for all messages sent from the client to the server.
298
- */
299
- type ClientMessage = ClientSessionHandshakeOffer | ClientSessionEnd | ClientInputText | ClientInputAudio | ClientInputAudioStream | ClientRequestCancel;
300
- /**
301
- * Sent by the server when a handshake offer is accepted.
302
- */
303
- type ServerSessionHandshakeAccept = {
304
- type: "session.handshake.accept"
305
- agent_id: string
306
- conversation_id: string
307
- };
308
- /**
309
- * Sent by the server when a handshake offer is rejected.
310
- */
311
- type ServerSessionHandshakeReject = {
312
- type: "session.handshake.reject"
313
- reason: string
265
+ type: "input_audio_stream"
314
266
  };
315
267
  /**
316
- * Signals that the session has officially started.
268
+ * Requests interruption of the current streaming response.
317
269
  */
318
- type ServerSessionStart = {
319
- type: "session.start"
320
- conversation_id: string
321
- start_time: number
322
- end_time: number
323
- max_duration_ms: number
270
+ type ClientCancel = {
271
+ type: "cancel"
324
272
  };
325
273
  /**
326
- * Signals that the server has ended the session.
274
+ * Requests graceful session termination.
327
275
  */
328
- type ServerSessionEnd = {
329
- type: "session.end"
330
- conversation_id: string
276
+ type ClientEndSession = {
277
+ type: "end"
331
278
  };
332
279
  /**
333
- * Periodic update from the server regarding session timing.
280
+ * Union type for all JSON messages sent from the client to the server.
334
281
  */
335
- type ServerSessionTick = {
336
- type: "session.tick"
337
- conversation_id: string
338
- max_duration_ms: number
339
- remaining_time_ms: number
340
- elapsed_time_ms: number
341
- };
282
+ type ClientMessage = ClientHandshake | ClientInputText | ClientInputAudio | ClientInputAudioStream | ClientCancel | ClientEndSession;
342
283
  /**
343
- * Sent when a session ends due to inactivity or exceeding max duration.
284
+ * Confirms successful session initialization.
344
285
  */
345
- type ServerSessionTimeout = {
346
- type: "session.timeout"
286
+ type ServerHandshakeOK = {
287
+ type: "handshake_ok"
347
288
  conversation_id: string
348
289
  };
349
290
  /**
350
- * Signals that the server has detected the start of speech.
291
+ * Indicates that the session could not be created.
351
292
  */
352
- type ServerSpeechStart = {
353
- type: "speech.start"
354
- conversation_id: string
293
+ type ServerHandshakeError = {
294
+ type: "handshake_error"
295
+ reason: string
355
296
  };
356
297
  /**
357
- * Signals that the server has detected the end of speech.
298
+ * Carries a full text message generated by the runtime.
358
299
  */
359
- type ServerSpeechEnd = {
360
- type: "speech.end"
361
- conversation_id: string
300
+ type ServerTextFull = {
301
+ type: "text_full"
302
+ content: string
362
303
  };
363
304
  /**
364
- * A partial text response chunk sent by the server.
305
+ * Carries a streamed text fragment generated by the runtime.
365
306
  */
366
- type ServerResponseDeltaText = {
367
- type: "response.delta.text"
368
- conversation_id: string
307
+ type ServerTextDelta = {
308
+ type: "text_delta"
369
309
  chunk: string
370
310
  };
371
311
  /**
372
- * A partial audio response chunk sent by the server.
312
+ * Carries metadata for a streamed audio fragment generated by the runtime.
313
+ * The actual binary audio data is delivered in the immediately following binary frame
314
+ * and merged into this object by the transport layer.
373
315
  */
374
- type ServerResponseDeltaAudio = {
375
- type: "response.delta.audio"
376
- conversation_id: string
316
+ type ServerAudioDelta = {
317
+ type: "audio_delta"
318
+ /** Expected size of the incoming binary payload in bytes */
377
319
  size: number
320
+ /** The binary audio data attached dynamically by the transport layer */
378
321
  audio: Blob
379
322
  };
380
323
  /**
381
- * Signals that the full response (text or audio) has been delivered.
324
+ * Signals that the runtime has completed its turn and is awaiting further user input.
382
325
  */
383
- type ServerResponseComplete = {
384
- type: "response.complete"
385
- conversation_id: string
326
+ type ServerTurnComplete = {
327
+ type: "turn_complete"
386
328
  };
387
329
  /**
388
- * Sent when an ongoing response is cancelled successfully.
330
+ * Signals that the conversation flow reached its exit node.
331
+ * The session will close shortly after this event.
389
332
  */
390
- type ServerResponseCancel = {
391
- type: "response.cancel"
392
- conversation_id: string
333
+ type ServerFlowComplete = {
334
+ type: "flow_complete"
393
335
  };
394
336
  /**
395
- * Sent when an error occurs during processing a request.
337
+ * Represents a non-fatal execution or protocol error.
338
+ * The session remains active unless followed by a SessionClosed event.
396
339
  */
397
- type ServerResponseError = {
398
- type: "response.error"
399
- conversation_id: string
340
+ type ServerError = {
341
+ type: "error"
400
342
  code: string
401
343
  message: string
402
344
  };
403
345
  /**
404
- * Union type for all messages sent from the server to the client.
346
+ * Provides periodic timing information for the active session.
405
347
  */
406
- type ServerMessage = ServerSessionHandshakeAccept | ServerSessionHandshakeReject | ServerSessionStart | ServerSessionEnd | ServerSessionTick | ServerSessionTimeout | ServerSpeechStart | ServerSpeechEnd | ServerResponseDeltaText | ServerResponseDeltaAudio | ServerResponseComplete | ServerResponseCancel | ServerResponseError;
348
+ type ServerTick = {
349
+ type: "tick"
350
+ /** Milliseconds until the session times out */
351
+ remaining_ms: number
352
+ /** Milliseconds elapsed since the session started */
353
+ elapsed_ms: number
354
+ };
407
355
  /**
408
- * Union type for all messages sent from the client to the server before the session is established.
356
+ * Indicates that the session is closing.
409
357
  */
410
- type ServerConnectionMessage = ServerSessionHandshakeAccept | ServerSessionHandshakeReject | ServerSessionStart;
358
+ type ServerSessionClosed = {
359
+ type: "session_closed"
360
+ reason: "flow_complete" | "ended_by_agent" | "ended_by_user" | "timeout" | "connection_closed" | "error"
361
+ };
411
362
  /**
412
- * Union type for all messages sent from the server to the client during the session lifecycle.
363
+ * Union type for all messages sent from the server to the client.
413
364
  */
414
- type ServerSessionLifecycleMessage = ServerSessionEnd | ServerSessionTick | ServerSessionTimeout;
365
+ type ServerMessage = ServerHandshakeOK | ServerHandshakeError | ServerTextFull | ServerTextDelta | ServerAudioDelta | ServerTurnComplete | ServerFlowComplete | ServerError | ServerTick | ServerSessionClosed;
415
366
  /**
416
- * Union type for all messages sent from the server to the client during the session activity.
367
+ * Messages related to the initial connection phase.
417
368
  */
418
- type ServerSessionActivityMessage = ServerSpeechStart | ServerSpeechEnd | ServerResponseDeltaText | ServerResponseDeltaAudio | ServerResponseComplete | ServerResponseCancel | ServerResponseError;
369
+ type ServerConnectionMessage = ServerHandshakeOK | ServerHandshakeError;
419
370
  /**
420
- * Synthetic event emitted when the connection to the server is lost.
371
+ * Synthetic event emitted when the WebSocket connection to the server drops unexpectedly.
421
372
  */
422
373
  type SessionConnectionLostEvent = {
423
- type: "session.connection.lost"
424
- conversation_id: string
374
+ type: "connection_lost"
425
375
  reason: "socket_closed" | "socket_error"
426
376
  };
427
377
  /**
428
- * Events emitted during an active session
429
- *
430
- * @remarks
431
- * Connection messages are handled internally during connection establishment
432
- * and are not exposed through the session's `onEvent` callback.
378
+ * Union type of all events that can be emitted during an active conversation session.
433
379
  */
434
- type ServerSessionEvent = ServerSessionLifecycleMessage | ServerSessionActivityMessage | SessionConnectionLostEvent;
380
+ type ServerSessionEvent = ServerTextFull | ServerTextDelta | ServerAudioDelta | ServerTurnComplete | ServerFlowComplete | ServerError | ServerTick | ServerSessionClosed | SessionConnectionLostEvent;
435
381
  /**
436
- * Error thrown by the transport layer.
382
+ * Error thrown by the transport layer when connection or state issues occur.
437
383
  */
438
384
  declare class ConversationTransportError extends Error {
439
385
  readonly code: string;
440
386
  constructor(message: string, code?: string);
441
387
  }
442
388
  /**
443
- * Low-level WebSocket transport for conversation protocol.
444
- * Handles connection lifecycle and binary/JSON message routing.
389
+ * Low-level WebSocket transport for the conversation protocol.
390
+ * Handles connection lifecycle and distinct routing for JSON and binary messages.
445
391
  */
446
392
  declare class ConversationTransport {
447
393
  private;
@@ -451,108 +397,139 @@ declare class ConversationTransport {
451
397
  private _onBinary;
452
398
  private _onError;
453
399
  private _onClose;
400
+ /**
401
+ * Creates a new ConversationTransport.
402
+ * @param _ws The underlying WebSocket instance to wrap.
403
+ */
454
404
  constructor(_ws: WebSocket);
455
405
  /**
456
- * Waits for WebSocket to open.
457
- * @throws {ConversationTransportError} If already connected or connection fails
406
+ * Waits for the WebSocket connection to fully open.
407
+ * @throws {ConversationTransportError} If the socket is already connected, closing, or fails to connect.
458
408
  */
459
409
  connect(): Promise<void>;
460
410
  /**
461
- * Sends a JSON message to the server.
462
- * @throws {ConversationTransportError} If not connected
411
+ * Sends a structured JSON message to the server.
412
+ * @param message The client message to send.
413
+ * @throws {ConversationTransportError} If the WebSocket is not currently open.
463
414
  */
464
415
  send(message: ClientMessage): void;
465
416
  /**
466
- * Sends binary data (audio) to the server.
467
- * @throws {ConversationTransportError} If not connected
417
+ * Sends binary data (like raw audio) to the server via a pure binary WebSocket frame.
418
+ * @param data The Blob representing the binary data.
419
+ * @throws {ConversationTransportError} If the WebSocket is not currently open.
468
420
  */
469
421
  sendBinary(data: Blob): Promise<void>;
470
422
  /**
471
- * Registers a callback for incoming JSON messages.
423
+ * Registers a callback to be invoked when a JSON message is received from the server.
424
+ * @param handler The callback function.
472
425
  */
473
426
  onMessage(handler: (msg: ServerMessage) => void): void;
474
427
  /**
475
- * Registers a callback for incoming binary data.
428
+ * Registers a callback to be invoked when a pure binary frame is received from the server.
429
+ * @param handler The callback function.
476
430
  */
477
431
  onBinary(handler: (blob: Blob) => void): void;
478
432
  /**
479
- * Registers a callback for connection errors.
433
+ * Registers a callback to be invoked if a WebSocket error occurs.
434
+ * @param handler The callback function.
480
435
  */
481
436
  onError(handler: (error: Error) => void): void;
482
437
  /**
483
- * Registers a callback for connection closure.
438
+ * Registers a callback to be invoked when the WebSocket connection is closed.
439
+ * @param handler The callback function.
484
440
  */
485
441
  onClose(handler: () => void): void;
486
442
  /**
487
- * Closes the WebSocket connection and cleans up resources.
443
+ * Closes the underlying WebSocket connection and cleans up resources.
488
444
  */
489
445
  close(): void;
490
446
  }
491
447
  /**
492
- * Configuration for a conversation session.
448
+ * Configuration options provided when establishing a conversation session.
493
449
  */
494
450
  type ConversationSessionConfig = {
495
451
  /**
496
- * Event handler for all server messages.
452
+ * A callback function invoked whenever an event is emitted by the server during the session.
497
453
  *
498
454
  * @remarks
499
- * Important: `response.delta.audio` events combine the audio `Blob` and the metadata.
455
+ * For `audio_delta` events, the SDK automatically waits for the subsequent binary WebSocket frame
456
+ * and attaches the resulting `Blob` to the event object before triggering this callback.
500
457
  */
501
458
  onEvent?: (event: ServerSessionEvent) => void
502
459
  };
503
460
  /**
504
- * Represents an active conversation session with a KIVOX agent.
505
- * Provides methods to send messages and audio, and a callback to handle all server events.
461
+ * Represents an active, real-time conversation session with a KIVOX agent.
462
+ * Provides methods to dispatch user inputs and lifecycle commands, as well as tracking basic session state.
506
463
  */
507
464
  declare class ConversationSession {
508
465
  private;
509
466
  private _transport;
510
467
  private _conversationId;
511
- private _maxDurationMs;
512
- private _startTime;
513
- private _endTime;
514
468
  private _closed;
515
- private _pendingAudioMetadata;
516
469
  private _onEvent;
517
- /** The unique conversation identifier */
470
+ private _pendingAudioMetadata;
471
+ private _remainingMs;
472
+ private _elapsedMs;
473
+ /**
474
+ * The unique server-generated ID for this active conversation.
475
+ */
518
476
  get conversationId(): string;
519
- /** Maximum session duration in milliseconds */
520
- get maxDurationMs(): number;
521
- /** Unix timestamp when session started */
522
- get startTime(): number;
523
- /** Unix timestamp when session will end */
524
- get endTime(): number;
525
- /** Whether the session has been closed */
477
+ /**
478
+ * The number of milliseconds remaining until the engine enforces a session timeout.
479
+ * This value is periodically synced via server 'tick' events.
480
+ */
481
+ get remainingMs(): number;
482
+ /**
483
+ * The number of milliseconds elapsed since the session was initiated.
484
+ * This value is periodically synced via server 'tick' events.
485
+ */
486
+ get elapsedMs(): number;
487
+ /**
488
+ * Indicates whether the session has been permanently closed.
489
+ */
526
490
  get closed(): boolean;
527
- constructor(transport: ConversationTransport, conversationId: string, maxDurationMs: number, startTime: number, endTime: number, config: ConversationSessionConfig);
528
491
  /**
529
- * Sends a text message to the agent.
530
- * @param text The message text to send
492
+ * Constructs a new active ConversationSession.
493
+ * Normally called internally by `ConversationClient.connect()`.
494
+ *
495
+ * @param transport The underlying active WebSocket transport.
496
+ * @param conversationId The ID assigned by the server during the handshake.
497
+ * @param config User-provided configuration options (e.g., event handlers).
498
+ */
499
+ constructor(transport: ConversationTransport, conversationId: string, config: ConversationSessionConfig);
500
+ /**
501
+ * Sends a single textual user input to the agent.
502
+ *
503
+ * @param text The textual payload provided by the user.
531
504
  */
532
505
  sendText(text: string): void;
533
506
  /**
534
- * Sends a complete audio message to the agent.
535
- * @param audio Audio blob to send
507
+ * Sends a complete, recorded audio buffer to the agent.
508
+ * First sends an `input_audio` header, then dispatches the binary frame.
509
+ *
510
+ * @param audio The raw binary audio file/buffer.
536
511
  */
537
512
  sendAudio(audio: Blob): void;
538
513
  /**
539
- * Streams an audio chunk to the agent (for real-time audio input).
540
- * @param chunk Audio chunk to stream
514
+ * Streams a fragment of real-time audio to the agent.
515
+ * First sends an `input_audio_stream` header, then dispatches the binary frame.
516
+ *
517
+ * @param chunk The raw binary audio fragment.
541
518
  */
542
519
  streamAudio(chunk: Blob): void;
543
520
  /**
544
- * Requests the session to be cancelled.
545
- * The 'session.cancel' event will be triggered when complete.
521
+ * Requests that the server immediately halt the currently streaming assistant response.
522
+ * The session remains open and the agent will await the next user input.
546
523
  */
547
524
  cancelRequest(): void;
548
525
  /**
549
- * Requests the session to end gracefully.
550
- * The 'session.end' event will be triggered when complete.
526
+ * Instructs the server to gracefully terminate the conversation.
527
+ * Wait for the resulting `session_closed` event to confirm termination.
551
528
  */
552
529
  end(): void;
553
530
  /**
554
- * Immediately closes the session and transport.
555
- * No further events will be received.
531
+ * Immediately tears down the underlying WebSocket transport and marks the session closed locally.
532
+ * Does not wait for a graceful server acknowledgment.
556
533
  */
557
534
  close(): void;
558
535
  }
@@ -563,49 +540,43 @@ type Conversation = {
563
540
  ended_at?: Date
564
541
  };
565
542
  /**
566
- * Configuration for listing conversations.
543
+ * Configuration parameters for fetching a list of past conversations.
567
544
  */
568
545
  type ConversationListParams = {
569
- /** Agent ID to filter by */
546
+ /** Agent ID to filter the conversations by */
570
547
  agentId: string
571
- /** Number of results per page */
548
+ /** Number of results per page (defaults to 20) */
572
549
  limit?: number
573
- /** Page number (1-indexed) */
550
+ /** Page number, 1-indexed (defaults to 1) */
574
551
  page?: number
575
552
  };
576
553
  /**
577
- * Extended configuration for the connect method.
554
+ * Configuration parameters for connecting to an agent and establishing a session.
578
555
  */
579
556
  type ConversationConnectParams = ConversationSessionConfig & {
580
- /** Agent ID to connect to */
557
+ /** The unique ID of the agent to connect to */
581
558
  agentId: string
582
559
  /**
583
- * Optional callback for connection events.
584
- * Called during connection establishment, before the session is created.
560
+ * Optional callback invoked during the initial handshake phase.
561
+ * Can be used to hook into successful or failed connection attempts before the session object is returned.
585
562
  */
586
563
  onConnection?: (event: ServerConnectionMessage) => void
587
564
  };
588
565
  /**
589
- * Client for managing conversations and establishing sessions.
566
+ * Client service for managing conversational records and establishing real-time WebSocket sessions.
590
567
  *
591
568
  * @example
592
569
  * ```ts
593
- * // List past conversations
594
- * const conversations = await kivox.conversations.list();
595
- *
596
- * // Connect to an agent
570
+ * // Connect to a KIVOX agent
597
571
  * const session = await kivox.conversations.connect({
598
- * agentId: 'agent-123',
599
- * onEvent: (event) => {
600
- * switch (event.type) {
601
- * case 'response.delta.text':
602
- * console.log(event.chunk);
603
- * break;
604
- * case 'response.delta.audio':
605
- * audioPlayer.enqueue(event.audio);
606
- * break;
607
- * }
608
- * }
572
+ * agentId: 'agent-123',
573
+ * onEvent: (event) => {
574
+ * if (event.type === 'text_delta') {
575
+ * console.log(event.chunk);
576
+ * } else if (event.type === 'audio_delta') {
577
+ * audioPlayer.enqueue(event.audio);
578
+ * }
579
+ * }
609
580
  * });
610
581
  *
611
582
  * session.sendText('Hello!');
@@ -614,27 +585,33 @@ type ConversationConnectParams = ConversationSessionConfig & {
614
585
  declare class ConversationClient {
615
586
  private readonly http;
616
587
  private readonly ws;
588
+ /**
589
+ * Instantiates a new ConversationClient.
590
+ * @param http The HTTP transport for REST endpoints.
591
+ * @param ws The WebSocket transport factory.
592
+ */
617
593
  constructor(http: HttpTransport, ws: WebSocketTransport);
618
594
  /**
619
- * Lists conversations with optional filtering and pagination.
595
+ * Retrieves a paginated list of past conversations for a specific agent.
620
596
  *
621
- * @param params List configuration
622
- * @returns Paginated list of conversations
597
+ * @param params Query parameters for pagination and filtering.
598
+ * @returns A promise resolving to a paginated list of conversations.
623
599
  */
624
600
  list(params: ConversationListParams): Promise<Paginated<Conversation[]>>;
625
601
  /**
626
- * Gets a single conversation by ID.
602
+ * Retrieves the details of a single conversation by its ID.
627
603
  *
628
- * @param id Conversation ID
629
- * @returns Conversation details
604
+ * @param id The unique identifier of the conversation.
605
+ * @returns A promise resolving to the conversation data.
630
606
  */
631
607
  get(id: string): Promise<Conversation>;
632
608
  /**
633
- * Connects to an agent and establishes a conversation session.
609
+ * Establishes a real-time WebSocket session with an agent.
610
+ * Handles the handshake protocol and returns an active session instance.
634
611
  *
635
- * @param params Connection configuration
636
- * @returns Active conversation session
637
- * @throws {Error} If handshake fails or connection is lost
612
+ * @param params Configuration for the connection and subsequent session.
613
+ * @returns A promise resolving to an active `ConversationSession`.
614
+ * @throws {Error} If the handshake fails or the connection drops during negotiation.
638
615
  */
639
616
  connect(params: ConversationConnectParams): Promise<ConversationSession>;
640
617
  }
@@ -803,4 +780,18 @@ declare class KivoxClient {
803
780
  readonly messages: MessageClient;
804
781
  constructor(config: KivoxConfig);
805
782
  }
806
- export { TemplateUpdate, TemplateCreate, Template, SessionConnectionLostEvent, ServerSpeechStart, ServerSpeechEnd, ServerSessionTimeout, ServerSessionTick, ServerSessionStart, ServerSessionLifecycleMessage, ServerSessionHandshakeReject, ServerSessionHandshakeAccept, ServerSessionEvent, ServerSessionEnd, ServerSessionActivityMessage, ServerResponseError, ServerResponseDeltaText, ServerResponseDeltaAudio, ServerResponseComplete, ServerResponseCancel, ServerMessage, ServerConnectionMessage, Paginated, NodeType, NodePosition, NodeDimensions, Node, MessageRole, Message, KivoxConfig, KivoxClient, HttpTransportError, Edge, ConversationTransportError, ConversationSessionConfig, ConversationSession, Conversation, ClientSessionHandshakeOffer, ClientSessionEnd, ClientRequestCancel, ClientMessage, ClientInputText, ClientInputAudioStream, ClientInputAudio, AgentUpdate, AgentStatus, AgentCreate, AgentConfig, AgentBlueprint, Agent };
783
+ import { AgentBlueprint as AgentBlueprint2, AgentConfig as AgentConfig2, AgentVariable as AgentVariable2 } from "@kivox/client";
784
+ declare const REGEX_VARIABLE: RegExp;
785
+ /**
786
+ * Extracts all variable references from config and blueprint.
787
+ */
788
+ declare function extractAllVariables(config: AgentConfig2, blueprint: AgentBlueprint2<Record<string, unknown>, Record<string, unknown>>): string[];
789
+ /**
790
+ * Replaces variable references with their values
791
+ */
792
+ declare function interpolateVariables(text: string, variables: AgentVariable2[]): string;
793
+ /**
794
+ * Checks if a string contains any variable references.
795
+ */
796
+ 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 };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- class J{http;constructor(x){this.http=x}async list(x={}){let K={limit:(x.limit??10).toString(),page:(x.page??1).toString()};if(x.search)K.search=x.search;let B=await this.http.get("/agents",K);return{...B,items:J.parseAgents(B.items)}}async listLive(x={}){let K={limit:(x.limit??10).toString(),page:(x.page??1).toString()};if(x.search)K.search=x.search;let B=await this.http.get("/agents/live",K);return{...B,items:J.parseAgents(B.items)}}async listDraft(x={}){let K={limit:(x.limit??10).toString(),page:(x.page??1).toString()};if(x.search)K.search=x.search;let B=await this.http.get("/agents/draft",K);return{...B,items:J.parseAgents(B.items)}}async listArchived(x={}){let K={limit:(x.limit??10).toString(),page:(x.page??1).toString()};if(x.search)K.search=x.search;let B=await this.http.get("/agents/archived",K);return{...B,items:J.parseAgents(B.items)}}async get(x){let K=await this.http.get(`/agents/${x}`);return J.parseAgent(K)}async create(x){let K=await this.http.post("/agents",x);return J.parseAgent(K)}async update(x,K){let B=await this.http.put(`/agents/${x}`,K);return J.parseAgent(B)}async markAsLive(x){let K=await this.http.patch(`/agents/${x}/live`);return J.parseAgent(K)}async markAsDraft(x){let K=await this.http.patch(`/agents/${x}/draft`);return J.parseAgent(K)}async markAsArchived(x){let K=await this.http.patch(`/agents/${x}/archived`);return J.parseAgent(K)}async delete(x){let K=await this.http.delete(`/agents/${x}`);return J.parseAgent(K)}static parseAgents(x){return x.map(J.parseAgent)}static parseAgent(x){return{...x,config:JSON.parse(x.config),blueprint:JSON.parse(x.blueprint)}}}class _{_transport;_conversationId;_maxDurationMs;_startTime;_endTime;_closed=!1;_pendingAudioMetadata=null;_onEvent;get conversationId(){return this._conversationId}get maxDurationMs(){return this._maxDurationMs}get startTime(){return this._startTime}get endTime(){return this._endTime}get closed(){return this._closed}constructor(x,K,B,F,Q,Y){this._transport=x,this._conversationId=K,this._maxDurationMs=B,this._startTime=F,this._endTime=Q,this._onEvent=Y.onEvent,this.#x()}sendText(x){if(this._closed){console.warn("Cannot send text: session is closed");return}this._transport.send({type:"input.text",conversation_id:this._conversationId,text:x})}sendAudio(x){if(this._closed){console.warn("Cannot send audio: session is closed");return}this._transport.send({type:"input.audio",conversation_id:this._conversationId}),this._transport.sendBinary(x)}streamAudio(x){if(this._closed){console.warn("Cannot stream audio: session is closed");return}this._transport.send({type:"input.audio.stream",conversation_id:this._conversationId}),this._transport.sendBinary(x)}cancelRequest(){if(this._closed){console.warn("Cannot cancel session: session is closed");return}this._transport.send({type:"request.cancel",conversation_id:this._conversationId})}end(){if(this._closed){console.warn("Cannot end session: session is closed");return}this._transport.send({type:"session.end",conversation_id:this._conversationId})}close(){if(this._closed)return;this._closed=!0,this._transport.close()}#x(){this._transport.onMessage((x)=>{let K=x;if(K.type==="response.delta.audio"){this._pendingAudioMetadata={conversation_id:K.conversation_id,size:K.size};return}if(K.type==="session.end"||K.type==="session.timeout"||K.type==="session.connection.lost")this._closed=!0;this._onEvent?.(K)}),this._transport.onBinary((x)=>{if(this._pendingAudioMetadata){let K={type:"response.delta.audio",conversation_id:this._pendingAudioMetadata.conversation_id,size:this._pendingAudioMetadata.size,audio:x};this._pendingAudioMetadata=null,this._onEvent?.(K)}}),this._transport.onClose(()=>{if(this._closed)return;this._closed=!0,this._onEvent?.({type:"session.connection.lost",conversation_id:this._conversationId,reason:"socket_closed"})}),this._transport.onError(()=>{if(this._closed)return;this._closed=!0,this._onEvent?.({type:"session.connection.lost",conversation_id:this._conversationId,reason:"socket_error"})})}}class Z extends Error{code;constructor(x,K="TRANSPORT_ERROR"){super(x);this.code=K;this.name="ConversationTransportError"}}class ${_ws;_closed=!1;_onMessage=null;_onBinary=null;_onError=null;_onClose=null;constructor(x){this._ws=x}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((x,K)=>{let B=()=>{this._ws.removeEventListener("open",B),this._ws.removeEventListener("error",F),this.#x(),x()},F=()=>{this._ws.removeEventListener("open",B),this._ws.removeEventListener("error",F),K(new Z("Connection failed","CONNECTION_FAILED"))};this._ws.addEventListener("open",B),this._ws.addEventListener("error",F)})}send(x){if(this._ws.readyState!==WebSocket.OPEN)throw new Z("Not connected","NOT_CONNECTED");this._ws.send(JSON.stringify(x))}async sendBinary(x){if(this._ws.readyState!==WebSocket.OPEN)throw new Z("Not connected","NOT_CONNECTED");let K=await x.arrayBuffer();this._ws.send(K)}onMessage(x){this._onMessage=x}onBinary(x){this._onBinary=x}onError(x){this._onError=x}onClose(x){this._onClose=x}close(){if(this._closed)return;if(this._closed=!0,this._ws.readyState===WebSocket.OPEN||this._ws.readyState===WebSocket.CONNECTING)this._ws.close();this._onClose?.()}#x(){this._ws.addEventListener("message",(x)=>{if(x.data instanceof Blob)this._onBinary?.(x.data);else if(x.data instanceof ArrayBuffer)this._onBinary?.(new Blob([x.data]));else try{let K=JSON.parse(x.data);this._onMessage?.(K)}catch(K){console.warn("Failed to parse message:",x.data,K)}}),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 z{http;ws;constructor(x,K){this.http=x;this.ws=K}async list(x){let K={limit:(x.limit??20).toString(),page:(x.page??1).toString()};return this.http.get(`/agents/${x.agentId}/conversations`,K)}async get(x){return this.http.get(`/conversations/${x}`)}async connect(x){let K=this.ws.connect("/conversations/websocket"),B=new $(K);return await B.connect(),B.send({type:"session.handshake.offer",agent_id:x.agentId}),new Promise((F,Q)=>{let Y=!1;B.onMessage((N)=>{switch(N.type){case"session.handshake.accept":x.onConnection?.(N);break;case"session.handshake.reject":Y=!0,x.onConnection?.(N),B.close(),Q(Error(`Handshake rejected: ${N.reason}`));break;case"session.start":{Y=!0,x.onConnection?.(N);let W=new _(B,N.conversation_id,N.max_duration_ms,N.start_time,N.end_time,x);F(W);break}default:break}}),B.onError((N)=>{if(!Y)Q(N)}),B.onClose(()=>{if(!Y)Q(Error("WebSocket connection lost during handshake"))})})}}function O(x){let K=new URL(x),B=K.protocol==="https:"?"wss:":"ws:";return{rest:`${K.origin}/v1`,ws:`${B}//${K.host}/v1`}}class G extends Error{status;statusText;body;constructor(x,K,B,F){super(x);this.status=K;this.statusText=B;this.body=F;this.name="HttpTransportError"}}class V{config;constructor(x){this.config=x}async request(x,K){let B=`${this.config.baseUrl}${x}`,F=await fetch(B,{...K,headers:{"Content-Type":"application/json",...this.config.headers,...K?.headers}});if(!F.ok){let Q=await F.text();throw new G(`HTTP ${F.status}: ${F.statusText}`,F.status,F.statusText,Q)}return F.json()}async get(x,K){let B=new URL(`${this.config.baseUrl}${x}`);if(K){let F=Object.entries(K);for(let[Q,Y]of F)B.searchParams.set(Q,Y)}return this.request(x+B.search,{method:"GET"})}async post(x,K){return this.request(x,{method:"POST",body:K?JSON.stringify(K):void 0})}async put(x,K){return this.request(x,{method:"PUT",body:K?JSON.stringify(K):void 0})}async patch(x,K){return this.request(x,{method:"PATCH",body:K?JSON.stringify(K):void 0})}async delete(x){return this.request(x,{method:"DELETE"})}}class X{config;constructor(x){this.config=x}connect(x){let K=`${this.config.baseUrl}${x}`;return new WebSocket(K)}}class L{http;constructor(x){this.http=x}async list(x={}){let K={limit:(x.limit??50).toString(),page:(x.page??1).toString()};if(x.conversationId)K.conversation_id=x.conversationId;return this.http.get("/messages",K)}async get(x){return this.http.get(`/messages/${x}`)}}class H{http;constructor(x){this.http=x}async list(x={}){let K={limit:(x.limit??10).toString(),page:(x.page??1).toString()};return this.http.get("/templates",K)}async get(x){return this.http.get(`/templates/${x}`)}async create(x){return this.http.post("/templates",x)}async update(x,K){return this.http.put(`/templates/${x}`,K)}async delete(x){return this.http.delete(`/templates/${x}`)}}class U{agents;conversations;templates;messages;constructor(x){let K=O(x.baseUrl),B=new V({baseUrl:K.rest,headers:x.headers}),F=new X({baseUrl:K.ws,headers:x.headers});this.agents=new J(B),this.templates=new H(B),this.conversations=new z(B,F),this.messages=new L(B)}}export{U as KivoxClient,G as HttpTransportError,Z as ConversationTransportError,_ 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)}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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kivox/client",
3
- "version": "0.1.0-beta.14",
3
+ "version": "0.1.0-beta.16",
4
4
  "description": "JavaScript/TypeScript client for KIVOX",
5
5
  "homepage": "https://github.com/ekisa-team/kivox#readme",
6
6
  "bugs": {