@paymanai/payman-ask-sdk 4.0.11 → 4.0.12

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.mjs CHANGED
@@ -1,11 +1,9 @@
1
- import { useVoice, useChatV2 } from '@paymanai/payman-typescript-ask-sdk';
2
- export { buildFormattedThinking, cancelUserAction, createInitialV2State, generateId, processStreamEventV2, resendUserAction, streamWorkflowEvents, submitUserAction, useChatV2, useVoice } from '@paymanai/payman-typescript-ask-sdk';
1
+ import { createContext, forwardRef, useCallback, useRef, useState, useImperativeHandle, useEffect, useMemo, useLayoutEffect, useContext } from 'react';
3
2
  import { AnimatePresence, motion } from 'framer-motion';
4
- import { createContext, forwardRef, useRef, useState, useCallback, useImperativeHandle, useEffect, useMemo, useLayoutEffect, useContext } from 'react';
5
3
  import { clsx } from 'clsx';
6
4
  import { twMerge } from 'tailwind-merge';
7
5
  import * as Sentry from '@sentry/react';
8
- import { ArrowDown, Pencil, X, RotateCcw, Telescope, Zap, Plus, ImagePlus, Paperclip, Mic, ArrowUp, Check, AlertCircle, Copy, WifiOff, ThumbsUp, ThumbsDown, Binoculars, ShieldCheck, Download, Loader2, ChevronDown, User, Clock, Sparkles, ImageOff, Eye, ChevronRight } from 'lucide-react';
6
+ import { ArrowDown, Pencil, X, RotateCcw, Telescope, Zap, Plus, ImagePlus, Paperclip, Mic, ArrowUp, Check, AlertCircle, Copy, WifiOff, ThumbsUp, ThumbsDown, Binoculars, Info, Download, Loader2, ChevronDown, User, Clock, Sparkles, ImageOff, Eye, ChevronRight, ShieldCheck } from 'lucide-react';
9
7
  import ReactMarkdown from 'react-markdown';
10
8
  import remarkGfm from 'remark-gfm';
11
9
  import { createPortal } from 'react-dom';
@@ -13,6 +11,2026 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
13
11
  import remarkBreaks from 'remark-breaks';
14
12
  import { DotLottieReact } from '@lottiefiles/dotlottie-react';
15
13
 
