@paymanai/payman-ask-sdk 4.0.11 → 4.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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, FileText, 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,1963 @@ 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 createCancelledMessageUpdate(steps, currentMessage) {
796
+ const updatedSteps = steps.map((step) => {
797
+ if (step.status === "in_progress") {
798
+ return { ...step, status: "pending" };
799
+ }
800
+ return step;
801
+ });
802
+ return {
803
+ isStreaming: false,
804
+ isCancelled: true,
805
+ steps: updatedSteps,
806
+ currentExecutingStepId: void 0,
807
+ // Preserve currentMessage so UI can show it with X icon
808
+ currentMessage: currentMessage || "Thinking..."
809
+ };
810
+ }
811
+ var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
812
+ function buildRequestBody(config, userMessage, sessionId, options) {
813
+ const sessionOwner = config.sessionParams;
814
+ const sessionOwnerId = sessionOwner?.id?.trim();
815
+ if (!sessionOwnerId) {
816
+ throw new Error("ChatConfig.sessionParams.id is required to send ask requests.");
817
+ }
818
+ const sessionAttributes = sessionOwner?.attributes && Object.keys(sessionOwner.attributes).length > 0 ? sessionOwner.attributes : void 0;
819
+ return {
820
+ agentId: config.agentId,
821
+ userInput: userMessage,
822
+ sessionId,
823
+ sessionOwnerId,
824
+ sessionOwnerLabel: sessionOwner?.name || void 0,
825
+ sessionAttributes,
826
+ analysisMode: options?.analysisMode,
827
+ locale: resolveLocale(config.locale),
828
+ timezone: resolveTimezone(config.timezone)
829
+ };
830
+ }
831
+ function resolveLocale(configured) {
832
+ if (configured && configured.trim().length > 0) return configured.trim();
833
+ if (typeof navigator !== "undefined") {
834
+ const navLocale = navigator.language;
835
+ if (navLocale && navLocale.length > 0) return navLocale;
836
+ }
837
+ return "en-US";
838
+ }
839
+ function resolveTimezone(configured) {
840
+ if (configured && configured.trim().length > 0) return configured.trim();
841
+ try {
842
+ const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
843
+ if (zone && zone.length > 0) return zone;
844
+ } catch {
845
+ }
846
+ return "UTC";
847
+ }
848
+ function buildStreamingUrl(config) {
849
+ const endpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
850
+ const stage = config.stage || "DEV";
851
+ const stageParamName = config.stageQueryParam ?? "stage";
852
+ const queryParams = new URLSearchParams({ [stageParamName]: stage });
853
+ return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
854
+ }
855
+ function buildUserActionUrl(config, userActionId, action) {
856
+ const endpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
857
+ const [endpointPath] = endpoint.split("?");
858
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
859
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
860
+ const encodedUserActionId = encodeURIComponent(userActionId);
861
+ return `${config.api.baseUrl}${basePath}/user-action/${encodedUserActionId}/${action}`;
862
+ }
863
+ function buildResolveImagesUrl(config) {
864
+ if (config.api.resolveImagesEndpoint) {
865
+ return `${config.api.baseUrl}${config.api.resolveImagesEndpoint}`;
866
+ }
867
+ const streamEndpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
868
+ const [endpointPath] = streamEndpoint.split("?");
869
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
870
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
871
+ return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
872
+ }
873
+ function buildRequestHeaders(config) {
874
+ const headers = {
875
+ ...config.api.headers
876
+ };
877
+ if (config.api.authToken) {
878
+ headers.Authorization = `Bearer ${config.api.authToken}`;
879
+ }
880
+ return headers;
881
+ }
882
+ var UserActionStaleError = class extends Error {
883
+ constructor(userActionId, message = "User action is no longer actionable") {
884
+ super(message);
885
+ __publicField(this, "userActionId");
886
+ this.name = "UserActionStaleError";
887
+ this.userActionId = userActionId;
888
+ }
889
+ };
890
+ async function sendUserActionRequest(config, userActionId, action, data) {
891
+ const url = buildUserActionUrl(config, userActionId, action);
892
+ const baseHeaders = buildRequestHeaders(config);
893
+ const hasBody = data !== void 0;
894
+ const headers = hasBody ? { "Content-Type": "application/json", ...baseHeaders } : baseHeaders;
895
+ const response = await fetch(url, {
896
+ method: "POST",
897
+ headers,
898
+ body: hasBody ? JSON.stringify(data) : void 0
899
+ });
900
+ if (response.status === 404) {
901
+ throw new UserActionStaleError(userActionId);
902
+ }
903
+ if (!response.ok) {
904
+ const errorText = await response.text();
905
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
906
+ }
907
+ return await response.json();
908
+ }
909
+ async function submitUserAction(config, userActionId, content) {
910
+ return sendUserActionRequest(config, userActionId, "submit", content ?? {});
911
+ }
912
+ async function cancelUserAction(config, userActionId) {
913
+ return sendUserActionRequest(config, userActionId, "cancel");
914
+ }
915
+ async function resendUserAction(config, userActionId) {
916
+ return sendUserActionRequest(config, userActionId, "resend");
917
+ }
918
+ var memoryStore = /* @__PURE__ */ new Map();
919
+ var chatStore = {
920
+ get(key) {
921
+ return memoryStore.get(key) ?? [];
922
+ },
923
+ set(key, messages) {
924
+ memoryStore.set(key, messages);
925
+ },
926
+ delete(key) {
927
+ memoryStore.delete(key);
928
+ }
929
+ };
930
+ var streams = /* @__PURE__ */ new Map();
931
+ var activeStreamStore = {
932
+ has(key) {
933
+ return streams.has(key);
934
+ },
935
+ get(key) {
936
+ const entry = streams.get(key);
937
+ if (!entry) return null;
938
+ return { messages: entry.messages, isWaiting: entry.isWaiting };
939
+ },
940
+ // Called before startStream — registers the controller and initial messages
941
+ start(key, abortController, initialMessages) {
942
+ const existing = streams.get(key);
943
+ streams.set(key, {
944
+ messages: initialMessages,
945
+ isWaiting: true,
946
+ abortController,
947
+ listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
948
+ });
949
+ },
950
+ // Called by the stream on every event — applies the same updater pattern React uses
951
+ applyMessages(key, updater) {
952
+ const entry = streams.get(key);
953
+ if (!entry) return;
954
+ const next = typeof updater === "function" ? updater(entry.messages) : updater;
955
+ entry.messages = next;
956
+ entry.listeners.forEach((l) => l(next, entry.isWaiting));
957
+ },
958
+ setWaiting(key, waiting) {
959
+ const entry = streams.get(key);
960
+ if (!entry) return;
961
+ entry.isWaiting = waiting;
962
+ entry.listeners.forEach((l) => l(entry.messages, waiting));
963
+ },
964
+ // Called when stream completes — persists to chatStore and cleans up
965
+ complete(key) {
966
+ const entry = streams.get(key);
967
+ if (!entry) return;
968
+ entry.isWaiting = false;
969
+ entry.listeners.forEach((l) => l(entry.messages, false));
970
+ const toSave = entry.messages.filter((m) => !m.isStreaming);
971
+ if (toSave.length > 0) chatStore.set(key, toSave);
972
+ streams.delete(key);
973
+ },
974
+ // Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
975
+ subscribe(key, listener) {
976
+ const entry = streams.get(key);
977
+ if (!entry) return () => {
978
+ };
979
+ entry.listeners.add(listener);
980
+ return () => {
981
+ streams.get(key)?.listeners.delete(listener);
982
+ };
983
+ },
984
+ // Rename an entry — used when the server assigns a new session ID mid-stream
985
+ rename(oldKey, newKey) {
986
+ const entry = streams.get(oldKey);
987
+ if (!entry) return;
988
+ streams.set(newKey, entry);
989
+ streams.delete(oldKey);
990
+ },
991
+ // Explicit user cancel — aborts the controller and removes the entry
992
+ abort(key) {
993
+ const entry = streams.get(key);
994
+ if (!entry) return;
995
+ entry.abortController.abort();
996
+ streams.delete(key);
997
+ }
998
+ };
999
+ var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
1000
+ function hasRagImages(content) {
1001
+ return RAG_IMAGE_REGEX.test(content);
1002
+ }
1003
+ async function waitForNextPaint(signal) {
1004
+ if (signal?.aborted) return;
1005
+ await new Promise((resolve) => {
1006
+ let isSettled = false;
1007
+ const finish = () => {
1008
+ if (isSettled) return;
1009
+ isSettled = true;
1010
+ signal?.removeEventListener("abort", finish);
1011
+ resolve();
1012
+ };
1013
+ signal?.addEventListener("abort", finish, { once: true });
1014
+ if (typeof requestAnimationFrame === "function") {
1015
+ requestAnimationFrame(() => {
1016
+ setTimeout(finish, 0);
1017
+ });
1018
+ return;
1019
+ }
1020
+ setTimeout(finish, 0);
1021
+ });
1022
+ }
1023
+ async function resolveRagImageUrls(config, content, signal) {
1024
+ const url = buildResolveImagesUrl(config);
1025
+ const baseHeaders = buildRequestHeaders(config);
1026
+ const headers = { "Content-Type": "application/json", ...baseHeaders };
1027
+ const response = await fetch(url, {
1028
+ method: "POST",
1029
+ headers,
1030
+ body: JSON.stringify({ input: content }),
1031
+ signal
1032
+ });
1033
+ if (!response.ok) {
1034
+ const errorText = await response.text();
1035
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
1036
+ }
1037
+ const text = await response.text();
1038
+ try {
1039
+ const parsed = JSON.parse(text);
1040
+ if (typeof parsed === "string") return parsed;
1041
+ if (typeof parsed.output === "string") return parsed.output;
1042
+ if (typeof parsed.result === "string") return parsed.result;
1043
+ } catch {
1044
+ }
1045
+ return text;
1046
+ }
1047
+ var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
1048
+ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForResponse) {
1049
+ const abortControllerRef = useRef(null);
1050
+ const configRef = useRef(config);
1051
+ configRef.current = config;
1052
+ const callbacksRef = useRef(callbacks);
1053
+ callbacksRef.current = callbacks;
1054
+ const startStream = useCallback(
1055
+ async (userMessage, streamingId, sessionId, externalAbortController, options) => {
1056
+ abortControllerRef.current?.abort();
1057
+ const abortController = externalAbortController ?? new AbortController();
1058
+ abortControllerRef.current = abortController;
1059
+ const state = createInitialV2State();
1060
+ const updateMessage = (update) => {
1061
+ if (abortController.signal.aborted) return;
1062
+ setMessages(
1063
+ (prev) => prev.map(
1064
+ (msg) => msg.id === streamingId ? { ...msg, ...update } : msg
1065
+ )
1066
+ );
1067
+ };
1068
+ try {
1069
+ const currentConfig = configRef.current;
1070
+ const requestBody = buildRequestBody(
1071
+ currentConfig,
1072
+ userMessage,
1073
+ sessionId,
1074
+ options
1075
+ );
1076
+ const url = buildStreamingUrl(currentConfig);
1077
+ const headers = buildRequestHeaders(currentConfig);
1078
+ await streamWorkflowEvents(url, requestBody, headers, {
1079
+ signal: abortController.signal,
1080
+ onEvent: (event) => {
1081
+ if (abortController.signal.aborted) return;
1082
+ processStreamEventV2(event, state);
1083
+ if (state.lastUserAction) {
1084
+ callbacksRef.current.onUserActionRequired?.(state.lastUserAction);
1085
+ }
1086
+ if (state.lastNotification) {
1087
+ callbacksRef.current.onUserNotification?.(state.lastNotification);
1088
+ }
1089
+ const activeStep = state.steps.find((s) => s.id === state.currentExecutingStepId);
1090
+ const lastInProgressStep = [...state.steps].reverse().find((s) => s.status === "in_progress");
1091
+ const useful = (m) => m && !isBlandStatus(m) ? m : void 0;
1092
+ const latestUsefulStep = [...state.steps].reverse().find(
1093
+ (s) => s.message && !isBlandStatus(s.message)
1094
+ );
1095
+ const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
1096
+ // nothing useful seen yet). Render the bland label so the
1097
+ // bubble isn't blank.
1098
+ activeStep?.message ?? lastInProgressStep?.message ?? getEventMessage(event);
1099
+ if (currentMessage) {
1100
+ callbacksRef.current.onStatusMessage?.(currentMessage);
1101
+ }
1102
+ callbacksRef.current.onStepsUpdate?.([...state.steps]);
1103
+ if (state.hasError) {
1104
+ updateMessage({
1105
+ streamingContent: FRIENDLY_ERROR_MESSAGE,
1106
+ content: FRIENDLY_ERROR_MESSAGE,
1107
+ streamProgress: "error",
1108
+ isError: true,
1109
+ errorDetails: state.errorMessage,
1110
+ formattedThinkingText: state.formattedThinkingText || void 0,
1111
+ steps: [...state.steps],
1112
+ currentExecutingStepId: void 0,
1113
+ executionId: state.executionId,
1114
+ sessionId: state.sessionId
1115
+ });
1116
+ } else {
1117
+ updateMessage({
1118
+ streamingContent: state.finalResponse,
1119
+ content: "",
1120
+ currentMessage,
1121
+ streamProgress: "processing",
1122
+ isError: false,
1123
+ formattedThinkingText: state.formattedThinkingText || void 0,
1124
+ steps: [...state.steps],
1125
+ currentExecutingStepId: state.currentExecutingStepId,
1126
+ executionId: state.executionId,
1127
+ sessionId: state.sessionId,
1128
+ isCancelled: false
1129
+ });
1130
+ }
1131
+ },
1132
+ onError: (error) => {
1133
+ setIsWaitingForResponse(false);
1134
+ callbacksRef.current.onStatusMessage?.(null);
1135
+ if (error.name !== "AbortError") {
1136
+ callbacksRef.current.onError?.(error);
1137
+ }
1138
+ const isAborted = error.name === "AbortError";
1139
+ setMessages(
1140
+ (prev) => prev.map(
1141
+ (msg) => msg.id === streamingId ? {
1142
+ ...msg,
1143
+ isStreaming: false,
1144
+ streamProgress: isAborted ? "processing" : "error",
1145
+ isError: !isAborted,
1146
+ isCancelled: isAborted,
1147
+ errorDetails: isAborted ? void 0 : error.message,
1148
+ content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1149
+ currentMessage: isAborted ? "Thinking..." : void 0,
1150
+ formattedThinkingText: state.formattedThinkingText || void 0,
1151
+ steps: [...state.steps].map((step) => {
1152
+ if (step.status === "in_progress" && isAborted) {
1153
+ return { ...step, status: "pending" };
1154
+ }
1155
+ return step;
1156
+ }),
1157
+ currentExecutingStepId: void 0
1158
+ } : msg
1159
+ )
1160
+ );
1161
+ },
1162
+ onComplete: () => {
1163
+ setIsWaitingForResponse(false);
1164
+ callbacksRef.current.onStatusMessage?.(null);
1165
+ callbacksRef.current.onStepsUpdate?.([]);
1166
+ if (state.sessionId && state.sessionId !== sessionId) {
1167
+ callbacksRef.current.onSessionIdChange?.(state.sessionId);
1168
+ }
1169
+ const needsImageResolve = !state.hasError && !abortController.signal.aborted && hasRagImages(state.finalResponse);
1170
+ const finalMessage = {
1171
+ id: streamingId,
1172
+ sessionId: state.sessionId || sessionId,
1173
+ role: "assistant",
1174
+ content: state.hasError ? FRIENDLY_ERROR_MESSAGE : state.finalResponse || "",
1175
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1176
+ isStreaming: false,
1177
+ streamProgress: state.hasError ? "error" : "completed",
1178
+ isError: state.hasError,
1179
+ errorDetails: state.hasError ? state.errorMessage : void 0,
1180
+ executionId: state.executionId,
1181
+ // Defensive: tracingData must be an object (or undefined)
1182
+ // so the JSON viewer doesn't char-iterate a string. The
1183
+ // event processor already drops bare strings; this is
1184
+ // the last stop before the message leaves the SDK.
1185
+ tracingData: state.finalData != null && typeof state.finalData === "object" ? state.finalData : void 0,
1186
+ steps: state.hasError ? [] : [...state.steps],
1187
+ isCancelled: false,
1188
+ currentExecutingStepId: void 0,
1189
+ formattedThinkingText: state.hasError ? void 0 : state.formattedThinkingText || void 0,
1190
+ isResolvingImages: needsImageResolve,
1191
+ totalElapsedMs: state.hasError ? void 0 : state.totalElapsedMs
1192
+ };
1193
+ setMessages(
1194
+ (prev) => prev.map(
1195
+ (msg) => msg.id === streamingId ? finalMessage : msg
1196
+ )
1197
+ );
1198
+ callbacksRef.current.onStreamComplete?.(finalMessage);
1199
+ }
1200
+ });
1201
+ const shouldResolveImages = !abortController.signal.aborted && !state.hasError && hasRagImages(state.finalResponse);
1202
+ if (shouldResolveImages) {
1203
+ await waitForNextPaint(abortController.signal);
1204
+ }
1205
+ if (shouldResolveImages && !abortController.signal.aborted) {
1206
+ try {
1207
+ const resolvedContent = await resolveRagImageUrls(
1208
+ currentConfig,
1209
+ state.finalResponse,
1210
+ abortController.signal
1211
+ );
1212
+ setMessages(
1213
+ (prev) => prev.map(
1214
+ (msg) => msg.id === streamingId ? { ...msg, content: resolvedContent, isResolvingImages: false } : msg
1215
+ )
1216
+ );
1217
+ } catch {
1218
+ setMessages(
1219
+ (prev) => prev.map(
1220
+ (msg) => msg.id === streamingId ? { ...msg, isResolvingImages: false } : msg
1221
+ )
1222
+ );
1223
+ }
1224
+ }
1225
+ return state.sessionId;
1226
+ } catch (error) {
1227
+ setIsWaitingForResponse(false);
1228
+ if (error.name !== "AbortError") {
1229
+ callbacksRef.current.onError?.(error);
1230
+ }
1231
+ const isAborted = error.name === "AbortError";
1232
+ setMessages(
1233
+ (prev) => prev.map(
1234
+ (msg) => msg.id === streamingId ? {
1235
+ ...msg,
1236
+ isStreaming: false,
1237
+ streamProgress: isAborted ? "processing" : "error",
1238
+ isError: !isAborted,
1239
+ isCancelled: isAborted,
1240
+ errorDetails: isAborted ? void 0 : error.message,
1241
+ content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1242
+ formattedThinkingText: state.formattedThinkingText || void 0,
1243
+ steps: [...state.steps].map((step) => {
1244
+ if (step.status === "in_progress" && isAborted) {
1245
+ return { ...step, status: "pending" };
1246
+ }
1247
+ return step;
1248
+ }),
1249
+ currentExecutingStepId: void 0
1250
+ } : msg
1251
+ )
1252
+ );
1253
+ return state.sessionId;
1254
+ }
1255
+ },
1256
+ [setMessages, setIsWaitingForResponse]
1257
+ );
1258
+ const cancelStream = useCallback(() => {
1259
+ abortControllerRef.current?.abort();
1260
+ }, []);
1261
+ return {
1262
+ startStream,
1263
+ cancelStream,
1264
+ abortControllerRef
1265
+ };
1266
+ }
1267
+ var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1268
+ function upsertPrompt(prompts, req) {
1269
+ const active = { ...req, status: "pending" };
1270
+ const idx = prompts.findIndex(
1271
+ (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
1272
+ );
1273
+ if (idx >= 0) {
1274
+ const next = prompts.slice();
1275
+ next[idx] = active;
1276
+ return next;
1277
+ }
1278
+ return [...prompts, active];
1279
+ }
1280
+ function getStoredOrInitialMessages(config) {
1281
+ if (!config.userId) return config.initialMessages ?? [];
1282
+ const activeStream = activeStreamStore.get(config.userId);
1283
+ if (activeStream) return activeStream.messages;
1284
+ const stored = chatStore.get(config.userId);
1285
+ if (stored.length > 0) return stored;
1286
+ if (config.initialMessages?.length) {
1287
+ chatStore.set(config.userId, config.initialMessages);
1288
+ return config.initialMessages;
1289
+ }
1290
+ return [];
1291
+ }
1292
+ function getSessionIdFromMessages(messages) {
1293
+ return messages.find((message) => message.sessionId)?.sessionId;
1294
+ }
1295
+ function useChatV2(config, callbacks = {}) {
1296
+ const [messages, setMessages] = useState(() => getStoredOrInitialMessages(config));
1297
+ const [isWaitingForResponse, setIsWaitingForResponse] = useState(() => {
1298
+ if (!config.userId) return false;
1299
+ return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1300
+ });
1301
+ const sessionIdRef = useRef(
1302
+ getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1303
+ );
1304
+ const prevUserIdRef = useRef(config.userId);
1305
+ const streamUserIdRef = useRef(void 0);
1306
+ const subscriptionPrevUserIdRef = useRef(config.userId);
1307
+ const callbacksRef = useRef(callbacks);
1308
+ callbacksRef.current = callbacks;
1309
+ const configRef = useRef(config);
1310
+ configRef.current = config;
1311
+ const messagesRef = useRef(messages);
1312
+ messagesRef.current = messages;
1313
+ const storeAwareSetMessages = useCallback(
1314
+ (updater) => {
1315
+ const streamUserId = streamUserIdRef.current;
1316
+ const currentUserId = configRef.current.userId;
1317
+ const storeKey = streamUserId ?? currentUserId;
1318
+ if (storeKey && activeStreamStore.has(storeKey)) {
1319
+ activeStreamStore.applyMessages(storeKey, updater);
1320
+ }
1321
+ if (!streamUserId || streamUserId === currentUserId) {
1322
+ setMessages(updater);
1323
+ }
1324
+ },
1325
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1326
+ []
1327
+ );
1328
+ const storeAwareSetIsWaiting = useCallback(
1329
+ (waiting) => {
1330
+ const streamUserId = streamUserIdRef.current;
1331
+ const currentUserId = configRef.current.userId;
1332
+ const storeKey = streamUserId ?? currentUserId;
1333
+ if (storeKey && activeStreamStore.has(storeKey)) {
1334
+ activeStreamStore.setWaiting(storeKey, waiting);
1335
+ }
1336
+ if (!streamUserId || streamUserId === currentUserId) {
1337
+ setIsWaitingForResponse(waiting);
1338
+ }
1339
+ },
1340
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1341
+ []
1342
+ );
1343
+ const [userActionState, setUserActionState] = useState(EMPTY_USER_ACTION_STATE);
1344
+ const wrappedCallbacks = useMemo(() => ({
1345
+ ...callbacksRef.current,
1346
+ onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
1347
+ onStreamStart: () => callbacksRef.current.onStreamStart?.(),
1348
+ onStreamComplete: (message) => callbacksRef.current.onStreamComplete?.(message),
1349
+ onError: (error) => callbacksRef.current.onError?.(error),
1350
+ onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1351
+ onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1352
+ onUserActionRequired: (request) => {
1353
+ setUserActionState((prev) => ({
1354
+ ...prev,
1355
+ prompts: upsertPrompt(prev.prompts, request)
1356
+ }));
1357
+ callbacksRef.current.onUserActionRequired?.(request);
1358
+ },
1359
+ onUserNotification: (notification) => {
1360
+ setUserActionState(
1361
+ (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1362
+ );
1363
+ callbacksRef.current.onUserNotification?.(notification);
1364
+ }
1365
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1366
+ }), []);
1367
+ const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManagerV2(
1368
+ config,
1369
+ wrappedCallbacks,
1370
+ storeAwareSetMessages,
1371
+ storeAwareSetIsWaiting
1372
+ );
1373
+ const sendMessage = useCallback(
1374
+ async (userMessage, options) => {
1375
+ if (!userMessage.trim()) return;
1376
+ if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1377
+ sessionIdRef.current = generateId();
1378
+ callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1379
+ }
1380
+ const userMessageId = `user-${Date.now()}`;
1381
+ const userMsg = {
1382
+ id: userMessageId,
1383
+ sessionId: sessionIdRef.current,
1384
+ role: "user",
1385
+ content: userMessage,
1386
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1387
+ };
1388
+ setMessages((prev) => [...prev, userMsg]);
1389
+ callbacksRef.current.onMessageSent?.(userMessage);
1390
+ setIsWaitingForResponse(true);
1391
+ callbacksRef.current.onStreamStart?.();
1392
+ const streamingId = `assistant-${Date.now()}`;
1393
+ const streamingMsg = {
1394
+ id: streamingId,
1395
+ sessionId: sessionIdRef.current,
1396
+ role: "assistant",
1397
+ content: "",
1398
+ streamingContent: "",
1399
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1400
+ isStreaming: true,
1401
+ streamProgress: "started",
1402
+ steps: [],
1403
+ currentExecutingStepId: void 0,
1404
+ isCancelled: false,
1405
+ currentMessage: void 0
1406
+ };
1407
+ setMessages((prev) => [...prev, streamingMsg]);
1408
+ const abortController = new AbortController();
1409
+ const { userId } = configRef.current;
1410
+ if (userId) {
1411
+ streamUserIdRef.current = userId;
1412
+ const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
1413
+ activeStreamStore.start(userId, abortController, initialMessages);
1414
+ }
1415
+ const newSessionId = await startStream(
1416
+ userMessage,
1417
+ streamingId,
1418
+ sessionIdRef.current,
1419
+ abortController,
1420
+ options
1421
+ );
1422
+ const finalStreamUserId = streamUserIdRef.current ?? userId;
1423
+ if (finalStreamUserId) {
1424
+ activeStreamStore.complete(finalStreamUserId);
1425
+ }
1426
+ streamUserIdRef.current = void 0;
1427
+ if (!abortController.signal.aborted && newSessionId && newSessionId !== sessionIdRef.current) {
1428
+ sessionIdRef.current = newSessionId;
1429
+ }
1430
+ },
1431
+ [startStream]
1432
+ );
1433
+ const clearMessages = useCallback(() => {
1434
+ if (configRef.current.userId) {
1435
+ chatStore.delete(configRef.current.userId);
1436
+ }
1437
+ setMessages([]);
1438
+ }, []);
1439
+ const prependMessages = useCallback((msgs) => {
1440
+ setMessages((prev) => [...msgs, ...prev]);
1441
+ }, []);
1442
+ const cancelStream = useCallback(() => {
1443
+ const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1444
+ if (streamUserId) {
1445
+ activeStreamStore.abort(streamUserId);
1446
+ }
1447
+ streamUserIdRef.current = void 0;
1448
+ cancelStreamManager();
1449
+ setIsWaitingForResponse(false);
1450
+ setUserActionState((prev) => ({ ...prev, prompts: [] }));
1451
+ setMessages(
1452
+ (prev) => prev.map((msg) => {
1453
+ if (msg.isStreaming) {
1454
+ return {
1455
+ ...msg,
1456
+ ...createCancelledMessageUpdate(
1457
+ msg.steps || [],
1458
+ msg.currentMessage
1459
+ )
1460
+ };
1461
+ }
1462
+ return msg;
1463
+ })
1464
+ );
1465
+ }, [cancelStreamManager]);
1466
+ const resetSession = useCallback(() => {
1467
+ const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1468
+ if (streamUserId) {
1469
+ activeStreamStore.abort(streamUserId);
1470
+ }
1471
+ if (configRef.current.userId) {
1472
+ chatStore.delete(configRef.current.userId);
1473
+ }
1474
+ streamUserIdRef.current = void 0;
1475
+ setMessages([]);
1476
+ sessionIdRef.current = void 0;
1477
+ abortControllerRef.current?.abort();
1478
+ setIsWaitingForResponse(false);
1479
+ setUserActionState(EMPTY_USER_ACTION_STATE);
1480
+ }, []);
1481
+ const getSessionId = useCallback(() => {
1482
+ return sessionIdRef.current;
1483
+ }, []);
1484
+ const getMessages = useCallback(() => {
1485
+ return messages;
1486
+ }, [messages]);
1487
+ const setPromptStatus = useCallback(
1488
+ (userActionId, status) => {
1489
+ setUserActionState((prev) => ({
1490
+ ...prev,
1491
+ prompts: prev.prompts.map(
1492
+ (p) => p.userActionId === userActionId ? { ...p, status } : p
1493
+ )
1494
+ }));
1495
+ },
1496
+ []
1497
+ );
1498
+ const removePrompt = useCallback((userActionId) => {
1499
+ setUserActionState((prev) => ({
1500
+ ...prev,
1501
+ prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
1502
+ }));
1503
+ }, []);
1504
+ const submitUserAction2 = useCallback(
1505
+ async (userActionId, content) => {
1506
+ setPromptStatus(userActionId, "submitting");
1507
+ try {
1508
+ await submitUserAction(configRef.current, userActionId, content);
1509
+ removePrompt(userActionId);
1510
+ } catch (error) {
1511
+ if (error instanceof UserActionStaleError) {
1512
+ setPromptStatus(userActionId, "stale");
1513
+ return;
1514
+ }
1515
+ setPromptStatus(userActionId, "pending");
1516
+ callbacksRef.current.onError?.(error);
1517
+ throw error;
1518
+ }
1519
+ },
1520
+ [removePrompt, setPromptStatus]
1521
+ );
1522
+ const cancelUserAction2 = useCallback(
1523
+ async (userActionId) => {
1524
+ setPromptStatus(userActionId, "submitting");
1525
+ try {
1526
+ await cancelUserAction(configRef.current, userActionId);
1527
+ removePrompt(userActionId);
1528
+ } catch (error) {
1529
+ if (error instanceof UserActionStaleError) {
1530
+ removePrompt(userActionId);
1531
+ return;
1532
+ }
1533
+ setPromptStatus(userActionId, "pending");
1534
+ callbacksRef.current.onError?.(error);
1535
+ throw error;
1536
+ }
1537
+ },
1538
+ [removePrompt, setPromptStatus]
1539
+ );
1540
+ const resendUserAction2 = useCallback(
1541
+ async (userActionId) => {
1542
+ setPromptStatus(userActionId, "submitting");
1543
+ try {
1544
+ await resendUserAction(configRef.current, userActionId);
1545
+ setPromptStatus(userActionId, "pending");
1546
+ } catch (error) {
1547
+ if (error instanceof UserActionStaleError) {
1548
+ setPromptStatus(userActionId, "stale");
1549
+ return;
1550
+ }
1551
+ setPromptStatus(userActionId, "pending");
1552
+ callbacksRef.current.onError?.(error);
1553
+ throw error;
1554
+ }
1555
+ },
1556
+ [setPromptStatus]
1557
+ );
1558
+ const dismissNotification = useCallback((id) => {
1559
+ setUserActionState((prev) => ({
1560
+ ...prev,
1561
+ notifications: prev.notifications.filter((n) => n.id !== id)
1562
+ }));
1563
+ }, []);
1564
+ useEffect(() => {
1565
+ const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
1566
+ subscriptionPrevUserIdRef.current = config.userId;
1567
+ const { userId } = config;
1568
+ if (!userId) return;
1569
+ if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
1570
+ streamUserIdRef.current = userId;
1571
+ }
1572
+ const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1573
+ setMessages(msgs);
1574
+ setIsWaitingForResponse(isWaiting);
1575
+ });
1576
+ const active = activeStreamStore.get(userId);
1577
+ if (active) {
1578
+ setMessages(active.messages);
1579
+ setIsWaitingForResponse(active.isWaiting);
1580
+ }
1581
+ return unsubscribe;
1582
+ }, [config.userId]);
1583
+ useEffect(() => {
1584
+ if (!config.userId) return;
1585
+ if (prevUserIdRef.current !== config.userId) return;
1586
+ const toSave = messages.filter((m) => !m.isStreaming);
1587
+ if (toSave.length > 0) {
1588
+ chatStore.set(config.userId, toSave);
1589
+ }
1590
+ }, [messages, config.userId]);
1591
+ useEffect(() => {
1592
+ if (!config.userId || activeStreamStore.has(config.userId)) return;
1593
+ if (!config.initialMessages?.length || messagesRef.current.length > 0) return;
1594
+ chatStore.set(config.userId, config.initialMessages);
1595
+ setMessages(config.initialMessages);
1596
+ sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1597
+ }, [config.initialMessages, config.initialSessionId, config.userId]);
1598
+ useEffect(() => {
1599
+ const prevUserId = prevUserIdRef.current;
1600
+ prevUserIdRef.current = config.userId;
1601
+ if (prevUserId === config.userId) return;
1602
+ if (prevUserId && !config.userId) {
1603
+ chatStore.delete(prevUserId);
1604
+ setMessages([]);
1605
+ sessionIdRef.current = void 0;
1606
+ setIsWaitingForResponse(false);
1607
+ setUserActionState(EMPTY_USER_ACTION_STATE);
1608
+ } else if (config.userId) {
1609
+ const active = activeStreamStore.get(config.userId);
1610
+ if (active) {
1611
+ setMessages(active.messages);
1612
+ setIsWaitingForResponse(active.isWaiting);
1613
+ sessionIdRef.current = getSessionIdFromMessages(active.messages) ?? config.initialSessionId;
1614
+ return;
1615
+ }
1616
+ const nextMessages = getStoredOrInitialMessages(config);
1617
+ setMessages(nextMessages);
1618
+ sessionIdRef.current = getSessionIdFromMessages(nextMessages) ?? config.initialSessionId;
1619
+ setIsWaitingForResponse(false);
1620
+ }
1621
+ }, [config]);
1622
+ return {
1623
+ messages,
1624
+ sendMessage,
1625
+ clearMessages,
1626
+ prependMessages,
1627
+ cancelStream,
1628
+ resetSession,
1629
+ getSessionId,
1630
+ getMessages,
1631
+ isWaitingForResponse,
1632
+ sessionId: sessionIdRef.current,
1633
+ userActionState,
1634
+ submitUserAction: submitUserAction2,
1635
+ cancelUserAction: cancelUserAction2,
1636
+ resendUserAction: resendUserAction2,
1637
+ dismissNotification
1638
+ };
1639
+ }
1640
+ function getSpeechRecognition() {
1641
+ if (typeof window === "undefined") return null;
1642
+ return window.SpeechRecognition || window.webkitSpeechRecognition || null;
1643
+ }
1644
+ function useVoice(config = {}, callbacks = {}) {
1645
+ const [voiceState, setVoiceState] = useState("idle");
1646
+ const [transcribedText, setTranscribedText] = useState("");
1647
+ const [isAvailable, setIsAvailable] = useState(false);
1648
+ const [isRecording, setIsRecording] = useState(false);
1649
+ const recognitionRef = useRef(null);
1650
+ const autoStopTimerRef = useRef(null);
1651
+ const {
1652
+ lang = "en-US",
1653
+ interimResults = true,
1654
+ continuous = true,
1655
+ maxAlternatives = 1,
1656
+ autoStopAfterSilence
1657
+ } = config;
1658
+ const { onStart, onEnd, onResult, onError, onStateChange } = callbacks;
1659
+ useEffect(() => {
1660
+ const SpeechRecognitionAPI = getSpeechRecognition();
1661
+ setIsAvailable(SpeechRecognitionAPI !== null);
1662
+ }, []);
1663
+ useEffect(() => {
1664
+ onStateChange?.(voiceState);
1665
+ }, [voiceState, onStateChange]);
1666
+ const requestPermissions = useCallback(async () => {
1667
+ try {
1668
+ const result = await navigator.mediaDevices.getUserMedia({
1669
+ audio: true
1670
+ });
1671
+ result.getTracks().forEach((track) => track.stop());
1672
+ return {
1673
+ granted: true,
1674
+ status: "granted"
1675
+ };
1676
+ } catch (error) {
1677
+ return {
1678
+ granted: false,
1679
+ status: "denied"
1680
+ };
1681
+ }
1682
+ }, []);
1683
+ const getPermissions = useCallback(async () => {
1684
+ if (typeof navigator === "undefined" || !navigator.permissions) {
1685
+ return {
1686
+ granted: false,
1687
+ status: "undetermined"
1688
+ };
1689
+ }
1690
+ try {
1691
+ const result = await navigator.permissions.query({
1692
+ name: "microphone"
1693
+ });
1694
+ return {
1695
+ granted: result.state === "granted",
1696
+ status: result.state === "granted" ? "granted" : result.state === "denied" ? "denied" : "undetermined"
1697
+ };
1698
+ } catch {
1699
+ return {
1700
+ granted: false,
1701
+ status: "undetermined"
1702
+ };
1703
+ }
1704
+ }, []);
1705
+ const clearAutoStopTimer = useCallback(() => {
1706
+ if (autoStopTimerRef.current) {
1707
+ clearTimeout(autoStopTimerRef.current);
1708
+ autoStopTimerRef.current = null;
1709
+ }
1710
+ }, []);
1711
+ const resetAutoStopTimer = useCallback(() => {
1712
+ clearAutoStopTimer();
1713
+ if (autoStopAfterSilence && autoStopAfterSilence > 0) {
1714
+ autoStopTimerRef.current = setTimeout(() => {
1715
+ if (recognitionRef.current && isRecording) {
1716
+ recognitionRef.current.stop();
1717
+ }
1718
+ }, autoStopAfterSilence);
1719
+ }
1720
+ }, [autoStopAfterSilence, clearAutoStopTimer, isRecording]);
1721
+ const stopRecording = useCallback(() => {
1722
+ if (recognitionRef.current) {
1723
+ try {
1724
+ recognitionRef.current.stop();
1725
+ } catch (error) {
1726
+ console.warn("Error stopping speech recognition:", error);
1727
+ }
1728
+ }
1729
+ clearAutoStopTimer();
1730
+ setIsRecording(false);
1731
+ setVoiceState("idle");
1732
+ }, [clearAutoStopTimer]);
1733
+ const startRecording = useCallback(async () => {
1734
+ const SpeechRecognitionAPI = getSpeechRecognition();
1735
+ if (!SpeechRecognitionAPI) {
1736
+ onError?.("Speech recognition not supported in this browser");
1737
+ return;
1738
+ }
1739
+ try {
1740
+ try {
1741
+ await navigator.mediaDevices.getUserMedia({ audio: true });
1742
+ } catch (permError) {
1743
+ onError?.("Microphone access denied. Please allow microphone access in your browser settings.");
1744
+ return;
1745
+ }
1746
+ const recognition = new SpeechRecognitionAPI();
1747
+ recognition.continuous = continuous;
1748
+ recognition.interimResults = interimResults;
1749
+ recognition.lang = lang;
1750
+ recognition.maxAlternatives = maxAlternatives;
1751
+ recognition.onstart = () => {
1752
+ setVoiceState("listening");
1753
+ setIsRecording(true);
1754
+ onStart?.();
1755
+ resetAutoStopTimer();
1756
+ };
1757
+ recognition.onend = () => {
1758
+ setVoiceState("idle");
1759
+ setIsRecording(false);
1760
+ clearAutoStopTimer();
1761
+ onEnd?.();
1762
+ };
1763
+ recognition.onresult = (event) => {
1764
+ const results = event.results;
1765
+ let transcript = "";
1766
+ for (let i = 0; i < results.length; i++) {
1767
+ const result = results[i];
1768
+ if (result && result[0]) {
1769
+ const text = result[0].transcript;
1770
+ if (transcript && !transcript.endsWith(" ") && !text.startsWith(" ")) {
1771
+ transcript += " " + text;
1772
+ } else {
1773
+ transcript += text;
1774
+ }
1775
+ }
1776
+ }
1777
+ transcript = transcript.trim();
1778
+ if (transcript) {
1779
+ setTranscribedText(transcript);
1780
+ onResult?.(transcript);
1781
+ resetAutoStopTimer();
1782
+ }
1783
+ };
1784
+ recognition.onerror = (event) => {
1785
+ setVoiceState("error");
1786
+ setIsRecording(false);
1787
+ clearAutoStopTimer();
1788
+ let errorMessage = event.error;
1789
+ if (event.error === "not-allowed") {
1790
+ errorMessage = "Microphone access denied. Please allow microphone access in your browser settings.";
1791
+ } else if (event.error === "no-speech") {
1792
+ errorMessage = "No speech detected. Please try again.";
1793
+ } else if (event.error === "audio-capture") {
1794
+ errorMessage = "No microphone found or microphone is in use.";
1795
+ } else if (event.error === "network") {
1796
+ errorMessage = "Network error occurred. Please check your connection.";
1797
+ }
1798
+ onError?.(errorMessage);
1799
+ };
1800
+ recognitionRef.current = recognition;
1801
+ setTranscribedText("");
1802
+ recognition.start();
1803
+ } catch (error) {
1804
+ setVoiceState("error");
1805
+ setIsRecording(false);
1806
+ onError?.(
1807
+ error instanceof Error ? error.message : "Failed to start recording"
1808
+ );
1809
+ }
1810
+ }, [
1811
+ lang,
1812
+ interimResults,
1813
+ continuous,
1814
+ maxAlternatives,
1815
+ onStart,
1816
+ onEnd,
1817
+ onResult,
1818
+ onError,
1819
+ getPermissions,
1820
+ resetAutoStopTimer,
1821
+ clearAutoStopTimer
1822
+ ]);
1823
+ const clearTranscript = useCallback(() => {
1824
+ setTranscribedText("");
1825
+ }, []);
1826
+ const reset = useCallback(() => {
1827
+ stopRecording();
1828
+ setTranscribedText("");
1829
+ setVoiceState("idle");
1830
+ }, [stopRecording]);
1831
+ useEffect(() => {
1832
+ return () => {
1833
+ if (recognitionRef.current) {
1834
+ try {
1835
+ recognitionRef.current.stop();
1836
+ } catch {
1837
+ }
1838
+ }
1839
+ clearAutoStopTimer();
1840
+ };
1841
+ }, [clearAutoStopTimer]);
1842
+ return {
1843
+ voiceState,
1844
+ transcribedText,
1845
+ isAvailable,
1846
+ isRecording,
1847
+ startRecording,
1848
+ stopRecording,
1849
+ requestPermissions,
1850
+ getPermissions,
1851
+ clearTranscript,
1852
+ reset
1853
+ };
1854
+ }
1855
+ function classifyField(field) {
1856
+ if (!field) return "text";
1857
+ if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
1858
+ switch (field.type) {
1859
+ case "boolean":
1860
+ return "boolean";
1861
+ case "integer":
1862
+ return "integer";
1863
+ case "number":
1864
+ return "decimal";
1865
+ case "string":
1866
+ return "text";
1867
+ default:
1868
+ return "text";
1869
+ }
1870
+ }
1871
+ function isNestedOrUnsupported(field) {
1872
+ if (!field) return false;
1873
+ if (field.type === "object" || field.type === "array") return true;
1874
+ if ("properties" in field && field.properties != null) return true;
1875
+ if ("items" in field && field.items != null) return true;
1876
+ return false;
1877
+ }
1878
+ function getOptions(field) {
1879
+ if (!field || !Array.isArray(field.oneOf)) return [];
1880
+ return field.oneOf.filter(
1881
+ (o) => !!o && typeof o === "object" && typeof o.const === "string"
1882
+ );
1883
+ }
1884
+ function isRequired(schema, key) {
1885
+ return Array.isArray(schema?.required) && schema.required.includes(key);
1886
+ }
1887
+ function coerceValue(field, raw) {
1888
+ const widget = classifyField(field);
1889
+ if (widget === "boolean") {
1890
+ if (typeof raw === "boolean") return raw;
1891
+ if (raw === "true") return true;
1892
+ if (raw === "false") return false;
1893
+ return Boolean(raw);
1894
+ }
1895
+ if (widget === "integer" || widget === "decimal") {
1896
+ if (raw === "" || raw == null) return void 0;
1897
+ const num = typeof raw === "number" ? raw : Number(String(raw).trim());
1898
+ if (Number.isNaN(num)) return void 0;
1899
+ return widget === "integer" ? Math.trunc(num) : num;
1900
+ }
1901
+ if (raw == null) return void 0;
1902
+ const str = String(raw);
1903
+ return str === "" ? void 0 : str;
1904
+ }
1905
+ function defaultValueFor(field) {
1906
+ if (!field || field.default === void 0) {
1907
+ return classifyField(field) === "boolean" ? false : "";
1908
+ }
1909
+ return field.default;
1910
+ }
1911
+ function validateField(field, value, required) {
1912
+ const widget = classifyField(field);
1913
+ const label = field?.title || "This field";
1914
+ const isEmpty = value === void 0 || value === null || typeof value === "string" && value.trim() === "";
1915
+ if (isEmpty) {
1916
+ if (required && widget !== "boolean") return `${label} is required.`;
1917
+ return null;
1918
+ }
1919
+ if (widget === "integer" || widget === "decimal") {
1920
+ const num = typeof value === "number" ? value : Number(value);
1921
+ if (Number.isNaN(num)) return `${label} must be a number.`;
1922
+ if (widget === "integer" && !Number.isInteger(num)) {
1923
+ return `${label} must be a whole number.`;
1924
+ }
1925
+ if (typeof field?.minimum === "number" && num < field.minimum) {
1926
+ return `${label} must be at least ${field.minimum}.`;
1927
+ }
1928
+ if (typeof field?.maximum === "number" && num > field.maximum) {
1929
+ return `${label} must be at most ${field.maximum}.`;
1930
+ }
1931
+ return null;
1932
+ }
1933
+ if (widget === "select") {
1934
+ const allowed = getOptions(field).map((o) => o.const);
1935
+ if (allowed.length > 0 && !allowed.includes(String(value))) {
1936
+ return `${label} has an invalid selection.`;
1937
+ }
1938
+ return null;
1939
+ }
1940
+ const str = String(value);
1941
+ if (typeof field?.minLength === "number" && str.length < field.minLength) {
1942
+ return `${label} must be at least ${field.minLength} characters.`;
1943
+ }
1944
+ if (typeof field?.maxLength === "number" && str.length > field.maxLength) {
1945
+ return `${label} must be at most ${field.maxLength} characters.`;
1946
+ }
1947
+ return null;
1948
+ }
1949
+ function renderableFields(schema) {
1950
+ const props = schema?.properties;
1951
+ if (!props) return [];
1952
+ return Object.entries(props).filter(([, field]) => !isNestedOrUnsupported(field));
1953
+ }
1954
+ function validateForm(schema, values) {
1955
+ const errors = {};
1956
+ for (const [key, field] of renderableFields(schema)) {
1957
+ const coerced = coerceValue(field, values[key]);
1958
+ const err = validateField(field, coerced, isRequired(schema, key));
1959
+ if (err) errors[key] = err;
1960
+ }
1961
+ return errors;
1962
+ }
1963
+ function buildContent(schema, values) {
1964
+ const content = {};
1965
+ for (const [key, field] of renderableFields(schema)) {
1966
+ const coerced = coerceValue(field, values[key]);
1967
+ if (coerced !== void 0) content[key] = coerced;
1968
+ }
1969
+ return content;
1970
+ }
16
1971
  var PaymanChatContext = createContext(void 0);
