@personaai/react 0.7.6 → 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
@@ -335,6 +356,16 @@ interface UseVoiceOptions {
335
356
  * (rejected with a 400, not truncated).
336
357
  */
337
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>;
338
369
  }
339
370
  interface UseVoiceResult {
340
371
  state: PersonaVoiceState;
@@ -354,6 +385,15 @@ interface UseVoiceResult {
354
385
  mute: (muted: boolean) => void;
355
386
  /** Sends a typed message mid-call instead of speaking — the agent may reply in either voice or text. */
356
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;
357
397
  }
358
398
 
359
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
@@ -335,6 +356,16 @@ interface UseVoiceOptions {
335
356
  * (rejected with a 400, not truncated).
336
357
  */
337
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>;
338
369
  }
339
370
  interface UseVoiceResult {
340
371
  state: PersonaVoiceState;
@@ -354,6 +385,15 @@ interface UseVoiceResult {
354
385
  mute: (muted: boolean) => void;
355
386
  /** Sends a typed message mid-call instead of speaking — the agent may reply in either voice or text. */
356
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;
357
397
  }
358
398
 
359
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
  });
@@ -1393,6 +1398,7 @@ function useVoice(options = {}) {
1393
1398
  const agentId = options.agentId || defaultAgentId;
1394
1399
  const threadId = options.threadId;
1395
1400
  const contextOverride = options.contextOverride;
1401
+ const context = options.context;
1396
1402
  const [state, setState] = useState4("idle");
1397
1403
  const [isMuted, setIsMuted] = useState4(false);
1398
1404
  const [transcript, setTranscript] = useState4(
@@ -1665,7 +1671,8 @@ function useVoice(options = {}) {
1665
1671
  voiceLogger.debug("start() called", {
1666
1672
  agentId,
1667
1673
  threadId,
1668
- hasContextOverride: !!contextOverride
1674
+ hasContextOverride: !!contextOverride,
1675
+ hasContext: !!context
1669
1676
  });
1670
1677
  try {
1671
1678
  const res = await fetchWithAuth("/voice/sessions", {
@@ -1674,7 +1681,8 @@ function useVoice(options = {}) {
1674
1681
  body: JSON.stringify({
1675
1682
  agentId,
1676
1683
  ...threadId ? { threadId } : {},
1677
- ...contextOverride ? { contextOverride } : {}
1684
+ ...contextOverride ? { contextOverride } : {},
1685
+ ...context ? { context } : {}
1678
1686
  })
1679
1687
  });
1680
1688
  if (!res.ok) {
@@ -1710,7 +1718,7 @@ function useVoice(options = {}) {
1710
1718
  }
1711
1719
  teardownAudio();
1712
1720
  }
1713
- }, [agentId, threadId, contextOverride, fetchWithAuth, handleMessage, teardownAudio, voiceLogger]);
1721
+ }, [agentId, threadId, contextOverride, context, fetchWithAuth, handleMessage, teardownAudio, voiceLogger]);
1714
1722
  const mute = useCallback4((muted) => {
1715
1723
  setIsMuted(muted);
1716
1724
  streamRef.current?.getAudioTracks().forEach((track) => {
@@ -1721,6 +1729,10 @@ function useVoice(options = {}) {
1721
1729
  if (!text?.trim() || wsRef.current?.readyState !== WebSocket.OPEN) return;
1722
1730
  wsRef.current.send(JSON.stringify({ type: "voice.text", text }));
1723
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
+ }, []);
1724
1736
  return {
1725
1737
  state,
1726
1738
  isMuted,
@@ -1732,7 +1744,8 @@ function useVoice(options = {}) {
1732
1744
  start,
1733
1745
  stop,
1734
1746
  mute,
1735
- sendText
1747
+ sendText,
1748
+ updateContext
1736
1749
  };
1737
1750
  }
1738
1751