@graineai/inapp-react-native 0.35.0 → 0.36.0

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/INTEGRATION.md CHANGED
@@ -815,9 +815,9 @@ is not happening, and the table says which.
815
815
  | Wait | What it is | When it is paid | Typical |
816
816
  |---|---|---|---|
817
817
  | Page load | The voice engine is a page in a WebView | **Before the tap** — `GraineFab` mounts the engine on open; a custom bar mounts `GraineVoiceLauncher` at launch with `active={false}` and flips it on open (0.27.5) | ~4s, hidden |
818
- | Mint + REGISTER | A 120-second SIP credential, then registration on the SBC | **Before the tap**, re-warmed as it nears expiry | ~1.6s, hidden |
818
+ | Mint + REGISTER | A 120-second SIP credential, then registration on the SBC | **Before the tap**, re-warmed as it nears expiry and whenever the app comes back to the foreground (0.36.0) | ~1s, hidden |
819
819
  | Microphone | The OS permission prompt, first time only | At the tap | 0 once granted |
820
- | INVITE → answer | The call itself | At the tap | ~0.3s |
820
+ | INVITE → answer | The call itself, on a network path prepared before the tap | At the tap | ~0.2s |
821
821
 
822
822
  **The INVITE is bounded, not left to the browser.** WebRTC gathers network
823
823
  candidates before dialling, and Chrome does not report gathering complete
@@ -21,6 +21,8 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, activ
21
21
  const [connecting, setConnecting] = useState(false);
22
22
  const [muted, setMutedState] = useState(false);
23
23
  const [ready, setReady] = useState(false);
24
+ const readyRef = useRef(false);
25
+ readyRef.current = ready;
24
26
  const [captions, setCaptions] = useState([]);
25
27
  const [awaitingAgent, setAwaitingAgent] = useState(false);
26
28
  const [captionsOn, setCaptionsOn] = useState(true);
@@ -132,7 +134,8 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, activ
132
134
  scheduleRetry("load-cap");
133
135
  return;
134
136
  }
135
- readyTimerRef.current = setTimeout(tick, Math.max(250, Math.min(limit - since, HARD_CAP_MS - total)));
137
+ const floor = Math.min(250, limit / 4);
138
+ readyTimerRef.current = setTimeout(tick, Math.max(floor, Math.min(limit - since, HARD_CAP_MS - total)));
136
139
  };
137
140
  readyTimerRef.current = setTimeout(tick, Math.min(STALL_TIMEOUT_MS, 1000));
138
141
  }, [scheduleRetry, STALL_TIMEOUT_MS, BOOT_TIMEOUT_MS, HARD_CAP_MS]);
@@ -462,6 +465,8 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, activ
462
465
  pending = null;
463
466
  }
464
467
  pushContext();
468
+ if (readyRef.current && !startedRef.current && !connectedRef.current)
469
+ post({ type: "graine:warm" });
465
470
  return;
466
471
  }
467
472
  if (next === "background" && wasActive && !pending) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graineai/inapp-react-native",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "type": "module",
5
5
  "description": "Graine in-app agent for React Native — an agent that sees the screen your customer is on and can act on it.",
6
6
  "license": "MIT",