17
1972
  function usePaymanChat() {
18
1973
  const context = useContext(PaymanChatContext);
@@ -232,7 +2187,7 @@ var NEGATIVE_FEEDBACK_REASONS = [
232
2187
  { value: "REPORT_CONTENT", label: "Report content" },
233
2188
  { value: "OTHER", label: "Other" }
234
2189
  ];
235
- var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
2190
+ var DEFAULT_STREAM_ENDPOINT2 = "/api/playground/ask/stream";
236
2191
  async function submitFeedback({
237
2192
  baseUrl,
238
2193
  streamEndpoint,
@@ -246,7 +2201,7 @@ async function submitFeedback({
246
2201
  signal
247
2202
  }) {
248
2203
  const base = baseUrl.replace(/\/+$/, "");
249
- const endpointPath = (streamEndpoint || DEFAULT_STREAM_ENDPOINT).split("?")[0].replace(/\/+$/, "");
2204
+ const endpointPath = (streamEndpoint || DEFAULT_STREAM_ENDPOINT2).split("?")[0].replace(/\/+$/, "");
250
2205
  const basePath = endpointPath.endsWith("/stream") ? endpointPath.slice(0, -"/stream".length) : endpointPath;
251
2206
  const query = new URLSearchParams();
252
2207
  if (stage) query.set(stageQueryParam ?? "stage", stage);
@@ -518,47 +2473,7 @@ function createMarkdownComponents(options = {}) {
518
2473
  td: ({ children }) => /* @__PURE__ */ jsx("td", { className: "p-3 align-middle text-sm whitespace-nowrap", children })
519
2474
  };
520
2475
  }
521
- function ThinkingBlock({ text }) {
522
- const [isOpen, setIsOpen] = useState(false);
523
- const hasContent = typeof text === "string" && text.trim().length > 0;
524
- if (!hasContent) return null;
525
- return /* @__PURE__ */ jsxs("div", { className: "mt-1.5 mb-1.5", children: [
526
- /* @__PURE__ */ jsxs(
527
- "button",
528
- {
529
- type: "button",
530
- onClick: () => setIsOpen((prev) => !prev),
531
- className: "inline-flex items-center gap-1 text-[10px] payman-agent-thinking-toggle transition-colors",
532
- "aria-expanded": isOpen,
533
- "aria-label": isOpen ? "Collapse thought process" : "Expand thought process",
534
- children: [
535
- /* @__PURE__ */ jsx(
536
- motion.div,
537
- {
538
- animate: { rotate: isOpen ? 180 : 0 },
539
- transition: { duration: 0.15 },
540
- className: "shrink-0",
541
- children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
542
- }
543
- ),
544
- /* @__PURE__ */ jsx("span", { children: "Thought process" })
545
- ]
546
- }
547
- ),
548
- /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx(
549
- motion.div,
550
- {
551
- initial: { height: 0, opacity: 0 },
552
- animate: { height: "auto", opacity: 1 },
553
- exit: { height: 0, opacity: 0 },
554
- transition: { duration: 0.2, ease: "easeInOut" },
555
- className: "overflow-hidden",
556
- children: /* @__PURE__ */ jsx("div", { className: "mt-1 payman-agent-thinking-block rounded-md p-2 overflow-y-auto overflow-x-hidden", children: /* @__PURE__ */ jsx("p", { className: "m-0 text-xs payman-agent-thinking-block-text whitespace-pre-wrap leading-relaxed", children: text }) })
557
- }
558
- ) })
559
- ] });
560
- }
561
- var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
2476
+ var FRIENDLY_ERROR_MESSAGE2 = "Oops, something went wrong. Please try again.";
562
2477
  function looksLikeRawError(text) {
563
2478
  if (!text || text.length < 10) return false;
564
2479
  return text.includes("errorType=") || /failed:\s*\{/.test(text);
@@ -600,8 +2515,6 @@ function AgentMessage({
600
2515
  const completedWithNoContent = !isStreaming && !isCancelled && content.length === 0 && (message.streamProgress === "completed" || message.streamProgress === "error");
601
2516
  const conflictErrorMessage = getConflictErrorMessage(message.errorDetails);
602
2517
  const isError = !!conflictErrorMessage || (isFriendlyWorkflowError(message.errorDetails) || looksLikeRawError(content)) && !hasMeaningfulContent || completedWithNoContent;
603
- const activeThinkingText = message.activeThinkingText;
604
- const allThinkingText = message.allThinkingText;
605
2518
  const currentStep = useMemo(
606
2519
  () => message.steps?.find(
607
2520
  (s) => s.id === currentExecutingStepId && s.status === "in_progress"
@@ -644,7 +2557,7 @@ function AgentMessage({
644
2557
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5 payman-agent-step-icon--error" }) });
645
2558
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full payman-agent-step-icon--pending-dim" }) });
646
2559
  };
647
- const stepsListContent = hasSteps && /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isStepsExpanded && /* @__PURE__ */ jsxs(
2560
+ const stepsListContent = hasSteps && /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isStepsExpanded && /* @__PURE__ */ jsx(
648
2561
  motion.div,
649
2562
  {
650
2563
  initial: { height: 0, opacity: 0 },
@@ -652,37 +2565,34 @@ function AgentMessage({
652
2565
  exit: { height: 0, opacity: 0 },
653
2566
  transition: { duration: 0.2, ease: "easeInOut" },
654
2567
  className: "overflow-hidden",
655
- children: [
656
- !isStreaming && allThinkingText?.trim() && /* @__PURE__ */ jsx(ThinkingBlock, { text: allThinkingText }),
657
- /* @__PURE__ */ jsx("div", { className: "pt-1.5", children: message.steps.map((step) => {
658
- const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
659
- const hasTime = step.elapsedMs != null && step.elapsedMs > 0;
660
- return /* @__PURE__ */ jsxs("div", { className: "mb-1.5", children: [
661
- /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
662
- /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
663
- /* @__PURE__ */ jsx(
664
- "span",
665
- {
666
- className: cn(
667
- "text-xs leading-relaxed min-w-0 break-words",
668
- isCurrentlyExecuting && "shimmer-text font-medium",
669
- !isCurrentlyExecuting && step.status === "error" && "payman-agent-step-text--error",
670
- !isCurrentlyExecuting && step.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
671
- !isCurrentlyExecuting && step.status === "completed" && "payman-agent-step-text--completed",
672
- !isCurrentlyExecuting && step.status === "pending" && "payman-agent-step-text--pending",
673
- !isCurrentlyExecuting && step.status === "in_progress" && "payman-agent-step-text--in-progress"
674
- ),
675
- children: step.message
676
- }
677
- )
678
- ] }),
679
- hasTime && /* @__PURE__ */ jsx("div", { className: "pl-[22px] mt-1", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md payman-agent-step-time leading-none", children: [
680
- /* @__PURE__ */ jsx(Clock, { className: "h-2.5 w-2.5 shrink-0 payman-agent-step-time-icon" }),
681
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step.elapsedMs) })
682
- ] }) })
683
- ] }, step.id);
684
- }) })
685
- ]
2568
+ children: /* @__PURE__ */ jsx("div", { className: "pt-1.5", children: message.steps.map((step) => {
2569
+ const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
2570
+ const hasTime = step.elapsedMs != null && step.elapsedMs > 0;
2571
+ return /* @__PURE__ */ jsxs("div", { className: "mb-1.5", children: [
2572
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
2573
+ /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
2574
+ /* @__PURE__ */ jsx(
2575
+ "span",
2576
+ {
2577
+ className: cn(
2578
+ "text-xs leading-relaxed min-w-0 break-words",
2579
+ isCurrentlyExecuting && "shimmer-text font-medium",
2580
+ !isCurrentlyExecuting && step.status === "error" && "payman-agent-step-text--error",
2581
+ !isCurrentlyExecuting && step.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
2582
+ !isCurrentlyExecuting && step.status === "completed" && "payman-agent-step-text--completed",
2583
+ !isCurrentlyExecuting && step.status === "pending" && "payman-agent-step-text--pending",
2584
+ !isCurrentlyExecuting && step.status === "in_progress" && "payman-agent-step-text--in-progress"
2585
+ ),
2586
+ children: step.message
2587
+ }
2588
+ )
2589
+ ] }),
2590
+ hasTime && /* @__PURE__ */ jsx("div", { className: "pl-[22px] mt-1", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md payman-agent-step-time leading-none", children: [
2591
+ /* @__PURE__ */ jsx(Clock, { className: "h-2.5 w-2.5 shrink-0 payman-agent-step-time-icon" }),
2592
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step.elapsedMs) })
2593
+ ] }) })
2594
+ ] }, step.id);
2595
+ }) })
686
2596
  }
