@sagepilot-ai/react-native-sdk 0.3.0 → 0.3.1
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 +8 -4
- package/dist/index.js +2 -95
- package/dist/index.mjs +2 -95
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ The SDK opens the Sagepilot-hosted chat experience inside a React Native WebView
|
|
|
19
19
|
Use this install when your app wants Sagepilot chat without Sagepilot's Android CameraX implementation:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
npm install @sagepilot-ai/react-native-sdk react-native-webview
|
|
22
|
+
npm install @sagepilot-ai/react-native-sdk@latest react-native-webview
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
This installs the base SDK only. It includes `SagepilotChat.configure`, hosted chat WebView, auth/session handling, the native bridge, attachment bridge protocol, `SagepilotFilePickerAdapter`, and custom picker support. It does not include CameraX or Android CameraX Gradle dependencies.
|
|
@@ -105,9 +105,11 @@ export function App() {
|
|
|
105
105
|
Install this addon only when you want Sagepilot's Android in-app CameraX picker. Apps that skip this step keep the smaller base SDK install.
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
|
-
npm install @sagepilot-ai/react-native-camera-addon
|
|
108
|
+
npm install @sagepilot-ai/react-native-camera-addon@latest
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
The addon declares `@sagepilot-ai/react-native-sdk` as a peer dependency. Keep the base SDK installed directly in your app; the addon should not be treated as a replacement SDK.
|
|
112
|
+
|
|
111
113
|
After installing the addon, run a fresh native Android build. A Metro reload is not enough for React Native autolinking to register `NativeModules.SagepilotInAppCamera`.
|
|
112
114
|
|
|
113
115
|
Then pass the addon picker into `SagepilotChat.configure`:
|
|
@@ -281,7 +283,7 @@ Configure `filePicker` so the attach button uses native pickers instead. Capture
|
|
|
281
283
|
The base package does not include CameraX. Apps that want the smallest install can use only:
|
|
282
284
|
|
|
283
285
|
```bash
|
|
284
|
-
npm install @sagepilot-ai/react-native-sdk react-native-webview
|
|
286
|
+
npm install @sagepilot-ai/react-native-sdk@latest react-native-webview
|
|
285
287
|
```
|
|
286
288
|
|
|
287
289
|
### Optional Android CameraX Addon
|
|
@@ -289,9 +291,11 @@ npm install @sagepilot-ai/react-native-sdk react-native-webview
|
|
|
289
291
|
Install the CameraX addon only when you want Sagepilot's Android in-app camera implementation:
|
|
290
292
|
|
|
291
293
|
```bash
|
|
292
|
-
npm install @sagepilot-ai/react-native-camera-addon
|
|
294
|
+
npm install @sagepilot-ai/react-native-camera-addon@latest
|
|
293
295
|
```
|
|
294
296
|
|
|
297
|
+
The addon declares `@sagepilot-ai/react-native-sdk` as a peer dependency. Keep the base SDK installed directly in your app; the addon should not be treated as a replacement SDK.
|
|
298
|
+
|
|
295
299
|
Run a clean native Android rebuild after installing the addon. If you only Metro-reload, the native module is not linked yet and `createSagepilotCameraXFilePicker()` returns `undefined`, so the hosted widget falls back to the WebView file input.
|
|
296
300
|
|
|
297
301
|
Then configure the addon adapter:
|
package/dist/index.js
CHANGED
|
@@ -67,7 +67,7 @@ var SagepilotChatError = class extends Error {
|
|
|
67
67
|
|
|
68
68
|
// src/core/config/constants.ts
|
|
69
69
|
var SDK_NAME = "@sagepilot-ai/react-native-sdk";
|
|
70
|
-
var SDK_VERSION = "0.3.
|
|
70
|
+
var SDK_VERSION = "0.3.1";
|
|
71
71
|
var DEFAULT_HOST = "https://app.sagepilot.ai";
|
|
72
72
|
var DEFAULT_WIDGET_HOST = "https://app.sagepilot.ai";
|
|
73
73
|
var CUSTOMER_API_PREFIX = "/customer-api/v1";
|
|
@@ -1416,7 +1416,6 @@ var SAGEPILOT_DEFAULT_IMAGE_QUALITY = 0.8;
|
|
|
1416
1416
|
var SAGEPILOT_DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES = 15 * 1024 * 1024;
|
|
1417
1417
|
|
|
1418
1418
|
// src/core/native/filePicker.ts
|
|
1419
|
-
var DEBUG_PREFIX = "[SagepilotSDK][FilePicker]";
|
|
1420
1419
|
var DEFAULT_IMAGE_MAX_DIMENSION = SAGEPILOT_DEFAULT_IMAGE_MAX_DIMENSION;
|
|
1421
1420
|
var DEFAULT_IMAGE_QUALITY = SAGEPILOT_DEFAULT_IMAGE_QUALITY;
|
|
1422
1421
|
var DEFAULT_IMAGE_MIME_TYPE = "image/jpeg";
|
|
@@ -1433,9 +1432,6 @@ function estimateBase64ByteSize(dataBase64) {
|
|
|
1433
1432
|
function stripFileScheme(uri) {
|
|
1434
1433
|
return uri.startsWith("file://") ? uri.replace("file://", "") : uri;
|
|
1435
1434
|
}
|
|
1436
|
-
function debugFilePicker(message, details) {
|
|
1437
|
-
console.log(`${DEBUG_PREFIX} ${message}`, details ?? "");
|
|
1438
|
-
}
|
|
1439
1435
|
async function promptOpenSagepilotCameraSettings() {
|
|
1440
1436
|
if (import_react_native2.Platform.OS !== "android") return;
|
|
1441
1437
|
await new Promise((resolve) => {
|
|
@@ -1462,13 +1458,10 @@ async function ensureSagepilotAndroidCameraPermission() {
|
|
|
1462
1458
|
if (import_react_native2.Platform.OS !== "android") return;
|
|
1463
1459
|
let result;
|
|
1464
1460
|
try {
|
|
1465
|
-
debugFilePicker("requesting Android camera permission");
|
|
1466
1461
|
result = await import_react_native2.PermissionsAndroid.request(import_react_native2.PermissionsAndroid.PERMISSIONS.CAMERA);
|
|
1467
1462
|
} catch {
|
|
1468
|
-
debugFilePicker("camera permission request threw; continuing to native launch");
|
|
1469
1463
|
return;
|
|
1470
1464
|
}
|
|
1471
|
-
debugFilePicker("Android camera permission result", { result });
|
|
1472
1465
|
if (result === import_react_native2.PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
|
|
1473
1466
|
await promptOpenSagepilotCameraSettings();
|
|
1474
1467
|
throw new SagepilotFilePickerError(
|
|
@@ -1565,13 +1558,6 @@ function createSagepilotFilePicker(options) {
|
|
|
1565
1558
|
if (canUseCameraSource(imagePicker)) sources.push("camera");
|
|
1566
1559
|
if (imagePicker) sources.push("library");
|
|
1567
1560
|
if (documentsPicker) sources.push("documents");
|
|
1568
|
-
debugFilePicker("adapter created", {
|
|
1569
|
-
platform: import_react_native2.Platform.OS,
|
|
1570
|
-
hasImagePicker: Boolean(imagePicker),
|
|
1571
|
-
hasDocumentsPicker: Boolean(documentsPicker),
|
|
1572
|
-
hasFileReader: Boolean(fileReader),
|
|
1573
|
-
sources
|
|
1574
|
-
});
|
|
1575
1561
|
if (sources.length === 0) return void 0;
|
|
1576
1562
|
const imageOptions = {
|
|
1577
1563
|
mediaType: "photo",
|
|
@@ -1582,22 +1568,14 @@ function createSagepilotFilePicker(options) {
|
|
|
1582
1568
|
saveToPhotos: false
|
|
1583
1569
|
};
|
|
1584
1570
|
async function pickFromCamera() {
|
|
1585
|
-
debugFilePicker("camera pick requested", {
|
|
1586
|
-
platform: import_react_native2.Platform.OS,
|
|
1587
|
-
imageMaxDimension,
|
|
1588
|
-
imageQuality,
|
|
1589
|
-
imageMaxFileSizeBytes
|
|
1590
|
-
});
|
|
1591
1571
|
await ensureSagepilotAndroidCameraPermission();
|
|
1592
1572
|
if (!imagePicker) {
|
|
1593
1573
|
throw new SagepilotFilePickerError("camera_unavailable", "The camera picker is not available in this build.");
|
|
1594
1574
|
}
|
|
1595
|
-
debugFilePicker("launching image-picker camera", { platform: import_react_native2.Platform.OS });
|
|
1596
1575
|
return imageAssetsToPickedFiles(await imagePicker.launchCamera(imageOptions), imageMaxFileSizeBytes);
|
|
1597
1576
|
}
|
|
1598
1577
|
async function pickFromLibrary(multiple) {
|
|
1599
1578
|
if (!imagePicker) return [];
|
|
1600
|
-
debugFilePicker("library pick requested", { multiple, imageSelectionLimit });
|
|
1601
1579
|
return imageAssetsToPickedFiles(await imagePicker.launchImageLibrary({
|
|
1602
1580
|
...imageOptions,
|
|
1603
1581
|
// Bounded multi-select: 0 (unlimited) lets a huge batch OOM the bridge.
|
|
@@ -1606,7 +1584,6 @@ function createSagepilotFilePicker(options) {
|
|
|
1606
1584
|
}
|
|
1607
1585
|
async function pickDocuments(multiple) {
|
|
1608
1586
|
if (!documentsPicker) return [];
|
|
1609
|
-
debugFilePicker("document pick requested", { multiple, documentMaxFileSizeBytes });
|
|
1610
1587
|
let picked;
|
|
1611
1588
|
try {
|
|
1612
1589
|
picked = await documentsPicker.pick({ allowMultiSelection: multiple });
|
|
@@ -1656,7 +1633,6 @@ function createSagepilotFilePicker(options) {
|
|
|
1656
1633
|
return {
|
|
1657
1634
|
sources,
|
|
1658
1635
|
async pickFiles(request) {
|
|
1659
|
-
debugFilePicker("pickFiles request", request);
|
|
1660
1636
|
if (request.source === "camera") return pickFromCamera();
|
|
1661
1637
|
if (request.source === "library") return pickFromLibrary(request.multiple);
|
|
1662
1638
|
return pickDocuments(request.multiple);
|
|
@@ -1877,7 +1853,6 @@ var FILE_PICKER_SOURCE_ICON_COLORS = {
|
|
|
1877
1853
|
library: "#16a34a",
|
|
1878
1854
|
documents: "#7c3aed"
|
|
1879
1855
|
};
|
|
1880
|
-
var DEBUG_PREFIX2 = "[SagepilotSDK][Provider]";
|
|
1881
1856
|
var DELIVERY_RETRY_MS = 1500;
|
|
1882
1857
|
var DELIVERY_LEGACY_FALLBACK_ATTEMPTS = 2;
|
|
1883
1858
|
var DELIVERY_MAX_ATTEMPTS = 8;
|
|
@@ -1961,16 +1936,6 @@ function readFilePickerError(error) {
|
|
|
1961
1936
|
}
|
|
1962
1937
|
return { message: "Could not attach the selected file." };
|
|
1963
1938
|
}
|
|
1964
|
-
function debugProvider(message, details) {
|
|
1965
|
-
console.log(`${DEBUG_PREFIX2} ${message}`, details ?? "");
|
|
1966
|
-
}
|
|
1967
|
-
function describeAttachmentTarget(target) {
|
|
1968
|
-
return {
|
|
1969
|
-
targetChatId: target?.chatId,
|
|
1970
|
-
targetRouteChatId: target?.routeChatId,
|
|
1971
|
-
targetScreen: target?.hostedChatView.screen
|
|
1972
|
-
};
|
|
1973
|
-
}
|
|
1974
1939
|
function getHostedIdentityDispatchScript() {
|
|
1975
1940
|
const message = internalSagepilotChat.getHostedIdentityMessage();
|
|
1976
1941
|
if (!message) return "";
|
|
@@ -2120,10 +2085,6 @@ function SagepilotChatProvider({
|
|
|
2120
2085
|
const batch = pendingBatchesRef.current[0];
|
|
2121
2086
|
if (!batch) return;
|
|
2122
2087
|
if (internalSagepilotChat.restoreHostedAttachmentTarget(batch.target)) {
|
|
2123
|
-
debugProvider("restored hosted attachment target", {
|
|
2124
|
-
batchId: batch.batchId,
|
|
2125
|
-
...describeAttachmentTarget(batch.target)
|
|
2126
|
-
});
|
|
2127
2088
|
widgetReadyRef.current = false;
|
|
2128
2089
|
deliveryTimerRef.current = setTimeout(() => pumpDelivery(), DELIVERY_RETRY_MS);
|
|
2129
2090
|
return;
|
|
@@ -2133,13 +2094,6 @@ function SagepilotChatProvider({
|
|
|
2133
2094
|
const ref = nativeWebViewRef.current;
|
|
2134
2095
|
const shouldDeliver = ref && (widgetReadyRef.current || attempts >= DELIVERY_LEGACY_FALLBACK_ATTEMPTS);
|
|
2135
2096
|
if (shouldDeliver && ref) {
|
|
2136
|
-
debugProvider("delivering native picked files", {
|
|
2137
|
-
batchId: batch.batchId,
|
|
2138
|
-
attempt: attempts,
|
|
2139
|
-
widgetReady: widgetReadyRef.current,
|
|
2140
|
-
count: batch.files.length,
|
|
2141
|
-
...describeAttachmentTarget(batch.target)
|
|
2142
|
-
});
|
|
2143
2097
|
ref.injectJavaScript(buildFilesPickedScript(batch));
|
|
2144
2098
|
}
|
|
2145
2099
|
if (attempts < DELIVERY_MAX_ATTEMPTS) {
|
|
@@ -2177,21 +2131,12 @@ function SagepilotChatProvider({
|
|
|
2177
2131
|
storageKeys,
|
|
2178
2132
|
target: internalSagepilotChat.getHostedAttachmentTarget()
|
|
2179
2133
|
};
|
|
2180
|
-
debugProvider("queued native picked files", {
|
|
2181
|
-
batchId,
|
|
2182
|
-
count: files.length,
|
|
2183
|
-
totalBase64Bytes: totalBase64Bytes(files),
|
|
2184
|
-
persistedToFileStore: hasFileStore,
|
|
2185
|
-
...describeAttachmentTarget(batch.target)
|
|
2186
|
-
});
|
|
2187
2134
|
pendingBatchesRef.current = [...pendingBatchesRef.current, batch];
|
|
2188
2135
|
ensureDelivery();
|
|
2189
2136
|
writeManifest();
|
|
2190
2137
|
void writeBatchBytes(batch);
|
|
2191
2138
|
}, [ensureDelivery, writeManifest, writeBatchBytes]);
|
|
2192
|
-
const recoverNativeWebView = (0, import_react.useCallback)((
|
|
2193
|
-
const reason = typeof reasonOrEvent === "string" ? reasonOrEvent : "webview_render_process";
|
|
2194
|
-
debugProvider("recovering native WebView", { reason });
|
|
2139
|
+
const recoverNativeWebView = (0, import_react.useCallback)((_reasonOrEvent) => {
|
|
2195
2140
|
widgetReadyRef.current = false;
|
|
2196
2141
|
setNativeWebViewKey((key) => key + 1);
|
|
2197
2142
|
}, []);
|
|
@@ -2205,31 +2150,14 @@ function SagepilotChatProvider({
|
|
|
2205
2150
|
const runNativeFilePicker = (0, import_react.useCallback)((source, multiple) => {
|
|
2206
2151
|
const filePicker = internalSagepilotChat.getConfig()?.filePicker;
|
|
2207
2152
|
if (!filePicker) {
|
|
2208
|
-
debugProvider("native file picker requested but no adapter is configured", { source, multiple });
|
|
2209
2153
|
return;
|
|
2210
2154
|
}
|
|
2211
2155
|
if (pickerInFlightRef.current) {
|
|
2212
|
-
debugProvider("native file picker ignored because another picker is in flight", { source, multiple });
|
|
2213
2156
|
return;
|
|
2214
2157
|
}
|
|
2215
|
-
debugProvider("native file picker starting", {
|
|
2216
|
-
source,
|
|
2217
|
-
multiple,
|
|
2218
|
-
configuredSources: filePicker.sources
|
|
2219
|
-
});
|
|
2220
2158
|
pauseLivenessAfterPicker(6e4);
|
|
2221
2159
|
pickerInFlightRef.current = true;
|
|
2222
2160
|
filePicker.pickFiles({ source, multiple }).then((files) => {
|
|
2223
|
-
debugProvider("native file picker resolved", {
|
|
2224
|
-
source,
|
|
2225
|
-
count: files.length,
|
|
2226
|
-
files: files.map((file) => ({
|
|
2227
|
-
fileName: file.file_name,
|
|
2228
|
-
mimeType: file.mime_type,
|
|
2229
|
-
size: file.size,
|
|
2230
|
-
base64Length: file.data_base64.length
|
|
2231
|
-
}))
|
|
2232
|
-
});
|
|
2233
2161
|
if (files.length === 0) {
|
|
2234
2162
|
nativeWebViewRef.current?.injectJavaScript(buildFilePickerCancelledScript());
|
|
2235
2163
|
return;
|
|
@@ -2237,7 +2165,6 @@ function SagepilotChatProvider({
|
|
|
2237
2165
|
queuePickedFiles(files);
|
|
2238
2166
|
}).catch((error) => {
|
|
2239
2167
|
const { message, code } = readFilePickerError(error);
|
|
2240
|
-
debugProvider("native file picker failed", { source, code, message });
|
|
2241
2168
|
nativeWebViewRef.current?.injectJavaScript(buildFilePickerErrorScript(message, code));
|
|
2242
2169
|
}).finally(() => {
|
|
2243
2170
|
pickerInFlightRef.current = false;
|
|
@@ -2247,14 +2174,8 @@ function SagepilotChatProvider({
|
|
|
2247
2174
|
const openNativeFilePicker = (0, import_react.useCallback)((multiple) => {
|
|
2248
2175
|
const filePicker = internalSagepilotChat.getConfig()?.filePicker;
|
|
2249
2176
|
if (!filePicker || filePicker.sources.length === 0) {
|
|
2250
|
-
debugProvider("open native file picker ignored", {
|
|
2251
|
-
multiple,
|
|
2252
|
-
hasAdapter: Boolean(filePicker),
|
|
2253
|
-
sources: filePicker?.sources ?? []
|
|
2254
|
-
});
|
|
2255
2177
|
return;
|
|
2256
2178
|
}
|
|
2257
|
-
debugProvider("open native file picker", { multiple, sources: filePicker.sources });
|
|
2258
2179
|
const [onlySource] = filePicker.sources;
|
|
2259
2180
|
if (filePicker.sources.length === 1 && onlySource && onlySource !== "camera") {
|
|
2260
2181
|
runNativeFilePicker(onlySource, multiple);
|
|
@@ -2364,10 +2285,6 @@ function SagepilotChatProvider({
|
|
|
2364
2285
|
const pauseRemainingMs = livenessPausedUntilRef.current - Date.now();
|
|
2365
2286
|
if (pickerInFlightRef.current || pauseRemainingMs > 0) {
|
|
2366
2287
|
const retryDelayMs = pickerInFlightRef.current ? 500 : Math.max(0, pauseRemainingMs);
|
|
2367
|
-
debugProvider("liveness probe deferred after picker", {
|
|
2368
|
-
pickerInFlight: pickerInFlightRef.current,
|
|
2369
|
-
retryDelayMs
|
|
2370
|
-
});
|
|
2371
2288
|
clearDeferredLivenessProbe();
|
|
2372
2289
|
livenessResumeTimerRef.current = setTimeout(() => {
|
|
2373
2290
|
livenessResumeTimerRef.current = null;
|
|
@@ -2452,11 +2369,6 @@ function SagepilotChatProvider({
|
|
|
2452
2369
|
const ackBatchId = message.batch_id;
|
|
2453
2370
|
const head = pendingBatchesRef.current[0];
|
|
2454
2371
|
if (head && (!ackBatchId || ackBatchId === head.batchId)) {
|
|
2455
|
-
debugProvider("native picked files acknowledged", {
|
|
2456
|
-
batchId: head.batchId,
|
|
2457
|
-
ackBatchId,
|
|
2458
|
-
count: message.count
|
|
2459
|
-
});
|
|
2460
2372
|
acknowledgeHeadBatch();
|
|
2461
2373
|
}
|
|
2462
2374
|
return;
|
|
@@ -2466,10 +2378,6 @@ function SagepilotChatProvider({
|
|
|
2466
2378
|
if (pending && (!message.nonce || message.nonce === pending.nonce)) {
|
|
2467
2379
|
if (message.alive === false) {
|
|
2468
2380
|
clearPing();
|
|
2469
|
-
debugProvider("liveness probe reported dead widget", {
|
|
2470
|
-
supportsHeartbeat: message.supportsHeartbeat,
|
|
2471
|
-
remountCount: livenessRemountCountRef.current
|
|
2472
|
-
});
|
|
2473
2381
|
if (livenessRemountCountRef.current < LIVENESS_MAX_REMOUNTS) {
|
|
2474
2382
|
livenessRemountCountRef.current += 1;
|
|
2475
2383
|
recoverNativeWebView("liveness_dead_pong");
|
|
@@ -2499,7 +2407,6 @@ function SagepilotChatProvider({
|
|
|
2499
2407
|
nativeWebViewRef.current.injectJavaScript(getInjectedWebViewScript());
|
|
2500
2408
|
};
|
|
2501
2409
|
const handleNativeWebViewLoadEnd = () => {
|
|
2502
|
-
debugProvider("native WebView load end");
|
|
2503
2410
|
injectNativeBridgeToNativeWebView();
|
|
2504
2411
|
postIdentityToNativeWebView();
|
|
2505
2412
|
widgetReadyRef.current = false;
|
package/dist/index.mjs
CHANGED
|
@@ -18,7 +18,7 @@ var SagepilotChatError = class extends Error {
|
|
|
18
18
|
|
|
19
19
|
// src/core/config/constants.ts
|
|
20
20
|
var SDK_NAME = "@sagepilot-ai/react-native-sdk";
|
|
21
|
-
var SDK_VERSION = "0.3.
|
|
21
|
+
var SDK_VERSION = "0.3.1";
|
|
22
22
|
var DEFAULT_HOST = "https://app.sagepilot.ai";
|
|
23
23
|
var DEFAULT_WIDGET_HOST = "https://app.sagepilot.ai";
|
|
24
24
|
var CUSTOMER_API_PREFIX = "/customer-api/v1";
|
|
@@ -1367,7 +1367,6 @@ var SAGEPILOT_DEFAULT_IMAGE_QUALITY = 0.8;
|
|
|
1367
1367
|
var SAGEPILOT_DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES = 15 * 1024 * 1024;
|
|
1368
1368
|
|
|
1369
1369
|
// src/core/native/filePicker.ts
|
|
1370
|
-
var DEBUG_PREFIX = "[SagepilotSDK][FilePicker]";
|
|
1371
1370
|
var DEFAULT_IMAGE_MAX_DIMENSION = SAGEPILOT_DEFAULT_IMAGE_MAX_DIMENSION;
|
|
1372
1371
|
var DEFAULT_IMAGE_QUALITY = SAGEPILOT_DEFAULT_IMAGE_QUALITY;
|
|
1373
1372
|
var DEFAULT_IMAGE_MIME_TYPE = "image/jpeg";
|
|
@@ -1384,9 +1383,6 @@ function estimateBase64ByteSize(dataBase64) {
|
|
|
1384
1383
|
function stripFileScheme(uri) {
|
|
1385
1384
|
return uri.startsWith("file://") ? uri.replace("file://", "") : uri;
|
|
1386
1385
|
}
|
|
1387
|
-
function debugFilePicker(message, details) {
|
|
1388
|
-
console.log(`${DEBUG_PREFIX} ${message}`, details ?? "");
|
|
1389
|
-
}
|
|
1390
1386
|
async function promptOpenSagepilotCameraSettings() {
|
|
1391
1387
|
if (Platform2.OS !== "android") return;
|
|
1392
1388
|
await new Promise((resolve) => {
|
|
@@ -1413,13 +1409,10 @@ async function ensureSagepilotAndroidCameraPermission() {
|
|
|
1413
1409
|
if (Platform2.OS !== "android") return;
|
|
1414
1410
|
let result;
|
|
1415
1411
|
try {
|
|
1416
|
-
debugFilePicker("requesting Android camera permission");
|
|
1417
1412
|
result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA);
|
|
1418
1413
|
} catch {
|
|
1419
|
-
debugFilePicker("camera permission request threw; continuing to native launch");
|
|
1420
1414
|
return;
|
|
1421
1415
|
}
|
|
1422
|
-
debugFilePicker("Android camera permission result", { result });
|
|
1423
1416
|
if (result === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
|
|
1424
1417
|
await promptOpenSagepilotCameraSettings();
|
|
1425
1418
|
throw new SagepilotFilePickerError(
|
|
@@ -1516,13 +1509,6 @@ function createSagepilotFilePicker(options) {
|
|
|
1516
1509
|
if (canUseCameraSource(imagePicker)) sources.push("camera");
|
|
1517
1510
|
if (imagePicker) sources.push("library");
|
|
1518
1511
|
if (documentsPicker) sources.push("documents");
|
|
1519
|
-
debugFilePicker("adapter created", {
|
|
1520
|
-
platform: Platform2.OS,
|
|
1521
|
-
hasImagePicker: Boolean(imagePicker),
|
|
1522
|
-
hasDocumentsPicker: Boolean(documentsPicker),
|
|
1523
|
-
hasFileReader: Boolean(fileReader),
|
|
1524
|
-
sources
|
|
1525
|
-
});
|
|
1526
1512
|
if (sources.length === 0) return void 0;
|
|
1527
1513
|
const imageOptions = {
|
|
1528
1514
|
mediaType: "photo",
|
|
@@ -1533,22 +1519,14 @@ function createSagepilotFilePicker(options) {
|
|
|
1533
1519
|
saveToPhotos: false
|
|
1534
1520
|
};
|
|
1535
1521
|
async function pickFromCamera() {
|
|
1536
|
-
debugFilePicker("camera pick requested", {
|
|
1537
|
-
platform: Platform2.OS,
|
|
1538
|
-
imageMaxDimension,
|
|
1539
|
-
imageQuality,
|
|
1540
|
-
imageMaxFileSizeBytes
|
|
1541
|
-
});
|
|
1542
1522
|
await ensureSagepilotAndroidCameraPermission();
|
|
1543
1523
|
if (!imagePicker) {
|
|
1544
1524
|
throw new SagepilotFilePickerError("camera_unavailable", "The camera picker is not available in this build.");
|
|
1545
1525
|
}
|
|
1546
|
-
debugFilePicker("launching image-picker camera", { platform: Platform2.OS });
|
|
1547
1526
|
return imageAssetsToPickedFiles(await imagePicker.launchCamera(imageOptions), imageMaxFileSizeBytes);
|
|
1548
1527
|
}
|
|
1549
1528
|
async function pickFromLibrary(multiple) {
|
|
1550
1529
|
if (!imagePicker) return [];
|
|
1551
|
-
debugFilePicker("library pick requested", { multiple, imageSelectionLimit });
|
|
1552
1530
|
return imageAssetsToPickedFiles(await imagePicker.launchImageLibrary({
|
|
1553
1531
|
...imageOptions,
|
|
1554
1532
|
// Bounded multi-select: 0 (unlimited) lets a huge batch OOM the bridge.
|
|
@@ -1557,7 +1535,6 @@ function createSagepilotFilePicker(options) {
|
|
|
1557
1535
|
}
|
|
1558
1536
|
async function pickDocuments(multiple) {
|
|
1559
1537
|
if (!documentsPicker) return [];
|
|
1560
|
-
debugFilePicker("document pick requested", { multiple, documentMaxFileSizeBytes });
|
|
1561
1538
|
let picked;
|
|
1562
1539
|
try {
|
|
1563
1540
|
picked = await documentsPicker.pick({ allowMultiSelection: multiple });
|
|
@@ -1607,7 +1584,6 @@ function createSagepilotFilePicker(options) {
|
|
|
1607
1584
|
return {
|
|
1608
1585
|
sources,
|
|
1609
1586
|
async pickFiles(request) {
|
|
1610
|
-
debugFilePicker("pickFiles request", request);
|
|
1611
1587
|
if (request.source === "camera") return pickFromCamera();
|
|
1612
1588
|
if (request.source === "library") return pickFromLibrary(request.multiple);
|
|
1613
1589
|
return pickDocuments(request.multiple);
|
|
@@ -1840,7 +1816,6 @@ var FILE_PICKER_SOURCE_ICON_COLORS = {
|
|
|
1840
1816
|
library: "#16a34a",
|
|
1841
1817
|
documents: "#7c3aed"
|
|
1842
1818
|
};
|
|
1843
|
-
var DEBUG_PREFIX2 = "[SagepilotSDK][Provider]";
|
|
1844
1819
|
var DELIVERY_RETRY_MS = 1500;
|
|
1845
1820
|
var DELIVERY_LEGACY_FALLBACK_ATTEMPTS = 2;
|
|
1846
1821
|
var DELIVERY_MAX_ATTEMPTS = 8;
|
|
@@ -1924,16 +1899,6 @@ function readFilePickerError(error) {
|
|
|
1924
1899
|
}
|
|
1925
1900
|
return { message: "Could not attach the selected file." };
|
|
1926
1901
|
}
|
|
1927
|
-
function debugProvider(message, details) {
|
|
1928
|
-
console.log(`${DEBUG_PREFIX2} ${message}`, details ?? "");
|
|
1929
|
-
}
|
|
1930
|
-
function describeAttachmentTarget(target) {
|
|
1931
|
-
return {
|
|
1932
|
-
targetChatId: target?.chatId,
|
|
1933
|
-
targetRouteChatId: target?.routeChatId,
|
|
1934
|
-
targetScreen: target?.hostedChatView.screen
|
|
1935
|
-
};
|
|
1936
|
-
}
|
|
1937
1902
|
function getHostedIdentityDispatchScript() {
|
|
1938
1903
|
const message = internalSagepilotChat.getHostedIdentityMessage();
|
|
1939
1904
|
if (!message) return "";
|
|
@@ -2083,10 +2048,6 @@ function SagepilotChatProvider({
|
|
|
2083
2048
|
const batch = pendingBatchesRef.current[0];
|
|
2084
2049
|
if (!batch) return;
|
|
2085
2050
|
if (internalSagepilotChat.restoreHostedAttachmentTarget(batch.target)) {
|
|
2086
|
-
debugProvider("restored hosted attachment target", {
|
|
2087
|
-
batchId: batch.batchId,
|
|
2088
|
-
...describeAttachmentTarget(batch.target)
|
|
2089
|
-
});
|
|
2090
2051
|
widgetReadyRef.current = false;
|
|
2091
2052
|
deliveryTimerRef.current = setTimeout(() => pumpDelivery(), DELIVERY_RETRY_MS);
|
|
2092
2053
|
return;
|
|
@@ -2096,13 +2057,6 @@ function SagepilotChatProvider({
|
|
|
2096
2057
|
const ref = nativeWebViewRef.current;
|
|
2097
2058
|
const shouldDeliver = ref && (widgetReadyRef.current || attempts >= DELIVERY_LEGACY_FALLBACK_ATTEMPTS);
|
|
2098
2059
|
if (shouldDeliver && ref) {
|
|
2099
|
-
debugProvider("delivering native picked files", {
|
|
2100
|
-
batchId: batch.batchId,
|
|
2101
|
-
attempt: attempts,
|
|
2102
|
-
widgetReady: widgetReadyRef.current,
|
|
2103
|
-
count: batch.files.length,
|
|
2104
|
-
...describeAttachmentTarget(batch.target)
|
|
2105
|
-
});
|
|
2106
2060
|
ref.injectJavaScript(buildFilesPickedScript(batch));
|
|
2107
2061
|
}
|
|
2108
2062
|
if (attempts < DELIVERY_MAX_ATTEMPTS) {
|
|
@@ -2140,21 +2094,12 @@ function SagepilotChatProvider({
|
|
|
2140
2094
|
storageKeys,
|
|
2141
2095
|
target: internalSagepilotChat.getHostedAttachmentTarget()
|
|
2142
2096
|
};
|
|
2143
|
-
debugProvider("queued native picked files", {
|
|
2144
|
-
batchId,
|
|
2145
|
-
count: files.length,
|
|
2146
|
-
totalBase64Bytes: totalBase64Bytes(files),
|
|
2147
|
-
persistedToFileStore: hasFileStore,
|
|
2148
|
-
...describeAttachmentTarget(batch.target)
|
|
2149
|
-
});
|
|
2150
2097
|
pendingBatchesRef.current = [...pendingBatchesRef.current, batch];
|
|
2151
2098
|
ensureDelivery();
|
|
2152
2099
|
writeManifest();
|
|
2153
2100
|
void writeBatchBytes(batch);
|
|
2154
2101
|
}, [ensureDelivery, writeManifest, writeBatchBytes]);
|
|
2155
|
-
const recoverNativeWebView = useCallback((
|
|
2156
|
-
const reason = typeof reasonOrEvent === "string" ? reasonOrEvent : "webview_render_process";
|
|
2157
|
-
debugProvider("recovering native WebView", { reason });
|
|
2102
|
+
const recoverNativeWebView = useCallback((_reasonOrEvent) => {
|
|
2158
2103
|
widgetReadyRef.current = false;
|
|
2159
2104
|
setNativeWebViewKey((key) => key + 1);
|
|
2160
2105
|
}, []);
|
|
@@ -2168,31 +2113,14 @@ function SagepilotChatProvider({
|
|
|
2168
2113
|
const runNativeFilePicker = useCallback((source, multiple) => {
|
|
2169
2114
|
const filePicker = internalSagepilotChat.getConfig()?.filePicker;
|
|
2170
2115
|
if (!filePicker) {
|
|
2171
|
-
debugProvider("native file picker requested but no adapter is configured", { source, multiple });
|
|
2172
2116
|
return;
|
|
2173
2117
|
}
|
|
2174
2118
|
if (pickerInFlightRef.current) {
|
|
2175
|
-
debugProvider("native file picker ignored because another picker is in flight", { source, multiple });
|
|
2176
2119
|
return;
|
|
2177
2120
|
}
|
|
2178
|
-
debugProvider("native file picker starting", {
|
|
2179
|
-
source,
|
|
2180
|
-
multiple,
|
|
2181
|
-
configuredSources: filePicker.sources
|
|
2182
|
-
});
|
|
2183
2121
|
pauseLivenessAfterPicker(6e4);
|
|
2184
2122
|
pickerInFlightRef.current = true;
|
|
2185
2123
|
filePicker.pickFiles({ source, multiple }).then((files) => {
|
|
2186
|
-
debugProvider("native file picker resolved", {
|
|
2187
|
-
source,
|
|
2188
|
-
count: files.length,
|
|
2189
|
-
files: files.map((file) => ({
|
|
2190
|
-
fileName: file.file_name,
|
|
2191
|
-
mimeType: file.mime_type,
|
|
2192
|
-
size: file.size,
|
|
2193
|
-
base64Length: file.data_base64.length
|
|
2194
|
-
}))
|
|
2195
|
-
});
|
|
2196
2124
|
if (files.length === 0) {
|
|
2197
2125
|
nativeWebViewRef.current?.injectJavaScript(buildFilePickerCancelledScript());
|
|
2198
2126
|
return;
|
|
@@ -2200,7 +2128,6 @@ function SagepilotChatProvider({
|
|
|
2200
2128
|
queuePickedFiles(files);
|
|
2201
2129
|
}).catch((error) => {
|
|
2202
2130
|
const { message, code } = readFilePickerError(error);
|
|
2203
|
-
debugProvider("native file picker failed", { source, code, message });
|
|
2204
2131
|
nativeWebViewRef.current?.injectJavaScript(buildFilePickerErrorScript(message, code));
|
|
2205
2132
|
}).finally(() => {
|
|
2206
2133
|
pickerInFlightRef.current = false;
|
|
@@ -2210,14 +2137,8 @@ function SagepilotChatProvider({
|
|
|
2210
2137
|
const openNativeFilePicker = useCallback((multiple) => {
|
|
2211
2138
|
const filePicker = internalSagepilotChat.getConfig()?.filePicker;
|
|
2212
2139
|
if (!filePicker || filePicker.sources.length === 0) {
|
|
2213
|
-
debugProvider("open native file picker ignored", {
|
|
2214
|
-
multiple,
|
|
2215
|
-
hasAdapter: Boolean(filePicker),
|
|
2216
|
-
sources: filePicker?.sources ?? []
|
|
2217
|
-
});
|
|
2218
2140
|
return;
|
|
2219
2141
|
}
|
|
2220
|
-
debugProvider("open native file picker", { multiple, sources: filePicker.sources });
|
|
2221
2142
|
const [onlySource] = filePicker.sources;
|
|
2222
2143
|
if (filePicker.sources.length === 1 && onlySource && onlySource !== "camera") {
|
|
2223
2144
|
runNativeFilePicker(onlySource, multiple);
|
|
@@ -2327,10 +2248,6 @@ function SagepilotChatProvider({
|
|
|
2327
2248
|
const pauseRemainingMs = livenessPausedUntilRef.current - Date.now();
|
|
2328
2249
|
if (pickerInFlightRef.current || pauseRemainingMs > 0) {
|
|
2329
2250
|
const retryDelayMs = pickerInFlightRef.current ? 500 : Math.max(0, pauseRemainingMs);
|
|
2330
|
-
debugProvider("liveness probe deferred after picker", {
|
|
2331
|
-
pickerInFlight: pickerInFlightRef.current,
|
|
2332
|
-
retryDelayMs
|
|
2333
|
-
});
|
|
2334
2251
|
clearDeferredLivenessProbe();
|
|
2335
2252
|
livenessResumeTimerRef.current = setTimeout(() => {
|
|
2336
2253
|
livenessResumeTimerRef.current = null;
|
|
@@ -2415,11 +2332,6 @@ function SagepilotChatProvider({
|
|
|
2415
2332
|
const ackBatchId = message.batch_id;
|
|
2416
2333
|
const head = pendingBatchesRef.current[0];
|
|
2417
2334
|
if (head && (!ackBatchId || ackBatchId === head.batchId)) {
|
|
2418
|
-
debugProvider("native picked files acknowledged", {
|
|
2419
|
-
batchId: head.batchId,
|
|
2420
|
-
ackBatchId,
|
|
2421
|
-
count: message.count
|
|
2422
|
-
});
|
|
2423
2335
|
acknowledgeHeadBatch();
|
|
2424
2336
|
}
|
|
2425
2337
|
return;
|
|
@@ -2429,10 +2341,6 @@ function SagepilotChatProvider({
|
|
|
2429
2341
|
if (pending && (!message.nonce || message.nonce === pending.nonce)) {
|
|
2430
2342
|
if (message.alive === false) {
|
|
2431
2343
|
clearPing();
|
|
2432
|
-
debugProvider("liveness probe reported dead widget", {
|
|
2433
|
-
supportsHeartbeat: message.supportsHeartbeat,
|
|
2434
|
-
remountCount: livenessRemountCountRef.current
|
|
2435
|
-
});
|
|
2436
2344
|
if (livenessRemountCountRef.current < LIVENESS_MAX_REMOUNTS) {
|
|
2437
2345
|
livenessRemountCountRef.current += 1;
|
|
2438
2346
|
recoverNativeWebView("liveness_dead_pong");
|
|
@@ -2462,7 +2370,6 @@ function SagepilotChatProvider({
|
|
|
2462
2370
|
nativeWebViewRef.current.injectJavaScript(getInjectedWebViewScript());
|
|
2463
2371
|
};
|
|
2464
2372
|
const handleNativeWebViewLoadEnd = () => {
|
|
2465
|
-
debugProvider("native WebView load end");
|
|
2466
2373
|
injectNativeBridgeToNativeWebView();
|
|
2467
2374
|
postIdentityToNativeWebView();
|
|
2468
2375
|
widgetReadyRef.current = false;
|