@personaai/react 0.7.5 → 0.7.8

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/dist/index.d.cts CHANGED
@@ -268,6 +268,20 @@ interface UseChatOptions {
268
268
  * host app still owns starting/stopping the call itself (e.g. `voice.start()`/`voice.stop()`).
269
269
  */
270
270
  voice?: UseVoiceResult;
271
+ /**
272
+ * Caller-supplied context — never shown to the model in any form, unlike
273
+ * `contextOverride` (see {@link SendMessageOverride.contextOverride}).
274
+ * Only ever read live by an RCP tool's resolver at the moment it's
275
+ * actually called. Accepts a plain object, or a getter (`() => object`)
276
+ * so `sendMessage()` always reads your app's *current* state at send
277
+ * time — no ref, no effect to keep it in sync as the value changes (e.g.
278
+ * the user navigates to a different page mid-conversation). Shallow-merged
279
+ * with any call-level `sendMessage(text, { context })` override, which
280
+ * wins on key collisions. Must be a flat object of string/number/boolean
281
+ * values only, capped at 2000 bytes serialized — a violation is rejected
282
+ * with a 400, not silently truncated or coerced.
283
+ */
284
+ context?: Record<string, unknown> | (() => Record<string, unknown>);
271
285
  }
272
286
  interface SendMessageOverride {
273
287
  agentId?: string;
@@ -291,6 +305,13 @@ interface SendMessageOverride {
291
305
  * `@personaai/sdk`'s `SendMessageOptions.contextOverride`.
292
306
  */
293
307
  contextOverride?: string;
308
+ /**
309
+ * One-off override for this send only — shallow-merged on top of
310
+ * `useChat`'s hook-level `context` option (this wins on key collisions).
311
+ * Same shape/cap/contract as the hook-level option; see
312
+ * {@link UseChatOptions.context}.
313
+ */
314
+ context?: Record<string, unknown>;
294
315
  }
295
316
  /**
296
317
  * `useVoice`'s call state, driven by `voice_activity` events from the
@@ -325,6 +346,26 @@ interface UseVoiceOptions {
325
346
  * leave undefined) to start a fresh conversation.
326
347
  */
327
348
  threadId?: string;
349
+ /**
350
+ * Caller-supplied context appended to the Agent's system instruction for
351
+ * this call — same field/cap/contract as `useChat`'s `sendMessage(text, {
352
+ * contextOverride })`. Unlike text chat, this is a **one-time append at
353
+ * connect**, not re-applied per turn: Gemini Live's system instruction is
354
+ * fixed for the whole call, so changing it means ending this call and
355
+ * starting a new `start()`. Capped at 4000 characters server-side
356
+ * (rejected with a 400, not truncated).
357
+ */
358
+ contextOverride?: string;
359
+ /**
360
+ * Caller-supplied context — never shown to the model, only ever read live
361
+ * by an RCP tool's resolver. This is the **at-connect seed only**, passed
362
+ * to `start()`'s ticket-mint call. To refresh it for the rest of an
363
+ * already-open call (a voice call can run up to 15 minutes — long enough
364
+ * for the caller's own context to change), use the returned
365
+ * `updateContext()` instead, which sends it live over the open WebSocket
366
+ * with no reconnect and no interruption to the audio.
367
+ */
368
+ context?: Record<string, unknown>;
328
369
  }
