@personaai/react 0.8.1 → 0.10.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/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ ARCHITECT_AGENT_ID: () => ARCHITECT_AGENT_ID,
23
24
  PersonaProvider: () => PersonaProvider,
24
25
  VERSION: () => VERSION,
25
26
  createLogger: () => import_logger2.createLogger,
@@ -30,6 +31,7 @@ __export(index_exports, {
30
31
  setLogLevel: () => import_logger2.setLogLevel,
31
32
  supportsStreamingFetch: () => supportsStreamingFetch,
32
33
  useAgents: () => useAgents,
34
+ useArchitectChat: () => useArchitectChat,
33
35
  useChat: () => useChat,
34
36
  useConnection: () => useConnection,
35
37
  useFiles: () => useFiles,
@@ -38,7 +40,11 @@ __export(index_exports, {
38
40
  useMemory: () => useMemory,
39
41
  usePersonaContext: () => usePersonaContext,
40
42
  useThreads: () => useThreads,
41
- useVoice: () => useVoice
43
+ useVoice: () => useVoice,
44
+ useWorkflow: () => useWorkflow,
45
+ useWorkflowRuns: () => useWorkflowRuns,
46
+ useWorkflowStream: () => useWorkflowStream,
47
+ useWorkflows: () => useWorkflows
42
48
  });
43
49
  module.exports = __toCommonJS(index_exports);
44
50
 
@@ -177,7 +183,8 @@ function fetchReader(response, controller) {
177
183
  function xhrStream(opts) {
178
184
  return new Promise((resolveStream, rejectStream) => {
179
185
  const xhr = new XMLHttpRequest();
180
- xhr.open("POST", opts.url);
186
+ const method = opts.method ?? "POST";
187
+ xhr.open(method, opts.url);
181
188
  for (const [key, val] of Object.entries(opts.headers))
182
189
  xhr.setRequestHeader(key, val);
183
190
  let consumed = 0;
@@ -276,7 +283,7 @@ function xhrStream(opts) {
276
283
  });
277
284
  }
278
285
  }
279
- xhr.send(opts.body);
286
+ xhr.send(method === "GET" ? null : opts.body ?? null);
280
287
  });
281
288
  }