687
2597
  ) });
688
2598
  const stepsToggleRef = useRef(null);
@@ -724,95 +2634,77 @@ function AgentMessage({
724
2634
  /* @__PURE__ */ jsx("div", { className: "h-2.5 w-2.5 rounded-full payman-agent-avatar-dot animate-pulse" }),
725
2635
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 h-2.5 w-2.5 rounded-full payman-agent-avatar-dot-ping animate-ping" })
726
2636
  ] }) }) : /* @__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(
2637
+ /* @__PURE__ */ jsx(
728
2638
  "div",
729
2639
  {
730
2640
  className: cn(
731
2641
  "min-w-0",
732
2642
  layout === "centered" ? "max-w-[85%]" : showAvatar ? "max-w-[85%]" : "max-w-[80%]"
733
2643
  ),
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
- )
2644
+ children: /* @__PURE__ */ jsxs(
2645
+ "div",
2646
+ {
2647
+ className: cn(
2648
+ "overflow-hidden w-full min-w-0 px-4 py-3 rounded-2xl rounded-tl-md transition-all duration-200",
2649
+ layout === "centered" ? cn(
2650
+ "payman-agent-bubble payman-agent-bubble--centered",
2651
+ isStreaming && "streaming",
2652
+ isError && "error"
2653
+ ) : cn(
2654
+ "payman-agent-bubble",
2655
+ isError && "payman-agent-bubble--error"
2656
+ )
2657
+ ),
2658
+ children: [
2659
+ showAgentName && /* @__PURE__ */ jsx(
2660
+ "p",
2661
+ {
2662
+ className: cn(
2663
+ "text-sm font-semibold mb-1 leading-none payman-agent-name",
2664
+ isStreaming && !content && "animate-pulse"
2665
+ ),
2666
+ children: isStreaming && !content ? "Thought Process" : agentName
2667
+ }
755
2668
  ),
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..." })
788
- ] })
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
- ]
2669
+ /* @__PURE__ */ jsx(
2670
+ "div",
2671
+ {
2672
+ className: cn(
2673
+ "text-sm leading-relaxed min-w-0 w-full break-words overflow-wrap-anywhere",
2674
+ showAgentName && "mt-1"
2675
+ ),
2676
+ children: isStreaming && !content ? currentStep ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
2677
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 shrink-0", children: renderStepIcon(currentStep, true) }),
2678
+ /* @__PURE__ */ jsx("span", { className: "text-sm leading-relaxed min-w-0 break-words shimmer-text font-medium flex-1", children: currentStep.message })
2679
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
2680
+ /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 mt-0.5 payman-agent-thinking-spinner animate-spin shrink-0" }),
2681
+ /* @__PURE__ */ jsx("span", { className: "text-sm payman-agent-thinking-text flex-1", children: currentMessage || "Thinking..." })
2682
+ ] }) : isCancelled && !content ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
2683
+ /* @__PURE__ */ jsx(X, { className: "w-4 h-4 mt-0.5 payman-agent-cancelled-icon shrink-0" }),
2684
+ /* @__PURE__ */ jsx("span", { className: "text-sm payman-agent-cancelled-text italic flex-1", children: currentMessage || "Request was stopped." })
2685
+ ] }) : /* @__PURE__ */ jsx(
2686
+ "div",
2687
+ {
2688
+ className: cn(
2689
+ "payman-markdown payman-agent-markdown prose prose-sm dark:prose-invert max-w-none min-w-0 w-full break-words overflow-wrap-anywhere",
2690
+ isError && "payman-agent-markdown--error",
2691
+ isStreaming && content && "streaming-cursor"
2692
+ ),
2693
+ children: /* @__PURE__ */ jsx(
2694
+ ReactMarkdown,
2695
+ {
2696
+ remarkPlugins: [remarkGfm],
2697
+ components: markdownRenderers,
2698
+ children: isError ? conflictErrorMessage ?? FRIENDLY_ERROR_MESSAGE2 : content || (isStreaming ? "Thinking..." : isCancelled ? "Request was stopped." : "")
2699
+ }
2700
+ )
2701
+ }
2702
+ )
2703
+ }
2704
+ )
2705
+ ]
2706
+ }
2707
+ )
816
2708
  }