14
+ var __defProp = Object.defineProperty;
15
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
16
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value);
17
+ function generateId() {
18
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
19
+ const r = Math.random() * 16 | 0;
20
+ const v = c === "x" ? r : r & 3 | 8;
21
+ return v.toString(16);
22
+ });
23
+ }
24
+ function yieldAfterProgressEvent(event) {
25
+ const t = event.eventType;
26
+ if (t === "RUN_IN_PROGRESS" || t === "INTENT_PROGRESS" || t === "AGGREGATOR_THINKING_CONT" || t === "INTENT_THINKING_CONT" || t === "THINKING_DELTA") {
27
+ return new Promise((resolve) => setTimeout(resolve, 0));
28
+ }
29
+ return Promise.resolve();
30
+ }
31
+ function parseJSONBuffer(buffer) {
32
+ const events = [];
33
+ let braceCount = 0;
34
+ let startIndex = 0;
35
+ let inString = false;
36
+ let escapeNext = false;
37
+ let lastParsedIndex = -1;
38
+ for (let i = 0; i < buffer.length; i++) {
39
+ const char = buffer[i];
40
+ if (escapeNext) {
41
+ escapeNext = false;
42
+ continue;
43
+ }
44
+ if (char === "\\") {
45
+ escapeNext = true;
46
+ continue;
47
+ }
48
+ if (char === '"' && !escapeNext) {
49
+ inString = !inString;
50
+ continue;
51
+ }
52
+ if (inString) {
53
+ continue;
54
+ }
55
+ if (char === "{") {
56
+ if (braceCount === 0) {
57
+ startIndex = i;
58
+ }
59
+ braceCount++;
60
+ } else if (char === "}") {
61
+ braceCount--;
62
+ if (braceCount === 0) {
63
+ const jsonStr = buffer.substring(startIndex, i + 1);
64
+ try {
65
+ const parsed = JSON.parse(jsonStr);
66
+ const event = parsed;
67
+ events.push(event);
68
+ lastParsedIndex = i;
69
+ } catch (err) {
70
+ console.error("Failed to parse JSON event:", jsonStr, err);
71
+ }
72
+ }
73
+ }
74
+ }
75
+ const remaining = lastParsedIndex >= 0 ? buffer.substring(lastParsedIndex + 1) : buffer;
76
+ return { events, remaining };
77
+ }
78
+ async function streamWorkflowEvents(url, body, headers, options = {}) {
79
+ const { signal, onEvent, onError, onComplete } = options;
80
+ try {
81
+ const response = await fetch(url, {
82
+ method: "POST",
83
+ headers: {
84
+ "Content-Type": "application/json",
85
+ ...headers
86
+ },
87
+ body: JSON.stringify(body),
88
+ signal
89
+ });
90
+ if (!response.ok) {
91
+ const errorText = await response.text();
92
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
93
+ }
94
+ const reader = response.body?.getReader();
95
+ if (!reader) {
96
+ throw new Error("No response body");
97
+ }
98
+ const decoder = new TextDecoder();
99
+ let buffer = "";
100
+ while (true) {
101
+ const { done, value } = await reader.read();
102
+ if (done) {
103
+ if (buffer.trim()) {
104
+ const { events: events2 } = parseJSONBuffer(buffer);
105
+ for (const event of events2) {
106
+ onEvent?.(event);
107
+ await yieldAfterProgressEvent(event);
108
+ }
109
+ }
110
+ break;
111
+ }
112
+ buffer += decoder.decode(value, { stream: true });
113
+ const { events, remaining } = parseJSONBuffer(buffer);
114
+ for (const event of events) {
115
+ onEvent?.(event);
116
+ await yieldAfterProgressEvent(event);
117
+ }
118
+ buffer = remaining;
119
+ }
120
+ onComplete?.();
121
+ } catch (error) {
122
+ if (error instanceof Error && error.name === "AbortError") {
123
+ return;
124
+ }
125
+ onError?.(error);
126
+ }
127
+ }
128
+ var STAGE_LABELS = {
129
+ sanitizer: "Checking your request",
130
+ analyzer: "Understanding what you're asking",
131
+ prefetcher: "Gathering context",
132
+ planner: "Planning how to handle this",
133
+ execution: "Working on it",
134
+ formatter: "Writing the response"
135
+ };
136
+ function stageLabel(stage) {
137
+ const label = STAGE_LABELS[stage];
138
+ if (label) return label;
139
+ const pretty = stage.replace(/[_-]/g, " ");
140
+ return `Running ${pretty}`;
141
+ }
142
+ function isBlandStatus(message) {
143
+ if (!message) return true;
144
+ const normalized = message.trim().toLowerCase().replace(/[…\.]+$/, "").trim();
145
+ return BLAND_STATUS_LABELS.has(normalized);
146
+ }
147
+ var BLAND_STATUS_LABELS = /* @__PURE__ */ new Set([
148
+ "executing",
149
+ "working on it",
150
+ "thinking",
151
+ "processing",
152
+ "reviewing your request",
153
+ "composing response",
154
+ "checking your request",
155
+ "polishing the response"
156
+ ]);
157
+ function getEventMessage(event) {
158
+ if (event.message?.trim()) {
159
+ return event.message.trim();
160
+ }
161
+ if (event.errorMessage?.trim()) {
162
+ return event.errorMessage.trim();
163
+ }
164
+ const eventType = event.eventType;
165
+ switch (eventType) {
166
+ case "RUN_STARTED":
167
+ return "Starting agent run...";
168
+ case "TOOL_CALL_STARTED": {
169
+ const description = typeof event.description === "string" ? event.description.trim() : "";
170
+ if (description) return description;
171
+ const toolName = typeof event.toolName === "string" ? event.toolName : "";
172
+ return toolName ? `Calling ${toolName}...` : "Calling tool...";
173
+ }
174
+ case "TOOL_CALL_COMPLETED": {
175
+ const description = typeof event.description === "string" ? event.description.trim() : "";
176
+ if (description) return description;
177
+ const toolName = typeof event.toolName === "string" ? event.toolName : "";
178
+ return toolName ? `${toolName} completed` : "Tool call completed";
179
+ }
180
+ case "RUN_IN_PROGRESS":
181
+ return event.partialText || "Thinking...";
182
+ case "RUN_COMPLETED":
183
+ return "Agent run completed";
184
+ case "RUN_FAILED":
185
+ return event.errorMessage || "Agent run failed";
186
+ case "KEEP_ALIVE":
187
+ return event.description || "";
188
+ case "THINKING_DELTA":
189
+ return event.text || "";
190
+ default:
191
+ return eventType;
192
+ }
193
+ }
194
+ function extractResponseContent(response) {
195
+ if (typeof response === "string") {
196
+ return response;
197
+ }
198
+ if (typeof response === "object" && response !== null) {
199
+ const resp = response;
200
+ if ("text" in resp && typeof resp.text === "string") {
201
+ return resp.text;
202
+ }
203
+ if ("content" in resp && typeof resp.content === "string") {
204
+ return resp.content;
205
+ }
206
+ if ("message" in resp && typeof resp.message === "string") {
207
+ return resp.message;
208
+ }
209
+ if ("answer" in resp && typeof resp.answer === "string") {
210
+ return resp.answer;
211
+ }
212
+ return JSON.stringify(response);
213
+ }
214
+ return "";
215
+ }
216
+ function normalizeEvent(event) {
217
+ const type = event.eventType;
218
+ switch (type) {
219
+ case "RUN_STARTED":
220
+ return { ...event, eventType: "WORKFLOW_STARTED" };
221
+ case "TOOL_CALL_STARTED": {
222
+ const toolName = typeof event.toolName === "string" ? event.toolName : void 0;
223
+ const description = typeof event.description === "string" ? event.description.trim() : "";
224
+ return {
225
+ ...event,
226
+ eventType: "INTENT_STARTED",
227
+ workerName: toolName ?? event.workerName,
228
+ message: description || event.message
229
+ };
230
+ }
231
+ case "TOOL_CALL_COMPLETED": {
232
+ const toolName = typeof event.toolName === "string" ? event.toolName : void 0;
233
+ const description = typeof event.description === "string" ? event.description.trim() : "";
234
+ return {
235
+ ...event,
236
+ eventType: "INTENT_COMPLETED",
237
+ workerName: toolName ?? event.workerName,
238
+ message: description || event.message
239
+ };
240
+ }
241
+ case "RUN_IN_PROGRESS":
242
+ return {
243
+ ...event,
244
+ eventType: "INTENT_PROGRESS",
245
+ message: event.partialText ?? event.message
246
+ };
247
+ case "RUN_COMPLETED":
248
+ return {
249
+ ...event,
250
+ eventType: "WORKFLOW_COMPLETED",
251
+ // state machine reads event.response for the final content
252
+ response: event.response ?? event.message
253
+ };
254
+ case "RUN_FAILED":
255
+ return {
256
+ ...event,
257
+ eventType: "WORKFLOW_ERROR",
258
+ errorMessage: event.errorMessage ?? event.message
259
+ };
260
+ default:
261
+ return event;
262
+ }
263
+ }
264
+ function isVerificationSchema(schema) {
265
+ const props = schema?.properties;
266
+ if (!props) return false;
267
+ const keys = Object.keys(props);
268
+ return keys.length === 1 && keys[0] === "verificationCode";
269
+ }
270
+ function classifyUserActionKind(action, schema) {
271
+ switch ((action || "").toLowerCase()) {
272
+ case "userverificationrequest":
273
+ return "verification";
274
+ case "usernotificationrequest":
275
+ return "notification";
276
+ case "userformrequest":
277
+ return isVerificationSchema(schema) ? "verification" : "form";
278
+ default:
279
+ return isVerificationSchema(schema) ? "verification" : "form";
280
+ }
281
+ }
282
+ function userActionHeader(kind) {
283
+ return kind === "verification" ? "**Verification required**" : "**Action required**";
284
+ }
285
+ function workingPhaseDetailForDisplay(raw) {
286
+ const t = raw.trim();
287
+ if (!t) return "";
288
+ if (/^Identified\s+\d+\s+tasks?\s+to\s+execute\.?$/i.test(t)) {
289
+ return "";
290
+ }
291
+ return t;
292
+ }
293
+ function getEventText(event, field) {
294
+ const value = event[field];
295
+ return typeof value === "string" ? value.trim() : "";
296
+ }
297
+ function shouldShowIntentHeader(event) {
298
+ const workerName = getEventText(event, "workerName");
299
+ const intentId = getEventText(event, "intentId");
300
+ return Boolean(workerName && intentId && workerName === intentId);
301
+ }
302
+ function addThinkingHeader(state, header) {
303
+ state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + header;
304
+ }
305
+ function addThinkingDetail(state, detail) {
306
+ const trimmed = detail.trim();
307
+ if (!trimmed) return;
308
+ state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + trimmed;
309
+ }
310
+ function addThinkingLine(state, header, detail) {
311
+ state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + header + "\n" + detail;
312
+ }
313
+ function appendThinkingText(state, text) {
314
+ state.formattedThinkingText += text;
315
+ }
316
+ function updateExecutionStageMessage(state, msg) {
317
+ if (!msg) return;
318
+ for (let i = state.steps.length - 1; i >= 0; i--) {
319
+ const s = state.steps[i];
320
+ if (s.eventType === "STAGE_STARTED" && (s.stage === "executor" || s.stage === "execution") && s.status === "in_progress") {
321
+ s.message = msg;
322
+ return;
323
+ }
324
+ }
325
+ }
326
+ function completeLastInProgressStep(steps) {
327
+ for (let i = steps.length - 1; i >= 0; i--) {
328
+ if (steps[i].status === "in_progress") {
329
+ steps[i].status = "completed";
330
+ return;
331
+ }
332
+ }
333
+ }
334
+ function createInitialV2State() {
335
+ return {
336
+ formattedThinkingText: "",
337
+ finalResponse: "",
338
+ currentWorker: "",
339
+ lastEventType: "",
340
+ sessionId: void 0,
341
+ executionId: void 0,
342
+ hasError: false,
343
+ errorMessage: "",
344
+ userActions: [],
345
+ notifications: [],
346
+ lastUserAction: void 0,
347
+ lastNotification: void 0,
348
+ finalData: void 0,
349
+ steps: [],
350
+ stepCounter: 0,
351
+ currentExecutingStepId: void 0
352
+ };
353
+ }
354
+ function upsertUserAction(state, req) {
355
+ const active = { ...req, status: "pending" };
356
+ const matchIdx = state.userActions.findIndex(
357
+ (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
358
+ );
359
+ if (matchIdx >= 0) {
360
+ state.userActions[matchIdx] = active;
361
+ } else {
362
+ state.userActions.push(active);
363
+ }
364
+ }
365
+ function processStreamEventV2(rawEvent, state) {
366
+ const event = normalizeEvent(rawEvent);
367
+ const eventType = event.eventType;
368
+ state.lastUserAction = void 0;
369
+ state.lastNotification = void 0;
370
+ if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
371
+ if (event.executionId) state.executionId = event.executionId;
372
+ if (event.sessionId) state.sessionId = event.sessionId;
373
+ const description = typeof event.description === "string" ? event.description : "";
374
+ if (description) {
375
+ for (let i = state.steps.length - 1; i >= 0; i--) {
376
+ if (state.steps[i].status === "in_progress") {
377
+ state.steps[i].message = description;
378
+ break;
379
+ }
380
+ }
381
+ }
382
+ return state;
383
+ }
384
+ if (typeof eventType === "string" && eventType.toUpperCase() === "THINKING_DELTA") {
385
+ const text = typeof event.text === "string" ? event.text : "";
386
+ if (text) {
387
+ appendThinkingText(state, text);
388
+ }
389
+ if (event.executionId) state.executionId = event.executionId;
390
+ if (event.sessionId) state.sessionId = event.sessionId;
391
+ state.lastEventType = "THINKING_DELTA";
392
+ return state;
393
+ }
394
+ if (event.executionId) state.executionId = event.executionId;
395
+ if (event.sessionId) state.sessionId = event.sessionId;
396
+ const message = getEventMessage(event);
397
+ switch (eventType) {
398
+ case "WORKFLOW_STARTED":
399
+ case "STARTED":
400
+ state.lastEventType = eventType;
401
+ break;
402
+ case "INTENT_PROGRESS": {
403
+ const rawMessage = typeof event.message === "string" ? event.message : "";
404
+ const rawPartial = typeof event.partialText === "string" ? event.partialText : "";
405
+ const delta = rawMessage || rawPartial;
406
+ if (delta.length > 0) {
407
+ state.finalResponse += delta;
408
+ }
409
+ state.lastEventType = eventType;
410
+ break;
411
+ }
412
+ case "INTENT_THINKING": {
413
+ const worker = getEventText(event, "workerName") || "Worker";
414
+ const msg = getEventText(event, "message") || "Thinking...";
415
+ const showHeader = shouldShowIntentHeader(event);
416
+ if (worker !== state.currentWorker) {
417
+ state.currentWorker = worker;
418
+ if (showHeader && msg && msg !== worker) {
419
+ addThinkingLine(state, `**${worker}**`, msg);
420
+ } else if (showHeader) {
421
+ addThinkingHeader(state, `**${worker}**`);
422
+ } else if (msg !== "Thinking...") {
423
+ addThinkingDetail(state, msg);
424
+ }
425
+ } else if (showHeader || msg !== "Thinking...") {
426
+ appendThinkingText(state, "\n" + msg);
427
+ }
428
+ const lastInProgress = [...state.steps].reverse().find((s) => s.status === "in_progress");
429
+ if (lastInProgress) {
430
+ lastInProgress.thinkingText = "";
431
+ lastInProgress.isThinking = true;
432
+ }
433
+ state.lastEventType = "INTENT_THINKING";
434
+ break;
435
+ }
436
+ case "INTENT_THINKING_CONT": {
437
+ const msg = event.message || "";
438
+ if (!msg) break;
439
+ if (state.lastEventType === "INTENT_THINKING") {
440
+ appendThinkingText(state, "\n" + msg);
441
+ } else {
442
+ appendThinkingText(state, msg);
443
+ }
444
+ const thinkingStep = [...state.steps].reverse().find((s) => s.isThinking);
445
+ if (thinkingStep) {
446
+ thinkingStep.thinkingText = (thinkingStep.thinkingText || "") + msg;
447
+ }
448
+ state.lastEventType = "INTENT_THINKING_CONT";
449
+ break;
450
+ }
451
+ case "ORCHESTRATOR_THINKING": {
452
+ addThinkingLine(state, "**Planning**", event.message || "Understanding your request...");
453
+ const stepId = `step-${state.stepCounter++}`;
454
+ state.steps.push({
455
+ id: stepId,
456
+ eventType,
457
+ message,
458
+ status: "in_progress",
459
+ timestamp: Date.now(),
460
+ elapsedMs: event.elapsedMs
461
+ });
462
+ state.currentExecutingStepId = stepId;
463
+ state.lastEventType = eventType;
464
+ break;
465
+ }
466
+ case "ORCHESTRATOR_COMPLETED": {
467
+ const workingDetail = workingPhaseDetailForDisplay(message);
468
+ if (workingDetail) {
469
+ addThinkingLine(state, "**Working**", workingDetail);
470
+ } else {
471
+ addThinkingHeader(state, "**Working**");
472
+ }
473
+ state.steps.push({
474
+ id: `step-${state.stepCounter++}`,
475
+ eventType: "WORKING",
476
+ message: workingDetail,
477
+ status: "completed",
478
+ timestamp: Date.now()
479
+ });
480
+ const step = state.steps.find((s) => s.eventType === "ORCHESTRATOR_THINKING" && s.status === "in_progress");
481
+ if (step) {
482
+ step.status = "completed";
483
+ if (event.elapsedMs) step.elapsedMs = event.elapsedMs;
484
+ if (step.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
485
+ }
486
+ state.lastEventType = eventType;
487
+ break;
488
+ }
489
+ case "INTENT_STARTED": {
490
+ const worker = getEventText(event, "workerName") || "Worker";
491
+ const msg = getEventText(event, "message") || "Starting...";
492
+ const showHeader = shouldShowIntentHeader(event);
493
+ state.currentWorker = worker;
494
+ if (showHeader && msg !== worker) {
495
+ addThinkingLine(state, `**${worker}**`, msg);
496
+ } else if (showHeader) {
497
+ addThinkingHeader(state, `**${worker}**`);
498
+ } else {
499
+ addThinkingDetail(state, msg);
500
+ }
501
+ const thinkingStep = state.steps.find((s) => s.isThinking);
502
+ if (thinkingStep) thinkingStep.isThinking = false;
503
+ const stepId = `step-${state.stepCounter++}`;
504
+ state.steps.push({
505
+ id: stepId,
506
+ eventType,
507
+ message,
508
+ status: "in_progress",
509
+ timestamp: Date.now(),
510
+ elapsedMs: event.elapsedMs
511
+ });
512
+ state.currentExecutingStepId = stepId;
513
+ updateExecutionStageMessage(state, message);
514
+ state.lastEventType = eventType;
515
+ break;
516
+ }
517
+ case "INTENT_COMPLETED": {
518
+ const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
519
+ if (intentStep) {
520
+ intentStep.status = "completed";
521
+ intentStep.isThinking = false;
522
+ if (event.elapsedMs) intentStep.elapsedMs = event.elapsedMs;
523
+ if (intentStep.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
524
+ }
525
+ state.lastEventType = eventType;
526
+ break;
527
+ }
528
+ case "AGGREGATOR_THINKING": {
529
+ addThinkingLine(state, "**Finalizing**", event.message || "Preparing response...");
530
+ const stepId = `step-${state.stepCounter++}`;
531
+ state.steps.push({
532
+ id: stepId,
533
+ eventType,
534
+ message,
535
+ status: "in_progress",
536
+ timestamp: Date.now(),
537
+ elapsedMs: event.elapsedMs
538
+ });
539
+ state.currentExecutingStepId = stepId;
540
+ state.lastEventType = eventType;
541
+ break;
542
+ }
543
+ case "AGGREGATOR_COMPLETED": {
544
+ appendThinkingText(state, "\n" + (event.message || "Response ready"));
545
+ const step = state.steps.find((s) => s.eventType === "AGGREGATOR_THINKING" && s.status === "in_progress");
546
+ if (step) {
547
+ step.status = "completed";
548
+ if (event.elapsedMs) step.elapsedMs = event.elapsedMs;
549
+ if (step.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
550
+ }
551
+ state.lastEventType = eventType;
552
+ break;
553
+ }
554
+ case "WORKFLOW_COMPLETED":
555
+ case "COMPLETED": {
556
+ const totalTime = Number(event.totalTimeMs);
557
+ if (Number.isFinite(totalTime) && totalTime > 0) {
558
+ state.totalElapsedMs = totalTime;
559
+ }
560
+ let content = extractResponseContent(event.response);
561
+ const trace = event.trace && typeof event.trace === "object" ? event.trace : null;
562
+ if (!content && trace?.workflowMsg && typeof trace.workflowMsg === "string") {
563
+ content = trace.workflowMsg;
564
+ }
565
+ if (!content && trace?.aggregator && typeof trace.aggregator === "object") {
566
+ const agg = trace.aggregator;
567
+ if (typeof agg.response === "string") content = agg.response;
568
+ else content = extractResponseContent(agg.response);
569
+ }
570
+ if (content) {
571
+ state.finalResponse = content;
572
+ if (event.trace && typeof event.trace === "object") {
573
+ state.finalData = event.trace;
574
+ }
575
+ state.hasError = false;
576
+ state.errorMessage = "";
577
+ } else {
578
+ state.hasError = true;
579
+ state.errorMessage = "WORKFLOW_FAILED";
580
+ }
581
+ state.steps.forEach((step) => {
582
+ if (step.status === "in_progress") {
583
+ step.status = "completed";
584
+ step.isThinking = false;
585
+ }
586
+ });
587
+ state.lastEventType = eventType;
588
+ break;
589
+ }
590
+ case "USER_ACTION_REQUIRED": {
591
+ const rawAction = typeof event.action === "string" ? event.action : void 0;
592
+ const schema = event.requestedSchema;
593
+ const kind = classifyUserActionKind(rawAction, schema);
594
+ const promptMessage = typeof event.message === "string" && event.message.trim() || message || "";
595
+ const userActionId = typeof event.userActionId === "string" ? event.userActionId : "";
596
+ if (kind === "notification") {
597
+ const notification = {
598
+ id: userActionId || `note-${state.stepCounter++}`,
599
+ message: promptMessage
600
+ };
601
+ state.notifications.push(notification);
602
+ state.lastNotification = notification;
603
+ if (promptMessage) addThinkingDetail(state, promptMessage);
604
+ state.lastEventType = eventType;
605
+ break;
606
+ }
607
+ if (!userActionId) {
608
+ state.lastEventType = eventType;
609
+ break;
610
+ }
611
+ completeLastInProgressStep(state.steps);
612
+ const verificationType = event.verificationType === "ALPHANUMERIC_CODE" || event.verificationType === "NUMERIC_CODE" ? event.verificationType : void 0;
613
+ const request = {
614
+ userActionId,
615
+ kind,
616
+ rawAction,
617
+ subAction: typeof event.subAction === "string" ? event.subAction : void 0,
618
+ verificationType,
619
+ expirySeconds: typeof event.expirySeconds === "number" ? event.expirySeconds : void 0,
620
+ message: promptMessage || void 0,
621
+ requestedSchema: schema,
622
+ metadata: event.metadata,
623
+ toolCallId: typeof event.toolCallId === "string" ? event.toolCallId : void 0,
624
+ executionId: state.executionId,
625
+ sessionId: state.sessionId
626
+ };
627
+ upsertUserAction(state, request);
628
+ state.lastUserAction = request;
629
+ const header = userActionHeader(kind);
630
+ if (promptMessage) addThinkingLine(state, header, promptMessage);
631
+ else addThinkingHeader(state, header);
632
+ const stepId = `step-${state.stepCounter++}`;
633
+ state.steps.push({
634
+ id: stepId,
635
+ eventType,
636
+ message: promptMessage || (kind === "verification" ? "Waiting for verification..." : "Waiting for your input..."),
637
+ status: "in_progress",
638
+ timestamp: Date.now(),
639
+ elapsedMs: event.elapsedMs
640
+ });
641
+ state.currentExecutingStepId = stepId;
642
+ state.lastEventType = eventType;
643
+ break;
644
+ }
645
+ case "USER_NOTIFICATION": {
646
+ const noteMessage = typeof event.message === "string" && event.message.trim() || message || "";
647
+ const userActionId = typeof event.userActionId === "string" ? event.userActionId : "";
648
+ if (noteMessage || userActionId) {
649
+ const notification = {
650
+ id: userActionId || `note-${state.stepCounter++}`,
651
+ message: noteMessage
652
+ };
653
+ state.notifications.push(notification);
654
+ state.lastNotification = notification;
655
+ if (noteMessage) addThinkingDetail(state, noteMessage);
656
+ }
657
+ state.lastEventType = eventType;
658
+ break;
659
+ }
660
+ case "WORKFLOW_ERROR":
661
+ case "ERROR":
662
+ state.hasError = true;
663
+ state.errorMessage = event.errorMessage || event.message || "Workflow error";
664
+ state.lastEventType = eventType;
665
+ break;
666
+ case "INTENT_ERROR": {
667
+ state.errorMessage = message || event.errorMessage || "An error occurred";
668
+ const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
669
+ if (intentStep) {
670
+ intentStep.status = "error";
671
+ intentStep.isThinking = false;
672
+ }
673
+ state.lastEventType = eventType;
674
+ break;
675
+ }
676
+ // ---- K2 pipeline stage lifecycle events ----
677
+ //
678
+ // The k2-server playground streaming API emits
679
+ // STAGE_STARTED / STAGE_COMPLETED / STAGE_FAILED /
680
+ // STAGE_SKIPPED around each pipeline stage so the UI can
681
+ // show real progress (sanitizer → analyzer → planner → …)
682
+ // instead of a static placeholder. Each STAGE_STARTED
683
+ // pushes an in-progress step with a user-facing label; the
684
+ // matching STAGE_COMPLETED/FAILED closes it WITHOUT
685
+ // rewriting the label — a terse "completed" flash between
686
+ // stages was more noise than signal. STAGE_SKIPPED removes
687
+ // the just-opened step entirely (skipping is expected on
688
+ // DIRECT_RESPONSE short-circuits).
689
+ //
690
+ // Finding the matching step uses `stepId` directly rather
691
+ // than comparing labels — the processor remembers which id
692
+ // it minted for the latest open STAGE_STARTED and the
693
+ // closing events look it up from state.currentExecutingStepId.
694
+ case "STAGE_STARTED": {
695
+ const stage = getEventText(event, "stage");
696
+ if (!stage) {
697
+ state.lastEventType = eventType;
698
+ break;
699
+ }
700
+ const serverMessage = getEventText(event, "message");
701
+ let initialMessage = serverMessage || stageLabel(stage);
702
+ if (stage === "executor" || stage === "execution") {
703
+ for (let i = state.steps.length - 1; i >= 0; i--) {
704
+ const s = state.steps[i];
705
+ if (s.eventType === "STAGE_STARTED" && s.stage === "analyzer" && s.status === "completed" && s.message) {
706
+ initialMessage = s.message;
707
+ break;
708
+ }
709
+ }
710
+ }
711
+ const stepId = `stage-${stage}-${state.stepCounter++}`;
712
+ state.steps.push({
713
+ id: stepId,
714
+ eventType,
715
+ message: initialMessage,
716
+ status: "in_progress",
717
+ timestamp: Date.now(),
718
+ stage
719
+ });
720
+ state.currentExecutingStepId = stepId;
721
+ state.lastEventType = eventType;
722
+ break;
723
+ }
724
+ case "STAGE_COMPLETED": {
725
+ const stage = getEventText(event, "stage");
726
+ if (!stage) {
727
+ state.lastEventType = eventType;
728
+ break;
729
+ }
730
+ const serverMessage = getEventText(event, "message");
731
+ for (let i = state.steps.length - 1; i >= 0; i--) {
732
+ const s = state.steps[i];
733
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
734
+ if (serverMessage) s.message = serverMessage;
735
+ s.status = "completed";
736
+ const durationMs = Number(event.durationMs);
737
+ if (Number.isFinite(durationMs)) s.elapsedMs = durationMs;
738
+ if (s.id === state.currentExecutingStepId) {
739
+ state.currentExecutingStepId = void 0;
740
+ }
741
+ break;
742
+ }
743
+ }
744
+ state.lastEventType = eventType;
745
+ break;
746
+ }
747
+ case "STAGE_FAILED": {
748
+ const stage = getEventText(event, "stage");
749
+ if (!stage) {
750
+ state.lastEventType = eventType;
751
+ break;
752
+ }
753
+ const serverMessage = getEventText(event, "message");
754
+ for (let i = state.steps.length - 1; i >= 0; i--) {
755
+ const s = state.steps[i];
756
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
757
+ if (serverMessage) s.message = serverMessage;
758
+ s.status = "error";
759
+ const durationMs = Number(event.durationMs);
760
+ if (Number.isFinite(durationMs)) s.elapsedMs = durationMs;
761
+ if (s.id === state.currentExecutingStepId) {
762
+ state.currentExecutingStepId = void 0;
763
+ }
764
+ break;
765
+ }
766
+ }
767
+ state.lastEventType = eventType;
768
+ break;
769
+ }
770
+ case "STAGE_SKIPPED": {
771
+ const stage = getEventText(event, "stage");
772
+ if (!stage) {
773
+ state.lastEventType = eventType;
774
+ break;
775
+ }
776
+ for (let i = state.steps.length - 1; i >= 0; i--) {
777
+ const s = state.steps[i];
778
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
779
+ if (s.id === state.currentExecutingStepId) {
780
+ state.currentExecutingStepId = void 0;
781
+ }
782
+ s.status = "skipped";
783
+ break;
784
+ }
785
+ }
786
+ state.lastEventType = eventType;
787
+ break;
788
+ }
789
+ default:
790
+ state.lastEventType = eventType;
791
+ break;
792
+ }
793
+ return state;
794
+ }
795
+ function buildFormattedThinking(steps, allThinkingText) {
796
+ const parts = [];
797
+ const safeSteps = steps ?? [];
798
+ const cleanAll = allThinkingText.replace(/^\s+/, "");
799
+ if (cleanAll) {
800
+ const firstStepWithThinking = safeSteps.find(
801
+ (s) => s.thinkingText && s.thinkingText.trim()
802
+ );
803
+ if (!firstStepWithThinking) {
804
+ parts.push("**Preflight**");
805
+ parts.push(cleanAll);
806
+ } else {
807
+ const stepText = firstStepWithThinking.thinkingText.trim();
808
+ const idx = cleanAll.indexOf(stepText);
809
+ if (idx > 0) {
810
+ const orphaned = cleanAll.substring(0, idx).replace(/\s+$/, "");
811
+ if (orphaned) {
812
+ parts.push("**Preflight**");
813
+ parts.push(orphaned);
814
+ }
815
+ }
816
+ }
817
+ }
818
+ for (const step of safeSteps) {
819
+ switch (step.eventType) {
820
+ case "STAGE_STARTED": {
821
+ if (step.message) parts.push(`**${step.message}**`);
822
+ break;
823
+ }
824
+ case "ORCHESTRATOR_THINKING":
825
+ parts.push("**Planning**");
826
+ if (step.message) parts.push(step.message);
827
+ break;
828
+ case "INTENT_STARTED": {
829
+ let label = step.message || "Processing";
830
+ const started = label.match(/^(.+?)\s+started$/i);
831
+ const progress = label.match(/^(.+?)\s+in progress$/i);
832
+ if (started) label = started[1];
833
+ else if (progress) label = progress[1];
834
+ parts.push(`**${label}**`);
835
+ if (step.thinkingText) parts.push(step.thinkingText);
836
+ break;
837
+ }
838
+ case "INTENT_PROGRESS": {
839
+ if (step.thinkingText) parts.push(step.thinkingText);
840
+ else if (step.message) parts.push(step.message);
841
+ break;
842
+ }
843
+ case "AGGREGATOR_THINKING":
844
+ parts.push("**Finalizing**");
845
+ if (step.message) parts.push(step.message);
846
+ break;
847
+ case "USER_ACTION_REQUIRED":
848
+ parts.push("**Action required**");
849
+ if (step.message) parts.push(step.message);
850
+ break;
851
+ }
852
+ }
853
+ return parts.length > 0 ? parts.join("\n") : allThinkingText;
854
+ }
855
+ function createCancelledMessageUpdate(steps, currentMessage) {
856
+ const updatedSteps = steps.map((step) => {
857
+ if (step.status === "in_progress") {
858
+ return { ...step, status: "pending" };
859
+ }
860
+ return step;
861
+ });
862
+ return {
863
+ isStreaming: false,
864
+ isCancelled: true,
865
+ steps: updatedSteps,
866
+ currentExecutingStepId: void 0,
867
+ // Preserve currentMessage so UI can show it with X icon
868
+ currentMessage: currentMessage || "Thinking..."
869
+ };
870
+ }
871
+ var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
872
+ function buildRequestBody(config, userMessage, sessionId, options) {
873
+ const sessionOwner = config.sessionParams;
874
+ const sessionOwnerId = sessionOwner?.id?.trim();
875
+ if (!sessionOwnerId) {
876
+ throw new Error("ChatConfig.sessionParams.id is required to send ask requests.");
877
+ }
878
+ const sessionAttributes = sessionOwner?.attributes && Object.keys(sessionOwner.attributes).length > 0 ? sessionOwner.attributes : void 0;
879
+ return {
880
+ agentId: config.agentId,
881
+ userInput: userMessage,
882
+ sessionId,
883
+ sessionOwnerId,
884
+ sessionOwnerLabel: sessionOwner?.name || void 0,
885
+ sessionAttributes,
886
+ analysisMode: options?.analysisMode,
887
+ locale: resolveLocale(config.locale),
888
+ timezone: resolveTimezone(config.timezone)
889
+ };
890
+ }
891
+ function resolveLocale(configured) {
892
+ if (configured && configured.trim().length > 0) return configured.trim();
893
+ if (typeof navigator !== "undefined") {
894
+ const navLocale = navigator.language;
895
+ if (navLocale && navLocale.length > 0) return navLocale;
896
+ }
897
+ return "en-US";
898
+ }
899
+ function resolveTimezone(configured) {
900
+ if (configured && configured.trim().length > 0) return configured.trim();
901
+ try {
902
+ const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
903
+ if (zone && zone.length > 0) return zone;
904
+ } catch {
905
+ }
906
+ return "UTC";
907
+ }
908
+ function buildStreamingUrl(config) {
909
+ const endpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
910
+ const stage = config.stage || "DEV";
911
+ const stageParamName = config.stageQueryParam ?? "stage";
912
+ const queryParams = new URLSearchParams({ [stageParamName]: stage });
913
+ return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
914
+ }
915
+ function buildUserActionUrl(config, userActionId, action) {
916
+ const endpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
917
+ const [endpointPath] = endpoint.split("?");
918
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
919
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
920
+ const encodedUserActionId = encodeURIComponent(userActionId);
921
+ return `${config.api.baseUrl}${basePath}/user-action/${encodedUserActionId}/${action}`;
922
+ }
923
+ function buildResolveImagesUrl(config) {
924
+ if (config.api.resolveImagesEndpoint) {
925
+ return `${config.api.baseUrl}${config.api.resolveImagesEndpoint}`;
926
+ }
927
+ const streamEndpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
928
+ const [endpointPath] = streamEndpoint.split("?");
929
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
930
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
931
+ return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
932
+ }
933
+ function buildRequestHeaders(config) {
934
+ const headers = {
935
+ ...config.api.headers
936
+ };
937
+ if (config.api.authToken) {
938
+ headers.Authorization = `Bearer ${config.api.authToken}`;
939
+ }
940
+ return headers;
941
+ }
942
+ var UserActionStaleError = class extends Error {
943
+ constructor(userActionId, message = "User action is no longer actionable") {
944
+ super(message);
945
+ __publicField(this, "userActionId");
946
+ this.name = "UserActionStaleError";
947
+ this.userActionId = userActionId;
948
+ }
949
+ };
950
+ async function sendUserActionRequest(config, userActionId, action, data) {
951
+ const url = buildUserActionUrl(config, userActionId, action);
952
+ const baseHeaders = buildRequestHeaders(config);
953
+ const hasBody = data !== void 0;
954
+ const headers = hasBody ? { "Content-Type": "application/json", ...baseHeaders } : baseHeaders;
955
+ const response = await fetch(url, {
956
+ method: "POST",
957
+ headers,
958
+ body: hasBody ? JSON.stringify(data) : void 0
959
+ });
960
+ if (response.status === 404) {
961
+ throw new UserActionStaleError(userActionId);
962
+ }
963
+ if (!response.ok) {
964
+ const errorText = await response.text();
965
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
966
+ }
967
+ return await response.json();
968
+ }
969
+ async function submitUserAction(config, userActionId, content) {
970
+ return sendUserActionRequest(config, userActionId, "submit", content ?? {});
971
+ }
972
+ async function cancelUserAction(config, userActionId) {
973
+ return sendUserActionRequest(config, userActionId, "cancel");
974
+ }
975
+ async function resendUserAction(config, userActionId) {
976
+ return sendUserActionRequest(config, userActionId, "resend");
977
+ }
978
+ var memoryStore = /* @__PURE__ */ new Map();
979
+ var chatStore = {
980
+ get(key) {
981
+ return memoryStore.get(key) ?? [];
982
+ },
983
+ set(key, messages) {
984
+ memoryStore.set(key, messages);
985
+ },
986
+ delete(key) {
987
+ memoryStore.delete(key);
988
+ }
989
+ };
990
+ var streams = /* @__PURE__ */ new Map();
991
+ var activeStreamStore = {
992
+ has(key) {
993
+ return streams.has(key);
994
+ },
995
+ get(key) {
996
+ const entry = streams.get(key);
997
+ if (!entry) return null;
998
+ return { messages: entry.messages, isWaiting: entry.isWaiting };
999
+ },
1000
+ // Called before startStream — registers the controller and initial messages
1001
+ start(key, abortController, initialMessages) {
1002
+ const existing = streams.get(key);
1003
+ streams.set(key, {
1004
+ messages: initialMessages,
1005
+ isWaiting: true,
1006
+ abortController,
1007
+ listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
1008
+ });
1009
+ },
1010
+ // Called by the stream on every event — applies the same updater pattern React uses
1011
+ applyMessages(key, updater) {
1012
+ const entry = streams.get(key);
1013
+ if (!entry) return;
1014
+ const next = typeof updater === "function" ? updater(entry.messages) : updater;
1015
+ entry.messages = next;
1016
+ entry.listeners.forEach((l) => l(next, entry.isWaiting));
1017
+ },
1018
+ setWaiting(key, waiting) {
1019
+ const entry = streams.get(key);
1020
+ if (!entry) return;
1021
+ entry.isWaiting = waiting;
1022
+ entry.listeners.forEach((l) => l(entry.messages, waiting));
1023
+ },
1024
+ // Called when stream completes — persists to chatStore and cleans up
1025
+ complete(key) {
1026
+ const entry = streams.get(key);
1027
+ if (!entry) return;
1028
+ entry.isWaiting = false;
1029
+ entry.listeners.forEach((l) => l(entry.messages, false));
1030
+ const toSave = entry.messages.filter((m) => !m.isStreaming);
1031
+ if (toSave.length > 0) chatStore.set(key, toSave);
1032
+ streams.delete(key);
1033
+ },
1034
+ // Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
1035
+ subscribe(key, listener) {
1036
+ const entry = streams.get(key);
1037
+ if (!entry) return () => {
1038
+ };
1039
+ entry.listeners.add(listener);
1040
+ return () => {
1041
+ streams.get(key)?.listeners.delete(listener);
1042
+ };
1043
+ },
1044
+ // Rename an entry — used when the server assigns a new session ID mid-stream
1045
+ rename(oldKey, newKey) {
1046
+ const entry = streams.get(oldKey);
1047
+ if (!entry) return;
1048
+ streams.set(newKey, entry);
1049
+ streams.delete(oldKey);
1050
+ },
1051
+ // Explicit user cancel — aborts the controller and removes the entry
1052
+ abort(key) {
1053
+ const entry = streams.get(key);
1054
+ if (!entry) return;
1055
+ entry.abortController.abort();
1056
+ streams.delete(key);
1057
+ }
1058
+ };
1059
+ var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
1060
+ function hasRagImages(content) {
1061
+ return RAG_IMAGE_REGEX.test(content);
1062
+ }
1063
+ async function waitForNextPaint(signal) {
1064
+ if (signal?.aborted) return;
1065
+ await new Promise((resolve) => {
1066
+ let isSettled = false;
1067
+ const finish = () => {
1068
+ if (isSettled) return;
1069
+ isSettled = true;
1070
+ signal?.removeEventListener("abort", finish);
1071
+ resolve();
1072
+ };
1073
+ signal?.addEventListener("abort", finish, { once: true });
1074
+ if (typeof requestAnimationFrame === "function") {
1075
+ requestAnimationFrame(() => {
1076
+ setTimeout(finish, 0);
1077
+ });
1078
+ return;
1079
+ }
1080
+ setTimeout(finish, 0);
1081
+ });
1082
+ }
1083
+ async function resolveRagImageUrls(config, content, signal) {
1084
+ const url = buildResolveImagesUrl(config);
1085
+ const baseHeaders = buildRequestHeaders(config);
1086
+ const headers = { "Content-Type": "application/json", ...baseHeaders };
1087
+ const response = await fetch(url, {
1088
+ method: "POST",
1089
+ headers,
1090
+ body: JSON.stringify({ input: content }),
1091
+ signal
1092
+ });
1093
+ if (!response.ok) {
1094
+ const errorText = await response.text();
1095
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
1096
+ }
1097
+ const text = await response.text();
1098
+ try {
1099
+ const parsed = JSON.parse(text);
1100
+ if (typeof parsed === "string") return parsed;
1101
+ if (typeof parsed.output === "string") return parsed.output;
1102
+ if (typeof parsed.result === "string") return parsed.result;
1103
+ } catch {
1104
+ }
1105
+ return text;
1106
+ }
1107
+ var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
1108
+ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForResponse) {
1109
+ const abortControllerRef = useRef(null);
1110
+ const configRef = useRef(config);
1111
+ configRef.current = config;
1112
+ const callbacksRef = useRef(callbacks);
1113
+ callbacksRef.current = callbacks;
1114
+ const startStream = useCallback(
1115
+ async (userMessage, streamingId, sessionId, externalAbortController, options) => {
1116
+ abortControllerRef.current?.abort();
1117
+ const abortController = externalAbortController ?? new AbortController();
1118
+ abortControllerRef.current = abortController;
1119
+ const state = createInitialV2State();
1120
+ const updateMessage = (update) => {
1121
+ if (abortController.signal.aborted) return;
1122
+ setMessages(
1123
+ (prev) => prev.map(
1124
+ (msg) => msg.id === streamingId ? { ...msg, ...update } : msg
1125
+ )
1126
+ );
1127
+ };
1128
+ try {
1129
+ const currentConfig = configRef.current;
1130
+ const requestBody = buildRequestBody(
1131
+ currentConfig,
1132
+ userMessage,
1133
+ sessionId,
1134
+ options
1135
+ );
1136
+ const url = buildStreamingUrl(currentConfig);
1137
+ const headers = buildRequestHeaders(currentConfig);
1138
+ await streamWorkflowEvents(url, requestBody, headers, {
1139
+ signal: abortController.signal,
1140
+ onEvent: (event) => {
1141
+ if (abortController.signal.aborted) return;
1142
+ processStreamEventV2(event, state);
1143
+ if (state.lastUserAction) {
1144
+ callbacksRef.current.onUserActionRequired?.(state.lastUserAction);
1145
+ }
1146
+ if (state.lastNotification) {
1147
+ callbacksRef.current.onUserNotification?.(state.lastNotification);
1148
+ }
1149
+ const activeStep = state.steps.find((s) => s.id === state.currentExecutingStepId);
1150
+ const lastInProgressStep = [...state.steps].reverse().find((s) => s.status === "in_progress");
1151
+ const useful = (m) => m && !isBlandStatus(m) ? m : void 0;
1152
+ const latestUsefulStep = [...state.steps].reverse().find(
1153
+ (s) => s.message && !isBlandStatus(s.message)
1154
+ );
1155
+ const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
1156
+ // nothing useful seen yet). Render the bland label so the
1157
+ // bubble isn't blank.
1158
+ activeStep?.message ?? lastInProgressStep?.message ?? getEventMessage(event);
1159
+ if (currentMessage) {
1160
+ callbacksRef.current.onStatusMessage?.(currentMessage);
1161
+ }
1162
+ callbacksRef.current.onStepsUpdate?.([...state.steps]);
1163
+ if (state.hasError) {
1164
+ updateMessage({
1165
+ streamingContent: FRIENDLY_ERROR_MESSAGE,
1166
+ content: FRIENDLY_ERROR_MESSAGE,
1167
+ streamProgress: "error",
1168
+ isError: true,
1169
+ errorDetails: state.errorMessage,
1170
+ formattedThinkingText: state.formattedThinkingText || void 0,
1171
+ steps: [...state.steps],
1172
+ currentExecutingStepId: void 0,
1173
+ executionId: state.executionId,
1174
+ sessionId: state.sessionId
1175
+ });
1176
+ } else {
1177
+ updateMessage({
1178
+ streamingContent: state.finalResponse,
1179
+ content: "",
1180
+ currentMessage,
1181
+ streamProgress: "processing",
1182
+ isError: false,
1183
+ formattedThinkingText: state.formattedThinkingText || void 0,
1184
+ steps: [...state.steps],
1185
+ currentExecutingStepId: state.currentExecutingStepId,
1186
+ executionId: state.executionId,
1187
+ sessionId: state.sessionId,
1188
+ isCancelled: false
1189
+ });
1190
+ }
1191
+ },
1192
+ onError: (error) => {
1193
+ setIsWaitingForResponse(false);
1194
+ callbacksRef.current.onStatusMessage?.(null);
1195
+ if (error.name !== "AbortError") {
1196
+ callbacksRef.current.onError?.(error);
1197
+ }
1198
+ const isAborted = error.name === "AbortError";
1199
+ setMessages(
1200
+ (prev) => prev.map(
1201
+ (msg) => msg.id === streamingId ? {
1202
+ ...msg,
1203
+ isStreaming: false,
1204
+ streamProgress: isAborted ? "processing" : "error",
1205
+ isError: !isAborted,
1206
+ isCancelled: isAborted,
1207
+ errorDetails: isAborted ? void 0 : error.message,
1208
+ content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1209
+ currentMessage: isAborted ? "Thinking..." : void 0,
1210
+ formattedThinkingText: state.formattedThinkingText || void 0,
1211
+ steps: [...state.steps].map((step) => {
1212
+ if (step.status === "in_progress" && isAborted) {
1213
+ return { ...step, status: "pending" };
1214
+ }
1215
+ return step;
1216
+ }),
1217
+ currentExecutingStepId: void 0
1218
+ } : msg
1219
+ )
1220
+ );
1221
+ },
1222
+ onComplete: () => {
1223
+ setIsWaitingForResponse(false);
1224
+ callbacksRef.current.onStatusMessage?.(null);
1225
+ callbacksRef.current.onStepsUpdate?.([]);
1226
+ if (state.sessionId && state.sessionId !== sessionId) {
1227
+ callbacksRef.current.onSessionIdChange?.(state.sessionId);
1228
+ }
1229
+ const needsImageResolve = !state.hasError && !abortController.signal.aborted && hasRagImages(state.finalResponse);
1230
+ const finalMessage = {
1231
+ id: streamingId,
1232
+ sessionId: state.sessionId || sessionId,
1233
+ role: "assistant",
1234
+ content: state.hasError ? FRIENDLY_ERROR_MESSAGE : state.finalResponse || "",
1235
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1236
+ isStreaming: false,
1237
+ streamProgress: state.hasError ? "error" : "completed",
1238
+ isError: state.hasError,
1239
+ errorDetails: state.hasError ? state.errorMessage : void 0,
1240
+ executionId: state.executionId,
1241
+ // Defensive: tracingData must be an object (or undefined)
1242
+ // so the JSON viewer doesn't char-iterate a string. The
1243
+ // event processor already drops bare strings; this is
1244
+ // the last stop before the message leaves the SDK.
1245
+ tracingData: state.finalData != null && typeof state.finalData === "object" ? state.finalData : void 0,
1246
+ steps: state.hasError ? [] : [...state.steps],
1247
+ isCancelled: false,
1248
+ currentExecutingStepId: void 0,
1249
+ formattedThinkingText: state.hasError ? void 0 : state.formattedThinkingText || void 0,
1250
+ isResolvingImages: needsImageResolve,
1251
+ totalElapsedMs: state.hasError ? void 0 : state.totalElapsedMs
1252
+ };
1253
+ setMessages(
1254
+ (prev) => prev.map(
1255
+ (msg) => msg.id === streamingId ? finalMessage : msg
1256
+ )
1257
+ );
1258
+ callbacksRef.current.onStreamComplete?.(finalMessage);
1259
+ }
1260
+ });
1261
+ const shouldResolveImages = !abortController.signal.aborted && !state.hasError && hasRagImages(state.finalResponse);
1262
+ if (shouldResolveImages) {
1263
+ await waitForNextPaint(abortController.signal);
1264
+ }
1265
+ if (shouldResolveImages && !abortController.signal.aborted) {
1266
+ try {
1267
+ const resolvedContent = await resolveRagImageUrls(
1268
+ currentConfig,
1269
+ state.finalResponse,
1270
+ abortController.signal
1271
+ );
1272
+ setMessages(
1273
+ (prev) => prev.map(
1274
+ (msg) => msg.id === streamingId ? { ...msg, content: resolvedContent, isResolvingImages: false } : msg
1275
+ )
1276
+ );
1277
+ } catch {
1278
+ setMessages(
1279
+ (prev) => prev.map(
1280
+ (msg) => msg.id === streamingId ? { ...msg, isResolvingImages: false } : msg
1281
+ )
1282
+ );
1283
+ }
1284
+ }
1285
+ return state.sessionId;
1286
+ } catch (error) {
1287
+ setIsWaitingForResponse(false);
1288
+ if (error.name !== "AbortError") {
1289
+ callbacksRef.current.onError?.(error);
1290
+ }
1291
+ const isAborted = error.name === "AbortError";
1292
+ setMessages(
1293
+ (prev) => prev.map(
1294
+ (msg) => msg.id === streamingId ? {
1295
+ ...msg,
1296
+ isStreaming: false,
1297
+ streamProgress: isAborted ? "processing" : "error",
1298
+ isError: !isAborted,
1299
+ isCancelled: isAborted,
1300
+ errorDetails: isAborted ? void 0 : error.message,
1301
+ content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1302
+ formattedThinkingText: state.formattedThinkingText || void 0,
1303
+ steps: [...state.steps].map((step) => {
1304
+ if (step.status === "in_progress" && isAborted) {
1305
+ return { ...step, status: "pending" };
1306
+ }
1307
+ return step;
1308
+ }),
1309
+ currentExecutingStepId: void 0
1310
+ } : msg
1311
+ )
1312
+ );
1313
+ return state.sessionId;
1314
+ }
1315
+ },
1316
+ [setMessages, setIsWaitingForResponse]
1317
+ );
1318
+ const cancelStream = useCallback(() => {
1319
+ abortControllerRef.current?.abort();
1320
+ }, []);
1321
+ return {
1322
+ startStream,
1323
+ cancelStream,
1324
+ abortControllerRef
1325
+ };
1326
+ }
1327
+ var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1328
+ function upsertPrompt(prompts, req) {
1329
+ const active = { ...req, status: "pending" };
1330
+ const idx = prompts.findIndex(
1331
+ (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
1332
+ );
1333
+ if (idx >= 0) {
1334
+ const next = prompts.slice();
1335
+ next[idx] = active;
1336
+ return next;
1337
+ }
1338
+ return [...prompts, active];
1339
+ }
1340
+ function getStoredOrInitialMessages(config) {
1341
+ if (!config.userId) return config.initialMessages ?? [];
1342
+ const activeStream = activeStreamStore.get(config.userId);
1343
+ if (activeStream) return activeStream.messages;
1344
+ const stored = chatStore.get(config.userId);
1345
+ if (stored.length > 0) return stored;
1346
+ if (config.initialMessages?.length) {
1347
+ chatStore.set(config.userId, config.initialMessages);
1348
+ return config.initialMessages;
1349
+ }
1350
+ return [];
1351
+ }
1352
+ function getSessionIdFromMessages(messages) {
1353
+ return messages.find((message) => message.sessionId)?.sessionId;
1354
+ }
1355
+ function useChatV2(config, callbacks = {}) {
1356
+ const [messages, setMessages] = useState(() => getStoredOrInitialMessages(config));
1357
+ const [isWaitingForResponse, setIsWaitingForResponse] = useState(() => {
1358
+ if (!config.userId) return false;
1359
+ return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1360
+ });
1361
+ const sessionIdRef = useRef(
1362
+ getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1363
+ );
1364
+ const prevUserIdRef = useRef(config.userId);
1365
+ const streamUserIdRef = useRef(void 0);
1366
+ const subscriptionPrevUserIdRef = useRef(config.userId);
1367
+ const callbacksRef = useRef(callbacks);
1368
+ callbacksRef.current = callbacks;
1369
+ const configRef = useRef(config);
1370
+ configRef.current = config;
1371
+ const messagesRef = useRef(messages);
1372
+ messagesRef.current = messages;
1373
+ const storeAwareSetMessages = useCallback(
1374
+ (updater) => {
1375
+ const streamUserId = streamUserIdRef.current;
1376
+ const currentUserId = configRef.current.userId;
1377
+ const storeKey = streamUserId ?? currentUserId;
1378
+ if (storeKey && activeStreamStore.has(storeKey)) {
1379
+ activeStreamStore.applyMessages(storeKey, updater);
1380
+ }
1381
+ if (!streamUserId || streamUserId === currentUserId) {
1382
+ setMessages(updater);
1383
+ }
1384
+ },
1385
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1386
+ []
1387
+ );
1388
+ const storeAwareSetIsWaiting = useCallback(
1389
+ (waiting) => {
1390
+ const streamUserId = streamUserIdRef.current;
1391
+ const currentUserId = configRef.current.userId;
1392
+ const storeKey = streamUserId ?? currentUserId;
1393
+ if (storeKey && activeStreamStore.has(storeKey)) {
1394
+ activeStreamStore.setWaiting(storeKey, waiting);
1395
+ }
1396
+ if (!streamUserId || streamUserId === currentUserId) {
1397
+ setIsWaitingForResponse(waiting);
1398
+ }
1399
+ },
1400
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1401
+ []
1402
+ );
1403
+ const [userActionState, setUserActionState] = useState(EMPTY_USER_ACTION_STATE);
1404
+ const wrappedCallbacks = useMemo(() => ({
1405
+ ...callbacksRef.current,
1406
+ onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
1407
+ onStreamStart: () => callbacksRef.current.onStreamStart?.(),
1408
+ onStreamComplete: (message) => callbacksRef.current.onStreamComplete?.(message),
1409
+ onError: (error) => callbacksRef.current.onError?.(error),
1410
+ onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1411
+ onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1412
+ onUserActionRequired: (request) => {
1413
+ setUserActionState((prev) => ({
1414
+ ...prev,
1415
+ prompts: upsertPrompt(prev.prompts, request)
1416
+ }));
1417
+ callbacksRef.current.onUserActionRequired?.(request);
1418
+ },
1419
+ onUserNotification: (notification) => {
1420
+ setUserActionState(
1421
+ (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1422
+ );
1423
+ callbacksRef.current.onUserNotification?.(notification);
1424
+ }
1425
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1426
+ }), []);
1427
+ const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManagerV2(
1428
+ config,
1429
+ wrappedCallbacks,
1430
+ storeAwareSetMessages,
1431
+ storeAwareSetIsWaiting
1432
+ );
1433
+ const sendMessage = useCallback(
1434
+ async (userMessage, options) => {
1435
+ if (!userMessage.trim()) return;
1436
+ if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1437
+ sessionIdRef.current = generateId();
1438
+ callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1439
+ }
1440
+ const userMessageId = `user-${Date.now()}`;
1441
+ const userMsg = {
1442
+ id: userMessageId,
1443
+ sessionId: sessionIdRef.current,
1444
+ role: "user",
1445
+ content: userMessage,
1446
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1447
+ };
1448
+ setMessages((prev) => [...prev, userMsg]);
1449
+ callbacksRef.current.onMessageSent?.(userMessage);
1450
+ setIsWaitingForResponse(true);
1451
+ callbacksRef.current.onStreamStart?.();
1452
+ const streamingId = `assistant-${Date.now()}`;
1453
+ const streamingMsg = {
1454
+ id: streamingId,
1455
+ sessionId: sessionIdRef.current,
1456
+ role: "assistant",
1457
+ content: "",
1458
+ streamingContent: "",
1459
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1460
+ isStreaming: true,
1461
+ streamProgress: "started",
1462
+ steps: [],
1463
+ currentExecutingStepId: void 0,
1464
+ isCancelled: false,
1465
+ currentMessage: void 0
1466
+ };
1467
+ setMessages((prev) => [...prev, streamingMsg]);
1468
+ const abortController = new AbortController();
1469
+ const { userId } = configRef.current;
1470
+ if (userId) {
1471
+ streamUserIdRef.current = userId;
1472
+ const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
1473
+ activeStreamStore.start(userId, abortController, initialMessages);
1474
+ }
1475
+ const newSessionId = await startStream(
1476
+ userMessage,
1477
+ streamingId,
1478
+ sessionIdRef.current,
1479
+ abortController,
1480
+ options
1481
+ );
1482
+ const finalStreamUserId = streamUserIdRef.current ?? userId;
1483
+ if (finalStreamUserId) {
1484
+ activeStreamStore.complete(finalStreamUserId);
1485
+ }
1486
+ streamUserIdRef.current = void 0;
1487
+ if (!abortController.signal.aborted && newSessionId && newSessionId !== sessionIdRef.current) {
1488
+ sessionIdRef.current = newSessionId;
1489
+ }
1490
+ },
1491
+ [startStream]
1492
+ );
1493
+ const clearMessages = useCallback(() => {
1494
+ if (configRef.current.userId) {
1495
+ chatStore.delete(configRef.current.userId);
1496
+ }
1497
+ setMessages([]);
1498
+ }, []);
1499
+ const prependMessages = useCallback((msgs) => {
1500
+ setMessages((prev) => [...msgs, ...prev]);
1501
+ }, []);
1502
+ const cancelStream = useCallback(() => {
1503
+ const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1504
+ if (streamUserId) {
1505
+ activeStreamStore.abort(streamUserId);
1506
+ }
1507
+ streamUserIdRef.current = void 0;
1508
+ cancelStreamManager();
1509
+ setIsWaitingForResponse(false);
1510
+ setUserActionState((prev) => ({ ...prev, prompts: [] }));
1511
+ setMessages(
1512
+ (prev) => prev.map((msg) => {
1513
+ if (msg.isStreaming) {
1514
+ return {
1515
+ ...msg,
1516
+ ...createCancelledMessageUpdate(
1517
+ msg.steps || [],
1518
+ msg.currentMessage
1519
+ )
1520
+ };
1521
+ }
1522
+ return msg;
1523
+ })
1524
+ );
1525
+ }, [cancelStreamManager]);
1526
+ const resetSession = useCallback(() => {
1527
+ const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1528
+ if (streamUserId) {
1529
+ activeStreamStore.abort(streamUserId);
1530
+ }
1531
+ if (configRef.current.userId) {
1532
+ chatStore.delete(configRef.current.userId);
1533
+ }
1534
+ streamUserIdRef.current = void 0;
1535
+ setMessages([]);
1536
+ sessionIdRef.current = void 0;
1537
+ abortControllerRef.current?.abort();
1538
+ setIsWaitingForResponse(false);
1539
+ setUserActionState(EMPTY_USER_ACTION_STATE);
1540
+ }, []);
1541
+ const getSessionId = useCallback(() => {
1542
+ return sessionIdRef.current;
1543
+ }, []);
1544
+ const getMessages = useCallback(() => {
1545
+ return messages;
1546
+ }, [messages]);
1547
+ const setPromptStatus = useCallback(
1548
+ (userActionId, status) => {
1549
+ setUserActionState((prev) => ({
1550
+ ...prev,
1551
+ prompts: prev.prompts.map(
1552
+ (p) => p.userActionId === userActionId ? { ...p, status } : p
1553
+ )
1554
+ }));
1555
+ },
1556
+ []
1557
+ );
1558
+ const removePrompt = useCallback((userActionId) => {
1559
+ setUserActionState((prev) => ({
1560
+ ...prev,
1561
+ prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
1562
+ }));
1563
+ }, []);
1564
+ const submitUserAction2 = useCallback(
1565
+ async (userActionId, content) => {
1566
+ setPromptStatus(userActionId, "submitting");
1567
+ try {
1568
+ await submitUserAction(configRef.current, userActionId, content);
1569
+ removePrompt(userActionId);
1570
+ } catch (error) {
1571
+ if (error instanceof UserActionStaleError) {
1572
+ setPromptStatus(userActionId, "stale");
1573
+ return;
1574
+ }
1575
+ setPromptStatus(userActionId, "pending");
1576
+ callbacksRef.current.onError?.(error);
1577
+ throw error;
1578
+ }
1579
+ },
1580
+ [removePrompt, setPromptStatus]
1581
+ );
1582
+ const cancelUserAction2 = useCallback(
1583
+ async (userActionId) => {
1584
+ setPromptStatus(userActionId, "submitting");
1585
+ try {
1586
+ await cancelUserAction(configRef.current, userActionId);
1587
+ removePrompt(userActionId);
1588
+ } catch (error) {
1589
+ if (error instanceof UserActionStaleError) {
1590
+ removePrompt(userActionId);
1591
+ return;
1592
+ }
1593
+ setPromptStatus(userActionId, "pending");
1594
+ callbacksRef.current.onError?.(error);
1595
+ throw error;
1596
+ }
1597
+ },
1598
+ [removePrompt, setPromptStatus]
1599
+ );
1600
+ const resendUserAction2 = useCallback(
1601
+ async (userActionId) => {
1602
+ setPromptStatus(userActionId, "submitting");
1603
+ try {
1604
+ await resendUserAction(configRef.current, userActionId);
1605
+ setPromptStatus(userActionId, "pending");
1606
+ } catch (error) {
1607
+ if (error instanceof UserActionStaleError) {
1608
+ setPromptStatus(userActionId, "stale");
1609
+ return;
1610
+ }
1611
+ setPromptStatus(userActionId, "pending");
1612
+ callbacksRef.current.onError?.(error);
1613
+ throw error;
1614
+ }
1615
+ },
1616
+ [setPromptStatus]
1617
+ );
1618
+ const dismissNotification = useCallback((id) => {
1619
+ setUserActionState((prev) => ({
1620
+ ...prev,
1621
+ notifications: prev.notifications.filter((n) => n.id !== id)
1622
+ }));
1623
+ }, []);
1624
+ useEffect(() => {
1625
+ const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
1626
+ subscriptionPrevUserIdRef.current = config.userId;
1627
+ const { userId } = config;
1628
+ if (!userId) return;
1629
+ if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
1630
+ streamUserIdRef.current = userId;
1631
+ }
1632
+ const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1633
+ setMessages(msgs);
1634
+ setIsWaitingForResponse(isWaiting);
1635
+ });
1636
+ const active = activeStreamStore.get(userId);
1637
+ if (active) {
1638
+ setMessages(active.messages);
1639
+ setIsWaitingForResponse(active.isWaiting);
1640
+ }
1641
+ return unsubscribe;
1642
+ }, [config.userId]);
1643
+ useEffect(() => {
1644
+ if (!config.userId) return;
1645
+ if (prevUserIdRef.current !== config.userId) return;
1646
+ const toSave = messages.filter((m) => !m.isStreaming);
1647
+ if (toSave.length > 0) {
1648
+ chatStore.set(config.userId, toSave);
1649
+ }
1650
+ }, [messages, config.userId]);
1651
+ useEffect(() => {
1652
+ if (!config.userId || activeStreamStore.has(config.userId)) return;
1653
+ if (!config.initialMessages?.length || messagesRef.current.length > 0) return;
1654
+ chatStore.set(config.userId, config.initialMessages);
1655
+ setMessages(config.initialMessages);
1656
+ sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1657
+ }, [config.initialMessages, config.initialSessionId, config.userId]);
1658
+ useEffect(() => {
1659
+ const prevUserId = prevUserIdRef.current;
1660
+ prevUserIdRef.current = config.userId;
1661
+ if (prevUserId === config.userId) return;
1662
+ if (prevUserId && !config.userId) {
1663
+ chatStore.delete(prevUserId);
1664
+ setMessages([]);
1665
+ sessionIdRef.current = void 0;
1666
+ setIsWaitingForResponse(false);
1667
+ setUserActionState(EMPTY_USER_ACTION_STATE);
1668
+ } else if (config.userId) {
1669
+ const active = activeStreamStore.get(config.userId);
1670
+ if (active) {
1671
+ setMessages(active.messages);
1672
+ setIsWaitingForResponse(active.isWaiting);
1673
+ sessionIdRef.current = getSessionIdFromMessages(active.messages) ?? config.initialSessionId;
1674
+ return;
1675
+ }
1676
+ const nextMessages = getStoredOrInitialMessages(config);
1677
+ setMessages(nextMessages);
1678
+ sessionIdRef.current = getSessionIdFromMessages(nextMessages) ?? config.initialSessionId;
1679
+ setIsWaitingForResponse(false);
1680
+ }
1681
+ }, [config]);
1682
+ return {
1683
+ messages,
1684
+ sendMessage,
1685
+ clearMessages,
1686
+ prependMessages,
1687
+ cancelStream,
1688
+ resetSession,
1689
+ getSessionId,
1690
+ getMessages,
1691
+ isWaitingForResponse,
1692
+ sessionId: sessionIdRef.current,
1693
+ userActionState,
1694
+ submitUserAction: submitUserAction2,
1695
+ cancelUserAction: cancelUserAction2,
1696
+ resendUserAction: resendUserAction2,
1697
+ dismissNotification
1698
+ };
1699
+ }
1700
+ function getSpeechRecognition() {
1701
+ if (typeof window === "undefined") return null;
1702
+ return window.SpeechRecognition || window.webkitSpeechRecognition || null;
1703
+ }
1704
+ function useVoice(config = {}, callbacks = {}) {
1705
+ const [voiceState, setVoiceState] = useState("idle");
1706
+ const [transcribedText, setTranscribedText] = useState("");
1707
+ const [isAvailable, setIsAvailable] = useState(false);
1708
+ const [isRecording, setIsRecording] = useState(false);
1709
+ const recognitionRef = useRef(null);
1710
+ const autoStopTimerRef = useRef(null);
1711
+ const {
1712
+ lang = "en-US",
1713
+ interimResults = true,
1714
+ continuous = true,
1715
+ maxAlternatives = 1,
1716
+ autoStopAfterSilence
1717
+ } = config;
1718
+ const { onStart, onEnd, onResult, onError, onStateChange } = callbacks;
1719
+ useEffect(() => {
1720
+ const SpeechRecognitionAPI = getSpeechRecognition();
1721
+ setIsAvailable(SpeechRecognitionAPI !== null);
1722
+ }, []);
1723
+ useEffect(() => {
1724
+ onStateChange?.(voiceState);
1725
+ }, [voiceState, onStateChange]);
1726
+ const requestPermissions = useCallback(async () => {
1727
+ try {
1728
+ const result = await navigator.mediaDevices.getUserMedia({
1729
+ audio: true
1730
+ });
1731
+ result.getTracks().forEach((track) => track.stop());
1732
+ return {
1733
+ granted: true,
1734
+ status: "granted"
1735
+ };
1736
+ } catch (error) {
1737
+ return {
1738
+ granted: false,
1739
+ status: "denied"
1740
+ };
1741
+ }
1742
+ }, []);
1743
+ const getPermissions = useCallback(async () => {
1744
+ if (typeof navigator === "undefined" || !navigator.permissions) {
1745
+ return {
1746
+ granted: false,
1747
+ status: "undetermined"
1748
+ };
1749
+ }
1750
+ try {
1751
+ const result = await navigator.permissions.query({
1752
+ name: "microphone"
1753
+ });
1754
+ return {
1755
+ granted: result.state === "granted",
1756
+ status: result.state === "granted" ? "granted" : result.state === "denied" ? "denied" : "undetermined"
1757
+ };
1758
+ } catch {
1759
+ return {
1760
+ granted: false,
1761
+ status: "undetermined"
1762
+ };
1763
+ }
1764
+ }, []);
1765
+ const clearAutoStopTimer = useCallback(() => {
1766
+ if (autoStopTimerRef.current) {
1767
+ clearTimeout(autoStopTimerRef.current);
1768
+ autoStopTimerRef.current = null;
1769
+ }
1770
+ }, []);
1771
+ const resetAutoStopTimer = useCallback(() => {
1772
+ clearAutoStopTimer();
1773
+ if (autoStopAfterSilence && autoStopAfterSilence > 0) {
1774
+ autoStopTimerRef.current = setTimeout(() => {
1775
+ if (recognitionRef.current && isRecording) {
1776
+ recognitionRef.current.stop();
1777
+ }
1778
+ }, autoStopAfterSilence);
1779
+ }
1780
+ }, [autoStopAfterSilence, clearAutoStopTimer, isRecording]);
1781
+ const stopRecording = useCallback(() => {
1782
+ if (recognitionRef.current) {
1783
+ try {
1784
+ recognitionRef.current.stop();
1785
+ } catch (error) {
1786
+ console.warn("Error stopping speech recognition:", error);
1787
+ }
1788
+ }
1789
+ clearAutoStopTimer();
1790
+ setIsRecording(false);
1791
+ setVoiceState("idle");
1792
+ }, [clearAutoStopTimer]);
1793
+ const startRecording = useCallback(async () => {
1794
+ const SpeechRecognitionAPI = getSpeechRecognition();
1795
+ if (!SpeechRecognitionAPI) {
1796
+ onError?.("Speech recognition not supported in this browser");
1797
+ return;
1798
+ }
1799
+ try {
1800
+ try {
1801
+ await navigator.mediaDevices.getUserMedia({ audio: true });
1802
+ } catch (permError) {
1803
+ onError?.("Microphone access denied. Please allow microphone access in your browser settings.");
1804
+ return;
1805
+ }
1806
+ const recognition = new SpeechRecognitionAPI();
1807
+ recognition.continuous = continuous;
1808
+ recognition.interimResults = interimResults;
1809
+ recognition.lang = lang;
1810
+ recognition.maxAlternatives = maxAlternatives;
1811
+ recognition.onstart = () => {
1812
+ setVoiceState("listening");
1813
+ setIsRecording(true);
1814
+ onStart?.();
1815
+ resetAutoStopTimer();
1816
+ };
1817
+ recognition.onend = () => {
1818
+ setVoiceState("idle");
1819
+ setIsRecording(false);
1820
+ clearAutoStopTimer();
1821
+ onEnd?.();
1822
+ };
1823
+ recognition.onresult = (event) => {
1824
+ const results = event.results;
1825
+ let transcript = "";
1826
+ for (let i = 0; i < results.length; i++) {
1827
+ const result = results[i];
1828
+ if (result && result[0]) {
1829
+ const text = result[0].transcript;
1830
+ if (transcript && !transcript.endsWith(" ") && !text.startsWith(" ")) {
1831
+ transcript += " " + text;
1832
+ } else {
1833
+ transcript += text;
1834
+ }
1835
+ }
1836
+ }
1837
+ transcript = transcript.trim();
1838
+ if (transcript) {
1839
+ setTranscribedText(transcript);
1840
+ onResult?.(transcript);
1841
+ resetAutoStopTimer();
1842
+ }
1843
+ };
1844
+ recognition.onerror = (event) => {
1845
+ setVoiceState("error");
1846
+ setIsRecording(false);
1847
+ clearAutoStopTimer();
1848
+ let errorMessage = event.error;
1849
+ if (event.error === "not-allowed") {
1850
+ errorMessage = "Microphone access denied. Please allow microphone access in your browser settings.";
1851
+ } else if (event.error === "no-speech") {
1852
+ errorMessage = "No speech detected. Please try again.";
1853
+ } else if (event.error === "audio-capture") {
1854
+ errorMessage = "No microphone found or microphone is in use.";
1855
+ } else if (event.error === "network") {
1856
+ errorMessage = "Network error occurred. Please check your connection.";
1857
+ }
1858
+ onError?.(errorMessage);
1859
+ };
1860
+ recognitionRef.current = recognition;
1861
+ setTranscribedText("");
1862
+ recognition.start();
1863
+ } catch (error) {
1864
+ setVoiceState("error");
1865
+ setIsRecording(false);
1866
+ onError?.(
1867
+ error instanceof Error ? error.message : "Failed to start recording"
1868
+ );
1869
+ }
1870
+ }, [
1871
+ lang,
1872
+ interimResults,
1873
+ continuous,
1874
+ maxAlternatives,
1875
+ onStart,
1876
+ onEnd,
1877
+ onResult,
1878
+ onError,
1879
+ getPermissions,
1880
+ resetAutoStopTimer,
1881
+ clearAutoStopTimer
1882
+ ]);
1883
+ const clearTranscript = useCallback(() => {
1884
+ setTranscribedText("");
1885
+ }, []);
1886
+ const reset = useCallback(() => {
1887
+ stopRecording();
1888
+ setTranscribedText("");
1889
+ setVoiceState("idle");
1890
+ }, [stopRecording]);
1891
+ useEffect(() => {
1892
+ return () => {
1893
+ if (recognitionRef.current) {
1894
+ try {
1895
+ recognitionRef.current.stop();
1896
+ } catch {
1897
+ }
1898
+ }
1899
+ clearAutoStopTimer();
1900
+ };
1901
+ }, [clearAutoStopTimer]);
1902
+ return {
1903
+ voiceState,
1904
+ transcribedText,
1905
+ isAvailable,
1906
+ isRecording,
1907
+ startRecording,
1908
+ stopRecording,
1909
+ requestPermissions,
1910
+ getPermissions,
1911
+ clearTranscript,
1912
+ reset
1913
+ };
1914
+ }
1915
+ function classifyField(field) {
1916
+ if (!field) return "text";
1917
+ if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
1918
+ switch (field.type) {
1919
+ case "boolean":
1920
+ return "boolean";
1921
+ case "integer":
1922
+ return "integer";
1923
+ case "number":
1924
+ return "decimal";
1925
+ case "string":
1926
+ return "text";
1927
+ default:
1928
+ return "text";
1929
+ }
1930
+ }
1931
+ function isNestedOrUnsupported(field) {
1932
+ if (!field) return false;
1933
+ if (field.type === "object" || field.type === "array") return true;
1934
+ if ("properties" in field && field.properties != null) return true;
1935
+ if ("items" in field && field.items != null) return true;
1936
+ return false;
1937
+ }
1938
+ function getOptions(field) {
1939
+ if (!field || !Array.isArray(field.oneOf)) return [];
1940
+ return field.oneOf.filter(
1941
+ (o) => !!o && typeof o === "object" && typeof o.const === "string"
1942
+ );
1943
+ }
1944
+ function isRequired(schema, key) {
1945
+ return Array.isArray(schema?.required) && schema.required.includes(key);
1946
+ }
1947
+ function coerceValue(field, raw) {
1948
+ const widget = classifyField(field);
1949
+ if (widget === "boolean") {
1950
+ if (typeof raw === "boolean") return raw;
1951
+ if (raw === "true") return true;
1952
+ if (raw === "false") return false;
1953
+ return Boolean(raw);
1954
+ }
1955
+ if (widget === "integer" || widget === "decimal") {
1956
+ if (raw === "" || raw == null) return void 0;
1957
+ const num = typeof raw === "number" ? raw : Number(String(raw).trim());
1958
+ if (Number.isNaN(num)) return void 0;
1959
+ return widget === "integer" ? Math.trunc(num) : num;
1960
+ }
1961
+ if (raw == null) return void 0;
1962
+ const str = String(raw);
1963
+ return str === "" ? void 0 : str;
1964
+ }
1965
+ function defaultValueFor(field) {
1966
+ if (!field || field.default === void 0) {
1967
+ return classifyField(field) === "boolean" ? false : "";
1968
+ }
1969
+ return field.default;
1970
+ }
1971
+ function validateField(field, value, required) {
1972
+ const widget = classifyField(field);
1973
+ const label = field?.title || "This field";
1974
+ const isEmpty = value === void 0 || value === null || typeof value === "string" && value.trim() === "";
1975
+ if (isEmpty) {
1976
+ if (required && widget !== "boolean") return `${label} is required.`;
1977
+ return null;
1978
+ }
1979
+ if (widget === "integer" || widget === "decimal") {
1980
+ const num = typeof value === "number" ? value : Number(value);
1981
+ if (Number.isNaN(num)) return `${label} must be a number.`;
1982
+ if (widget === "integer" && !Number.isInteger(num)) {
1983
+ return `${label} must be a whole number.`;
1984
+ }
1985
+ if (typeof field?.minimum === "number" && num < field.minimum) {
1986
+ return `${label} must be at least ${field.minimum}.`;
1987
+ }
1988
+ if (typeof field?.maximum === "number" && num > field.maximum) {
1989
+ return `${label} must be at most ${field.maximum}.`;
1990
+ }
1991
+ return null;
1992
+ }
1993
+ if (widget === "select") {
1994
+ const allowed = getOptions(field).map((o) => o.const);
1995
+ if (allowed.length > 0 && !allowed.includes(String(value))) {
1996
+ return `${label} has an invalid selection.`;
1997
+ }
1998
+ return null;
1999
+ }
2000
+ const str = String(value);
2001
+ if (typeof field?.minLength === "number" && str.length < field.minLength) {
2002
+ return `${label} must be at least ${field.minLength} characters.`;
2003
+ }
2004
+ if (typeof field?.maxLength === "number" && str.length > field.maxLength) {
2005
+ return `${label} must be at most ${field.maxLength} characters.`;
2006
+ }
2007
+ return null;
2008
+ }
2009
+ function renderableFields(schema) {
2010
+ const props = schema?.properties;
2011
+ if (!props) return [];
2012
+ return Object.entries(props).filter(([, field]) => !isNestedOrUnsupported(field));
2013
+ }
2014
+ function validateForm(schema, values) {
2015
+ const errors = {};
2016
+ for (const [key, field] of renderableFields(schema)) {
2017
+ const coerced = coerceValue(field, values[key]);
2018
+ const err = validateField(field, coerced, isRequired(schema, key));
2019
+ if (err) errors[key] = err;
2020
+ }
2021
+ return errors;
2022
+ }
2023
+ function buildContent(schema, values) {
2024
+ const content = {};
2025
+ for (const [key, field] of renderableFields(schema)) {
2026
+ const coerced = coerceValue(field, values[key]);
2027
+ if (coerced !== void 0) content[key] = coerced;
2028
+ }
2029
+ return content;
2030
+ }
2031
+ function migrateActiveStream(oldUserId, newUserId) {
2032
+ activeStreamStore.rename(oldUserId, newUserId);
2033
+ }
16
2034
  var PaymanChatContext = createContext(void 0);
