@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 +14 -10
- package/dist/index.cjs +603 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +222 -3
- package/dist/index.d.ts +222 -3
- package/dist/index.js +598 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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
|
|
50
|
-
|
|
|
51
|
-
| `useChat`
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|