@paymanai/payman-typescript-ask-sdk 4.0.9 → 4.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +194 -47
- package/dist/index.d.ts +194 -47
- package/dist/index.js +506 -654
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +494 -654
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +509 -657
- package/dist/index.native.js.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
|
|
4
6
|
|
|
5
7
|
// src/utils/generateId.ts
|
|
6
8
|
function generateId() {
|
|
@@ -11,10 +13,95 @@ function generateId() {
|
|
|
11
13
|
});
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
// src/utils/chatStore.ts
|
|
17
|
+
var memoryStore = /* @__PURE__ */ new Map();
|
|
18
|
+
var chatStore = {
|
|
19
|
+
get(key) {
|
|
20
|
+
return memoryStore.get(key) ?? [];
|
|
21
|
+
},
|
|
22
|
+
set(key, messages) {
|
|
23
|
+
memoryStore.set(key, messages);
|
|
24
|
+
},
|
|
25
|
+
delete(key) {
|
|
26
|
+
memoryStore.delete(key);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/utils/activeStreamStore.ts
|
|
31
|
+
var streams = /* @__PURE__ */ new Map();
|
|
32
|
+
var activeStreamStore = {
|
|
33
|
+
has(key) {
|
|
34
|
+
return streams.has(key);
|
|
35
|
+
},
|
|
36
|
+
get(key) {
|
|
37
|
+
const entry = streams.get(key);
|
|
38
|
+
if (!entry) return null;
|
|
39
|
+
return { messages: entry.messages, isWaiting: entry.isWaiting };
|
|
40
|
+
},
|
|
41
|
+
// Called before startStream — registers the controller and initial messages
|
|
42
|
+
start(key, abortController, initialMessages) {
|
|
43
|
+
const existing = streams.get(key);
|
|
44
|
+
streams.set(key, {
|
|
45
|
+
messages: initialMessages,
|
|
46
|
+
isWaiting: true,
|
|
47
|
+
abortController,
|
|
48
|
+
listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
// Called by the stream on every event — applies the same updater pattern React uses
|
|
52
|
+
applyMessages(key, updater) {
|
|
53
|
+
const entry = streams.get(key);
|
|
54
|
+
if (!entry) return;
|
|
55
|
+
const next = typeof updater === "function" ? updater(entry.messages) : updater;
|
|
56
|
+
entry.messages = next;
|
|
57
|
+
entry.listeners.forEach((l) => l(next, entry.isWaiting));
|
|
58
|
+
},
|
|
59
|
+
setWaiting(key, waiting) {
|
|
60
|
+
const entry = streams.get(key);
|
|
61
|
+
if (!entry) return;
|
|
62
|
+
entry.isWaiting = waiting;
|
|
63
|
+
entry.listeners.forEach((l) => l(entry.messages, waiting));
|
|
64
|
+
},
|
|
65
|
+
// Called when stream completes — persists to chatStore and cleans up
|
|
66
|
+
complete(key) {
|
|
67
|
+
const entry = streams.get(key);
|
|
68
|
+
if (!entry) return;
|
|
69
|
+
entry.isWaiting = false;
|
|
70
|
+
entry.listeners.forEach((l) => l(entry.messages, false));
|
|
71
|
+
const toSave = entry.messages.filter((m) => !m.isStreaming);
|
|
72
|
+
if (toSave.length > 0) chatStore.set(key, toSave);
|
|
73
|
+
streams.delete(key);
|
|
74
|
+
},
|
|
75
|
+
// Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
|
|
76
|
+
subscribe(key, listener) {
|
|
77
|
+
const entry = streams.get(key);
|
|
78
|
+
if (!entry) return () => {
|
|
79
|
+
};
|
|
80
|
+
entry.listeners.add(listener);
|
|
81
|
+
return () => {
|
|
82
|
+
streams.get(key)?.listeners.delete(listener);
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
// Rename an entry — used when the server assigns a new session ID mid-stream
|
|
86
|
+
rename(oldKey, newKey) {
|
|
87
|
+
const entry = streams.get(oldKey);
|
|
88
|
+
if (!entry) return;
|
|
89
|
+
streams.set(newKey, entry);
|
|
90
|
+
streams.delete(oldKey);
|
|
91
|
+
},
|
|
92
|
+
// Explicit user cancel — aborts the controller and removes the entry
|
|
93
|
+
abort(key) {
|
|
94
|
+
const entry = streams.get(key);
|
|
95
|
+
if (!entry) return;
|
|
96
|
+
entry.abortController.abort();
|
|
97
|
+
streams.delete(key);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
14
101
|
// src/utils/streamingClient.ts
|
|
15
102
|
function yieldAfterProgressEvent(event) {
|
|
16
103
|
const t = event.eventType;
|
|
17
|
-
if (t === "RUN_IN_PROGRESS" || t === "INTENT_PROGRESS" || t === "
|
|
104
|
+
if (t === "RUN_IN_PROGRESS" || t === "INTENT_PROGRESS" || t === "THINKING_DELTA") {
|
|
18
105
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
19
106
|
}
|
|
20
107
|
return Promise.resolve();
|
|
@@ -256,62 +343,28 @@ function normalizeEvent(event) {
|
|
|
256
343
|
return event;
|
|
257
344
|
}
|
|
258
345
|
}
|
|
259
|
-
function
|
|
260
|
-
|
|
346
|
+
function isVerificationSchema(schema) {
|
|
347
|
+
const props = schema?.properties;
|
|
348
|
+
if (!props) return false;
|
|
349
|
+
const keys = Object.keys(props);
|
|
350
|
+
return keys.length === 1 && keys[0] === "verificationCode";
|
|
261
351
|
}
|
|
262
|
-
function
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
case "
|
|
267
|
-
return
|
|
268
|
-
case "
|
|
269
|
-
return
|
|
270
|
-
case "USER_ACTION_INVALID":
|
|
271
|
-
return safeRaw || "Invalid code. Please try again.";
|
|
272
|
-
case "USER_ACTION_REJECTED":
|
|
273
|
-
return safeRaw || "Verification cancelled";
|
|
274
|
-
case "USER_ACTION_EXPIRED":
|
|
275
|
-
return safeRaw || "Verification expired";
|
|
276
|
-
case "USER_ACTION_RESENT":
|
|
277
|
-
return safeRaw || "Verification code resent";
|
|
278
|
-
case "USER_ACTION_FAILED":
|
|
279
|
-
return safeRaw || "Verification failed";
|
|
352
|
+
function classifyUserActionKind(action, schema) {
|
|
353
|
+
switch ((action || "").toLowerCase()) {
|
|
354
|
+
case "userverificationrequest":
|
|
355
|
+
return "verification";
|
|
356
|
+
case "usernotificationrequest":
|
|
357
|
+
return "notification";
|
|
358
|
+
case "userformrequest":
|
|
359
|
+
return isVerificationSchema(schema) ? "verification" : "form";
|
|
280
360
|
default:
|
|
281
|
-
return
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
function workingPhaseDetailForDisplay(raw) {
|
|
285
|
-
const t = raw.trim();
|
|
286
|
-
if (!t) return "";
|
|
287
|
-
if (/^Identified\s+\d+\s+tasks?\s+to\s+execute\.?$/i.test(t)) {
|
|
288
|
-
return "";
|
|
361
|
+
return isVerificationSchema(schema) ? "verification" : "form";
|
|
289
362
|
}
|
|
290
|
-
return t;
|
|
291
363
|
}
|
|
292
364
|
function getEventText(event, field) {
|
|
293
365
|
const value = event[field];
|
|
294
366
|
return typeof value === "string" ? value.trim() : "";
|
|
295
367
|
}
|
|
296
|
-
function shouldShowIntentHeader(event) {
|
|
297
|
-
const workerName = getEventText(event, "workerName");
|
|
298
|
-
const intentId = getEventText(event, "intentId");
|
|
299
|
-
return Boolean(workerName && intentId && workerName === intentId);
|
|
300
|
-
}
|
|
301
|
-
function addThinkingHeader(state, header) {
|
|
302
|
-
state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + header;
|
|
303
|
-
}
|
|
304
|
-
function addThinkingDetail(state, detail) {
|
|
305
|
-
const trimmed = detail.trim();
|
|
306
|
-
if (!trimmed) return;
|
|
307
|
-
state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + trimmed;
|
|
308
|
-
}
|
|
309
|
-
function addThinkingLine(state, header, detail) {
|
|
310
|
-
state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + header + "\n" + detail;
|
|
311
|
-
}
|
|
312
|
-
function appendThinkingText(state, text) {
|
|
313
|
-
state.formattedThinkingText += text;
|
|
314
|
-
}
|
|
315
368
|
function updateExecutionStageMessage(state, msg) {
|
|
316
369
|
if (!msg) return;
|
|
317
370
|
for (let i = state.steps.length - 1; i >= 0; i--) {
|
|
@@ -332,26 +385,38 @@ function completeLastInProgressStep(steps) {
|
|
|
332
385
|
}
|
|
333
386
|
function createInitialV2State() {
|
|
334
387
|
return {
|
|
335
|
-
formattedThinkingText: "",
|
|
336
388
|
finalResponse: "",
|
|
337
|
-
currentWorker: "",
|
|
338
389
|
lastEventType: "",
|
|
339
390
|
sessionId: void 0,
|
|
340
391
|
executionId: void 0,
|
|
341
392
|
hasError: false,
|
|
342
393
|
errorMessage: "",
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
394
|
+
userActions: [],
|
|
395
|
+
notifications: [],
|
|
396
|
+
lastUserAction: void 0,
|
|
397
|
+
lastNotification: void 0,
|
|
346
398
|
finalData: void 0,
|
|
347
399
|
steps: [],
|
|
348
400
|
stepCounter: 0,
|
|
349
401
|
currentExecutingStepId: void 0
|
|
350
402
|
};
|
|
351
403
|
}
|
|
404
|
+
function upsertUserAction(state, req) {
|
|
405
|
+
const active = { ...req, status: "pending" };
|
|
406
|
+
const matchIdx = state.userActions.findIndex(
|
|
407
|
+
(p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
|
|
408
|
+
);
|
|
409
|
+
if (matchIdx >= 0) {
|
|
410
|
+
state.userActions[matchIdx] = active;
|
|
411
|
+
} else {
|
|
412
|
+
state.userActions.push(active);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
352
415
|
function processStreamEventV2(rawEvent, state) {
|
|
353
416
|
const event = normalizeEvent(rawEvent);
|
|
354
417
|
const eventType = event.eventType;
|
|
418
|
+
state.lastUserAction = void 0;
|
|
419
|
+
state.lastNotification = void 0;
|
|
355
420
|
if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
|
|
356
421
|
if (event.executionId) state.executionId = event.executionId;
|
|
357
422
|
if (event.sessionId) state.sessionId = event.sessionId;
|
|
@@ -367,10 +432,6 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
367
432
|
return state;
|
|
368
433
|
}
|
|
369
434
|
if (typeof eventType === "string" && eventType.toUpperCase() === "THINKING_DELTA") {
|
|
370
|
-
const text = typeof event.text === "string" ? event.text : "";
|
|
371
|
-
if (text) {
|
|
372
|
-
appendThinkingText(state, text);
|
|
373
|
-
}
|
|
374
435
|
if (event.executionId) state.executionId = event.executionId;
|
|
375
436
|
if (event.sessionId) state.sessionId = event.sessionId;
|
|
376
437
|
state.lastEventType = "THINKING_DELTA";
|
|
@@ -381,7 +442,6 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
381
442
|
const message = getEventMessage(event);
|
|
382
443
|
switch (eventType) {
|
|
383
444
|
case "WORKFLOW_STARTED":
|
|
384
|
-
case "STARTED":
|
|
385
445
|
state.lastEventType = eventType;
|
|
386
446
|
break;
|
|
387
447
|
case "INTENT_PROGRESS": {
|
|
@@ -394,97 +454,7 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
394
454
|
state.lastEventType = eventType;
|
|
395
455
|
break;
|
|
396
456
|
}
|
|
397
|
-
case "INTENT_THINKING": {
|
|
398
|
-
const worker = getEventText(event, "workerName") || "Worker";
|
|
399
|
-
const msg = getEventText(event, "message") || "Thinking...";
|
|
400
|
-
const showHeader = shouldShowIntentHeader(event);
|
|
401
|
-
if (worker !== state.currentWorker) {
|
|
402
|
-
state.currentWorker = worker;
|
|
403
|
-
if (showHeader && msg && msg !== worker) {
|
|
404
|
-
addThinkingLine(state, `**${worker}**`, msg);
|
|
405
|
-
} else if (showHeader) {
|
|
406
|
-
addThinkingHeader(state, `**${worker}**`);
|
|
407
|
-
} else if (msg !== "Thinking...") {
|
|
408
|
-
addThinkingDetail(state, msg);
|
|
409
|
-
}
|
|
410
|
-
} else if ((showHeader || msg !== "Thinking...")) {
|
|
411
|
-
appendThinkingText(state, "\n" + msg);
|
|
412
|
-
}
|
|
413
|
-
const lastInProgress = [...state.steps].reverse().find((s) => s.status === "in_progress");
|
|
414
|
-
if (lastInProgress) {
|
|
415
|
-
lastInProgress.thinkingText = "";
|
|
416
|
-
lastInProgress.isThinking = true;
|
|
417
|
-
}
|
|
418
|
-
state.lastEventType = "INTENT_THINKING";
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
case "INTENT_THINKING_CONT": {
|
|
422
|
-
const msg = event.message || "";
|
|
423
|
-
if (!msg) break;
|
|
424
|
-
if (state.lastEventType === "INTENT_THINKING") {
|
|
425
|
-
appendThinkingText(state, "\n" + msg);
|
|
426
|
-
} else {
|
|
427
|
-
appendThinkingText(state, msg);
|
|
428
|
-
}
|
|
429
|
-
const thinkingStep = [...state.steps].reverse().find((s) => s.isThinking);
|
|
430
|
-
if (thinkingStep) {
|
|
431
|
-
thinkingStep.thinkingText = (thinkingStep.thinkingText || "") + msg;
|
|
432
|
-
}
|
|
433
|
-
state.lastEventType = "INTENT_THINKING_CONT";
|
|
434
|
-
break;
|
|
435
|
-
}
|
|
436
|
-
case "ORCHESTRATOR_THINKING": {
|
|
437
|
-
addThinkingLine(state, "**Planning**", event.message || "Understanding your request...");
|
|
438
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
439
|
-
state.steps.push({
|
|
440
|
-
id: stepId,
|
|
441
|
-
eventType,
|
|
442
|
-
message,
|
|
443
|
-
status: "in_progress",
|
|
444
|
-
timestamp: Date.now(),
|
|
445
|
-
elapsedMs: event.elapsedMs
|
|
446
|
-
});
|
|
447
|
-
state.currentExecutingStepId = stepId;
|
|
448
|
-
state.lastEventType = eventType;
|
|
449
|
-
break;
|
|
450
|
-
}
|
|
451
|
-
case "ORCHESTRATOR_COMPLETED": {
|
|
452
|
-
const workingDetail = workingPhaseDetailForDisplay(message);
|
|
453
|
-
if (workingDetail) {
|
|
454
|
-
addThinkingLine(state, "**Working**", workingDetail);
|
|
455
|
-
} else {
|
|
456
|
-
addThinkingHeader(state, "**Working**");
|
|
457
|
-
}
|
|
458
|
-
state.steps.push({
|
|
459
|
-
id: `step-${state.stepCounter++}`,
|
|
460
|
-
eventType: "WORKING",
|
|
461
|
-
message: workingDetail,
|
|
462
|
-
status: "completed",
|
|
463
|
-
timestamp: Date.now()
|
|
464
|
-
});
|
|
465
|
-
const step = state.steps.find((s) => s.eventType === "ORCHESTRATOR_THINKING" && s.status === "in_progress");
|
|
466
|
-
if (step) {
|
|
467
|
-
step.status = "completed";
|
|
468
|
-
if (event.elapsedMs) step.elapsedMs = event.elapsedMs;
|
|
469
|
-
if (step.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
|
|
470
|
-
}
|
|
471
|
-
state.lastEventType = eventType;
|
|
472
|
-
break;
|
|
473
|
-
}
|
|
474
457
|
case "INTENT_STARTED": {
|
|
475
|
-
const worker = getEventText(event, "workerName") || "Worker";
|
|
476
|
-
const msg = getEventText(event, "message") || "Starting...";
|
|
477
|
-
const showHeader = shouldShowIntentHeader(event);
|
|
478
|
-
state.currentWorker = worker;
|
|
479
|
-
if (showHeader && msg !== worker) {
|
|
480
|
-
addThinkingLine(state, `**${worker}**`, msg);
|
|
481
|
-
} else if (showHeader) {
|
|
482
|
-
addThinkingHeader(state, `**${worker}**`);
|
|
483
|
-
} else {
|
|
484
|
-
addThinkingDetail(state, msg);
|
|
485
|
-
}
|
|
486
|
-
const thinkingStep = state.steps.find((s) => s.isThinking);
|
|
487
|
-
if (thinkingStep) thinkingStep.isThinking = false;
|
|
488
458
|
const stepId = `step-${state.stepCounter++}`;
|
|
489
459
|
state.steps.push({
|
|
490
460
|
id: stepId,
|
|
@@ -503,55 +473,18 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
503
473
|
const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
|
|
504
474
|
if (intentStep) {
|
|
505
475
|
intentStep.status = "completed";
|
|
506
|
-
intentStep.isThinking = false;
|
|
507
476
|
if (event.elapsedMs) intentStep.elapsedMs = event.elapsedMs;
|
|
508
477
|
if (intentStep.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
|
|
509
478
|
}
|
|
510
479
|
state.lastEventType = eventType;
|
|
511
480
|
break;
|
|
512
481
|
}
|
|
513
|
-
case "
|
|
514
|
-
addThinkingLine(state, "**Finalizing**", event.message || "Preparing response...");
|
|
515
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
516
|
-
state.steps.push({
|
|
517
|
-
id: stepId,
|
|
518
|
-
eventType,
|
|
519
|
-
message,
|
|
520
|
-
status: "in_progress",
|
|
521
|
-
timestamp: Date.now(),
|
|
522
|
-
elapsedMs: event.elapsedMs
|
|
523
|
-
});
|
|
524
|
-
state.currentExecutingStepId = stepId;
|
|
525
|
-
state.lastEventType = eventType;
|
|
526
|
-
break;
|
|
527
|
-
}
|
|
528
|
-
case "AGGREGATOR_COMPLETED": {
|
|
529
|
-
appendThinkingText(state, "\n" + (event.message || "Response ready"));
|
|
530
|
-
const step = state.steps.find((s) => s.eventType === "AGGREGATOR_THINKING" && s.status === "in_progress");
|
|
531
|
-
if (step) {
|
|
532
|
-
step.status = "completed";
|
|
533
|
-
if (event.elapsedMs) step.elapsedMs = event.elapsedMs;
|
|
534
|
-
if (step.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
|
|
535
|
-
}
|
|
536
|
-
state.lastEventType = eventType;
|
|
537
|
-
break;
|
|
538
|
-
}
|
|
539
|
-
case "WORKFLOW_COMPLETED":
|
|
540
|
-
case "COMPLETED": {
|
|
482
|
+
case "WORKFLOW_COMPLETED": {
|
|
541
483
|
const totalTime = Number(event.totalTimeMs);
|
|
542
484
|
if (Number.isFinite(totalTime) && totalTime > 0) {
|
|
543
485
|
state.totalElapsedMs = totalTime;
|
|
544
486
|
}
|
|
545
|
-
|
|
546
|
-
const trace = event.trace && typeof event.trace === "object" ? event.trace : null;
|
|
547
|
-
if (!content && trace?.workflowMsg && typeof trace.workflowMsg === "string") {
|
|
548
|
-
content = trace.workflowMsg;
|
|
549
|
-
}
|
|
550
|
-
if (!content && trace?.aggregator && typeof trace.aggregator === "object") {
|
|
551
|
-
const agg = trace.aggregator;
|
|
552
|
-
if (typeof agg.response === "string") content = agg.response;
|
|
553
|
-
else content = extractResponseContent(agg.response);
|
|
554
|
-
}
|
|
487
|
+
const content = extractResponseContent(event.response);
|
|
555
488
|
if (content) {
|
|
556
489
|
state.finalResponse = content;
|
|
557
490
|
if (event.trace && typeof event.trace === "object") {
|
|
@@ -566,178 +499,81 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
566
499
|
state.steps.forEach((step) => {
|
|
567
500
|
if (step.status === "in_progress") {
|
|
568
501
|
step.status = "completed";
|
|
569
|
-
step.isThinking = false;
|
|
570
502
|
}
|
|
571
503
|
});
|
|
572
504
|
state.lastEventType = eventType;
|
|
573
505
|
break;
|
|
574
506
|
}
|
|
575
507
|
case "USER_ACTION_REQUIRED": {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
508
|
+
const rawAction = typeof event.action === "string" ? event.action : void 0;
|
|
509
|
+
const schema = event.requestedSchema;
|
|
510
|
+
const kind = classifyUserActionKind(rawAction, schema);
|
|
511
|
+
const promptMessage = typeof event.message === "string" && event.message.trim() || message || "";
|
|
512
|
+
const userActionId = typeof event.userActionId === "string" ? event.userActionId : "";
|
|
513
|
+
if (kind === "notification") {
|
|
514
|
+
const notification = {
|
|
515
|
+
id: userActionId || `note-${state.stepCounter++}`,
|
|
516
|
+
message: promptMessage
|
|
584
517
|
};
|
|
518
|
+
state.notifications.push(notification);
|
|
519
|
+
state.lastNotification = notification;
|
|
520
|
+
state.lastEventType = eventType;
|
|
521
|
+
break;
|
|
585
522
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
eventType,
|
|
590
|
-
req?.message || message
|
|
591
|
-
);
|
|
592
|
-
if (req) {
|
|
593
|
-
addThinkingLine(state, "**Verification Required**", displayMessage);
|
|
523
|
+
if (!userActionId) {
|
|
524
|
+
state.lastEventType = eventType;
|
|
525
|
+
break;
|
|
594
526
|
}
|
|
595
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
596
|
-
state.steps.push({
|
|
597
|
-
id: stepId,
|
|
598
|
-
eventType,
|
|
599
|
-
message: displayMessage,
|
|
600
|
-
status: "in_progress",
|
|
601
|
-
timestamp: Date.now(),
|
|
602
|
-
elapsedMs: event.elapsedMs
|
|
603
|
-
});
|
|
604
|
-
state.currentExecutingStepId = stepId;
|
|
605
|
-
state.lastEventType = eventType;
|
|
606
|
-
break;
|
|
607
|
-
}
|
|
608
|
-
case "USER_ACTION_SUCCESS": {
|
|
609
|
-
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
610
|
-
appendThinkingText(state, "\n\u2713 " + displayMessage);
|
|
611
527
|
completeLastInProgressStep(state.steps);
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
528
|
+
const verificationType = event.verificationType === "ALPHANUMERIC_CODE" || event.verificationType === "NUMERIC_CODE" ? event.verificationType : void 0;
|
|
529
|
+
const request = {
|
|
530
|
+
userActionId,
|
|
531
|
+
kind,
|
|
532
|
+
rawAction,
|
|
533
|
+
subAction: typeof event.subAction === "string" ? event.subAction : void 0,
|
|
534
|
+
verificationType,
|
|
535
|
+
expirySeconds: typeof event.expirySeconds === "number" ? event.expirySeconds : void 0,
|
|
536
|
+
message: promptMessage || void 0,
|
|
537
|
+
requestedSchema: schema,
|
|
538
|
+
metadata: event.metadata,
|
|
539
|
+
toolCallId: typeof event.toolCallId === "string" ? event.toolCallId : void 0,
|
|
540
|
+
executionId: state.executionId,
|
|
541
|
+
sessionId: state.sessionId
|
|
542
|
+
};
|
|
543
|
+
upsertUserAction(state, request);
|
|
544
|
+
state.lastUserAction = request;
|
|
615
545
|
const stepId = `step-${state.stepCounter++}`;
|
|
616
546
|
state.steps.push({
|
|
617
547
|
id: stepId,
|
|
618
548
|
eventType,
|
|
619
|
-
message:
|
|
620
|
-
status: "completed",
|
|
621
|
-
timestamp: Date.now(),
|
|
622
|
-
elapsedMs: event.elapsedMs
|
|
623
|
-
});
|
|
624
|
-
state.lastEventType = eventType;
|
|
625
|
-
break;
|
|
626
|
-
}
|
|
627
|
-
case "USER_ACTION_INVALID": {
|
|
628
|
-
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
629
|
-
appendThinkingText(state, "\n\u2717 " + displayMessage);
|
|
630
|
-
completeLastInProgressStep(state.steps);
|
|
631
|
-
const errorStepId = `step-${state.stepCounter++}`;
|
|
632
|
-
state.steps.push({
|
|
633
|
-
id: errorStepId,
|
|
634
|
-
eventType,
|
|
635
|
-
message: displayMessage,
|
|
636
|
-
status: "error",
|
|
637
|
-
timestamp: Date.now(),
|
|
638
|
-
elapsedMs: event.elapsedMs
|
|
639
|
-
});
|
|
640
|
-
const retryStepId = `step-${state.stepCounter++}`;
|
|
641
|
-
state.steps.push({
|
|
642
|
-
id: retryStepId,
|
|
643
|
-
eventType: "USER_ACTION_REQUIRED",
|
|
644
|
-
message: "Waiting for verification...",
|
|
549
|
+
message: promptMessage || (kind === "verification" ? "Waiting for verification..." : "Waiting for your input..."),
|
|
645
550
|
status: "in_progress",
|
|
646
|
-
timestamp: Date.now()
|
|
647
|
-
});
|
|
648
|
-
state.currentExecutingStepId = retryStepId;
|
|
649
|
-
state.lastEventType = eventType;
|
|
650
|
-
break;
|
|
651
|
-
}
|
|
652
|
-
case "USER_ACTION_REJECTED": {
|
|
653
|
-
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
654
|
-
appendThinkingText(state, "\n" + displayMessage);
|
|
655
|
-
completeLastInProgressStep(state.steps);
|
|
656
|
-
state.userActionRequest = void 0;
|
|
657
|
-
state.userActionPending = false;
|
|
658
|
-
state.userActionResult = "rejected";
|
|
659
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
660
|
-
state.steps.push({
|
|
661
|
-
id: stepId,
|
|
662
|
-
eventType,
|
|
663
|
-
message: displayMessage,
|
|
664
|
-
status: "completed",
|
|
665
|
-
timestamp: Date.now(),
|
|
666
|
-
elapsedMs: event.elapsedMs
|
|
667
|
-
});
|
|
668
|
-
state.lastEventType = eventType;
|
|
669
|
-
break;
|
|
670
|
-
}
|
|
671
|
-
case "USER_ACTION_EXPIRED": {
|
|
672
|
-
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
673
|
-
appendThinkingText(state, "\n\u2717 " + displayMessage);
|
|
674
|
-
completeLastInProgressStep(state.steps);
|
|
675
|
-
state.userActionRequest = void 0;
|
|
676
|
-
state.userActionPending = false;
|
|
677
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
678
|
-
state.steps.push({
|
|
679
|
-
id: stepId,
|
|
680
|
-
eventType,
|
|
681
|
-
message: displayMessage,
|
|
682
|
-
status: "error",
|
|
683
|
-
timestamp: Date.now(),
|
|
684
|
-
elapsedMs: event.elapsedMs
|
|
685
|
-
});
|
|
686
|
-
state.lastEventType = eventType;
|
|
687
|
-
break;
|
|
688
|
-
}
|
|
689
|
-
case "USER_ACTION_RESENT": {
|
|
690
|
-
const displayMessage = getUserActionDisplayMessage(eventType, event.message);
|
|
691
|
-
appendThinkingText(state, "\n" + displayMessage);
|
|
692
|
-
const stepId = `step-${state.stepCounter++}`;
|
|
693
|
-
state.steps.push({
|
|
694
|
-
id: stepId,
|
|
695
|
-
eventType,
|
|
696
|
-
message: displayMessage,
|
|
697
|
-
status: "completed",
|
|
698
551
|
timestamp: Date.now(),
|
|
699
552
|
elapsedMs: event.elapsedMs
|
|
700
553
|
});
|
|
554
|
+
state.currentExecutingStepId = stepId;
|
|
701
555
|
state.lastEventType = eventType;
|
|
702
556
|
break;
|
|
703
557
|
}
|
|
704
|
-
case "
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
id: stepId,
|
|
716
|
-
eventType,
|
|
717
|
-
message: displayMessage,
|
|
718
|
-
status: "error",
|
|
719
|
-
timestamp: Date.now(),
|
|
720
|
-
elapsedMs: event.elapsedMs
|
|
721
|
-
});
|
|
558
|
+
case "USER_NOTIFICATION": {
|
|
559
|
+
const noteMessage = typeof event.message === "string" && event.message.trim() || message || "";
|
|
560
|
+
const userActionId = typeof event.userActionId === "string" ? event.userActionId : "";
|
|
561
|
+
if (noteMessage || userActionId) {
|
|
562
|
+
const notification = {
|
|
563
|
+
id: userActionId || `note-${state.stepCounter++}`,
|
|
564
|
+
message: noteMessage
|
|
565
|
+
};
|
|
566
|
+
state.notifications.push(notification);
|
|
567
|
+
state.lastNotification = notification;
|
|
568
|
+
}
|
|
722
569
|
state.lastEventType = eventType;
|
|
723
570
|
break;
|
|
724
571
|
}
|
|
725
572
|
case "WORKFLOW_ERROR":
|
|
726
|
-
case "ERROR":
|
|
727
573
|
state.hasError = true;
|
|
728
574
|
state.errorMessage = event.errorMessage || event.message || "Workflow error";
|
|
729
575
|
state.lastEventType = eventType;
|
|
730
576
|
break;
|
|
731
|
-
case "INTENT_ERROR": {
|
|
732
|
-
state.errorMessage = message || event.errorMessage || "An error occurred";
|
|
733
|
-
const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
|
|
734
|
-
if (intentStep) {
|
|
735
|
-
intentStep.status = "error";
|
|
736
|
-
intentStep.isThinking = false;
|
|
737
|
-
}
|
|
738
|
-
state.lastEventType = eventType;
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
577
|
// ---- K2 pipeline stage lifecycle events ----
|
|
742
578
|
//
|
|
743
579
|
// The k2-server playground streaming API emits
|
|
@@ -858,96 +694,6 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
858
694
|
return state;
|
|
859
695
|
}
|
|
860
696
|
|
|
861
|
-
// src/utils/messageStateManager.ts
|
|
862
|
-
function buildFormattedThinking(steps, allThinkingText) {
|
|
863
|
-
const parts = [];
|
|
864
|
-
const safeSteps = steps ?? [];
|
|
865
|
-
const cleanAll = allThinkingText.replace(/^\s+/, "");
|
|
866
|
-
if (cleanAll) {
|
|
867
|
-
const firstStepWithThinking = safeSteps.find(
|
|
868
|
-
(s) => s.thinkingText && s.thinkingText.trim()
|
|
869
|
-
);
|
|
870
|
-
if (!firstStepWithThinking) {
|
|
871
|
-
parts.push("**Preflight**");
|
|
872
|
-
parts.push(cleanAll);
|
|
873
|
-
} else {
|
|
874
|
-
const stepText = firstStepWithThinking.thinkingText.trim();
|
|
875
|
-
const idx = cleanAll.indexOf(stepText);
|
|
876
|
-
if (idx > 0) {
|
|
877
|
-
const orphaned = cleanAll.substring(0, idx).replace(/\s+$/, "");
|
|
878
|
-
if (orphaned) {
|
|
879
|
-
parts.push("**Preflight**");
|
|
880
|
-
parts.push(orphaned);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
for (const step of safeSteps) {
|
|
886
|
-
switch (step.eventType) {
|
|
887
|
-
case "STAGE_STARTED": {
|
|
888
|
-
if (step.message) parts.push(`**${step.message}**`);
|
|
889
|
-
break;
|
|
890
|
-
}
|
|
891
|
-
case "ORCHESTRATOR_THINKING":
|
|
892
|
-
parts.push("**Planning**");
|
|
893
|
-
if (step.message) parts.push(step.message);
|
|
894
|
-
break;
|
|
895
|
-
case "INTENT_STARTED": {
|
|
896
|
-
let label = step.message || "Processing";
|
|
897
|
-
const started = label.match(/^(.+?)\s+started$/i);
|
|
898
|
-
const progress = label.match(/^(.+?)\s+in progress$/i);
|
|
899
|
-
if (started) label = started[1];
|
|
900
|
-
else if (progress) label = progress[1];
|
|
901
|
-
parts.push(`**${label}**`);
|
|
902
|
-
if (step.thinkingText) parts.push(step.thinkingText);
|
|
903
|
-
break;
|
|
904
|
-
}
|
|
905
|
-
case "INTENT_PROGRESS": {
|
|
906
|
-
if (step.thinkingText) parts.push(step.thinkingText);
|
|
907
|
-
else if (step.message) parts.push(step.message);
|
|
908
|
-
break;
|
|
909
|
-
}
|
|
910
|
-
case "AGGREGATOR_THINKING":
|
|
911
|
-
parts.push("**Finalizing**");
|
|
912
|
-
if (step.message) parts.push(step.message);
|
|
913
|
-
break;
|
|
914
|
-
case "USER_ACTION_REQUIRED":
|
|
915
|
-
parts.push("**Verification Required**");
|
|
916
|
-
if (step.message) parts.push(step.message);
|
|
917
|
-
break;
|
|
918
|
-
case "USER_ACTION_SUCCESS":
|
|
919
|
-
parts.push(`\u2713 ${step.message || "Verification successful"}`);
|
|
920
|
-
break;
|
|
921
|
-
case "USER_ACTION_REJECTED":
|
|
922
|
-
parts.push(`\u2717 ${step.message || "Verification rejected"}`);
|
|
923
|
-
break;
|
|
924
|
-
case "USER_ACTION_EXPIRED":
|
|
925
|
-
parts.push(`\u2717 ${step.message || "Verification expired"}`);
|
|
926
|
-
break;
|
|
927
|
-
case "USER_ACTION_FAILED":
|
|
928
|
-
parts.push(`\u2717 ${step.message || "Verification failed"}`);
|
|
929
|
-
break;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
return parts.length > 0 ? parts.join("\n") : allThinkingText;
|
|
933
|
-
}
|
|
934
|
-
function createCancelledMessageUpdate(steps, currentMessage) {
|
|
935
|
-
const updatedSteps = steps.map((step) => {
|
|
936
|
-
if (step.status === "in_progress") {
|
|
937
|
-
return { ...step, status: "pending" };
|
|
938
|
-
}
|
|
939
|
-
return step;
|
|
940
|
-
});
|
|
941
|
-
return {
|
|
942
|
-
isStreaming: false,
|
|
943
|
-
isCancelled: true,
|
|
944
|
-
steps: updatedSteps,
|
|
945
|
-
currentExecutingStepId: void 0,
|
|
946
|
-
// Preserve currentMessage so UI can show it with X icon
|
|
947
|
-
currentMessage: currentMessage || "Thinking..."
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
|
|
951
697
|
// src/utils/requestBuilder.ts
|
|
952
698
|
var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
|
|
953
699
|
function buildRequestBody(config, userMessage, sessionId, options) {
|
|
@@ -1021,111 +767,6 @@ function buildRequestHeaders(config) {
|
|
|
1021
767
|
return headers;
|
|
1022
768
|
}
|
|
1023
769
|
|
|
1024
|
-
// src/utils/userActionClient.ts
|
|
1025
|
-
async function sendUserActionRequest(config, userActionId, action, data) {
|
|
1026
|
-
const url = buildUserActionUrl(config, userActionId, action);
|
|
1027
|
-
const baseHeaders = buildRequestHeaders(config);
|
|
1028
|
-
const hasBody = data !== void 0;
|
|
1029
|
-
const headers = hasBody ? { "Content-Type": "application/json", ...baseHeaders } : baseHeaders;
|
|
1030
|
-
const response = await fetch(url, {
|
|
1031
|
-
method: "POST",
|
|
1032
|
-
headers,
|
|
1033
|
-
body: hasBody ? JSON.stringify(data) : void 0
|
|
1034
|
-
});
|
|
1035
|
-
if (!response.ok) {
|
|
1036
|
-
const errorText = await response.text();
|
|
1037
|
-
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1038
|
-
}
|
|
1039
|
-
return await response.json();
|
|
1040
|
-
}
|
|
1041
|
-
async function submitUserAction(config, userActionId, data) {
|
|
1042
|
-
return sendUserActionRequest(config, userActionId, "submit", data);
|
|
1043
|
-
}
|
|
1044
|
-
async function cancelUserAction(config, userActionId) {
|
|
1045
|
-
return sendUserActionRequest(config, userActionId, "cancel");
|
|
1046
|
-
}
|
|
1047
|
-
async function resendUserAction(config, userActionId) {
|
|
1048
|
-
return sendUserActionRequest(config, userActionId, "resend");
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
// src/utils/chatStore.ts
|
|
1052
|
-
var memoryStore = /* @__PURE__ */ new Map();
|
|
1053
|
-
var chatStore = {
|
|
1054
|
-
get(key) {
|
|
1055
|
-
return memoryStore.get(key) ?? [];
|
|
1056
|
-
},
|
|
1057
|
-
set(key, messages) {
|
|
1058
|
-
memoryStore.set(key, messages);
|
|
1059
|
-
},
|
|
1060
|
-
delete(key) {
|
|
1061
|
-
memoryStore.delete(key);
|
|
1062
|
-
}
|
|
1063
|
-
};
|
|
1064
|
-
|
|
1065
|
-
// src/utils/activeStreamStore.ts
|
|
1066
|
-
var streams = /* @__PURE__ */ new Map();
|
|
1067
|
-
var activeStreamStore = {
|
|
1068
|
-
has(key) {
|
|
1069
|
-
return streams.has(key);
|
|
1070
|
-
},
|
|
1071
|
-
get(key) {
|
|
1072
|
-
const entry = streams.get(key);
|
|
1073
|
-
if (!entry) return null;
|
|
1074
|
-
return { messages: entry.messages, isWaiting: entry.isWaiting };
|
|
1075
|
-
},
|
|
1076
|
-
// Called before startStream — registers the controller and initial messages
|
|
1077
|
-
start(key, abortController, initialMessages) {
|
|
1078
|
-
const existing = streams.get(key);
|
|
1079
|
-
streams.set(key, {
|
|
1080
|
-
messages: initialMessages,
|
|
1081
|
-
isWaiting: true,
|
|
1082
|
-
abortController,
|
|
1083
|
-
listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
|
|
1084
|
-
});
|
|
1085
|
-
},
|
|
1086
|
-
// Called by the stream on every event — applies the same updater pattern React uses
|
|
1087
|
-
applyMessages(key, updater) {
|
|
1088
|
-
const entry = streams.get(key);
|
|
1089
|
-
if (!entry) return;
|
|
1090
|
-
const next = typeof updater === "function" ? updater(entry.messages) : updater;
|
|
1091
|
-
entry.messages = next;
|
|
1092
|
-
entry.listeners.forEach((l) => l(next, entry.isWaiting));
|
|
1093
|
-
},
|
|
1094
|
-
setWaiting(key, waiting) {
|
|
1095
|
-
const entry = streams.get(key);
|
|
1096
|
-
if (!entry) return;
|
|
1097
|
-
entry.isWaiting = waiting;
|
|
1098
|
-
entry.listeners.forEach((l) => l(entry.messages, waiting));
|
|
1099
|
-
},
|
|
1100
|
-
// Called when stream completes — persists to chatStore and cleans up
|
|
1101
|
-
complete(key) {
|
|
1102
|
-
const entry = streams.get(key);
|
|
1103
|
-
if (!entry) return;
|
|
1104
|
-
entry.isWaiting = false;
|
|
1105
|
-
entry.listeners.forEach((l) => l(entry.messages, false));
|
|
1106
|
-
const toSave = entry.messages.filter((m) => !m.isStreaming);
|
|
1107
|
-
if (toSave.length > 0) chatStore.set(key, toSave);
|
|
1108
|
-
streams.delete(key);
|
|
1109
|
-
},
|
|
1110
|
-
// Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
|
|
1111
|
-
subscribe(key, listener) {
|
|
1112
|
-
const entry = streams.get(key);
|
|
1113
|
-
if (!entry) return () => {
|
|
1114
|
-
};
|
|
1115
|
-
entry.listeners.add(listener);
|
|
1116
|
-
return () => {
|
|
1117
|
-
streams.get(key)?.listeners.delete(listener);
|
|
1118
|
-
};
|
|
1119
|
-
},
|
|
1120
|
-
// Explicit user cancel — aborts the controller and removes the entry
|
|
1121
|
-
abort(key) {
|
|
1122
|
-
const entry = streams.get(key);
|
|
1123
|
-
if (!entry) return;
|
|
1124
|
-
entry.abortController.abort();
|
|
1125
|
-
streams.delete(key);
|
|
1126
|
-
}
|
|
1127
|
-
};
|
|
1128
|
-
|
|
1129
770
|
// src/utils/ragImageResolver.ts
|
|
1130
771
|
var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
|
|
1131
772
|
function hasRagImages(content) {
|
|
@@ -1213,15 +854,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1213
854
|
onEvent: (event) => {
|
|
1214
855
|
if (abortController.signal.aborted) return;
|
|
1215
856
|
processStreamEventV2(event, state);
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
eventType,
|
|
1222
|
-
event.message?.trim() || event.errorMessage?.trim() || getEventMessage(event)
|
|
1223
|
-
);
|
|
1224
|
-
callbacksRef.current.onUserActionEvent?.(eventType, msg);
|
|
857
|
+
if (state.lastUserAction) {
|
|
858
|
+
callbacksRef.current.onUserActionRequired?.(state.lastUserAction);
|
|
859
|
+
}
|
|
860
|
+
if (state.lastNotification) {
|
|
861
|
+
callbacksRef.current.onUserNotification?.(state.lastNotification);
|
|
1225
862
|
}
|
|
1226
863
|
const activeStep = state.steps.find((s) => s.id === state.currentExecutingStepId);
|
|
1227
864
|
const lastInProgressStep = [...state.steps].reverse().find((s) => s.status === "in_progress");
|
|
@@ -1244,7 +881,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1244
881
|
streamProgress: "error",
|
|
1245
882
|
isError: true,
|
|
1246
883
|
errorDetails: state.errorMessage,
|
|
1247
|
-
formattedThinkingText: state.formattedThinkingText || void 0,
|
|
1248
884
|
steps: [...state.steps],
|
|
1249
885
|
currentExecutingStepId: void 0,
|
|
1250
886
|
executionId: state.executionId,
|
|
@@ -1257,12 +893,10 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1257
893
|
currentMessage,
|
|
1258
894
|
streamProgress: "processing",
|
|
1259
895
|
isError: false,
|
|
1260
|
-
formattedThinkingText: state.formattedThinkingText || void 0,
|
|
1261
896
|
steps: [...state.steps],
|
|
1262
897
|
currentExecutingStepId: state.currentExecutingStepId,
|
|
1263
898
|
executionId: state.executionId,
|
|
1264
899
|
sessionId: state.sessionId,
|
|
1265
|
-
userActionResult: state.userActionResult,
|
|
1266
900
|
isCancelled: false
|
|
1267
901
|
});
|
|
1268
902
|
}
|
|
@@ -1273,14 +907,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1273
907
|
if (error.name !== "AbortError") {
|
|
1274
908
|
callbacksRef.current.onError?.(error);
|
|
1275
909
|
}
|
|
1276
|
-
if (state.userActionPending) {
|
|
1277
|
-
state.userActionPending = false;
|
|
1278
|
-
state.userActionRequest = void 0;
|
|
1279
|
-
callbacksRef.current.onUserActionEvent?.(
|
|
1280
|
-
"USER_ACTION_FAILED",
|
|
1281
|
-
"Connection lost. Please try again."
|
|
1282
|
-
);
|
|
1283
|
-
}
|
|
1284
910
|
const isAborted = error.name === "AbortError";
|
|
1285
911
|
setMessages(
|
|
1286
912
|
(prev) => prev.map(
|
|
@@ -1293,7 +919,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1293
919
|
errorDetails: isAborted ? void 0 : error.message,
|
|
1294
920
|
content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
|
|
1295
921
|
currentMessage: isAborted ? "Thinking..." : void 0,
|
|
1296
|
-
formattedThinkingText: state.formattedThinkingText || void 0,
|
|
1297
922
|
steps: [...state.steps].map((step) => {
|
|
1298
923
|
if (step.status === "in_progress" && isAborted) {
|
|
1299
924
|
return { ...step, status: "pending" };
|
|
@@ -1309,14 +934,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1309
934
|
setIsWaitingForResponse(false);
|
|
1310
935
|
callbacksRef.current.onStatusMessage?.(null);
|
|
1311
936
|
callbacksRef.current.onStepsUpdate?.([]);
|
|
1312
|
-
if (state.userActionPending) {
|
|
1313
|
-
state.userActionPending = false;
|
|
1314
|
-
state.userActionRequest = void 0;
|
|
1315
|
-
callbacksRef.current.onUserActionEvent?.(
|
|
1316
|
-
"USER_ACTION_FAILED",
|
|
1317
|
-
"Verification could not be completed."
|
|
1318
|
-
);
|
|
1319
|
-
}
|
|
1320
937
|
if (state.sessionId && state.sessionId !== sessionId) {
|
|
1321
938
|
callbacksRef.current.onSessionIdChange?.(state.sessionId);
|
|
1322
939
|
}
|
|
@@ -1340,8 +957,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1340
957
|
steps: state.hasError ? [] : [...state.steps],
|
|
1341
958
|
isCancelled: false,
|
|
1342
959
|
currentExecutingStepId: void 0,
|
|
1343
|
-
userActionResult: state.userActionResult,
|
|
1344
|
-
formattedThinkingText: state.hasError ? void 0 : state.formattedThinkingText || void 0,
|
|
1345
960
|
isResolvingImages: needsImageResolve,
|
|
1346
961
|
totalElapsedMs: state.hasError ? void 0 : state.totalElapsedMs
|
|
1347
962
|
};
|
|
@@ -1383,14 +998,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1383
998
|
if (error.name !== "AbortError") {
|
|
1384
999
|
callbacksRef.current.onError?.(error);
|
|
1385
1000
|
}
|
|
1386
|
-
if (state.userActionPending) {
|
|
1387
|
-
state.userActionPending = false;
|
|
1388
|
-
state.userActionRequest = void 0;
|
|
1389
|
-
callbacksRef.current.onUserActionEvent?.(
|
|
1390
|
-
"USER_ACTION_FAILED",
|
|
1391
|
-
"Connection lost. Please try again."
|
|
1392
|
-
);
|
|
1393
|
-
}
|
|
1394
1001
|
const isAborted = error.name === "AbortError";
|
|
1395
1002
|
setMessages(
|
|
1396
1003
|
(prev) => prev.map(
|
|
@@ -1402,7 +1009,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1402
1009
|
isCancelled: isAborted,
|
|
1403
1010
|
errorDetails: isAborted ? void 0 : error.message,
|
|
1404
1011
|
content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
|
|
1405
|
-
formattedThinkingText: state.formattedThinkingText || void 0,
|
|
1406
1012
|
steps: [...state.steps].map((step) => {
|
|
1407
1013
|
if (step.status === "in_progress" && isAborted) {
|
|
1408
1014
|
return { ...step, status: "pending" };
|
|
@@ -1428,9 +1034,79 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1428
1034
|
};
|
|
1429
1035
|
}
|
|
1430
1036
|
|
|
1037
|
+
// src/utils/messageStateManager.ts
|
|
1038
|
+
function createCancelledMessageUpdate(steps, currentMessage) {
|
|
1039
|
+
const updatedSteps = steps.map((step) => {
|
|
1040
|
+
if (step.status === "in_progress") {
|
|
1041
|
+
return { ...step, status: "pending" };
|
|
1042
|
+
}
|
|
1043
|
+
return step;
|
|
1044
|
+
});
|
|
1045
|
+
return {
|
|
1046
|
+
isStreaming: false,
|
|
1047
|
+
isCancelled: true,
|
|
1048
|
+
steps: updatedSteps,
|
|
1049
|
+
currentExecutingStepId: void 0,
|
|
1050
|
+
currentMessage: currentMessage || "Thinking..."
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// src/utils/userActionClient.ts
|
|
1055
|
+
var UserActionStaleError = class extends Error {
|
|
1056
|
+
constructor(userActionId, message = "User action is no longer actionable") {
|
|
1057
|
+
super(message);
|
|
1058
|
+
__publicField(this, "userActionId");
|
|
1059
|
+
this.name = "UserActionStaleError";
|
|
1060
|
+
this.userActionId = userActionId;
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
async function sendUserActionRequest(config, userActionId, action, data) {
|
|
1064
|
+
const url = buildUserActionUrl(config, userActionId, action);
|
|
1065
|
+
const baseHeaders = buildRequestHeaders(config);
|
|
1066
|
+
const hasBody = data !== void 0;
|
|
1067
|
+
const headers = hasBody ? { "Content-Type": "application/json", ...baseHeaders } : baseHeaders;
|
|
1068
|
+
const response = await fetch(url, {
|
|
1069
|
+
method: "POST",
|
|
1070
|
+
headers,
|
|
1071
|
+
body: hasBody ? JSON.stringify(data) : void 0
|
|
1072
|
+
});
|
|
1073
|
+
if (response.status === 404) {
|
|
1074
|
+
throw new UserActionStaleError(userActionId);
|
|
1075
|
+
}
|
|
1076
|
+
if (!response.ok) {
|
|
1077
|
+
const errorText = await response.text();
|
|
1078
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1079
|
+
}
|
|
1080
|
+
return await response.json();
|
|
1081
|
+
}
|
|
1082
|
+
async function submitUserAction(config, userActionId, content) {
|
|
1083
|
+
return sendUserActionRequest(config, userActionId, "submit", content ?? {});
|
|
1084
|
+
}
|
|
1085
|
+
async function cancelUserAction(config, userActionId) {
|
|
1086
|
+
return sendUserActionRequest(config, userActionId, "cancel");
|
|
1087
|
+
}
|
|
1088
|
+
async function resendUserAction(config, userActionId) {
|
|
1089
|
+
return sendUserActionRequest(config, userActionId, "resend");
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1431
1092
|
// src/hooks/useChatV2.ts
|
|
1093
|
+
var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
|
|
1094
|
+
function upsertPrompt(prompts, req) {
|
|
1095
|
+
const active = { ...req, status: "pending" };
|
|
1096
|
+
const idx = prompts.findIndex(
|
|
1097
|
+
(p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
|
|
1098
|
+
);
|
|
1099
|
+
if (idx >= 0) {
|
|
1100
|
+
const next = prompts.slice();
|
|
1101
|
+
next[idx] = active;
|
|
1102
|
+
return next;
|
|
1103
|
+
}
|
|
1104
|
+
return [...prompts, active];
|
|
1105
|
+
}
|
|
1432
1106
|
function getStoredOrInitialMessages(config) {
|
|
1433
1107
|
if (!config.userId) return config.initialMessages ?? [];
|
|
1108
|
+
const activeStream = activeStreamStore.get(config.userId);
|
|
1109
|
+
if (activeStream) return activeStream.messages;
|
|
1434
1110
|
const stored = chatStore.get(config.userId);
|
|
1435
1111
|
if (stored.length > 0) return stored;
|
|
1436
1112
|
if (config.initialMessages?.length) {
|
|
@@ -1444,11 +1120,16 @@ function getSessionIdFromMessages(messages) {
|
|
|
1444
1120
|
}
|
|
1445
1121
|
function useChatV2(config, callbacks = {}) {
|
|
1446
1122
|
const [messages, setMessages] = useState(() => getStoredOrInitialMessages(config));
|
|
1447
|
-
const [isWaitingForResponse, setIsWaitingForResponse] = useState(
|
|
1123
|
+
const [isWaitingForResponse, setIsWaitingForResponse] = useState(() => {
|
|
1124
|
+
if (!config.userId) return false;
|
|
1125
|
+
return activeStreamStore.get(config.userId)?.isWaiting ?? false;
|
|
1126
|
+
});
|
|
1448
1127
|
const sessionIdRef = useRef(
|
|
1449
1128
|
getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
|
|
1450
1129
|
);
|
|
1451
1130
|
const prevUserIdRef = useRef(config.userId);
|
|
1131
|
+
const streamUserIdRef = useRef(void 0);
|
|
1132
|
+
const subscriptionPrevUserIdRef = useRef(config.userId);
|
|
1452
1133
|
const callbacksRef = useRef(callbacks);
|
|
1453
1134
|
callbacksRef.current = callbacks;
|
|
1454
1135
|
const configRef = useRef(config);
|
|
@@ -1457,33 +1138,35 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1457
1138
|
messagesRef.current = messages;
|
|
1458
1139
|
const storeAwareSetMessages = useCallback(
|
|
1459
1140
|
(updater) => {
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1462
|
-
|
|
1141
|
+
const streamUserId = streamUserIdRef.current;
|
|
1142
|
+
const currentUserId = configRef.current.userId;
|
|
1143
|
+
const storeKey = streamUserId ?? currentUserId;
|
|
1144
|
+
if (storeKey && activeStreamStore.has(storeKey)) {
|
|
1145
|
+
activeStreamStore.applyMessages(storeKey, updater);
|
|
1146
|
+
}
|
|
1147
|
+
if (!streamUserId || streamUserId === currentUserId) {
|
|
1148
|
+
setMessages(updater);
|
|
1463
1149
|
}
|
|
1464
|
-
setMessages(updater);
|
|
1465
1150
|
},
|
|
1466
1151
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1467
1152
|
[]
|
|
1468
1153
|
);
|
|
1469
1154
|
const storeAwareSetIsWaiting = useCallback(
|
|
1470
1155
|
(waiting) => {
|
|
1471
|
-
const
|
|
1472
|
-
|
|
1473
|
-
|
|
1156
|
+
const streamUserId = streamUserIdRef.current;
|
|
1157
|
+
const currentUserId = configRef.current.userId;
|
|
1158
|
+
const storeKey = streamUserId ?? currentUserId;
|
|
1159
|
+
if (storeKey && activeStreamStore.has(storeKey)) {
|
|
1160
|
+
activeStreamStore.setWaiting(storeKey, waiting);
|
|
1161
|
+
}
|
|
1162
|
+
if (!streamUserId || streamUserId === currentUserId) {
|
|
1163
|
+
setIsWaitingForResponse(waiting);
|
|
1474
1164
|
}
|
|
1475
|
-
setIsWaitingForResponse(waiting);
|
|
1476
1165
|
},
|
|
1477
1166
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1478
1167
|
[]
|
|
1479
1168
|
);
|
|
1480
|
-
const [userActionState, setUserActionState] = useState(
|
|
1481
|
-
request: null,
|
|
1482
|
-
result: null,
|
|
1483
|
-
clearOtpTrigger: 0
|
|
1484
|
-
});
|
|
1485
|
-
const userActionStateRef = useRef(userActionState);
|
|
1486
|
-
userActionStateRef.current = userActionState;
|
|
1169
|
+
const [userActionState, setUserActionState] = useState(EMPTY_USER_ACTION_STATE);
|
|
1487
1170
|
const wrappedCallbacks = useMemo(() => ({
|
|
1488
1171
|
...callbacksRef.current,
|
|
1489
1172
|
onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
|
|
@@ -1493,26 +1176,17 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1493
1176
|
onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
|
|
1494
1177
|
onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
|
|
1495
1178
|
onUserActionRequired: (request) => {
|
|
1496
|
-
setUserActionState((prev) => ({
|
|
1179
|
+
setUserActionState((prev) => ({
|
|
1180
|
+
...prev,
|
|
1181
|
+
prompts: upsertPrompt(prev.prompts, request)
|
|
1182
|
+
}));
|
|
1497
1183
|
callbacksRef.current.onUserActionRequired?.(request);
|
|
1498
1184
|
},
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
case "USER_ACTION_REJECTED":
|
|
1505
|
-
setUserActionState((prev) => ({ ...prev, request: null, result: "rejected" }));
|
|
1506
|
-
break;
|
|
1507
|
-
case "USER_ACTION_EXPIRED":
|
|
1508
|
-
case "USER_ACTION_FAILED":
|
|
1509
|
-
setUserActionState((prev) => ({ ...prev, request: null }));
|
|
1510
|
-
break;
|
|
1511
|
-
case "USER_ACTION_INVALID":
|
|
1512
|
-
setUserActionState((prev) => ({ ...prev, clearOtpTrigger: prev.clearOtpTrigger + 1 }));
|
|
1513
|
-
break;
|
|
1514
|
-
}
|
|
1515
|
-
callbacksRef.current.onUserActionEvent?.(eventType, message);
|
|
1185
|
+
onUserNotification: (notification) => {
|
|
1186
|
+
setUserActionState(
|
|
1187
|
+
(prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
|
|
1188
|
+
);
|
|
1189
|
+
callbacksRef.current.onUserNotification?.(notification);
|
|
1516
1190
|
}
|
|
1517
1191
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1518
1192
|
}), []);
|
|
@@ -1560,6 +1234,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1560
1234
|
const abortController = new AbortController();
|
|
1561
1235
|
const { userId } = configRef.current;
|
|
1562
1236
|
if (userId) {
|
|
1237
|
+
streamUserIdRef.current = userId;
|
|
1563
1238
|
const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
|
|
1564
1239
|
activeStreamStore.start(userId, abortController, initialMessages);
|
|
1565
1240
|
}
|
|
@@ -1570,9 +1245,11 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1570
1245
|
abortController,
|
|
1571
1246
|
options
|
|
1572
1247
|
);
|
|
1573
|
-
|
|
1574
|
-
|
|
1248
|
+
const finalStreamUserId = streamUserIdRef.current ?? userId;
|
|
1249
|
+
if (finalStreamUserId) {
|
|
1250
|
+
activeStreamStore.complete(finalStreamUserId);
|
|
1575
1251
|
}
|
|
1252
|
+
streamUserIdRef.current = void 0;
|
|
1576
1253
|
if (!abortController.signal.aborted && newSessionId && newSessionId !== sessionIdRef.current) {
|
|
1577
1254
|
sessionIdRef.current = newSessionId;
|
|
1578
1255
|
}
|
|
@@ -1589,12 +1266,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1589
1266
|
setMessages((prev) => [...msgs, ...prev]);
|
|
1590
1267
|
}, []);
|
|
1591
1268
|
const cancelStream = useCallback(() => {
|
|
1592
|
-
|
|
1593
|
-
|
|
1269
|
+
const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
|
|
1270
|
+
if (streamUserId) {
|
|
1271
|
+
activeStreamStore.abort(streamUserId);
|
|
1594
1272
|
}
|
|
1273
|
+
streamUserIdRef.current = void 0;
|
|
1595
1274
|
cancelStreamManager();
|
|
1596
1275
|
setIsWaitingForResponse(false);
|
|
1597
|
-
setUserActionState((prev) => ({ ...prev,
|
|
1276
|
+
setUserActionState((prev) => ({ ...prev, prompts: [] }));
|
|
1598
1277
|
setMessages(
|
|
1599
1278
|
(prev) => prev.map((msg) => {
|
|
1600
1279
|
if (msg.isStreaming) {
|
|
@@ -1611,15 +1290,19 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1611
1290
|
);
|
|
1612
1291
|
}, [cancelStreamManager]);
|
|
1613
1292
|
const resetSession = useCallback(() => {
|
|
1293
|
+
const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
|
|
1294
|
+
if (streamUserId) {
|
|
1295
|
+
activeStreamStore.abort(streamUserId);
|
|
1296
|
+
}
|
|
1614
1297
|
if (configRef.current.userId) {
|
|
1615
|
-
activeStreamStore.abort(configRef.current.userId);
|
|
1616
1298
|
chatStore.delete(configRef.current.userId);
|
|
1617
1299
|
}
|
|
1300
|
+
streamUserIdRef.current = void 0;
|
|
1618
1301
|
setMessages([]);
|
|
1619
1302
|
sessionIdRef.current = void 0;
|
|
1620
1303
|
abortControllerRef.current?.abort();
|
|
1621
1304
|
setIsWaitingForResponse(false);
|
|
1622
|
-
setUserActionState(
|
|
1305
|
+
setUserActionState(EMPTY_USER_ACTION_STATE);
|
|
1623
1306
|
}, []);
|
|
1624
1307
|
const getSessionId = useCallback(() => {
|
|
1625
1308
|
return sessionIdRef.current;
|
|
@@ -1627,59 +1310,91 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1627
1310
|
const getMessages = useCallback(() => {
|
|
1628
1311
|
return messages;
|
|
1629
1312
|
}, [messages]);
|
|
1630
|
-
const
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1313
|
+
const setPromptStatus = useCallback(
|
|
1314
|
+
(userActionId, status) => {
|
|
1315
|
+
setUserActionState((prev) => ({
|
|
1316
|
+
...prev,
|
|
1317
|
+
prompts: prev.prompts.map(
|
|
1318
|
+
(p) => p.userActionId === userActionId ? { ...p, status } : p
|
|
1319
|
+
)
|
|
1320
|
+
}));
|
|
1321
|
+
},
|
|
1322
|
+
[]
|
|
1323
|
+
);
|
|
1324
|
+
const removePrompt = useCallback((userActionId) => {
|
|
1325
|
+
setUserActionState((prev) => ({
|
|
1326
|
+
...prev,
|
|
1327
|
+
prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
|
|
1328
|
+
}));
|
|
1329
|
+
}, []);
|
|
1330
|
+
const submitUserAction2 = useCallback(
|
|
1331
|
+
async (userActionId, content) => {
|
|
1332
|
+
setPromptStatus(userActionId, "submitting");
|
|
1634
1333
|
try {
|
|
1635
|
-
await submitUserAction(configRef.current,
|
|
1334
|
+
await submitUserAction(configRef.current, userActionId, content);
|
|
1335
|
+
removePrompt(userActionId);
|
|
1636
1336
|
} catch (error) {
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
}
|
|
1337
|
+
if (error instanceof UserActionStaleError) {
|
|
1338
|
+
setPromptStatus(userActionId, "stale");
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
setPromptStatus(userActionId, "pending");
|
|
1641
1342
|
callbacksRef.current.onError?.(error);
|
|
1642
1343
|
throw error;
|
|
1643
1344
|
}
|
|
1644
1345
|
},
|
|
1645
|
-
[]
|
|
1346
|
+
[removePrompt, setPromptStatus]
|
|
1646
1347
|
);
|
|
1647
|
-
const
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
}
|
|
1348
|
+
const cancelUserAction2 = useCallback(
|
|
1349
|
+
async (userActionId) => {
|
|
1350
|
+
setPromptStatus(userActionId, "submitting");
|
|
1351
|
+
try {
|
|
1352
|
+
await cancelUserAction(configRef.current, userActionId);
|
|
1353
|
+
removePrompt(userActionId);
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
if (error instanceof UserActionStaleError) {
|
|
1356
|
+
removePrompt(userActionId);
|
|
1357
|
+
return;
|
|
1658
1358
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1359
|
+
setPromptStatus(userActionId, "pending");
|
|
1360
|
+
callbacksRef.current.onError?.(error);
|
|
1361
|
+
throw error;
|
|
1362
|
+
}
|
|
1363
|
+
},
|
|
1364
|
+
[removePrompt, setPromptStatus]
|
|
1365
|
+
);
|
|
1366
|
+
const resendUserAction2 = useCallback(
|
|
1367
|
+
async (userActionId) => {
|
|
1368
|
+
setPromptStatus(userActionId, "submitting");
|
|
1369
|
+
try {
|
|
1370
|
+
await resendUserAction(configRef.current, userActionId);
|
|
1371
|
+
setPromptStatus(userActionId, "pending");
|
|
1372
|
+
} catch (error) {
|
|
1373
|
+
if (error instanceof UserActionStaleError) {
|
|
1374
|
+
setPromptStatus(userActionId, "stale");
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
setPromptStatus(userActionId, "pending");
|
|
1378
|
+
callbacksRef.current.onError?.(error);
|
|
1379
|
+
throw error;
|
|
1380
|
+
}
|
|
1381
|
+
},
|
|
1382
|
+
[setPromptStatus]
|
|
1383
|
+
);
|
|
1384
|
+
const dismissNotification = useCallback((id) => {
|
|
1385
|
+
setUserActionState((prev) => ({
|
|
1386
|
+
...prev,
|
|
1387
|
+
notifications: prev.notifications.filter((n) => n.id !== id)
|
|
1388
|
+
}));
|
|
1679
1389
|
}, []);
|
|
1680
1390
|
useEffect(() => {
|
|
1391
|
+
const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
|
|
1392
|
+
subscriptionPrevUserIdRef.current = config.userId;
|
|
1681
1393
|
const { userId } = config;
|
|
1682
1394
|
if (!userId) return;
|
|
1395
|
+
if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
|
|
1396
|
+
streamUserIdRef.current = userId;
|
|
1397
|
+
}
|
|
1683
1398
|
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
|
|
1684
1399
|
setMessages(msgs);
|
|
1685
1400
|
setIsWaitingForResponse(isWaiting);
|
|
@@ -1693,6 +1408,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1693
1408
|
}, [config.userId]);
|
|
1694
1409
|
useEffect(() => {
|
|
1695
1410
|
if (!config.userId) return;
|
|
1411
|
+
if (prevUserIdRef.current !== config.userId) return;
|
|
1696
1412
|
const toSave = messages.filter((m) => !m.isStreaming);
|
|
1697
1413
|
if (toSave.length > 0) {
|
|
1698
1414
|
chatStore.set(config.userId, toSave);
|
|
@@ -1714,7 +1430,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1714
1430
|
setMessages([]);
|
|
1715
1431
|
sessionIdRef.current = void 0;
|
|
1716
1432
|
setIsWaitingForResponse(false);
|
|
1717
|
-
setUserActionState(
|
|
1433
|
+
setUserActionState(EMPTY_USER_ACTION_STATE);
|
|
1718
1434
|
} else if (config.userId) {
|
|
1719
1435
|
const active = activeStreamStore.get(config.userId);
|
|
1720
1436
|
if (active) {
|
|
@@ -1741,9 +1457,10 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1741
1457
|
isWaitingForResponse,
|
|
1742
1458
|
sessionId: sessionIdRef.current,
|
|
1743
1459
|
userActionState,
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1460
|
+
submitUserAction: submitUserAction2,
|
|
1461
|
+
cancelUserAction: cancelUserAction2,
|
|
1462
|
+
resendUserAction: resendUserAction2,
|
|
1463
|
+
dismissNotification
|
|
1747
1464
|
};
|
|
1748
1465
|
}
|
|
1749
1466
|
function getSpeechRecognition() {
|
|
@@ -1962,6 +1679,129 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1962
1679
|
};
|
|
1963
1680
|
}
|
|
1964
1681
|
|
|
1965
|
-
|
|
1682
|
+
// src/utils/jsonSchemaForm.ts
|
|
1683
|
+
function classifyField(field) {
|
|
1684
|
+
if (!field) return "text";
|
|
1685
|
+
if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
|
|
1686
|
+
switch (field.type) {
|
|
1687
|
+
case "boolean":
|
|
1688
|
+
return "boolean";
|
|
1689
|
+
case "integer":
|
|
1690
|
+
return "integer";
|
|
1691
|
+
case "number":
|
|
1692
|
+
return "decimal";
|
|
1693
|
+
case "string":
|
|
1694
|
+
return "text";
|
|
1695
|
+
default:
|
|
1696
|
+
return "text";
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
function isNestedOrUnsupported(field) {
|
|
1700
|
+
if (!field) return false;
|
|
1701
|
+
if (field.type === "object" || field.type === "array") return true;
|
|
1702
|
+
if ("properties" in field && field.properties != null) return true;
|
|
1703
|
+
if ("items" in field && field.items != null) return true;
|
|
1704
|
+
return false;
|
|
1705
|
+
}
|
|
1706
|
+
function getOptions(field) {
|
|
1707
|
+
if (!field || !Array.isArray(field.oneOf)) return [];
|
|
1708
|
+
return field.oneOf.filter(
|
|
1709
|
+
(o) => !!o && typeof o === "object" && typeof o.const === "string"
|
|
1710
|
+
);
|
|
1711
|
+
}
|
|
1712
|
+
function isRequired(schema, key) {
|
|
1713
|
+
return Array.isArray(schema?.required) && schema.required.includes(key);
|
|
1714
|
+
}
|
|
1715
|
+
function coerceValue(field, raw) {
|
|
1716
|
+
const widget = classifyField(field);
|
|
1717
|
+
if (widget === "boolean") {
|
|
1718
|
+
if (typeof raw === "boolean") return raw;
|
|
1719
|
+
if (raw === "true") return true;
|
|
1720
|
+
if (raw === "false") return false;
|
|
1721
|
+
return Boolean(raw);
|
|
1722
|
+
}
|
|
1723
|
+
if (widget === "integer" || widget === "decimal") {
|
|
1724
|
+
if (raw === "" || raw == null) return void 0;
|
|
1725
|
+
const num = typeof raw === "number" ? raw : Number(String(raw).trim());
|
|
1726
|
+
if (Number.isNaN(num)) return void 0;
|
|
1727
|
+
return widget === "integer" ? Math.trunc(num) : num;
|
|
1728
|
+
}
|
|
1729
|
+
if (raw == null) return void 0;
|
|
1730
|
+
const str = String(raw);
|
|
1731
|
+
return str === "" ? void 0 : str;
|
|
1732
|
+
}
|
|
1733
|
+
function defaultValueFor(field) {
|
|
1734
|
+
if (!field || field.default === void 0) {
|
|
1735
|
+
return classifyField(field) === "boolean" ? false : "";
|
|
1736
|
+
}
|
|
1737
|
+
return field.default;
|
|
1738
|
+
}
|
|
1739
|
+
function validateField(field, value, required) {
|
|
1740
|
+
const widget = classifyField(field);
|
|
1741
|
+
const label = field?.title || "This field";
|
|
1742
|
+
const isEmpty = value === void 0 || value === null || typeof value === "string" && value.trim() === "";
|
|
1743
|
+
if (isEmpty) {
|
|
1744
|
+
if (required && widget !== "boolean") return `${label} is required.`;
|
|
1745
|
+
return null;
|
|
1746
|
+
}
|
|
1747
|
+
if (widget === "integer" || widget === "decimal") {
|
|
1748
|
+
const num = typeof value === "number" ? value : Number(value);
|
|
1749
|
+
if (Number.isNaN(num)) return `${label} must be a number.`;
|
|
1750
|
+
if (widget === "integer" && !Number.isInteger(num)) {
|
|
1751
|
+
return `${label} must be a whole number.`;
|
|
1752
|
+
}
|
|
1753
|
+
if (typeof field?.minimum === "number" && num < field.minimum) {
|
|
1754
|
+
return `${label} must be at least ${field.minimum}.`;
|
|
1755
|
+
}
|
|
1756
|
+
if (typeof field?.maximum === "number" && num > field.maximum) {
|
|
1757
|
+
return `${label} must be at most ${field.maximum}.`;
|
|
1758
|
+
}
|
|
1759
|
+
return null;
|
|
1760
|
+
}
|
|
1761
|
+
if (widget === "select") {
|
|
1762
|
+
const allowed = getOptions(field).map((o) => o.const);
|
|
1763
|
+
if (allowed.length > 0 && !allowed.includes(String(value))) {
|
|
1764
|
+
return `${label} has an invalid selection.`;
|
|
1765
|
+
}
|
|
1766
|
+
return null;
|
|
1767
|
+
}
|
|
1768
|
+
const str = String(value);
|
|
1769
|
+
if (typeof field?.minLength === "number" && str.length < field.minLength) {
|
|
1770
|
+
return `${label} must be at least ${field.minLength} characters.`;
|
|
1771
|
+
}
|
|
1772
|
+
if (typeof field?.maxLength === "number" && str.length > field.maxLength) {
|
|
1773
|
+
return `${label} must be at most ${field.maxLength} characters.`;
|
|
1774
|
+
}
|
|
1775
|
+
return null;
|
|
1776
|
+
}
|
|
1777
|
+
function renderableFields(schema) {
|
|
1778
|
+
const props = schema?.properties;
|
|
1779
|
+
if (!props) return [];
|
|
1780
|
+
return Object.entries(props).filter(([, field]) => !isNestedOrUnsupported(field));
|
|
1781
|
+
}
|
|
1782
|
+
function validateForm(schema, values) {
|
|
1783
|
+
const errors = {};
|
|
1784
|
+
for (const [key, field] of renderableFields(schema)) {
|
|
1785
|
+
const coerced = coerceValue(field, values[key]);
|
|
1786
|
+
const err = validateField(field, coerced, isRequired(schema, key));
|
|
1787
|
+
if (err) errors[key] = err;
|
|
1788
|
+
}
|
|
1789
|
+
return errors;
|
|
1790
|
+
}
|
|
1791
|
+
function buildContent(schema, values) {
|
|
1792
|
+
const content = {};
|
|
1793
|
+
for (const [key, field] of renderableFields(schema)) {
|
|
1794
|
+
const coerced = coerceValue(field, values[key]);
|
|
1795
|
+
if (coerced !== void 0) content[key] = coerced;
|
|
1796
|
+
}
|
|
1797
|
+
return content;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
// src/index.ts
|
|
1801
|
+
function migrateActiveStream(oldUserId, newUserId) {
|
|
1802
|
+
activeStreamStore.rename(oldUserId, newUserId);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
export { UserActionStaleError, buildContent, cancelUserAction, classifyField, classifyUserActionKind, coerceValue, createInitialV2State, defaultValueFor, generateId, getOptions, isNestedOrUnsupported, isRequired, migrateActiveStream, processStreamEventV2, renderableFields, resendUserAction, streamWorkflowEvents, submitUserAction, useChatV2, useVoice, validateField, validateForm };
|
|
1966
1806
|
//# sourceMappingURL=index.mjs.map
|
|
1967
1807
|
//# sourceMappingURL=index.mjs.map
|