17
2035
  function usePaymanChat() {
18
2036
  const context = useContext(PaymanChatContext);
@@ -232,7 +2250,7 @@ var NEGATIVE_FEEDBACK_REASONS = [
232
2250
  { value: "REPORT_CONTENT", label: "Report content" },
233
2251
  { value: "OTHER", label: "Other" }
234
2252
  ];
235
- var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
2253
+ var DEFAULT_STREAM_ENDPOINT2 = "/api/playground/ask/stream";
236
2254
  async function submitFeedback({
237
2255
  baseUrl,
238
2256
  streamEndpoint,
@@ -246,7 +2264,7 @@ async function submitFeedback({
246
2264
  signal
247
2265
  }) {
248
2266
  const base = baseUrl.replace(/\/+$/, "");
249
- const endpointPath = (streamEndpoint || DEFAULT_STREAM_ENDPOINT).split("?")[0].replace(/\/+$/, "");
2267
+ const endpointPath = (streamEndpoint || DEFAULT_STREAM_ENDPOINT2).split("?")[0].replace(/\/+$/, "");
250
2268
  const basePath = endpointPath.endsWith("/stream") ? endpointPath.slice(0, -"/stream".length) : endpointPath;
251
2269
  const query = new URLSearchParams();
252
2270
  if (stage) query.set(stageQueryParam ?? "stage", stage);
@@ -558,7 +2576,7 @@ function ThinkingBlock({ text }) {
558
2576
  ) })
559
2577
  ] });
