@sagepilot-ai/react-native-sdk 0.2.2 → 0.2.4

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
@@ -38,7 +38,7 @@ You configure the SDK with:
38
38
  - `tokenStorage`: secure storage adapter for SDK-created customer session tokens
39
39
  - optional request, presentation, and behavior settings
40
40
 
41
- For normal Sagepilot cloud usage, omit `host`; API calls and the WebView default to `https://app.sagepilot.ai`.
41
+ For standard Sagepilot cloud usage, omit `host`. The SDK connects to Sagepilot's hosted endpoints automatically. Set `host` only when Sagepilot provides a dedicated endpoint for your workspace.
42
42
 
43
43
  ```ts
44
44
  await SagepilotChat.configure({
@@ -46,7 +46,7 @@ await SagepilotChat.configure({
46
46
  });
47
47
  ```
48
48
 
49
- If your workspace uses a dedicated host:
49
+ If Sagepilot provides a dedicated endpoint for your workspace, pass it as an optional override:
50
50
 
51
51
  ```ts
52
52
  await SagepilotChat.configure({
@@ -64,8 +64,8 @@ These options are part of the supported React Native SDK configuration contract.
64
64
  | Option | Purpose |
65
65
  |---|---|
66
66
  | `key` | Public routing key in the format `workspace_id:channel_id`. |
67
- | `host` | Sagepilot app/API host override. Most apps should omit this. |
68
- | `widgetHost` | Optional hosted widget origin override for development or dedicated widget routing. Most apps should omit this. |
67
+ | `host` | Optional Sagepilot host override. Most apps should omit this unless Sagepilot provides a dedicated endpoint. |
68
+ | `widgetHost` | Optional hosted widget origin override. Use only when Sagepilot provides a separate widget endpoint. |
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. |
@@ -83,7 +83,7 @@ These options are part of the supported React Native SDK configuration contract.
83
83
  | `behavior.enableUnreadPolling` | Starts or disables automatic unread-count polling after configuration. |
84
84
  | `behavior.unreadPollIntervalMs` | Sets the unread polling interval in milliseconds. |
85
85
 
86
- `SagepilotChatProvider` renders the Sagepilot-hosted chat UI in a React Native WebView. Most Sagepilot cloud apps do not need any host override.
86
+ `SagepilotChatProvider` renders the Sagepilot-hosted chat UI in a React Native WebView. Most apps do not need any host override.
87
87
 
88
88
  ### Chat Launcher Theme
89
89
 
@@ -312,6 +312,33 @@ Other open helpers:
312
312
  SagepilotChat.present();
313
313
  SagepilotChat.presentMessages();
314
314
  SagepilotChat.presentMessageComposer("I need help with my order");
315
+ SagepilotChat.presentMessageComposer("Start a new request", { mode: "new" });
316
+ SagepilotChat.presentMessageComposer("I need help with this order", { chatId: savedChatId });
317
+ ```
318
+
319
+ `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.
320
+
321
+ Use `{ mode: "new" }` only when the button or workflow should always start a fresh conversation.
322
+
323
+ 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.
324
+
325
+ To store the chat id when a customer creates a conversation from a screen-specific composer:
326
+
327
+ ```ts
328
+ SagepilotChat.presentMessageComposer("I need help with this order", {
329
+ metadata: { orderId: "10212984" },
330
+ onConversationCreated: ({ chat_id, metadata }) => {
331
+ // Store metadata.orderId -> chat_id in your app.
332
+ }
333
+ });
334
+ ```
335
+
336
+ You can also subscribe globally:
337
+
338
+ ```ts
339
+ const unsubscribe = SagepilotChat.onConversationCreated(({ chat_id }) => {
340
+ // Persist chat_id for later use.
341
+ });
315
342
  ```
316
343
 
317
344
  ## Identity
@@ -375,11 +402,12 @@ await SagepilotChat.destroy();
375
402
 
376
403
  - `SagepilotChat.present()`: opens the hosted chat home screen.
377
404
  - `SagepilotChat.presentMessages()`: opens the hosted conversations/messages screen.
378
- - `SagepilotChat.presentMessageComposer(message?)`: opens a fresh message composer and optionally pre-fills the composer text. It does not auto-send.
405
+ - `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.
379
406
  - `SagepilotChat.dismiss()`: closes the hosted chat modal.