817
2709
  )
818
2710
  ] }),
@@ -1656,7 +3548,62 @@ function MarkdownImageV2({
1656
3548
  }
1657
3549
  ) });
1658
3550
  }
1659
- function buildComponents(onImageClick, isResolvingRef) {
3551
+ function PdfBlockV2({ title, href, onOpen }) {
3552
+ return /* @__PURE__ */ jsxs(
3553
+ "button",
3554
+ {
3555
+ type: "button",
3556
+ className: "payman-v2-pdf-block",
3557
+ onClick: (e) => {
3558
+ e.preventDefault();
3559
+ onOpen(href, title);
3560
+ },
3561
+ "aria-label": `Open PDF: ${title}`,
3562
+ children: [
3563
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-pdf-block-icon-area", children: /* @__PURE__ */ jsx(FileText, { size: 18, strokeWidth: 1.5 }) }),
3564
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-block-body", children: [
3565
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-pdf-block-name", children: title }),
3566
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-pdf-block-sub", children: "PDF \xB7 Click to preview" })
3567
+ ] })
3568
+ ]
3569
+ }
3570
+ );
3571
+ }
3572
+ function isPdfUrl(href) {
3573
+ if (!href) return false;
3574
+ try {
3575
+ const url = new URL(href);
3576
+ const filename = url.searchParams.get("filename");
3577
+ if (filename?.toLowerCase().endsWith(".pdf")) return true;
3578
+ return url.pathname.toLowerCase().endsWith(".pdf");
3579
+ } catch {
3580
+ return href.toLowerCase().endsWith(".pdf");
3581
+ }
3582
+ }
3583
+ function getPdfTitleFromUrl(href) {
3584
+ try {
3585
+ const url = new URL(href);
3586
+ const filename = url.searchParams.get("filename");
3587
+ if (filename) {
3588
+ return filename.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
3589
+ }
3590
+ const parts = url.pathname.split("/").filter(Boolean);
3591
+ const last = parts[parts.length - 1];
3592
+ if (last) return decodeURIComponent(last).replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
3593
+ } catch {
3594
+ }
3595
+ return "Document";
3596
+ }
3597
+ function childrenToText(children) {
3598
+ if (typeof children === "string") return children;
3599
+ if (typeof children === "number") return String(children);
3600
+ if (Array.isArray(children)) return children.map(childrenToText).join("");
3601
+ if (children && typeof children === "object" && "props" in children) {
3602
+ return childrenToText(children.props.children);
3603
+ }
3604
+ return "";
3605
+ }
3606
+ function buildComponents(onImageClick, isResolvingRef, onPdfClick) {
1660
3607
  return {
1661
3608
  p: ({ children }) => /* @__PURE__ */ jsx("p", { children }),
1662
3609
  code: ({ children }) => /* @__PURE__ */ jsx("code", { children }),
@@ -1671,7 +3618,15 @@ function buildComponents(onImageClick, isResolvingRef) {
1671
3618
  em: ({ children }) => /* @__PURE__ */ jsx("em", { children }),
1672
3619
  blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { children }),
1673
3620
  hr: () => /* @__PURE__ */ jsx("hr", {}),
1674
- a: ({ href, children }) => /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children }),
3621
+ a: ({ href, children }) => {
3622
+ const url = href ?? "";
3623
+ if (onPdfClick && isPdfUrl(url)) {
3624
+ const linkText = childrenToText(children).trim();
3625
+ const title = linkText || getPdfTitleFromUrl(url);
3626
+ return /* @__PURE__ */ jsx(PdfBlockV2, { href: url, title, onOpen: onPdfClick });
3627
+ }
3628
+ return /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children });
3629
+ },
1675
3630
  img: ({ src, alt }) => /* @__PURE__ */ jsx(
1676
3631
  MarkdownImageV2,
1677
3632
  {
@@ -1692,13 +3647,14 @@ function MarkdownRendererV2({
1692
3647
  content,
1693
3648
  isStreaming,
1694
3649
  isResolvingImages,
1695
- onImageClick
3650
+ onImageClick,
3651
+ onPdfClick
1696
3652
  }) {
1697
3653
  const isResolvingRef = useRef(isResolvingImages);
1698
3654
  isResolvingRef.current = isResolvingImages;
1699
3655
  const components = useMemo(
1700
- () => buildComponents(onImageClick, isResolvingRef),
1701
- [onImageClick]
3656
+ () => buildComponents(onImageClick, isResolvingRef, onPdfClick),
3657
+ [onImageClick, onPdfClick]
1702
3658
  );
1703
3659
  return /* @__PURE__ */ jsx(
1704
3660
  "div",
@@ -1963,6 +3919,199 @@ function FeedbackReasonModal({
1963
3919
  }
1964
3920
  ) : null });
1965
3921
  }
3922
+ var MIN_WIDTH = 320;
3923
+ var MAX_WIDTH_RATIO = 0.92;
3924
+ var DEFAULT_WIDTH = 680;
3925
+ function PdfSheetV2({ src, title, onClose }) {
3926
+ const [isMounted, setIsMounted] = useState(false);
3927
+ const [isLoaded, setIsLoaded] = useState(false);
3928
+ const [width, setWidth] = useState(DEFAULT_WIDTH);
3929
+ const panelRef = useRef(null);
3930
+ const isDragging = useRef(false);
3931
+ const dragStartX = useRef(0);
3932
+ const dragStartWidth = useRef(0);
3933
+ useEffect(() => {
3934
+ setIsMounted(true);
3935
+ return () => setIsMounted(false);
3936
+ }, []);
3937
+ useEffect(() => {
3938
+ setIsLoaded(false);
3939
+ }, [src]);
3940
+ useEffect(() => {
3941
+ const onResize = () => {
3942
+ const max = Math.floor(window.innerWidth * MAX_WIDTH_RATIO);
3943
+ setWidth((w) => Math.min(w, max));
3944
+ };
3945
+ window.addEventListener("resize", onResize);
3946
+ return () => window.removeEventListener("resize", onResize);
3947
+ }, []);
3948
+ const handleKeyDown = useCallback(
3949
+ (e) => {
3950
+ if (e.key === "Escape") onClose();
3951
+ },
3952
+ [onClose]
3953
+ );
3954
+ useEffect(() => {
3955
+ if (!src || typeof document === "undefined") return;
3956
+ document.addEventListener("keydown", handleKeyDown);
3957
+ const previousOverflow = document.body.style.overflow;
3958
+ document.body.style.overflow = "hidden";
3959
+ return () => {
3960
+ document.removeEventListener("keydown", handleKeyDown);
3961
+ document.body.style.overflow = previousOverflow;
3962
+ };
3963
+ }, [src, handleKeyDown]);
3964
+ const handleDownload = () => {
3965
+ if (!src || typeof document === "undefined") return;
3966
+ const a = document.createElement("a");
3967
+ a.href = src;
3968
+ a.download = title ? `${title}.pdf` : "document.pdf";
3969
+ document.body.appendChild(a);
3970
+ a.click();
3971
+ a.remove();
3972
+ };
3973
+ const onResizeMouseDown = (e) => {
3974
+ e.preventDefault();
3975
+ isDragging.current = true;
3976
+ dragStartX.current = e.clientX;
3977
+ dragStartWidth.current = panelRef.current?.offsetWidth ?? width;
3978
+ document.body.style.cursor = "ew-resize";
3979
+ document.body.style.userSelect = "none";
3980
+ };
3981
+ useEffect(() => {
3982
+ const onMouseMove = (e) => {
3983
+ if (!isDragging.current || !panelRef.current) return;
3984
+ const delta = dragStartX.current - e.clientX;
3985
+ const maxW = Math.floor(window.innerWidth * MAX_WIDTH_RATIO);
3986
+ const newW = Math.max(MIN_WIDTH, Math.min(maxW, dragStartWidth.current + delta));
3987
+ panelRef.current.style.width = `${newW}px`;
3988
+ };
3989
+ const onMouseUp = () => {
3990
+ if (!isDragging.current) return;
3991
+ isDragging.current = false;
3992
+ document.body.style.cursor = "";
3993
+ document.body.style.userSelect = "";
3994
+ if (panelRef.current) {
3995
+ setWidth(panelRef.current.offsetWidth);
3996
+ }
3997
+ };
3998
+ document.addEventListener("mousemove", onMouseMove);
3999
+ document.addEventListener("mouseup", onMouseUp);
4000
+ return () => {
4001
+ document.removeEventListener("mousemove", onMouseMove);
4002
+ document.removeEventListener("mouseup", onMouseUp);
4003
+ };
4004
+ }, []);
4005
+ if (!isMounted || typeof document === "undefined") return null;
4006
+ return createPortal(
4007
+ /* @__PURE__ */ jsx(AnimatePresence, { children: src ? /* @__PURE__ */ jsxs(Fragment, { children: [
4008
+ /* @__PURE__ */ jsx(
4009
+ motion.div,
4010
+ {
4011
+ className: "payman-v2-pdf-sheet-overlay",
4012
+ initial: { opacity: 0 },
4013
+ animate: { opacity: 1 },
4014
+ exit: { opacity: 0 },
4015
+ transition: { duration: 0.22 },
4016
+ onClick: onClose,
4017
+ "aria-hidden": "true"
4018
+ },
4019
+ "pdf-sheet-backdrop"
4020
+ ),
4021
+ /* @__PURE__ */ jsxs(
4022
+ motion.div,
4023
+ {
4024
+ ref: panelRef,
4025
+ className: "payman-v2-pdf-sheet",
4026
+ style: { width },
4027
+ initial: { x: "100%" },
4028
+ animate: { x: 0 },
4029
+ exit: { x: "100%" },
4030
+ transition: {
4031
+ type: "spring",
4032
+ stiffness: 340,
4033
+ damping: 34,
4034
+ mass: 0.85
4035
+ },
4036
+ role: "dialog",
4037
+ "aria-modal": "true",
4038
+ "aria-label": title || "PDF Preview",
4039
+ children: [
4040
+ /* @__PURE__ */ jsx(
4041
+ "div",
4042
+ {
4043
+ className: "payman-v2-pdf-sheet-resize-handle",
4044
+ onMouseDown: onResizeMouseDown,
4045
+ "aria-hidden": "true",
4046
+ children: /* @__PURE__ */ jsx("div", { className: "payman-v2-pdf-sheet-resize-grip" })
4047
+ }
4048
+ ),
4049
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-header", children: [
4050
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-header-left", children: [
4051
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-pdf-sheet-file-icon", children: /* @__PURE__ */ jsx(FileText, { size: 14, strokeWidth: 1.75 }) }),
4052
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-pdf-sheet-title", title, children: title || "Document" })
4053
+ ] }),
4054
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-header-actions", children: [
4055
+ /* @__PURE__ */ jsxs(
4056
+ "button",
4057
+ {
4058
+ type: "button",
4059
+ className: "payman-v2-pdf-sheet-download-btn",
4060
+ "aria-label": "Download PDF",
4061
+ onClick: handleDownload,
4062
+ children: [
4063
+ /* @__PURE__ */ jsx(Download, { size: 13, strokeWidth: 2 }),
4064
+ /* @__PURE__ */ jsx("span", { children: "Download" })
4065
+ ]
4066
+ }
4067
+ ),
4068
+ /* @__PURE__ */ jsx(
4069
+ "button",
4070
+ {
4071
+ type: "button",
4072
+ className: "payman-v2-pdf-sheet-close-btn",
4073
+ "aria-label": "Close preview",
4074
+ onClick: onClose,
4075
+ children: /* @__PURE__ */ jsx(X, { size: 15, strokeWidth: 2.25 })
4076
+ }
4077
+ )
4078
+ ] })
4079
+ ] }),
4080
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-body", children: [
4081
+ !isLoaded && /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-loading", children: [
4082
+ /* @__PURE__ */ jsx(
4083
+ Loader2,
4084
+ {
4085
+ size: 20,
4086
+ strokeWidth: 2,
4087
+ style: {
4088
+ animation: "payman-v2-spin 0.65s linear infinite",
4089
+ color: "var(--payman-v2-text-3)"
4090
+ }
4091
+ }
4092
+ ),
4093
+ /* @__PURE__ */ jsx("p", { className: "payman-v2-pdf-sheet-loading-text", children: "Loading PDF\u2026" })
4094
+ ] }),
4095
+ /* @__PURE__ */ jsx(
4096
+ "iframe",
4097
+ {
4098
+ src,
4099
+ title: title || "PDF Preview",
4100
+ className: "payman-v2-pdf-sheet-iframe",
4101
+ style: { opacity: isLoaded ? 1 : 0, transition: "opacity 0.3s ease" },
4102
+ onLoad: () => setIsLoaded(true)
4103
+ },
4104
+ src
4105
+ )
4106
+ ] })
4107
+ ]
4108
+ },
4109
+ "pdf-sheet-panel"
4110
+ )
4111
+ ] }) : null }),
4112
+ document.body
4113
+ );
4114
+ }
1966
4115
  var RESPONSE_SPEED = {
1967
4116
  normal: [2, 4],
1968
4117
  fast: 1,
@@ -2090,6 +4239,10 @@ function AssistantMessageV2({
2090
4239
  () => getFeedbackState(message)
2091
4240
  );
2092
4241
  const [reasonModalOpen, setReasonModalOpen] = useState(false);
4242
+ const [pdfSheet, setPdfSheet] = useState(null);
4243
+ const handlePdfClick = useCallback((href, title) => {
4244
+ setPdfSheet({ href, title });
4245
+ }, []);
2093
4246
  const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
2094
4247
  const [toast, setToast] = useState(null);
2095
4248
  const copyResetTimerRef = useRef(null);
@@ -2287,7 +4440,8 @@ function AssistantMessageV2({
2287
4440
  content: displayContent,
2288
4441
  isStreaming: message.isStreaming && !isCancelled || isResponseTyping,
2289
4442
  isResolvingImages: message.isResolvingImages,
2290
- onImageClick
4443
+ onImageClick,
4444
+ onPdfClick: handlePdfClick
2291
4445
  }
2292
4446
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
2293
4447
  isCancelled && message.isStreaming && /* @__PURE__ */ jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
@@ -2385,6 +4539,14 @@ function AssistantMessageV2({
2385
4539
  showToast("Thank you for your feedback", "success");
2386
4540
  }
2387
4541
  }
4542
+ ),
4543
+ /* @__PURE__ */ jsx(
4544
+ PdfSheetV2,
4545
+ {
4546
+ src: pdfSheet?.href ?? null,
4547
+ title: pdfSheet?.title ?? "",
4548
+ onClose: () => setPdfSheet(null)
4549
+ }
2388
4550
  )
2389
4551
  ] });