560
2578
  }
561
- var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
2579
+ var FRIENDLY_ERROR_MESSAGE2 = "Oops, something went wrong. Please try again.";
562
2580
  function looksLikeRawError(text) {
563
2581
  if (!text || text.length < 10) return false;
564
2582
  return text.includes("errorType=") || /failed:\s*\{/.test(text);
@@ -724,95 +2742,86 @@ function AgentMessage({
724
2742
  /* @__PURE__ */ jsx("div", { className: "h-2.5 w-2.5 rounded-full payman-agent-avatar-dot animate-pulse" }),
725
2743
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 h-2.5 w-2.5 rounded-full payman-agent-avatar-dot-ping animate-ping" })
726
2744
  ] }) }) : /* @__PURE__ */ jsx("div", { className: "h-8 w-8 rounded-full payman-agent-avatar flex items-center justify-center", children: /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 payman-agent-avatar-icon" }) }) }),
727
- /* @__PURE__ */ jsxs(
2745
+ /* @__PURE__ */ jsx(
728
2746
  "div",
729
2747
  {
730
2748
  className: cn(
731
2749
  "min-w-0",
732
2750
  layout === "centered" ? "max-w-[85%]" : showAvatar ? "max-w-[85%]" : "max-w-[80%]"
733
2751
  ),
734
- children: [
735
- message.userActionResult && /* @__PURE__ */ jsx("div", { className: "mb-2", children: message.userActionResult === "approved" ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 px-4 py-1.5 text-xs font-semibold rounded-full payman-agent-approved shrink-0", children: [
736
- /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5 shrink-0", strokeWidth: 2.5 }),
737
- "Verified"
738
- ] }) : /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 px-4 py-1.5 text-xs font-semibold rounded-full payman-agent-rejected shrink-0", children: [
739
- /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5 shrink-0", strokeWidth: 2.5 }),
740
- "Rejected"
741
- ] }) }),
742
- /* @__PURE__ */ jsxs(
743
- "div",
744
- {
745
- className: cn(
746
- "overflow-hidden w-full min-w-0 px-4 py-3 rounded-2xl rounded-tl-md transition-all duration-200",
747
- layout === "centered" ? cn(
748
- "payman-agent-bubble payman-agent-bubble--centered",
749
- isStreaming && "streaming",
750
- isError && "error"
751
- ) : cn(
752
- "payman-agent-bubble",
753
- isError && "payman-agent-bubble--error"
754
- )
2752
+ children: /* @__PURE__ */ jsxs(
2753
+ "div",
2754
+ {
2755
+ className: cn(
2756
+ "overflow-hidden w-full min-w-0 px-4 py-3 rounded-2xl rounded-tl-md transition-all duration-200",
2757
+ layout === "centered" ? cn(
2758
+ "payman-agent-bubble payman-agent-bubble--centered",
2759
+ isStreaming && "streaming",
2760
+ isError && "error"
2761
+ ) : cn(
2762
+ "payman-agent-bubble",
2763
+ isError && "payman-agent-bubble--error"
2764
+ )
2765
+ ),
2766
+ children: [
2767
+ showAgentName && /* @__PURE__ */ jsx(
2768
+ "p",
2769
+ {
2770
+ className: cn(
2771
+ "text-sm font-semibold mb-1 leading-none payman-agent-name",
2772
+ isStreaming && !content && "animate-pulse"
2773
+ ),
2774
+ children: isStreaming && !content ? "Thought Process" : agentName
2775
+ }
755
2776
  ),
756
- children: [
757
- showAgentName && /* @__PURE__ */ jsx(
758
- "p",
759
- {
760
- className: cn(
761
- "text-sm font-semibold mb-1 leading-none payman-agent-name",
762
- isStreaming && !content && "animate-pulse"
763
- ),
764
- children: isStreaming && !content ? "Thought Process" : agentName
765
- }
766
- ),
767
- /* @__PURE__ */ jsx(
768
- "div",
769
- {
770
- className: cn(
771
- "text-sm leading-relaxed min-w-0 w-full break-words overflow-wrap-anywhere",
772
- showAgentName && "mt-1"
773
- ),
774
- children: isStreaming && !content ? (
775
- // Streaming without content — show thinking/step indicator
776
- activeThinkingText ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-start", children: [
777
- /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 mt-0.5 payman-agent-thinking-spinner animate-spin shrink-0" }),
778
- /* @__PURE__ */ jsxs("span", { className: "text-sm leading-relaxed min-w-0 break-words payman-agent-thinking-text whitespace-pre-wrap flex-1", children: [
779
- activeThinkingText,
780
- /* @__PURE__ */ jsx("span", { className: "inline-block w-0.5 h-3.5 payman-agent-thinking-cursor animate-pulse ml-0.5 align-text-bottom" })
781
- ] })
782
- ] }) : currentStep ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
783
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 shrink-0", children: renderStepIcon(currentStep, true) }),
784
- /* @__PURE__ */ jsx("span", { className: "text-sm leading-relaxed min-w-0 break-words shimmer-text font-medium flex-1", children: currentStep.message })
785
- ] }) : /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
786
- /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 mt-0.5 payman-agent-thinking-spinner animate-spin shrink-0" }),
787
- /* @__PURE__ */ jsx("span", { className: "text-sm payman-agent-thinking-text flex-1", children: currentMessage || "Thinking..." })
2777
+ /* @__PURE__ */ jsx(
2778
+ "div",
2779
+ {
2780
+ className: cn(
2781
+ "text-sm leading-relaxed min-w-0 w-full break-words overflow-wrap-anywhere",
2782
+ showAgentName && "mt-1"
2783
+ ),
2784
+ children: isStreaming && !content ? (
2785
+ // Streaming without content show thinking/step indicator
2786
+ activeThinkingText ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-start", children: [
2787
+ /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 mt-0.5 payman-agent-thinking-spinner animate-spin shrink-0" }),
2788
+ /* @__PURE__ */ jsxs("span", { className: "text-sm leading-relaxed min-w-0 break-words payman-agent-thinking-text whitespace-pre-wrap flex-1", children: [
2789
+ activeThinkingText,
2790
+ /* @__PURE__ */ jsx("span", { className: "inline-block w-0.5 h-3.5 payman-agent-thinking-cursor animate-pulse ml-0.5 align-text-bottom" })
788
2791
  ] })
789
- ) : isCancelled && !content ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
790
- /* @__PURE__ */ jsx(X, { className: "w-4 h-4 mt-0.5 payman-agent-cancelled-icon shrink-0" }),
791
- /* @__PURE__ */ jsx("span", { className: "text-sm payman-agent-cancelled-text italic flex-1", children: currentMessage || "Request was stopped." })
792
- ] }) : /* @__PURE__ */ jsx(
793
- "div",
794
- {
795
- className: cn(
796
- "payman-markdown payman-agent-markdown prose prose-sm dark:prose-invert max-w-none min-w-0 w-full break-words overflow-wrap-anywhere",
797
- isError && "payman-agent-markdown--error",
798
- isStreaming && content && "streaming-cursor"
799
- ),
800
- children: /* @__PURE__ */ jsx(
801
- ReactMarkdown,
802
- {
803
- remarkPlugins: [remarkGfm],
804
- components: markdownRenderers,
805
- children: isError ? conflictErrorMessage ?? FRIENDLY_ERROR_MESSAGE : content || (isStreaming ? "Thinking..." : isCancelled ? "Request was stopped." : "")
806
- }
807
- )
808
- }
809
- )
810
- }
811
- )
812
- ]
813
- }
814
- )
815
- ]
2792
+ ] }) : currentStep ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
2793
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 shrink-0", children: renderStepIcon(currentStep, true) }),
2794
+ /* @__PURE__ */ jsx("span", { className: "text-sm leading-relaxed min-w-0 break-words shimmer-text font-medium flex-1", children: currentStep.message })
2795
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
2796
+ /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 mt-0.5 payman-agent-thinking-spinner animate-spin shrink-0" }),
2797
+ /* @__PURE__ */ jsx("span", { className: "text-sm payman-agent-thinking-text flex-1", children: currentMessage || "Thinking..." })
2798
+ ] })
2799
+ ) : isCancelled && !content ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
2800
+ /* @__PURE__ */ jsx(X, { className: "w-4 h-4 mt-0.5 payman-agent-cancelled-icon shrink-0" }),
2801
+ /* @__PURE__ */ jsx("span", { className: "text-sm payman-agent-cancelled-text italic flex-1", children: currentMessage || "Request was stopped." })
2802
+ ] }) : /* @__PURE__ */ jsx(
2803
+ "div",
2804
+ {
2805
+ className: cn(
2806
+ "payman-markdown payman-agent-markdown prose prose-sm dark:prose-invert max-w-none min-w-0 w-full break-words overflow-wrap-anywhere",
2807
+ isError && "payman-agent-markdown--error",
2808
+ isStreaming && content && "streaming-cursor"
2809
+ ),
2810
+ children: /* @__PURE__ */ jsx(
2811
+ ReactMarkdown,
2812
+ {
2813
+ remarkPlugins: [remarkGfm],
2814
+ components: markdownRenderers,
2815
+ children: isError ? conflictErrorMessage ?? FRIENDLY_ERROR_MESSAGE2 : content || (isStreaming ? "Thinking..." : isCancelled ? "Request was stopped." : "")
2816
+ }
2817
+ )
2818
+ }
2819
+ )
2820
+ }
2821
+ )
2822
+ ]
2823
+ }
2824
+ )
816
2825
  }