329
370
  interface UseVoiceResult {
330
371
  state: PersonaVoiceState;
@@ -344,6 +385,15 @@ interface UseVoiceResult {
344
385
  mute: (muted: boolean) => void;
345
386
  /** Sends a typed message mid-call instead of speaking — the agent may reply in either voice or text. */
346
387
  sendText: (text: string) => void;
388
+ /**
389
+ * Live-refreshes context for the rest of this already-open call — merged
390
+ * into, not replacing, whatever context is already set (from `start()`'s
391
+ * `context` option or a previous `updateContext()` call). Sent directly
392
+ * over the open WebSocket (`{ type: 'voice.context', context }`); a no-op
393
+ * if the call isn't currently active. No reconnect, no dropped audio —
394
+ * the next tool call in this session reads the new value.
395
+ */
396
+ updateContext: (context: Record<string, unknown>) => void;
347
397
  }
348
398
 
349
399
  interface PersonaContextValue {
package/dist/index.d.ts CHANGED
@@ -268,6 +268,20 @@ interface UseChatOptions {
268
268
  * host app still owns starting/stopping the call itself (e.g. `voice.start()`/`voice.stop()`).
269
269
  */
270
270
  voice?: UseVoiceResult;
271
+ /**
272
+ * Caller-supplied context — never shown to the model in any form, unlike
273
+ * `contextOverride` (see {@link SendMessageOverride.contextOverride}).
274
+ * Only ever read live by an RCP tool's resolver at the moment it's
275
+ * actually called. Accepts a plain object, or a getter (`() => object`)
276
+ * so `sendMessage()` always reads your app's *current* state at send
277
+ * time — no ref, no effect to keep it in sync as the value changes (e.g.
278
+ * the user navigates to a different page mid-conversation). Shallow-merged
279
+ * with any call-level `sendMessage(text, { context })` override, which
280
+ * wins on key collisions. Must be a flat object of string/number/boolean
281
+ * values only, capped at 2000 bytes serialized — a violation is rejected
282
+ * with a 400, not silently truncated or coerced.
283
+ */
284
+ context?: Record<string, unknown> | (() => Record<string, unknown>);
271
285
  }
272
286
  interface SendMessageOverride {
273
287
  agentId?: string;
@@ -291,6 +305,13 @@ interface SendMessageOverride {
291
305
  * `@personaai/sdk`'s `SendMessageOptions.contextOverride`.
292
306
  */
293
307
  contextOverride?: string;
308
+ /**
309
+ * One-off override for this send only — shallow-merged on top of
310
+ * `useChat`'s hook-level `context` option (this wins on key collisions).
311
+ * Same shape/cap/contract as the hook-level option; see
312
+ * {@link UseChatOptions.context}.
313
+ */
314
+ context?: Record<string, unknown>;
294
315
  }
295
316
  /**
296
317
  * `useVoice`'s call state, driven by `voice_activity` events from the
@@ -325,6 +346,26 @@ interface UseVoiceOptions {
325
346
  * leave undefined) to start a fresh conversation.
326
347
  */
327
348
  threadId?: string;
349
+ /**
350
+ * Caller-supplied context appended to the Agent's system instruction for
351
+ * this call — same field/cap/contract as `useChat`'s `sendMessage(text, {
352
+ * contextOverride })`. Unlike text chat, this is a **one-time append at
353
+ * connect**, not re-applied per turn: Gemini Live's system instruction is
354
+ * fixed for the whole call, so changing it means ending this call and
355
+ * starting a new `start()`. Capped at 4000 characters server-side
356
+ * (rejected with a 400, not truncated).
357
+ */
358
+ contextOverride?: string;
359
+ /**
360
+ * Caller-supplied context — never shown to the model, only ever read live
361
+ * by an RCP tool's resolver. This is the **at-connect seed only**, passed
362
+ * to `start()`'s ticket-mint call. To refresh it for the rest of an
363
+ * already-open call (a voice call can run up to 15 minutes — long enough
364
+ * for the caller's own context to change), use the returned
365
+ * `updateContext()` instead, which sends it live over the open WebSocket
366
+ * with no reconnect and no interruption to the audio.
367
+ */
368
+ context?: Record<string, unknown>;
328
369
  }
329
370
  interface UseVoiceResult {
330
371
  state: PersonaVoiceState;
@@ -344,6 +385,15 @@ interface UseVoiceResult {
344
385
  mute: (muted: boolean) => void;
345
386
  /** Sends a typed message mid-call instead of speaking — the agent may reply in either voice or text. */
346
387
  sendText: (text: string) => void;
388
+ /**
389
+ * Live-refreshes context for the rest of this already-open call — merged
390
+ * into, not replacing, whatever context is already set (from `start()`'s
391
+ * `context` option or a previous `updateContext()` call). Sent directly
392
+ * over the open WebSocket (`{ type: 'voice.context', context }`); a no-op
393
+ * if the call isn't currently active. No reconnect, no dropped audio —
394
+ * the next tool call in this session reads the new value.
395
+ */
396
+ updateContext: (context: Record<string, unknown>) => void;
347
397
  }
348
398
 
349
399
  interface PersonaContextValue {
package/dist/index.js CHANGED
@@ -642,10 +642,14 @@ function useChat(options = {}) {
642
642
  const resolvedThreadId = await (overrideOptions?.threadId ?? options.threadId);
643
643
  chatLogger.trace("resolved threadId", { threadId: resolvedThreadId });
644
644
  const token = getAuthToken ? await getAuthToken() : null;
645
+ const resolvedHookContext = typeof options.context === "function" ? options.context() : options.context;
646
+ const mergedContext = { ...resolvedHookContext, ...overrideOptions?.context };
647
+ const hasContext = Object.keys(mergedContext).length > 0;
645
648
  chatLogger.debug("opening SSE stream", {
646
649
  agentId: targetAgentId,
647
650
  hasToken: !!token,
648
- hasThreadId: !!resolvedThreadId
651
+ hasThreadId: !!resolvedThreadId,
652
+ hasContext
649
653
  });
650
654
  const stream = await openSSEStream({
651
655
  url: `${baseUrl}/chat`,
@@ -658,7 +662,8 @@ function useChat(options = {}) {
658
662
  messages: payloadMessages,
659
663
  threadId: resolvedThreadId,
660
664
  resume: overrideOptions?.resume,
661
- contextOverride: overrideOptions?.contextOverride
665
+ contextOverride: overrideOptions?.contextOverride,
666
+ ...hasContext ? { context: mergedContext } : {}
662
667
  }),
663
668
  signal: controller.signal
664
669
  });
@@ -1392,6 +1397,8 @@ function useVoice(options = {}) {
1392
1397
  const voiceLogger = useMemo3(() => logger.child("voice"), [logger]);
1393
1398
  const agentId = options.agentId || defaultAgentId;
1394
1399
  const threadId = options.threadId;
1400
+ const contextOverride = options.contextOverride;
1401
+ const context = options.context;
1395
1402
  const [state, setState] = useState4("idle");
1396
1403
  const [isMuted, setIsMuted] = useState4(false);
1397
1404
  const [transcript, setTranscript] = useState4(
@@ -1661,14 +1668,21 @@ function useVoice(options = {}) {
1661
1668
  setToolCalls([]);
1662
1669
  acceptedTurnSeqRef.current = 0;
1663
1670
  setState("connecting");
1664
- voiceLogger.debug("start() called", { agentId, threadId });
1671
+ voiceLogger.debug("start() called", {
1672
+ agentId,
1673
+ threadId,
1674
+ hasContextOverride: !!contextOverride,
1675
+ hasContext: !!context
1676
+ });
1665
1677
  try {
1666
1678
  const res = await fetchWithAuth("/voice/sessions", {
1667
1679
  method: "POST",
1668
1680
  headers: { "Content-Type": "application/json" },
1669
1681
  body: JSON.stringify({
1670
1682
  agentId,
1671
- ...threadId ? { threadId } : {}
1683
+ ...threadId ? { threadId } : {},
1684
+ ...contextOverride ? { contextOverride } : {},
1685
+ ...context ? { context } : {}
1672
1686
  })
1673
1687
  });
1674
1688
  if (!res.ok) {
@@ -1704,7 +1718,7 @@ function useVoice(options = {}) {
1704
1718
  }
1705
1719
  teardownAudio();
1706
1720
  }
1707
- }, [agentId, threadId, fetchWithAuth, handleMessage, teardownAudio, voiceLogger]);
1721
+ }, [agentId, threadId, contextOverride, context, fetchWithAuth, handleMessage, teardownAudio, voiceLogger]);
1708
1722
  const mute = useCallback4((muted) => {
1709
1723
  setIsMuted(muted);
1710
1724
  streamRef.current?.getAudioTracks().forEach((track) => {
@@ -1715,6 +1729,10 @@ function useVoice(options = {}) {
1715
1729
  if (!text?.trim() || wsRef.current?.readyState !== WebSocket.OPEN) return;
1716
1730
  wsRef.current.send(JSON.stringify({ type: "voice.text", text }));
1717
1731
  }, []);
1732
+ const updateContext = useCallback4((newContext) => {
1733
+ if (wsRef.current?.readyState !== WebSocket.OPEN) return;
1734
+ wsRef.current.send(JSON.stringify({ type: "voice.context", context: newContext }));
1735
+ }, []);
1718
1736
  return {
1719
1737
  state,
1720
1738
  isMuted,
@@ -1726,7 +1744,8 @@ function useVoice(options = {}) {
1726
1744
  start,
1727
1745
  stop,
1728
1746
  mute,
1729
- sendText
1747
+ sendText,
1748
+ updateContext
1730
1749
  };
1731
1750
  }
1732
1751