@dialgem/client 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ /** A minimal typed event emitter. A handler that throws never stops the others or the call. */
2
+ declare class Emitter<Events extends Record<string, unknown>> {
3
+ private handlers;
4
+ on<K extends keyof Events>(event: K, handler: (payload: Events[K]) => void): () => void;
5
+ off<K extends keyof Events>(event: K, handler: (payload: Events[K]) => void): void;
6
+ protected emit<K extends keyof Events>(event: K, payload: Events[K]): void;
7
+ }
8
+
9
+ type CallState = "idle" | "connecting" | "active" | "ended" | "error";
10
+ /** How a call that started came to an end. */
11
+ type EndReason =
12
+ /** `stop()` was called. */
13
+ "hangup"
14
+ /** The agent ended the call, or a call limit (maximum length, silence) did. */
15
+ | "agent_ended"
16
+ /** The connection dropped. The call cannot be resumed: start a new one. */
17
+ | "connection_lost";
18
+ type ErrorCode =
19
+ /** The browser lacks something the call needs (WebSocket, Web Audio, AudioWorklet, a microphone API). */
20
+ "unsupported"
21
+ /** The visitor refused microphone access, or it is blocked for this page. */
22
+ | "mic_denied"
23
+ /** No microphone, or it could not be opened. */
24
+ | "mic_unavailable"
25
+ /** `accessToken` was a function and it threw or returned nothing. */
26
+ | "token_unavailable"
27
+ /** The token was invalid, expired or already used, or this page's origin is not allowed. */
28
+ | "refused"
29
+ /** The organization has too many calls running. */
30
+ | "busy"
31
+ /** The server declined the call; `reason` says why (for example `insufficient_credit`). */
32
+ | "call_refused" | "server_error" | "connection_failed";
33
+ interface CallError {
34
+ code: ErrorCode;
35
+ message: string;
36
+ /** For `call_refused`: the server's reason. */
37
+ reason?: string | undefined;
38
+ }
39
+ interface TranscriptEvent {
40
+ role: "user" | "agent";
41
+ text: string;
42
+ /** False while the caller is still speaking (the words so far); true once the line is settled. */
43
+ final: boolean;
44
+ }
45
+ interface CallEvents extends Record<string, unknown> {
46
+ state: CallState;
47
+ started: {
48
+ callId: string;
49
+ };
50
+ transcript: TranscriptEvent;
51
+ agent_speaking: {
52
+ speaking: boolean;
53
+ };
54
+ /** The caller talked over the agent and its speech was cut off. */
55
+ interrupted: Record<string, never>;
56
+ /** Microphone level, 0 to 1, about ten times a second. Not emitted while muted. */
57
+ level: {
58
+ level: number;
59
+ };
60
+ ended: {
61
+ reason: EndReason;
62
+ };
63
+ error: CallError;
64
+ }
65
+ interface MicrophoneOptions {
66
+ deviceId?: string | undefined;
67
+ /** Default true: without it the agent's own voice comes back through the microphone and it answers itself. */
68
+ echoCancellation?: boolean | undefined;
69
+ noiseSuppression?: boolean | undefined;
70
+ autoGainControl?: boolean | undefined;
71
+ }
72
+ interface DialgemCallOptions {
73
+ /**
74
+ * The access token from your backend's `POST /v1/calls/web`, or a function that returns it. A function
75
+ * is called **after** the visitor has allowed the microphone, because the token only lives 60 seconds and
76
+ * the permission prompt can take longer than that.
77
+ */
78
+ accessToken: string | (() => string | Promise<string>);
79
+ /** Only for a self-hosted deployment. Default `wss://api.dialgem.com`. */
80
+ baseURL?: string | undefined;
81
+ microphone?: MicrophoneOptions | undefined;
82
+ /** @internal Stand-ins for the browser, for tests. */
83
+ platform?: Partial<Platform> | undefined;
84
+ }
85
+ /** What the call needs from the browser. */
86
+ interface Platform {
87
+ WebSocket: typeof WebSocket;
88
+ AudioContext: typeof AudioContext;
89
+ AudioWorkletNode: typeof AudioWorkletNode;
90
+ getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>;
91
+ createObjectURL(blob: Blob): string;
92
+ revokeObjectURL(url: string): void;
93
+ Blob: typeof Blob;
94
+ }
95
+ /**
96
+ * One conversation with one agent.
97
+ *
98
+ * const call = new DialgemCall({ accessToken: () => fetch("/api/start-call").then((r) => r.text()) })
99
+ * call.on("transcript", (t) => ...)
100
+ * await call.start() // from a click: browsers only let a page open the microphone and play sound after one
101
+ * ...
102
+ * call.stop()
103
+ *
104
+ * A call object is used once. Its token is single-use, so a second call needs a new object (and a new token).
105
+ * There is no reconnecting: the conversation lives on the server for as long as the socket does, and a call
106
+ * that drops has ended.
107
+ */
108
+ declare class DialgemCall extends Emitter<CallEvents> {
109
+ private readonly options;
110
+ private _state;
111
+ private _muted;
112
+ private _callId;
113
+ private readonly platform;
114
+ private ctx;
115
+ private stream;
116
+ private ws;
117
+ private player;
118
+ private source;
119
+ private node;
120
+ private sink;
121
+ private moduleUrl;
122
+ private speakingTimer;
123
+ private drainTimer;
124
+ private ending;
125
+ private pendingRefusal;
126
+ private failure;
127
+ private lastLevelAt;
128
+ private speaking;
129
+ constructor(options: DialgemCallOptions);
130
+ get state(): CallState;
131
+ /** Set once the call has started. */
132
+ get callId(): string | null;
133
+ get muted(): boolean;
134
+ /**
135
+ * Opens the microphone and the call. Call it directly from a click or tap: the browser decides whether the page
136
+ * may play sound at the moment this runs, and a call started from a timer or after other awaits can be silent.
137
+ * Resolves when the connection is open; the call has started when `started` fires. A failure is reported as an
138
+ * `error` event and also rejects.
139
+ */
140
+ start(): Promise<void>;
141
+ /** Ends the call. Safe to call at any time, any number of times. */
142
+ stop(): void;
143
+ /** While muted the agent hears silence. The call stays open. */
144
+ setMuted(muted: boolean): void;
145
+ private onMessage;
146
+ private onStarted;
147
+ private onCaptured;
148
+ private onAudio;
149
+ private setSpeaking;
150
+ private onCallEnded;
151
+ private onClose;
152
+ private dead;
153
+ /** Ends the call as failed and reports it once. Returns the error, for `start()` to throw. */
154
+ private fail;
155
+ private finish;
156
+ private setState;
157
+ private cleanup;
158
+ }
159
+
160
+ export { type CallError as C, DialgemCall as D, type EndReason as E, type MicrophoneOptions as M, type TranscriptEvent as T, type CallEvents as a, type CallState as b, type DialgemCallOptions as c, type ErrorCode as d };