817
2826
  )
818
2827
  ] }),
@@ -2483,56 +4492,61 @@ function OtpInputV2({
2483
4492
  i
2484
4493
  )) });
2485
4494
  }
4495
+ var DEFAULT_CODE_LEN = 6;
2486
4496
  var RESEND_COOLDOWN_S = 30;
2487
- var OTP_LEN = 6;
2488
- function VerificationCardV2({
2489
- messageId,
2490
- action,
2491
- status,
2492
- clearOtpTrigger = 0,
2493
- onApprove,
2494
- onReject,
4497
+ function codeLengthFromSchema(prompt) {
4498
+ const field = prompt.requestedSchema?.properties?.verificationCode;
4499
+ const max = typeof field?.maxLength === "number" ? field.maxLength : void 0;
4500
+ const min = typeof field?.minLength === "number" ? field.minLength : void 0;
4501
+ return max ?? min ?? DEFAULT_CODE_LEN;
4502
+ }
4503
+ function VerificationInline({
4504
+ prompt,
4505
+ secondsLeft,
4506
+ expired,
4507
+ onSubmit,
4508
+ onCancel,
2495
4509
  onResend
2496
4510
  }) {
2497
- const [otp, setOtp] = useState("");
2498
- const [otpErrored, setOtpErrored] = useState(false);
4511
+ const isNumeric = prompt.verificationType !== "ALPHANUMERIC_CODE";
4512
+ const codeLen = useMemo(() => codeLengthFromSchema(prompt), [prompt]);
4513
+ const [code, setCode] = useState("");
4514
+ const [errored, setErrored] = useState(false);
2499
4515
  const [resendSec, setResendSec] = useState(0);
2500
- const [localPending, setLocalPending] = useState(false);
2501
4516
  const lastSubmittedRef = useRef(null);
2502
4517
  const resendTimerRef = useRef(void 0);
4518
+ const status = prompt.status;
4519
+ const busy = status === "submitting";
4520
+ const stale = status === "stale";
4521
+ const locked = busy || stale || expired;
2503
4522
  useEffect(() => {
2504
- if (clearOtpTrigger <= 0) return;
2505
- setOtpErrored(true);
2506
- const t = window.setTimeout(() => {
2507
- setOtp("");
2508
- setOtpErrored(false);
2509
- setLocalPending(false);
2510
- lastSubmittedRef.current = null;
2511
- }, 600);
2512
- return () => window.clearTimeout(t);
2513
- }, [clearOtpTrigger]);
2514
- useEffect(() => {
2515
- if (otp.length < OTP_LEN) {
4523
+ if (prompt.subAction === "SubmissionInvalid") {
4524
+ setErrored(true);
4525
+ setCode("");
2516
4526
  lastSubmittedRef.current = null;
2517
4527
  }
2518
- }, [otp]);
4528
+ }, [prompt.subAction, prompt.userActionId]);
2519
4529
  useEffect(() => {
2520
- if (status !== "pending") {
2521
- setLocalPending(false);
2522
- }
2523
- }, [status]);
4530
+ if (code.length < codeLen) lastSubmittedRef.current = null;
4531
+ }, [code, codeLen]);
4532
+ const doSubmit = useCallback(
4533
+ (value) => {
4534
+ if (locked || !value) return;
4535
+ if (lastSubmittedRef.current === value) return;
4536
+ lastSubmittedRef.current = value;
4537
+ void onSubmit(prompt.userActionId, { verificationCode: value }).catch(() => {
4538
+ lastSubmittedRef.current = null;
4539
+ setErrored(true);
4540
+ });
4541
+ },
4542
+ [locked, onSubmit, prompt.userActionId]
4543
+ );
2524
4544
  useEffect(() => {
2525
- if (otp.length !== OTP_LEN || !/^\d+$/.test(otp) || status !== "pending") {
2526
- return;
4545
+ if (!isNumeric || locked) return;
4546
+ if (code.length === codeLen && /^\d+$/.test(code)) {
4547
+ doSubmit(code);
2527
4548
  }
2528
- if (lastSubmittedRef.current === otp) return;
2529
- lastSubmittedRef.current = otp;
2530
- setLocalPending(true);
2531
- void onApprove(messageId, otp).catch(() => {
2532
- lastSubmittedRef.current = null;
2533
- setLocalPending(false);
2534
- });
2535
- }, [messageId, onApprove, otp, status]);
4549
+ }, [code, codeLen, doSubmit, isNumeric, locked]);
2536
4550
  useEffect(() => {
2537
4551
  return () => {
2538
4552
  if (typeof resendTimerRef.current === "number") {
@@ -2559,94 +4573,309 @@ function VerificationCardV2({
2559
4573
  }, 1e3);
2560
4574
  }, []);
2561
4575
  const handleResend = useCallback(async () => {
2562
- if (resendSec > 0 || status === "verifying" || localPending) return;
2563
- setLocalPending(true);
4576
+ if (locked || resendSec > 0) return;
4577
+ setErrored(false);
4578
+ setCode("");
4579
+ lastSubmittedRef.current = null;
2564
4580
  try {
2565
- await onResend(messageId);
4581
+ await onResend(prompt.userActionId);
2566
4582
  startResendCooldown();
2567
- } finally {
2568
- setLocalPending(false);
2569
- }
2570
- }, [localPending, messageId, onResend, resendSec, startResendCooldown, status]);
2571
- const handleCancel = useCallback(async () => {
2572
- if (status === "verifying" || localPending) return;
2573
- setLocalPending(true);
2574
- try {
2575
- await onReject(messageId);
2576
- } finally {
2577
- setLocalPending(false);
4583
+ } catch {
2578
4584
  }
2579
- }, [localPending, messageId, onReject, status]);
2580
- const busy = status === "verifying" || localPending;
2581
- if (status === "approved" || status === "rejected") {
2582
- return null;
2583
- }
2584
- return /* @__PURE__ */ jsxs(
2585
- motion.div,
2586
- {
2587
- className: "payman-v2-verification",
2588
- initial: { opacity: 0, y: 10 },
2589
- animate: { opacity: 1, y: 0 },
2590
- transition: { type: "spring", stiffness: 320, damping: 28 },
2591
- children: [
2592
- /* @__PURE__ */ jsxs(
2593
- "div",
4585
+ }, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
4586
+ const handleCancel = useCallback(() => {
4587
+ if (busy) return;
4588
+ void onCancel(prompt.userActionId);
4589
+ }, [busy, onCancel, prompt.userActionId]);
4590
+ const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4591
+ return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
4592
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4593
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
4594
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Verification required" }),
4595
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4596
+ ] }),
4597
+ /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: description }),
4598
+ stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4599
+ isNumeric ? /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-field", children: /* @__PURE__ */ jsx(
4600
+ OtpInputV2,
4601
+ {
4602
+ value: code,
4603
+ onChange: (v) => {
4604
+ setErrored(false);
4605
+ setCode(v);
4606
+ },
4607
+ maxLength: codeLen,
4608
+ disabled: locked,
4609
+ error: errored
4610
+ }
4611
+ ) }) : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-field", children: /* @__PURE__ */ jsx(
4612
+ "input",
4613
+ {
4614
+ type: "text",
4615
+ className: cn("payman-v2-ua-input", errored && "payman-v2-ua-input-error"),
4616
+ value: code,
4617
+ disabled: locked,
4618
+ autoComplete: "one-time-code",
4619
+ placeholder: "Verification code",
4620
+ onChange: (e) => {
4621
+ setErrored(false);
4622
+ setCode(e.target.value);
4623
+ },
4624
+ onKeyDown: (e) => {
4625
+ if (e.key === "Enter") {
4626
+ e.preventDefault();
4627
+ doSubmit(code.trim());
4628
+ }
4629
+ }
4630
+ }
4631
+ ) }),
4632
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-actions", children: [
4633
+ !isNumeric && /* @__PURE__ */ jsx(
4634
+ "button",
2594
4635
  {
2595
- className: cn(
2596
- "payman-v2-verification-card",
2597
- busy && "payman-v2-verification-card-busy"
2598
- ),
2599
- children: [
2600
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-verification-header", children: [
2601
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-verification-header-row", children: [
2602
- /* @__PURE__ */ jsx(
2603
- ShieldCheck,
2604
- {
2605
- className: "payman-v2-verification-icon",
2606
- size: 18,
2607
- strokeWidth: 1.75,
2608
- "aria-hidden": true
2609
- }
2610
- ),
2611
- /* @__PURE__ */ jsx("p", { className: "payman-v2-verification-title", children: "Verify" }),
2612
- action.amount != null ? /* @__PURE__ */ jsx("span", { className: "payman-v2-verification-amount", children: action.amount }) : action.payeeName ? /* @__PURE__ */ jsx("span", { className: "payman-v2-verification-payee", children: action.payeeName }) : null
2613
- ] }),
2614
- /* @__PURE__ */ jsx("p", { className: "payman-v2-verification-description", children: "Enter the 6-digit code sent to your phone" }),
2615
- /* @__PURE__ */ jsx(
2616
- OtpInputV2,
2617
- {
2618
- value: otp,
2619
- onChange: setOtp,
2620
- maxLength: OTP_LEN,
2621
- disabled: busy,
2622
- error: otpErrored
2623
- }
2624
- )
2625
- ] }),
2626
- /* @__PURE__ */ jsx("div", { className: "payman-v2-verification-actions", children: /* @__PURE__ */ jsx(
2627
- "button",
2628
- {
2629
- type: "button",
2630
- className: "payman-v2-verification-cancel-btn",
2631
- disabled: busy,
2632
- onClick: () => void handleCancel(),
2633
- children: "Cancel"
2634
- }
2635
- ) })
2636
- ]
4636
+ type: "button",
4637
+ className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
4638
+ disabled: locked || !code.trim(),
4639
+ onClick: () => doSubmit(code.trim()),
4640
+ children: busy ? "Submitting\u2026" : "Submit"
2637
4641
  }
