@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/README.md +37 -10
- package/dist/index.cjs +1279 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +299 -3
- package/dist/index.d.ts +299 -3
- package/dist/index.js +1277 -129
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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/
|
|
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/
|
|
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,
|
|
1717
|
+
const [memory, setMemory] = (0, import_react4.useState)({
|
|
1156
1718
|
userFiles: [],
|
|
1157
1719
|
agentMemories: []
|
|
1158
1720
|
});
|
|
1159
|
-
const [isLoading, setIsLoading] = (0,
|
|
1160
|
-
const [error, setError] = (0,
|
|
1161
|
-
const fetchMemory = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
1800
|
+
var import_react5 = require("react");
|
|
1239
1801
|
function useThreads(autoFetch = true) {
|
|
1240
1802
|
const { fetchWithAuth, defaultAgentId } = usePersonaContext();
|
|
1241
|
-
const [threads, setThreads] = (0,
|
|
1242
|
-
const [isLoading, setIsLoading] = (0,
|
|
1243
|
-
const [error, setError] = (0,
|
|
1244
|
-
const fetchThreads = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
1893
|
+
const renameThread = (0, import_react5.useCallback)(
|
|
1332
1894
|
(threadId, title) => updateThread(threadId, { title }),
|
|
1333
1895
|
[updateThread]
|
|
1334
1896
|
);
|
|
1335
|
-
const resetThread = (0,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
1490
|
-
const [isMuted, setIsMuted] = (0,
|
|
1491
|
-
const [transcript, setTranscript] = (0,
|
|
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,
|
|
2056
|
+
const [partial, setPartial] = (0, import_react6.useState)(
|
|
1495
2057
|
null
|
|
1496
2058
|
);
|
|
1497
|
-
const [toolCalls, setToolCalls] = (0,
|
|
1498
|
-
const [error, setError] = (0,
|
|
1499
|
-
const [endReason, setEndReason] = (0,
|
|
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,
|
|
1503
|
-
const streamRef = (0,
|
|
1504
|
-
const inputCtxRef = (0,
|
|
1505
|
-
const outputCtxRef = (0,
|
|
1506
|
-
const recorderNodeRef = (0,
|
|
1507
|
-
const playerNodeRef = (0,
|
|
1508
|
-
const acceptedTurnSeqRef = (0,
|
|
1509
|
-
const mountedRef = (0,
|
|
1510
|
-
const lastFinalTurnRef = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
1557
|
-
const startCapture = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
2402
|
+
var import_react7 = require("react");
|
|
1841
2403
|
function useFiles(autoFetch = true) {
|
|
1842
2404
|
const { fetchWithAuth } = usePersonaContext();
|
|
1843
|
-
const [files, setFiles] = (0,
|
|
1844
|
-
const [isLoading, setIsLoading] = (0,
|
|
1845
|
-
const [isUploading, setIsUploading] = (0,
|
|
1846
|
-
const [error, setError] = (0,
|
|
1847
|
-
const fetchFiles = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
2483
|
+
const getDownloadUrl = (0, import_react7.useCallback)((fileId) => {
|
|
1922
2484
|
return `/files/${fileId}`;
|
|
1923
2485
|
}, []);
|
|
1924
|
-
(0,
|
|
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
|
|
2505
|
+
var import_react8 = require("react");
|
|
1944
2506
|
function useAgents(autoFetch = true) {
|
|
1945
2507
|
const { fetchWithAuth } = usePersonaContext();
|
|
1946
|
-
const [agents, setAgents] = (0,
|
|
1947
|
-
const [isLoading, setIsLoading] = (0,
|
|
1948
|
-
const [error, setError] = (0,
|
|
1949
|
-
const fetchAgents = (0,
|
|
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,
|
|
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
|
|
2543
|
+
var import_react9 = require("react");
|
|
1982
2544
|
function useConnection(autoCheck = true) {
|
|
1983
2545
|
const { fetchWithAuth } = usePersonaContext();
|
|
1984
|
-
const [health, setHealth] = (0,
|
|
1985
|
-
const [isConnected, setIsConnected] = (0,
|
|
1986
|
-
const [isLoading, setIsLoading] = (0,
|
|
1987
|
-
const checkHealth = (0,
|
|
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,
|
|
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
|
|
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,
|
|
2026
|
-
const [isLoading, setIsLoading] = (0,
|
|
2027
|
-
const [error, setError] = (0,
|
|
2028
|
-
const fetchConnections = (0,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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.
|
|
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
|