2390
4552
  }
@@ -2483,56 +4645,61 @@ function OtpInputV2({
2483
4645
  i
2484
4646
  )) });
2485
4647
  }
4648
+ var DEFAULT_CODE_LEN = 6;
2486
4649
  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,
4650
+ function codeLengthFromSchema(prompt) {
4651
+ const field = prompt.requestedSchema?.properties?.verificationCode;
4652
+ const max = typeof field?.maxLength === "number" ? field.maxLength : void 0;
4653
+ const min = typeof field?.minLength === "number" ? field.minLength : void 0;
4654
+ return max ?? min ?? DEFAULT_CODE_LEN;
4655
+ }
4656
+ function VerificationInline({
4657
+ prompt,
4658
+ secondsLeft,
4659
+ expired,
4660
+ onSubmit,
4661
+ onCancel,
2495
4662
  onResend
2496
4663
  }) {
2497
- const [otp, setOtp] = useState("");
2498
- const [otpErrored, setOtpErrored] = useState(false);
4664
+ const isNumeric = prompt.verificationType !== "ALPHANUMERIC_CODE";
4665
+ const codeLen = useMemo(() => codeLengthFromSchema(prompt), [prompt]);
4666
+ const [code, setCode] = useState("");
4667
+ const [errored, setErrored] = useState(false);
2499
4668
  const [resendSec, setResendSec] = useState(0);
2500
- const [localPending, setLocalPending] = useState(false);
2501
4669
  const lastSubmittedRef = useRef(null);
2502
4670
  const resendTimerRef = useRef(void 0);
4671
+ const status = prompt.status;
4672
+ const busy = status === "submitting";
4673
+ const stale = status === "stale";
4674
+ const locked = busy || stale || expired;
2503
4675
  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) {
4676
+ if (prompt.subAction === "SubmissionInvalid") {
4677
+ setErrored(true);
4678
+ setCode("");
2516
4679
  lastSubmittedRef.current = null;
2517
4680
  }
2518
- }, [otp]);
4681
+ }, [prompt.subAction, prompt.userActionId]);
2519
4682
  useEffect(() => {
2520
- if (status !== "pending") {
2521
- setLocalPending(false);
2522
- }
2523
- }, [status]);
4683
+ if (code.length < codeLen) lastSubmittedRef.current = null;
4684
+ }, [code, codeLen]);
4685
+ const doSubmit = useCallback(
4686
+ (value) => {
4687
+ if (locked || !value) return;
4688
+ if (lastSubmittedRef.current === value) return;
4689
+ lastSubmittedRef.current = value;
4690
+ void onSubmit(prompt.userActionId, { verificationCode: value }).catch(() => {
4691
+ lastSubmittedRef.current = null;
4692
+ setErrored(true);
4693
+ });
4694
+ },
4695
+ [locked, onSubmit, prompt.userActionId]
4696
+ );
2524
4697
  useEffect(() => {
2525
- if (otp.length !== OTP_LEN || !/^\d+$/.test(otp) || status !== "pending") {
2526
- return;
4698
+ if (!isNumeric || locked) return;
4699
+ if (code.length === codeLen && /^\d+$/.test(code)) {
4700
+ doSubmit(code);
2527
4701
  }
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]);
4702
+ }, [code, codeLen, doSubmit, isNumeric, locked]);
2536
4703
  useEffect(() => {
2537
4704
  return () => {
2538
4705
  if (typeof resendTimerRef.current === "number") {
@@ -2559,94 +4726,309 @@ function VerificationCardV2({
2559
4726
  }, 1e3);
2560
4727
  }, []);
2561
4728
  const handleResend = useCallback(async () => {
2562
- if (resendSec > 0 || status === "verifying" || localPending) return;
2563
- setLocalPending(true);
4729
+ if (locked || resendSec > 0) return;
4730
+ setErrored(false);
4731
+ setCode("");
4732
+ lastSubmittedRef.current = null;
2564
4733
  try {
2565
- await onResend(messageId);
4734
+ await onResend(prompt.userActionId);
2566
4735
  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);
4736
+ } catch {
2578
4737
  }
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",
4738
+ }, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
4739
+ const handleCancel = useCallback(() => {
4740
+ if (busy) return;
4741
+ void onCancel(prompt.userActionId);
4742
+ }, [busy, onCancel, prompt.userActionId]);
4743
+ const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4744
+ return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
4745
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4746
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
4747
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Verification required" }),
4748
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4749
+ ] }),
4750
+ /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: description }),
4751
+ stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4752
+ isNumeric ? /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-field", children: /* @__PURE__ */ jsx(
4753
+ OtpInputV2,
4754
+ {
4755
+ value: code,
4756
+ onChange: (v) => {
4757
+ setErrored(false);
4758
+ setCode(v);
4759
+ },
4760
+ maxLength: codeLen,
4761
+ disabled: locked,
4762
+ error: errored
4763
+ }
4764
+ ) }) : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-field", children: /* @__PURE__ */ jsx(
4765
+ "input",
4766
+ {
4767
+ type: "text",
4768
+ className: cn("payman-v2-ua-input", errored && "payman-v2-ua-input-error"),
4769
+ value: code,
4770
+ disabled: locked,
4771
+ autoComplete: "one-time-code",
4772
+ placeholder: "Verification code",
4773
+ onChange: (e) => {
4774
+ setErrored(false);
4775
+ setCode(e.target.value);
4776
+ },
4777
+ onKeyDown: (e) => {
4778
+ if (e.key === "Enter") {
4779
+ e.preventDefault();
4780
+ doSubmit(code.trim());
4781
+ }
4782
+ }
4783
+ }
4784
+ ) }),
4785
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-actions", children: [
4786
+ !isNumeric && /* @__PURE__ */ jsx(
4787
+ "button",
2594
4788
  {
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
- ]
4789
+ type: "button",
4790
+ className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
4791
+ disabled: locked || !code.trim(),
4792
+ onClick: () => doSubmit(code.trim()),
4793
+ children: busy ? "Submitting\u2026" : "Submit"
2637
4794
  }