380
407
  - `SagepilotChat.hide()`: alias for `dismiss()`.
381
408
  - `SagepilotChat.toggle()`: opens the chat when closed and closes it when open.
382
409
  - `SagepilotChat.isPresented()`: returns whether the hosted chat modal is open.
410
+ - `SagepilotChat.onConversationCreated(callback)`: subscribes to hosted conversations created from the React Native widget and returns `chat_id` plus any composer metadata.
383
411
  - `SagepilotChatProvider`: renders the hosted Sagepilot chat inside a React Native modal WebView.
384
412
 
385
413
  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`.
@@ -0,0 +1,53 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+ }
7
+
8
+ plugins {
9
+ id "com.android.library"
10
+ }
11
+
12
+ def isNewArchitectureEnabled() {
13
+ return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
14
+ }
15
+
16
+ if (isNewArchitectureEnabled()) {
17
+ apply plugin: "com.facebook.react"
18
+ }
19
+
20
+ def safeExtGet(prop, fallback) {
21
+ return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
22
+ }
23
+
24
+ android {
25
+ namespace "ai.sagepilot.reactnativesdk"
26
+ compileSdkVersion safeExtGet("compileSdkVersion", 35)
27
+
28
+ defaultConfig {
29
+ minSdkVersion safeExtGet("minSdkVersion", 24)
30
+ targetSdkVersion safeExtGet("targetSdkVersion", 35)
31
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
32
+ }
33
+
34
+ compileOptions {
35
+ sourceCompatibility JavaVersion.VERSION_17
36
+ targetCompatibility JavaVersion.VERSION_17
37
+ }
38
+
39
+ sourceSets.main {
40
+ java {
41
+ if (isNewArchitectureEnabled()) {
42
+ srcDirs += "${project.buildDir}/generated/source/codegen/java"
43
+ } else {
44
+ srcDirs += "src/paper/java"
45
+ }
46
+ }
47
+ }
48
+ }
49
+
50
+ dependencies {
51
+ implementation "com.facebook.react:react-android"
52
+ implementation "androidx.core:core:1.13.1"
53
+ }
@@ -0,0 +1 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
@@ -0,0 +1,26 @@
1
+ package ai.sagepilot.reactnativesdk;
2
+
3
+ import androidx.annotation.Nullable;
4
+ import com.facebook.react.bridge.WritableMap;
5
+ import com.facebook.react.uimanager.events.Event;
6
+
7
+ public class SagepilotInsetsChangeEvent extends Event<SagepilotInsetsChangeEvent> {
8
+ public static final String EVENT_NAME = "topInsetsChange";
9
+ private final WritableMap eventData;
10
+
11
+ public SagepilotInsetsChangeEvent(int surfaceId, int viewTag, WritableMap eventData) {
12
+ super(surfaceId, viewTag);
13
+ this.eventData = eventData;
14
+ }
15
+
16
+ @Override
17
+ public String getEventName() {
18
+ return EVENT_NAME;
19
+ }
20
+
21
+ @Nullable
22
+ @Override
23
+ protected WritableMap getEventData() {
24
+ return eventData;
25
+ }
26
+ }
@@ -0,0 +1,78 @@
1
+ package ai.sagepilot.reactnativesdk;
2
+
3
+ import android.content.Context;
4
+ import android.view.View;
5
+ import android.widget.FrameLayout;
6
+ import com.facebook.react.bridge.Arguments;
7
+ import com.facebook.react.bridge.ReactContext;
8
+ import com.facebook.react.bridge.WritableMap;
9
+ import com.facebook.react.uimanager.PixelUtil;
10
+ import com.facebook.react.uimanager.UIManagerHelper;
11
+ import com.facebook.react.uimanager.events.EventDispatcher;
12
+ import androidx.core.graphics.Insets;
13
+ import androidx.core.view.ViewCompat;
14
+ import androidx.core.view.WindowInsetsCompat;
15
+
16
+ public class SagepilotInsetsView extends FrameLayout {
17
+ private int appliedTopInset;
18
+ private int appliedBottomInset;
19
+
20
+ public SagepilotInsetsView(Context context) {
21
+ super(context);
22
+ installInsetsListener();
23
+ }
24
+
25
+ @Override
26
+ protected void onAttachedToWindow() {
27
+ super.onAttachedToWindow();
28
+ ViewCompat.requestApplyInsets(this);
29
+ }
30
+
31
+ private void installInsetsListener() {
32
+ ViewCompat.setOnApplyWindowInsetsListener(this, (view, windowInsets) -> {
33
+ Insets imeInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime());
34
+ Insets systemBarInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
35
+ int topInset = systemBarInsets.top;
36
+ int bottomInset = Math.max(imeInsets.bottom, systemBarInsets.bottom);
37
+ updateInsets(topInset, bottomInset, imeInsets.bottom, systemBarInsets.bottom);
38
+ return WindowInsetsCompat.CONSUMED;
39
+ });
40
+ }
41
+
42
+ private void updateInsets(int topInset, int bottomInset, int imeBottom, int systemBarsBottom) {
43
+ int nextTopInset = Math.max(0, topInset);
44
+ int nextBottomInset = Math.max(0, bottomInset);
45
+ if (nextTopInset == appliedTopInset && nextBottomInset == appliedBottomInset) {
46
+ return;
47
+ }
48
+
49
+ appliedTopInset = nextTopInset;
50
+ appliedBottomInset = nextBottomInset;
51
+ emitInsetsChange(nextTopInset, nextBottomInset, Math.max(0, imeBottom), Math.max(0, systemBarsBottom));
52
+ }
53
+
54
+ private void emitInsetsChange(int topInset, int bottomInset, int imeBottom, int systemBarsBottom) {
55
+ if (getId() == View.NO_ID || !(getContext() instanceof ReactContext)) {
56
+ return;
57
+ }
58
+
59
+ WritableMap event = Arguments.createMap();
60
+ event.putDouble("top", PixelUtil.toDIPFromPixel(topInset));
61
+ event.putDouble("bottom", PixelUtil.toDIPFromPixel(bottomInset));
62
+ event.putDouble("imeBottom", PixelUtil.toDIPFromPixel(imeBottom));
63
+ event.putDouble("systemBarsBottom", PixelUtil.toDIPFromPixel(systemBarsBottom));
64
+ event.putBoolean("keyboardVisible", imeBottom > systemBarsBottom);
65
+
66
+ ReactContext reactContext = (ReactContext) getContext();
67
+ EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId());
68
+ if (eventDispatcher == null) {
69
+ return;
70
+ }
71
+
72
+ eventDispatcher.dispatchEvent(new SagepilotInsetsChangeEvent(
73
+ UIManagerHelper.getSurfaceId(this),
74
+ getId(),
75
+ event
76
+ ));
77
+ }
78
+ }
@@ -0,0 +1,59 @@
1
+ package ai.sagepilot.reactnativesdk;
2
+
3
+ import android.view.View;
4
+ import com.facebook.react.common.MapBuilder;
5
+ import com.facebook.react.uimanager.ViewManagerDelegate;
6
+ import com.facebook.react.uimanager.ThemedReactContext;
7
+ import com.facebook.react.uimanager.ViewGroupManager;
8
+ import com.facebook.react.viewmanagers.SagepilotInsetsViewManagerDelegate;
9
+ import com.facebook.react.viewmanagers.SagepilotInsetsViewManagerInterface;
10
+ import java.util.Map;
11
+
12
+ public class SagepilotInsetsViewManager extends ViewGroupManager<SagepilotInsetsView>
13
+ implements SagepilotInsetsViewManagerInterface<SagepilotInsetsView> {
14
+ public static final String REACT_CLASS = "SagepilotInsetsView";
15
+ private final ViewManagerDelegate<SagepilotInsetsView> delegate = new SagepilotInsetsViewManagerDelegate<>(this);
16
+
17
+ @Override
18
+ protected ViewManagerDelegate<SagepilotInsetsView> getDelegate() {
19
+ return delegate;
20
+ }
21
+
22
+ @Override
23
+ public String getName() {
24
+ return REACT_CLASS;
25
+ }
26
+
27
+ @Override
28
+ public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
29
+ return MapBuilder.of(
30
+ SagepilotInsetsChangeEvent.EVENT_NAME,
31
+ MapBuilder.of("registrationName", "onInsetsChange")
32
+ );
33
+ }
34
+
35
+ @Override
36
+ protected SagepilotInsetsView createViewInstance(ThemedReactContext reactContext) {
37
+ return new SagepilotInsetsView(reactContext);
38
+ }
39
+
40
+ @Override
41
+ public void addView(SagepilotInsetsView parent, View child, int index) {
42
+ parent.addView(child, index);
43
+ }
44
+
45
+ @Override
46
+ public int getChildCount(SagepilotInsetsView parent) {
47
+ return parent.getChildCount();
48
+ }
49
+
50
+ @Override
51
+ public View getChildAt(SagepilotInsetsView parent, int index) {
52
+ return parent.getChildAt(index);
53
+ }
54
+
55
+ @Override
56
+ public void removeViewAt(SagepilotInsetsView parent, int index) {
57
+ parent.removeViewAt(index);
58
+ }
59
+ }
@@ -0,0 +1,20 @@
1
+ package ai.sagepilot.reactnativesdk;
2
+
3
+ import com.facebook.react.ReactPackage;
4
+ import com.facebook.react.bridge.NativeModule;
5
+ import com.facebook.react.bridge.ReactApplicationContext;
6
+ import com.facebook.react.uimanager.ViewManager;
7
+ import java.util.Collections;
8
+ import java.util.List;
9
+
10
+ public class SagepilotReactNativeSdkPackage implements ReactPackage {
11
+ @Override
12
+ public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
13
+ return Collections.emptyList();
14
+ }
15
+
16
+ @Override
17
+ public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
18
+ return Collections.singletonList(new SagepilotInsetsViewManager());
19
+ }
20
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * This code mirrors react-native-codegen output so Paper builds can compile when codegen is off.
3
+ */
4
+ package com.facebook.react.viewmanagers;
5
+
6
+ import android.view.View;
7
+ import androidx.annotation.Nullable;
8
+ import com.facebook.react.uimanager.BaseViewManager;
9
+ import com.facebook.react.uimanager.BaseViewManagerDelegate;
10
+ import com.facebook.react.uimanager.LayoutShadowNode;
11
+
12
+ public class SagepilotInsetsViewManagerDelegate<
13
+ T extends View,
14
+ U extends
15
+ BaseViewManager<T, ? extends LayoutShadowNode> & SagepilotInsetsViewManagerInterface<T>>
16
+ extends BaseViewManagerDelegate<T, U> {
17
+ public SagepilotInsetsViewManagerDelegate(U viewManager) {
18
+ super(viewManager);
19
+ }
20
+
21
+ @Override
22
+ public void setProperty(T view, String propName, @Nullable Object value) {
23
+ super.setProperty(view, propName, value);
24
+ }
25
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * This code mirrors react-native-codegen output so Paper builds can compile when codegen is off.
3
+ */
4
+ package com.facebook.react.viewmanagers;
5
+
6
+ import android.view.View;
7
+
8
+ public interface SagepilotInsetsViewManagerInterface<T extends View> {
9
+ // No props
10
+ }
@@ -216,6 +216,39 @@ type SagepilotMobileState = SagepilotLifecycleState & {
216
216
  theme: SagepilotMobileThemeState;
217
217
  };
218
218
  type SagepilotMobileSubscription = () => void;
219
+ type SagepilotMessageComposerMode = "auto" | "new";
220
+ type SagepilotConversationCreatedEvent = {
221
+ chat_id: string;
222
+ workspace_id?: string;
223
+ channel_id?: string;
224
+ session_id?: string;
225
+ customer_id?: string;
226
+ message_id?: string;
227
+ created_at?: string;
228
+ metadata?: Record<string, unknown>;
229
+ };
230
+ type SagepilotMessageComposerOptions = {
231
+ /**
232
+ * auto: prefill the composer and let the hosted widget reuse an existing
233
+ * conversation when one is available.
234
+ * new: force the hosted widget to start a fresh conversation.
235
+ */
236
+ mode?: SagepilotMessageComposerMode;
237
+ /**
238
+ * Opens a specific hosted conversation. This value is treated as an opaque
239
+ * conversation handle and is verified by the hosted customer session.
240
+ */
241
+ chatId?: string;
242
+ /**
243
+ * App-owned metadata returned to the conversation-created callback so callers
244
+ * can persist mappings such as order_id -> chat_id.
245
+ */
246
+ metadata?: Record<string, unknown>;
247
+ /**
248
+ * Fires when this composer invocation creates a new conversation.
249
+ */
250
+ onConversationCreated?: (event: SagepilotConversationCreatedEvent) => void;
251
+ };
219
252
  type SagepilotChatHookState = {
220
253
  configured: boolean;
221
254
  isPresented: boolean;
@@ -224,13 +257,14 @@ type SagepilotChatHookState = {
224
257
  theme: SagepilotMobileThemeState;
225
258
  present: () => boolean;
226
259
  presentMessages: () => boolean;
227
- presentMessageComposer: (message?: string) => boolean;
260
+ presentMessageComposer: (message?: string, options?: SagepilotMessageComposerOptions) => boolean;
228
261
  dismiss: () => boolean;
229
262
  hide: () => boolean;
230
263
  toggle: () => boolean;
231
264
  identify: (identity: SagepilotIdentity) => Promise<SagepilotIdentifyResult>;
232
265
  logout: () => Promise<SagepilotLogoutResponse>;
233
266
  getUnreadCount: () => Promise<number>;
267
+ onConversationCreated: (callback: (event: SagepilotConversationCreatedEvent) => void) => SagepilotMobileSubscription;
234
268
  };
235
269
  type SagepilotChatProviderProps = {
236
270
  children?: ReactNode;
@@ -250,13 +284,14 @@ declare const SagepilotChat: {
250
284
  pending: boolean;
251
285
  };
252
286
  onIdentify: (callback: Callback<SagepilotIdentifyResult>) => SagepilotMobileSubscription;
287
+ onConversationCreated: (callback: Callback<SagepilotConversationCreatedEvent>) => SagepilotMobileSubscription;
253
288
  getUnreadCount: () => Promise<number>;
254
289
  onUnreadChange: (callback: Callback<SagepilotUnreadState>) => SagepilotMobileSubscription;
255
290
  startUnreadPolling: (intervalMs?: number) => () => void;
256
291
  stopUnreadPolling: () => void;
257
292
  present: () => boolean;
258
293
  presentMessages: () => boolean;
259
- presentMessageComposer: (message?: string) => boolean;
294
+ presentMessageComposer: (message?: string, options?: SagepilotMessageComposerOptions) => boolean;
260
295
  dismiss: () => boolean;
261
296
  hide: () => boolean;
262
297
  toggle: () => boolean;
@@ -307,4 +342,4 @@ declare function SagepilotChatProvider({ children, closeLabel, loadingLabel }: S
307
342
 
308
343
  declare function useSagepilotChat(): SagepilotChatHookState;
309
344
 
310
- 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 };
345
+ export { type SagepilotBiometricsAdapter, type SagepilotBootstrapChannelResponse, type SagepilotCacheStorage, SagepilotChat, SagepilotChatError, type SagepilotChatErrorCode, type SagepilotChatHookState, SagepilotChatProvider, type SagepilotChatProviderProps, type SagepilotConversationCreatedEvent, type SagepilotDeviceInfo, type SagepilotDeviceInfoAdapter, type SagepilotIdentifyResult, type SagepilotIdentity, type SagepilotIdentityState, 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 SagepilotSessionState, type SagepilotTokenStorage, type SagepilotUnreadState, createAsyncStorageCacheStorage, createKeychainTokenStorage, useSagepilotChat };
package/dist/index.d.ts CHANGED
@@ -216,6 +216,39 @@ type SagepilotMobileState = SagepilotLifecycleState & {
216
216
  theme: SagepilotMobileThemeState;
217
217
  };
218
218
  type SagepilotMobileSubscription = () => void;
219
+ type SagepilotMessageComposerMode = "auto" | "new";
220
+ type SagepilotConversationCreatedEvent = {
221
+ chat_id: string;
222
+ workspace_id?: string;
223
+ channel_id?: string;
224
+ session_id?: string;
225
+ customer_id?: string;
226
+ message_id?: string;
227
+ created_at?: string;
228
+ metadata?: Record<string, unknown>;
229
+ };
230
+ type SagepilotMessageComposerOptions = {
231
+ /**
232
+ * auto: prefill the composer and let the hosted widget reuse an existing
233
+ * conversation when one is available.
234
+ * new: force the hosted widget to start a fresh conversation.
235
+ */
236
+ mode?: SagepilotMessageComposerMode;
237
+ /**
238
+ * Opens a specific hosted conversation. This value is treated as an opaque
239
+ * conversation handle and is verified by the hosted customer session.
240
+ */
241
+ chatId?: string;
242
+ /**
243
+ * App-owned metadata returned to the conversation-created callback so callers
244
+ * can persist mappings such as order_id -> chat_id.
245
+ */
246
+ metadata?: Record<string, unknown>;
247
+ /**
248
+ * Fires when this composer invocation creates a new conversation.
249
+ */
250
+ onConversationCreated?: (event: SagepilotConversationCreatedEvent) => void;
251
+ };
219
252
  type SagepilotChatHookState = {
220
253
  configured: boolean;
221
254
  isPresented: boolean;
@@ -224,13 +257,14 @@ type SagepilotChatHookState = {
224
257
  theme: SagepilotMobileThemeState;
225
258
  present: () => boolean;
226
259
  presentMessages: () => boolean;
227
- presentMessageComposer: (message?: string) => boolean;
260
+ presentMessageComposer: (message?: string, options?: SagepilotMessageComposerOptions) => boolean;
228
261
  dismiss: () => boolean;
229
262
  hide: () => boolean;
230
263
  toggle: () => boolean;
231
264
  identify: (identity: SagepilotIdentity) => Promise<SagepilotIdentifyResult>;
232
265
  logout: () => Promise<SagepilotLogoutResponse>;
233
266
  getUnreadCount: () => Promise<number>;
267
+ onConversationCreated: (callback: (event: SagepilotConversationCreatedEvent) => void) => SagepilotMobileSubscription;
234
268
  };
235
269
  type SagepilotChatProviderProps = {
236
270
  children?: ReactNode;
@@ -250,13 +284,14 @@ declare const SagepilotChat: {
250
284
  pending: boolean;
251
285
  };
252
286
  onIdentify: (callback: Callback<SagepilotIdentifyResult>) => SagepilotMobileSubscription;
287
+ onConversationCreated: (callback: Callback<SagepilotConversationCreatedEvent>) => SagepilotMobileSubscription;
253
288
  getUnreadCount: () => Promise<number>;
254
289
  onUnreadChange: (callback: Callback<SagepilotUnreadState>) => SagepilotMobileSubscription;
255
290
  startUnreadPolling: (intervalMs?: number) => () => void;
256
291
  stopUnreadPolling: () => void;
257
292
  present: () => boolean;
258
293
  presentMessages: () => boolean;
259
- presentMessageComposer: (message?: string) => boolean;
294
+ presentMessageComposer: (message?: string, options?: SagepilotMessageComposerOptions) => boolean;
260
295
  dismiss: () => boolean;
261
296
  hide: () => boolean;
262
297
  toggle: () => boolean;
@@ -307,4 +342,4 @@ declare function SagepilotChatProvider({ children, closeLabel, loadingLabel }: S
307
342
 
308
343
  declare function useSagepilotChat(): SagepilotChatHookState;
309
344
 
310
- 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 };
345
+ export { type SagepilotBiometricsAdapter, type SagepilotBootstrapChannelResponse, type SagepilotCacheStorage, SagepilotChat, SagepilotChatError, type SagepilotChatErrorCode, type SagepilotChatHookState, SagepilotChatProvider, type SagepilotChatProviderProps, type SagepilotConversationCreatedEvent, type SagepilotDeviceInfo, type SagepilotDeviceInfoAdapter, type SagepilotIdentifyResult, type SagepilotIdentity, type SagepilotIdentityState, 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 SagepilotSessionState, type SagepilotTokenStorage, type SagepilotUnreadState, createAsyncStorageCacheStorage, createKeychainTokenStorage, useSagepilotChat };