@personaai/react 0.8.1 → 0.9.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  React SDK for [Persona](https://persona.hasanraiyan.me) — hooks and a context provider for building chat UIs against any Persona backend.
4
4
 
5
- > **v0.8.0.** Requires React 18+. Client-side only — never holds a credential.
5
+ > **v0.9.0.** Requires React 18+. Client-side only — never holds a credential.
6
6
 
7
7
  ## Install
8
8
 
@@ -46,15 +46,19 @@ function Chat() {
46
46
 
47
47
  ## Hooks
48
48
 
49
- | Hook | Purpose |
50
- | --------------- | ----------------------------------------------------------------------------------------------- |
51
- | `useChat` | Streaming chat — messages, send, stop, reload, interrupts, workspace files, ephemeral new chat |
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 |
49
+ | Hook | Purpose |
50
+ | -------------------- | ----------------------------------------------------------------------------------------------- |
51
+ | `useChat` | Streaming chat — messages, send, stop, reload, interrupts, workspace files, ephemeral new chat |
52
+ | `useWorkflowStream` | Live streaming workflow execution — real-time node state tracking, cancellation, and resume |
53
+ | `useWorkflows` | Workflow discovery & creation — list, paginate, filter, and create workflows |
54
+ | `useWorkflow` | Single workflow authoring — drafts, publishing, versions, and Mermaid diagrams |
55
+ | `useWorkflowRuns` | Workflow run history — list historical runs, get run details, and cancel in-flight runs |
56
+ | `useVoice` | Real-time voice calls (Gemini Live) — start/stop/mute, live transcript, tool calls |
57
+ | `useThreads` | Thread CRUD — list, create, delete, rename, reset, archive |
58
+ | `useFiles` | Upload management — list, upload, delete |
59
+ | `useMemory` | Persistent memory — read, write, delete |
60
+ | `useAgents` | Agent discovery — list available agents |
61
+ | `useConnection` | Health check — backend connectivity status |
58
62
 
59
63
  ## Voice
60
64
 
package/dist/index.cjs CHANGED
@@ -38,7 +38,11 @@ __export(index_exports, {
38
38
  useMemory: () => useMemory,
39
39
  usePersonaContext: () => usePersonaContext,
40
40
  useThreads: () => useThreads,
41
- useVoice: () => useVoice
41
+ useVoice: () => useVoice,
42
+ useWorkflow: () => useWorkflow,
43
+ useWorkflowRuns: () => useWorkflowRuns,
44
+ useWorkflowStream: () => useWorkflowStream,
45
+ useWorkflows: () => useWorkflows
42
46
  });
43
47
  module.exports = __toCommonJS(index_exports);
44
48
 
@@ -177,7 +181,8 @@ function fetchReader(response, controller) {
177
181
  function xhrStream(opts) {
178
182
  return new Promise((resolveStream, rejectStream) => {
179
183
  const xhr = new XMLHttpRequest();
180
- xhr.open("POST", opts.url);
184
+ const method = opts.method ?? "POST";
185
+ xhr.open(method, opts.url);
181
186
  for (const [key, val] of Object.entries(opts.headers))
182
187
  xhr.setRequestHeader(key, val);
183
188
  let consumed = 0;
@@ -276,7 +281,7 @@ function xhrStream(opts) {
276
281
  });
277
282
  }
278
283
  }
279
- xhr.send(opts.body);
284
+ xhr.send(method === "GET" ? null : opts.body ?? null);
280
285
  });
281
286
  }