2638
4642
  ),
2639
4643
  /* @__PURE__ */ jsx(
2640
4644
  "button",
2641
4645
  {
2642
4646
  type: "button",
2643
- className: "payman-v2-verification-resend-link",
2644
- disabled: busy || resendSec > 0,
4647
+ className: "payman-v2-ua-link",
4648
+ disabled: locked || resendSec > 0,
2645
4649
  onClick: () => void handleResend(),
2646
4650
  children: resendSec > 0 ? `Resend (${resendSec}s)` : "Resend"
2647
4651
  }
4652
+ ),
4653
+ /* @__PURE__ */ jsx(
4654
+ "button",
4655
+ {
4656
+ type: "button",
4657
+ className: "payman-v2-ua-link payman-v2-ua-link-danger",
4658
+ disabled: busy,
4659
+ onClick: handleCancel,
4660
+ children: "Cancel"
4661
+ }
2648
4662
  )
2649
- ]
4663
+ ] })
4664
+ ] })
4665
+ ] });
4666
+ }
4667
+ function SchemaFormInline({
4668
+ prompt,
4669
+ secondsLeft,
4670
+ expired,
4671
+ onSubmit,
4672
+ onCancel
4673
+ }) {
4674
+ const schema = prompt.requestedSchema;
4675
+ const fields = useMemo(() => renderableFields(schema), [schema]);
4676
+ const [values, setValues] = useState(() => {
4677
+ const init2 = {};
4678
+ for (const [key, field] of fields) init2[key] = defaultValueFor(field);
4679
+ return init2;
4680
+ });
4681
+ const [errors, setErrors] = useState({});
4682
+ const status = prompt.status;
4683
+ const busy = status === "submitting";
4684
+ const stale = status === "stale";
4685
+ const locked = busy || stale || expired;
4686
+ const setValue = (key, value) => {
4687
+ setValues((prev) => ({ ...prev, [key]: value }));
4688
+ setErrors((prev) => {
4689
+ if (!prev[key]) return prev;
4690
+ const next = { ...prev };
4691
+ delete next[key];
4692
+ return next;
4693
+ });
4694
+ };
4695
+ const handleSubmit = () => {
4696
+ if (locked) return;
4697
+ const validation = validateForm(schema, values);
4698
+ if (Object.keys(validation).length > 0) {
4699
+ setErrors(validation);
4700
+ return;
4701
+ }
4702
+ void onSubmit(prompt.userActionId, buildContent(schema, values)).catch(() => {
4703
+ });
4704
+ };
4705
+ return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Action required", children: [
4706
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4707
+ /* @__PURE__ */ jsx(Pencil, { className: "payman-v2-ua-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4708
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4709
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4710
+ ] }),
4711
+ prompt.message?.trim() && /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: prompt.message }),
4712
+ stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: "This action has no inputs to fill." }) : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
4713
+ const widget = classifyField(field);
4714
+ const label = field.title || key;
4715
+ const required = isRequired(schema, key);
4716
+ const err = errors[key];
4717
+ const fieldId = `ua-${prompt.userActionId}-${key}`;
4718
+ if (widget === "boolean") {
4719
+ return /* @__PURE__ */ jsxs("label", { className: "payman-v2-ua-check", children: [
4720
+ /* @__PURE__ */ jsx(
4721
+ "input",
4722
+ {
4723
+ type: "checkbox",
4724
+ checked: Boolean(values[key]),
4725
+ disabled: locked,
4726
+ onChange: (e) => setValue(key, e.target.checked)
4727
+ }
4728
+ ),
4729
+ /* @__PURE__ */ jsxs("span", { children: [
4730
+ label,
4731
+ required && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-req", children: "*" })
4732
+ ] })
4733
+ ] }, key);
4734
+ }
4735
+ return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-row", children: [
4736
+ /* @__PURE__ */ jsxs("label", { htmlFor: fieldId, className: "payman-v2-ua-label", children: [
4737
+ label,
4738
+ required && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-req", children: "*" })
4739
+ ] }),
4740
+ field.description && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-hint", children: field.description }),
4741
+ widget === "select" ? /* @__PURE__ */ jsxs(
4742
+ "select",
4743
+ {
4744
+ id: fieldId,
4745
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4746
+ value: String(values[key] ?? ""),
4747
+ disabled: locked,
4748
+ onChange: (e) => setValue(key, e.target.value),
4749
+ children: [
4750
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select\u2026" }),
4751
+ getOptions(field).map((opt) => /* @__PURE__ */ jsx("option", { value: opt.const, children: opt.title || opt.const }, opt.const))
4752
+ ]
4753
+ }
4754
+ ) : /* @__PURE__ */ jsx(
4755
+ "input",
4756
+ {
4757
+ id: fieldId,
4758
+ type: widget === "integer" || widget === "decimal" ? "number" : "text",
4759
+ inputMode: widget === "integer" ? "numeric" : widget === "decimal" ? "decimal" : void 0,
4760
+ step: widget === "decimal" ? "any" : widget === "integer" ? "1" : void 0,
4761
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4762
+ value: String(values[key] ?? ""),
4763
+ disabled: locked,
4764
+ placeholder: field.description ? void 0 : label,
4765
+ onChange: (e) => setValue(key, e.target.value),
4766
+ onKeyDown: (e) => {
4767
+ if (e.key === "Enter") {
4768
+ e.preventDefault();
4769
+ handleSubmit();
4770
+ }
4771
+ }
4772
+ }
4773
+ ),
4774
+ err && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-error", children: err })
4775
+ ] }, key);
4776
+ }) }),
4777
+ !stale && /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-actions", children: [
4778
+ /* @__PURE__ */ jsx(
4779
+ "button",
4780
+ {
4781
+ type: "button",
4782
+ className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
4783
+ disabled: locked,
4784
+ onClick: handleSubmit,
4785
+ children: busy ? "Submitting\u2026" : "Submit"
4786
+ }
4787
+ ),
4788
+ /* @__PURE__ */ jsx(
4789
+ "button",
4790
+ {
4791
+ type: "button",
4792
+ className: "payman-v2-ua-link payman-v2-ua-link-danger",
4793
+ disabled: busy,
4794
+ onClick: () => void onCancel(prompt.userActionId),
4795
+ children: "Cancel"
4796
+ }
4797
+ )
4798
+ ] })
4799
+ ] });
4800
+ }
4801
+ function NotificationInline({ notification, onDismiss }) {
4802
+ return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-note", role: "status", children: [
4803
+ /* @__PURE__ */ jsx(Info, { className: "payman-v2-ua-note-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4804
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-note-text", children: notification.message }),
4805
+ onDismiss && /* @__PURE__ */ jsx(
4806
+ "button",
4807
+ {
4808
+ type: "button",
4809
+ className: "payman-v2-ua-note-dismiss",
4810
+ "aria-label": "Dismiss notification",
4811
+ onClick: () => onDismiss(notification.id),
4812
+ children: /* @__PURE__ */ jsx(X, { size: 13, strokeWidth: 2 })
4813
+ }
4814
+ )
4815
+ ] });
4816
+ }
4817
+ function useExpiryCountdown(prompt) {
4818
+ const initial = typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0 ? Math.floor(prompt.expirySeconds) : void 0;
4819
+ const [secondsLeft, setSecondsLeft] = useState(initial);
4820
+ useEffect(() => {
4821
+ if (initial === void 0) {
4822
+ setSecondsLeft(void 0);
4823
+ return;
4824
+ }
4825
+ setSecondsLeft(initial);
4826
+ const id = window.setInterval(() => {
4827
+ setSecondsLeft((s) => {
4828
+ if (s === void 0) return s;
4829
+ if (s <= 1) {
4830
+ window.clearInterval(id);
4831
+ return 0;
4832
+ }
4833
+ return s - 1;
4834
+ });
4835
+ }, 1e3);
4836
+ return () => window.clearInterval(id);
4837
+ }, [prompt.userActionId, prompt.subAction, initial]);
4838
+ const expired = initial !== void 0 && secondsLeft === 0;
4839
+ return [secondsLeft, expired];
4840
+ }
4841
+ function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
4842
+ const [secondsLeft, expired] = useExpiryCountdown(prompt);
4843
+ let body;
4844
+ if (prompt.kind === "verification") {
4845
+ body = /* @__PURE__ */ jsx(
4846
+ VerificationInline,
4847
+ {
4848
+ prompt,
4849
+ secondsLeft,
4850
+ expired,
4851
+ onSubmit,
4852
+ onCancel,
4853
+ onResend
4854
+ }
4855
+ );
4856
+ } else if (prompt.kind === "notification") {
4857
+ const note = { id: prompt.userActionId, message: prompt.message ?? "" };
4858
+ body = /* @__PURE__ */ jsx(NotificationInline, { notification: note });
4859
+ } else {
4860
+ body = /* @__PURE__ */ jsx(
4861
+ SchemaFormInline,
4862
+ {
4863
+ prompt,
4864
+ secondsLeft,
4865
+ expired,
4866
+ onSubmit,
4867
+ onCancel
4868
+ }
4869
+ );
4870
+ }
4871
+ return /* @__PURE__ */ jsx(
4872
+ motion.div,
4873
+ {
4874
+ className: "payman-v2-ua-wrap",
4875
+ initial: { opacity: 0, y: 8 },
4876
+ animate: { opacity: 1, y: 0 },
4877
+ transition: { type: "spring", stiffness: 320, damping: 28 },
4878
+ children: body
2650
4879
  }
2651
4880
  );
