@personaai/react 0.6.0 → 0.7.1

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
@@ -13,7 +13,7 @@ npm install @personaai/react
13
13
  ## Quickstart
14
14
 
15
15
  ```tsx
16
- import { PersonaProvider, useChat } from '@personaai/react';
16
+ import { PersonaProvider, useChat } from "@personaai/react";
17
17
 
18
18
  function App() {
19
19
  return (
@@ -46,14 +46,78 @@ function Chat() {
46
46
 
47
47
  ## Hooks
48
48
 
49
- | Hook | Purpose |
50
- | --- | --- |
51
- | `useChat` | Streaming chat — messages, send, stop, reload, interrupts, workspace files |
52
- | `useThreads` | Thread CRUD — list, create, delete, rename, archive |
53
- | `useFiles` | Upload management — list, upload, delete |
54
- | `useMemory` | Persistent memory — read, write, delete |
55
- | `useAgents` | Agent discovery — list available agents |
56
- | `useConnection` | Health check — backend connectivity status |
49
+ | Hook | Purpose |
50
+ | --------------- | -------------------------------------------------------------------------- |
51
+ | `useChat` | Streaming chat — messages, send, stop, reload, interrupts, workspace files |
52
+ | `useVoice` | Real-time voice calls (Gemini Live) — start/stop/mute, live transcript, tool calls |
53
+ | `useThreads` | Thread CRUD — list, create, delete, rename, reset, archive |
54
+ | `useFiles` | Upload management — list, upload, delete |
55
+ | `useMemory` | Persistent memory — read, write, delete |
56
+ | `useAgents` | Agent discovery — list available agents |
57
+ | `useConnection` | Health check — backend connectivity status |
58
+
59
+ ## Voice
60
+
61
+ Real-time voice calls with an Agent, powered by Gemini Live. Requires your backend to be running
62
+ `@personaai/runtime@^0.7.0` (or a framework adapter on top of it) so `POST /voice/sessions` exists
63
+ to mint tickets — see that package's README for why the actual call bypasses your backend and
64
+ goes straight to Persona instead of relaying through it like `useChat` does.
65
+
66
+ ```tsx
67
+ import { useVoice } from "@personaai/react";
68
+
69
+ function VoiceCall() {
70
+ const { state, transcript, partial, toolCalls, start, stop, mute, isMuted } = useVoice();
71
+
72
+ return (
73
+ <div>
74
+ <p>State: {state}</p>
75
+ {state === "idle" ? (
76
+ <button onClick={start}>Start call</button>
77
+ ) : (
78
+ <>
79
+ <button onClick={() => mute(!isMuted)}>{isMuted ? "Unmute" : "Mute"}</button>
80
+ <button onClick={stop}>End call</button>
81
+ </>
82
+ )}
83
+ {transcript.map((line) => (
84
+ <p key={line.id}>
85
+ <b>{line.speaker}:</b> {line.text}
86
+ </p>
87
+ ))}
88
+ {partial && <p style={{ opacity: 0.6 }}>{partial.text}</p>}
89
+ </div>
90
+ );
91
+ }
92
+ ```
93
+
94
+ Requires a browser with `AudioWorklet` support (every current evergreen browser). Needs an HTTPS
95
+ page in production — browsers refuse an insecure `ws://` connection from an `https:` page.
96
+
97
+ ## Devtools
98
+
99
+ Floating panel for local debugging — inspect hooks, messages and threads with zero runtime changes. Dev-only, not bundled to production unless imported.
100
+
101
+ ```bash
102
+ npm install -D @personaai/devtools
103
+ ```
104
+
105
+ ```tsx
106
+ import { PersonaDevtools } from "@personaai/devtools/react";
107
+ import { useChat, useThreads } from "@personaai/react";
108
+
109
+ function Devtools() {
110
+ const { threads } = useThreads();
111
+ const { messages, files, todos } = useChat();
112
+ return <PersonaDevtools clientState={{ threads, messages, files, todos }} />;
113
+ }
114
+
115
+ {
116
+ process.env.NODE_ENV === "development" && <Devtools />;
117
+ }
118
+ ```
119
+
120
+ See `@personaai/devtools` `sdk/devtools/README.md` for `core`/`react`/`nextjs` subpaths and `baseUrl` polling option.
57
121
 
