@voxdiscover/voiceserver-react 0.1.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/LICENSE +21 -0
- package/README.md +418 -0
- package/dist/index.cjs +343 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +334 -0
- package/dist/index.d.ts +334 -0
- package/dist/index.js +319 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode, Dispatch, SetStateAction } from 'react';
|
|
4
|
+
import { VoiceAgentConfig, VoiceAgent, ConnectionState, TranscriptData } from '@voxdiscover/voiceserver';
|
|
5
|
+
export * from '@voxdiscover/voiceserver';
|
|
6
|
+
export { ConnectionState, ReconnectionManager, TranscriptData, VoiceAgent, VoiceAgentConfig, VoiceAgentErrorCode, VoiceAgentEvents } from '@voxdiscover/voiceserver';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Props for VoiceAgentProvider component.
|
|
10
|
+
* Per user decision: Config object pattern for flexibility.
|
|
11
|
+
*/
|
|
12
|
+
interface VoiceAgentProviderProps extends VoiceAgentConfig {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
/** Callback when connection is established */
|
|
15
|
+
onConnect?: () => void;
|
|
16
|
+
/** Callback when connection is closed */
|
|
17
|
+
onDisconnect?: () => void;
|
|
18
|
+
/** Callback for connection errors */
|
|
19
|
+
onError?: (error: Error) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Context provider for VoiceAgent instance.
|
|
23
|
+
*
|
|
24
|
+
* Per user decisions:
|
|
25
|
+
* - Creates VoiceAgent instance but waits for explicit connect() call (manual connect pattern)
|
|
26
|
+
* - Auto-disconnects and cleans up WebRTC resources on unmount
|
|
27
|
+
* - Provides stable agent reference via useRef (prevents re-render cascades)
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* function App() {
|
|
32
|
+
* return (
|
|
33
|
+
* <VoiceAgentProvider
|
|
34
|
+
* token={sessionToken}
|
|
35
|
+
* onConnect={() => console.log('Connected!')}
|
|
36
|
+
* onError={(err) => console.error('Error:', err)}
|
|
37
|
+
* >
|
|
38
|
+
* <VoiceChat />
|
|
39
|
+
* </VoiceAgentProvider>
|
|
40
|
+
* );
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare function VoiceAgentProvider({ children, token, baseUrl, reconnection, onConnect, onDisconnect, onError, }: VoiceAgentProviderProps): react_jsx_runtime.JSX.Element;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Context value containing VoiceAgent instance and token.
|
|
48
|
+
* Per user decision: Context only holds agent instance, not UI state.
|
|
49
|
+
* Token included for session ID extraction (transcript persistence).
|
|
50
|
+
*/
|
|
51
|
+
interface VoiceAgentContextValue {
|
|
52
|
+
agent: VoiceAgent | null;
|
|
53
|
+
token: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* React context for VoiceAgent instance.
|
|
57
|
+
* Provides stable reference to agent across component tree.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const context = useContext(VoiceAgentContext);
|
|
62
|
+
* if (!context?.agent) throw new Error('No agent available');
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
declare const VoiceAgentContext: react.Context<VoiceAgentContextValue | null>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Retry state exposed for UI feedback.
|
|
69
|
+
* Per user decision: UI can display attempt count and countdown timer.
|
|
70
|
+
*/
|
|
71
|
+
interface RetryState {
|
|
72
|
+
/** Whether a retry is currently in progress */
|
|
73
|
+
isRetrying: boolean;
|
|
74
|
+
/** Current retry attempt number (0-based) */
|
|
75
|
+
attempt: number;
|
|
76
|
+
/** Maximum retry attempts configured */
|
|
77
|
+
maxAttempts: number;
|
|
78
|
+
/** Milliseconds until next retry attempt */
|
|
79
|
+
nextRetryIn: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Hook providing automatic retry logic with exponential backoff.
|
|
83
|
+
*
|
|
84
|
+
* Per RESEARCH.md Pattern 4 and user decision:
|
|
85
|
+
* - Exponential backoff: delay = min(1000 * 2^(attempt-1), 10000)
|
|
86
|
+
* - Countdown timer updates every 100ms for UI feedback
|
|
87
|
+
* - Reset retry state on success
|
|
88
|
+
* - Keep attempt count on failure for next retry
|
|
89
|
+
*
|
|
90
|
+
* @param connectFn - Async function to retry (typically agent.connect)
|
|
91
|
+
* @param maxAttempts - Maximum retry attempts (default: 5)
|
|
92
|
+
* @returns Retry state and retry function
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const { retryState, retry } = useConnectionRetry(
|
|
97
|
+
* async () => agent.connect(),
|
|
98
|
+
* 5
|
|
99
|
+
* );
|
|
100
|
+
*
|
|
101
|
+
* // Show retry UI
|
|
102
|
+
* if (retryState.isRetrying) {
|
|
103
|
+
* return <div>Retrying... ({retryState.attempt}/{retryState.maxAttempts})
|
|
104
|
+
* Next attempt in {Math.ceil(retryState.nextRetryIn / 1000)}s</div>
|
|
105
|
+
* }
|
|
106
|
+
*
|
|
107
|
+
* // Manual retry
|
|
108
|
+
* <button onClick={retry}>Retry Connection</button>
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function useConnectionRetry(connectFn: () => Promise<void>, maxAttempts?: number): {
|
|
112
|
+
retryState: RetryState;
|
|
113
|
+
retry: () => Promise<void>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Return type for useVoiceAgent hook.
|
|
118
|
+
* Per user decision: Flat return structure with all properties at top level.
|
|
119
|
+
*/
|
|
120
|
+
interface UseVoiceAgentReturn {
|
|
121
|
+
/** Raw VoiceAgent instance for advanced use cases */
|
|
122
|
+
agent: VoiceAgent;
|
|
123
|
+
/** Current connection state */
|
|
124
|
+
callState: ConnectionState;
|
|
125
|
+
/** All transcripts accumulated during session */
|
|
126
|
+
transcripts: TranscriptData[];
|
|
127
|
+
/** Current error, if any (auto-cleared on success) */
|
|
128
|
+
error: Error | null;
|
|
129
|
+
/** Derived: true if callState === 'connected' */
|
|
130
|
+
isConnected: boolean;
|
|
131
|
+
/** Derived: true if callState === 'connecting' */
|
|
132
|
+
isConnecting: boolean;
|
|
133
|
+
/** Derived: true if callState === 'reconnecting' */
|
|
134
|
+
isReconnecting: boolean;
|
|
135
|
+
/** Retry state for UI feedback (attempt count, countdown) */
|
|
136
|
+
retryState: RetryState;
|
|
137
|
+
/** Connect to voice session */
|
|
138
|
+
connect: () => Promise<void>;
|
|
139
|
+
/** Disconnect from voice session */
|
|
140
|
+
disconnect: () => Promise<void>;
|
|
141
|
+
/** Mute microphone */
|
|
142
|
+
mute: () => void;
|
|
143
|
+
/** Unmute microphone */
|
|
144
|
+
unmute: () => void;
|
|
145
|
+
/** Manual retry connection (with exponential backoff) */
|
|
146
|
+
retryConnect: () => Promise<void>;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Comprehensive hook for VoiceAgent functionality.
|
|
150
|
+
*
|
|
151
|
+
* Per user decision:
|
|
152
|
+
* - Single comprehensive hook returning everything (not multiple focused hooks)
|
|
153
|
+
* - Flat return structure with all properties at top level
|
|
154
|
+
* - Includes raw agent instance for advanced use cases
|
|
155
|
+
* - Unlimited transcript accumulation in memory during session
|
|
156
|
+
* - Auto-clear errors when next action succeeds
|
|
157
|
+
*
|
|
158
|
+
* Per RESEARCH.md patterns:
|
|
159
|
+
* - Uses useSyncExternalStore for connection state (concurrent-safe)
|
|
160
|
+
* - Uses useState for transcripts and errors
|
|
161
|
+
* - Proper cleanup in useEffect to prevent memory leaks
|
|
162
|
+
* - useCallback for wrapped methods
|
|
163
|
+
*
|
|
164
|
+
* @throws {Error} When used outside VoiceAgentProvider
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```tsx
|
|
168
|
+
* function VoiceChat() {
|
|
169
|
+
* const {
|
|
170
|
+
* connect,
|
|
171
|
+
* disconnect,
|
|
172
|
+
* mute,
|
|
173
|
+
* unmute,
|
|
174
|
+
* callState,
|
|
175
|
+
* transcripts,
|
|
176
|
+
* error,
|
|
177
|
+
* isConnected,
|
|
178
|
+
* } = useVoiceAgent();
|
|
179
|
+
*
|
|
180
|
+
* return (
|
|
181
|
+
* <div>
|
|
182
|
+
* <button onClick={connect} disabled={isConnected}>
|
|
183
|
+
* {isConnected ? 'Connected' : 'Connect'}
|
|
184
|
+
* </button>
|
|
185
|
+
* {error && <div>Error: {error.message}</div>}
|
|
186
|
+
* <div>
|
|
187
|
+
* {transcripts.map((t, i) => (
|
|
188
|
+
* <p key={i}>{t.speaker}: {t.text}</p>
|
|
189
|
+
* ))}
|
|
190
|
+
* </div>
|
|
191
|
+
* </div>
|
|
192
|
+
* );
|
|
193
|
+
* }
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
declare function useVoiceAgent(): UseVoiceAgentReturn;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Hook to persist transcripts to sessionStorage.
|
|
200
|
+
*
|
|
201
|
+
* Per RESEARCH.md Pattern 3 and user decision:
|
|
202
|
+
* - Load persisted transcripts on mount
|
|
203
|
+
* - Save transcripts to sessionStorage on change
|
|
204
|
+
* - Clear on unmount (sessionStorage auto-clears on tab close)
|
|
205
|
+
* - Handle errors silently (log to console, don't crash)
|
|
206
|
+
* - No limits on transcript array size (browser quota is the limit)
|
|
207
|
+
*
|
|
208
|
+
* Per RESEARCH.md Pitfall 4:
|
|
209
|
+
* - Uses SSR-safe storage utilities
|
|
210
|
+
* - Won't crash in Next.js/Remix server-side rendering
|
|
211
|
+
*
|
|
212
|
+
* @param sessionId - Session ID to namespace storage key
|
|
213
|
+
* @param transcripts - Current transcripts array
|
|
214
|
+
* @param setTranscripts - State setter to restore transcripts
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* const [transcripts, setTranscripts] = useState<TranscriptData[]>([]);
|
|
219
|
+
* useTranscriptPersistence(sessionId, transcripts, setTranscripts);
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
declare function useTranscriptPersistence(sessionId: string, transcripts: TranscriptData[], setTranscripts: Dispatch<SetStateAction<TranscriptData[]>>): void;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* SSR-safe sessionStorage utilities.
|
|
226
|
+
*
|
|
227
|
+
* Per RESEARCH.md Pitfall 4:
|
|
228
|
+
* - Guard with typeof window !== 'undefined' check
|
|
229
|
+
* - Wrap in try-catch (storage can be disabled or full)
|
|
230
|
+
* - Return null/false on error or SSR
|
|
231
|
+
*
|
|
232
|
+
* @packageDocumentation
|
|
233
|
+
*/
|
|
234
|
+
/**
|
|
235
|
+
* Get item from sessionStorage in SSR-safe manner.
|
|
236
|
+
* Returns null if running in SSR, storage disabled, or key doesn't exist.
|
|
237
|
+
*
|
|
238
|
+
* @param key - Storage key
|
|
239
|
+
* @returns Value or null
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const transcripts = getSessionStorage('voice-agent-transcripts');
|
|
244
|
+
* if (transcripts) {
|
|
245
|
+
* // Use transcripts
|
|
246
|
+
* }
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
declare function getSessionStorage(key: string): string | null;
|
|
250
|
+
/**
|
|
251
|
+
* Set item in sessionStorage in SSR-safe manner.
|
|
252
|
+
* Returns true on success, false on failure (SSR, quota exceeded, disabled).
|
|
253
|
+
*
|
|
254
|
+
* @param key - Storage key
|
|
255
|
+
* @param value - Value to store
|
|
256
|
+
* @returns Success flag
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* const success = setSessionStorage('voice-agent-transcripts', JSON.stringify(transcripts));
|
|
261
|
+
* if (!success) {
|
|
262
|
+
* console.warn('Failed to persist transcripts');
|
|
263
|
+
* }
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare function setSessionStorage(key: string, value: string): boolean;
|
|
267
|
+
/**
|
|
268
|
+
* Remove item from sessionStorage in SSR-safe manner.
|
|
269
|
+
* Returns true on success, false on failure (SSR, disabled).
|
|
270
|
+
*
|
|
271
|
+
* @param key - Storage key
|
|
272
|
+
* @returns Success flag
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```typescript
|
|
276
|
+
* removeSessionStorage('voice-agent-transcripts-session-123');
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
declare function removeSessionStorage(key: string): boolean;
|
|
280
|
+
/**
|
|
281
|
+
* Get and parse JSON from sessionStorage.
|
|
282
|
+
* Returns null if parsing fails, SSR, or key doesn't exist.
|
|
283
|
+
*
|
|
284
|
+
* @param key - Storage key
|
|
285
|
+
* @returns Parsed value or null
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* const transcripts = getSessionStorageJSON<TranscriptData[]>('voice-agent-transcripts');
|
|
290
|
+
* if (transcripts) {
|
|
291
|
+
* // Use typed transcripts array
|
|
292
|
+
* }
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
declare function getSessionStorageJSON<T>(key: string): T | null;
|
|
296
|
+
/**
|
|
297
|
+
* Stringify and store JSON in sessionStorage.
|
|
298
|
+
* Returns true on success, false on failure (SSR, quota, parse error).
|
|
299
|
+
*
|
|
300
|
+
* @param key - Storage key
|
|
301
|
+
* @param value - Value to stringify and store
|
|
302
|
+
* @returns Success flag
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```typescript
|
|
306
|
+
* const success = setSessionStorageJSON('voice-agent-transcripts', transcripts);
|
|
307
|
+
* if (!success) {
|
|
308
|
+
* console.warn('Failed to persist transcripts');
|
|
309
|
+
* }
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
declare function setSessionStorageJSON<T>(key: string, value: T): boolean;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Simple JWT token decoder for extracting session ID.
|
|
316
|
+
* Per plan: Client-side JWT decoding (backend already validated).
|
|
317
|
+
*/
|
|
318
|
+
/**
|
|
319
|
+
* Extract session ID from JWT session token.
|
|
320
|
+
* Uses atob() for browser compatibility.
|
|
321
|
+
*
|
|
322
|
+
* @param token - JWT session token
|
|
323
|
+
* @returns Session ID string
|
|
324
|
+
* @throws {Error} If token is malformed
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```typescript
|
|
328
|
+
* const sessionId = extractSessionId(sessionToken);
|
|
329
|
+
* // Use sessionId for storage key
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
declare function extractSessionId(token: string): string;
|
|
333
|
+
|
|
334
|
+
export { type RetryState, type UseVoiceAgentReturn, VoiceAgentContext, type VoiceAgentContextValue, VoiceAgentProvider, type VoiceAgentProviderProps, extractSessionId, getSessionStorage, getSessionStorageJSON, removeSessionStorage, setSessionStorage, setSessionStorageJSON, useConnectionRetry, useTranscriptPersistence, useVoiceAgent };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode, Dispatch, SetStateAction } from 'react';
|
|
4
|
+
import { VoiceAgentConfig, VoiceAgent, ConnectionState, TranscriptData } from '@voxdiscover/voiceserver';
|
|
5
|
+
export * from '@voxdiscover/voiceserver';
|
|
6
|
+
export { ConnectionState, ReconnectionManager, TranscriptData, VoiceAgent, VoiceAgentConfig, VoiceAgentErrorCode, VoiceAgentEvents } from '@voxdiscover/voiceserver';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Props for VoiceAgentProvider component.
|
|
10
|
+
* Per user decision: Config object pattern for flexibility.
|
|
11
|
+
*/
|
|
12
|
+
interface VoiceAgentProviderProps extends VoiceAgentConfig {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
/** Callback when connection is established */
|
|
15
|
+
onConnect?: () => void;
|
|
16
|
+
/** Callback when connection is closed */
|
|
17
|
+
onDisconnect?: () => void;
|
|
18
|
+
/** Callback for connection errors */
|
|
19
|
+
onError?: (error: Error) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Context provider for VoiceAgent instance.
|
|
23
|
+
*
|
|
24
|
+
* Per user decisions:
|
|
25
|
+
* - Creates VoiceAgent instance but waits for explicit connect() call (manual connect pattern)
|
|
26
|
+
* - Auto-disconnects and cleans up WebRTC resources on unmount
|
|
27
|
+
* - Provides stable agent reference via useRef (prevents re-render cascades)
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* function App() {
|
|
32
|
+
* return (
|
|
33
|
+
* <VoiceAgentProvider
|
|
34
|
+
* token={sessionToken}
|
|
35
|
+
* onConnect={() => console.log('Connected!')}
|
|
36
|
+
* onError={(err) => console.error('Error:', err)}
|
|
37
|
+
* >
|
|
38
|
+
* <VoiceChat />
|
|
39
|
+
* </VoiceAgentProvider>
|
|
40
|
+
* );
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare function VoiceAgentProvider({ children, token, baseUrl, reconnection, onConnect, onDisconnect, onError, }: VoiceAgentProviderProps): react_jsx_runtime.JSX.Element;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Context value containing VoiceAgent instance and token.
|
|
48
|
+
* Per user decision: Context only holds agent instance, not UI state.
|
|
49
|
+
* Token included for session ID extraction (transcript persistence).
|
|
50
|
+
*/
|
|
51
|
+
interface VoiceAgentContextValue {
|
|
52
|
+
agent: VoiceAgent | null;
|
|
53
|
+
token: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* React context for VoiceAgent instance.
|
|
57
|
+
* Provides stable reference to agent across component tree.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const context = useContext(VoiceAgentContext);
|
|
62
|
+
* if (!context?.agent) throw new Error('No agent available');
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
declare const VoiceAgentContext: react.Context<VoiceAgentContextValue | null>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Retry state exposed for UI feedback.
|
|
69
|
+
* Per user decision: UI can display attempt count and countdown timer.
|
|
70
|
+
*/
|
|
71
|
+
interface RetryState {
|
|
72
|
+
/** Whether a retry is currently in progress */
|
|
73
|
+
isRetrying: boolean;
|
|
74
|
+
/** Current retry attempt number (0-based) */
|
|
75
|
+
attempt: number;
|
|
76
|
+
/** Maximum retry attempts configured */
|
|
77
|
+
maxAttempts: number;
|
|
78
|
+
/** Milliseconds until next retry attempt */
|
|
79
|
+
nextRetryIn: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Hook providing automatic retry logic with exponential backoff.
|
|
83
|
+
*
|
|
84
|
+
* Per RESEARCH.md Pattern 4 and user decision:
|
|
85
|
+
* - Exponential backoff: delay = min(1000 * 2^(attempt-1), 10000)
|
|
86
|
+
* - Countdown timer updates every 100ms for UI feedback
|
|
87
|
+
* - Reset retry state on success
|
|
88
|
+
* - Keep attempt count on failure for next retry
|
|
89
|
+
*
|
|
90
|
+
* @param connectFn - Async function to retry (typically agent.connect)
|
|
91
|
+
* @param maxAttempts - Maximum retry attempts (default: 5)
|
|
92
|
+
* @returns Retry state and retry function
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const { retryState, retry } = useConnectionRetry(
|
|
97
|
+
* async () => agent.connect(),
|
|
98
|
+
* 5
|
|
99
|
+
* );
|
|
100
|
+
*
|
|
101
|
+
* // Show retry UI
|
|
102
|
+
* if (retryState.isRetrying) {
|
|
103
|
+
* return <div>Retrying... ({retryState.attempt}/{retryState.maxAttempts})
|
|
104
|
+
* Next attempt in {Math.ceil(retryState.nextRetryIn / 1000)}s</div>
|
|
105
|
+
* }
|
|
106
|
+
*
|
|
107
|
+
* // Manual retry
|
|
108
|
+
* <button onClick={retry}>Retry Connection</button>
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function useConnectionRetry(connectFn: () => Promise<void>, maxAttempts?: number): {
|
|
112
|
+
retryState: RetryState;
|
|
113
|
+
retry: () => Promise<void>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Return type for useVoiceAgent hook.
|
|
118
|
+
* Per user decision: Flat return structure with all properties at top level.
|
|
119
|
+
*/
|
|
120
|
+
interface UseVoiceAgentReturn {
|
|
121
|
+
/** Raw VoiceAgent instance for advanced use cases */
|
|
122
|
+
agent: VoiceAgent;
|
|
123
|
+
/** Current connection state */
|
|
124
|
+
callState: ConnectionState;
|
|
125
|
+
/** All transcripts accumulated during session */
|
|
126
|
+
transcripts: TranscriptData[];
|
|
127
|
+
/** Current error, if any (auto-cleared on success) */
|
|
128
|
+
error: Error | null;
|
|
129
|
+
/** Derived: true if callState === 'connected' */
|
|
130
|
+
isConnected: boolean;
|
|
131
|
+
/** Derived: true if callState === 'connecting' */
|
|
132
|
+
isConnecting: boolean;
|
|
133
|
+
/** Derived: true if callState === 'reconnecting' */
|
|
134
|
+
isReconnecting: boolean;
|
|
135
|
+
/** Retry state for UI feedback (attempt count, countdown) */
|
|
136
|
+
retryState: RetryState;
|
|
137
|
+
/** Connect to voice session */
|
|
138
|
+
connect: () => Promise<void>;
|
|
139
|
+
/** Disconnect from voice session */
|
|
140
|
+
disconnect: () => Promise<void>;
|
|
141
|
+
/** Mute microphone */
|
|
142
|
+
mute: () => void;
|
|
143
|
+
/** Unmute microphone */
|
|
144
|
+
unmute: () => void;
|
|
145
|
+
/** Manual retry connection (with exponential backoff) */
|
|
146
|
+
retryConnect: () => Promise<void>;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Comprehensive hook for VoiceAgent functionality.
|
|
150
|
+
*
|
|
151
|
+
* Per user decision:
|
|
152
|
+
* - Single comprehensive hook returning everything (not multiple focused hooks)
|
|
153
|
+
* - Flat return structure with all properties at top level
|
|
154
|
+
* - Includes raw agent instance for advanced use cases
|
|
155
|
+
* - Unlimited transcript accumulation in memory during session
|
|
156
|
+
* - Auto-clear errors when next action succeeds
|
|
157
|
+
*
|
|
158
|
+
* Per RESEARCH.md patterns:
|
|
159
|
+
* - Uses useSyncExternalStore for connection state (concurrent-safe)
|
|
160
|
+
* - Uses useState for transcripts and errors
|
|
161
|
+
* - Proper cleanup in useEffect to prevent memory leaks
|
|
162
|
+
* - useCallback for wrapped methods
|
|
163
|
+
*
|
|
164
|
+
* @throws {Error} When used outside VoiceAgentProvider
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```tsx
|
|
168
|
+
* function VoiceChat() {
|
|
169
|
+
* const {
|
|
170
|
+
* connect,
|
|
171
|
+
* disconnect,
|
|
172
|
+
* mute,
|
|
173
|
+
* unmute,
|
|
174
|
+
* callState,
|
|
175
|
+
* transcripts,
|
|
176
|
+
* error,
|
|
177
|
+
* isConnected,
|
|
178
|
+
* } = useVoiceAgent();
|
|
179
|
+
*
|
|
180
|
+
* return (
|
|
181
|
+
* <div>
|
|
182
|
+
* <button onClick={connect} disabled={isConnected}>
|
|
183
|
+
* {isConnected ? 'Connected' : 'Connect'}
|
|
184
|
+
* </button>
|
|
185
|
+
* {error && <div>Error: {error.message}</div>}
|
|
186
|
+
* <div>
|
|
187
|
+
* {transcripts.map((t, i) => (
|
|
188
|
+
* <p key={i}>{t.speaker}: {t.text}</p>
|
|
189
|
+
* ))}
|
|
190
|
+
* </div>
|
|
191
|
+
* </div>
|
|
192
|
+
* );
|
|
193
|
+
* }
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
declare function useVoiceAgent(): UseVoiceAgentReturn;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Hook to persist transcripts to sessionStorage.
|
|
200
|
+
*
|
|
201
|
+
* Per RESEARCH.md Pattern 3 and user decision:
|
|
202
|
+
* - Load persisted transcripts on mount
|
|
203
|
+
* - Save transcripts to sessionStorage on change
|
|
204
|
+
* - Clear on unmount (sessionStorage auto-clears on tab close)
|
|
205
|
+
* - Handle errors silently (log to console, don't crash)
|
|
206
|
+
* - No limits on transcript array size (browser quota is the limit)
|
|
207
|
+
*
|
|
208
|
+
* Per RESEARCH.md Pitfall 4:
|
|
209
|
+
* - Uses SSR-safe storage utilities
|
|
210
|
+
* - Won't crash in Next.js/Remix server-side rendering
|
|
211
|
+
*
|
|
212
|
+
* @param sessionId - Session ID to namespace storage key
|
|
213
|
+
* @param transcripts - Current transcripts array
|
|
214
|
+
* @param setTranscripts - State setter to restore transcripts
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* const [transcripts, setTranscripts] = useState<TranscriptData[]>([]);
|
|
219
|
+
* useTranscriptPersistence(sessionId, transcripts, setTranscripts);
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
declare function useTranscriptPersistence(sessionId: string, transcripts: TranscriptData[], setTranscripts: Dispatch<SetStateAction<TranscriptData[]>>): void;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* SSR-safe sessionStorage utilities.
|
|
226
|
+
*
|
|
227
|
+
* Per RESEARCH.md Pitfall 4:
|
|
228
|
+
* - Guard with typeof window !== 'undefined' check
|
|
229
|
+
* - Wrap in try-catch (storage can be disabled or full)
|
|
230
|
+
* - Return null/false on error or SSR
|
|
231
|
+
*
|
|
232
|
+
* @packageDocumentation
|
|
233
|
+
*/
|
|
234
|
+
/**
|
|
235
|
+
* Get item from sessionStorage in SSR-safe manner.
|
|
236
|
+
* Returns null if running in SSR, storage disabled, or key doesn't exist.
|
|
237
|
+
*
|
|
238
|
+
* @param key - Storage key
|
|
239
|
+
* @returns Value or null
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const transcripts = getSessionStorage('voice-agent-transcripts');
|
|
244
|
+
* if (transcripts) {
|
|
245
|
+
* // Use transcripts
|
|
246
|
+
* }
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
declare function getSessionStorage(key: string): string | null;
|
|
250
|
+
/**
|
|
251
|
+
* Set item in sessionStorage in SSR-safe manner.
|
|
252
|
+
* Returns true on success, false on failure (SSR, quota exceeded, disabled).
|
|
253
|
+
*
|
|
254
|
+
* @param key - Storage key
|
|
255
|
+
* @param value - Value to store
|
|
256
|
+
* @returns Success flag
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* const success = setSessionStorage('voice-agent-transcripts', JSON.stringify(transcripts));
|
|
261
|
+
* if (!success) {
|
|
262
|
+
* console.warn('Failed to persist transcripts');
|
|
263
|
+
* }
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare function setSessionStorage(key: string, value: string): boolean;
|
|
267
|
+
/**
|
|
268
|
+
* Remove item from sessionStorage in SSR-safe manner.
|
|
269
|
+
* Returns true on success, false on failure (SSR, disabled).
|
|
270
|
+
*
|
|
271
|
+
* @param key - Storage key
|
|
272
|
+
* @returns Success flag
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```typescript
|
|
276
|
+
* removeSessionStorage('voice-agent-transcripts-session-123');
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
declare function removeSessionStorage(key: string): boolean;
|
|
280
|
+
/**
|
|
281
|
+
* Get and parse JSON from sessionStorage.
|
|
282
|
+
* Returns null if parsing fails, SSR, or key doesn't exist.
|
|
283
|
+
*
|
|
284
|
+
* @param key - Storage key
|
|
285
|
+
* @returns Parsed value or null
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* const transcripts = getSessionStorageJSON<TranscriptData[]>('voice-agent-transcripts');
|
|
290
|
+
* if (transcripts) {
|
|
291
|
+
* // Use typed transcripts array
|
|
292
|
+
* }
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
declare function getSessionStorageJSON<T>(key: string): T | null;
|
|
296
|
+
/**
|
|
297
|
+
* Stringify and store JSON in sessionStorage.
|
|
298
|
+
* Returns true on success, false on failure (SSR, quota, parse error).
|
|
299
|
+
*
|
|
300
|
+
* @param key - Storage key
|
|
301
|
+
* @param value - Value to stringify and store
|
|
302
|
+
* @returns Success flag
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```typescript
|
|
306
|
+
* const success = setSessionStorageJSON('voice-agent-transcripts', transcripts);
|
|
307
|
+
* if (!success) {
|
|
308
|
+
* console.warn('Failed to persist transcripts');
|
|
309
|
+
* }
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
declare function setSessionStorageJSON<T>(key: string, value: T): boolean;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Simple JWT token decoder for extracting session ID.
|
|
316
|
+
* Per plan: Client-side JWT decoding (backend already validated).
|
|
317
|
+
*/
|
|
318
|
+
/**
|
|
319
|
+
* Extract session ID from JWT session token.
|
|
320
|
+
* Uses atob() for browser compatibility.
|
|
321
|
+
*
|
|
322
|
+
* @param token - JWT session token
|
|
323
|
+
* @returns Session ID string
|
|
324
|
+
* @throws {Error} If token is malformed
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```typescript
|
|
328
|
+
* const sessionId = extractSessionId(sessionToken);
|
|
329
|
+
* // Use sessionId for storage key
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
declare function extractSessionId(token: string): string;
|
|
333
|
+
|
|
334
|
+
export { type RetryState, type UseVoiceAgentReturn, VoiceAgentContext, type VoiceAgentContextValue, VoiceAgentProvider, type VoiceAgentProviderProps, extractSessionId, getSessionStorage, getSessionStorageJSON, removeSessionStorage, setSessionStorage, setSessionStorageJSON, useConnectionRetry, useTranscriptPersistence, useVoiceAgent };
|