2652
4881
  }
@@ -2662,13 +4891,17 @@ var MessageListV2 = forwardRef(
2662
4891
  onExecutionTraceClick,
2663
4892
  messageActions,
2664
4893
  retryDisabled = false,
2665
- userAction,
2666
- onApproveAction,
2667
- onRejectAction,
2668
- onResendAction,
4894
+ userActionPrompts,
4895
+ notifications,
4896
+ onSubmitUserAction,
4897
+ onCancelUserAction,
4898
+ onResendUserAction,
4899
+ onDismissNotification,
2669
4900
  onSubmitFeedback,
2670
4901
  typingSpeed = 4
2671
4902
  }, ref) {
4903
+ const noop = useCallback(async () => {
4904
+ }, []);
2672
4905
  const scrollRef = useRef(null);
2673
4906
  const scrollInnerRef = useRef(null);
2674
4907
  const isNearBottomRef = useRef(true);
@@ -2811,20 +5044,24 @@ var MessageListV2 = forwardRef(
2811
5044
  typingSpeed
2812
5045
  }
2813
5046
  ) }, message.id)),
2814
- userAction && userAction.status !== "approved" && userAction.status !== "rejected" && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
2815
- VerificationCardV2,
5047
+ notifications?.map((note) => /* @__PURE__ */ jsx(
5048
+ NotificationInline,
2816
5049
  {
2817
- messageId: userAction.messageId,
2818
- action: userAction.action,
2819
- status: userAction.status,
2820
- onApprove: onApproveAction ?? (async () => {
2821
- }),
2822
- onReject: onRejectAction ?? (async () => {
2823
- }),
2824
- onResend: onResendAction ?? (async () => {
2825
- })
2826
- }
2827
- ) })
5050
+ notification: note,
5051
+ onDismiss: onDismissNotification
5052
+ },
5053
+ note.id
5054
+ )),
5055
+ userActionPrompts?.map((prompt) => /* @__PURE__ */ jsx(
5056
+ UserActionInline,
5057
+ {
5058
+ prompt,
5059
+ onSubmit: onSubmitUserAction ?? noop,
5060
+ onCancel: onCancelUserAction ?? noop,
5061
+ onResend: onResendUserAction ?? noop
5062
+ },
5063
+ prompt.toolCallId || prompt.userActionId
5064
+ ))
2828
5065
  ]