58
122
  ## Full documentation
59
123
 
package/dist/index.cjs CHANGED
@@ -36,7 +36,8 @@ __export(index_exports, {
36
36
  useMcpConnections: () => useMcpConnections,
37
37
  useMemory: () => useMemory,
38
38
  usePersonaContext: () => usePersonaContext,
39
- useThreads: () => useThreads
39
+ useThreads: () => useThreads,
40
+ useVoice: () => useVoice
40
41
  });
41
42
  module.exports = __toCommonJS(index_exports);
42
43
 
@@ -1252,15 +1253,443 @@ function useThreads(autoFetch = true) {
1252
1253
  };
1253
1254
  }
1254
1255
 
1255
- // src/hooks/useFiles.ts
1256
+ // src/hooks/useVoice.ts
1256
1257
  var import_react5 = require("react");
1258
+
1259
+ // src/hooks/voiceWorklets.ts
1260
+ var RECORDER_WORKLET_SOURCE = `
1261
+ class PCMRecorderProcessor extends AudioWorkletProcessor {
1262
+ constructor() {
1263
+ super();
1264
+ this._buffer = [];
1265
+ this._chunkSamples = 320; // 20ms @ 16kHz
1266
+ }
1267
+
1268
+ process(inputs) {
1269
+ const input = inputs[0];
1270
+ const channel = input && input[0];
1271
+ if (channel) {
1272
+ for (let i = 0; i < channel.length; i++) {
1273
+ this._buffer.push(channel[i]);
1274
+ }
1275
+ while (this._buffer.length >= this._chunkSamples) {
1276
+ const chunk = this._buffer.splice(0, this._chunkSamples);
1277
+ const int16 = new Int16Array(chunk.length);
1278
+ for (let i = 0; i < chunk.length; i++) {
1279
+ const s = Math.max(-1, Math.min(1, chunk[i]));
1280
+ int16[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
1281
+ }
1282
+ this.port.postMessage(int16.buffer, [int16.buffer]);
1283
+ }
1284
+ }
1285
+ return true;
1286
+ }
1287
+ }
1288
+
1289
+ registerProcessor('pcm-recorder-processor', PCMRecorderProcessor);
1290
+ `;
1291
+ var PLAYER_WORKLET_SOURCE = `
1292
+ class PCMPlayerProcessor extends AudioWorkletProcessor {
1293
+ constructor() {
1294
+ super();
1295
+ this._queue = [];
1296
+ this._readOffset = 0;
1297
+ this.port.onmessage = (event) => {
1298
+ const msg = event.data;
1299
+ if (msg && msg.type === 'flush') {
1300
+ this._queue = [];
1301
+ this._readOffset = 0;
1302
+ return;
1303
+ }
1304
+ const int16 = new Int16Array(msg);
1305
+ const float32 = new Float32Array(int16.length);
1306
+ for (let i = 0; i < int16.length; i++) {
1307
+ float32[i] = int16[i] / 0x8000;
1308
+ }
1309
+ this._queue.push(float32);
1310
+ };
1311
+ }
1312
+
1313
+ process(_inputs, outputs) {
1314
+ const output = outputs[0][0];
1315
+ let outIdx = 0;
1316
+ while (outIdx < output.length) {
1317
+ if (this._queue.length === 0) {
1318
+ output[outIdx++] = 0;
1319
+ continue;
1320
+ }
1321
+ const current = this._queue[0];
1322
+ const remaining = current.length - this._readOffset;
1323
+ const toCopy = Math.min(remaining, output.length - outIdx);
1324
+ output.set(current.subarray(this._readOffset, this._readOffset + toCopy), outIdx);
1325
+ outIdx += toCopy;
1326
+ this._readOffset += toCopy;
1327
+ if (this._readOffset >= current.length) {
1328
+ this._queue.shift();
1329
+ this._readOffset = 0;
1330
+ }
1331
+ }
1332
+ return true;
1333
+ }
1334
+ }
1335
+
1336
+ registerProcessor('pcm-player-processor', PCMPlayerProcessor);
1337
+ `;
1338
+ function toModuleUrl(source) {
1339
+ const blob = new Blob([source], { type: "application/javascript" });
1340
+ return URL.createObjectURL(blob);
1341
+ }
1342
+ function recorderWorkletUrl() {
1343
+ return toModuleUrl(RECORDER_WORKLET_SOURCE);
1344
+ }
1345
+ function playerWorkletUrl() {
1346
+ return toModuleUrl(PLAYER_WORKLET_SOURCE);
1347
+ }
1348
+
1349
+ // src/hooks/useVoice.ts
1350
+ function useVoice(options = {}) {
1351
+ const { defaultAgentId, fetchWithAuth, logger } = usePersonaContext();
1352
+ const voiceLogger = (0, import_react5.useMemo)(() => logger.child("voice"), [logger]);
1353
+ const agentId = options.agentId || defaultAgentId;
1354
+ const threadId = options.threadId;
1355
+ const [state, setState] = (0, import_react5.useState)("idle");
1356
+ const [isMuted, setIsMuted] = (0, import_react5.useState)(false);
1357
+ const [transcript, setTranscript] = (0, import_react5.useState)(
1358
+ []
1359
+ );
1360
+ const [partial, setPartial] = (0, import_react5.useState)(
1361
+ null
1362
+ );
1363
+ const [toolCalls, setToolCalls] = (0, import_react5.useState)([]);
1364
+ const [error, setError] = (0, import_react5.useState)(null);
1365
+ const [endReason, setEndReason] = (0, import_react5.useState)(
1366
+ null
1367
+ );
1368
+ const wsRef = (0, import_react5.useRef)(null);
1369
+ const streamRef = (0, import_react5.useRef)(null);
1370
+ const inputCtxRef = (0, import_react5.useRef)(null);
1371
+ const outputCtxRef = (0, import_react5.useRef)(null);
1372
+ const recorderNodeRef = (0, import_react5.useRef)(null);
1373
+ const playerNodeRef = (0, import_react5.useRef)(null);
1374
+ const acceptedTurnSeqRef = (0, import_react5.useRef)(0);
1375
+ const mountedRef = (0, import_react5.useRef)(true);
1376
+ (0, import_react5.useEffect)(() => {
1377
+ mountedRef.current = true;
1378
+ return () => {
1379
+ mountedRef.current = false;
1380
+ };
1381
+ }, []);
1382
+ const teardownAudio = (0, import_react5.useCallback)(() => {
1383
+ try {
1384
+ streamRef.current?.getTracks().forEach((track) => track.stop());
1385
+ } catch {
1386
+ }
1387
+ streamRef.current = null;
1388
+ try {
1389
+ recorderNodeRef.current?.disconnect();
1390
+ } catch {
1391
+ }
1392
+ recorderNodeRef.current = null;
1393
+ try {
1394
+ playerNodeRef.current?.disconnect();
1395
+ } catch {
1396
+ }
1397
+ playerNodeRef.current = null;
1398
+ try {
1399
+ void inputCtxRef.current?.close();
1400
+ } catch {
1401
+ }
1402
+ inputCtxRef.current = null;
1403
+ try {
1404
+ void outputCtxRef.current?.close();
1405
+ } catch {
1406
+ }
1407
+ outputCtxRef.current = null;
1408
+ }, []);
1409
+ const stop = (0, import_react5.useCallback)(() => {
1410
+ voiceLogger.debug("stop() called");
1411
+ try {
1412
+ wsRef.current?.close(1e3, "client_stop");
1413
+ } catch {
1414
+ }
1415
+ wsRef.current = null;
1416
+ teardownAudio();
1417
+ if (mountedRef.current) setState("idle");
1418
+ }, [teardownAudio, voiceLogger]);
1419
+ (0, import_react5.useEffect)(() => stop, [stop]);
1420
+ const startCapture = (0, import_react5.useCallback)(async (inputSampleRate) => {
1421
+ const stream = await navigator.mediaDevices.getUserMedia({
1422
+ audio: {
1423
+ echoCancellation: true,
1424
+ noiseSuppression: true,
1425
+ autoGainControl: true,
1426
+ channelCount: 1
1427
+ }
1428
+ });
1429
+ streamRef.current = stream;
1430
+ const inputCtx = new AudioContext({ sampleRate: inputSampleRate });
1431
+ inputCtxRef.current = inputCtx;
1432
+ await inputCtx.audioWorklet.addModule(recorderWorkletUrl());
1433
+ const source = inputCtx.createMediaStreamSource(stream);
1434
+ const recorderNode = new AudioWorkletNode(
1435
+ inputCtx,
1436
+ "pcm-recorder-processor"
1437
+ );
1438
+ recorderNode.port.onmessage = (event) => {
1439
+ if (wsRef.current?.readyState === WebSocket.OPEN) {
1440
+ wsRef.current.send(event.data);
1441
+ }
1442
+ };
1443
+ recorderNodeRef.current = recorderNode;
1444
+ source.connect(recorderNode);
1445
+ }, []);
1446
+ const setupPlayback = (0, import_react5.useCallback)(async (outputSampleRate) => {
1447
+ const outputCtx = new AudioContext({ sampleRate: outputSampleRate });
1448
+ outputCtxRef.current = outputCtx;
1449
+ await outputCtx.audioWorklet.addModule(playerWorkletUrl());
1450
+ const playerNode = new AudioWorkletNode(outputCtx, "pcm-player-processor");
1451
+ playerNode.connect(outputCtx.destination);
1452
+ playerNodeRef.current = playerNode;
1453
+ }, []);
1454
+ const upsertToolCall = (0, import_react5.useCallback)(
1455
+ (id, patch) => {
1456
+ setToolCalls((prev) => {
1457
+ const idx = prev.findIndex((t) => t.id === id);
1458
+ if (idx === -1) return [...prev, { id, status: "running", ...patch }];
1459
+ const next = [...prev];
1460
+ next[idx] = { ...next[idx], ...patch };
1461
+ return next;
1462
+ });
1463
+ },
1464
+ []
1465
+ );
1466
+ const handleTranscript = (0, import_react5.useCallback)(
1467
+ (value) => {
1468
+ const { speaker, text, isFinal } = value;
1469
+ if (isFinal) {
1470
+ setPartial((prev) => prev?.speaker === speaker ? null : prev);
1471
+ setTranscript((prev) => [
1472
+ ...prev,
1473
+ { id: `${speaker}-${prev.length}`, speaker, text }
1474
+ ]);
1475
+ } else {
1476
+ setPartial({ id: "partial", speaker, text });
1477
+ }
1478
+ },
1479
+ []
1480
+ );
1481
+ const handleCustomEvent = (0, import_react5.useCallback)(
1482
+ (name, value) => {
1483
+ switch (name) {
1484
+ case "voice_session_ready":
1485
+ voiceLogger.debug("voice_session_ready", value);
1486
+ Promise.all([
1487
+ setupPlayback(value.outputSampleRate),
1488
+ startCapture(value.inputSampleRate)
1489
+ ]).then(() => {
1490
+ if (mountedRef.current) setState("listening");
1491
+ }).catch((err) => {
1492
+ voiceLogger.error("audio graph setup failed", {
1493
+ error: err instanceof Error ? err.message : String(err)
1494
+ });
1495
+ if (mountedRef.current) {
1496
+ setError(err instanceof Error ? err : new Error(String(err)));
1497
+ setState("error");
1498
+ }
1499
+ });
1500
+ break;
1501
+ case "voice_activity":
1502
+ if (value.speaker === "user" && value.state === "start") {
1503
+ setState("listening");
1504
+ } else if (value.speaker === "user" && value.state === "end") {
1505
+ setState("thinking");
1506
+ } else if (value.speaker === "agent" && value.state === "start") {
1507
+ setState("speaking");
1508
+ } else if (value.speaker === "agent" && value.state === "end") {
1509
+ setState("listening");
1510
+ }
1511
+ break;
1512
+ case "voice_transcript":
1513
+ handleTranscript(
1514
+ value
1515
+ );
1516
+ break;
1517
+ case "voice_interrupted":
1518
+ acceptedTurnSeqRef.current = value.turnSeq;
1519
+ playerNodeRef.current?.port.postMessage({ type: "flush" });
1520
+ break;
1521
+ case "voice_session_resumed":
1522
+ voiceLogger.info("session transparently resumed");
1523
+ break;
1524
+ case "voice_session_ended":
1525
+ voiceLogger.info("session ended", { reason: value.reason });
1526
+ setState("ended");
1527
+ setEndReason(value.reason ?? null);
1528
+ teardownAudio();
1529
+ try {
1530
+ wsRef.current?.close();
1531
+ } catch {
1532
+ }
1533
+ wsRef.current = null;
1534
+ break;
1535
+ default:
1536
+ break;
1537
+ }
1538
+ },
1539
+ [handleTranscript, setupPlayback, startCapture, teardownAudio, voiceLogger]
1540
+ );
1541
+ const handleMessage = (0, import_react5.useCallback)(
1542
+ (event) => {
1543
+ if (typeof event.data !== "string") {
1544
+ const buf = event.data;
1545
+ const view = new DataView(buf);
1546
+ const turnSeq = view.getUint32(0, true);
1547
+ if (turnSeq >= acceptedTurnSeqRef.current) {
1548
+ acceptedTurnSeqRef.current = turnSeq;
1549
+ const pcmBytes = buf.slice(4);
1550
+ playerNodeRef.current?.port.postMessage(pcmBytes, [pcmBytes]);
1551
+ }
1552
+ return;
1553
+ }
1554
+ let msg;
1555
+ try {
1556
+ msg = JSON.parse(event.data);
1557
+ } catch {
1558
+ return;
1559
+ }
1560
+ switch (msg.type) {
1561
+ case "CUSTOM":
1562
+ handleCustomEvent(
1563
+ msg.name,
1564
+ msg.value
1565
+ );
1566
+ break;
1567
+ case "TOOL_CALL_CHUNK":
1568
+ upsertToolCall(msg.toolCallId, {
1569
+ name: msg.toolCallName,
1570
+ status: "running"
1571
+ });
1572
+ break;
1573
+ case "TOOL_CALL_RESULT": {
1574
+ let parsed = null;
1575
+ try {
1576
+ parsed = JSON.parse(msg.content);
1577
+ } catch {
1578
+ parsed = { output: msg.content };
1579
+ }
1580
+ upsertToolCall(msg.toolCallId, {
1581
+ status: parsed?.error ? "error" : "done",
1582
+ summary: parsed?.error || (typeof parsed?.output === "string" ? parsed.output : JSON.stringify(parsed?.output ?? ""))
1583
+ });
1584
+ break;
1585
+ }
1586
+ case "RUN_ERROR":
1587
+ voiceLogger.warn("RUN_ERROR", { message: msg.message });
1588
+ setError(
1589
+ new Error(msg.message || "The agent run failed.")
1590
+ );
1591
+ setState("error");
1592
+ break;
1593
+ default:
1594
+ break;
1595
+ }
1596
+ },
1597
+ [handleCustomEvent, upsertToolCall, voiceLogger]
1598
+ );
1599
+ const start = (0, import_react5.useCallback)(async () => {
1600
+ if (!agentId) {
1601
+ const err = new Error(
1602
+ "useVoice: no agentId provided and no defaultAgentId set on PersonaProvider"
1603
+ );
1604
+ setError(err);
1605
+ setState("error");
1606
+ return;
1607
+ }
1608
+ setError(null);
1609
+ setEndReason(null);
1610
+ setTranscript([]);
1611
+ setPartial(null);
1612
+ setToolCalls([]);
1613
+ acceptedTurnSeqRef.current = 0;
1614
+ setState("connecting");
1615
+ voiceLogger.debug("start() called", { agentId, threadId });
1616
+ try {
1617
+ const res = await fetchWithAuth("/voice/sessions", {
1618
+ method: "POST",
1619
+ headers: { "Content-Type": "application/json" },
1620
+ body: JSON.stringify({
1621
+ agentId,
1622
+ ...threadId ? { threadId } : {}
1623
+ })
1624
+ });
1625
+ if (!res.ok) {
1626
+ throw new Error(`Failed to start voice session: ${res.statusText}`);
1627
+ }
1628
+ const data = await res.json();
1629
+ const ticket = data?.data ?? data;
1630
+ if (!ticket?.wsUrl) {
1631
+ throw new Error("Server did not return a voice session URL.");
1632
+ }
1633
+ const ws = new WebSocket(ticket.wsUrl);
1634
+ ws.binaryType = "arraybuffer";
1635
+ wsRef.current = ws;
1636
+ ws.onmessage = handleMessage;
1637
+ ws.onerror = () => {
1638
+ voiceLogger.warn("WebSocket error");
1639
+ if (mountedRef.current) {
1640
+ setError(new Error("Voice connection error."));
1641
+ setState("error");
1642
+ }
1643
+ };
1644
+ ws.onclose = () => {
1645
+ if (mountedRef.current && wsRef.current === ws) {
1646
+ setState((prev) => prev === "error" ? prev : "idle");
1647
+ }
1648
+ };
1649
+ } catch (err) {
1650
+ const errorObj = err instanceof Error ? err : new Error(String(err));
1651
+ voiceLogger.error("start() failed", { error: errorObj.message });
1652
+ if (mountedRef.current) {
1653
+ setError(errorObj);
1654
+ setState("error");
1655
+ }
1656
+ teardownAudio();
1657
+ }
1658
+ }, [agentId, threadId, fetchWithAuth, handleMessage, teardownAudio, voiceLogger]);
1659
+ const mute = (0, import_react5.useCallback)((muted) => {
1660
+ setIsMuted(muted);
1661
+ streamRef.current?.getAudioTracks().forEach((track) => {
1662
+ track.enabled = !muted;
1663
+ });
1664
+ }, []);
1665
+ const sendText = (0, import_react5.useCallback)((text) => {
1666
+ if (!text?.trim() || wsRef.current?.readyState !== WebSocket.OPEN) return;
1667
+ wsRef.current.send(JSON.stringify({ type: "voice.text", text }));
1668
+ }, []);
1669
+ return {
1670
+ state,
1671
+ isMuted,
1672
+ transcript,
1673
+ partial,
1674
+ toolCalls,
1675
+ error,
1676
+ endReason,
1677
+ start,
1678
+ stop,
1679
+ mute,
1680
+ sendText
1681
+ };
1682
+ }
1683
+
1684
+ // src/hooks/useFiles.ts
1685
+ var import_react6 = require("react");
1257
1686
  function useFiles(autoFetch = true) {
1258
1687
  const { fetchWithAuth } = usePersonaContext();
1259
- const [files, setFiles] = (0, import_react5.useState)([]);
1260
- const [isLoading, setIsLoading] = (0, import_react5.useState)(false);
1261
- const [isUploading, setIsUploading] = (0, import_react5.useState)(false);
1262
- const [error, setError] = (0, import_react5.useState)(null);
1263
- const fetchFiles = (0, import_react5.useCallback)(async () => {
1688
+ const [files, setFiles] = (0, import_react6.useState)([]);
1689
+ const [isLoading, setIsLoading] = (0, import_react6.useState)(false);
1690
+ const [isUploading, setIsUploading] = (0, import_react6.useState)(false);
1691
+ const [error, setError] = (0, import_react6.useState)(null);
1692
+ const fetchFiles = (0, import_react6.useCallback)(async () => {
1264
1693
  setIsLoading(true);
1265
1694
  setError(null);
1266
1695
  try {
@@ -1278,7 +1707,7 @@ function useFiles(autoFetch = true) {
1278
1707
  setIsLoading(false);
1279
1708
  }
1280
1709
  }, [fetchWithAuth]);
1281
- const uploadFile = (0, import_react5.useCallback)(
1710
+ const uploadFile = (0, import_react6.useCallback)(
1282
1711
  async (fileOrFormData) => {
1283
1712
  setIsUploading(true);
1284
1713
  setError(null);
@@ -1311,7 +1740,7 @@ function useFiles(autoFetch = true) {
1311
1740
  },
1312
1741
  [fetchWithAuth, fetchFiles]
1313
1742
  );
1314
- const deleteFile = (0, import_react5.useCallback)(
1743
+ const deleteFile = (0, import_react6.useCallback)(
1315
1744
  async (fileId) => {
1316
1745
  const res = await fetchWithAuth(`/files/${fileId}`, { method: "DELETE" });
1317
1746
  if (!res.ok) throw new Error(`Failed to delete file: ${res.statusText}`);
@@ -1319,7 +1748,7 @@ function useFiles(autoFetch = true) {
1319
1748
  },
1320
1749
  [fetchWithAuth]
1321
1750
  );
1322
- const bulkDeleteFiles = (0, import_react5.useCallback)(
1751
+ const bulkDeleteFiles = (0, import_react6.useCallback)(
1323
1752
  async (fileIds) => {
1324
1753
  const res = await fetchWithAuth("/files/bulk-delete", {
1325
1754
  method: "POST",
@@ -1334,10 +1763,10 @@ function useFiles(autoFetch = true) {
1334
1763
  },
1335
1764
  [fetchWithAuth]
1336
1765
  );
1337
- const getDownloadUrl = (0, import_react5.useCallback)((fileId) => {
1766
+ const getDownloadUrl = (0, import_react6.useCallback)((fileId) => {
1338
1767
  return `/files/${fileId}`;
1339
1768
  }, []);
1340
- (0, import_react5.useEffect)(() => {
1769
+ (0, import_react6.useEffect)(() => {
1341
1770
  if (autoFetch) {
1342
1771
  void fetchFiles();
1343
1772
  }
@@ -1356,13 +1785,13 @@ function useFiles(autoFetch = true) {
1356
1785
  }
1357
1786
 
1358
1787
  // src/hooks/useAgents.ts
1359
- var import_react6 = require("react");
1788
+ var import_react7 = require("react");
1360
1789
  function useAgents(autoFetch = true) {
1361
1790
  const { fetchWithAuth } = usePersonaContext();
1362
- const [agents, setAgents] = (0, import_react6.useState)([]);
1363
- const [isLoading, setIsLoading] = (0, import_react6.useState)(false);
1364
- const [error, setError] = (0, import_react6.useState)(null);
1365
- const fetchAgents = (0, import_react6.useCallback)(async () => {
1791
+ const [agents, setAgents] = (0, import_react7.useState)([]);
1792
+ const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
1793
+ const [error, setError] = (0, import_react7.useState)(null);
1794
+ const fetchAgents = (0, import_react7.useCallback)(async () => {
1366
1795
  setIsLoading(true);
1367
1796
  setError(null);
1368
1797
  try {
@@ -1380,7 +1809,7 @@ function useAgents(autoFetch = true) {
1380
1809
  setIsLoading(false);
1381
1810
  }
1382
1811
  }, [fetchWithAuth]);
1383
- (0, import_react6.useEffect)(() => {
1812
+ (0, import_react7.useEffect)(() => {
1384
1813
  if (autoFetch) {
1385
1814
  void fetchAgents();
1386
1815
  }
@@ -1394,13 +1823,13 @@ function useAgents(autoFetch = true) {
1394
1823
  }
1395
1824
 
1396
1825
  // src/hooks/useConnection.ts
1397
- var import_react7 = require("react");
1826
+ var import_react8 = require("react");
1398
1827
  function useConnection(autoCheck = true) {
1399
1828
  const { fetchWithAuth } = usePersonaContext();
1400
- const [health, setHealth] = (0, import_react7.useState)(null);
1401
- const [isConnected, setIsConnected] = (0, import_react7.useState)(false);
1402
- const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
1403
- const checkHealth = (0, import_react7.useCallback)(async () => {
1829
+ const [health, setHealth] = (0, import_react8.useState)(null);
1830
+ const [isConnected, setIsConnected] = (0, import_react8.useState)(false);
1831
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
1832
+ const checkHealth = (0, import_react8.useCallback)(async () => {
1404
1833
  setIsLoading(true);
1405
1834
  try {
1406
1835
  const res = await fetchWithAuth("/health");
@@ -1419,7 +1848,7 @@ function useConnection(autoCheck = true) {
1419
1848
  setIsLoading(false);
1420
1849
  }
1421
1850
  }, [fetchWithAuth]);
1422
- (0, import_react7.useEffect)(() => {
1851
+ (0, import_react8.useEffect)(() => {
1423
1852
  if (autoCheck) {
1424
1853
  void checkHealth();
1425
1854
  }
@@ -1433,15 +1862,15 @@ function useConnection(autoCheck = true) {
1433
1862
  }
1434
1863
 
1435
1864
  // src/hooks/useMcpConnections.ts
1436
- var import_react8 = require("react");
1865
+ var import_react9 = require("react");
1437
1866
  function useMcpConnections(options = {}) {
1438
1867
  const { defaultAgentId, fetchWithAuth } = usePersonaContext();
1439
1868
  const agentId = options.agentId ?? defaultAgentId;
1440
1869
  const autoFetch = options.autoFetch ?? true;
1441
- const [connections, setConnections] = (0, import_react8.useState)([]);
1442
- const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
1443
- const [error, setError] = (0, import_react8.useState)(null);
1444
- const fetchConnections = (0, import_react8.useCallback)(async () => {
1870
+ const [connections, setConnections] = (0, import_react9.useState)([]);
1871
+ const [isLoading, setIsLoading] = (0, import_react9.useState)(false);
1872
+ const [error, setError] = (0, import_react9.useState)(null);
1873
+ const fetchConnections = (0, import_react9.useCallback)(async () => {
1445
1874
  if (!agentId) return [];
1446
1875
  setIsLoading(true);
1447
1876
  setError(null);
@@ -1465,7 +1894,7 @@ function useMcpConnections(options = {}) {
1465
1894
  setIsLoading(false);
1466
1895
  }
1467
1896
  }, [agentId, fetchWithAuth, options.returnTo]);
1468
- (0, import_react8.useEffect)(() => {
1897
+ (0, import_react9.useEffect)(() => {
1469
1898
  if (autoFetch) void fetchConnections();
1470
1899
  }, [autoFetch, fetchConnections]);
1471
1900
  return {
@@ -1480,7 +1909,7 @@ function useMcpConnections(options = {}) {
1480
1909
 
1481
1910
  // src/index.ts
1482
1911
  var import_logger2 = require("@personaai/logger");
1483
- var VERSION = "0.6.0";
1912
+ var VERSION = "0.7.0";
1484
1913
  // Annotate the CommonJS export names for ESM import in node:
1485
1914
  0 && (module.exports = {
1486
1915
  PersonaProvider,
@@ -1499,6 +1928,7 @@ var VERSION = "0.6.0";
1499
1928
  useMcpConnections,
1500
1929
  useMemory,
1501
1930
  usePersonaContext,
1502
- useThreads
1931
+ useThreads,
1932
+ useVoice
1503
1933
  });
1504
1934
  //# sourceMappingURL=index.cjs.map