@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 +11 -3
- package/ios/SessionReplayPcmPlayer.m +12 -0
- package/package.json +1 -1
- package/src/SessionReplayProvider.tsx +3 -3
- package/src/voice.ts +35 -16
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`
|
|
144
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
// ~
|
|
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 =
|
|
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.
|
|
41
|
-
*
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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). */
|