2829
5066
  }
2830
5067
  )
@@ -3885,10 +6122,13 @@ function TimelineBars({
3885
6122
  ] });
3886
6123
  }
3887
6124
  var DEFAULT_USER_ACTION_STATE = {
3888
- request: null,
3889
- result: null};
6125
+ prompts: [],
6126
+ notifications: []
6127
+ };
3890
6128
  var NOOP_ASYNC = async () => {
3891
6129
  };
6130
+ var NOOP = () => {
6131
+ };
3892
6132
  function useSentryChatCallbacks(callbacks, config) {
3893
6133
  const sentryCtxRef = useRef({});
3894
6134
  const callbacksRef = useRef(callbacks);
@@ -3958,7 +6198,7 @@ function useSentryChatCallbacks(callbacks, config) {
3958
6198
  onAttachFileClick: () => callbacksRef.current?.onAttachFileClick?.(),
3959
6199
  onMessageFeedback: (data) => callbacksRef.current?.onMessageFeedback?.(data),
3960
6200
  onUserActionRequired: (req) => callbacksRef.current?.onUserActionRequired?.(req),
3961
- onUserActionEvent: (evType, msg) => callbacksRef.current?.onUserActionEvent?.(evType, msg),
6201
+ onUserNotification: (note) => callbacksRef.current?.onUserNotification?.(note),
3962
6202
  onStatusMessage: (msg) => callbacksRef.current?.onStatusMessage?.(msg),
3963
6203
  onStepsUpdate: (steps) => callbacksRef.current?.onStepsUpdate?.(steps)
3964
6204
  }),
@@ -4026,10 +6266,11 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
4026
6266
  }
4027
6267
  }, [editingMessageId, messages]);
4028
6268
  const userActionState = chat.userActionState ?? DEFAULT_USER_ACTION_STATE;
4029
- const approveUserAction = chat.approveUserAction ?? NOOP_ASYNC;
4030
- const rejectUserAction = chat.rejectUserAction ?? NOOP_ASYNC;
4031
- const resendOtp = chat.resendOtp ?? NOOP_ASYNC;
4032
- const isUserActionSupported = typeof chat.approveUserAction === "function" && typeof chat.rejectUserAction === "function" && typeof chat.resendOtp === "function";
6269
+ const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
6270
+ const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
6271
+ const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6272
+ const dismissNotification = chat.dismissNotification ?? NOOP;
6273
+ const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
4033
6274
  const {
4034
6275
  transcribedText,
4035
6276
  isAvailable: voiceAvailable,
@@ -4270,23 +6511,8 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
4270
6511
  setLightboxSrc(src);
4271
6512
  setLightboxAlt(alt);
4272
6513
  };
4273
- const v2UserAction = useMemo(() => {
4274
- if (!isUserActionSupported || !userActionState.request) return null;
4275
- const req = userActionState.request;
4276
- let status = "pending";
4277
- if (userActionState.result === "approved") status = "approved";
4278
- else if (userActionState.result === "rejected") status = "rejected";
4279
- return {
4280
- messageId: `ua-${req.userActionId || Date.now()}`,
4281
- action: {
4282
- type: req.userActionType || "generic",
4283
- message: req.message || "Verify this action",
4284
- amount: req.metadata?.amount,
4285
- payeeName: req.metadata?.payeeName
4286
- },
4287
- status
4288
- };
4289
- }, [isUserActionSupported, userActionState.request, userActionState.result]);
6514
+ const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
6515
+ const notifications = userActionState.notifications;
4290
6516
  const handleV2Send = (text) => {
4291
6517
  if (isRecording) stopRecording();
4292
6518
  if (text.trim() && !disableInput && isSessionParamsConfigured) {
@@ -4438,16 +6664,12 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
4438
6664
  messageActions,
4439
6665
  retryDisabled: isWaitingForResponse,
4440
6666
  typingSpeed: config.typingSpeed ?? 4,
4441
- userAction: v2UserAction,
4442
- onApproveAction: isUserActionSupported ? async (_msgId, otp) => {
4443
- await approveUserAction(otp);
4444
- } : void 0,
4445
- onRejectAction: isUserActionSupported ? async () => {
4446
- await rejectUserAction();
4447
- } : void 0,
4448
- onResendAction: isUserActionSupported ? async () => {
4449
- await resendOtp();
4450
- } : void 0,
6667
+ userActionPrompts,
6668
+ notifications,
6669
+ onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6670
+ onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6671
+ onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6672
+ onDismissNotification: dismissNotification,
4451
6673
  onSubmitFeedback: handleSubmitFeedback
4452
6674
  }
4453
6675
  ),
@@ -4530,6 +6752,6 @@ var PaymanChat = forwardRef(
4530
6752
  }
4531
6753
  );
4532
6754
 
4533
- export { PaymanChat, PaymanChatContext, captureSentryError, cn, formatDate, usePaymanChat };
6755
+ export { PaymanChat, PaymanChatContext, UserActionStaleError, buildContent, buildFormattedThinking, cancelUserAction, captureSentryError, classifyField, classifyUserActionKind, cn, coerceValue, createInitialV2State, defaultValueFor, formatDate, generateId, getOptions, isNestedOrUnsupported, isRequired, migrateActiveStream, processStreamEventV2, renderableFields, resendUserAction, streamWorkflowEvents, submitUserAction, useChatV2, usePaymanChat, useVoice, validateField, validateForm };
4534
6756
  //# sourceMappingURL=index.mjs.map
4535
6757
  //# sourceMappingURL=index.mjs.map