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