@qafka/react-native 2.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.
Files changed (178) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/CONTRIBUTING.md +92 -0
  3. package/LICENSE +22 -0
  4. package/README.md +109 -0
  5. package/SECURITY.md +67 -0
  6. package/android/build.gradle +35 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/java/com/qafka/attestation/QafkaAttestationModule.kt +92 -0
  9. package/android/src/main/java/com/qafka/attestation/QafkaAttestationPackage.kt +22 -0
  10. package/android/src/main/java/com/qafka/audio/QafkaAudioModule.kt +290 -0
  11. package/android/src/main/java/com/qafka/clipboard/QafkaClipboardModule.kt +28 -0
  12. package/android/src/main/java/com/qafka/storage/QafkaStorageModule.kt +80 -0
  13. package/app.plugin.js +1 -0
  14. package/dist/QafkaSDK.d.ts +174 -0
  15. package/dist/QafkaSDK.js +461 -0
  16. package/dist/cards/bindings/resolveFieldName.d.ts +25 -0
  17. package/dist/cards/bindings/resolveFieldName.js +82 -0
  18. package/dist/cards/cta/CardContext.d.ts +16 -0
  19. package/dist/cards/cta/CardContext.js +58 -0
  20. package/dist/cards/cta/dispatcher.d.ts +7 -0
  21. package/dist/cards/cta/dispatcher.js +90 -0
  22. package/dist/cards/cta/types.d.ts +66 -0
  23. package/dist/cards/cta/types.js +2 -0
  24. package/dist/cards/index.d.ts +20 -0
  25. package/dist/cards/index.js +34 -0
  26. package/dist/cards/primitives/QButton.d.ts +10 -0
  27. package/dist/cards/primitives/QButton.js +115 -0
  28. package/dist/cards/primitives/QDivider.d.ts +7 -0
  29. package/dist/cards/primitives/QDivider.js +17 -0
  30. package/dist/cards/primitives/QIcon.d.ts +13 -0
  31. package/dist/cards/primitives/QIcon.js +26 -0
  32. package/dist/cards/primitives/QImage.d.ts +9 -0
  33. package/dist/cards/primitives/QImage.js +22 -0
  34. package/dist/cards/primitives/QText.d.ts +9 -0
  35. package/dist/cards/primitives/QText.js +30 -0
  36. package/dist/cards/primitives/QView.d.ts +8 -0
  37. package/dist/cards/primitives/QView.js +19 -0
  38. package/dist/cards/renderer/CardRenderer.d.ts +19 -0
  39. package/dist/cards/renderer/CardRenderer.js +64 -0
  40. package/dist/cards/renderer/renderNode.d.ts +13 -0
  41. package/dist/cards/renderer/renderNode.js +42 -0
  42. package/dist/cards/types.d.ts +110 -0
  43. package/dist/cards/types.js +6 -0
  44. package/dist/components/ActionResultBadge.d.ts +12 -0
  45. package/dist/components/ActionResultBadge.js +58 -0
  46. package/dist/components/ChatPage.d.ts +44 -0
  47. package/dist/components/ChatPage.js +84 -0
  48. package/dist/components/DataChip.d.ts +8 -0
  49. package/dist/components/DataChip.js +80 -0
  50. package/dist/components/DataChipList.d.ts +13 -0
  51. package/dist/components/DataChipList.js +21 -0
  52. package/dist/components/FloatingButton.d.ts +11 -0
  53. package/dist/components/FloatingButton.js +162 -0
  54. package/dist/components/InputArea.d.ts +57 -0
  55. package/dist/components/InputArea.js +142 -0
  56. package/dist/components/MarkdownText.d.ts +15 -0
  57. package/dist/components/MarkdownText.js +283 -0
  58. package/dist/components/MessageBubble.d.ts +134 -0
  59. package/dist/components/MessageBubble.js +384 -0
  60. package/dist/components/NavigationSuggestion.d.ts +11 -0
  61. package/dist/components/NavigationSuggestion.js +109 -0
  62. package/dist/components/Qafka.d.ts +39 -0
  63. package/dist/components/Qafka.handlers.d.ts +21 -0
  64. package/dist/components/Qafka.handlers.js +54 -0
  65. package/dist/components/Qafka.js +493 -0
  66. package/dist/components/Qafka.styles.d.ts +19 -0
  67. package/dist/components/Qafka.styles.js +101 -0
  68. package/dist/components/Qafka.types.d.ts +744 -0
  69. package/dist/components/Qafka.types.js +2 -0
  70. package/dist/components/Qafka.utils.d.ts +7 -0
  71. package/dist/components/Qafka.utils.js +34 -0
  72. package/dist/components/QafkaProvider.d.ts +12 -0
  73. package/dist/components/QafkaProvider.js +87 -0
  74. package/dist/components/QuickReplies.d.ts +14 -0
  75. package/dist/components/QuickReplies.js +48 -0
  76. package/dist/components/StepProgressIndicator.d.ts +12 -0
  77. package/dist/components/StepProgressIndicator.js +48 -0
  78. package/dist/components/SuggestionButton.d.ts +42 -0
  79. package/dist/components/SuggestionButton.js +67 -0
  80. package/dist/components/ToolStatusPill.d.ts +20 -0
  81. package/dist/components/ToolStatusPill.js +43 -0
  82. package/dist/components/TypingIndicator.d.ts +28 -0
  83. package/dist/components/TypingIndicator.js +109 -0
  84. package/dist/components/VoicePage.d.ts +48 -0
  85. package/dist/components/VoicePage.js +683 -0
  86. package/dist/components/defaults/DefaultCard.d.ts +14 -0
  87. package/dist/components/defaults/DefaultCard.js +156 -0
  88. package/dist/components/defaults/DefaultDetail.d.ts +14 -0
  89. package/dist/components/defaults/DefaultDetail.js +138 -0
  90. package/dist/components/defaults/DefaultList.d.ts +12 -0
  91. package/dist/components/defaults/DefaultList.js +98 -0
  92. package/dist/components/defaults/DefaultTable.d.ts +14 -0
  93. package/dist/components/defaults/DefaultTable.js +204 -0
  94. package/dist/components/defaults/index.d.ts +14 -0
  95. package/dist/components/defaults/index.js +25 -0
  96. package/dist/components/index.d.ts +22 -0
  97. package/dist/components/index.js +36 -0
  98. package/dist/constants.d.ts +10 -0
  99. package/dist/constants.js +13 -0
  100. package/dist/hooks/useChatMessages.d.ts +72 -0
  101. package/dist/hooks/useChatMessages.js +505 -0
  102. package/dist/hooks/useContextManager.d.ts +12 -0
  103. package/dist/hooks/useContextManager.js +46 -0
  104. package/dist/hooks/useProjectTheme.d.ts +19 -0
  105. package/dist/hooks/useProjectTheme.js +163 -0
  106. package/dist/hooks/useSDK.d.ts +31 -0
  107. package/dist/hooks/useSDK.js +103 -0
  108. package/dist/hooks/useVoiceChat.d.ts +110 -0
  109. package/dist/hooks/useVoiceChat.js +436 -0
  110. package/dist/index.d.ts +13 -0
  111. package/dist/index.js +59 -0
  112. package/dist/native/QafkaAttestation.d.ts +23 -0
  113. package/dist/native/QafkaAttestation.js +70 -0
  114. package/dist/native/QafkaAudio.d.ts +14 -0
  115. package/dist/native/QafkaAudio.js +31 -0
  116. package/dist/native/QafkaClipboard.d.ts +11 -0
  117. package/dist/native/QafkaClipboard.js +14 -0
  118. package/dist/native/QafkaStorage.d.ts +15 -0
  119. package/dist/native/QafkaStorage.js +12 -0
  120. package/dist/resolve-project-config.d.ts +35 -0
  121. package/dist/resolve-project-config.js +41 -0
  122. package/dist/runtime-config-loader.d.ts +37 -0
  123. package/dist/runtime-config-loader.js +53 -0
  124. package/dist/services/AttestationManager.d.ts +38 -0
  125. package/dist/services/AttestationManager.js +296 -0
  126. package/dist/services/BackendService.d.ts +156 -0
  127. package/dist/services/BackendService.js +755 -0
  128. package/dist/services/ConversationManager.d.ts +43 -0
  129. package/dist/services/ConversationManager.js +96 -0
  130. package/dist/services/NavigationHandler.d.ts +29 -0
  131. package/dist/services/NavigationHandler.js +70 -0
  132. package/dist/services/RealtimeService.d.ts +83 -0
  133. package/dist/services/RealtimeService.js +203 -0
  134. package/dist/services/storage.d.ts +11 -0
  135. package/dist/services/storage.js +15 -0
  136. package/dist/services/storageCore.d.ts +17 -0
  137. package/dist/services/storageCore.js +46 -0
  138. package/dist/themes/dark.d.ts +5 -0
  139. package/dist/themes/dark.js +129 -0
  140. package/dist/themes/index.d.ts +12 -0
  141. package/dist/themes/index.js +33 -0
  142. package/dist/themes/light.d.ts +5 -0
  143. package/dist/themes/light.js +129 -0
  144. package/dist/themes/types.d.ts +155 -0
  145. package/dist/themes/types.js +5 -0
  146. package/dist/types/chat.d.ts +126 -0
  147. package/dist/types/chat.js +5 -0
  148. package/dist/types/components.d.ts +56 -0
  149. package/dist/types/components.js +16 -0
  150. package/dist/types/external-navigation.d.ts +19 -0
  151. package/dist/types/external-navigation.js +8 -0
  152. package/dist/types/index.d.ts +9 -0
  153. package/dist/types/index.js +25 -0
  154. package/dist/types/navigation.d.ts +86 -0
  155. package/dist/types/navigation.js +5 -0
  156. package/dist/types/sdk.d.ts +36 -0
  157. package/dist/types/sdk.js +5 -0
  158. package/dist/utils/deepMerge.d.ts +46 -0
  159. package/dist/utils/deepMerge.js +70 -0
  160. package/dist/utils/fontUtils.d.ts +8 -0
  161. package/dist/utils/fontUtils.js +16 -0
  162. package/dist/validate-end-user.d.ts +18 -0
  163. package/dist/validate-end-user.js +74 -0
  164. package/expo-plugin/withQafkaAttestation.js +57 -0
  165. package/ios/QafkaAttestation.m +25 -0
  166. package/ios/QafkaAttestation.swift +128 -0
  167. package/ios/QafkaAudio.m +23 -0
  168. package/ios/QafkaAudio.swift +519 -0
  169. package/ios/QafkaClipboard.m +10 -0
  170. package/ios/QafkaClipboard.swift +21 -0
  171. package/ios/QafkaReactImports.h +2 -0
  172. package/ios/QafkaStorage.m +26 -0
  173. package/ios/QafkaStorage.swift +118 -0
  174. package/package.json +82 -0
  175. package/qafka.config.d.ts +9 -0
  176. package/qafka.config.js +9 -0
  177. package/react-native-qafka.podspec +28 -0
  178. package/react-native.config.js +14 -0
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useProjectTheme = useProjectTheme;
4
+ const react_1 = require("react");
5
+ const themes_1 = require("../themes");
6
+ const deepMerge_1 = require("../utils/deepMerge");
7
+ const QafkaSDK_1 = require("../QafkaSDK");
8
+ const storage_1 = require("../services/storage");
9
+ const THEME_CACHE_PREFIX = '@qafka/theme:';
10
+ const getThemeCacheKey = (subProjectId) => `${THEME_CACHE_PREFIX}${subProjectId ?? 'main'}`;
11
+ const defined = (obj) => {
12
+ const result = {};
13
+ for (const [k, v] of Object.entries(obj)) {
14
+ if (v !== undefined && v !== null)
15
+ result[k] = v;
16
+ }
17
+ return result;
18
+ };
19
+ function mapRawThemeToOverride(rawTheme) {
20
+ return {
21
+ colors: defined({
22
+ background: rawTheme.containerBackground,
23
+ userBubble: rawTheme.userMessageBackground,
24
+ userBubbleText: rawTheme.userMessageText,
25
+ aiBubble: rawTheme.aiMessageBackground,
26
+ aiBubbleText: rawTheme.aiMessageText,
27
+ inputBackground: rawTheme.inputBackground,
28
+ text: rawTheme.inputText,
29
+ inputBorder: rawTheme.inputBorder,
30
+ buttonBackground: rawTheme.sendButtonBackground,
31
+ buttonText: rawTheme.sendButtonText,
32
+ }),
33
+ typography: defined({
34
+ fontSize: defined({ md: rawTheme.fontSize }),
35
+ fontWeight: defined({
36
+ regular: '400',
37
+ medium: '500',
38
+ semibold: '600',
39
+ bold: rawTheme.fontWeight,
40
+ }),
41
+ lineHeight: defined({ normal: rawTheme.lineHeight }),
42
+ }),
43
+ borderRadius: defined({
44
+ lg: rawTheme.userMessageBorderRadius ?? rawTheme.aiMessageBorderRadius,
45
+ xl: rawTheme.inputBorderRadius,
46
+ }),
47
+ spacing: defined({
48
+ md: rawTheme.containerPadding,
49
+ lg: rawTheme.userMessagePadding ?? rawTheme.aiMessagePadding,
50
+ }),
51
+ messages: {
52
+ user: {
53
+ padding: rawTheme.userMessagePadding ?? 12,
54
+ maxWidth: rawTheme.userMessageMaxWidth ?? 70,
55
+ },
56
+ ai: {
57
+ padding: rawTheme.aiMessagePadding ?? 12,
58
+ maxWidth: rawTheme.aiMessageMaxWidth ?? 70,
59
+ },
60
+ },
61
+ };
62
+ }
63
+ function useProjectTheme({ sdkReady, themeName, customTheme, themeOverride, subProjectId, }) {
64
+ const [apiThemeOverride, setApiThemeOverride] = (0, react_1.useState)(null);
65
+ const [greeting, setGreeting] = (0, react_1.useState)(null);
66
+ const [initialMessage, setInitialMessage] = (0, react_1.useState)(null);
67
+ const [isInitialMessageEnabled, setIsInitialMessageEnabled] = (0, react_1.useState)(false);
68
+ // Start as `true` to gate the initial render until we either hit cache
69
+ // or finish the API fetch. Otherwise the first paint shows ChatPage's
70
+ // English fallback greeting before the project-specific one arrives.
71
+ const [isLoadingTheme, setIsLoadingTheme] = (0, react_1.useState)(true);
72
+ const [voiceEnabled, setVoiceEnabled] = (0, react_1.useState)(false);
73
+ (0, react_1.useEffect)(() => {
74
+ let cancelled = false;
75
+ const applyThemeData = (themeData) => {
76
+ if (!themeData)
77
+ return;
78
+ const modeTheme = typeof themeName === 'string' ? themeName : 'light';
79
+ const rawTheme = themeData[modeTheme];
80
+ if (rawTheme) {
81
+ setApiThemeOverride(mapRawThemeToOverride(rawTheme));
82
+ }
83
+ if (themeData.greetingMessage)
84
+ setGreeting(themeData.greetingMessage);
85
+ if (themeData.initialMessage)
86
+ setInitialMessage(themeData.initialMessage);
87
+ if (themeData.isInitialMessageEnabled)
88
+ setIsInitialMessageEnabled(true);
89
+ if (themeData.features?.voiceEnabled)
90
+ setVoiceEnabled(true);
91
+ };
92
+ const loadTheme = async () => {
93
+ const cacheKey = getThemeCacheKey(subProjectId);
94
+ // Step 1 — stale-while-revalidate: hydrate from cache so a warm open
95
+ // paints the correct branded greeting immediately.
96
+ try {
97
+ const cached = await storage_1.storage.getItem(cacheKey);
98
+ if (cached && !cancelled) {
99
+ applyThemeData(JSON.parse(cached));
100
+ setIsLoadingTheme(false);
101
+ }
102
+ }
103
+ catch {
104
+ // Ignore cache read failures (corrupt JSON, native module missing, etc.)
105
+ }
106
+ // Step 2 — prefer the prefetch promise (started inside QafkaSDK.initialize
107
+ // parallel to attestation). Theme is gated by API key only, not session
108
+ // token, so it can resolve before sdkReady. Falls back to a direct call
109
+ // only when sdkReady — covers hosts that bypass `<Qafka>`-driven init.
110
+ const sdk = QafkaSDK_1.QafkaSDK.getInstance();
111
+ const prefetch = sdk.getThemePrefetch();
112
+ let themeData = null;
113
+ try {
114
+ if (prefetch) {
115
+ themeData = await prefetch;
116
+ }
117
+ else if (sdkReady) {
118
+ themeData = await sdk.getProjectTheme();
119
+ }
120
+ }
121
+ catch {
122
+ // Theme load failed — cache (if any) already applied above.
123
+ }
124
+ if (cancelled)
125
+ return;
126
+ if (themeData) {
127
+ applyThemeData(themeData);
128
+ try {
129
+ await storage_1.storage.setItem(cacheKey, JSON.stringify(themeData));
130
+ }
131
+ catch {
132
+ // Cache write failures are non-fatal.
133
+ }
134
+ }
135
+ if (!cancelled)
136
+ setIsLoadingTheme(false);
137
+ };
138
+ loadTheme();
139
+ return () => {
140
+ cancelled = true;
141
+ };
142
+ }, [sdkReady, themeName, subProjectId]);
143
+ const theme = (0, react_1.useMemo)(() => {
144
+ let baseTheme;
145
+ if (customTheme) {
146
+ baseTheme = customTheme;
147
+ }
148
+ else if (typeof themeName === 'string') {
149
+ baseTheme = (0, themes_1.getTheme)(themeName);
150
+ }
151
+ else {
152
+ baseTheme = themeName;
153
+ }
154
+ if (apiThemeOverride) {
155
+ baseTheme = (0, deepMerge_1.deepMerge)(baseTheme, apiThemeOverride);
156
+ }
157
+ if (themeOverride) {
158
+ baseTheme = (0, deepMerge_1.deepMerge)(baseTheme, themeOverride);
159
+ }
160
+ return baseTheme;
161
+ }, [themeName, customTheme, themeOverride, apiThemeOverride]);
162
+ return { theme, isLoadingTheme, greeting, initialMessage, isInitialMessageEnabled, voiceEnabled };
163
+ }
@@ -0,0 +1,31 @@
1
+ export interface UseSDKOptions {
2
+ apiUrl?: string;
3
+ subProjectId?: string;
4
+ /** Project identifier when the runtime config registers more than one project. */
5
+ projectId?: string;
6
+ /**
7
+ * BCP 47 locale. Forwarded to the SDK on init and re-applied any
8
+ * time it changes — keeps `sdkContext.locale` in sync with the host app.
9
+ */
10
+ locale?: string;
11
+ onReady?: () => void;
12
+ onError?: (error: Error) => void;
13
+ }
14
+ export interface UseSDKResult {
15
+ sdkReady: boolean;
16
+ error: string | null;
17
+ /**
18
+ * Dev-only: the resolved developmentKey from `qafka.config.js`. Null in
19
+ * production builds (Metro dead-code-eliminates the dev config branch).
20
+ * Used by `useVoiceChat` whose WebSocket auth still relies on apiKey.
21
+ */
22
+ resolvedApiKey: string | null;
23
+ /** Backend URL the SDK is actually using (after dev-config override). */
24
+ resolvedApiUrl: string | undefined;
25
+ initializeSDK: () => Promise<void>;
26
+ loadHistory: () => Promise<any[]>;
27
+ }
28
+ /**
29
+ * Custom hook for managing SDK initialization and lifecycle
30
+ */
31
+ export declare const useSDK: ({ apiUrl, subProjectId, projectId, locale, onReady, onError, }: UseSDKOptions) => UseSDKResult;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSDK = void 0;
4
+ const react_1 = require("react");
5
+ const QafkaSDK_1 = require("../QafkaSDK");
6
+ const runtime_config_loader_1 = require("../runtime-config-loader");
7
+ const resolve_project_config_1 = require("../resolve-project-config");
8
+ /**
9
+ * Custom hook for managing SDK initialization and lifecycle
10
+ */
11
+ const useSDK = ({ apiUrl, subProjectId, projectId, locale, onReady, onError, }) => {
12
+ const [sdkReady, setSdkReady] = (0, react_1.useState)(false);
13
+ const [error, setError] = (0, react_1.useState)(null);
14
+ const [resolvedApiKey, setResolvedApiKey] = (0, react_1.useState)(null);
15
+ const [resolvedApiUrl, setResolvedApiUrl] = (0, react_1.useState)(apiUrl);
16
+ const onReadyRef = (0, react_1.useRef)(onReady);
17
+ const onErrorRef = (0, react_1.useRef)(onError);
18
+ onReadyRef.current = onReady;
19
+ onErrorRef.current = onError;
20
+ const initializeSDK = async () => {
21
+ let apiKey = null;
22
+ let resolvedUrl = apiUrl;
23
+ if (__DEV__) {
24
+ // Dev path: load qafka.config.js, pick developmentKey for the current project.
25
+ // Metro dead-code elimination removes this branch entirely in production
26
+ // builds, so qafka.config.js never lands in the prod bundle.
27
+ try {
28
+ const cfg = (0, resolve_project_config_1.resolveProjectConfig)({ apiUrl, projectId }, (0, runtime_config_loader_1.loadRuntimeConfig)());
29
+ apiKey = cfg.apiKey;
30
+ resolvedUrl = cfg.apiUrl;
31
+ }
32
+ catch (err) {
33
+ if (__DEV__) {
34
+ console.warn('[Qafka] Dev config missing or invalid:', err instanceof Error ? err.message : err);
35
+ }
36
+ }
37
+ }
38
+ // Expose resolved values so `useVoiceChat` (WebSocket auth still
39
+ // apiKey-based) can pick up the dev key. Null in prod.
40
+ setResolvedApiKey(apiKey);
41
+ setResolvedApiUrl(resolvedUrl);
42
+ try {
43
+ const sdk = QafkaSDK_1.QafkaSDK.getInstance();
44
+ await sdk.initialize({
45
+ apiKey, // null in prod; developmentKey in dev
46
+ apiUrl: resolvedUrl,
47
+ subProjectId,
48
+ locale,
49
+ onStatusChange: (status) => {
50
+ if (status === 'ready') {
51
+ setSdkReady(true);
52
+ onReadyRef.current?.();
53
+ }
54
+ else if (status === 'error') {
55
+ setError('Failed to initialize SDK');
56
+ onErrorRef.current?.(new Error('SDK initialization failed'));
57
+ }
58
+ },
59
+ });
60
+ }
61
+ catch (err) {
62
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
63
+ setError(errorMessage);
64
+ onErrorRef.current?.(err instanceof Error ? err : new Error(errorMessage));
65
+ }
66
+ };
67
+ const loadHistory = async () => {
68
+ try {
69
+ const sdk = QafkaSDK_1.QafkaSDK.getInstance();
70
+ return await sdk.getConversationHistory();
71
+ }
72
+ catch {
73
+ return [];
74
+ }
75
+ };
76
+ (0, react_1.useEffect)(() => {
77
+ const sdk = QafkaSDK_1.QafkaSDK.getInstance();
78
+ setSdkReady(false);
79
+ setError(null);
80
+ initializeSDK();
81
+ return () => {
82
+ setSdkReady(false);
83
+ sdk.destroy();
84
+ };
85
+ // eslint-disable-next-line react-hooks/exhaustive-deps
86
+ }, [subProjectId, projectId]);
87
+ // re-apply locale changes without forcing a full SDK re-init —
88
+ // the host app may switch language mid-session.
89
+ (0, react_1.useEffect)(() => {
90
+ if (!sdkReady)
91
+ return;
92
+ QafkaSDK_1.QafkaSDK.getInstance().setLocale(locale ?? null);
93
+ }, [locale, sdkReady]);
94
+ return {
95
+ sdkReady,
96
+ error,
97
+ resolvedApiKey,
98
+ resolvedApiUrl,
99
+ initializeSDK,
100
+ loadHistory,
101
+ };
102
+ };
103
+ exports.useSDK = useSDK;
@@ -0,0 +1,110 @@
1
+ export type VoiceChatState = 'idle' | 'connecting' | 'listening' | 'thinking' | 'speaking';
2
+ export interface VoiceToolStatus {
3
+ toolCallId: string;
4
+ toolKey: string;
5
+ loadingMessage: string;
6
+ }
7
+ export type VoiceRenderedTool = {
8
+ kind?: 'tool';
9
+ toolCallId: string;
10
+ toolKey: string;
11
+ data: any;
12
+ tool: any;
13
+ } | {
14
+ kind: 'display';
15
+ toolCallId: string;
16
+ items: Array<{
17
+ label: string;
18
+ value: string;
19
+ copyable?: boolean;
20
+ }>;
21
+ };
22
+ export interface VoiceToolAddResponseResult {
23
+ ok: boolean;
24
+ toolName?: string;
25
+ toolOutput?: any;
26
+ message?: string;
27
+ }
28
+ /**
29
+ * One completed conversational turn during a voice session. Drives the
30
+ * chat-style transcript view. Live (in-flight) user/AI text is NOT in this
31
+ * list — see the live `transcript` / `userTranscript` strings on the hook
32
+ * result for the streaming/just-finished partial.
33
+ */
34
+ export interface VoiceTranscriptTurn {
35
+ id: string;
36
+ role: 'user' | 'assistant';
37
+ text: string;
38
+ }
39
+ export type { ToolResponseMode } from '../components/Qafka.types';
40
+ import type { ToolResponseMode } from '../components/Qafka.types';
41
+ /**
42
+ * Resolve / preview a tool result.
43
+ *
44
+ * @param data Payload for UI (and AI when mode commits). Shape is
45
+ * consumer-defined; common pattern is `{ data: ... }`.
46
+ * @param tool The full tool object passed by `onToolSuggested`. The key is
47
+ * read from `tool.key`; no separate toolKey argument is needed.
48
+ * @param mode See {@link ToolResponseMode}. Defaults to `"both"`.
49
+ */
50
+ export type VoiceToolAddResponse = (data: any, tool: any, mode?: ToolResponseMode) => Promise<VoiceToolAddResponseResult>;
51
+ export type VoiceOnToolSuggested = (tools: any[], addResponse: VoiceToolAddResponse) => void | Promise<void>;
52
+ /**
53
+ * Controls how renderedTools accumulates as new tool results stream in.
54
+ * - "upsert" (default): same toolKey replaces its previous entry; different
55
+ * toolKeys coexist on screen. Good for showing multiple unrelated cards.
56
+ * - "replace": every new tool result clears the previous renderedTools list,
57
+ * leaving only the latest single entry. Good for single-focus voice flows
58
+ * where a new tool means "switch the whole view".
59
+ */
60
+ export type VoiceToolRenderMode = 'upsert' | 'replace';
61
+ interface UseVoiceChatOptions {
62
+ apiUrl: string;
63
+ apiKey: string;
64
+ userContext?: Record<string, any>;
65
+ contextDescription?: string;
66
+ onToolSuggested?: VoiceOnToolSuggested;
67
+ toolRenderMode?: VoiceToolRenderMode;
68
+ /**
69
+ * Returns the current device-attestation session token, or null if no
70
+ * session has been established yet. Required in production: the server
71
+ * rejects WS connections without a valid session token.
72
+ */
73
+ getSessionToken?: () => Promise<string | null>;
74
+ /**
75
+ * Text/voice parity for the explicit end-user lane. Already validated
76
+ * by `<Qafka />` before reaching this hook.
77
+ */
78
+ endUserId?: string;
79
+ endUserData?: Record<string, unknown>;
80
+ }
81
+ interface UseVoiceChatResult {
82
+ state: VoiceChatState;
83
+ transcript: string;
84
+ userTranscript: string;
85
+ transcriptHistory: VoiceTranscriptTurn[];
86
+ amplitude: number;
87
+ connect: () => Promise<void>;
88
+ disconnect: () => Promise<void>;
89
+ pauseMic: () => Promise<void>;
90
+ resumeMic: () => Promise<void>;
91
+ isConnected: boolean;
92
+ toolStatus: VoiceToolStatus | null;
93
+ renderedTools: VoiceRenderedTool[];
94
+ transcriptOverrideForTurn: boolean;
95
+ clearRenderedTools: () => void;
96
+ setToolStatusManually: (status: {
97
+ toolKey: string;
98
+ loadingMessage: string;
99
+ } | null) => void;
100
+ /**
101
+ * User-controlled mute state. Independent from the internal tool-flow mute
102
+ * the hook applies during AI/tool transitions: even when system unmute
103
+ * fires, the mic stays off until the user clears their own mute.
104
+ */
105
+ isMuted: boolean;
106
+ mute: () => void;
107
+ unmute: () => void;
108
+ toggleMute: () => void;
109
+ }
110
+ export declare function useVoiceChat({ apiUrl, apiKey, userContext, contextDescription, onToolSuggested, toolRenderMode, getSessionToken, endUserId, endUserData, }: UseVoiceChatOptions): UseVoiceChatResult;