2638
4795
  ),
2639
4796
  /* @__PURE__ */ jsx(
2640
4797
  "button",
2641
4798
  {
2642
4799
  type: "button",
2643
- className: "payman-v2-verification-resend-link",
2644
- disabled: busy || resendSec > 0,
4800
+ className: "payman-v2-ua-link",
4801
+ disabled: locked || resendSec > 0,
2645
4802
  onClick: () => void handleResend(),
2646
4803
  children: resendSec > 0 ? `Resend (${resendSec}s)` : "Resend"
2647
4804
  }
4805
+ ),
4806
+ /* @__PURE__ */ jsx(
4807
+ "button",
4808
+ {
4809
+ type: "button",
4810
+ className: "payman-v2-ua-link payman-v2-ua-link-danger",
4811
+ disabled: busy,
4812
+ onClick: handleCancel,
4813
+ children: "Cancel"
4814
+ }
2648
4815
  )
2649
- ]
4816
+ ] })
4817
+ ] })
4818
+ ] });
4819
+ }
4820
+ function SchemaFormInline({
4821
+ prompt,
4822
+ secondsLeft,
4823
+ expired,
4824
+ onSubmit,
4825
+ onCancel
4826
+ }) {
4827
+ const schema = prompt.requestedSchema;
4828
+ const fields = useMemo(() => renderableFields(schema), [schema]);
4829
+ const [values, setValues] = useState(() => {
4830
+ const init2 = {};
4831
+ for (const [key, field] of fields) init2[key] = defaultValueFor(field);
4832
+ return init2;
4833
+ });
4834
+ const [errors, setErrors] = useState({});
4835
+ const status = prompt.status;
4836
+ const busy = status === "submitting";
4837
+ const stale = status === "stale";
4838
+ const locked = busy || stale || expired;
4839
+ const setValue = (key, value) => {
4840
+ setValues((prev) => ({ ...prev, [key]: value }));
4841
+ setErrors((prev) => {
4842
+ if (!prev[key]) return prev;
4843
+ const next = { ...prev };
4844
+ delete next[key];
4845
+ return next;
4846
+ });
4847
+ };
4848
+ const handleSubmit = () => {
4849
+ if (locked) return;
4850
+ const validation = validateForm(schema, values);
4851
+ if (Object.keys(validation).length > 0) {
4852
+ setErrors(validation);
4853
+ return;
4854
+ }
4855
+ void onSubmit(prompt.userActionId, buildContent(schema, values)).catch(() => {
4856
+ });
4857
+ };
4858
+ return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Action required", children: [
4859
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4860
+ /* @__PURE__ */ jsx(Pencil, { className: "payman-v2-ua-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4861
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4862
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4863
+ ] }),
4864
+ prompt.message?.trim() && /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: prompt.message }),
4865
+ 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]) => {
4866
+ const widget = classifyField(field);
4867
+ const label = field.title || key;
4868
+ const required = isRequired(schema, key);
4869
+ const err = errors[key];
4870
+ const fieldId = `ua-${prompt.userActionId}-${key}`;
4871
+ if (widget === "boolean") {
4872
+ return /* @__PURE__ */ jsxs("label", { className: "payman-v2-ua-check", children: [
4873
+ /* @__PURE__ */ jsx(
4874
+ "input",
4875
+ {
4876
+ type: "checkbox",
4877
+ checked: Boolean(values[key]),
4878
+ disabled: locked,
4879
+ onChange: (e) => setValue(key, e.target.checked)
4880
+ }
4881
+ ),
4882
+ /* @__PURE__ */ jsxs("span", { children: [
4883
+ label,
4884
+ required && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-req", children: "*" })
4885
+ ] })
4886
+ ] }, key);
4887
+ }
4888
+ return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-row", children: [
4889
+ /* @__PURE__ */ jsxs("label", { htmlFor: fieldId, className: "payman-v2-ua-label", children: [
4890
+ label,
4891
+ required && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-req", children: "*" })
4892
+ ] }),
4893
+ field.description && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-hint", children: field.description }),
4894
+ widget === "select" ? /* @__PURE__ */ jsxs(
4895
+ "select",
4896
+ {
4897
+ id: fieldId,
4898
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4899
+ value: String(values[key] ?? ""),
4900
+ disabled: locked,
4901
+ onChange: (e) => setValue(key, e.target.value),
4902
+ children: [
4903
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select\u2026" }),
4904
+ getOptions(field).map((opt) => /* @__PURE__ */ jsx("option", { value: opt.const, children: opt.title || opt.const }, opt.const))
4905
+ ]
4906
+ }
4907
+ ) : /* @__PURE__ */ jsx(
4908
+ "input",
4909
+ {
4910
+ id: fieldId,
4911
+ type: widget === "integer" || widget === "decimal" ? "number" : "text",
4912
+ inputMode: widget === "integer" ? "numeric" : widget === "decimal" ? "decimal" : void 0,
4913
+ step: widget === "decimal" ? "any" : widget === "integer" ? "1" : void 0,
4914
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4915
+ value: String(values[key] ?? ""),
4916
+ disabled: locked,
4917
+ placeholder: field.description ? void 0 : label,
4918
+ onChange: (e) => setValue(key, e.target.value),
4919
+ onKeyDown: (e) => {
4920
+ if (e.key === "Enter") {
4921
+ e.preventDefault();
4922
+ handleSubmit();
4923
+ }
4924
+ }
4925
+ }
4926
+ ),
4927
+ err && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-error", children: err })
4928
+ ] }, key);
4929
+ }) }),
4930
+ !stale && /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-actions", children: [
4931
+ /* @__PURE__ */ jsx(
4932
+ "button",
4933
+ {
4934
+ type: "button",
4935
+ className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
4936
+ disabled: locked,
4937
+ onClick: handleSubmit,
4938
+ children: busy ? "Submitting\u2026" : "Submit"
4939
+ }
4940
+ ),
4941
+ /* @__PURE__ */ jsx(
4942
+ "button",
4943
+ {
4944
+ type: "button",
4945
+ className: "payman-v2-ua-link payman-v2-ua-link-danger",
4946
+ disabled: busy,
4947
+ onClick: () => void onCancel(prompt.userActionId),
4948
+ children: "Cancel"
4949
+ }
4950
+ )
4951
+ ] })
4952
+ ] });
4953
+ }
4954
+ function NotificationInline({ notification, onDismiss }) {
4955
+ return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-note", role: "status", children: [
4956
+ /* @__PURE__ */ jsx(Info, { className: "payman-v2-ua-note-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4957
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-note-text", children: notification.message }),
4958
+ onDismiss && /* @__PURE__ */ jsx(
4959
+ "button",
4960
+ {
4961
+ type: "button",
4962
+ className: "payman-v2-ua-note-dismiss",
4963
+ "aria-label": "Dismiss notification",
4964
+ onClick: () => onDismiss(notification.id),
4965
+ children: /* @__PURE__ */ jsx(X, { size: 13, strokeWidth: 2 })
4966
+ }
4967
+ )
4968
+ ] });
4969
+ }
4970
+ function useExpiryCountdown(prompt) {
4971
+ const initial = typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0 ? Math.floor(prompt.expirySeconds) : void 0;
4972
+ const [secondsLeft, setSecondsLeft] = useState(initial);
4973
+ useEffect(() => {
4974
+ if (initial === void 0) {
4975
+ setSecondsLeft(void 0);
4976
+ return;
4977
+ }
4978
+ setSecondsLeft(initial);
4979
+ const id = window.setInterval(() => {
4980
+ setSecondsLeft((s) => {
4981
+ if (s === void 0) return s;
4982
+ if (s <= 1) {
4983
+ window.clearInterval(id);
4984
+ return 0;
4985
+ }
4986
+ return s - 1;
4987
+ });
4988
+ }, 1e3);
4989
+ return () => window.clearInterval(id);
4990
+ }, [prompt.userActionId, prompt.subAction, initial]);
4991
+ const expired = initial !== void 0 && secondsLeft === 0;
4992
+ return [secondsLeft, expired];
4993
+ }
4994
+ function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
4995
+ const [secondsLeft, expired] = useExpiryCountdown(prompt);
4996
+ let body;
4997
+ if (prompt.kind === "verification") {
4998
+ body = /* @__PURE__ */ jsx(
4999
+ VerificationInline,
5000
+ {
5001
+ prompt,
5002
+ secondsLeft,
5003
+ expired,
5004
+ onSubmit,
5005
+ onCancel,
5006
+ onResend
5007
+ }
5008
+ );
5009
+ } else if (prompt.kind === "notification") {
5010
+ const note = { id: prompt.userActionId, message: prompt.message ?? "" };
5011
+ body = /* @__PURE__ */ jsx(NotificationInline, { notification: note });
5012
+ } else {
5013
+ body = /* @__PURE__ */ jsx(
5014
+ SchemaFormInline,
5015
+ {
5016
+ prompt,
5017
+ secondsLeft,
5018
+ expired,
5019
+ onSubmit,
5020
+ onCancel
5021
+ }
5022
+ );
5023
+ }
5024
+ return /* @__PURE__ */ jsx(
5025
+ motion.div,
5026
+ {
5027
+ className: "payman-v2-ua-wrap",
5028
+ initial: { opacity: 0, y: 8 },
5029
+ animate: { opacity: 1, y: 0 },
5030
+ transition: { type: "spring", stiffness: 320, damping: 28 },
5031
+ children: body
2650
5032
  }
2651
5033
  );
