@akhilpulse/samadhaan-session-replay 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -140,8 +140,16 @@ cd ios && pod install
140
140
  the first time the assistant opens. If the user denies it, the voice bar
141
141
  shows "Mic blocked — allow microphone access in Settings" instead of
142
142
  pretending to listen.
143
- - **iOS** — add `NSMicrophoneUsageDescription` to `Info.plist` or the OS will
144
- kill the app when recording starts.
143
+ - **iOS** — add `NSMicrophoneUsageDescription` to `Info.plist` (**required**
144
+ without it iOS terminates the app the moment the mic is touched):
145
+ ```xml
146
+ <key>NSMicrophoneUsageDescription</key>
147
+ <string>Needed so the voice assistant can hear you.</string>
148
+ ```
149
+ Starting in **0.6.2 the SDK explicitly triggers the system mic prompt** when
150
+ the assistant opens (via the bundled native module — run `pod install` and
151
+ rebuild after upgrading). If the user denies it, the voice bar shows
152
+ "Mic blocked" instead of pretending to listen.
145
153
 
146
154
  #### Agent voice playback
147
155
 
@@ -177,7 +185,7 @@ iOS: `cd ios && pod install && cd .. && npx react-native run-ios`.
177
185
 
178
186
  - Creates a session against `POST /api/guided-support/sessions` and persists `sessionId` in AsyncStorage so it can be resumed after relaunch.
179
187
  - Captures gestures (tap, swipe) using a passive PanResponder + onTouchEnd bubble listener — host taps still work.
180
- - Captures screenshots every 10 s in background mode, every 200 ms in live mode, via `react-native-view-shot`. Uploads via `POST /api/guided-support/storage/uploads/request-url` then PUTs to the returned URL.
188
+ - Captures screenshots every 10 s in background mode, every 500 ms (~2 fps) in live/assistant mode, via `react-native-view-shot`. Uploads via `POST /api/guided-support/storage/uploads/request-url` then PUTs to the returned URL.
181
189
  - Auto-connects to `WS /ws/guided-support?sessionId=…&role=device&token=…` when the dashboard flips the session to `live`.
182
190
  - Streams binary frames as `[ts u32 BE | width u16 BE | height u16 BE] + JPEG bytes`.
183
191
  - Renders a remote-control overlay that visualizes incoming taps. Native touch dispatch (iOS UIEvent / Android AccessibilityService) is wired by the integrator via a small custom native module.
@@ -148,6 +148,18 @@ RCT_EXPORT_METHOD(stop)
148
148
  [self tearDownEngine];
149
149
  }
150
150
 
151
+ // Explicitly triggers the iOS microphone permission prompt (first call shows
152
+ // the system dialog; later calls resolve with the remembered answer).
153
+ // REQUIRES NSMicrophoneUsageDescription in the host app's Info.plist —
154
+ // without it iOS terminates the app the moment the mic is touched.
155
+ RCT_EXPORT_METHOD(requestMicPermission:(RCTPromiseResolveBlock)resolve
156
+ rejecter:(__unused RCTPromiseRejectBlock)reject)
157
+ {
158
+ [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
159
+ resolve(@(granted));
160
+ }];
161
+ }
162
+
151
163
  - (void)invalidate
152
164
  {
153
165
  [self tearDownEngine];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akhilpulse/samadhaan-session-replay",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "React Native SDK for Samadhaan Guided Support — session recording, live remote screen-share, take-control, guided-tour overlays, and Shake-to-Assist AI voice guide.",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
@@ -56,7 +56,7 @@ let pixelCopyDisabled = false; // turned true after repeated runtime failures to
56
56
  let pixelCopyFailStreak = 0; // consecutive non-busy failures; resets on success
57
57
 
58
58
  const STORAGE_KEY = "@akhilpulse/samadhaan-session-replay/sessionId";
59
- const SDK_VERSION = "0.6.0";
59
+ const SDK_VERSION = "0.6.2";
60
60
 
61
61
  // ---- Module-level session singleton --------------------------------------
62
62
  // Multiple SessionReplayProvider mounts (intentional or via React StrictMode's
@@ -370,9 +370,9 @@ export function SessionReplayProvider({ config, children }: { config: Config; ch
370
370
  // waste CPU/battery/network on periodic screenshots.
371
371
  if (config.disableScreenshots || !sessionId || !streamActive) return;
372
372
  let stopped = false;
373
- // ~4 fps target — low device load while still feeling responsive. Capture
373
+ // ~2 fps target — low device load while still feeling responsive. Capture
374
374
  // takes time, so the loop self-paces: next tick is scheduled AFTER the previous capture completes.
375
- const liveTargetMs = 250;
375
+ const liveTargetMs = 500;
376
376
  let lastUploadFrameAt = 0;
377
377
  const tick = async () => {
378
378
  if (stopped) return;
package/src/voice.ts CHANGED
@@ -37,25 +37,44 @@ function loadOptional<T = any>(name: string): T | null {
37
37
  /**
38
38
  * Android requires a runtime RECORD_AUDIO grant — react-native-live-audio-stream
39
39
  * does NOT request it itself; without it the recorder silently captures nothing
40
- * and the assistant can never hear the user. iOS shows its own prompt when the
41
- * recorder starts (host app must have NSMicrophoneUsageDescription in Info.plist).
40
+ * and the assistant can never hear the user.
41
+ * iOS (SDK 0.6.2+): the bundled native module explicitly triggers the system
42
+ * mic prompt via AVAudioSession requestRecordPermission. The host app MUST have
43
+ * NSMicrophoneUsageDescription in Info.plist or iOS kills the app on first use.
44
+ * On older iOS builds without the native method, we fall through and let the
45
+ * recorder's own first mic access raise the prompt.
42
46
  */
43
47
  async function ensureMicPermission(): Promise<boolean> {
44
- if (Platform.OS !== "android") return true;
45
- try {
46
- const perm = PermissionsAndroid.PERMISSIONS.RECORD_AUDIO;
47
- const already = await PermissionsAndroid.check(perm);
48
- if (already) return true;
49
- const res = await PermissionsAndroid.request(perm, {
50
- title: "Microphone access",
51
- message: "Needed so the voice assistant can hear you.",
52
- buttonPositive: "Allow",
53
- buttonNegative: "Not now",
54
- });
55
- return res === PermissionsAndroid.RESULTS.GRANTED;
56
- } catch {
57
- return false;
48
+ if (Platform.OS === "android") {
49
+ try {
50
+ const perm = PermissionsAndroid.PERMISSIONS.RECORD_AUDIO;
51
+ const already = await PermissionsAndroid.check(perm);
52
+ if (already) return true;
53
+ const res = await PermissionsAndroid.request(perm, {
54
+ title: "Microphone access",
55
+ message: "Needed so the voice assistant can hear you.",
56
+ buttonPositive: "Allow",
57
+ buttonNegative: "Not now",
58
+ });
59
+ return res === PermissionsAndroid.RESULTS.GRANTED;
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+ if (Platform.OS === "ios") {
65
+ const native: any = (NativeModules as any).SessionReplayPcmPlayer;
66
+ if (native && typeof native.requestMicPermission === "function") {
67
+ try {
68
+ return !!(await native.requestMicPermission());
69
+ } catch {
70
+ // Native call failed — don't block; recorder start may still prompt.
71
+ return true;
72
+ }
73
+ }
74
+ // Old build without the native method — recorder start triggers the prompt.
75
+ return true;
58
76
  }
77
+ return true;
59
78
  }
60
79
 
61
80
  /** Why voice input isn't working (surfaced so the UI can tell the user). */