@sagepilot-ai/react-native-sdk 0.2.3 → 0.2.5
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.md +21 -0
- package/README.md +110 -1
- package/dist/index.d.mts +228 -1
- package/dist/index.d.ts +228 -1
- package/dist/index.js +948 -40
- package/dist/index.mjs +932 -25
- package/package.json +13 -1
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sagepilot AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -69,6 +69,9 @@ These options are part of the supported React Native SDK configuration contract.
|
|
|
69
69
|
| `headers` | Additional headers for SDK service requests. Do not pass server API keys or long-lived secrets from a mobile app. |
|
|
70
70
|
| `fetch` | Custom fetch implementation for runtimes that need one. |
|
|
71
71
|
| `tokenStorage` | Secure storage adapter for SDK-created customer session tokens. |
|
|
72
|
+
| `cacheStorage` | Optional durable cache adapter used by SDK features such as native file-picker batch recovery. |
|
|
73
|
+
| `filePicker` | Optional native picker adapter created with `createSagepilotFilePicker(...)` for camera, gallery, and document attachments. |
|
|
74
|
+
| `fileStore` | Optional native file-store adapter created with `createSagepilotFileStore(...)` for durable picked-file bytes. |
|
|
72
75
|
| `anonymousId` | Optional stable anonymous identifier sent when the SDK creates a customer session. |
|
|
73
76
|
| `metadata` | Optional app metadata merged into the session creation payload. |
|
|
74
77
|
| `deviceInfo` | Optional adapter that returns device metadata to include during session creation. |
|
|
@@ -178,6 +181,89 @@ export function ChatLauncher() {
|
|
|
178
181
|
}
|
|
179
182
|
```
|
|
180
183
|
|
|
184
|
+
## Push Notifications With Webhooks
|
|
185
|
+
|
|
186
|
+
The React Native SDK does not register APNs/FCM device tokens and does not send operating-system push notifications directly. Use Sagepilot webhooks to connect new support messages to your own push-notification system.
|
|
187
|
+
|
|
188
|
+
Recommended flow:
|
|
189
|
+
|
|
190
|
+
1. Register and store the app user's push token in your backend.
|
|
191
|
+
2. Configure a Sagepilot webhook subscription for `support.message.created`.
|
|
192
|
+
3. When your webhook receives a new customer-visible support message, map the webhook's customer/conversation data to your app user.
|
|
193
|
+
4. Send the push notification from your backend through APNs, FCM, or your notification provider.
|
|
194
|
+
|
|
195
|
+
The webhook event includes conversation, customer, message, and SDK context so your backend can decide whether to notify the user. Keep push-token storage, notification preferences, and "user is currently viewing chat" suppression logic in your app/backend.
|
|
196
|
+
|
|
197
|
+
## Native File Picker (Strongly Recommended)
|
|
198
|
+
|
|
199
|
+
By default, the hosted chat widget's attach button uses the WebView's `<input type="file">`. On low-RAM Android devices this is unreliable: while the system camera is in the foreground, Android can kill the WebView render process, leaving a dead white screen and losing the captured photo.
|
|
200
|
+
|
|
201
|
+
Configure `filePicker` so the attach button uses native pickers instead. Captured files are delivered to the widget over a hardened bridge (protocol v2): they are held in app memory **and** mirrored to `cacheStorage`, then **re-delivered until the widget acknowledges them** — surviving the WebView hydration race, renderer crashes, and (for small batches) full app-process death. On Android the SDK also runs a liveness watchdog on app-resume that repaints and, if the widget is confirmed dead, remounts the WebView — healing the blank-white-screen-after-camera case that no reload-on-crash handler can catch.
|
|
202
|
+
|
|
203
|
+
For the strongest resilience (a photo captured right before the OS kills the app survives the restart, within the widget's upload limits), pass BOTH `cacheStorage` (stores the tiny manifest) and `fileStore` (stores the bytes on disk). `cacheStorage` alone keeps only small (≤~2MB) batches durable, because Android's AsyncStorage caps a single row near 2MB; `fileStore` removes that durability bottleneck by writing bytes to an app-private file and persisting only file keys in the manifest. The hosted widget still enforces the final upload limits listed below.
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
207
|
+
import * as imagePicker from "react-native-image-picker";
|
|
208
|
+
import * as documentsPicker from "@react-native-documents/picker";
|
|
209
|
+
import ReactNativeBlobUtil from "react-native-blob-util";
|
|
210
|
+
import {
|
|
211
|
+
createAsyncStorageCacheStorage,
|
|
212
|
+
createSagepilotFilePicker,
|
|
213
|
+
createSagepilotFileStore
|
|
214
|
+
} from "@sagepilot-ai/react-native-sdk";
|
|
215
|
+
|
|
216
|
+
await SagepilotChat.configure({
|
|
217
|
+
key: "workspace_id:channel_id",
|
|
218
|
+
cacheStorage: createAsyncStorageCacheStorage(AsyncStorage),
|
|
219
|
+
fileStore: createSagepilotFileStore(ReactNativeBlobUtil),
|
|
220
|
+
filePicker: createSagepilotFilePicker({ imagePicker, documentsPicker, fileReader: ReactNativeBlobUtil })
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Install the optional dependencies your app needs:
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
# Camera and photo gallery (required for the native picker)
|
|
228
|
+
npm install react-native-image-picker
|
|
229
|
+
|
|
230
|
+
# Document/file picking (optional)
|
|
231
|
+
npm install @react-native-documents/picker
|
|
232
|
+
|
|
233
|
+
# Reliable document reads (optional, recommended with the documents picker)
|
|
234
|
+
npm install react-native-blob-util
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Then pass the modules to `createSagepilotFilePicker`:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import * as imagePicker from "react-native-image-picker";
|
|
241
|
+
import * as documentsPicker from "@react-native-documents/picker";
|
|
242
|
+
import ReactNativeBlobUtil from "react-native-blob-util";
|
|
243
|
+
import {
|
|
244
|
+
SagepilotChat,
|
|
245
|
+
createSagepilotFilePicker
|
|
246
|
+
} from "@sagepilot-ai/react-native-sdk";
|
|
247
|
+
|
|
248
|
+
await SagepilotChat.configure({
|
|
249
|
+
key: "workspace_id:channel_id",
|
|
250
|
+
filePicker: createSagepilotFilePicker({
|
|
251
|
+
imagePicker,
|
|
252
|
+
documentsPicker,
|
|
253
|
+
fileReader: ReactNativeBlobUtil
|
|
254
|
+
})
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Notes:
|
|
259
|
+
|
|
260
|
+
- Camera and gallery images are downscaled natively (1920px longest edge, JPEG quality 0.8 by default; override with `imageMaxDimension` / `imageQuality`). This keeps memory and upload sizes low on constrained devices.
|
|
261
|
+
- The hosted widget enforces the final attachment limits for both WebView and native picker flows: up to 5 files at a time, total previewable images up to 5MB, and total non-image files up to 10MB.
|
|
262
|
+
- The native picker also has earlier safety guards before files are read into memory: gallery multi-select is capped at 5 by default (`imageSelectionLimit`), documents are rejected above 20MB (`documentMaxFileSizeBytes`), and images above 15MB (`imageMaxFileSizeBytes`). These higher guards prevent out-of-memory crashes; they do not raise the hosted widget's final upload limits.
|
|
263
|
+
- Failures are never silent: the picker distinguishes user-cancel from real errors and surfaces a typed `code` (`permission_denied`, `camera_unavailable`, `encode_failed`, `file_too_large`, `read_failed`) to the widget.
|
|
264
|
+
- Android apps that declare the `CAMERA` permission in their manifest must grant it at runtime; the SDK requests it automatically before opening the camera and surfaces a clear message (with a Settings hint) when it is denied. iOS apps need `NSCameraUsageDescription` and `NSPhotoLibraryUsageDescription` in `Info.plist`.
|
|
265
|
+
- Apps that skip `filePicker` keep the WebView file input. The SDK still recovers from WebView renderer crashes by reloading the widget, but a photo captured at crash time cannot be restored in that mode.
|
|
266
|
+
|
|
181
267
|
## Secure Session Storage
|
|
182
268
|
|
|
183
269
|
The SDK creates opaque customer session tokens at runtime. These tokens are not exposed through hooks or public session APIs.
|
|
@@ -313,12 +399,34 @@ SagepilotChat.present();
|
|
|
313
399
|
SagepilotChat.presentMessages();
|
|
314
400
|
SagepilotChat.presentMessageComposer("I need help with my order");
|
|
315
401
|
SagepilotChat.presentMessageComposer("Start a new request", { mode: "new" });
|
|
402
|
+
SagepilotChat.presentMessageComposer("I need help with this order", { chatId: savedChatId });
|
|
316
403
|
```
|
|
317
404
|
|
|
318
405
|
`presentMessageComposer(message)` pre-fills the composer and lets Sagepilot reuse an existing conversation when one is available. If there is no existing conversation, Sagepilot starts a new one when the customer sends the message.
|
|
319
406
|
|
|
320
407
|
Use `{ mode: "new" }` only when the button or workflow should always start a fresh conversation.
|
|
321
408
|
|
|
409
|
+
Pass `{ chatId }` when an app-owned surface should reopen a specific conversation, such as an order help button. The SDK sends this as the hosted widget `chat_id` and keeps the existing no-`chatId` behavior unchanged.
|
|
410
|
+
|
|
411
|
+
To store the chat id when a customer creates a conversation from a screen-specific composer:
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
SagepilotChat.presentMessageComposer("I need help with this order", {
|
|
415
|
+
metadata: { orderId: "10212984" },
|
|
416
|
+
onConversationCreated: ({ chat_id, metadata }) => {
|
|
417
|
+
// Store metadata.orderId -> chat_id in your app.
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
You can also subscribe globally:
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
const unsubscribe = SagepilotChat.onConversationCreated(({ chat_id }) => {
|
|
426
|
+
// Persist chat_id for later use.
|
|
427
|
+
});
|
|
428
|
+
```
|
|
429
|
+
|
|
322
430
|
## Identity
|
|
323
431
|
|
|
324
432
|
Call `identify()` when you know the signed-in app user.
|
|
@@ -380,11 +488,12 @@ await SagepilotChat.destroy();
|
|
|
380
488
|
|
|
381
489
|
- `SagepilotChat.present()`: opens the hosted chat home screen.
|
|
382
490
|
- `SagepilotChat.presentMessages()`: opens the hosted conversations/messages screen.
|
|
383
|
-
- `SagepilotChat.presentMessageComposer(message?, options?)`: opens the message composer and optionally pre-fills the composer text. By default, Sagepilot reuses an existing conversation when one is available. Pass `{ mode: "new" }` to force a fresh conversation. It does not auto-send.
|
|
491
|
+
- `SagepilotChat.presentMessageComposer(message?, options?)`: opens the message composer and optionally pre-fills the composer text. By default, Sagepilot reuses an existing conversation when one is available. Pass `{ mode: "new" }` to force a fresh conversation, or `{ chatId }` to open a specific conversation. It does not auto-send.
|
|
384
492
|
- `SagepilotChat.dismiss()`: closes the hosted chat modal.
|
|
385
493
|
- `SagepilotChat.hide()`: alias for `dismiss()`.
|
|
386
494
|
- `SagepilotChat.toggle()`: opens the chat when closed and closes it when open.
|
|
387
495
|
- `SagepilotChat.isPresented()`: returns whether the hosted chat modal is open.
|
|
496
|
+
- `SagepilotChat.onConversationCreated(callback)`: subscribes to hosted conversations created from the React Native widget and returns `chat_id` plus any composer metadata.
|
|
388
497
|
- `SagepilotChatProvider`: renders the hosted Sagepilot chat inside a React Native modal WebView.
|
|
389
498
|
|
|
390
499
|
When `SagepilotChatProvider` is mounted, the SDK preloads a hidden hosted WebView after `configure()` so the first visible open can reuse warmed network/cache state. Disable this with `behavior.preloadWebView: false`.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,191 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Wire shape delivered to the hosted widget. Matches the Customer API
|
|
5
|
+
* attachment input (`data_base64`) so the widget can reuse its existing
|
|
6
|
+
* validation and send path without backend changes.
|
|
7
|
+
*/
|
|
8
|
+
type SagepilotPickedFile = {
|
|
9
|
+
file_name: string;
|
|
10
|
+
mime_type: string;
|
|
11
|
+
size: number;
|
|
12
|
+
data_base64: string;
|
|
13
|
+
};
|
|
14
|
+
type SagepilotFilePickerSource = "camera" | "library" | "documents";
|
|
15
|
+
type SagepilotFilePickerRequest = {
|
|
16
|
+
source: SagepilotFilePickerSource;
|
|
17
|
+
multiple: boolean;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Host-app-provided native file picker. When configured, the hosted widget's
|
|
21
|
+
* attach button routes through native pickers instead of the WebView's
|
|
22
|
+
* `<input type="file">`. This is required for reliable camera capture on
|
|
23
|
+
* low-RAM Android devices: the OS can kill the WebView render process while
|
|
24
|
+
* the camera activity is foregrounded, which destroys any in-WebView file
|
|
25
|
+
* chooser state. Natively captured files survive that kill and are
|
|
26
|
+
* re-delivered after the WebView reloads.
|
|
27
|
+
*/
|
|
28
|
+
type SagepilotFilePickerAdapter = {
|
|
29
|
+
/** Sources offered to the customer in the attachment chooser. */
|
|
30
|
+
sources: SagepilotFilePickerSource[];
|
|
31
|
+
/** Resolves picked files, or an empty array when the customer cancels. */
|
|
32
|
+
pickFiles: (request: SagepilotFilePickerRequest) => Promise<SagepilotPickedFile[]>;
|
|
33
|
+
};
|
|
34
|
+
/** Structural subset of `react-native-image-picker` used by the SDK. */
|
|
35
|
+
type SagepilotImagePickerModule = {
|
|
36
|
+
launchCamera: (options: Record<string, unknown>) => Promise<SagepilotImagePickerResult>;
|
|
37
|
+
launchImageLibrary: (options: Record<string, unknown>) => Promise<SagepilotImagePickerResult>;
|
|
38
|
+
};
|
|
39
|
+
type SagepilotImagePickerResult = {
|
|
40
|
+
didCancel?: boolean;
|
|
41
|
+
errorCode?: string;
|
|
42
|
+
errorMessage?: string;
|
|
43
|
+
assets?: Array<{
|
|
44
|
+
fileName?: string | null;
|
|
45
|
+
type?: string | null;
|
|
46
|
+
fileSize?: number | null;
|
|
47
|
+
base64?: string | null;
|
|
48
|
+
uri?: string | null;
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
/** Structural subset of `@react-native-documents/picker` used by the SDK. */
|
|
52
|
+
type SagepilotDocumentsPickerModule = {
|
|
53
|
+
pick: (options?: Record<string, unknown>) => Promise<Array<{
|
|
54
|
+
uri: string;
|
|
55
|
+
name: string | null;
|
|
56
|
+
size: number | null;
|
|
57
|
+
type: string | null;
|
|
58
|
+
}>>;
|
|
59
|
+
keepLocalCopy?: (options: {
|
|
60
|
+
files: Array<{
|
|
61
|
+
uri: string;
|
|
62
|
+
fileName: string;
|
|
63
|
+
}>;
|
|
64
|
+
destination: "cachesDirectory" | "documentDirectory";
|
|
65
|
+
}) => Promise<Array<{
|
|
66
|
+
status: string;
|
|
67
|
+
sourceUri: string;
|
|
68
|
+
localUri?: string;
|
|
69
|
+
copyError?: string;
|
|
70
|
+
}>>;
|
|
71
|
+
};
|
|
72
|
+
/** Structural subset of `react-native-blob-util` used for document reads. */
|
|
73
|
+
type SagepilotFileReaderModule = {
|
|
74
|
+
fs: {
|
|
75
|
+
readFile: (path: string, encoding: string) => Promise<unknown>;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
type SagepilotCreateFilePickerOptions = {
|
|
79
|
+
imagePicker?: SagepilotImagePickerModule;
|
|
80
|
+
documentsPicker?: SagepilotDocumentsPickerModule;
|
|
81
|
+
/** Optional `react-native-blob-util` for reliable document reads. */
|
|
82
|
+
fileReader?: SagepilotFileReaderModule;
|
|
83
|
+
/**
|
|
84
|
+
* Longest image edge in pixels for camera/library picks. Downscaling
|
|
85
|
+
* natively keeps memory and upload size low on low-RAM devices.
|
|
86
|
+
*/
|
|
87
|
+
imageMaxDimension?: number;
|
|
88
|
+
/** JPEG quality (0-1) applied to camera/library picks. */
|
|
89
|
+
imageQuality?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Max images per gallery selection. Defaults to 5. Set 0 for unlimited
|
|
92
|
+
* (not recommended: a large batch produces a multi-megabyte bridge payload).
|
|
93
|
+
*/
|
|
94
|
+
imageSelectionLimit?: number;
|
|
95
|
+
/** Reject documents larger than this (bytes) before reading them. */
|
|
96
|
+
documentMaxFileSizeBytes?: number;
|
|
97
|
+
/** Reject camera/library images larger than this (bytes). */
|
|
98
|
+
imageMaxFileSizeBytes?: number;
|
|
99
|
+
};
|
|
100
|
+
type SagepilotFilePickerErrorCode = "permission_denied" | "camera_unavailable" | "encode_failed" | "file_too_large" | "read_failed" | "unknown";
|
|
101
|
+
/**
|
|
102
|
+
* Structured picker failure. The provider maps `.code` to a user-facing message
|
|
103
|
+
* and surfaces it via sagepilot:file_picker_error so a failed capture is never
|
|
104
|
+
* silently swallowed (the historical bug: cancel, OEM cancel-misreport, and
|
|
105
|
+
* base64-encode failure all collapsed into an empty array indistinguishable
|
|
106
|
+
* from a real user cancel).
|
|
107
|
+
*/
|
|
108
|
+
declare class SagepilotFilePickerError extends Error {
|
|
109
|
+
readonly code: SagepilotFilePickerErrorCode;
|
|
110
|
+
constructor(code: SagepilotFilePickerErrorCode, message: string);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Builds the SDK file picker adapter from host-app-installed native modules.
|
|
114
|
+
* Modules are passed in (rather than required by the SDK) so Metro never
|
|
115
|
+
* tries to resolve packages the host app has not installed.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* import * as imagePicker from "react-native-image-picker";
|
|
119
|
+
* import * as documentsPicker from "@react-native-documents/picker";
|
|
120
|
+
*
|
|
121
|
+
* SagepilotChat.configure({
|
|
122
|
+
* key: "...",
|
|
123
|
+
* filePicker: createSagepilotFilePicker({ imagePicker, documentsPicker })
|
|
124
|
+
* });
|
|
125
|
+
*/
|
|
126
|
+
declare function createSagepilotFilePicker(options: SagepilotCreateFilePickerOptions): SagepilotFilePickerAdapter | undefined;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Durable file outbox for natively-picked attachments.
|
|
130
|
+
*
|
|
131
|
+
* Persisting attachment BYTES in `cacheStorage` (AsyncStorage) does not scale:
|
|
132
|
+
* Android's SQLite-backed store caps a single row at ~2MB (CursorWindow), so a
|
|
133
|
+
* multi-megabyte capture cannot be stored there at all. Instead we write the
|
|
134
|
+
* bytes to a stable app-private file and persist only a tiny manifest of file
|
|
135
|
+
* keys + metadata in `cacheStorage`. This makes durability size-independent and
|
|
136
|
+
* lets a capture survive a full app-process death during the camera round-trip.
|
|
137
|
+
*
|
|
138
|
+
* The store is built from the host app's `react-native-blob-util` so the SDK
|
|
139
|
+
* itself depends on no filesystem package (same injection pattern as the
|
|
140
|
+
* native file picker).
|
|
141
|
+
*/
|
|
142
|
+
/** Structural subset of `react-native-blob-util` used by the file store. */
|
|
143
|
+
type SagepilotBlobUtilLike = {
|
|
144
|
+
fs: {
|
|
145
|
+
dirs: {
|
|
146
|
+
DocumentDir: string;
|
|
147
|
+
};
|
|
148
|
+
writeFile: (path: string, data: string, encoding: string) => Promise<unknown>;
|
|
149
|
+
readFile: (path: string, encoding: string) => Promise<unknown>;
|
|
150
|
+
unlink: (path: string) => Promise<unknown>;
|
|
151
|
+
exists: (path: string) => Promise<boolean>;
|
|
152
|
+
ls: (path: string) => Promise<string[]>;
|
|
153
|
+
mkdir: (path: string) => Promise<unknown>;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Persists attachment bytes to disk under stable, app-private keys. Keys (not
|
|
158
|
+
* absolute paths) are stored in the manifest so persistence survives the
|
|
159
|
+
* platform relocating the app container between launches (notably iOS).
|
|
160
|
+
*/
|
|
161
|
+
type SagepilotPersistedFileStore = {
|
|
162
|
+
/** Writes base64 content under `key`; resolves once durably on disk. */
|
|
163
|
+
write: (key: string, base64: string) => Promise<void>;
|
|
164
|
+
/** Reads previously-written content back as base64. */
|
|
165
|
+
read: (key: string) => Promise<string>;
|
|
166
|
+
/** Best-effort delete of a single key. */
|
|
167
|
+
remove: (key: string) => Promise<void>;
|
|
168
|
+
/** Best-effort delete of every stored file whose key is not in `keepKeys`. */
|
|
169
|
+
prune: (keepKeys: string[]) => Promise<void>;
|
|
170
|
+
};
|
|
171
|
+
type SagepilotCreateFileStoreOptions = {
|
|
172
|
+
/** Sub-directory under DocumentDir. Defaults to "sagepilot-attachments". */
|
|
173
|
+
directoryName?: string;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Builds a {@link SagepilotPersistedFileStore} backed by react-native-blob-util.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* import ReactNativeBlobUtil from "react-native-blob-util";
|
|
180
|
+
* SagepilotChat.configure({
|
|
181
|
+
* key: "...",
|
|
182
|
+
* cacheStorage: createAsyncStorageCacheStorage(AsyncStorage),
|
|
183
|
+
* fileStore: createSagepilotFileStore(ReactNativeBlobUtil),
|
|
184
|
+
* filePicker: createSagepilotFilePicker({ imagePicker, documentsPicker, fileReader: ReactNativeBlobUtil })
|
|
185
|
+
* });
|
|
186
|
+
*/
|
|
187
|
+
declare function createSagepilotFileStore(blobUtil: SagepilotBlobUtilLike, options?: SagepilotCreateFileStoreOptions): SagepilotPersistedFileStore;
|
|
188
|
+
|
|
3
189
|
type SagepilotMobilePresentationStyle = "sheet" | "fullScreen" | "push";
|
|
4
190
|
type SagepilotMobileColorScheme = "system" | "light" | "dark";
|
|
5
191
|
type SagepilotChatErrorCode = "invalid_config" | "not_initialized" | "already_initialized" | "network_error" | "invalid_response" | "invalid_request" | "unauthorized" | "forbidden" | "session_not_found" | "session_expired" | "channel_not_found" | "conversation_not_found" | "message_not_found" | "contact_required" | "otp_required" | "otp_invalid" | "otp_expired" | "identity_verification_failed" | "rate_limited" | "idempotency_conflict" | "not_implemented" | "internal_error";
|
|
@@ -110,6 +296,21 @@ type SagepilotMobileConfig = {
|
|
|
110
296
|
cacheStorage?: SagepilotCacheStorage;
|
|
111
297
|
deviceInfo?: SagepilotDeviceInfoAdapter;
|
|
112
298
|
biometrics?: SagepilotBiometricsAdapter;
|
|
299
|
+
/**
|
|
300
|
+
* Native file picker used by the hosted chat widget's attach button.
|
|
301
|
+
* Strongly recommended: in-WebView camera capture is unreliable on
|
|
302
|
+
* low-RAM Android devices (the OS can kill the WebView render process
|
|
303
|
+
* while the camera is open). Build with createSagepilotFilePicker().
|
|
304
|
+
*/
|
|
305
|
+
filePicker?: SagepilotFilePickerAdapter;
|
|
306
|
+
/**
|
|
307
|
+
* Durable on-disk store for picked attachments. When provided, a captured
|
|
308
|
+
* photo/document survives a full app-process death during the camera
|
|
309
|
+
* round-trip (the SDK rehydrates and re-delivers it on relaunch), with no
|
|
310
|
+
* size limit. Without it, durability falls back to small batches stored in
|
|
311
|
+
* cacheStorage. Build with createSagepilotFileStore(ReactNativeBlobUtil).
|
|
312
|
+
*/
|
|
313
|
+
fileStore?: SagepilotPersistedFileStore;
|
|
113
314
|
presentation?: SagepilotMobilePresentationConfig;
|
|
114
315
|
theme?: SagepilotMobileThemeConfig;
|
|
115
316
|
behavior?: SagepilotMobileBehaviorConfig;
|
|
@@ -217,6 +418,16 @@ type SagepilotMobileState = SagepilotLifecycleState & {
|
|
|
217
418
|
};
|
|
218
419
|
type SagepilotMobileSubscription = () => void;
|
|
219
420
|
type SagepilotMessageComposerMode = "auto" | "new";
|
|
421
|
+
type SagepilotConversationCreatedEvent = {
|
|
422
|
+
chat_id: string;
|
|
423
|
+
workspace_id?: string;
|
|
424
|
+
channel_id?: string;
|
|
425
|
+
session_id?: string;
|
|
426
|
+
customer_id?: string;
|
|
427
|
+
message_id?: string;
|
|
428
|
+
created_at?: string;
|
|
429
|
+
metadata?: Record<string, unknown>;
|
|
430
|
+
};
|
|
220
431
|
type SagepilotMessageComposerOptions = {
|
|
221
432
|
/**
|
|
222
433
|
* auto: prefill the composer and let the hosted widget reuse an existing
|
|
@@ -224,6 +435,20 @@ type SagepilotMessageComposerOptions = {
|
|
|
224
435
|
* new: force the hosted widget to start a fresh conversation.
|
|
225
436
|
*/
|
|
226
437
|
mode?: SagepilotMessageComposerMode;
|
|
438
|
+
/**
|
|
439
|
+
* Opens a specific hosted conversation. This value is treated as an opaque
|
|
440
|
+
* conversation handle and is verified by the hosted customer session.
|
|
441
|
+
*/
|
|
442
|
+
chatId?: string;
|
|
443
|
+
/**
|
|
444
|
+
* App-owned metadata returned to the conversation-created callback so callers
|
|
445
|
+
* can persist mappings such as order_id -> chat_id.
|
|
446
|
+
*/
|
|
447
|
+
metadata?: Record<string, unknown>;
|
|
448
|
+
/**
|
|
449
|
+
* Fires when this composer invocation creates a new conversation.
|
|
450
|
+
*/
|
|
451
|
+
onConversationCreated?: (event: SagepilotConversationCreatedEvent) => void;
|
|
227
452
|
};
|
|
228
453
|
type SagepilotChatHookState = {
|
|
229
454
|
configured: boolean;
|
|
@@ -240,6 +465,7 @@ type SagepilotChatHookState = {
|
|
|
240
465
|
identify: (identity: SagepilotIdentity) => Promise<SagepilotIdentifyResult>;
|
|
241
466
|
logout: () => Promise<SagepilotLogoutResponse>;
|
|
242
467
|
getUnreadCount: () => Promise<number>;
|
|
468
|
+
onConversationCreated: (callback: (event: SagepilotConversationCreatedEvent) => void) => SagepilotMobileSubscription;
|
|
243
469
|
};
|
|
244
470
|
type SagepilotChatProviderProps = {
|
|
245
471
|
children?: ReactNode;
|
|
@@ -259,6 +485,7 @@ declare const SagepilotChat: {
|
|
|
259
485
|
pending: boolean;
|
|
260
486
|
};
|
|
261
487
|
onIdentify: (callback: Callback<SagepilotIdentifyResult>) => SagepilotMobileSubscription;
|
|
488
|
+
onConversationCreated: (callback: Callback<SagepilotConversationCreatedEvent>) => SagepilotMobileSubscription;
|
|
262
489
|
getUnreadCount: () => Promise<number>;
|
|
263
490
|
onUnreadChange: (callback: Callback<SagepilotUnreadState>) => SagepilotMobileSubscription;
|
|
264
491
|
startUnreadPolling: (intervalMs?: number) => () => void;
|
|
@@ -316,4 +543,4 @@ declare function SagepilotChatProvider({ children, closeLabel, loadingLabel }: S
|
|
|
316
543
|
|
|
317
544
|
declare function useSagepilotChat(): SagepilotChatHookState;
|
|
318
545
|
|
|
319
|
-
export { type SagepilotBiometricsAdapter, type SagepilotBootstrapChannelResponse, type SagepilotCacheStorage, SagepilotChat, SagepilotChatError, type SagepilotChatErrorCode, type SagepilotChatHookState, SagepilotChatProvider, type SagepilotChatProviderProps, type SagepilotDeviceInfo, type SagepilotDeviceInfoAdapter, type SagepilotIdentifyResult, type SagepilotIdentity, type SagepilotIdentityState, type SagepilotLifecycleState, type SagepilotLogoutResponse, type SagepilotMobileBehaviorConfig, type SagepilotMobileColorScheme, type SagepilotMobileConfig, type SagepilotMobileConfigureResult, type SagepilotMobileLauncherConfig, type SagepilotMobilePresentationConfig, type SagepilotMobilePresentationStyle, type SagepilotMobileResolvedLauncherConfig, type SagepilotMobileState, type SagepilotMobileSubscription, type SagepilotMobileThemeConfig, type SagepilotMobileThemeState, type SagepilotSessionState, type SagepilotTokenStorage, type SagepilotUnreadState, createAsyncStorageCacheStorage, createKeychainTokenStorage, useSagepilotChat };
|
|
546
|
+
export { type SagepilotBiometricsAdapter, type SagepilotBlobUtilLike, type SagepilotBootstrapChannelResponse, type SagepilotCacheStorage, SagepilotChat, SagepilotChatError, type SagepilotChatErrorCode, type SagepilotChatHookState, SagepilotChatProvider, type SagepilotChatProviderProps, type SagepilotConversationCreatedEvent, type SagepilotCreateFilePickerOptions, type SagepilotCreateFileStoreOptions, type SagepilotDeviceInfo, type SagepilotDeviceInfoAdapter, type SagepilotDocumentsPickerModule, type SagepilotFilePickerAdapter, SagepilotFilePickerError, type SagepilotFilePickerErrorCode, type SagepilotFilePickerRequest, type SagepilotFilePickerSource, type SagepilotFileReaderModule, type SagepilotIdentifyResult, type SagepilotIdentity, type SagepilotIdentityState, type SagepilotImagePickerModule, type SagepilotImagePickerResult, type SagepilotLifecycleState, type SagepilotLogoutResponse, type SagepilotMessageComposerOptions, type SagepilotMobileBehaviorConfig, type SagepilotMobileColorScheme, type SagepilotMobileConfig, type SagepilotMobileConfigureResult, type SagepilotMobileLauncherConfig, type SagepilotMobilePresentationConfig, type SagepilotMobilePresentationStyle, type SagepilotMobileResolvedLauncherConfig, type SagepilotMobileState, type SagepilotMobileSubscription, type SagepilotMobileThemeConfig, type SagepilotMobileThemeState, type SagepilotPersistedFileStore, type SagepilotPickedFile, type SagepilotSessionState, type SagepilotTokenStorage, type SagepilotUnreadState, createAsyncStorageCacheStorage, createKeychainTokenStorage, createSagepilotFilePicker, createSagepilotFileStore, useSagepilotChat };
|