2652
5034
  }
@@ -2662,13 +5044,17 @@ var MessageListV2 = forwardRef(
2662
5044
  onExecutionTraceClick,
2663
5045
  messageActions,
2664
5046
  retryDisabled = false,
2665
- userAction,
2666
- onApproveAction,
2667
- onRejectAction,
2668
- onResendAction,
5047
+ userActionPrompts,
5048
+ notifications,
5049
+ onSubmitUserAction,
5050
+ onCancelUserAction,
5051
+ onResendUserAction,
5052
+ onDismissNotification,
2669
5053
  onSubmitFeedback,
2670
5054
  typingSpeed = 4
2671
5055
  }, ref) {
5056
+ const noop = useCallback(async () => {
5057
+ }, []);
2672
5058
  const scrollRef = useRef(null);
2673
5059
  const scrollInnerRef = useRef(null);
2674
5060
  const isNearBottomRef = useRef(true);
@@ -2811,20 +5197,24 @@ var MessageListV2 = forwardRef(
2811
5197
  typingSpeed
2812
5198
  }
2813
5199
  ) }, message.id)),
2814
- userAction && userAction.status !== "approved" && userAction.status !== "rejected" && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
2815
- VerificationCardV2,
5200
+ notifications?.map((note) => /* @__PURE__ */ jsx(
5201
+ NotificationInline,
2816
5202
  {
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
- ) })
5203
+ notification: note,
5204
+ onDismiss: onDismissNotification
5205
+ },
5206
+ note.id
5207
+ )),
5208
+ userActionPrompts?.map((prompt) => /* @__PURE__ */ jsx(
5209
+ UserActionInline,
5210
+ {
5211
+ prompt,
5212
+ onSubmit: onSubmitUserAction ?? noop,
5213
+ onCancel: onCancelUserAction ?? noop,
5214
+ onResend: onResendUserAction ?? noop
5215
+ },
5216
+ prompt.toolCallId || prompt.userActionId
5217
+ ))
2828
5218
  ]