282
287
  async function openSSEStream(opts) {
@@ -288,10 +293,11 @@ async function openSSEStream(opts) {
288
293
  if (opts.signal.aborted) controller.abort();
289
294
  else opts.signal.addEventListener("abort", () => controller.abort());
290
295
  }
296
+ const method = opts.method ?? "POST";
291
297
  const response = await fetch(opts.url, {
292
- method: "POST",
298
+ method,
293
299
  headers: opts.headers,
294
- body: opts.body,
300
+ body: method === "GET" ? void 0 : opts.body,
295
301
  signal: controller.signal
296
302
  });
297
303
  if (!response.ok) {
@@ -2103,9 +2109,595 @@ function useMcp(options = {}) {
2103
2109
  };
2104
2110
  }
2105
2111
 
2112
+ // src/hooks/useWorkflows.ts
2113
+ var import_react11 = require("react");
2114
+ function useWorkflows(options) {
2115
+ const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
2116
+ const { autoFetch = true, search, status, page, limit } = opts;
2117
+ const { fetchWithAuth } = usePersonaContext();
2118
+ const [workflows, setWorkflows] = (0, import_react11.useState)([]);
2119
+ const [pagination, setPagination] = (0, import_react11.useState)({
2120
+ page: page ?? 1,
2121
+ limit: limit ?? 20,
2122
+ total: 0,
2123
+ totalPages: 0
2124
+ });
2125
+ const [isLoading, setIsLoading] = (0, import_react11.useState)(false);
2126
+ const [error, setError] = (0, import_react11.useState)(null);
2127
+ const fetchWorkflows = (0, import_react11.useCallback)(async () => {
2128
+ setIsLoading(true);
2129
+ setError(null);
2130
+ try {
2131
+ const params = new URLSearchParams();
2132
+ if (search) params.set("search", search);
2133
+ if (status) params.set("status", status);
2134
+ if (page) params.set("page", String(page));
2135
+ if (limit) params.set("limit", String(limit));
2136
+ const queryStr = params.toString() ? `?${params.toString()}` : "";
2137
+ const res = await fetchWithAuth(`/workflows${queryStr}`);
2138
+ if (!res.ok) {
2139
+ throw new Error(`Failed to list workflows: ${res.statusText}`);
2140
+ }
2141
+ const data = await res.json();
2142
+ const items = Array.isArray(data) ? data : data?.items || data?.workflows || [];
2143
+ setWorkflows(items);
2144
+ if (data?.pagination) {
2145
+ setPagination({
2146
+ page: data.pagination.page ?? 1,
2147
+ limit: data.pagination.limit ?? items.length,
2148
+ total: data.pagination.total ?? items.length,
2149
+ totalPages: data.pagination.totalPages ?? 1
2150
+ });
2151
+ } else {
2152
+ setPagination({
2153
+ page: 1,
2154
+ limit: items.length,
2155
+ total: items.length,
2156
+ totalPages: 1
2157
+ });
2158
+ }
2159
+ return items;
2160
+ } catch (err) {
2161
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2162
+ setError(errorObj);
2163
+ return [];
2164
+ } finally {
2165
+ setIsLoading(false);
2166
+ }
2167
+ }, [fetchWithAuth, search, status, page, limit]);
2168
+ const createWorkflow = (0, import_react11.useCallback)(
2169
+ async (input) => {
2170
+ setError(null);
2171
+ try {
2172
+ const res = await fetchWithAuth("/workflows", {
2173
+ method: "POST",
2174
+ headers: { "Content-Type": "application/json" },
2175
+ body: JSON.stringify(input)
2176
+ });
2177
+ if (!res.ok) {
2178
+ const errText = await res.text().catch(() => res.statusText);
2179
+ throw new Error(`Failed to create workflow (${res.status}): ${errText}`);
2180
+ }
2181
+ const created = await res.json();
2182
+ setWorkflows((prev) => [created, ...prev]);
2183
+ return created;
2184
+ } catch (err) {
2185
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2186
+ setError(errorObj);
2187
+ throw errorObj;
2188
+ }
2189
+ },
2190
+ [fetchWithAuth]
2191
+ );
2192
+ (0, import_react11.useEffect)(() => {
2193
+ if (autoFetch) {
2194
+ void fetchWorkflows();
2195
+ }
2196
+ }, [autoFetch, fetchWorkflows]);
2197
+ return {
2198
+ workflows,
2199
+ pagination,
2200
+ isLoading,
2201
+ error,
2202
+ refetch: fetchWorkflows,
2203
+ createWorkflow
2204
+ };
2205
+ }
2206
+
2207
+ // src/hooks/useWorkflow.ts
2208
+ var import_react12 = require("react");
2209
+ function useWorkflow(workflowId, options) {
2210
+ const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
2211
+ const { autoFetch = true } = opts;
2212
+ const { fetchWithAuth } = usePersonaContext();
2213
+ const [workflow, setWorkflow] = (0, import_react12.useState)(null);
2214
+ const [versions, setVersions] = (0, import_react12.useState)([]);
2215
+ const [mermaid, setMermaid] = (0, import_react12.useState)(null);
2216
+ const [isLoading, setIsLoading] = (0, import_react12.useState)(false);
2217
+ const [error, setError] = (0, import_react12.useState)(null);
2218
+ const fetchWorkflow = (0, import_react12.useCallback)(async () => {
2219
+ if (!workflowId) return null;
2220
+ setIsLoading(true);
2221
+ setError(null);
2222
+ try {
2223
+ const res = await fetchWithAuth(`/workflows/${workflowId}`);
2224
+ if (!res.ok) {
2225
+ throw new Error(`Failed to fetch workflow (${res.status}): ${res.statusText}`);
2226
+ }
2227
+ const data = await res.json();
2228
+ setWorkflow(data);
2229
+ return data;
2230
+ } catch (err) {
2231
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2232
+ setError(errorObj);
2233
+ return null;
2234
+ } finally {
2235
+ setIsLoading(false);
2236
+ }
2237
+ }, [fetchWithAuth, workflowId]);
2238
+ const fetchVersions = (0, import_react12.useCallback)(async () => {
2239
+ if (!workflowId) return [];
2240
+ try {
2241
+ const res = await fetchWithAuth(`/workflows/${workflowId}/versions`);
2242
+ if (!res.ok) return [];
2243
+ const data = await res.json();
2244
+ const items = Array.isArray(data) ? data : data?.items || [];
2245
+ setVersions(items);
2246
+ return items;
2247
+ } catch {
2248
+ return [];
2249
+ }
2250
+ }, [fetchWithAuth, workflowId]);
2251
+ const fetchMermaid = (0, import_react12.useCallback)(async () => {
2252
+ if (!workflowId) return "";
2253
+ try {
2254
+ const res = await fetchWithAuth(`/workflows/${workflowId}/mermaid`);
2255
+ if (!res.ok) return "";
2256
+ const data = await res.json();
2257
+ const code = typeof data?.mermaid === "string" ? data.mermaid : String(data ?? "");
2258
+ setMermaid(code);
2259
+ return code;
2260
+ } catch {
2261
+ return "";
2262
+ }
2263
+ }, [fetchWithAuth, workflowId]);
2264
+ const saveDraft = (0, import_react12.useCallback)(
2265
+ async (draft) => {
2266
+ if (!workflowId) throw new Error("Workflow ID is required");
2267
+ setError(null);
2268
+ const res = await fetchWithAuth(`/workflows/${workflowId}/draft`, {
2269
+ method: "POST",
2270
+ headers: { "Content-Type": "application/json" },
2271
+ body: JSON.stringify({ draft })
2272
+ });
2273
+ if (!res.ok) {
2274
+ const errText = await res.text().catch(() => res.statusText);
2275
+ throw new Error(`Failed to save draft (${res.status}): ${errText}`);
2276
+ }
2277
+ const updated = await res.json();
2278
+ setWorkflow(updated);
2279
+ return updated;
2280
+ },
2281
+ [fetchWithAuth, workflowId]
2282
+ );
2283
+ const publish = (0, import_react12.useCallback)(
2284
+ async (summary) => {
2285
+ if (!workflowId) throw new Error("Workflow ID is required");
2286
+ setError(null);
2287
+ const res = await fetchWithAuth(`/workflows/${workflowId}/publish`, {
2288
+ method: "POST",
2289
+ headers: { "Content-Type": "application/json" },
2290
+ body: JSON.stringify(summary ? { summary } : {})
2291
+ });
2292
+ if (!res.ok) {
2293
+ const errText = await res.text().catch(() => res.statusText);
2294
+ throw new Error(`Failed to publish workflow (${res.status}): ${errText}`);
2295
+ }
2296
+ const versionResult = await res.json();
2297
+ void fetchWorkflow();
2298
+ void fetchVersions();
2299
+ return versionResult;
2300
+ },
2301
+ [fetchWithAuth, workflowId, fetchWorkflow, fetchVersions]
2302
+ );
2303
+ const deleteWorkflow = (0, import_react12.useCallback)(async () => {
2304
+ if (!workflowId) throw new Error("Workflow ID is required");
2305
+ setError(null);
2306
+ const res = await fetchWithAuth(`/workflows/${workflowId}`, {
2307
+ method: "DELETE"
2308
+ });
2309
+ if (!res.ok) {
2310
+ throw new Error(`Failed to delete workflow (${res.status}): ${res.statusText}`);
2311
+ }
2312
+ setWorkflow(null);
2313
+ }, [fetchWithAuth, workflowId]);
2314
+ (0, import_react12.useEffect)(() => {
2315
+ if (autoFetch && workflowId) {
2316
+ void fetchWorkflow();
2317
+ }
2318
+ }, [autoFetch, workflowId, fetchWorkflow]);
2319
+ return {
2320
+ workflow,
2321
+ versions,
2322
+ mermaid,
2323
+ isLoading,
2324
+ error,
2325
+ refetch: fetchWorkflow,
2326
+ fetchVersions,
2327
+ fetchMermaid,
2328
+ saveDraft,
2329
+ publish,
2330
+ deleteWorkflow
2331
+ };
2332
+ }
2333
+
2334
+ // src/hooks/useWorkflowStream.ts
2335
+ var import_react13 = require("react");
2336
+ function useWorkflowStream(workflowId, options) {
2337
+ const { baseUrl, getAuthToken, fetchWithAuth, logger } = usePersonaContext();
2338
+ const [status, setStatus] = (0, import_react13.useState)("idle");
2339
+ const [runId, setRunId] = (0, import_react13.useState)(null);
2340
+ const [activeNodeId, setActiveNodeId] = (0, import_react13.useState)(null);
2341
+ const [nodeRuns, setNodeRuns] = (0, import_react13.useState)(
2342
+ {}
2343
+ );
2344
+ const [text, setText] = (0, import_react13.useState)("");
2345
+ const [output, setOutput] = (0, import_react13.useState)(null);
2346
+ const [error, setError] = (0, import_react13.useState)(null);
2347
+ const [events, setEvents] = (0, import_react13.useState)([]);
2348
+ const abortControllerRef = (0, import_react13.useRef)(null);
2349
+ const currentRunIdRef = (0, import_react13.useRef)(null);
2350
+ currentRunIdRef.current = runId;
2351
+ const optionsRef = (0, import_react13.useRef)(options);
2352
+ optionsRef.current = options;
2353
+ const cancel = (0, import_react13.useCallback)(async () => {
2354
+ if (abortControllerRef.current) {
2355
+ abortControllerRef.current.abort();
2356
+ abortControllerRef.current = null;
2357
+ }
2358
+ const currentRun = currentRunIdRef.current;
2359
+ if (currentRun) {
2360
+ try {
2361
+ await fetchWithAuth(`/workflows/runs/${currentRun}/cancel`, {
2362
+ method: "POST"
2363
+ });
2364
+ } catch (err) {
2365
+ logger.warn("Failed to send cancel request to workflow run", {
2366
+ runId: currentRun,
2367
+ error: String(err)
2368
+ });
2369
+ }
2370
+ }
2371
+ setStatus("cancelled");
2372
+ setActiveNodeId(null);
2373
+ }, [fetchWithAuth, logger]);
2374
+ const reset = (0, import_react13.useCallback)(() => {
2375
+ if (abortControllerRef.current) {
2376
+ abortControllerRef.current.abort();
2377
+ abortControllerRef.current = null;
2378
+ }
2379
+ setStatus("idle");
2380
+ setRunId(null);
2381
+ setActiveNodeId(null);
2382
+ setNodeRuns({});
2383
+ setText("");
2384
+ setOutput(null);
2385
+ setError(null);
2386
+ setEvents([]);
2387
+ }, []);
2388
+ const processStream = (0, import_react13.useCallback)(
2389
+ async (url, method, body, targetRunId) => {
2390
+ if (abortControllerRef.current) {
2391
+ abortControllerRef.current.abort();
2392
+ }
2393
+ const controller = new AbortController();
2394
+ abortControllerRef.current = controller;
2395
+ setStatus("running");
2396
+ setError(null);
2397
+ const token = getAuthToken ? await getAuthToken() : null;
2398
+ const headers = {
2399
+ Accept: "text/event-stream"
2400
+ };
2401
+ if (token) {
2402
+ headers["Authorization"] = `Bearer ${token}`;
2403
+ }
2404
+ if (method === "POST") {
2405
+ headers["Content-Type"] = "application/json";
2406
+ }
2407
+ let assignedRunId = targetRunId ?? null;
2408
+ let accumulatedText = "";
2409
+ let finalOutput = null;
2410
+ try {
2411
+ const stream = await openSSEStream({
2412
+ url,
2413
+ method,
2414
+ headers,
2415
+ body,
2416
+ signal: controller.signal
2417
+ });
2418
+ if (!stream.ok) {
2419
+ throw new Error(
2420
+ stream.errorText || `Stream request failed with status ${stream.status}`
2421
+ );
2422
+ }
2423
+ const runIdHeader = stream.getHeader("x-persona-run-id");
2424
+ if (runIdHeader) {
2425
+ assignedRunId = runIdHeader;
2426
+ setRunId(runIdHeader);
2427
+ }
2428
+ let buffer = "";
2429
+ while (true) {
2430
+ const { done, value } = await stream.reader.read();
2431
+ if (done) break;
2432
+ if (!value) continue;
2433
+ buffer += value;
2434
+ const lines = buffer.split("\n");
2435
+ buffer = lines.pop() ?? "";
2436
+ for (const line of lines) {
2437
+ const trimmed = line.trim();
2438
+ if (!trimmed || trimmed.startsWith(":")) continue;
2439
+ if (trimmed.startsWith("data:")) {
2440
+ const rawData = trimmed.slice(5).trim();
2441
+ if (!rawData || rawData === "[DONE]") continue;
2442
+ try {
2443
+ const event = JSON.parse(rawData);
2444
+ setEvents((prev) => [...prev, event]);
2445
+ optionsRef.current?.onEvent?.(event);
2446
+ if ("runId" in event && typeof event.runId === "string" && !assignedRunId) {
2447
+ assignedRunId = event.runId;
2448
+ setRunId(event.runId);
2449
+ }
2450
+ if (event.type === "TEXT_MESSAGE_CHUNK") {
2451
+ accumulatedText += event.delta;
2452
+ setText((prev) => prev + event.delta);
2453
+ } else if (event.type === "CUSTOM") {
2454
+ if (event.name === "workflow_node_started") {
2455
+ const val = event.value;
2456
+ setActiveNodeId(val.nodeId);
2457
+ setNodeRuns((prev) => ({
2458
+ ...prev,
2459
+ [val.nodeId]: {
2460
+ nodeId: val.nodeId,
2461
+ nodeType: val.nodeType,
2462
+ nodeLabel: val.nodeLabel,
2463
+ status: "running",
2464
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2465
+ input: val.input
2466
+ }
2467
+ }));
2468
+ optionsRef.current?.onNodeStarted?.(val);
2469
+ } else if (event.name === "workflow_node_completed") {
2470
+ const val = event.value;
2471
+ setNodeRuns((prev) => {
2472
+ const existing = prev[val.nodeId];
2473
+ return {
2474
+ ...prev,
2475
+ [val.nodeId]: {
2476
+ nodeId: val.nodeId,
2477
+ nodeType: existing?.nodeType ?? "unknown",
2478
+ nodeLabel: existing?.nodeLabel ?? val.nodeId,
2479
+ status: "completed",
2480
+ startedAt: existing?.startedAt,
2481
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
2482
+ input: existing?.input,
2483
+ output: val.output
2484
+ }
2485
+ };
2486
+ });
2487
+ setActiveNodeId((curr) => curr === val.nodeId ? null : curr);
2488
+ finalOutput = val.output;
2489
+ setOutput(val.output);
2490
+ optionsRef.current?.onNodeCompleted?.(val);
2491
+ } else if (event.name === "workflow_node_failed") {
2492
+ const val = event.value;
2493
+ setNodeRuns((prev) => {
2494
+ const existing = prev[val.nodeId];
2495
+ return {
2496
+ ...prev,
2497
+ [val.nodeId]: {
2498
+ nodeId: val.nodeId,
2499
+ nodeType: existing?.nodeType ?? "unknown",
2500
+ nodeLabel: existing?.nodeLabel ?? val.nodeId,
2501
+ status: "failed",
2502
+ startedAt: existing?.startedAt,
2503
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
2504
+ input: existing?.input,
2505
+ error: val.error
2506
+ }
2507
+ };
2508
+ });
2509
+ setActiveNodeId((curr) => curr === val.nodeId ? null : curr);
2510
+ optionsRef.current?.onNodeFailed?.(val);
2511
+ }
2512
+ } else if (event.type === "RUN_FINISHED") {
2513
+ setStatus("completed");
2514
+ setActiveNodeId(null);
2515
+ optionsRef.current?.onFinish?.({
2516
+ runId: assignedRunId || "",
2517
+ output: finalOutput,
2518
+ text: accumulatedText
2519
+ });
2520
+ } else if (event.type === "RUN_ERROR") {
2521
+ const runErr = new Error(event.message || event.code || "Workflow run error");
2522
+ setStatus("failed");
2523
+ setError(runErr);
2524
+ setActiveNodeId(null);
2525
+ optionsRef.current?.onError?.(runErr);
2526
+ }
2527
+ } catch {
2528
+ }
2529
+ }
2530
+ }
2531
+ }
2532
+ setStatus((prev) => prev === "running" ? "completed" : prev);
2533
+ return finalOutput;
2534
+ } catch (err) {
2535
+ if (controller.signal.aborted) {
2536
+ setStatus("cancelled");
2537
+ return null;
2538
+ }
2539
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2540
+ setStatus("failed");
2541
+ setError(errorObj);
2542
+ setActiveNodeId(null);
2543
+ optionsRef.current?.onError?.(errorObj);
2544
+ throw errorObj;
2545
+ } finally {
2546
+ if (abortControllerRef.current === controller) {
2547
+ abortControllerRef.current = null;
2548
+ }
2549
+ }
2550
+ },
2551
+ [getAuthToken]
2552
+ );
2553
+ const start = (0, import_react13.useCallback)(
2554
+ async (input, opts) => {
2555
+ const targetWfId = opts?.workflowId || workflowId;
2556
+ if (!targetWfId) {
2557
+ throw new Error("Workflow ID must be provided to start execution");
2558
+ }
2559
+ setNodeRuns({});
2560
+ setText("");
2561
+ setOutput(null);
2562
+ setEvents([]);
2563
+ setActiveNodeId(null);
2564
+ const cleanBase = baseUrl.replace(/\/+$/, "");
2565
+ const url = `${cleanBase}/workflows/${targetWfId}/stream`;
2566
+ const body = JSON.stringify({
2567
+ input: input ?? {},
2568
+ dryRun: opts?.dryRun ?? false
2569
+ });
2570
+ return processStream(url, "POST", body);
2571
+ },
2572
+ [workflowId, baseUrl, processStream]
2573
+ );
2574
+ const resume = (0, import_react13.useCallback)(
2575
+ async (targetRunId, sinceSeq) => {
2576
+ if (!targetRunId) {
2577
+ throw new Error("Run ID is required to resume stream");
2578
+ }
2579
+ setRunId(targetRunId);
2580
+ const cleanBase = baseUrl.replace(/\/+$/, "");
2581
+ const query = sinceSeq !== void 0 ? `?since=${sinceSeq}` : "";
2582
+ const url = `${cleanBase}/workflows/runs/${targetRunId}/resume${query}`;
2583
+ await processStream(url, "GET", void 0, targetRunId);
2584
+ },
2585
+ [baseUrl, processStream]
2586
+ );
2587
+ (0, import_react13.useEffect)(() => {
2588
+ return () => {
2589
+ if (abortControllerRef.current) {
2590
+ abortControllerRef.current.abort();
2591
+ }
2592
+ };
2593
+ }, []);
2594
+ return {
2595
+ runId,
2596
+ status,
2597
+ isRunning: status === "running",
2598
+ activeNodeId,
2599
+ nodeRuns,
2600
+ text,
2601
+ output,
2602
+ error,
2603
+ events,
2604
+ start,
2605
+ cancel,
2606
+ resume,
2607
+ reset
2608
+ };
2609
+ }
2610
+
2611
+ // src/hooks/useWorkflowRuns.ts
2612
+ var import_react14 = require("react");
2613
+ function useWorkflowRuns(workflowId, options) {
2614
+ const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
2615
+ const { autoFetch = true, page = 1, limit = 20 } = opts;
2616
+ const { fetchWithAuth } = usePersonaContext();
2617
+ const [runs, setRuns] = (0, import_react14.useState)([]);
2618
+ const [pagination, setPagination] = (0, import_react14.useState)({
2619
+ page,
2620
+ limit,
2621
+ total: 0,
2622
+ totalPages: 0
2623
+ });
2624
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
2625
+ const [error, setError] = (0, import_react14.useState)(null);
2626
+ const fetchRuns = (0, import_react14.useCallback)(async () => {
2627
+ if (!workflowId) return [];
2628
+ setIsLoading(true);
2629
+ setError(null);
2630
+ try {
2631
+ const query = `?page=${page}&limit=${limit}`;
2632
+ const res = await fetchWithAuth(`/workflows/${workflowId}/runs${query}`);
2633
+ if (!res.ok) {
2634
+ throw new Error(`Failed to list workflow runs: ${res.statusText}`);
2635
+ }
2636
+ const data = await res.json();
2637
+ const items = Array.isArray(data) ? data : data?.items || data?.runs || [];
2638
+ setRuns(items);
2639
+ if (data?.pagination) {
2640
+ setPagination({
2641
+ page: data.pagination.page ?? page,
2642
+ limit: data.pagination.limit ?? limit,
2643
+ total: data.pagination.total ?? items.length,
2644
+ totalPages: data.pagination.totalPages ?? 1
2645
+ });
2646
+ }
2647
+ return items;
2648
+ } catch (err) {
2649
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2650
+ setError(errorObj);
2651
+ return [];
2652
+ } finally {
2653
+ setIsLoading(false);
2654
+ }
2655
+ }, [fetchWithAuth, workflowId, page, limit]);
2656
+ const getRun = (0, import_react14.useCallback)(
2657
+ async (runId) => {
2658
+ if (!runId) throw new Error("Run ID is required");
2659
+ const res = await fetchWithAuth(`/workflows/runs/${runId}`);
2660
+ if (!res.ok) {
2661
+ throw new Error(`Failed to fetch workflow run (${res.status}): ${res.statusText}`);
2662
+ }
2663
+ return await res.json();
2664
+ },
2665
+ [fetchWithAuth]
2666
+ );
2667
+ const cancelRun = (0, import_react14.useCallback)(
2668
+ async (runId) => {
2669
+ if (!runId) throw new Error("Run ID is required");
2670
+ const res = await fetchWithAuth(`/workflows/runs/${runId}/cancel`, {
2671
+ method: "POST"
2672
+ });
2673
+ if (!res.ok) {
2674
+ throw new Error(`Failed to cancel workflow run (${res.status}): ${res.statusText}`);
2675
+ }
2676
+ setRuns(
2677
+ (prev) => prev.map((r) => r._id === runId ? { ...r, status: "cancelled" } : r)
2678
+ );
2679
+ },
2680
+ [fetchWithAuth]
2681
+ );
2682
+ (0, import_react14.useEffect)(() => {
2683
+ if (autoFetch && workflowId) {
2684
+ void fetchRuns();
2685
+ }
2686
+ }, [autoFetch, workflowId, fetchRuns]);
2687
+ return {
2688
+ runs,
2689
+ pagination,
2690
+ isLoading,
2691
+ error,
2692
+ refetch: fetchRuns,
2693
+ getRun,
2694
+ cancelRun
2695
+ };
2696
+ }
2697
+
2106
2698
  // src/index.ts
2107
2699
  var import_logger2 = require("@personaai/logger");
2108
- var VERSION = "0.8.1";
2700
+ var VERSION = "0.9.0";
2109
2701
  // Annotate the CommonJS export names for ESM import in node:
2110
2702
  0 && (module.exports = {
2111
2703
  PersonaProvider,
@@ -2126,6 +2718,10 @@ var VERSION = "0.8.1";
2126
2718
  useMemory,
2127
2719
  usePersonaContext,
2128
2720
  useThreads,
2129
- useVoice
2721
+ useVoice,
2722
+ useWorkflow,
2723
+ useWorkflowRuns,
2724
+ useWorkflowStream,
2725
+ useWorkflows
2130
2726
  });
2131
2727
  //# sourceMappingURL=index.cjs.map