282
289
  async function openSSEStream(opts) {
@@ -288,10 +295,11 @@ async function openSSEStream(opts) {
288
295
  if (opts.signal.aborted) controller.abort();
289
296
  else opts.signal.addEventListener("abort", () => controller.abort());
290
297
  }
298
+ const method = opts.method ?? "POST";
291
299
  const response = await fetch(opts.url, {
292
- method: "POST",
300
+ method,
293
301
  headers: opts.headers,
294
- body: opts.body,
302
+ body: method === "GET" ? void 0 : opts.body,
295
303
  signal: controller.signal
296
304
  });
297
305
  if (!response.ok) {
@@ -328,7 +336,7 @@ async function openSSEStream(opts) {
328
336
  };
329
337
  }
330
338
 
331
- // src/hooks/useChat.ts
339
+ // src/hooks/streamEventHelpers.ts
332
340
  function isErrorToolContent(content) {
333
341
  if (typeof content !== "string" || !content.trim().startsWith("{"))
334
342
  return false;
@@ -374,6 +382,38 @@ function parsePresentedFile(content) {
374
382
  return null;
375
383
  }
376
384
  }
385
+ function normalizeWorkspaceFiles(raw) {
386
+ const normalized = {};
387
+ for (const [path, file] of Object.entries(raw || {})) {
388
+ normalized[path] = {
389
+ content: file.content,
390
+ size: file.size,
391
+ createdAt: file.created_at,
392
+ modifiedAt: file.modified_at
393
+ };
394
+ }
395
+ return normalized;
396
+ }
397
+ function normalizePendingInterrupt(pending) {
398
+ if (!pending || typeof pending !== "object") return null;
399
+ const p = pending;
400
+ if (p.kind === "hitl") {
401
+ return {
402
+ kind: "hitl",
403
+ actionRequests: p.value?.actionRequests ?? [],
404
+ reviewConfigs: p.value?.reviewConfigs ?? []
405
+ };
406
+ }
407
+ if (p.kind === "clarification") {
408
+ return {
409
+ kind: "clarification",
410
+ questions: p.value?.questions ?? []
411
+ };
412
+ }
413
+ return null;
414
+ }
415
+
416
+ // src/hooks/useChat.ts
377
417
  function parseSandboxCommand(tc) {
378
418
  let command = tc.args ?? "";
379
419
  try {
@@ -405,36 +445,6 @@ function parseSandboxCommand(tc) {
405
445
  seq: tc.seq
406
446
  };
407
447
  }
408
- function normalizeWorkspaceFiles(raw) {
409
- const normalized = {};
410
- for (const [path, file] of Object.entries(raw || {})) {
411
- normalized[path] = {
412
- content: file.content,
413
- size: file.size,
414
- createdAt: file.created_at,
415
- modifiedAt: file.modified_at
416
- };
417
- }
418
- return normalized;
419
- }
420
- function normalizePendingInterrupt(pending) {
421
- if (!pending || typeof pending !== "object") return null;
422
- const p = pending;
423
- if (p.kind === "hitl") {
424
- return {
425
- kind: "hitl",
426
- actionRequests: p.value?.actionRequests ?? [],
427
- reviewConfigs: p.value?.reviewConfigs ?? []
428
- };
429
- }
430
- if (p.kind === "clarification") {
431
- return {
432
- kind: "clarification",
433
- questions: p.value?.questions ?? []
434
- };
435
- }
436
- return null;
437
- }
438
448
  function useChat(options = {}) {
439
449
  const { defaultAgentId, fetchWithAuth, baseUrl, getAuthToken, logger } = usePersonaContext();
440
450
  const chatLogger = (0, import_react2.useMemo)(() => logger.child("chat"), [logger]);
@@ -1148,17 +1158,569 @@ function useChat(options = {}) {
1148
1158
  };
1149
1159
  }
1150
1160
 
1151
- // src/hooks/useMemory.ts
1161
+ // src/hooks/useArchitectChat.ts
1152
1162
  var import_react3 = require("react");
1163
+ var ARCHITECT_AGENT_ID = "000000000000000000000002";
1164
+ function useArchitectChat(options = {}) {
1165
+ const { fetchWithAuth, baseUrl, getAuthToken, logger } = usePersonaContext();
1166
+ const chatLogger = (0, import_react3.useMemo)(() => logger.child("architect"), [logger]);
1167
+ const threadId = options.threadId;
1168
+ const [internalThreadId, setInternalThreadId] = (0, import_react3.useState)(void 0);
1169
+ const effectiveThreadId = threadId ?? internalThreadId;
1170
+ (0, import_react3.useEffect)(() => {
1171
+ if (threadId !== void 0) setInternalThreadId(threadId);
1172
+ }, [threadId]);
1173
+ const didLogInitRef = (0, import_react3.useRef)(false);
1174
+ if (!didLogInitRef.current) {
1175
+ didLogInitRef.current = true;
1176
+ chatLogger.debug("useArchitectChat init", {
1177
+ threadId,
1178
+ hasInitialMessages: !!options.initialMessages?.length
1179
+ });
1180
+ chatLogger.trace("useArchitectChat options", {
1181
+ threadId,
1182
+ initialMessageCount: options.initialMessages?.length ?? 0
1183
+ });
1184
+ }
1185
+ const [messages, setMessages] = (0, import_react3.useState)(
1186
+ options.initialMessages || []
1187
+ );
1188
+ const [input, setInput] = (0, import_react3.useState)("");
1189
+ const [isStreaming, setIsStreaming] = (0, import_react3.useState)(false);
1190
+ const [isLoadingHistory, setIsLoadingHistory] = (0, import_react3.useState)(false);
1191
+ const [error, setError] = (0, import_react3.useState)(null);
1192
+ const [interrupt, setInterrupt] = (0, import_react3.useState)(null);
1193
+ const [files, setFiles] = (0, import_react3.useState)({});
1194
+ const [todos, setTodos] = (0, import_react3.useState)([]);
1195
+ const [presentedFile, setPresentedFile] = (0, import_react3.useState)(null);
1196
+ const abortControllerRef = (0, import_react3.useRef)(null);
1197
+ const loadedThreadIdRef = (0, import_react3.useRef)(void 0);
1198
+ const stop = (0, import_react3.useCallback)(() => {
1199
+ if (abortControllerRef.current) {
1200
+ chatLogger.info("stop streaming", {});
1201
+ chatLogger.debug("abort controller", {
1202
+ hasController: !!abortControllerRef.current
1203
+ });
1204
+ abortControllerRef.current.abort();
1205
+ abortControllerRef.current = null;
1206
+ setIsStreaming(false);
1207
+ chatLogger.debug("streaming stopped");
1208
+ } else {
1209
+ chatLogger.trace("stop called \u2014 no active stream");
1210
+ }
1211
+ }, [chatLogger]);
1212
+ const clear = (0, import_react3.useCallback)(() => {
1213
+ chatLogger.info("clear chat", { messageCount: messages.length });
1214
+ chatLogger.debug("clear", { messageCount: messages.length });
1215
+ stop();
1216
+ setMessages([]);
1217
+ setError(null);
1218
+ setInterrupt(null);
1219
+ setFiles({});
1220
+ setTodos([]);
1221
+ setPresentedFile(null);
1222
+ loadedThreadIdRef.current = void 0;
1223
+ }, [stop, chatLogger, messages.length]);
1224
+ const startNewChat = (0, import_react3.useCallback)(() => {
1225
+ chatLogger.info("startNewChat \u2014 entering ephemeral mode");
1226
+ stop();
1227
+ setMessages([]);
1228
+ setError(null);
1229
+ setInterrupt(null);
1230
+ setFiles({});
1231
+ setTodos([]);
1232
+ setPresentedFile(null);
1233
+ setInput("");
1234
+ loadedThreadIdRef.current = void 0;
1235
+ setInternalThreadId(void 0);
1236
+ }, [stop, chatLogger]);
1237
+ const loadThreadMessages = (0, import_react3.useCallback)(
1238
+ async (id) => {
1239
+ chatLogger.debug("loadThreadMessages start", { threadId: id });
1240
+ chatLogger.trace("loadThreadMessages", { threadId: id });
1241
+ setIsLoadingHistory(true);
1242
+ setError(null);
1243
+ try {
1244
+ const res = await fetchWithAuth(`/threads/${id}/messages`);
1245
+ if (!res.ok)
1246
+ throw new Error(`Failed to load thread history: ${res.statusText}`);
1247
+ const body = await res.json();
1248
+ const data = body?.data ?? body;
1249
+ const raw = data?.messages ?? [];
1250
+ const subagentTraces = data?.subagentTraces ?? {};
1251
+ const loaded = raw.map((m, i) => ({
1252
+ id: m.id || `history-${id}-${i}`,
1253
+ role: m.role,
1254
+ content: m.content,
1255
+ createdAt: /* @__PURE__ */ new Date(),
1256
+ toolCalls: m.toolCalls?.map((tc) => {
1257
+ const trace = subagentTraces[tc.toolCallId];
1258
+ return Array.isArray(trace) && trace.length > 0 ? {
1259
+ ...tc,
1260
+ subagentActivity: persistedTraceToActivityEntries(trace)
1261
+ } : tc;
1262
+ })
1263
+ }));
1264
+ setMessages(loaded);
1265
+ setInterrupt(normalizePendingInterrupt(data?.pendingInterrupt));
1266
+ setFiles(normalizeWorkspaceFiles(data?.state?.files ?? {}));
1267
+ setTodos(data?.state?.todos ?? []);
1268
+ loadedThreadIdRef.current = id;
1269
+ chatLogger.info("loadThreadMessages succeeded", {
1270
+ threadId: id,
1271
+ messageCount: loaded.length
1272
+ });
1273
+ chatLogger.debug("loadThreadMessages completed", {
1274
+ threadId: id,
1275
+ messageCount: loaded.length
1276
+ });
1277
+ return loaded;
1278
+ } catch (err) {
1279
+ const errorObj = err instanceof Error ? err : new Error(String(err));
1280
+ chatLogger.warn("loadThreadMessages failed", {
1281
+ threadId: id,
1282
+ error: errorObj.message
1283
+ });
1284
+ chatLogger.error("loadThreadMessages error", {
1285
+ threadId: id,
1286
+ error: errorObj.message
1287
+ });
1288
+ setError(errorObj);
1289
+ if (loadedThreadIdRef.current === id) loadedThreadIdRef.current = void 0;
1290
+ return [];
1291
+ } finally {
1292
+ setIsLoadingHistory(false);
1293
+ chatLogger.trace("loadThreadMessages end", { threadId: id });
1294
+ }
1295
+ },
1296
+ [fetchWithAuth, chatLogger]
1297
+ );
1298
+ (0, import_react3.useEffect)(() => {
1299
+ if (!effectiveThreadId || isStreaming) {
1300
+ chatLogger.trace("auto-load skipped", { threadId: effectiveThreadId, isStreaming });
1301
+ return;
1302
+ }
1303
+ if (loadedThreadIdRef.current === effectiveThreadId) {
1304
+ chatLogger.trace("auto-load already loaded", { threadId: effectiveThreadId });
1305
+ return;
1306
+ }
1307
+ if (messages.length > 0) {
1308
+ chatLogger.trace("auto-load has messages", {
1309
+ threadId: effectiveThreadId,
1310
+ count: messages.length
1311
+ });
1312
+ return;
1313
+ }
1314
+ chatLogger.info("auto-load thread history", { threadId: effectiveThreadId });
1315
+ chatLogger.debug("loadThreadMessages trigger", { threadId: effectiveThreadId });
1316
+ void loadThreadMessages(effectiveThreadId);
1317
+ }, [effectiveThreadId, isStreaming, messages.length, loadThreadMessages, chatLogger]);
1318
+ const doSend = (0, import_react3.useCallback)(
1319
+ async (prompt, baseMessages, overrideOptions) => {
1320
+ chatLogger.debug("sendMessage start", {
1321
+ promptPreview: prompt.slice(0, 100),
1322
+ threadId: effectiveThreadId ?? overrideOptions?.threadId,
1323
+ hasResume: !!overrideOptions?.resume,
1324
+ messageCount: baseMessages.length
1325
+ });
1326
+ chatLogger.trace("sendMessage details", {
1327
+ promptPreview: prompt.slice(0, 200),
1328
+ hasResume: !!overrideOptions?.resume
1329
+ });
1330
+ const userMessage = {
1331
+ id: `user-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
1332
+ role: "user",
1333
+ content: prompt,
1334
+ createdAt: /* @__PURE__ */ new Date()
1335
+ };
1336
+ const assistantMessageId = `assistant-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
1337
+ const placeholderAssistant = {
1338
+ id: assistantMessageId,
1339
+ role: "assistant",
1340
+ content: "",
1341
+ createdAt: /* @__PURE__ */ new Date(),
1342
+ isStreaming: true,
1343
+ toolCalls: []
1344
+ };
1345
+ const nextMessages = [...baseMessages, userMessage];
1346
+ setMessages([...nextMessages, placeholderAssistant]);
1347
+ setInput("");
1348
+ setIsStreaming(true);
1349
+ setError(null);
1350
+ setInterrupt(null);
1351
+ const controller = new AbortController();
1352
+ abortControllerRef.current = controller;
1353
+ const finalizeReasoning = () => {
1354
+ setMessages(
1355
+ (prev) => prev.map((m) => m.role === "reasoning" && m.isStreaming ? { ...m, isStreaming: false } : m)
1356
+ );
1357
+ };
1358
+ try {
1359
+ const payloadMessages = nextMessages.filter((m) => m.role !== "reasoning").map((m) => ({
1360
+ role: m.role === "assistant" ? "assistant" : "user",
1361
+ content: m.content
1362
+ }));
1363
+ let resolvedThreadId = await overrideOptions?.threadId ?? effectiveThreadId;
1364
+ if (!resolvedThreadId) {
1365
+ chatLogger.info("minting real thread for ephemeral architect chat", {});
1366
+ const createRes = await fetchWithAuth("/threads", {
1367
+ method: "POST",
1368
+ headers: { "Content-Type": "application/json" },
1369
+ body: JSON.stringify({ agentId: ARCHITECT_AGENT_ID })
1370
+ });
1371
+ if (!createRes.ok) throw new Error(`Failed to create thread: ${createRes.statusText}`);
1372
+ const createBody = await createRes.json();
1373
+ const created = createBody?.data ?? createBody;
1374
+ resolvedThreadId = created._id ?? created.id;
1375
+ if (!resolvedThreadId) throw new Error("Thread creation returned no id");
1376
+ setInternalThreadId(resolvedThreadId);
1377
+ loadedThreadIdRef.current = resolvedThreadId;
1378
+ options.onThreadCreated?.(resolvedThreadId);
1379
+ chatLogger.info("ephemeral architect chat minted", { threadId: resolvedThreadId });
1380
+ }
1381
+ chatLogger.trace("resolved threadId", { threadId: resolvedThreadId });
1382
+ const token = getAuthToken ? await getAuthToken() : null;
1383
+ chatLogger.debug("opening SSE stream", {
1384
+ hasToken: !!token,
1385
+ hasThreadId: !!resolvedThreadId
1386
+ });
1387
+ const stream = await openSSEStream({
1388
+ url: `${baseUrl}/architect`,
1389
+ headers: {
1390
+ "Content-Type": "application/json",
1391
+ ...token ? { Authorization: `Bearer ${token}` } : {}
1392
+ },
1393
+ body: JSON.stringify({
1394
+ messages: payloadMessages,
1395
+ threadId: resolvedThreadId,
1396
+ resume: overrideOptions?.resume
1397
+ }),
1398
+ signal: controller.signal
1399
+ });
1400
+ if (!stream.ok) {
1401
+ chatLogger.warn("SSE stream not ok", { status: stream.status, errorText: stream.errorText });
1402
+ chatLogger.error("architect stream failed", { status: stream.status, error: stream.errorText });
1403
+ throw new Error(`Architect error (${stream.status}): ${stream.errorText ?? "Stream failed"}`);
1404
+ }
1405
+ chatLogger.debug("SSE stream opened", { status: stream.status });
1406
+ chatLogger.info("architect stream started", { threadId: resolvedThreadId });
1407
+ const reader = stream.reader;
1408
+ let buffer = "";
1409
+ let accumulatedText = "";
1410
+ const toolCallsMap = /* @__PURE__ */ new Map();
1411
+ let streamSeq = 0;
1412
+ let activeReasoningId = null;
1413
+ const reasoningById = /* @__PURE__ */ new Map();
1414
+ const patchAssistant = (patch) => {
1415
+ setMessages(
1416
+ (prev) => prev.map(
1417
+ (msg) => msg.id === assistantMessageId ? { ...msg, toolCalls: Array.from(toolCallsMap.values()), ...patch } : msg
1418
+ )
1419
+ );
1420
+ };
1421
+ const insertReasoningMessage = (id, seq) => {
1422
+ const msg = { id, role: "reasoning", content: "", createdAt: /* @__PURE__ */ new Date(), isStreaming: true, seq };
1423
+ setMessages((prev) => {
1424
+ const idx = prev.findIndex((m) => m.id === assistantMessageId);
1425
+ if (idx === -1) return [...prev, msg];
1426
+ const next = [...prev];
1427
+ next.splice(idx, 0, msg);
1428
+ return next;
1429
+ });
1430
+ };
1431
+ while (true) {
1432
+ const { done, value } = await reader.read();
1433
+ if (done) break;
1434
+ buffer += value ?? "";
1435
+ const lines = buffer.split("\n");
1436
+ buffer = lines.pop() ?? "";
1437
+ for (const line of lines) {
1438
+ const trimmed = line.trim();
1439
+ if (!trimmed || !trimmed.startsWith("data:")) continue;
1440
+ const raw = trimmed.replace(/^data:\s*/, "");
1441
+ if (raw === "[DONE]") break;
1442
+ try {
1443
+ const event = JSON.parse(raw);
1444
+ chatLogger.trace("stream event", { type: event.type, event });
1445
+ options.onEvent?.(event);
1446
+ if (event.type === "TEXT_MESSAGE_CHUNK" && event.delta) {
1447
+ chatLogger.debug("text chunk", { deltaLength: event.delta.length });
1448
+ accumulatedText += event.delta;
1449
+ patchAssistant({ content: accumulatedText, isStreaming: true });
1450
+ } else if (event.type === "TOOL_CALL_CHUNK" && event.toolCallId) {
1451
+ chatLogger.debug("tool call chunk", { toolCallId: event.toolCallId, toolCallName: event.toolCallName });
1452
+ const existing = toolCallsMap.get(event.toolCallId);
1453
+ if (existing) {
1454
+ existing.args = (existing.args || "") + (event.delta || "");
1455
+ } else {
1456
+ toolCallsMap.set(event.toolCallId, {
1457
+ toolCallId: event.toolCallId,
1458
+ toolName: event.toolCallName || "",
1459
+ args: event.delta || "",
1460
+ seq: streamSeq++
1461
+ });
1462
+ }
1463
+ patchAssistant({});
1464
+ } else if (event.type === "TOOL_CALL_RESULT") {
1465
+ chatLogger.debug("tool call result", { toolCallId: event.toolCallId });
1466
+ const existing = toolCallsMap.get(event.toolCallId);
1467
+ if (existing) {
1468
+ existing.result = event.content;
1469
+ existing.isError = isErrorToolContent(event.content);
1470
+ if (existing.isError) chatLogger.warn("tool call error", { toolCallId: event.toolCallId });
1471
+ if (existing.toolName === "present_file" && !existing.isError) {
1472
+ const presented = parsePresentedFile(event.content);
1473
+ if (presented) {
1474
+ chatLogger.info("present_file", { path: presented.path });
1475
+ setPresentedFile(presented);
1476
+ }
1477
+ }
1478
+ patchAssistant({});
1479
+ }
1480
+ } else if (event.type === "STATE_SNAPSHOT") {
1481
+ chatLogger.debug("state snapshot", {
1482
+ fileCount: Object.keys(event.snapshot.files ?? {}).length,
1483
+ todoCount: event.snapshot.todos?.length ?? 0
1484
+ });
1485
+ setFiles(normalizeWorkspaceFiles(event.snapshot.files ?? {}));
1486
+ setTodos(event.snapshot.todos ?? []);
1487
+ } else if (event.type === "REASONING_MESSAGE_START" && event.messageId) {
1488
+ chatLogger.debug("reasoning start", { messageId: event.messageId });
1489
+ activeReasoningId = event.messageId;
1490
+ reasoningById.set(event.messageId, { content: "" });
1491
+ insertReasoningMessage(event.messageId, streamSeq++);
1492
+ } else if (event.type === "REASONING_MESSAGE_CONTENT") {
1493
+ let rid = event.messageId || activeReasoningId || "";
1494
+ if (!rid || !reasoningById.has(rid)) {
1495
+ rid = rid || `reasoning-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
1496
+ activeReasoningId = rid;
1497
+ reasoningById.set(rid, { content: "" });
1498
+ insertReasoningMessage(rid, streamSeq++);
1499
+ chatLogger.debug("reasoning lazy start", { messageId: rid });
1500
+ }
1501
+ const entry = reasoningById.get(rid);
1502
+ entry.content += event.delta;
1503
+ chatLogger.trace("reasoning content", { messageId: rid, deltaLength: event.delta?.length ?? 0 });
1504
+ setMessages((prev) => prev.map((m) => m.id === rid ? { ...m, content: entry.content, isStreaming: true } : m));
1505
+ } else if (event.type === "REASONING_END") {
1506
+ chatLogger.debug("reasoning end", { messageId: activeReasoningId });
1507
+ const rid = activeReasoningId;
1508
+ activeReasoningId = null;
1509
+ if (rid) {
1510
+ setMessages((prev) => prev.map((m) => m.id === rid ? { ...m, isStreaming: false } : m));
1511
+ }
1512
+ } else if (event.type === "CUSTOM") {
1513
+ chatLogger.debug("custom event", { name: event.name });
1514
+ if (event.name === "hitl_request") {
1515
+ const value2 = event.value;
1516
+ chatLogger.info("hitl interrupt", { actionCount: value2.actionRequests?.length ?? 0 });
1517
+ setInterrupt({ kind: "hitl", actionRequests: value2.actionRequests, reviewConfigs: value2.reviewConfigs });
1518
+ } else if (event.name === "clarification_request") {
1519
+ const value2 = event.value;
1520
+ chatLogger.info("clarification interrupt", { questionCount: value2.questions?.length ?? 0 });
1521
+ setInterrupt({ kind: "clarification", questions: value2.questions });
1522
+ } else if (event.name === "subagent_activity") {
1523
+ const { toolCallId, ...entry } = event.value;
1524
+ chatLogger.trace("subagent activity", { toolCallId, kind: entry.kind });
1525
+ const existing = toolCallsMap.get(toolCallId);
1526
+ if (existing) {
1527
+ existing.subagentActivity = [...existing.subagentActivity || [], entry];
1528
+ patchAssistant({});
1529
+ }
1530
+ } else if (event.name === "mcp_app") {
1531
+ const val = event.value;
1532
+ if (val?.toolCallId && val?.resourceUri && val?.mcpId) {
1533
+ const existing = toolCallsMap.get(val.toolCallId);
1534
+ if (existing) {
1535
+ existing.mcpApp = { resourceUri: val.resourceUri, mcpId: val.mcpId };
1536
+ patchAssistant({});
1537
+ }
1538
+ }
1539
+ }
1540
+ } else if (event.type === "title") {
1541
+ const title = event.title;
1542
+ if (typeof title === "string" && title.trim()) {
1543
+ chatLogger.info("auto title", { title });
1544
+ options.onTitle?.(title);
1545
+ }
1546
+ } else if (event.type === "RUN_ERROR") {
1547
+ chatLogger.warn("run error", { message: event.message, code: event.code });
1548
+ chatLogger.error("stream run error", { code: event.code, message: event.message });
1549
+ throw new Error(event.message || "Stream error from agent");
1550
+ } else {
1551
+ chatLogger.trace("unhandled event", { type: event.type });
1552
+ }
1553
+ } catch (e) {
1554
+ if (e instanceof Error && e.message.startsWith("Stream error")) throw e;
1555
+ chatLogger.warn("event parse error", { raw: raw.slice(0, 200), error: e instanceof Error ? e.message : String(e) });
1556
+ }
1557
+ }
1558
+ }
1559
+ const finalMessage = {
1560
+ id: assistantMessageId,
1561
+ role: "assistant",
1562
+ content: accumulatedText,
1563
+ createdAt: /* @__PURE__ */ new Date(),
1564
+ isStreaming: false,
1565
+ toolCalls: Array.from(toolCallsMap.values())
1566
+ };
1567
+ setMessages((prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMessage : msg));
1568
+ chatLogger.info("sendMessage succeeded", { textLength: accumulatedText.length, toolCallCount: toolCallsMap.size });
1569
+ chatLogger.debug("sendMessage completed", { textLength: accumulatedText.length, eventCount: toolCallsMap.size + 1 });
1570
+ options.onFinish?.(finalMessage);
1571
+ return true;
1572
+ } catch (err) {
1573
+ if (controller.signal.aborted) {
1574
+ chatLogger.info("sendMessage aborted", {});
1575
+ chatLogger.debug("stream aborted", {});
1576
+ setMessages((prev) => prev.map((msg) => msg.id === assistantMessageId ? { ...msg, isStreaming: false } : msg));
1577
+ return false;
1578
+ }
1579
+ const errorObj = err instanceof Error ? err : new Error(String(err));
1580
+ chatLogger.warn("sendMessage failed", { error: errorObj.message });
1581
+ chatLogger.error("sendMessage error", { error: errorObj.message });
1582
+ setError(errorObj);
1583
+ options.onError?.(errorObj);
1584
+ setMessages(
1585
+ (prev) => prev.map(
1586
+ (msg) => msg.id === assistantMessageId ? { ...msg, content: msg.content || `\u26A0\uFE0F Error: ${errorObj.message || "Failed to get response."}`, isStreaming: false } : msg
1587
+ )
1588
+ );
1589
+ return false;
1590
+ } finally {
1591
+ setIsStreaming(false);
1592
+ abortControllerRef.current = null;
1593
+ finalizeReasoning();
1594
+ chatLogger.debug("sendMessage finally", { isStreaming: false });
1595
+ chatLogger.trace("sendMessage end", {});
1596
+ }
1597
+ },
1598
+ [baseUrl, getAuthToken, fetchWithAuth, options, chatLogger, effectiveThreadId]
1599
+ );
1600
+ const sendMessage = (0, import_react3.useCallback)(
1601
+ async (contentToSend, overrideOptions) => {
1602
+ const prompt = (contentToSend ?? input).trim();
1603
+ if (!prompt || isStreaming) {
1604
+ chatLogger.trace("sendMessage skipped", {
1605
+ hasPrompt: !!prompt,
1606
+ isStreaming
1607
+ });
1608
+ if (isStreaming) {
1609
+ chatLogger.warn("sendMessage dropped while streaming \u2014 caller should queue or disable send", {});
1610
+ }
1611
+ return false;
1612
+ }
1613
+ return doSend(prompt, messages, overrideOptions);
1614
+ },
1615
+ [input, isStreaming, messages, doSend, chatLogger]
1616
+ );
1617
+ const handleInputChange = (0, import_react3.useCallback)(
1618
+ (e) => {
1619
+ chatLogger.trace("handleInputChange", { length: e.target.value.length });
1620
+ setInput(e.target.value);
1621
+ },
1622
+ [chatLogger]
1623
+ );
1624
+ const handleSubmit = (0, import_react3.useCallback)(
1625
+ (e) => {
1626
+ if (e) e.preventDefault();
1627
+ chatLogger.debug("handleSubmit", {});
1628
+ void sendMessage();
1629
+ },
1630
+ [sendMessage, chatLogger]
1631
+ );
1632
+ const reload = (0, import_react3.useCallback)(async () => {
1633
+ if (messages.length === 0 || isStreaming) {
1634
+ chatLogger.trace("reload skipped", {
1635
+ messageCount: messages.length,
1636
+ isStreaming
1637
+ });
1638
+ return false;
1639
+ }
1640
+ let lastUserIndex = -1;
1641
+ for (let i = messages.length - 1; i >= 0; i--) {
1642
+ if (messages[i].role === "user") {
1643
+ lastUserIndex = i;
1644
+ break;
1645
+ }
1646
+ }
1647
+ if (lastUserIndex === -1) {
1648
+ chatLogger.warn("reload no user message", {});
1649
+ return false;
1650
+ }
1651
+ const lastUserMessage = messages[lastUserIndex];
1652
+ const truncated = messages.slice(0, lastUserIndex);
1653
+ chatLogger.info("reload", { messageId: lastUserMessage.id });
1654
+ chatLogger.debug("reload last user", {
1655
+ preview: lastUserMessage.content.slice(0, 100)
1656
+ });
1657
+ return doSend(lastUserMessage.content, truncated);
1658
+ }, [isStreaming, messages, doSend, chatLogger]);
1659
+ const resumeInterrupt = (0, import_react3.useCallback)(
1660
+ (resume, displayContent) => {
1661
+ chatLogger.info("resumeInterrupt", {
1662
+ kind: resume.decisions ? "hitl" : "clarification",
1663
+ preview: displayContent.slice(0, 50)
1664
+ });
1665
+ return sendMessage(displayContent, { resume });
1666
+ },
1667
+ [sendMessage, chatLogger]
1668
+ );
1669
+ const dismissPresentedFile = (0, import_react3.useCallback)(() => {
1670
+ chatLogger.debug("dismissPresentedFile", {});
1671
+ setPresentedFile(null);
1672
+ }, [chatLogger]);
1673
+ const openWorkspaceFile = (0, import_react3.useCallback)(
1674
+ (path) => {
1675
+ chatLogger.debug("openWorkspaceFile", { path });
1676
+ setPresentedFile({
1677
+ path,
1678
+ title: path.split("/").pop() || path,
1679
+ description: ""
1680
+ });
1681
+ },
1682
+ [chatLogger]
1683
+ );
1684
+ return {
1685
+ messages,
1686
+ input,
1687
+ setInput,
1688
+ handleInputChange,
1689
+ handleSubmit,
1690
+ sendMessage,
1691
+ isStreaming,
1692
+ isLoading: isStreaming,
1693
+ isLoadingHistory,
1694
+ error,
1695
+ interrupt,
1696
+ resumeInterrupt,
1697
+ files,
1698
+ todos,
1699
+ presentedFile,
1700
+ dismissPresentedFile,
1701
+ openWorkspaceFile,
1702
+ stop,
1703
+ reload,
1704
+ clear,
1705
+ startNewChat,
1706
+ currentThreadId: effectiveThreadId,
1707
+ isEphemeral: !effectiveThreadId,
1708
+ setMessages,
1709
+ loadThreadMessages
1710
+ };
1711
+ }
1712
+
1713
+ // src/hooks/useMemory.ts
1714
+ var import_react4 = require("react");
1153
1715
  function useMemory(autoFetch = true) {
1154
1716
  const { fetchWithAuth } = usePersonaContext();
1155
- const [memory, setMemory] = (0, import_react3.useState)({
1717
+ const [memory, setMemory] = (0, import_react4.useState)({
1156
1718
  userFiles: [],
1157
1719
  agentMemories: []
1158
1720
  });
1159
- const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
1160
- const [error, setError] = (0, import_react3.useState)(null);
1161
- const fetchMemory = (0, import_react3.useCallback)(async () => {
1721
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
1722
+ const [error, setError] = (0, import_react4.useState)(null);
1723
+ const fetchMemory = (0, import_react4.useCallback)(async () => {
1162
1724
  setIsLoading(true);
1163
1725
  setError(null);
1164
1726
  try {
@@ -1177,7 +1739,7 @@ function useMemory(autoFetch = true) {
1177
1739
  setIsLoading(false);
1178
1740
  }
1179
1741
  }, [fetchWithAuth]);
1180
- const getFile = (0, import_react3.useCallback)(
1742
+ const getFile = (0, import_react4.useCallback)(
1181
1743
  async (params) => {
1182
1744
  const query = new URLSearchParams({ path: params.path });
1183
1745
  if (params.scope) query.set("scope", params.scope);
@@ -1189,7 +1751,7 @@ function useMemory(autoFetch = true) {
1189
1751
  },
1190
1752
  [fetchWithAuth]
1191
1753
  );
1192
- const writeFile = (0, import_react3.useCallback)(
1754
+ const writeFile = (0, import_react4.useCallback)(
1193
1755
  async (params) => {
1194
1756
  const res = await fetchWithAuth("/memory/file", {
1195
1757
  method: "PUT",
@@ -1204,7 +1766,7 @@ function useMemory(autoFetch = true) {
1204
1766
  },
1205
1767
  [fetchWithAuth, fetchMemory]
1206
1768
  );
1207
- const deleteFile = (0, import_react3.useCallback)(
1769
+ const deleteFile = (0, import_react4.useCallback)(
1208
1770
  async (params) => {
1209
1771
  const query = new URLSearchParams({ path: params.path });
1210
1772
  if (params.scope) query.set("scope", params.scope);
@@ -1218,7 +1780,7 @@ function useMemory(autoFetch = true) {
1218
1780
  },
1219
1781
  [fetchWithAuth, fetchMemory]
1220
1782
  );
1221
- (0, import_react3.useEffect)(() => {
1783
+ (0, import_react4.useEffect)(() => {
1222
1784
  if (autoFetch) {
1223
1785
  void fetchMemory();
1224
1786
  }
@@ -1235,13 +1797,13 @@ function useMemory(autoFetch = true) {
1235
1797
  }
1236
1798
 
1237
1799
  // src/hooks/useThreads.ts
1238
- var import_react4 = require("react");
1800
+ var import_react5 = require("react");
1239
1801
  function useThreads(autoFetch = true) {
1240
1802
  const { fetchWithAuth, defaultAgentId } = usePersonaContext();
1241
- const [threads, setThreads] = (0, import_react4.useState)([]);
1242
- const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
1243
- const [error, setError] = (0, import_react4.useState)(null);
1244
- const fetchThreads = (0, import_react4.useCallback)(async () => {
1803
+ const [threads, setThreads] = (0, import_react5.useState)([]);
1804
+ const [isLoading, setIsLoading] = (0, import_react5.useState)(false);
1805
+ const [error, setError] = (0, import_react5.useState)(null);
1806
+ const fetchThreads = (0, import_react5.useCallback)(async () => {
1245
1807
  setIsLoading(true);
1246
1808
  setError(null);
1247
1809
  try {
@@ -1259,7 +1821,7 @@ function useThreads(autoFetch = true) {
1259
1821
  setIsLoading(false);
1260
1822
  }
1261
1823
  }, [fetchWithAuth]);
1262
- const createThread = (0, import_react4.useCallback)(
1824
+ const createThread = (0, import_react5.useCallback)(
1263
1825
  async (agentId) => {
1264
1826
  const targetAgentId = agentId || defaultAgentId;
1265
1827
  const res = await fetchWithAuth("/threads", {
@@ -1276,7 +1838,7 @@ function useThreads(autoFetch = true) {
1276
1838
  },
1277
1839
  [fetchWithAuth, defaultAgentId, fetchThreads]
1278
1840
  );
1279
- const deleteThread = (0, import_react4.useCallback)(
1841
+ const deleteThread = (0, import_react5.useCallback)(
1280
1842
  async (threadId) => {
1281
1843
  const res = await fetchWithAuth(`/threads/${threadId}`, {
1282
1844
  method: "DELETE"
@@ -1287,7 +1849,7 @@ function useThreads(autoFetch = true) {
1287
1849
  },
1288
1850
  [fetchWithAuth]
1289
1851
  );
1290
- const bulkDeleteThreads = (0, import_react4.useCallback)(
1852
+ const bulkDeleteThreads = (0, import_react5.useCallback)(
1291
1853
  async (threadIds) => {
1292
1854
  const res = await fetchWithAuth("/threads/bulk-delete", {
1293
1855
  method: "POST",
@@ -1304,13 +1866,13 @@ function useThreads(autoFetch = true) {
1304
1866
  },
1305
1867
  [fetchWithAuth]
1306
1868
  );
1307
- const deleteAllThreads = (0, import_react4.useCallback)(async () => {
1869
+ const deleteAllThreads = (0, import_react5.useCallback)(async () => {
1308
1870
  const ids = threads.map((t) => t._id);
1309
1871
  for (let i = 0; i < ids.length; i += 100) {
1310
1872
  await bulkDeleteThreads(ids.slice(i, i + 100));
1311
1873
  }
1312
1874
  }, [threads, bulkDeleteThreads]);
1313
- const updateThread = (0, import_react4.useCallback)(
1875
+ const updateThread = (0, import_react5.useCallback)(
1314
1876
  async (threadId, input) => {
1315
1877
  const res = await fetchWithAuth(`/threads/${threadId}`, {
1316
1878
  method: "PATCH",
@@ -1328,11 +1890,11 @@ function useThreads(autoFetch = true) {
1328
1890
  },
1329
1891
  [fetchWithAuth]
1330
1892
  );
1331
- const renameThread = (0, import_react4.useCallback)(
1893
+ const renameThread = (0, import_react5.useCallback)(
1332
1894
  (threadId, title) => updateThread(threadId, { title }),
1333
1895
  [updateThread]
1334
1896
  );
1335
- const resetThread = (0, import_react4.useCallback)(
1897
+ const resetThread = (0, import_react5.useCallback)(
1336
1898
  async (threadId) => {
1337
1899
  const res = await fetchWithAuth(`/threads/${threadId}/reset`, {
1338
1900
  method: "POST"
@@ -1347,7 +1909,7 @@ function useThreads(autoFetch = true) {
1347
1909
  },
1348
1910
  [fetchWithAuth]
1349
1911
  );
1350
- const getThread = (0, import_react4.useCallback)(
1912
+ const getThread = (0, import_react5.useCallback)(
1351
1913
  async (threadId) => {
1352
1914
  const res = await fetchWithAuth(`/threads/${threadId}`);
1353
1915
  if (!res.ok) throw new Error(`Failed to fetch thread: ${res.statusText}`);
@@ -1356,7 +1918,7 @@ function useThreads(autoFetch = true) {
1356
1918
  },
1357
1919
  [fetchWithAuth]
1358
1920
  );
1359
- (0, import_react4.useEffect)(() => {
1921
+ (0, import_react5.useEffect)(() => {
1360
1922
  if (autoFetch) {
1361
1923
  void fetchThreads();
1362
1924
  }
@@ -1378,7 +1940,7 @@ function useThreads(autoFetch = true) {
1378
1940
  }
1379
1941
 
1380
1942
  // src/hooks/useVoice.ts
1381
- var import_react5 = require("react");
1943
+ var import_react6 = require("react");
1382
1944
 
1383
1945
  // src/hooks/voiceWorklets.ts
1384
1946
  var RECORDER_WORKLET_SOURCE = `
@@ -1481,42 +2043,42 @@ function mergeTranscriptText(prev, next) {
1481
2043
  }
1482
2044
  function useVoice(options = {}) {
1483
2045
  const { defaultAgentId, fetchWithAuth, logger } = usePersonaContext();
1484
- const voiceLogger = (0, import_react5.useMemo)(() => logger.child("voice"), [logger]);
2046
+ const voiceLogger = (0, import_react6.useMemo)(() => logger.child("voice"), [logger]);
1485
2047
  const agentId = options.agentId || defaultAgentId;
1486
2048
  const threadId = options.threadId;
1487
2049
  const contextOverride = options.contextOverride;
1488
2050
  const context = options.context;
1489
- const [state, setState] = (0, import_react5.useState)("idle");
1490
- const [isMuted, setIsMuted] = (0, import_react5.useState)(false);
1491
- const [transcript, setTranscript] = (0, import_react5.useState)(
2051
+ const [state, setState] = (0, import_react6.useState)("idle");
2052
+ const [isMuted, setIsMuted] = (0, import_react6.useState)(false);
2053
+ const [transcript, setTranscript] = (0, import_react6.useState)(
1492
2054
  []
1493
2055
  );
1494
- const [partial, setPartial] = (0, import_react5.useState)(
2056
+ const [partial, setPartial] = (0, import_react6.useState)(
1495
2057
  null
1496
2058
  );
1497
- const [toolCalls, setToolCalls] = (0, import_react5.useState)([]);
1498
- const [error, setError] = (0, import_react5.useState)(null);
1499
- const [endReason, setEndReason] = (0, import_react5.useState)(
2059
+ const [toolCalls, setToolCalls] = (0, import_react6.useState)([]);
2060
+ const [error, setError] = (0, import_react6.useState)(null);
2061
+ const [endReason, setEndReason] = (0, import_react6.useState)(
1500
2062
  null
1501
2063
  );
1502
- const wsRef = (0, import_react5.useRef)(null);
1503
- const streamRef = (0, import_react5.useRef)(null);
1504
- const inputCtxRef = (0, import_react5.useRef)(null);
1505
- const outputCtxRef = (0, import_react5.useRef)(null);
1506
- const recorderNodeRef = (0, import_react5.useRef)(null);
1507
- const playerNodeRef = (0, import_react5.useRef)(null);
1508
- const acceptedTurnSeqRef = (0, import_react5.useRef)(0);
1509
- const mountedRef = (0, import_react5.useRef)(true);
1510
- const lastFinalTurnRef = (0, import_react5.useRef)(
2064
+ const wsRef = (0, import_react6.useRef)(null);
2065
+ const streamRef = (0, import_react6.useRef)(null);
2066
+ const inputCtxRef = (0, import_react6.useRef)(null);
2067
+ const outputCtxRef = (0, import_react6.useRef)(null);
2068
+ const recorderNodeRef = (0, import_react6.useRef)(null);
2069
+ const playerNodeRef = (0, import_react6.useRef)(null);
2070
+ const acceptedTurnSeqRef = (0, import_react6.useRef)(0);
2071
+ const mountedRef = (0, import_react6.useRef)(true);
2072
+ const lastFinalTurnRef = (0, import_react6.useRef)(
1511
2073
  null
1512
2074
  );
1513
- (0, import_react5.useEffect)(() => {
2075
+ (0, import_react6.useEffect)(() => {
1514
2076
  mountedRef.current = true;
1515
2077
  return () => {
1516
2078
  mountedRef.current = false;
1517
2079
  };
1518
2080
  }, []);
1519
- const teardownAudio = (0, import_react5.useCallback)(() => {
2081
+ const teardownAudio = (0, import_react6.useCallback)(() => {
1520
2082
  try {
1521
2083
  streamRef.current?.getTracks().forEach((track) => track.stop());
1522
2084
  } catch {
@@ -1543,7 +2105,7 @@ function useVoice(options = {}) {
1543
2105
  }
1544
2106
  outputCtxRef.current = null;
1545
2107
  }, []);
1546
- const stop = (0, import_react5.useCallback)(() => {
2108
+ const stop = (0, import_react6.useCallback)(() => {
1547
2109
  voiceLogger.debug("stop() called");
1548
2110
  try {
1549
2111
  wsRef.current?.close(1e3, "client_stop");
@@ -1553,8 +2115,8 @@ function useVoice(options = {}) {
1553
2115
  teardownAudio();
1554
2116
  if (mountedRef.current) setState("idle");
1555
2117
  }, [teardownAudio, voiceLogger]);
1556
- (0, import_react5.useEffect)(() => stop, [stop]);
1557
- const startCapture = (0, import_react5.useCallback)(async (inputSampleRate) => {
2118
+ (0, import_react6.useEffect)(() => stop, [stop]);
2119
+ const startCapture = (0, import_react6.useCallback)(async (inputSampleRate) => {
1558
2120
  const stream = await navigator.mediaDevices.getUserMedia({
1559
2121
  audio: {
1560
2122
  echoCancellation: true,
@@ -1580,7 +2142,7 @@ function useVoice(options = {}) {
1580
2142
  recorderNodeRef.current = recorderNode;
1581
2143
  source.connect(recorderNode);
1582
2144
  }, []);
1583
- const setupPlayback = (0, import_react5.useCallback)(async (outputSampleRate) => {
2145
+ const setupPlayback = (0, import_react6.useCallback)(async (outputSampleRate) => {
1584
2146
  const outputCtx = new AudioContext({ sampleRate: outputSampleRate });
1585
2147
  outputCtxRef.current = outputCtx;
1586
2148
  await outputCtx.audioWorklet.addModule(playerWorkletUrl());
@@ -1588,7 +2150,7 @@ function useVoice(options = {}) {
1588
2150
  playerNode.connect(outputCtx.destination);
1589
2151
  playerNodeRef.current = playerNode;
1590
2152
  }, []);
1591
- const upsertToolCall = (0, import_react5.useCallback)(
2153
+ const upsertToolCall = (0, import_react6.useCallback)(
1592
2154
  (id, patch) => {
1593
2155
  setToolCalls((prev) => {
1594
2156
  const idx = prev.findIndex((t) => t.id === id);
@@ -1600,7 +2162,7 @@ function useVoice(options = {}) {
1600
2162
  },
1601
2163
  []
1602
2164
  );
1603
- const handleTranscript = (0, import_react5.useCallback)((value) => {
2165
+ const handleTranscript = (0, import_react6.useCallback)((value) => {
1604
2166
  const { speaker, text, isFinal, turnSeq } = value;
1605
2167
  if (!isFinal) {
1606
2168
  setPartial({ id: "partial", speaker, text });
@@ -1623,7 +2185,7 @@ function useVoice(options = {}) {
1623
2185
  });
1624
2186
  lastFinalTurnRef.current = { speaker, turnSeq };
1625
2187
  }, []);
1626
- const handleCustomEvent = (0, import_react5.useCallback)(
2188
+ const handleCustomEvent = (0, import_react6.useCallback)(
1627
2189
  (name, value) => {
1628
2190
  switch (name) {
1629
2191
  case "voice_session_ready":
@@ -1681,7 +2243,7 @@ function useVoice(options = {}) {
1681
2243
  },
1682
2244
  [handleTranscript, setupPlayback, startCapture, teardownAudio, voiceLogger]
1683
2245
  );
1684
- const handleMessage = (0, import_react5.useCallback)(
2246
+ const handleMessage = (0, import_react6.useCallback)(
1685
2247
  (event) => {
1686
2248
  if (typeof event.data !== "string") {
1687
2249
  const buf = event.data;
@@ -1739,7 +2301,7 @@ function useVoice(options = {}) {
1739
2301
  },
1740
2302
  [handleCustomEvent, upsertToolCall, voiceLogger]
1741
2303
  );
1742
- const start = (0, import_react5.useCallback)(async () => {
2304
+ const start = (0, import_react6.useCallback)(async () => {
1743
2305
  if (!agentId) {
1744
2306
  const err = new Error(
1745
2307
  "useVoice: no agentId provided and no defaultAgentId set on PersonaProvider"
@@ -1806,17 +2368,17 @@ function useVoice(options = {}) {
1806
2368
  teardownAudio();
1807
2369
  }
1808
2370
  }, [agentId, threadId, contextOverride, context, fetchWithAuth, handleMessage, teardownAudio, voiceLogger]);
1809
- const mute = (0, import_react5.useCallback)((muted) => {
2371
+ const mute = (0, import_react6.useCallback)((muted) => {
1810
2372
  setIsMuted(muted);
1811
2373
  streamRef.current?.getAudioTracks().forEach((track) => {
1812
2374
  track.enabled = !muted;
1813
2375
  });
1814
2376
  }, []);
1815
- const sendText = (0, import_react5.useCallback)((text) => {
2377
+ const sendText = (0, import_react6.useCallback)((text) => {
1816
2378
  if (!text?.trim() || wsRef.current?.readyState !== WebSocket.OPEN) return;
1817
2379
  wsRef.current.send(JSON.stringify({ type: "voice.text", text }));
1818
2380
  }, []);
1819
- const updateContext = (0, import_react5.useCallback)((newContext) => {
2381
+ const updateContext = (0, import_react6.useCallback)((newContext) => {
1820
2382
  if (wsRef.current?.readyState !== WebSocket.OPEN) return;
1821
2383
  wsRef.current.send(JSON.stringify({ type: "voice.context", context: newContext }));
1822
2384
  }, []);
@@ -1837,14 +2399,14 @@ function useVoice(options = {}) {
1837
2399
  }
1838
2400
 
1839
2401
  // src/hooks/useFiles.ts
1840
- var import_react6 = require("react");
2402
+ var import_react7 = require("react");
1841
2403
  function useFiles(autoFetch = true) {
1842
2404
  const { fetchWithAuth } = usePersonaContext();
1843
- const [files, setFiles] = (0, import_react6.useState)([]);
1844
- const [isLoading, setIsLoading] = (0, import_react6.useState)(false);
1845
- const [isUploading, setIsUploading] = (0, import_react6.useState)(false);
1846
- const [error, setError] = (0, import_react6.useState)(null);
1847
- const fetchFiles = (0, import_react6.useCallback)(async () => {
2405
+ const [files, setFiles] = (0, import_react7.useState)([]);
2406
+ const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
2407
+ const [isUploading, setIsUploading] = (0, import_react7.useState)(false);
2408
+ const [error, setError] = (0, import_react7.useState)(null);
2409
+ const fetchFiles = (0, import_react7.useCallback)(async () => {
1848
2410
  setIsLoading(true);
1849
2411
  setError(null);
1850
2412
  try {
@@ -1862,7 +2424,7 @@ function useFiles(autoFetch = true) {
1862
2424
  setIsLoading(false);
1863
2425
  }
1864
2426
  }, [fetchWithAuth]);
1865
- const uploadFile = (0, import_react6.useCallback)(
2427
+ const uploadFile = (0, import_react7.useCallback)(
1866
2428
  async (fileOrFormData) => {
1867
2429
  setIsUploading(true);
1868
2430
  setError(null);
@@ -1895,7 +2457,7 @@ function useFiles(autoFetch = true) {
1895
2457
  },
1896
2458
  [fetchWithAuth, fetchFiles]
1897
2459
  );
1898
- const deleteFile = (0, import_react6.useCallback)(
2460
+ const deleteFile = (0, import_react7.useCallback)(
1899
2461
  async (fileId) => {
1900
2462
  const res = await fetchWithAuth(`/files/${fileId}`, { method: "DELETE" });
1901
2463
  if (!res.ok) throw new Error(`Failed to delete file: ${res.statusText}`);
@@ -1903,7 +2465,7 @@ function useFiles(autoFetch = true) {
1903
2465
  },
1904
2466
  [fetchWithAuth]
1905
2467
  );
1906
- const bulkDeleteFiles = (0, import_react6.useCallback)(
2468
+ const bulkDeleteFiles = (0, import_react7.useCallback)(
1907
2469
  async (fileIds) => {
1908
2470
  const res = await fetchWithAuth("/files/bulk-delete", {
1909
2471
  method: "POST",
@@ -1918,10 +2480,10 @@ function useFiles(autoFetch = true) {
1918
2480
  },
1919
2481
  [fetchWithAuth]
1920
2482
  );
1921
- const getDownloadUrl = (0, import_react6.useCallback)((fileId) => {
2483
+ const getDownloadUrl = (0, import_react7.useCallback)((fileId) => {
1922
2484
  return `/files/${fileId}`;
1923
2485
  }, []);
1924
- (0, import_react6.useEffect)(() => {
2486
+ (0, import_react7.useEffect)(() => {
1925
2487
  if (autoFetch) {
1926
2488
  void fetchFiles();
1927
2489
  }
@@ -1940,13 +2502,13 @@ function useFiles(autoFetch = true) {
1940
2502
  }
1941
2503
 
1942
2504
  // src/hooks/useAgents.ts
1943
- var import_react7 = require("react");
2505
+ var import_react8 = require("react");
1944
2506
  function useAgents(autoFetch = true) {
1945
2507
  const { fetchWithAuth } = usePersonaContext();
1946
- const [agents, setAgents] = (0, import_react7.useState)([]);
1947
- const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
1948
- const [error, setError] = (0, import_react7.useState)(null);
1949
- const fetchAgents = (0, import_react7.useCallback)(async () => {
2508
+ const [agents, setAgents] = (0, import_react8.useState)([]);
2509
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
2510
+ const [error, setError] = (0, import_react8.useState)(null);
2511
+ const fetchAgents = (0, import_react8.useCallback)(async () => {
1950
2512
  setIsLoading(true);
1951
2513
  setError(null);
1952
2514
  try {
@@ -1964,7 +2526,7 @@ function useAgents(autoFetch = true) {
1964
2526
  setIsLoading(false);
1965
2527
  }
1966
2528
  }, [fetchWithAuth]);
1967
- (0, import_react7.useEffect)(() => {
2529
+ (0, import_react8.useEffect)(() => {
1968
2530
  if (autoFetch) {
1969
2531
  void fetchAgents();
1970
2532
  }
@@ -1978,13 +2540,13 @@ function useAgents(autoFetch = true) {
1978
2540
  }
1979
2541
 
1980
2542
  // src/hooks/useConnection.ts
1981
- var import_react8 = require("react");
2543
+ var import_react9 = require("react");
1982
2544
  function useConnection(autoCheck = true) {
1983
2545
  const { fetchWithAuth } = usePersonaContext();
1984
- const [health, setHealth] = (0, import_react8.useState)(null);
1985
- const [isConnected, setIsConnected] = (0, import_react8.useState)(false);
1986
- const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
1987
- const checkHealth = (0, import_react8.useCallback)(async () => {
2546
+ const [health, setHealth] = (0, import_react9.useState)(null);
2547
+ const [isConnected, setIsConnected] = (0, import_react9.useState)(false);
2548
+ const [isLoading, setIsLoading] = (0, import_react9.useState)(false);
2549
+ const checkHealth = (0, import_react9.useCallback)(async () => {
1988
2550
  setIsLoading(true);
1989
2551
  try {
1990
2552
  const res = await fetchWithAuth("/health");
@@ -2003,7 +2565,7 @@ function useConnection(autoCheck = true) {
2003
2565
  setIsLoading(false);
2004
2566
  }
2005
2567
  }, [fetchWithAuth]);
2006
- (0, import_react8.useEffect)(() => {
2568
+ (0, import_react9.useEffect)(() => {
2007
2569
  if (autoCheck) {
2008
2570
  void checkHealth();
2009
2571
  }
@@ -2017,15 +2579,15 @@ function useConnection(autoCheck = true) {
2017
2579
  }
2018
2580
 
2019
2581
  // src/hooks/useMcpConnections.ts
2020
- var import_react9 = require("react");
2582
+ var import_react10 = require("react");
2021
2583
  function useMcpConnections(options = {}) {
2022
2584
  const { defaultAgentId, fetchWithAuth } = usePersonaContext();
2023
2585
  const agentId = options.agentId ?? defaultAgentId;
2024
2586
  const autoFetch = options.autoFetch ?? true;
2025
- const [connections, setConnections] = (0, import_react9.useState)([]);
2026
- const [isLoading, setIsLoading] = (0, import_react9.useState)(false);
2027
- const [error, setError] = (0, import_react9.useState)(null);
2028
- const fetchConnections = (0, import_react9.useCallback)(async () => {
2587
+ const [connections, setConnections] = (0, import_react10.useState)([]);
2588
+ const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
2589
+ const [error, setError] = (0, import_react10.useState)(null);
2590
+ const fetchConnections = (0, import_react10.useCallback)(async () => {
2029
2591
  if (!agentId) return [];
2030
2592
  setIsLoading(true);
2031
2593
  setError(null);
@@ -2049,7 +2611,7 @@ function useMcpConnections(options = {}) {
2049
2611
  setIsLoading(false);
2050
2612
  }
2051
2613
  }, [agentId, fetchWithAuth, options.returnTo]);
2052
- (0, import_react9.useEffect)(() => {
2614
+ (0, import_react10.useEffect)(() => {
2053
2615
  if (autoFetch) void fetchConnections();
2054
2616
  }, [autoFetch, fetchConnections]);
2055
2617
  return {
@@ -2063,11 +2625,11 @@ function useMcpConnections(options = {}) {
2063
2625
  }
2064
2626
 
2065
2627
  // src/hooks/useMcp.ts
2066
- var import_react10 = require("react");
2628
+ var import_react11 = require("react");
2067
2629
  function useMcp(options = {}) {
2068
2630
  const { fetchWithAuth } = usePersonaContext();
2069
2631
  const defaultMcpId = options.mcpId;
2070
- const readResource = (0, import_react10.useCallback)(
2632
+ const readResource = (0, import_react11.useCallback)(
2071
2633
  async (uri, mcpId) => {
2072
2634
  const id = mcpId ?? defaultMcpId;
2073
2635
  if (!id) throw new Error("MCP server ID is required to read resource");
@@ -2081,7 +2643,7 @@ function useMcp(options = {}) {
2081
2643
  },
2082
2644
  [fetchWithAuth, defaultMcpId]
2083
2645
  );
2084
- const callTool = (0, import_react10.useCallback)(
2646
+ const callTool = (0, import_react11.useCallback)(
2085
2647
  async (name, args, mcpId) => {
2086
2648
  const id = mcpId ?? defaultMcpId;
2087
2649
  if (!id) throw new Error("MCP server ID is required to call tool");
@@ -2103,11 +2665,598 @@ function useMcp(options = {}) {
2103
2665
  };
2104
2666
  }
2105
2667
 
2668
+ // src/hooks/useWorkflows.ts
2669
+ var import_react12 = require("react");
2670
+ function useWorkflows(options) {
2671
+ const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
2672
+ const { autoFetch = true, search, status, page, limit } = opts;
2673
+ const { fetchWithAuth } = usePersonaContext();
2674
+ const [workflows, setWorkflows] = (0, import_react12.useState)([]);
2675
+ const [pagination, setPagination] = (0, import_react12.useState)({
2676
+ page: page ?? 1,
2677
+ limit: limit ?? 20,
2678
+ total: 0,
2679
+ totalPages: 0
2680
+ });
2681
+ const [isLoading, setIsLoading] = (0, import_react12.useState)(false);
2682
+ const [error, setError] = (0, import_react12.useState)(null);
2683
+ const fetchWorkflows = (0, import_react12.useCallback)(async () => {
2684
+ setIsLoading(true);
2685
+ setError(null);
2686
+ try {
2687
+ const params = new URLSearchParams();
2688
+ if (search) params.set("search", search);
2689
+ if (status) params.set("status", status);
2690
+ if (page) params.set("page", String(page));
2691
+ if (limit) params.set("limit", String(limit));
2692
+ const queryStr = params.toString() ? `?${params.toString()}` : "";
2693
+ const res = await fetchWithAuth(`/workflows${queryStr}`);
2694
+ if (!res.ok) {
2695
+ throw new Error(`Failed to list workflows: ${res.statusText}`);
2696
+ }
2697
+ const data = await res.json();
2698
+ const items = Array.isArray(data) ? data : data?.items || data?.workflows || [];
2699
+ setWorkflows(items);
2700
+ if (data?.pagination) {
2701
+ setPagination({
2702
+ page: data.pagination.page ?? 1,
2703
+ limit: data.pagination.limit ?? items.length,
2704
+ total: data.pagination.total ?? items.length,
2705
+ totalPages: data.pagination.totalPages ?? 1
2706
+ });
2707
+ } else {
2708
+ setPagination({
2709
+ page: 1,
2710
+ limit: items.length,
2711
+ total: items.length,
2712
+ totalPages: 1
2713
+ });
2714
+ }
2715
+ return items;
2716
+ } catch (err) {
2717
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2718
+ setError(errorObj);
2719
+ return [];
2720
+ } finally {
2721
+ setIsLoading(false);
2722
+ }
2723
+ }, [fetchWithAuth, search, status, page, limit]);
2724
+ const createWorkflow = (0, import_react12.useCallback)(
2725
+ async (input) => {
2726
+ setError(null);
2727
+ try {
2728
+ const res = await fetchWithAuth("/workflows", {
2729
+ method: "POST",
2730
+ headers: { "Content-Type": "application/json" },
2731
+ body: JSON.stringify(input)
2732
+ });
2733
+ if (!res.ok) {
2734
+ const errText = await res.text().catch(() => res.statusText);
2735
+ throw new Error(`Failed to create workflow (${res.status}): ${errText}`);
2736
+ }
2737
+ const created = await res.json();
2738
+ setWorkflows((prev) => [created, ...prev]);
2739
+ return created;
2740
+ } catch (err) {
2741
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2742
+ setError(errorObj);
2743
+ throw errorObj;
2744
+ }
2745
+ },
2746
+ [fetchWithAuth]
2747
+ );
2748
+ (0, import_react12.useEffect)(() => {
2749
+ if (autoFetch) {
2750
+ void fetchWorkflows();
2751
+ }
2752
+ }, [autoFetch, fetchWorkflows]);
2753
+ return {
2754
+ workflows,
2755
+ pagination,
2756
+ isLoading,
2757
+ error,
2758
+ refetch: fetchWorkflows,
2759
+ createWorkflow
2760
+ };
2761
+ }
2762
+
2763
+ // src/hooks/useWorkflow.ts
2764
+ var import_react13 = require("react");
2765
+ function useWorkflow(workflowId, options) {
2766
+ const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
2767
+ const { autoFetch = true } = opts;
2768
+ const { fetchWithAuth } = usePersonaContext();
2769
+ const [workflow, setWorkflow] = (0, import_react13.useState)(null);
2770
+ const [versions, setVersions] = (0, import_react13.useState)([]);
2771
+ const [mermaid, setMermaid] = (0, import_react13.useState)(null);
2772
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(false);
2773
+ const [error, setError] = (0, import_react13.useState)(null);
2774
+ const fetchWorkflow = (0, import_react13.useCallback)(async () => {
2775
+ if (!workflowId) return null;
2776
+ setIsLoading(true);
2777
+ setError(null);
2778
+ try {
2779
+ const res = await fetchWithAuth(`/workflows/${workflowId}`);
2780
+ if (!res.ok) {
2781
+ throw new Error(`Failed to fetch workflow (${res.status}): ${res.statusText}`);
2782
+ }
2783
+ const data = await res.json();
2784
+ setWorkflow(data);
2785
+ return data;
2786
+ } catch (err) {
2787
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2788
+ setError(errorObj);
2789
+ return null;
2790
+ } finally {
2791
+ setIsLoading(false);
2792
+ }
2793
+ }, [fetchWithAuth, workflowId]);
2794
+ const fetchVersions = (0, import_react13.useCallback)(async () => {
2795
+ if (!workflowId) return [];
2796
+ try {
2797
+ const res = await fetchWithAuth(`/workflows/${workflowId}/versions`);
2798
+ if (!res.ok) return [];
2799
+ const data = await res.json();
2800
+ const items = Array.isArray(data) ? data : data?.items || [];
2801
+ setVersions(items);
2802
+ return items;
2803
+ } catch {
2804
+ return [];
2805
+ }
2806
+ }, [fetchWithAuth, workflowId]);
2807
+ const fetchMermaid = (0, import_react13.useCallback)(async () => {
2808
+ if (!workflowId) return "";
2809
+ try {
2810
+ const res = await fetchWithAuth(`/workflows/${workflowId}/mermaid`);
2811
+ if (!res.ok) return "";
2812
+ const data = await res.json();
2813
+ const code = typeof data?.mermaid === "string" ? data.mermaid : String(data ?? "");
2814
+ setMermaid(code);
2815
+ return code;
2816
+ } catch {
2817
+ return "";
2818
+ }
2819
+ }, [fetchWithAuth, workflowId]);
2820
+ const saveDraft = (0, import_react13.useCallback)(
2821
+ async (draft) => {
2822
+ if (!workflowId) throw new Error("Workflow ID is required");
2823
+ setError(null);
2824
+ const res = await fetchWithAuth(`/workflows/${workflowId}/draft`, {
2825
+ method: "POST",
2826
+ headers: { "Content-Type": "application/json" },
2827
+ body: JSON.stringify({ draft })
2828
+ });
2829
+ if (!res.ok) {
2830
+ const errText = await res.text().catch(() => res.statusText);
2831
+ throw new Error(`Failed to save draft (${res.status}): ${errText}`);
2832
+ }
2833
+ const updated = await res.json();
2834
+ setWorkflow(updated);
2835
+ return updated;
2836
+ },
2837
+ [fetchWithAuth, workflowId]
2838
+ );
2839
+ const publish = (0, import_react13.useCallback)(
2840
+ async (summary) => {
2841
+ if (!workflowId) throw new Error("Workflow ID is required");
2842
+ setError(null);
2843
+ const res = await fetchWithAuth(`/workflows/${workflowId}/publish`, {
2844
+ method: "POST",
2845
+ headers: { "Content-Type": "application/json" },
2846
+ body: JSON.stringify(summary ? { summary } : {})
2847
+ });
2848
+ if (!res.ok) {
2849
+ const errText = await res.text().catch(() => res.statusText);
2850
+ throw new Error(`Failed to publish workflow (${res.status}): ${errText}`);
2851
+ }
2852
+ const versionResult = await res.json();
2853
+ void fetchWorkflow();
2854
+ void fetchVersions();
2855
+ return versionResult;
2856
+ },
2857
+ [fetchWithAuth, workflowId, fetchWorkflow, fetchVersions]
2858
+ );
2859
+ const deleteWorkflow = (0, import_react13.useCallback)(async () => {
2860
+ if (!workflowId) throw new Error("Workflow ID is required");
2861
+ setError(null);
2862
+ const res = await fetchWithAuth(`/workflows/${workflowId}`, {
2863
+ method: "DELETE"
2864
+ });
2865
+ if (!res.ok) {
2866
+ throw new Error(`Failed to delete workflow (${res.status}): ${res.statusText}`);
2867
+ }
2868
+ setWorkflow(null);
2869
+ }, [fetchWithAuth, workflowId]);
2870
+ (0, import_react13.useEffect)(() => {
2871
+ if (autoFetch && workflowId) {
2872
+ void fetchWorkflow();
2873
+ }
2874
+ }, [autoFetch, workflowId, fetchWorkflow]);
2875
+ return {
2876
+ workflow,
2877
+ versions,
2878
+ mermaid,
2879
+ isLoading,
2880
+ error,
2881
+ refetch: fetchWorkflow,
2882
+ fetchVersions,
2883
+ fetchMermaid,
2884
+ saveDraft,
2885
+ publish,
2886
+ deleteWorkflow
2887
+ };
2888
+ }
2889
+
2890
+ // src/hooks/useWorkflowStream.ts
2891
+ var import_react14 = require("react");
2892
+ function useWorkflowStream(workflowId, options) {
2893
+ const { baseUrl, getAuthToken, fetchWithAuth, logger } = usePersonaContext();
2894
+ const [status, setStatus] = (0, import_react14.useState)("idle");
2895
+ const [runId, setRunId] = (0, import_react14.useState)(null);
2896
+ const [activeNodeId, setActiveNodeId] = (0, import_react14.useState)(null);
2897
+ const [nodeRuns, setNodeRuns] = (0, import_react14.useState)(
2898
+ {}
2899
+ );
2900
+ const [text, setText] = (0, import_react14.useState)("");
2901
+ const [output, setOutput] = (0, import_react14.useState)(null);
2902
+ const [error, setError] = (0, import_react14.useState)(null);
2903
+ const [events, setEvents] = (0, import_react14.useState)([]);
2904
+ const abortControllerRef = (0, import_react14.useRef)(null);
2905
+ const currentRunIdRef = (0, import_react14.useRef)(null);
2906
+ currentRunIdRef.current = runId;
2907
+ const optionsRef = (0, import_react14.useRef)(options);
2908
+ optionsRef.current = options;
2909
+ const cancel = (0, import_react14.useCallback)(async () => {
2910
+ if (abortControllerRef.current) {
2911
+ abortControllerRef.current.abort();
2912
+ abortControllerRef.current = null;
2913
+ }
2914
+ const currentRun = currentRunIdRef.current;
2915
+ if (currentRun) {
2916
+ try {
2917
+ await fetchWithAuth(`/workflows/runs/${currentRun}/cancel`, {
2918
+ method: "POST"
2919
+ });
2920
+ } catch (err) {
2921
+ logger.warn("Failed to send cancel request to workflow run", {
2922
+ runId: currentRun,
2923
+ error: String(err)
2924
+ });
2925
+ }
2926
+ }
2927
+ setStatus("cancelled");
2928
+ setActiveNodeId(null);
2929
+ }, [fetchWithAuth, logger]);
2930
+ const reset = (0, import_react14.useCallback)(() => {
2931
+ if (abortControllerRef.current) {
2932
+ abortControllerRef.current.abort();
2933
+ abortControllerRef.current = null;
2934
+ }
2935
+ setStatus("idle");
2936
+ setRunId(null);
2937
+ setActiveNodeId(null);
2938
+ setNodeRuns({});
2939
+ setText("");
2940
+ setOutput(null);
2941
+ setError(null);
2942
+ setEvents([]);
2943
+ }, []);
2944
+ const processStream = (0, import_react14.useCallback)(
2945
+ async (url, method, body, targetRunId) => {
2946
+ if (abortControllerRef.current) {
2947
+ abortControllerRef.current.abort();
2948
+ }
2949
+ const controller = new AbortController();
2950
+ abortControllerRef.current = controller;
2951
+ setStatus("running");
2952
+ setError(null);
2953
+ const token = getAuthToken ? await getAuthToken() : null;
2954
+ const headers = {
2955
+ Accept: "text/event-stream"
2956
+ };
2957
+ if (token) {
2958
+ headers["Authorization"] = `Bearer ${token}`;
2959
+ }
2960
+ if (method === "POST") {
2961
+ headers["Content-Type"] = "application/json";
2962
+ }
2963
+ let assignedRunId = targetRunId ?? null;
2964
+ let accumulatedText = "";
2965
+ let finalOutput = null;
2966
+ try {
2967
+ const stream = await openSSEStream({
2968
+ url,
2969
+ method,
2970
+ headers,
2971
+ body,
2972
+ signal: controller.signal
2973
+ });
2974
+ if (!stream.ok) {
2975
+ throw new Error(
2976
+ stream.errorText || `Stream request failed with status ${stream.status}`
2977
+ );
2978
+ }
2979
+ const runIdHeader = stream.getHeader("x-persona-run-id");
2980
+ if (runIdHeader) {
2981
+ assignedRunId = runIdHeader;
2982
+ setRunId(runIdHeader);
2983
+ }
2984
+ let buffer = "";
2985
+ while (true) {
2986
+ const { done, value } = await stream.reader.read();
2987
+ if (done) break;
2988
+ if (!value) continue;
2989
+ buffer += value;
2990
+ const lines = buffer.split("\n");
2991
+ buffer = lines.pop() ?? "";
2992
+ for (const line of lines) {
2993
+ const trimmed = line.trim();
2994
+ if (!trimmed || trimmed.startsWith(":")) continue;
2995
+ if (trimmed.startsWith("data:")) {
2996
+ const rawData = trimmed.slice(5).trim();
2997
+ if (!rawData || rawData === "[DONE]") continue;
2998
+ try {
2999
+ const event = JSON.parse(rawData);
3000
+ setEvents((prev) => [...prev, event]);
3001
+ optionsRef.current?.onEvent?.(event);
3002
+ if ("runId" in event && typeof event.runId === "string" && !assignedRunId) {
3003
+ assignedRunId = event.runId;
3004
+ setRunId(event.runId);
3005
+ }
3006
+ if (event.type === "TEXT_MESSAGE_CHUNK") {
3007
+ accumulatedText += event.delta;
3008
+ setText((prev) => prev + event.delta);
3009
+ } else if (event.type === "CUSTOM") {
3010
+ if (event.name === "workflow_node_started") {
3011
+ const val = event.value;
3012
+ setActiveNodeId(val.nodeId);
3013
+ setNodeRuns((prev) => ({
3014
+ ...prev,
3015
+ [val.nodeId]: {
3016
+ nodeId: val.nodeId,
3017
+ nodeType: val.nodeType,
3018
+ nodeLabel: val.nodeLabel,
3019
+ status: "running",
3020
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
3021
+ input: val.input
3022
+ }
3023
+ }));
3024
+ optionsRef.current?.onNodeStarted?.(val);
3025
+ } else if (event.name === "workflow_node_completed") {
3026
+ const val = event.value;
3027
+ setNodeRuns((prev) => {
3028
+ const existing = prev[val.nodeId];
3029
+ return {
3030
+ ...prev,
3031
+ [val.nodeId]: {
3032
+ nodeId: val.nodeId,
3033
+ nodeType: existing?.nodeType ?? "unknown",
3034
+ nodeLabel: existing?.nodeLabel ?? val.nodeId,
3035
+ status: "completed",
3036
+ startedAt: existing?.startedAt,
3037
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
3038
+ input: existing?.input,
3039
+ output: val.output
3040
+ }
3041
+ };
3042
+ });
3043
+ setActiveNodeId((curr) => curr === val.nodeId ? null : curr);
3044
+ finalOutput = val.output;
3045
+ setOutput(val.output);
3046
+ optionsRef.current?.onNodeCompleted?.(val);
3047
+ } else if (event.name === "workflow_node_failed") {
3048
+ const val = event.value;
3049
+ setNodeRuns((prev) => {
3050
+ const existing = prev[val.nodeId];
3051
+ return {
3052
+ ...prev,
3053
+ [val.nodeId]: {
3054
+ nodeId: val.nodeId,
3055
+ nodeType: existing?.nodeType ?? "unknown",
3056
+ nodeLabel: existing?.nodeLabel ?? val.nodeId,
3057
+ status: "failed",
3058
+ startedAt: existing?.startedAt,
3059
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
3060
+ input: existing?.input,
3061
+ error: val.error
3062
+ }
3063
+ };
3064
+ });
3065
+ setActiveNodeId((curr) => curr === val.nodeId ? null : curr);
3066
+ optionsRef.current?.onNodeFailed?.(val);
3067
+ }
3068
+ } else if (event.type === "RUN_FINISHED") {
3069
+ setStatus("completed");
3070
+ setActiveNodeId(null);
3071
+ optionsRef.current?.onFinish?.({
3072
+ runId: assignedRunId || "",
3073
+ output: finalOutput,
3074
+ text: accumulatedText
3075
+ });
3076
+ } else if (event.type === "RUN_ERROR") {
3077
+ const runErr = new Error(event.message || event.code || "Workflow run error");
3078
+ setStatus("failed");
3079
+ setError(runErr);
3080
+ setActiveNodeId(null);
3081
+ optionsRef.current?.onError?.(runErr);
3082
+ }
3083
+ } catch {
3084
+ }
3085
+ }
3086
+ }
3087
+ }
3088
+ setStatus((prev) => prev === "running" ? "completed" : prev);
3089
+ return finalOutput;
3090
+ } catch (err) {
3091
+ if (controller.signal.aborted) {
3092
+ setStatus("cancelled");
3093
+ return null;
3094
+ }
3095
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3096
+ setStatus("failed");
3097
+ setError(errorObj);
3098
+ setActiveNodeId(null);
3099
+ optionsRef.current?.onError?.(errorObj);
3100
+ throw errorObj;
3101
+ } finally {
3102
+ if (abortControllerRef.current === controller) {
3103
+ abortControllerRef.current = null;
3104
+ }
3105
+ }
3106
+ },
3107
+ [getAuthToken]
3108
+ );
3109
+ const start = (0, import_react14.useCallback)(
3110
+ async (input, opts) => {
3111
+ const targetWfId = opts?.workflowId || workflowId;
3112
+ if (!targetWfId) {
3113
+ throw new Error("Workflow ID must be provided to start execution");
3114
+ }
3115
+ setNodeRuns({});
3116
+ setText("");
3117
+ setOutput(null);
3118
+ setEvents([]);
3119
+ setActiveNodeId(null);
3120
+ const cleanBase = baseUrl.replace(/\/+$/, "");
3121
+ const url = `${cleanBase}/workflows/${targetWfId}/stream`;
3122
+ const body = JSON.stringify({
3123
+ input: input ?? {},
3124
+ dryRun: opts?.dryRun ?? false
3125
+ });
3126
+ return processStream(url, "POST", body);
3127
+ },
3128
+ [workflowId, baseUrl, processStream]
3129
+ );
3130
+ const resume = (0, import_react14.useCallback)(
3131
+ async (targetRunId, sinceSeq) => {
3132
+ if (!targetRunId) {
3133
+ throw new Error("Run ID is required to resume stream");
3134
+ }
3135
+ setRunId(targetRunId);
3136
+ const cleanBase = baseUrl.replace(/\/+$/, "");
3137
+ const query = sinceSeq !== void 0 ? `?since=${sinceSeq}` : "";
3138
+ const url = `${cleanBase}/workflows/runs/${targetRunId}/resume${query}`;
3139
+ await processStream(url, "GET", void 0, targetRunId);
3140
+ },
3141
+ [baseUrl, processStream]
3142
+ );
3143
+ (0, import_react14.useEffect)(() => {
3144
+ return () => {
3145
+ if (abortControllerRef.current) {
3146
+ abortControllerRef.current.abort();
3147
+ }
3148
+ };
3149
+ }, []);
3150
+ return {
3151
+ runId,
3152
+ status,
3153
+ isRunning: status === "running",
3154
+ activeNodeId,
3155
+ nodeRuns,
3156
+ text,
3157
+ output,
3158
+ error,
3159
+ events,
3160
+ start,
3161
+ cancel,
3162
+ resume,
3163
+ reset
3164
+ };
3165
+ }
3166
+
3167
+ // src/hooks/useWorkflowRuns.ts
3168
+ var import_react15 = require("react");
3169
+ function useWorkflowRuns(workflowId, options) {
3170
+ const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
3171
+ const { autoFetch = true, page = 1, limit = 20 } = opts;
3172
+ const { fetchWithAuth } = usePersonaContext();
3173
+ const [runs, setRuns] = (0, import_react15.useState)([]);
3174
+ const [pagination, setPagination] = (0, import_react15.useState)({
3175
+ page,
3176
+ limit,
3177
+ total: 0,
3178
+ totalPages: 0
3179
+ });
3180
+ const [isLoading, setIsLoading] = (0, import_react15.useState)(false);
3181
+ const [error, setError] = (0, import_react15.useState)(null);
3182
+ const fetchRuns = (0, import_react15.useCallback)(async () => {
3183
+ if (!workflowId) return [];
3184
+ setIsLoading(true);
3185
+ setError(null);
3186
+ try {
3187
+ const query = `?page=${page}&limit=${limit}`;
3188
+ const res = await fetchWithAuth(`/workflows/${workflowId}/runs${query}`);
3189
+ if (!res.ok) {
3190
+ throw new Error(`Failed to list workflow runs: ${res.statusText}`);
3191
+ }
3192
+ const data = await res.json();
3193
+ const items = Array.isArray(data) ? data : data?.items || data?.runs || [];
3194
+ setRuns(items);
3195
+ if (data?.pagination) {
3196
+ setPagination({
3197
+ page: data.pagination.page ?? page,
3198
+ limit: data.pagination.limit ?? limit,
3199
+ total: data.pagination.total ?? items.length,
3200
+ totalPages: data.pagination.totalPages ?? 1
3201
+ });
3202
+ }
3203
+ return items;
3204
+ } catch (err) {
3205
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3206
+ setError(errorObj);
3207
+ return [];
3208
+ } finally {
3209
+ setIsLoading(false);
3210
+ }
3211
+ }, [fetchWithAuth, workflowId, page, limit]);
3212
+ const getRun = (0, import_react15.useCallback)(
3213
+ async (runId) => {
3214
+ if (!runId) throw new Error("Run ID is required");
3215
+ const res = await fetchWithAuth(`/workflows/runs/${runId}`);
3216
+ if (!res.ok) {
3217
+ throw new Error(`Failed to fetch workflow run (${res.status}): ${res.statusText}`);
3218
+ }
3219
+ return await res.json();
3220
+ },
3221
+ [fetchWithAuth]
3222
+ );
3223
+ const cancelRun = (0, import_react15.useCallback)(
3224
+ async (runId) => {
3225
+ if (!runId) throw new Error("Run ID is required");
3226
+ const res = await fetchWithAuth(`/workflows/runs/${runId}/cancel`, {
3227
+ method: "POST"
3228
+ });
3229
+ if (!res.ok) {
3230
+ throw new Error(`Failed to cancel workflow run (${res.status}): ${res.statusText}`);
3231
+ }
3232
+ setRuns(
3233
+ (prev) => prev.map((r) => r._id === runId ? { ...r, status: "cancelled" } : r)
3234
+ );
3235
+ },
3236
+ [fetchWithAuth]
3237
+ );
3238
+ (0, import_react15.useEffect)(() => {
3239
+ if (autoFetch && workflowId) {
3240
+ void fetchRuns();
3241
+ }
3242
+ }, [autoFetch, workflowId, fetchRuns]);
3243
+ return {
3244
+ runs,
3245
+ pagination,
3246
+ isLoading,
3247
+ error,
3248
+ refetch: fetchRuns,
3249
+ getRun,
3250
+ cancelRun
3251
+ };
3252
+ }
3253
+
2106
3254
  // src/index.ts
2107
3255
  var import_logger2 = require("@personaai/logger");
2108
- var VERSION = "0.8.1";
3256
+ var VERSION = "0.10.0";
2109
3257
  // Annotate the CommonJS export names for ESM import in node:
2110
3258
  0 && (module.exports = {
3259
+ ARCHITECT_AGENT_ID,
2111
3260
  PersonaProvider,
2112
3261
  VERSION,
2113
3262
  createLogger,
@@ -2118,6 +3267,7 @@ var VERSION = "0.8.1";
2118
3267
  setLogLevel,
2119
3268
  supportsStreamingFetch,
2120
3269
  useAgents,
3270
+ useArchitectChat,
2121
3271
  useChat,
2122
3272
  useConnection,
2123
3273
  useFiles,
@@ -2126,6 +3276,10 @@ var VERSION = "0.8.1";
2126
3276
  useMemory,
2127
3277
  usePersonaContext,
2128
3278
  useThreads,
2129
- useVoice
3279
+ useVoice,
3280
+ useWorkflow,
3281
+ useWorkflowRuns,
3282
+ useWorkflowStream,
3283
+ useWorkflows
2130
3284
  });
2131
3285
  //# sourceMappingURL=index.cjs.map