2829
5219
  }
2830
5220
  )
@@ -3885,10 +6275,13 @@ function TimelineBars({
3885
6275
  ] });
3886
6276
  }
3887
6277
  var DEFAULT_USER_ACTION_STATE = {
3888
- request: null,
3889
- result: null};
6278
+ prompts: [],
6279
+ notifications: []
6280
+ };
3890
6281
  var NOOP_ASYNC = async () => {
3891
6282
  };
6283
+ var NOOP = () => {
6284
+ };
3892
6285
  function useSentryChatCallbacks(callbacks, config) {
3893
6286
  const sentryCtxRef = useRef({});
3894
6287
  const callbacksRef = useRef(callbacks);
@@ -3958,7 +6351,7 @@ function useSentryChatCallbacks(callbacks, config) {
3958
6351
  onAttachFileClick: () => callbacksRef.current?.onAttachFileClick?.(),
3959
6352
  onMessageFeedback: (data) => callbacksRef.current?.onMessageFeedback?.(data),
3960
6353
  onUserActionRequired: (req) => callbacksRef.current?.onUserActionRequired?.(req),
3961
- onUserActionEvent: (evType, msg) => callbacksRef.current?.onUserActionEvent?.(evType, msg),
6354
+ onUserNotification: (note) => callbacksRef.current?.onUserNotification?.(note),
3962
6355
  onStatusMessage: (msg) => callbacksRef.current?.onStatusMessage?.(msg),
3963
6356
  onStepsUpdate: (steps) => callbacksRef.current?.onStepsUpdate?.(steps)
3964
6357
  }),
@@ -4026,10 +6419,11 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
4026
6419
  }
4027
6420
  }, [editingMessageId, messages]);
4028
6421
  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";
6422
+ const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
6423
+ const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
6424
+ const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6425
+ const dismissNotification = chat.dismissNotification ?? NOOP;
6426
+ const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
4033
6427
  const {
4034
6428
  transcribedText,
4035
6429
  isAvailable: voiceAvailable,
@@ -4270,23 +6664,8 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
4270
6664
  setLightboxSrc(src);
4271
6665
  setLightboxAlt(alt);
4272
6666
  };
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]);
6667
+ const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
6668
+ const notifications = userActionState.notifications;
4290
6669
  const handleV2Send = (text) => {
4291
6670
  if (isRecording) stopRecording();
4292
6671
  if (text.trim() && !disableInput && isSessionParamsConfigured) {
@@ -4339,7 +6718,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
4339
6718
  style,
4340
6719
  children: [
4341
6720
  children,
4342
- isChatDisabled ? disabledComponent || /* @__PURE__ */ jsx("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "1rem" }, children: /* @__PURE__ */ jsx("div", { style: { textAlign: "center", color: "var(--payman-v2-text-3)", fontSize: "0.875rem" }, children: "Chat is currently disabled" }) }) : /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsx(
6721
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsx(
4343
6722
  motion.div,
4344
6723
  {
4345
6724
  initial: { opacity: 1 },
@@ -4438,16 +6817,12 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
4438
6817
  messageActions,
4439
6818
  retryDisabled: isWaitingForResponse,
4440
6819
  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,
6820
+ userActionPrompts,
6821
+ notifications,
6822
+ onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6823
+ onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6824
+ onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6825
+ onDismissNotification: dismissNotification,
4451
6826
  onSubmitFeedback: handleSubmitFeedback
4452
6827
  }
4453
6828
  ),
@@ -4530,6 +6905,6 @@ var PaymanChat = forwardRef(
4530
6905
  }
4531
6906
  );
4532
6907
 
4533
- export { PaymanChat, PaymanChatContext, captureSentryError, cn, formatDate, usePaymanChat };
6908
+ export { PaymanChat, PaymanChatContext, UserActionStaleError, cancelUserAction, captureSentryError, cn, formatDate, resendUserAction, submitUserAction, useChatV2, usePaymanChat, useVoice };
4534
6909
  //# sourceMappingURL=index.mjs.map
4535
6910
  //# sourceMappingURL=index.mjs.map