@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.js CHANGED
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var paymanTypescriptAskSdk = require('@paymanai/payman-typescript-ask-sdk');
4
- var framerMotion = require('framer-motion');
5
3
  var react = require('react');
4
+ var framerMotion = require('framer-motion');
6
5
  var clsx = require('clsx');
7
6
  var tailwindMerge = require('tailwind-merge');
8
7
  var Sentry = require('@sentry/react');
@@ -28,17 +27,1974 @@ function _interopNamespace(e) {
28
27
  get: function () { return e[k]; }
29
28
  });
30
29
  }
31
- });
30
+ });
31
+ }
32
+ n.default = e;
33
+ return Object.freeze(n);
34
+ }
35
+
36
+ var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
37
+ var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
38
+ var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
39
+ var remarkBreaks__default = /*#__PURE__*/_interopDefault(remarkBreaks);
40
+
41
+ var __defProp = Object.defineProperty;
42
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
43
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value);
44
+ function generateId() {
45
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
46
+ const r = Math.random() * 16 | 0;
47
+ const v = c === "x" ? r : r & 3 | 8;
48
+ return v.toString(16);
49
+ });
50
+ }
51
+ function yieldAfterProgressEvent(event) {
52
+ const t = event.eventType;
53
+ if (t === "RUN_IN_PROGRESS" || t === "INTENT_PROGRESS" || t === "AGGREGATOR_THINKING_CONT" || t === "INTENT_THINKING_CONT" || t === "THINKING_DELTA") {
54
+ return new Promise((resolve) => setTimeout(resolve, 0));
55
+ }
56
+ return Promise.resolve();
57
+ }
58
+ function parseJSONBuffer(buffer) {
59
+ const events = [];
60
+ let braceCount = 0;
61
+ let startIndex = 0;
62
+ let inString = false;
63
+ let escapeNext = false;
64
+ let lastParsedIndex = -1;
65
+ for (let i = 0; i < buffer.length; i++) {
66
+ const char = buffer[i];
67
+ if (escapeNext) {
68
+ escapeNext = false;
69
+ continue;
70
+ }
71
+ if (char === "\\") {
72
+ escapeNext = true;
73
+ continue;
74
+ }
75
+ if (char === '"' && !escapeNext) {
76
+ inString = !inString;
77
+ continue;
78
+ }
79
+ if (inString) {
80
+ continue;
81
+ }
82
+ if (char === "{") {
83
+ if (braceCount === 0) {
84
+ startIndex = i;
85
+ }
86
+ braceCount++;
87
+ } else if (char === "}") {
88
+ braceCount--;
89
+ if (braceCount === 0) {
90
+ const jsonStr = buffer.substring(startIndex, i + 1);
91
+ try {
92
+ const parsed = JSON.parse(jsonStr);
93
+ const event = parsed;
94
+ events.push(event);
95
+ lastParsedIndex = i;
96
+ } catch (err) {
97
+ console.error("Failed to parse JSON event:", jsonStr, err);
98
+ }
99
+ }
100
+ }
101
+ }
102
+ const remaining = lastParsedIndex >= 0 ? buffer.substring(lastParsedIndex + 1) : buffer;
103
+ return { events, remaining };
104
+ }
105
+ async function streamWorkflowEvents(url, body, headers, options = {}) {
106
+ const { signal, onEvent, onError, onComplete } = options;
107
+ try {
108
+ const response = await fetch(url, {
109
+ method: "POST",
110
+ headers: {
111
+ "Content-Type": "application/json",
112
+ ...headers
113
+ },
114
+ body: JSON.stringify(body),
115
+ signal
116
+ });
117
+ if (!response.ok) {
118
+ const errorText = await response.text();
119
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
120
+ }
121
+ const reader = response.body?.getReader();
122
+ if (!reader) {
123
+ throw new Error("No response body");
124
+ }
125
+ const decoder = new TextDecoder();
126
+ let buffer = "";
127
+ while (true) {
128
+ const { done, value } = await reader.read();
129
+ if (done) {
130
+ if (buffer.trim()) {
131
+ const { events: events2 } = parseJSONBuffer(buffer);
132
+ for (const event of events2) {
133
+ onEvent?.(event);
134
+ await yieldAfterProgressEvent(event);
135
+ }
136
+ }
137
+ break;
138
+ }
139
+ buffer += decoder.decode(value, { stream: true });
140
+ const { events, remaining } = parseJSONBuffer(buffer);
141
+ for (const event of events) {
142
+ onEvent?.(event);
143
+ await yieldAfterProgressEvent(event);
144
+ }
145
+ buffer = remaining;
146
+ }
147
+ onComplete?.();
148
+ } catch (error) {
149
+ if (error instanceof Error && error.name === "AbortError") {
150
+ return;
151
+ }
152
+ onError?.(error);
153
+ }
154
+ }
155
+ var STAGE_LABELS = {
156
+ sanitizer: "Checking your request",
157
+ analyzer: "Understanding what you're asking",
158
+ prefetcher: "Gathering context",
159
+ planner: "Planning how to handle this",
160
+ execution: "Working on it",
161
+ formatter: "Writing the response"
162
+ };
163
+ function stageLabel(stage) {
164
+ const label = STAGE_LABELS[stage];
165
+ if (label) return label;
166
+ const pretty = stage.replace(/[_-]/g, " ");
167
+ return `Running ${pretty}`;
168
+ }
169
+ function isBlandStatus(message) {
170
+ if (!message) return true;
171
+ const normalized = message.trim().toLowerCase().replace(/[…\.]+$/, "").trim();
172
+ return BLAND_STATUS_LABELS.has(normalized);
173
+ }
174
+ var BLAND_STATUS_LABELS = /* @__PURE__ */ new Set([
175
+ "executing",
176
+ "working on it",
177
+ "thinking",
178
+ "processing",
179
+ "reviewing your request",
180
+ "composing response",
181
+ "checking your request",
182
+ "polishing the response"
183
+ ]);
184
+ function getEventMessage(event) {
185
+ if (event.message?.trim()) {
186
+ return event.message.trim();
187
+ }
188
+ if (event.errorMessage?.trim()) {
189
+ return event.errorMessage.trim();
190
+ }
191
+ const eventType = event.eventType;
192
+ switch (eventType) {
193
+ case "RUN_STARTED":
194
+ return "Starting agent run...";
195
+ case "TOOL_CALL_STARTED": {
196
+ const description = typeof event.description === "string" ? event.description.trim() : "";
197
+ if (description) return description;
198
+ const toolName = typeof event.toolName === "string" ? event.toolName : "";
199
+ return toolName ? `Calling ${toolName}...` : "Calling tool...";
200
+ }
201
+ case "TOOL_CALL_COMPLETED": {
202
+ const description = typeof event.description === "string" ? event.description.trim() : "";
203
+ if (description) return description;
204
+ const toolName = typeof event.toolName === "string" ? event.toolName : "";
205
+ return toolName ? `${toolName} completed` : "Tool call completed";
206
+ }
207
+ case "RUN_IN_PROGRESS":
208
+ return event.partialText || "Thinking...";
209
+ case "RUN_COMPLETED":
210
+ return "Agent run completed";
211
+ case "RUN_FAILED":
212
+ return event.errorMessage || "Agent run failed";
213
+ case "KEEP_ALIVE":
214
+ return event.description || "";
215
+ case "THINKING_DELTA":
216
+ return event.text || "";
217
+ default:
218
+ return eventType;
219
+ }
220
+ }
221
+ function extractResponseContent(response) {
222
+ if (typeof response === "string") {
223
+ return response;
224
+ }
225
+ if (typeof response === "object" && response !== null) {
226
+ const resp = response;
227
+ if ("text" in resp && typeof resp.text === "string") {
228
+ return resp.text;
229
+ }
230
+ if ("content" in resp && typeof resp.content === "string") {
231
+ return resp.content;
232
+ }
233
+ if ("message" in resp && typeof resp.message === "string") {
234
+ return resp.message;
235
+ }
236
+ if ("answer" in resp && typeof resp.answer === "string") {
237
+ return resp.answer;
238
+ }
239
+ return JSON.stringify(response);
240
+ }
241
+ return "";
242
+ }
243
+ function normalizeEvent(event) {
244
+ const type = event.eventType;
245
+ switch (type) {
246
+ case "RUN_STARTED":
247
+ return { ...event, eventType: "WORKFLOW_STARTED" };
248
+ case "TOOL_CALL_STARTED": {
249
+ const toolName = typeof event.toolName === "string" ? event.toolName : void 0;
250
+ const description = typeof event.description === "string" ? event.description.trim() : "";
251
+ return {
252
+ ...event,
253
+ eventType: "INTENT_STARTED",
254
+ workerName: toolName ?? event.workerName,
255
+ message: description || event.message
256
+ };
257
+ }
258
+ case "TOOL_CALL_COMPLETED": {
259
+ const toolName = typeof event.toolName === "string" ? event.toolName : void 0;
260
+ const description = typeof event.description === "string" ? event.description.trim() : "";
261
+ return {
262
+ ...event,
263
+ eventType: "INTENT_COMPLETED",
264
+ workerName: toolName ?? event.workerName,
265
+ message: description || event.message
266
+ };
267
+ }
268
+ case "RUN_IN_PROGRESS":
269
+ return {
270
+ ...event,
271
+ eventType: "INTENT_PROGRESS",
272
+ message: event.partialText ?? event.message
273
+ };
274
+ case "RUN_COMPLETED":
275
+ return {
276
+ ...event,
277
+ eventType: "WORKFLOW_COMPLETED",
278
+ // state machine reads event.response for the final content
279
+ response: event.response ?? event.message
280
+ };
281
+ case "RUN_FAILED":
282
+ return {
283
+ ...event,
284
+ eventType: "WORKFLOW_ERROR",
285
+ errorMessage: event.errorMessage ?? event.message
286
+ };
287
+ default:
288
+ return event;
289
+ }
290
+ }
291
+ function isVerificationSchema(schema) {
292
+ const props = schema?.properties;
293
+ if (!props) return false;
294
+ const keys = Object.keys(props);
295
+ return keys.length === 1 && keys[0] === "verificationCode";
296
+ }
297
+ function classifyUserActionKind(action, schema) {
298
+ switch ((action || "").toLowerCase()) {
299
+ case "userverificationrequest":
300
+ return "verification";
301
+ case "usernotificationrequest":
302
+ return "notification";
303
+ case "userformrequest":
304
+ return isVerificationSchema(schema) ? "verification" : "form";
305
+ default:
306
+ return isVerificationSchema(schema) ? "verification" : "form";
307
+ }
308
+ }
309
+ function userActionHeader(kind) {
310
+ return kind === "verification" ? "**Verification required**" : "**Action required**";
311
+ }
312
+ function workingPhaseDetailForDisplay(raw) {
313
+ const t = raw.trim();
314
+ if (!t) return "";
315
+ if (/^Identified\s+\d+\s+tasks?\s+to\s+execute\.?$/i.test(t)) {
316
+ return "";
317
+ }
318
+ return t;
319
+ }
320
+ function getEventText(event, field) {
321
+ const value = event[field];
322
+ return typeof value === "string" ? value.trim() : "";
323
+ }
324
+ function shouldShowIntentHeader(event) {
325
+ const workerName = getEventText(event, "workerName");
326
+ const intentId = getEventText(event, "intentId");
327
+ return Boolean(workerName && intentId && workerName === intentId);
328
+ }
329
+ function addThinkingHeader(state, header) {
330
+ state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + header;
331
+ }
332
+ function addThinkingDetail(state, detail) {
333
+ const trimmed = detail.trim();
334
+ if (!trimmed) return;
335
+ state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + trimmed;
336
+ }
337
+ function addThinkingLine(state, header, detail) {
338
+ state.formattedThinkingText += (state.formattedThinkingText ? "\n" : "") + header + "\n" + detail;
339
+ }
340
+ function appendThinkingText(state, text) {
341
+ state.formattedThinkingText += text;
342
+ }
343
+ function updateExecutionStageMessage(state, msg) {
344
+ if (!msg) return;
345
+ for (let i = state.steps.length - 1; i >= 0; i--) {
346
+ const s = state.steps[i];
347
+ if (s.eventType === "STAGE_STARTED" && (s.stage === "executor" || s.stage === "execution") && s.status === "in_progress") {
348
+ s.message = msg;
349
+ return;
350
+ }
351
+ }
352
+ }
353
+ function completeLastInProgressStep(steps) {
354
+ for (let i = steps.length - 1; i >= 0; i--) {
355
+ if (steps[i].status === "in_progress") {
356
+ steps[i].status = "completed";
357
+ return;
358
+ }
359
+ }
360
+ }
361
+ function createInitialV2State() {
362
+ return {
363
+ formattedThinkingText: "",
364
+ finalResponse: "",
365
+ currentWorker: "",
366
+ lastEventType: "",
367
+ sessionId: void 0,
368
+ executionId: void 0,
369
+ hasError: false,
370
+ errorMessage: "",
371
+ userActions: [],
372
+ notifications: [],
373
+ lastUserAction: void 0,
374
+ lastNotification: void 0,
375
+ finalData: void 0,
376
+ steps: [],
377
+ stepCounter: 0,
378
+ currentExecutingStepId: void 0
379
+ };
380
+ }
381
+ function upsertUserAction(state, req) {
382
+ const active = { ...req, status: "pending" };
383
+ const matchIdx = state.userActions.findIndex(
384
+ (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
385
+ );
386
+ if (matchIdx >= 0) {
387
+ state.userActions[matchIdx] = active;
388
+ } else {
389
+ state.userActions.push(active);
390
+ }
391
+ }
392
+ function processStreamEventV2(rawEvent, state) {
393
+ const event = normalizeEvent(rawEvent);
394
+ const eventType = event.eventType;
395
+ state.lastUserAction = void 0;
396
+ state.lastNotification = void 0;
397
+ if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
398
+ if (event.executionId) state.executionId = event.executionId;
399
+ if (event.sessionId) state.sessionId = event.sessionId;
400
+ const description = typeof event.description === "string" ? event.description : "";
401
+ if (description) {
402
+ for (let i = state.steps.length - 1; i >= 0; i--) {
403
+ if (state.steps[i].status === "in_progress") {
404
+ state.steps[i].message = description;
405
+ break;
406
+ }
407
+ }
408
+ }
409
+ return state;
410
+ }
411
+ if (typeof eventType === "string" && eventType.toUpperCase() === "THINKING_DELTA") {
412
+ const text = typeof event.text === "string" ? event.text : "";
413
+ if (text) {
414
+ appendThinkingText(state, text);
415
+ }
416
+ if (event.executionId) state.executionId = event.executionId;
417
+ if (event.sessionId) state.sessionId = event.sessionId;
418
+ state.lastEventType = "THINKING_DELTA";
419
+ return state;
420
+ }
421
+ if (event.executionId) state.executionId = event.executionId;
422
+ if (event.sessionId) state.sessionId = event.sessionId;
423
+ const message = getEventMessage(event);
424
+ switch (eventType) {
425
+ case "WORKFLOW_STARTED":
426
+ case "STARTED":
427
+ state.lastEventType = eventType;
428
+ break;
429
+ case "INTENT_PROGRESS": {
430
+ const rawMessage = typeof event.message === "string" ? event.message : "";
431
+ const rawPartial = typeof event.partialText === "string" ? event.partialText : "";
432
+ const delta = rawMessage || rawPartial;
433
+ if (delta.length > 0) {
434
+ state.finalResponse += delta;
435
+ }
436
+ state.lastEventType = eventType;
437
+ break;
438
+ }
439
+ case "INTENT_THINKING": {
440
+ const worker = getEventText(event, "workerName") || "Worker";
441
+ const msg = getEventText(event, "message") || "Thinking...";
442
+ const showHeader = shouldShowIntentHeader(event);
443
+ if (worker !== state.currentWorker) {
444
+ state.currentWorker = worker;
445
+ if (showHeader && msg && msg !== worker) {
446
+ addThinkingLine(state, `**${worker}**`, msg);
447
+ } else if (showHeader) {
448
+ addThinkingHeader(state, `**${worker}**`);
449
+ } else if (msg !== "Thinking...") {
450
+ addThinkingDetail(state, msg);
451
+ }
452
+ } else if (showHeader || msg !== "Thinking...") {
453
+ appendThinkingText(state, "\n" + msg);
454
+ }
455
+ const lastInProgress = [...state.steps].reverse().find((s) => s.status === "in_progress");
456
+ if (lastInProgress) {
457
+ lastInProgress.thinkingText = "";
458
+ lastInProgress.isThinking = true;
459
+ }
460
+ state.lastEventType = "INTENT_THINKING";
461
+ break;
462
+ }
463
+ case "INTENT_THINKING_CONT": {
464
+ const msg = event.message || "";
465
+ if (!msg) break;
466
+ if (state.lastEventType === "INTENT_THINKING") {
467
+ appendThinkingText(state, "\n" + msg);
468
+ } else {
469
+ appendThinkingText(state, msg);
470
+ }
471
+ const thinkingStep = [...state.steps].reverse().find((s) => s.isThinking);
472
+ if (thinkingStep) {
473
+ thinkingStep.thinkingText = (thinkingStep.thinkingText || "") + msg;
474
+ }
475
+ state.lastEventType = "INTENT_THINKING_CONT";
476
+ break;
477
+ }
478
+ case "ORCHESTRATOR_THINKING": {
479
+ addThinkingLine(state, "**Planning**", event.message || "Understanding your request...");
480
+ const stepId = `step-${state.stepCounter++}`;
481
+ state.steps.push({
482
+ id: stepId,
483
+ eventType,
484
+ message,
485
+ status: "in_progress",
486
+ timestamp: Date.now(),
487
+ elapsedMs: event.elapsedMs
488
+ });
489
+ state.currentExecutingStepId = stepId;
490
+ state.lastEventType = eventType;
491
+ break;
492
+ }
493
+ case "ORCHESTRATOR_COMPLETED": {
494
+ const workingDetail = workingPhaseDetailForDisplay(message);
495
+ if (workingDetail) {
496
+ addThinkingLine(state, "**Working**", workingDetail);
497
+ } else {
498
+ addThinkingHeader(state, "**Working**");
499
+ }
500
+ state.steps.push({
501
+ id: `step-${state.stepCounter++}`,
502
+ eventType: "WORKING",
503
+ message: workingDetail,
504
+ status: "completed",
505
+ timestamp: Date.now()
506
+ });
507
+ const step = state.steps.find((s) => s.eventType === "ORCHESTRATOR_THINKING" && s.status === "in_progress");
508
+ if (step) {
509
+ step.status = "completed";
510
+ if (event.elapsedMs) step.elapsedMs = event.elapsedMs;
511
+ if (step.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
512
+ }
513
+ state.lastEventType = eventType;
514
+ break;
515
+ }
516
+ case "INTENT_STARTED": {
517
+ const worker = getEventText(event, "workerName") || "Worker";
518
+ const msg = getEventText(event, "message") || "Starting...";
519
+ const showHeader = shouldShowIntentHeader(event);
520
+ state.currentWorker = worker;
521
+ if (showHeader && msg !== worker) {
522
+ addThinkingLine(state, `**${worker}**`, msg);
523
+ } else if (showHeader) {
524
+ addThinkingHeader(state, `**${worker}**`);
525
+ } else {
526
+ addThinkingDetail(state, msg);
527
+ }
528
+ const thinkingStep = state.steps.find((s) => s.isThinking);
529
+ if (thinkingStep) thinkingStep.isThinking = false;
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
+ updateExecutionStageMessage(state, message);
541
+ state.lastEventType = eventType;
542
+ break;
543
+ }
544
+ case "INTENT_COMPLETED": {
545
+ const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
546
+ if (intentStep) {
547
+ intentStep.status = "completed";
548
+ intentStep.isThinking = false;
549
+ if (event.elapsedMs) intentStep.elapsedMs = event.elapsedMs;
550
+ if (intentStep.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
551
+ }
552
+ state.lastEventType = eventType;
553
+ break;
554
+ }
555
+ case "AGGREGATOR_THINKING": {
556
+ addThinkingLine(state, "**Finalizing**", event.message || "Preparing response...");
557
+ const stepId = `step-${state.stepCounter++}`;
558
+ state.steps.push({
559
+ id: stepId,
560
+ eventType,
561
+ message,
562
+ status: "in_progress",
563
+ timestamp: Date.now(),
564
+ elapsedMs: event.elapsedMs
565
+ });
566
+ state.currentExecutingStepId = stepId;
567
+ state.lastEventType = eventType;
568
+ break;
569
+ }
570
+ case "AGGREGATOR_COMPLETED": {
571
+ appendThinkingText(state, "\n" + (event.message || "Response ready"));
572
+ const step = state.steps.find((s) => s.eventType === "AGGREGATOR_THINKING" && s.status === "in_progress");
573
+ if (step) {
574
+ step.status = "completed";
575
+ if (event.elapsedMs) step.elapsedMs = event.elapsedMs;
576
+ if (step.id === state.currentExecutingStepId) state.currentExecutingStepId = void 0;
577
+ }
578
+ state.lastEventType = eventType;
579
+ break;
580
+ }
581
+ case "WORKFLOW_COMPLETED":
582
+ case "COMPLETED": {
583
+ const totalTime = Number(event.totalTimeMs);
584
+ if (Number.isFinite(totalTime) && totalTime > 0) {
585
+ state.totalElapsedMs = totalTime;
586
+ }
587
+ let content = extractResponseContent(event.response);
588
+ const trace = event.trace && typeof event.trace === "object" ? event.trace : null;
589
+ if (!content && trace?.workflowMsg && typeof trace.workflowMsg === "string") {
590
+ content = trace.workflowMsg;
591
+ }
592
+ if (!content && trace?.aggregator && typeof trace.aggregator === "object") {
593
+ const agg = trace.aggregator;
594
+ if (typeof agg.response === "string") content = agg.response;
595
+ else content = extractResponseContent(agg.response);
596
+ }
597
+ if (content) {
598
+ state.finalResponse = content;
599
+ if (event.trace && typeof event.trace === "object") {
600
+ state.finalData = event.trace;
601
+ }
602
+ state.hasError = false;
603
+ state.errorMessage = "";
604
+ } else {
605
+ state.hasError = true;
606
+ state.errorMessage = "WORKFLOW_FAILED";
607
+ }
608
+ state.steps.forEach((step) => {
609
+ if (step.status === "in_progress") {
610
+ step.status = "completed";
611
+ step.isThinking = false;
612
+ }
613
+ });
614
+ state.lastEventType = eventType;
615
+ break;
616
+ }
617
+ case "USER_ACTION_REQUIRED": {
618
+ const rawAction = typeof event.action === "string" ? event.action : void 0;
619
+ const schema = event.requestedSchema;
620
+ const kind = classifyUserActionKind(rawAction, schema);
621
+ const promptMessage = typeof event.message === "string" && event.message.trim() || message || "";
622
+ const userActionId = typeof event.userActionId === "string" ? event.userActionId : "";
623
+ if (kind === "notification") {
624
+ const notification = {
625
+ id: userActionId || `note-${state.stepCounter++}`,
626
+ message: promptMessage
627
+ };
628
+ state.notifications.push(notification);
629
+ state.lastNotification = notification;
630
+ if (promptMessage) addThinkingDetail(state, promptMessage);
631
+ state.lastEventType = eventType;
632
+ break;
633
+ }
634
+ if (!userActionId) {
635
+ state.lastEventType = eventType;
636
+ break;
637
+ }
638
+ completeLastInProgressStep(state.steps);
639
+ const verificationType = event.verificationType === "ALPHANUMERIC_CODE" || event.verificationType === "NUMERIC_CODE" ? event.verificationType : void 0;
640
+ const request = {
641
+ userActionId,
642
+ kind,
643
+ rawAction,
644
+ subAction: typeof event.subAction === "string" ? event.subAction : void 0,
645
+ verificationType,
646
+ expirySeconds: typeof event.expirySeconds === "number" ? event.expirySeconds : void 0,
647
+ message: promptMessage || void 0,
648
+ requestedSchema: schema,
649
+ metadata: event.metadata,
650
+ toolCallId: typeof event.toolCallId === "string" ? event.toolCallId : void 0,
651
+ executionId: state.executionId,
652
+ sessionId: state.sessionId
653
+ };
654
+ upsertUserAction(state, request);
655
+ state.lastUserAction = request;
656
+ const header = userActionHeader(kind);
657
+ if (promptMessage) addThinkingLine(state, header, promptMessage);
658
+ else addThinkingHeader(state, header);
659
+ const stepId = `step-${state.stepCounter++}`;
660
+ state.steps.push({
661
+ id: stepId,
662
+ eventType,
663
+ message: promptMessage || (kind === "verification" ? "Waiting for verification..." : "Waiting for your input..."),
664
+ status: "in_progress",
665
+ timestamp: Date.now(),
666
+ elapsedMs: event.elapsedMs
667
+ });
668
+ state.currentExecutingStepId = stepId;
669
+ state.lastEventType = eventType;
670
+ break;
671
+ }
672
+ case "USER_NOTIFICATION": {
673
+ const noteMessage = typeof event.message === "string" && event.message.trim() || message || "";
674
+ const userActionId = typeof event.userActionId === "string" ? event.userActionId : "";
675
+ if (noteMessage || userActionId) {
676
+ const notification = {
677
+ id: userActionId || `note-${state.stepCounter++}`,
678
+ message: noteMessage
679
+ };
680
+ state.notifications.push(notification);
681
+ state.lastNotification = notification;
682
+ if (noteMessage) addThinkingDetail(state, noteMessage);
683
+ }
684
+ state.lastEventType = eventType;
685
+ break;
686
+ }
687
+ case "WORKFLOW_ERROR":
688
+ case "ERROR":
689
+ state.hasError = true;
690
+ state.errorMessage = event.errorMessage || event.message || "Workflow error";
691
+ state.lastEventType = eventType;
692
+ break;
693
+ case "INTENT_ERROR": {
694
+ state.errorMessage = message || event.errorMessage || "An error occurred";
695
+ const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
696
+ if (intentStep) {
697
+ intentStep.status = "error";
698
+ intentStep.isThinking = false;
699
+ }
700
+ state.lastEventType = eventType;
701
+ break;
702
+ }
703
+ // ---- K2 pipeline stage lifecycle events ----
704
+ //
705
+ // The k2-server playground streaming API emits
706
+ // STAGE_STARTED / STAGE_COMPLETED / STAGE_FAILED /
707
+ // STAGE_SKIPPED around each pipeline stage so the UI can
708
+ // show real progress (sanitizer → analyzer → planner → …)
709
+ // instead of a static placeholder. Each STAGE_STARTED
710
+ // pushes an in-progress step with a user-facing label; the
711
+ // matching STAGE_COMPLETED/FAILED closes it WITHOUT
712
+ // rewriting the label — a terse "completed" flash between
713
+ // stages was more noise than signal. STAGE_SKIPPED removes
714
+ // the just-opened step entirely (skipping is expected on
715
+ // DIRECT_RESPONSE short-circuits).
716
+ //
717
+ // Finding the matching step uses `stepId` directly rather
718
+ // than comparing labels — the processor remembers which id
719
+ // it minted for the latest open STAGE_STARTED and the
720
+ // closing events look it up from state.currentExecutingStepId.
721
+ case "STAGE_STARTED": {
722
+ const stage = getEventText(event, "stage");
723
+ if (!stage) {
724
+ state.lastEventType = eventType;
725
+ break;
726
+ }
727
+ const serverMessage = getEventText(event, "message");
728
+ let initialMessage = serverMessage || stageLabel(stage);
729
+ if (stage === "executor" || stage === "execution") {
730
+ for (let i = state.steps.length - 1; i >= 0; i--) {
731
+ const s = state.steps[i];
732
+ if (s.eventType === "STAGE_STARTED" && s.stage === "analyzer" && s.status === "completed" && s.message) {
733
+ initialMessage = s.message;
734
+ break;
735
+ }
736
+ }
737
+ }
738
+ const stepId = `stage-${stage}-${state.stepCounter++}`;
739
+ state.steps.push({
740
+ id: stepId,
741
+ eventType,
742
+ message: initialMessage,
743
+ status: "in_progress",
744
+ timestamp: Date.now(),
745
+ stage
746
+ });
747
+ state.currentExecutingStepId = stepId;
748
+ state.lastEventType = eventType;
749
+ break;
750
+ }
751
+ case "STAGE_COMPLETED": {
752
+ const stage = getEventText(event, "stage");
753
+ if (!stage) {
754
+ state.lastEventType = eventType;
755
+ break;
756
+ }
757
+ const serverMessage = getEventText(event, "message");
758
+ for (let i = state.steps.length - 1; i >= 0; i--) {
759
+ const s = state.steps[i];
760
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
761
+ if (serverMessage) s.message = serverMessage;
762
+ s.status = "completed";
763
+ const durationMs = Number(event.durationMs);
764
+ if (Number.isFinite(durationMs)) s.elapsedMs = durationMs;
765
+ if (s.id === state.currentExecutingStepId) {
766
+ state.currentExecutingStepId = void 0;
767
+ }
768
+ break;
769
+ }
770
+ }
771
+ state.lastEventType = eventType;
772
+ break;
773
+ }
774
+ case "STAGE_FAILED": {
775
+ const stage = getEventText(event, "stage");
776
+ if (!stage) {
777
+ state.lastEventType = eventType;
778
+ break;
779
+ }
780
+ const serverMessage = getEventText(event, "message");
781
+ for (let i = state.steps.length - 1; i >= 0; i--) {
782
+ const s = state.steps[i];
783
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
784
+ if (serverMessage) s.message = serverMessage;
785
+ s.status = "error";
786
+ const durationMs = Number(event.durationMs);
787
+ if (Number.isFinite(durationMs)) s.elapsedMs = durationMs;
788
+ if (s.id === state.currentExecutingStepId) {
789
+ state.currentExecutingStepId = void 0;
790
+ }
791
+ break;
792
+ }
793
+ }
794
+ state.lastEventType = eventType;
795
+ break;
796
+ }
797
+ case "STAGE_SKIPPED": {
798
+ const stage = getEventText(event, "stage");
799
+ if (!stage) {
800
+ state.lastEventType = eventType;
801
+ break;
802
+ }
803
+ for (let i = state.steps.length - 1; i >= 0; i--) {
804
+ const s = state.steps[i];
805
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
806
+ if (s.id === state.currentExecutingStepId) {
807
+ state.currentExecutingStepId = void 0;
808
+ }
809
+ s.status = "skipped";
810
+ break;
811
+ }
812
+ }
813
+ state.lastEventType = eventType;
814
+ break;
815
+ }
816
+ default:
817
+ state.lastEventType = eventType;
818
+ break;
819
+ }
820
+ return state;
821
+ }
822
+ function createCancelledMessageUpdate(steps, currentMessage) {
823
+ const updatedSteps = steps.map((step) => {
824
+ if (step.status === "in_progress") {
825
+ return { ...step, status: "pending" };
826
+ }
827
+ return step;
828
+ });
829
+ return {
830
+ isStreaming: false,
831
+ isCancelled: true,
832
+ steps: updatedSteps,
833
+ currentExecutingStepId: void 0,
834
+ // Preserve currentMessage so UI can show it with X icon
835
+ currentMessage: currentMessage || "Thinking..."
836
+ };
837
+ }
838
+ var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
839
+ function buildRequestBody(config, userMessage, sessionId, options) {
840
+ const sessionOwner = config.sessionParams;
841
+ const sessionOwnerId = sessionOwner?.id?.trim();
842
+ if (!sessionOwnerId) {
843
+ throw new Error("ChatConfig.sessionParams.id is required to send ask requests.");
844
+ }
845
+ const sessionAttributes = sessionOwner?.attributes && Object.keys(sessionOwner.attributes).length > 0 ? sessionOwner.attributes : void 0;
846
+ return {
847
+ agentId: config.agentId,
848
+ userInput: userMessage,
849
+ sessionId,
850
+ sessionOwnerId,
851
+ sessionOwnerLabel: sessionOwner?.name || void 0,
852
+ sessionAttributes,
853
+ analysisMode: options?.analysisMode,
854
+ locale: resolveLocale(config.locale),
855
+ timezone: resolveTimezone(config.timezone)
856
+ };
857
+ }
858
+ function resolveLocale(configured) {
859
+ if (configured && configured.trim().length > 0) return configured.trim();
860
+ if (typeof navigator !== "undefined") {
861
+ const navLocale = navigator.language;
862
+ if (navLocale && navLocale.length > 0) return navLocale;
863
+ }
864
+ return "en-US";
865
+ }
866
+ function resolveTimezone(configured) {
867
+ if (configured && configured.trim().length > 0) return configured.trim();
868
+ try {
869
+ const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
870
+ if (zone && zone.length > 0) return zone;
871
+ } catch {
872
+ }
873
+ return "UTC";
874
+ }
875
+ function buildStreamingUrl(config) {
876
+ const endpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
877
+ const stage = config.stage || "DEV";
878
+ const stageParamName = config.stageQueryParam ?? "stage";
879
+ const queryParams = new URLSearchParams({ [stageParamName]: stage });
880
+ return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
881
+ }
882
+ function buildUserActionUrl(config, userActionId, action) {
883
+ const endpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
884
+ const [endpointPath] = endpoint.split("?");
885
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
886
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
887
+ const encodedUserActionId = encodeURIComponent(userActionId);
888
+ return `${config.api.baseUrl}${basePath}/user-action/${encodedUserActionId}/${action}`;
889
+ }
890
+ function buildResolveImagesUrl(config) {
891
+ if (config.api.resolveImagesEndpoint) {
892
+ return `${config.api.baseUrl}${config.api.resolveImagesEndpoint}`;
893
+ }
894
+ const streamEndpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
895
+ const [endpointPath] = streamEndpoint.split("?");
896
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
897
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
898
+ return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
899
+ }
900
+ function buildRequestHeaders(config) {
901
+ const headers = {
902
+ ...config.api.headers
903
+ };
904
+ if (config.api.authToken) {
905
+ headers.Authorization = `Bearer ${config.api.authToken}`;
906
+ }
907
+ return headers;
908
+ }
909
+ var UserActionStaleError = class extends Error {
910
+ constructor(userActionId, message = "User action is no longer actionable") {
911
+ super(message);
912
+ __publicField(this, "userActionId");
913
+ this.name = "UserActionStaleError";
914
+ this.userActionId = userActionId;
915
+ }
916
+ };
917
+ async function sendUserActionRequest(config, userActionId, action, data) {
918
+ const url = buildUserActionUrl(config, userActionId, action);
919
+ const baseHeaders = buildRequestHeaders(config);
920
+ const hasBody = data !== void 0;
921
+ const headers = hasBody ? { "Content-Type": "application/json", ...baseHeaders } : baseHeaders;
922
+ const response = await fetch(url, {
923
+ method: "POST",
924
+ headers,
925
+ body: hasBody ? JSON.stringify(data) : void 0
926
+ });
927
+ if (response.status === 404) {
928
+ throw new UserActionStaleError(userActionId);
929
+ }
930
+ if (!response.ok) {
931
+ const errorText = await response.text();
932
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
933
+ }
934
+ return await response.json();
935
+ }
936
+ async function submitUserAction(config, userActionId, content) {
937
+ return sendUserActionRequest(config, userActionId, "submit", content ?? {});
938
+ }
939
+ async function cancelUserAction(config, userActionId) {
940
+ return sendUserActionRequest(config, userActionId, "cancel");
941
+ }
942
+ async function resendUserAction(config, userActionId) {
943
+ return sendUserActionRequest(config, userActionId, "resend");
944
+ }
945
+ var memoryStore = /* @__PURE__ */ new Map();
946
+ var chatStore = {
947
+ get(key) {
948
+ return memoryStore.get(key) ?? [];
949
+ },
950
+ set(key, messages) {
951
+ memoryStore.set(key, messages);
952
+ },
953
+ delete(key) {
954
+ memoryStore.delete(key);
955
+ }
956
+ };
957
+ var streams = /* @__PURE__ */ new Map();
958
+ var activeStreamStore = {
959
+ has(key) {
960
+ return streams.has(key);
961
+ },
962
+ get(key) {
963
+ const entry = streams.get(key);
964
+ if (!entry) return null;
965
+ return { messages: entry.messages, isWaiting: entry.isWaiting };
966
+ },
967
+ // Called before startStream — registers the controller and initial messages
968
+ start(key, abortController, initialMessages) {
969
+ const existing = streams.get(key);
970
+ streams.set(key, {
971
+ messages: initialMessages,
972
+ isWaiting: true,
973
+ abortController,
974
+ listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
975
+ });
976
+ },
977
+ // Called by the stream on every event — applies the same updater pattern React uses
978
+ applyMessages(key, updater) {
979
+ const entry = streams.get(key);
980
+ if (!entry) return;
981
+ const next = typeof updater === "function" ? updater(entry.messages) : updater;
982
+ entry.messages = next;
983
+ entry.listeners.forEach((l) => l(next, entry.isWaiting));
984
+ },
985
+ setWaiting(key, waiting) {
986
+ const entry = streams.get(key);
987
+ if (!entry) return;
988
+ entry.isWaiting = waiting;
989
+ entry.listeners.forEach((l) => l(entry.messages, waiting));
990
+ },
991
+ // Called when stream completes — persists to chatStore and cleans up
992
+ complete(key) {
993
+ const entry = streams.get(key);
994
+ if (!entry) return;
995
+ entry.isWaiting = false;
996
+ entry.listeners.forEach((l) => l(entry.messages, false));
997
+ const toSave = entry.messages.filter((m) => !m.isStreaming);
998
+ if (toSave.length > 0) chatStore.set(key, toSave);
999
+ streams.delete(key);
1000
+ },
1001
+ // Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
1002
+ subscribe(key, listener) {
1003
+ const entry = streams.get(key);
1004
+ if (!entry) return () => {
1005
+ };
1006
+ entry.listeners.add(listener);
1007
+ return () => {
1008
+ streams.get(key)?.listeners.delete(listener);
1009
+ };
1010
+ },
1011
+ // Rename an entry — used when the server assigns a new session ID mid-stream
1012
+ rename(oldKey, newKey) {
1013
+ const entry = streams.get(oldKey);
1014
+ if (!entry) return;
1015
+ streams.set(newKey, entry);
1016
+ streams.delete(oldKey);
1017
+ },
1018
+ // Explicit user cancel — aborts the controller and removes the entry
1019
+ abort(key) {
1020
+ const entry = streams.get(key);
1021
+ if (!entry) return;
1022
+ entry.abortController.abort();
1023
+ streams.delete(key);
1024
+ }
1025
+ };
1026
+ var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
1027
+ function hasRagImages(content) {
1028
+ return RAG_IMAGE_REGEX.test(content);
1029
+ }
1030
+ async function waitForNextPaint(signal) {
1031
+ if (signal?.aborted) return;
1032
+ await new Promise((resolve) => {
1033
+ let isSettled = false;
1034
+ const finish = () => {
1035
+ if (isSettled) return;
1036
+ isSettled = true;
1037
+ signal?.removeEventListener("abort", finish);
1038
+ resolve();
1039
+ };
1040
+ signal?.addEventListener("abort", finish, { once: true });
1041
+ if (typeof requestAnimationFrame === "function") {
1042
+ requestAnimationFrame(() => {
1043
+ setTimeout(finish, 0);
1044
+ });
1045
+ return;
1046
+ }
1047
+ setTimeout(finish, 0);
1048
+ });
1049
+ }
1050
+ async function resolveRagImageUrls(config, content, signal) {
1051
+ const url = buildResolveImagesUrl(config);
1052
+ const baseHeaders = buildRequestHeaders(config);
1053
+ const headers = { "Content-Type": "application/json", ...baseHeaders };
1054
+ const response = await fetch(url, {
1055
+ method: "POST",
1056
+ headers,
1057
+ body: JSON.stringify({ input: content }),
1058
+ signal
1059
+ });
1060
+ if (!response.ok) {
1061
+ const errorText = await response.text();
1062
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
1063
+ }
1064
+ const text = await response.text();
1065
+ try {
1066
+ const parsed = JSON.parse(text);
1067
+ if (typeof parsed === "string") return parsed;
1068
+ if (typeof parsed.output === "string") return parsed.output;
1069
+ if (typeof parsed.result === "string") return parsed.result;
1070
+ } catch {
1071
+ }
1072
+ return text;
1073
+ }
1074
+ var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
1075
+ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForResponse) {
1076
+ const abortControllerRef = react.useRef(null);
1077
+ const configRef = react.useRef(config);
1078
+ configRef.current = config;
1079
+ const callbacksRef = react.useRef(callbacks);
1080
+ callbacksRef.current = callbacks;
1081
+ const startStream = react.useCallback(
1082
+ async (userMessage, streamingId, sessionId, externalAbortController, options) => {
1083
+ abortControllerRef.current?.abort();
1084
+ const abortController = externalAbortController ?? new AbortController();
1085
+ abortControllerRef.current = abortController;
1086
+ const state = createInitialV2State();
1087
+ const updateMessage = (update) => {
1088
+ if (abortController.signal.aborted) return;
1089
+ setMessages(
1090
+ (prev) => prev.map(
1091
+ (msg) => msg.id === streamingId ? { ...msg, ...update } : msg
1092
+ )
1093
+ );
1094
+ };
1095
+ try {
1096
+ const currentConfig = configRef.current;
1097
+ const requestBody = buildRequestBody(
1098
+ currentConfig,
1099
+ userMessage,
1100
+ sessionId,
1101
+ options
1102
+ );
1103
+ const url = buildStreamingUrl(currentConfig);
1104
+ const headers = buildRequestHeaders(currentConfig);
1105
+ await streamWorkflowEvents(url, requestBody, headers, {
1106
+ signal: abortController.signal,
1107
+ onEvent: (event) => {
1108
+ if (abortController.signal.aborted) return;
1109
+ processStreamEventV2(event, state);
1110
+ if (state.lastUserAction) {
1111
+ callbacksRef.current.onUserActionRequired?.(state.lastUserAction);
1112
+ }
1113
+ if (state.lastNotification) {
1114
+ callbacksRef.current.onUserNotification?.(state.lastNotification);
1115
+ }
1116
+ const activeStep = state.steps.find((s) => s.id === state.currentExecutingStepId);
1117
+ const lastInProgressStep = [...state.steps].reverse().find((s) => s.status === "in_progress");
1118
+ const useful = (m) => m && !isBlandStatus(m) ? m : void 0;
1119
+ const latestUsefulStep = [...state.steps].reverse().find(
1120
+ (s) => s.message && !isBlandStatus(s.message)
1121
+ );
1122
+ const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
1123
+ // nothing useful seen yet). Render the bland label so the
1124
+ // bubble isn't blank.
1125
+ activeStep?.message ?? lastInProgressStep?.message ?? getEventMessage(event);
1126
+ if (currentMessage) {
1127
+ callbacksRef.current.onStatusMessage?.(currentMessage);
1128
+ }
1129
+ callbacksRef.current.onStepsUpdate?.([...state.steps]);
1130
+ if (state.hasError) {
1131
+ updateMessage({
1132
+ streamingContent: FRIENDLY_ERROR_MESSAGE,
1133
+ content: FRIENDLY_ERROR_MESSAGE,
1134
+ streamProgress: "error",
1135
+ isError: true,
1136
+ errorDetails: state.errorMessage,
1137
+ formattedThinkingText: state.formattedThinkingText || void 0,
1138
+ steps: [...state.steps],
1139
+ currentExecutingStepId: void 0,
1140
+ executionId: state.executionId,
1141
+ sessionId: state.sessionId
1142
+ });
1143
+ } else {
1144
+ updateMessage({
1145
+ streamingContent: state.finalResponse,
1146
+ content: "",
1147
+ currentMessage,
1148
+ streamProgress: "processing",
1149
+ isError: false,
1150
+ formattedThinkingText: state.formattedThinkingText || void 0,
1151
+ steps: [...state.steps],
1152
+ currentExecutingStepId: state.currentExecutingStepId,
1153
+ executionId: state.executionId,
1154
+ sessionId: state.sessionId,
1155
+ isCancelled: false
1156
+ });
1157
+ }
1158
+ },
1159
+ onError: (error) => {
1160
+ setIsWaitingForResponse(false);
1161
+ callbacksRef.current.onStatusMessage?.(null);
1162
+ if (error.name !== "AbortError") {
1163
+ callbacksRef.current.onError?.(error);
1164
+ }
1165
+ const isAborted = error.name === "AbortError";
1166
+ setMessages(
1167
+ (prev) => prev.map(
1168
+ (msg) => msg.id === streamingId ? {
1169
+ ...msg,
1170
+ isStreaming: false,
1171
+ streamProgress: isAborted ? "processing" : "error",
1172
+ isError: !isAborted,
1173
+ isCancelled: isAborted,
1174
+ errorDetails: isAborted ? void 0 : error.message,
1175
+ content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1176
+ currentMessage: isAborted ? "Thinking..." : void 0,
1177
+ formattedThinkingText: state.formattedThinkingText || void 0,
1178
+ steps: [...state.steps].map((step) => {
1179
+ if (step.status === "in_progress" && isAborted) {
1180
+ return { ...step, status: "pending" };
1181
+ }
1182
+ return step;
1183
+ }),
1184
+ currentExecutingStepId: void 0
1185
+ } : msg
1186
+ )
1187
+ );
1188
+ },
1189
+ onComplete: () => {
1190
+ setIsWaitingForResponse(false);
1191
+ callbacksRef.current.onStatusMessage?.(null);
1192
+ callbacksRef.current.onStepsUpdate?.([]);
1193
+ if (state.sessionId && state.sessionId !== sessionId) {
1194
+ callbacksRef.current.onSessionIdChange?.(state.sessionId);
1195
+ }
1196
+ const needsImageResolve = !state.hasError && !abortController.signal.aborted && hasRagImages(state.finalResponse);
1197
+ const finalMessage = {
1198
+ id: streamingId,
1199
+ sessionId: state.sessionId || sessionId,
1200
+ role: "assistant",
1201
+ content: state.hasError ? FRIENDLY_ERROR_MESSAGE : state.finalResponse || "",
1202
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1203
+ isStreaming: false,
1204
+ streamProgress: state.hasError ? "error" : "completed",
1205
+ isError: state.hasError,
1206
+ errorDetails: state.hasError ? state.errorMessage : void 0,
1207
+ executionId: state.executionId,
1208
+ // Defensive: tracingData must be an object (or undefined)
1209
+ // so the JSON viewer doesn't char-iterate a string. The
1210
+ // event processor already drops bare strings; this is
1211
+ // the last stop before the message leaves the SDK.
1212
+ tracingData: state.finalData != null && typeof state.finalData === "object" ? state.finalData : void 0,
1213
+ steps: state.hasError ? [] : [...state.steps],
1214
+ isCancelled: false,
1215
+ currentExecutingStepId: void 0,
1216
+ formattedThinkingText: state.hasError ? void 0 : state.formattedThinkingText || void 0,
1217
+ isResolvingImages: needsImageResolve,
1218
+ totalElapsedMs: state.hasError ? void 0 : state.totalElapsedMs
1219
+ };
1220
+ setMessages(
1221
+ (prev) => prev.map(
1222
+ (msg) => msg.id === streamingId ? finalMessage : msg
1223
+ )
1224
+ );
1225
+ callbacksRef.current.onStreamComplete?.(finalMessage);
1226
+ }
1227
+ });
1228
+ const shouldResolveImages = !abortController.signal.aborted && !state.hasError && hasRagImages(state.finalResponse);
1229
+ if (shouldResolveImages) {
1230
+ await waitForNextPaint(abortController.signal);
1231
+ }
1232
+ if (shouldResolveImages && !abortController.signal.aborted) {
1233
+ try {
1234
+ const resolvedContent = await resolveRagImageUrls(
1235
+ currentConfig,
1236
+ state.finalResponse,
1237
+ abortController.signal
1238
+ );
1239
+ setMessages(
1240
+ (prev) => prev.map(
1241
+ (msg) => msg.id === streamingId ? { ...msg, content: resolvedContent, isResolvingImages: false } : msg
1242
+ )
1243
+ );
1244
+ } catch {
1245
+ setMessages(
1246
+ (prev) => prev.map(
1247
+ (msg) => msg.id === streamingId ? { ...msg, isResolvingImages: false } : msg
1248
+ )
1249
+ );
1250
+ }
1251
+ }
1252
+ return state.sessionId;
1253
+ } catch (error) {
1254
+ setIsWaitingForResponse(false);
1255
+ if (error.name !== "AbortError") {
1256
+ callbacksRef.current.onError?.(error);
1257
+ }
1258
+ const isAborted = error.name === "AbortError";
1259
+ setMessages(
1260
+ (prev) => prev.map(
1261
+ (msg) => msg.id === streamingId ? {
1262
+ ...msg,
1263
+ isStreaming: false,
1264
+ streamProgress: isAborted ? "processing" : "error",
1265
+ isError: !isAborted,
1266
+ isCancelled: isAborted,
1267
+ errorDetails: isAborted ? void 0 : error.message,
1268
+ content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1269
+ formattedThinkingText: state.formattedThinkingText || void 0,
1270
+ steps: [...state.steps].map((step) => {
1271
+ if (step.status === "in_progress" && isAborted) {
1272
+ return { ...step, status: "pending" };
1273
+ }
1274
+ return step;
1275
+ }),
1276
+ currentExecutingStepId: void 0
1277
+ } : msg
1278
+ )
1279
+ );
1280
+ return state.sessionId;
1281
+ }
1282
+ },
1283
+ [setMessages, setIsWaitingForResponse]
1284
+ );
1285
+ const cancelStream = react.useCallback(() => {
1286
+ abortControllerRef.current?.abort();
1287
+ }, []);
1288
+ return {
1289
+ startStream,
1290
+ cancelStream,
1291
+ abortControllerRef
1292
+ };
1293
+ }
1294
+ var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1295
+ function upsertPrompt(prompts, req) {
1296
+ const active = { ...req, status: "pending" };
1297
+ const idx = prompts.findIndex(
1298
+ (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
1299
+ );
1300
+ if (idx >= 0) {
1301
+ const next = prompts.slice();
1302
+ next[idx] = active;
1303
+ return next;
1304
+ }
1305
+ return [...prompts, active];
1306
+ }
1307
+ function getStoredOrInitialMessages(config) {
1308
+ if (!config.userId) return config.initialMessages ?? [];
1309
+ const activeStream = activeStreamStore.get(config.userId);
1310
+ if (activeStream) return activeStream.messages;
1311
+ const stored = chatStore.get(config.userId);
1312
+ if (stored.length > 0) return stored;
1313
+ if (config.initialMessages?.length) {
1314
+ chatStore.set(config.userId, config.initialMessages);
1315
+ return config.initialMessages;
1316
+ }
1317
+ return [];
1318
+ }
1319
+ function getSessionIdFromMessages(messages) {
1320
+ return messages.find((message) => message.sessionId)?.sessionId;
1321
+ }
1322
+ function useChatV2(config, callbacks = {}) {
1323
+ const [messages, setMessages] = react.useState(() => getStoredOrInitialMessages(config));
1324
+ const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(() => {
1325
+ if (!config.userId) return false;
1326
+ return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1327
+ });
1328
+ const sessionIdRef = react.useRef(
1329
+ getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1330
+ );
1331
+ const prevUserIdRef = react.useRef(config.userId);
1332
+ const streamUserIdRef = react.useRef(void 0);
1333
+ const subscriptionPrevUserIdRef = react.useRef(config.userId);
1334
+ const callbacksRef = react.useRef(callbacks);
1335
+ callbacksRef.current = callbacks;
1336
+ const configRef = react.useRef(config);
1337
+ configRef.current = config;
1338
+ const messagesRef = react.useRef(messages);
1339
+ messagesRef.current = messages;
1340
+ const storeAwareSetMessages = react.useCallback(
1341
+ (updater) => {
1342
+ const streamUserId = streamUserIdRef.current;
1343
+ const currentUserId = configRef.current.userId;
1344
+ const storeKey = streamUserId ?? currentUserId;
1345
+ if (storeKey && activeStreamStore.has(storeKey)) {
1346
+ activeStreamStore.applyMessages(storeKey, updater);
1347
+ }
1348
+ if (!streamUserId || streamUserId === currentUserId) {
1349
+ setMessages(updater);
1350
+ }
1351
+ },
1352
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1353
+ []
1354
+ );
1355
+ const storeAwareSetIsWaiting = react.useCallback(
1356
+ (waiting) => {
1357
+ const streamUserId = streamUserIdRef.current;
1358
+ const currentUserId = configRef.current.userId;
1359
+ const storeKey = streamUserId ?? currentUserId;
1360
+ if (storeKey && activeStreamStore.has(storeKey)) {
1361
+ activeStreamStore.setWaiting(storeKey, waiting);
1362
+ }
1363
+ if (!streamUserId || streamUserId === currentUserId) {
1364
+ setIsWaitingForResponse(waiting);
1365
+ }
1366
+ },
1367
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1368
+ []
1369
+ );
1370
+ const [userActionState, setUserActionState] = react.useState(EMPTY_USER_ACTION_STATE);
1371
+ const wrappedCallbacks = react.useMemo(() => ({
1372
+ ...callbacksRef.current,
1373
+ onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
1374
+ onStreamStart: () => callbacksRef.current.onStreamStart?.(),
1375
+ onStreamComplete: (message) => callbacksRef.current.onStreamComplete?.(message),
1376
+ onError: (error) => callbacksRef.current.onError?.(error),
1377
+ onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1378
+ onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1379
+ onUserActionRequired: (request) => {
1380
+ setUserActionState((prev) => ({
1381
+ ...prev,
1382
+ prompts: upsertPrompt(prev.prompts, request)
1383
+ }));
1384
+ callbacksRef.current.onUserActionRequired?.(request);
1385
+ },
1386
+ onUserNotification: (notification) => {
1387
+ setUserActionState(
1388
+ (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1389
+ );
1390
+ callbacksRef.current.onUserNotification?.(notification);
1391
+ }
1392
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1393
+ }), []);
1394
+ const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManagerV2(
1395
+ config,
1396
+ wrappedCallbacks,
1397
+ storeAwareSetMessages,
1398
+ storeAwareSetIsWaiting
1399
+ );
1400
+ const sendMessage = react.useCallback(
1401
+ async (userMessage, options) => {
1402
+ if (!userMessage.trim()) return;
1403
+ if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1404
+ sessionIdRef.current = generateId();
1405
+ callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1406
+ }
1407
+ const userMessageId = `user-${Date.now()}`;
1408
+ const userMsg = {
1409
+ id: userMessageId,
1410
+ sessionId: sessionIdRef.current,
1411
+ role: "user",
1412
+ content: userMessage,
1413
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1414
+ };
1415
+ setMessages((prev) => [...prev, userMsg]);
1416
+ callbacksRef.current.onMessageSent?.(userMessage);
1417
+ setIsWaitingForResponse(true);
1418
+ callbacksRef.current.onStreamStart?.();
1419
+ const streamingId = `assistant-${Date.now()}`;
1420
+ const streamingMsg = {
1421
+ id: streamingId,
1422
+ sessionId: sessionIdRef.current,
1423
+ role: "assistant",
1424
+ content: "",
1425
+ streamingContent: "",
1426
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1427
+ isStreaming: true,
1428
+ streamProgress: "started",
1429
+ steps: [],
1430
+ currentExecutingStepId: void 0,
1431
+ isCancelled: false,
1432
+ currentMessage: void 0
1433
+ };
1434
+ setMessages((prev) => [...prev, streamingMsg]);
1435
+ const abortController = new AbortController();
1436
+ const { userId } = configRef.current;
1437
+ if (userId) {
1438
+ streamUserIdRef.current = userId;
1439
+ const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
1440
+ activeStreamStore.start(userId, abortController, initialMessages);
1441
+ }
1442
+ const newSessionId = await startStream(
1443
+ userMessage,
1444
+ streamingId,
1445
+ sessionIdRef.current,
1446
+ abortController,
1447
+ options
1448
+ );
1449
+ const finalStreamUserId = streamUserIdRef.current ?? userId;
1450
+ if (finalStreamUserId) {
1451
+ activeStreamStore.complete(finalStreamUserId);
1452
+ }
1453
+ streamUserIdRef.current = void 0;
1454
+ if (!abortController.signal.aborted && newSessionId && newSessionId !== sessionIdRef.current) {
1455
+ sessionIdRef.current = newSessionId;
1456
+ }
1457
+ },
1458
+ [startStream]
1459
+ );
1460
+ const clearMessages = react.useCallback(() => {
1461
+ if (configRef.current.userId) {
1462
+ chatStore.delete(configRef.current.userId);
1463
+ }
1464
+ setMessages([]);
1465
+ }, []);
1466
+ const prependMessages = react.useCallback((msgs) => {
1467
+ setMessages((prev) => [...msgs, ...prev]);
1468
+ }, []);
1469
+ const cancelStream = react.useCallback(() => {
1470
+ const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1471
+ if (streamUserId) {
1472
+ activeStreamStore.abort(streamUserId);
1473
+ }
1474
+ streamUserIdRef.current = void 0;
1475
+ cancelStreamManager();
1476
+ setIsWaitingForResponse(false);
1477
+ setUserActionState((prev) => ({ ...prev, prompts: [] }));
1478
+ setMessages(
1479
+ (prev) => prev.map((msg) => {
1480
+ if (msg.isStreaming) {
1481
+ return {
1482
+ ...msg,
1483
+ ...createCancelledMessageUpdate(
1484
+ msg.steps || [],
1485
+ msg.currentMessage
1486
+ )
1487
+ };
1488
+ }
1489
+ return msg;
1490
+ })
1491
+ );
1492
+ }, [cancelStreamManager]);
1493
+ const resetSession = react.useCallback(() => {
1494
+ const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1495
+ if (streamUserId) {
1496
+ activeStreamStore.abort(streamUserId);
1497
+ }
1498
+ if (configRef.current.userId) {
1499
+ chatStore.delete(configRef.current.userId);
1500
+ }
1501
+ streamUserIdRef.current = void 0;
1502
+ setMessages([]);
1503
+ sessionIdRef.current = void 0;
1504
+ abortControllerRef.current?.abort();
1505
+ setIsWaitingForResponse(false);
1506
+ setUserActionState(EMPTY_USER_ACTION_STATE);
1507
+ }, []);
1508
+ const getSessionId = react.useCallback(() => {
1509
+ return sessionIdRef.current;
1510
+ }, []);
1511
+ const getMessages = react.useCallback(() => {
1512
+ return messages;
1513
+ }, [messages]);
1514
+ const setPromptStatus = react.useCallback(
1515
+ (userActionId, status) => {
1516
+ setUserActionState((prev) => ({
1517
+ ...prev,
1518
+ prompts: prev.prompts.map(
1519
+ (p) => p.userActionId === userActionId ? { ...p, status } : p
1520
+ )
1521
+ }));
1522
+ },
1523
+ []
1524
+ );
1525
+ const removePrompt = react.useCallback((userActionId) => {
1526
+ setUserActionState((prev) => ({
1527
+ ...prev,
1528
+ prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
1529
+ }));
1530
+ }, []);
1531
+ const submitUserAction2 = react.useCallback(
1532
+ async (userActionId, content) => {
1533
+ setPromptStatus(userActionId, "submitting");
1534
+ try {
1535
+ await submitUserAction(configRef.current, userActionId, content);
1536
+ removePrompt(userActionId);
1537
+ } catch (error) {
1538
+ if (error instanceof UserActionStaleError) {
1539
+ setPromptStatus(userActionId, "stale");
1540
+ return;
1541
+ }
1542
+ setPromptStatus(userActionId, "pending");
1543
+ callbacksRef.current.onError?.(error);
1544
+ throw error;
1545
+ }
1546
+ },
1547
+ [removePrompt, setPromptStatus]
1548
+ );
1549
+ const cancelUserAction2 = react.useCallback(
1550
+ async (userActionId) => {
1551
+ setPromptStatus(userActionId, "submitting");
1552
+ try {
1553
+ await cancelUserAction(configRef.current, userActionId);
1554
+ removePrompt(userActionId);
1555
+ } catch (error) {
1556
+ if (error instanceof UserActionStaleError) {
1557
+ removePrompt(userActionId);
1558
+ return;
1559
+ }
1560
+ setPromptStatus(userActionId, "pending");
1561
+ callbacksRef.current.onError?.(error);
1562
+ throw error;
1563
+ }
1564
+ },
1565
+ [removePrompt, setPromptStatus]
1566
+ );
1567
+ const resendUserAction2 = react.useCallback(
1568
+ async (userActionId) => {
1569
+ setPromptStatus(userActionId, "submitting");
1570
+ try {
1571
+ await resendUserAction(configRef.current, userActionId);
1572
+ setPromptStatus(userActionId, "pending");
1573
+ } catch (error) {
1574
+ if (error instanceof UserActionStaleError) {
1575
+ setPromptStatus(userActionId, "stale");
1576
+ return;
1577
+ }
1578
+ setPromptStatus(userActionId, "pending");
1579
+ callbacksRef.current.onError?.(error);
1580
+ throw error;
1581
+ }
1582
+ },
1583
+ [setPromptStatus]
1584
+ );
1585
+ const dismissNotification = react.useCallback((id) => {
1586
+ setUserActionState((prev) => ({
1587
+ ...prev,
1588
+ notifications: prev.notifications.filter((n) => n.id !== id)
1589
+ }));
1590
+ }, []);
1591
+ react.useEffect(() => {
1592
+ const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
1593
+ subscriptionPrevUserIdRef.current = config.userId;
1594
+ const { userId } = config;
1595
+ if (!userId) return;
1596
+ if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
1597
+ streamUserIdRef.current = userId;
1598
+ }
1599
+ const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1600
+ setMessages(msgs);
1601
+ setIsWaitingForResponse(isWaiting);
1602
+ });
1603
+ const active = activeStreamStore.get(userId);
1604
+ if (active) {
1605
+ setMessages(active.messages);
1606
+ setIsWaitingForResponse(active.isWaiting);
1607
+ }
1608
+ return unsubscribe;
1609
+ }, [config.userId]);
1610
+ react.useEffect(() => {
1611
+ if (!config.userId) return;
1612
+ if (prevUserIdRef.current !== config.userId) return;
1613
+ const toSave = messages.filter((m) => !m.isStreaming);
1614
+ if (toSave.length > 0) {
1615
+ chatStore.set(config.userId, toSave);
1616
+ }
1617
+ }, [messages, config.userId]);
1618
+ react.useEffect(() => {
1619
+ if (!config.userId || activeStreamStore.has(config.userId)) return;
1620
+ if (!config.initialMessages?.length || messagesRef.current.length > 0) return;
1621
+ chatStore.set(config.userId, config.initialMessages);
1622
+ setMessages(config.initialMessages);
1623
+ sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1624
+ }, [config.initialMessages, config.initialSessionId, config.userId]);
1625
+ react.useEffect(() => {
1626
+ const prevUserId = prevUserIdRef.current;
1627
+ prevUserIdRef.current = config.userId;
1628
+ if (prevUserId === config.userId) return;
1629
+ if (prevUserId && !config.userId) {
1630
+ chatStore.delete(prevUserId);
1631
+ setMessages([]);
1632
+ sessionIdRef.current = void 0;
1633
+ setIsWaitingForResponse(false);
1634
+ setUserActionState(EMPTY_USER_ACTION_STATE);
1635
+ } else if (config.userId) {
1636
+ const active = activeStreamStore.get(config.userId);
1637
+ if (active) {
1638
+ setMessages(active.messages);
1639
+ setIsWaitingForResponse(active.isWaiting);
1640
+ sessionIdRef.current = getSessionIdFromMessages(active.messages) ?? config.initialSessionId;
1641
+ return;
1642
+ }
1643
+ const nextMessages = getStoredOrInitialMessages(config);
1644
+ setMessages(nextMessages);
1645
+ sessionIdRef.current = getSessionIdFromMessages(nextMessages) ?? config.initialSessionId;
1646
+ setIsWaitingForResponse(false);
1647
+ }
1648
+ }, [config]);
1649
+ return {
1650
+ messages,
1651
+ sendMessage,
1652
+ clearMessages,
1653
+ prependMessages,
1654
+ cancelStream,
1655
+ resetSession,
1656
+ getSessionId,
1657
+ getMessages,
1658
+ isWaitingForResponse,
1659
+ sessionId: sessionIdRef.current,
1660
+ userActionState,
1661
+ submitUserAction: submitUserAction2,
1662
+ cancelUserAction: cancelUserAction2,
1663
+ resendUserAction: resendUserAction2,
1664
+ dismissNotification
1665
+ };
1666
+ }
1667
+ function getSpeechRecognition() {
1668
+ if (typeof window === "undefined") return null;
1669
+ return window.SpeechRecognition || window.webkitSpeechRecognition || null;
1670
+ }
1671
+ function useVoice(config = {}, callbacks = {}) {
1672
+ const [voiceState, setVoiceState] = react.useState("idle");
1673
+ const [transcribedText, setTranscribedText] = react.useState("");
1674
+ const [isAvailable, setIsAvailable] = react.useState(false);
1675
+ const [isRecording, setIsRecording] = react.useState(false);
1676
+ const recognitionRef = react.useRef(null);
1677
+ const autoStopTimerRef = react.useRef(null);
1678
+ const {
1679
+ lang = "en-US",
1680
+ interimResults = true,
1681
+ continuous = true,
1682
+ maxAlternatives = 1,
1683
+ autoStopAfterSilence
1684
+ } = config;
1685
+ const { onStart, onEnd, onResult, onError, onStateChange } = callbacks;
1686
+ react.useEffect(() => {
1687
+ const SpeechRecognitionAPI = getSpeechRecognition();
1688
+ setIsAvailable(SpeechRecognitionAPI !== null);
1689
+ }, []);
1690
+ react.useEffect(() => {
1691
+ onStateChange?.(voiceState);
1692
+ }, [voiceState, onStateChange]);
1693
+ const requestPermissions = react.useCallback(async () => {
1694
+ try {
1695
+ const result = await navigator.mediaDevices.getUserMedia({
1696
+ audio: true
1697
+ });
1698
+ result.getTracks().forEach((track) => track.stop());
1699
+ return {
1700
+ granted: true,
1701
+ status: "granted"
1702
+ };
1703
+ } catch (error) {
1704
+ return {
1705
+ granted: false,
1706
+ status: "denied"
1707
+ };
1708
+ }
1709
+ }, []);
1710
+ const getPermissions = react.useCallback(async () => {
1711
+ if (typeof navigator === "undefined" || !navigator.permissions) {
1712
+ return {
1713
+ granted: false,
1714
+ status: "undetermined"
1715
+ };
1716
+ }
1717
+ try {
1718
+ const result = await navigator.permissions.query({
1719
+ name: "microphone"
1720
+ });
1721
+ return {
1722
+ granted: result.state === "granted",
1723
+ status: result.state === "granted" ? "granted" : result.state === "denied" ? "denied" : "undetermined"
1724
+ };
1725
+ } catch {
1726
+ return {
1727
+ granted: false,
1728
+ status: "undetermined"
1729
+ };
1730
+ }
1731
+ }, []);
1732
+ const clearAutoStopTimer = react.useCallback(() => {
1733
+ if (autoStopTimerRef.current) {
1734
+ clearTimeout(autoStopTimerRef.current);
1735
+ autoStopTimerRef.current = null;
1736
+ }
1737
+ }, []);
1738
+ const resetAutoStopTimer = react.useCallback(() => {
1739
+ clearAutoStopTimer();
1740
+ if (autoStopAfterSilence && autoStopAfterSilence > 0) {
1741
+ autoStopTimerRef.current = setTimeout(() => {
1742
+ if (recognitionRef.current && isRecording) {
1743
+ recognitionRef.current.stop();
1744
+ }
1745
+ }, autoStopAfterSilence);
1746
+ }
1747
+ }, [autoStopAfterSilence, clearAutoStopTimer, isRecording]);
1748
+ const stopRecording = react.useCallback(() => {
1749
+ if (recognitionRef.current) {
1750
+ try {
1751
+ recognitionRef.current.stop();
1752
+ } catch (error) {
1753
+ console.warn("Error stopping speech recognition:", error);
1754
+ }
1755
+ }
1756
+ clearAutoStopTimer();
1757
+ setIsRecording(false);
1758
+ setVoiceState("idle");
1759
+ }, [clearAutoStopTimer]);
1760
+ const startRecording = react.useCallback(async () => {
1761
+ const SpeechRecognitionAPI = getSpeechRecognition();
1762
+ if (!SpeechRecognitionAPI) {
1763
+ onError?.("Speech recognition not supported in this browser");
1764
+ return;
1765
+ }
1766
+ try {
1767
+ try {
1768
+ await navigator.mediaDevices.getUserMedia({ audio: true });
1769
+ } catch (permError) {
1770
+ onError?.("Microphone access denied. Please allow microphone access in your browser settings.");
1771
+ return;
1772
+ }
1773
+ const recognition = new SpeechRecognitionAPI();
1774
+ recognition.continuous = continuous;
1775
+ recognition.interimResults = interimResults;
1776
+ recognition.lang = lang;
1777
+ recognition.maxAlternatives = maxAlternatives;
1778
+ recognition.onstart = () => {
1779
+ setVoiceState("listening");
1780
+ setIsRecording(true);
1781
+ onStart?.();
1782
+ resetAutoStopTimer();
1783
+ };
1784
+ recognition.onend = () => {
1785
+ setVoiceState("idle");
1786
+ setIsRecording(false);
1787
+ clearAutoStopTimer();
1788
+ onEnd?.();
1789
+ };
1790
+ recognition.onresult = (event) => {
1791
+ const results = event.results;
1792
+ let transcript = "";
1793
+ for (let i = 0; i < results.length; i++) {
1794
+ const result = results[i];
1795
+ if (result && result[0]) {
1796
+ const text = result[0].transcript;
1797
+ if (transcript && !transcript.endsWith(" ") && !text.startsWith(" ")) {
1798
+ transcript += " " + text;
1799
+ } else {
1800
+ transcript += text;
1801
+ }
1802
+ }
1803
+ }
1804
+ transcript = transcript.trim();
1805
+ if (transcript) {
1806
+ setTranscribedText(transcript);
1807
+ onResult?.(transcript);
1808
+ resetAutoStopTimer();
1809
+ }
1810
+ };
1811
+ recognition.onerror = (event) => {
1812
+ setVoiceState("error");
1813
+ setIsRecording(false);
1814
+ clearAutoStopTimer();
1815
+ let errorMessage = event.error;
1816
+ if (event.error === "not-allowed") {
1817
+ errorMessage = "Microphone access denied. Please allow microphone access in your browser settings.";
1818
+ } else if (event.error === "no-speech") {
1819
+ errorMessage = "No speech detected. Please try again.";
1820
+ } else if (event.error === "audio-capture") {
1821
+ errorMessage = "No microphone found or microphone is in use.";
1822
+ } else if (event.error === "network") {
1823
+ errorMessage = "Network error occurred. Please check your connection.";
1824
+ }
1825
+ onError?.(errorMessage);
1826
+ };
1827
+ recognitionRef.current = recognition;
1828
+ setTranscribedText("");
1829
+ recognition.start();
1830
+ } catch (error) {
1831
+ setVoiceState("error");
1832
+ setIsRecording(false);
1833
+ onError?.(
1834
+ error instanceof Error ? error.message : "Failed to start recording"
1835
+ );
1836
+ }
1837
+ }, [
1838
+ lang,
1839
+ interimResults,
1840
+ continuous,
1841
+ maxAlternatives,
1842
+ onStart,
1843
+ onEnd,
1844
+ onResult,
1845
+ onError,
1846
+ getPermissions,
1847
+ resetAutoStopTimer,
1848
+ clearAutoStopTimer
1849
+ ]);
1850
+ const clearTranscript = react.useCallback(() => {
1851
+ setTranscribedText("");
1852
+ }, []);
1853
+ const reset = react.useCallback(() => {
1854
+ stopRecording();
1855
+ setTranscribedText("");
1856
+ setVoiceState("idle");
1857
+ }, [stopRecording]);
1858
+ react.useEffect(() => {
1859
+ return () => {
1860
+ if (recognitionRef.current) {
1861
+ try {
1862
+ recognitionRef.current.stop();
1863
+ } catch {
1864
+ }
1865
+ }
1866
+ clearAutoStopTimer();
1867
+ };
1868
+ }, [clearAutoStopTimer]);
1869
+ return {
1870
+ voiceState,
1871
+ transcribedText,
1872
+ isAvailable,
1873
+ isRecording,
1874
+ startRecording,
1875
+ stopRecording,
1876
+ requestPermissions,
1877
+ getPermissions,
1878
+ clearTranscript,
1879
+ reset
1880
+ };
1881
+ }
1882
+ function classifyField(field) {
1883
+ if (!field) return "text";
1884
+ if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
1885
+ switch (field.type) {
1886
+ case "boolean":
1887
+ return "boolean";
1888
+ case "integer":
1889
+ return "integer";
1890
+ case "number":
1891
+ return "decimal";
1892
+ case "string":
1893
+ return "text";
1894
+ default:
1895
+ return "text";
32
1896
  }
33
- n.default = e;
34
- return Object.freeze(n);
35
1897
  }
36
-
37
- var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
38
- var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
39
- var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
40
- var remarkBreaks__default = /*#__PURE__*/_interopDefault(remarkBreaks);
41
-
1898
+ function isNestedOrUnsupported(field) {
1899
+ if (!field) return false;
1900
+ if (field.type === "object" || field.type === "array") return true;
1901
+ if ("properties" in field && field.properties != null) return true;
1902
+ if ("items" in field && field.items != null) return true;
1903
+ return false;
1904
+ }
1905
+ function getOptions(field) {
1906
+ if (!field || !Array.isArray(field.oneOf)) return [];
1907
+ return field.oneOf.filter(
1908
+ (o) => !!o && typeof o === "object" && typeof o.const === "string"
1909
+ );
1910
+ }
1911
+ function isRequired(schema, key) {
1912
+ return Array.isArray(schema?.required) && schema.required.includes(key);
1913
+ }
1914
+ function coerceValue(field, raw) {
1915
+ const widget = classifyField(field);
1916
+ if (widget === "boolean") {
1917
+ if (typeof raw === "boolean") return raw;
1918
+ if (raw === "true") return true;
1919
+ if (raw === "false") return false;
1920
+ return Boolean(raw);
1921
+ }
1922
+ if (widget === "integer" || widget === "decimal") {
1923
+ if (raw === "" || raw == null) return void 0;
1924
+ const num = typeof raw === "number" ? raw : Number(String(raw).trim());
1925
+ if (Number.isNaN(num)) return void 0;
1926
+ return widget === "integer" ? Math.trunc(num) : num;
1927
+ }
1928
+ if (raw == null) return void 0;
1929
+ const str = String(raw);
1930
+ return str === "" ? void 0 : str;
1931
+ }
1932
+ function defaultValueFor(field) {
1933
+ if (!field || field.default === void 0) {
1934
+ return classifyField(field) === "boolean" ? false : "";
1935
+ }
1936
+ return field.default;
1937
+ }
1938
+ function validateField(field, value, required) {
1939
+ const widget = classifyField(field);
1940
+ const label = field?.title || "This field";
1941
+ const isEmpty = value === void 0 || value === null || typeof value === "string" && value.trim() === "";
1942
+ if (isEmpty) {
1943
+ if (required && widget !== "boolean") return `${label} is required.`;
1944
+ return null;
1945
+ }
1946
+ if (widget === "integer" || widget === "decimal") {
1947
+ const num = typeof value === "number" ? value : Number(value);
1948
+ if (Number.isNaN(num)) return `${label} must be a number.`;
1949
+ if (widget === "integer" && !Number.isInteger(num)) {
1950
+ return `${label} must be a whole number.`;
1951
+ }
1952
+ if (typeof field?.minimum === "number" && num < field.minimum) {
1953
+ return `${label} must be at least ${field.minimum}.`;
1954
+ }
1955
+ if (typeof field?.maximum === "number" && num > field.maximum) {
1956
+ return `${label} must be at most ${field.maximum}.`;
1957
+ }
1958
+ return null;
1959
+ }
1960
+ if (widget === "select") {
1961
+ const allowed = getOptions(field).map((o) => o.const);
1962
+ if (allowed.length > 0 && !allowed.includes(String(value))) {
1963
+ return `${label} has an invalid selection.`;
1964
+ }
1965
+ return null;
1966
+ }
1967
+ const str = String(value);
1968
+ if (typeof field?.minLength === "number" && str.length < field.minLength) {
1969
+ return `${label} must be at least ${field.minLength} characters.`;
1970
+ }
1971
+ if (typeof field?.maxLength === "number" && str.length > field.maxLength) {
1972
+ return `${label} must be at most ${field.maxLength} characters.`;
1973
+ }
1974
+ return null;
1975
+ }
1976
+ function renderableFields(schema) {
1977
+ const props = schema?.properties;
1978
+ if (!props) return [];
1979
+ return Object.entries(props).filter(([, field]) => !isNestedOrUnsupported(field));
1980
+ }
1981
+ function validateForm(schema, values) {
1982
+ const errors = {};
1983
+ for (const [key, field] of renderableFields(schema)) {
1984
+ const coerced = coerceValue(field, values[key]);
1985
+ const err = validateField(field, coerced, isRequired(schema, key));
1986
+ if (err) errors[key] = err;
1987
+ }
1988
+ return errors;
1989
+ }
1990
+ function buildContent(schema, values) {
1991
+ const content = {};
1992
+ for (const [key, field] of renderableFields(schema)) {
1993
+ const coerced = coerceValue(field, values[key]);
1994
+ if (coerced !== void 0) content[key] = coerced;
1995
+ }
1996
+ return content;
1997
+ }
42
1998
  var PaymanChatContext = react.createContext(void 0);
43
1999
  function usePaymanChat() {
44
2000
  const context = react.useContext(PaymanChatContext);
@@ -258,7 +2214,7 @@ var NEGATIVE_FEEDBACK_REASONS = [
258
2214
  { value: "REPORT_CONTENT", label: "Report content" },
259
2215
  { value: "OTHER", label: "Other" }
260
2216
  ];
261
- var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
2217
+ var DEFAULT_STREAM_ENDPOINT2 = "/api/playground/ask/stream";
262
2218
  async function submitFeedback({
263
2219
  baseUrl,
264
2220
  streamEndpoint,
@@ -272,7 +2228,7 @@ async function submitFeedback({
272
2228
  signal
273
2229
  }) {
274
2230
  const base = baseUrl.replace(/\/+$/, "");
275
- const endpointPath = (streamEndpoint || DEFAULT_STREAM_ENDPOINT).split("?")[0].replace(/\/+$/, "");
2231
+ const endpointPath = (streamEndpoint || DEFAULT_STREAM_ENDPOINT2).split("?")[0].replace(/\/+$/, "");
276
2232
  const basePath = endpointPath.endsWith("/stream") ? endpointPath.slice(0, -"/stream".length) : endpointPath;
277
2233
  const query = new URLSearchParams();
278
2234
  if (stage) query.set(stageQueryParam ?? "stage", stage);
@@ -544,47 +2500,7 @@ function createMarkdownComponents(options = {}) {
544
2500
  td: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-3 align-middle text-sm whitespace-nowrap", children })
545
2501
  };
546
2502
  }
547
- function ThinkingBlock({ text }) {
548
- const [isOpen, setIsOpen] = react.useState(false);
549
- const hasContent = typeof text === "string" && text.trim().length > 0;
550
- if (!hasContent) return null;
551
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1.5 mb-1.5", children: [
552
- /* @__PURE__ */ jsxRuntime.jsxs(
553
- "button",
554
- {
555
- type: "button",
556
- onClick: () => setIsOpen((prev) => !prev),
557
- className: "inline-flex items-center gap-1 text-[10px] payman-agent-thinking-toggle transition-colors",
558
- "aria-expanded": isOpen,
559
- "aria-label": isOpen ? "Collapse thought process" : "Expand thought process",
560
- children: [
561
- /* @__PURE__ */ jsxRuntime.jsx(
562
- framerMotion.motion.div,
563
- {
564
- animate: { rotate: isOpen ? 180 : 0 },
565
- transition: { duration: 0.15 },
566
- className: "shrink-0",
567
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3 w-3" })
568
- }
569
- ),
570
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Thought process" })
571
- ]
572
- }
573
- ),
574
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsxRuntime.jsx(
575
- framerMotion.motion.div,
576
- {
577
- initial: { height: 0, opacity: 0 },
578
- animate: { height: "auto", opacity: 1 },
579
- exit: { height: 0, opacity: 0 },
580
- transition: { duration: 0.2, ease: "easeInOut" },
581
- className: "overflow-hidden",
582
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 payman-agent-thinking-block rounded-md p-2 overflow-y-auto overflow-x-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-xs payman-agent-thinking-block-text whitespace-pre-wrap leading-relaxed", children: text }) })
583
- }
584
- ) })
585
- ] });
586
- }
587
- var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
2503
+ var FRIENDLY_ERROR_MESSAGE2 = "Oops, something went wrong. Please try again.";
588
2504
  function looksLikeRawError(text) {
589
2505
  if (!text || text.length < 10) return false;
590
2506
  return text.includes("errorType=") || /failed:\s*\{/.test(text);
@@ -626,8 +2542,6 @@ function AgentMessage({
626
2542
  const completedWithNoContent = !isStreaming && !isCancelled && content.length === 0 && (message.streamProgress === "completed" || message.streamProgress === "error");
627
2543
  const conflictErrorMessage = getConflictErrorMessage(message.errorDetails);
628
2544
  const isError = !!conflictErrorMessage || (isFriendlyWorkflowError(message.errorDetails) || looksLikeRawError(content)) && !hasMeaningfulContent || completedWithNoContent;
629
- const activeThinkingText = message.activeThinkingText;
630
- const allThinkingText = message.allThinkingText;
631
2545
  const currentStep = react.useMemo(
632
2546
  () => message.steps?.find(
633
2547
  (s) => s.id === currentExecutingStepId && s.status === "in_progress"
@@ -670,7 +2584,7 @@ function AgentMessage({
670
2584
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapper, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5 payman-agent-step-icon--error" }) });
671
2585
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapper, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-1.5 rounded-full payman-agent-step-icon--pending-dim" }) });
672
2586
  };
673
- const stepsListContent = hasSteps && /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: isStepsExpanded && /* @__PURE__ */ jsxRuntime.jsxs(
2587
+ const stepsListContent = hasSteps && /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: isStepsExpanded && /* @__PURE__ */ jsxRuntime.jsx(
674
2588
  framerMotion.motion.div,
675
2589
  {
676
2590
  initial: { height: 0, opacity: 0 },
@@ -678,37 +2592,34 @@ function AgentMessage({
678
2592
  exit: { height: 0, opacity: 0 },
679
2593
  transition: { duration: 0.2, ease: "easeInOut" },
680
2594
  className: "overflow-hidden",
681
- children: [
682
- !isStreaming && allThinkingText?.trim() && /* @__PURE__ */ jsxRuntime.jsx(ThinkingBlock, { text: allThinkingText }),
683
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-1.5", children: message.steps.map((step) => {
684
- const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
685
- const hasTime = step.elapsedMs != null && step.elapsedMs > 0;
686
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1.5", children: [
687
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5 items-start", children: [
688
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
689
- /* @__PURE__ */ jsxRuntime.jsx(
690
- "span",
691
- {
692
- className: cn(
693
- "text-xs leading-relaxed min-w-0 break-words",
694
- isCurrentlyExecuting && "shimmer-text font-medium",
695
- !isCurrentlyExecuting && step.status === "error" && "payman-agent-step-text--error",
696
- !isCurrentlyExecuting && step.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
697
- !isCurrentlyExecuting && step.status === "completed" && "payman-agent-step-text--completed",
698
- !isCurrentlyExecuting && step.status === "pending" && "payman-agent-step-text--pending",
699
- !isCurrentlyExecuting && step.status === "in_progress" && "payman-agent-step-text--in-progress"
700
- ),
701
- children: step.message
702
- }
703
- )
704
- ] }),
705
- hasTime && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-[22px] mt-1", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md payman-agent-step-time leading-none", children: [
706
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-2.5 w-2.5 shrink-0 payman-agent-step-time-icon" }),
707
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step.elapsedMs) })
708
- ] }) })
709
- ] }, step.id);
710
- }) })
711
- ]
2595
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-1.5", children: message.steps.map((step) => {
2596
+ const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
2597
+ const hasTime = step.elapsedMs != null && step.elapsedMs > 0;
2598
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1.5", children: [
2599
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5 items-start", children: [
2600
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
2601
+ /* @__PURE__ */ jsxRuntime.jsx(
2602
+ "span",
2603
+ {
2604
+ className: cn(
2605
+ "text-xs leading-relaxed min-w-0 break-words",
2606
+ isCurrentlyExecuting && "shimmer-text font-medium",
2607
+ !isCurrentlyExecuting && step.status === "error" && "payman-agent-step-text--error",
2608
+ !isCurrentlyExecuting && step.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
2609
+ !isCurrentlyExecuting && step.status === "completed" && "payman-agent-step-text--completed",
2610
+ !isCurrentlyExecuting && step.status === "pending" && "payman-agent-step-text--pending",
2611
+ !isCurrentlyExecuting && step.status === "in_progress" && "payman-agent-step-text--in-progress"
2612
+ ),
2613
+ children: step.message
2614
+ }
2615
+ )
2616
+ ] }),
2617
+ hasTime && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-[22px] mt-1", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md payman-agent-step-time leading-none", children: [
2618
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-2.5 w-2.5 shrink-0 payman-agent-step-time-icon" }),
2619
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step.elapsedMs) })
2620
+ ] }) })
2621
+ ] }, step.id);
2622
+ }) })
712
2623
  }
713
2624
  ) });
714
2625
  const stepsToggleRef = react.useRef(null);
@@ -750,95 +2661,77 @@ function AgentMessage({
750
2661
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2.5 w-2.5 rounded-full payman-agent-avatar-dot animate-pulse" }),
751
2662
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 h-2.5 w-2.5 rounded-full payman-agent-avatar-dot-ping animate-ping" })
752
2663
  ] }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-8 rounded-full payman-agent-avatar flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "h-3.5 w-3.5 payman-agent-avatar-icon" }) }) }),
753
- /* @__PURE__ */ jsxRuntime.jsxs(
2664
+ /* @__PURE__ */ jsxRuntime.jsx(
754
2665
  "div",
755
2666
  {
756
2667
  className: cn(
757
2668
  "min-w-0",
758
2669
  layout === "centered" ? "max-w-[85%]" : showAvatar ? "max-w-[85%]" : "max-w-[80%]"
759
2670
  ),
760
- children: [
761
- message.userActionResult && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: message.userActionResult === "approved" ? /* @__PURE__ */ jsxRuntime.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: [
762
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-3.5 h-3.5 shrink-0", strokeWidth: 2.5 }),
763
- "Verified"
764
- ] }) : /* @__PURE__ */ jsxRuntime.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: [
765
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5 shrink-0", strokeWidth: 2.5 }),
766
- "Rejected"
767
- ] }) }),
768
- /* @__PURE__ */ jsxRuntime.jsxs(
769
- "div",
770
- {
771
- className: cn(
772
- "overflow-hidden w-full min-w-0 px-4 py-3 rounded-2xl rounded-tl-md transition-all duration-200",
773
- layout === "centered" ? cn(
774
- "payman-agent-bubble payman-agent-bubble--centered",
775
- isStreaming && "streaming",
776
- isError && "error"
777
- ) : cn(
778
- "payman-agent-bubble",
779
- isError && "payman-agent-bubble--error"
780
- )
2671
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2672
+ "div",
2673
+ {
2674
+ className: cn(
2675
+ "overflow-hidden w-full min-w-0 px-4 py-3 rounded-2xl rounded-tl-md transition-all duration-200",
2676
+ layout === "centered" ? cn(
2677
+ "payman-agent-bubble payman-agent-bubble--centered",
2678
+ isStreaming && "streaming",
2679
+ isError && "error"
2680
+ ) : cn(
2681
+ "payman-agent-bubble",
2682
+ isError && "payman-agent-bubble--error"
2683
+ )
2684
+ ),
2685
+ children: [
2686
+ showAgentName && /* @__PURE__ */ jsxRuntime.jsx(
2687
+ "p",
2688
+ {
2689
+ className: cn(
2690
+ "text-sm font-semibold mb-1 leading-none payman-agent-name",
2691
+ isStreaming && !content && "animate-pulse"
2692
+ ),
2693
+ children: isStreaming && !content ? "Thought Process" : agentName
2694
+ }
781
2695
  ),
782
- children: [
783
- showAgentName && /* @__PURE__ */ jsxRuntime.jsx(
784
- "p",
785
- {
786
- className: cn(
787
- "text-sm font-semibold mb-1 leading-none payman-agent-name",
788
- isStreaming && !content && "animate-pulse"
789
- ),
790
- children: isStreaming && !content ? "Thought Process" : agentName
791
- }
792
- ),
793
- /* @__PURE__ */ jsxRuntime.jsx(
794
- "div",
795
- {
796
- className: cn(
797
- "text-sm leading-relaxed min-w-0 w-full break-words overflow-wrap-anywhere",
798
- showAgentName && "mt-1"
799
- ),
800
- children: isStreaming && !content ? (
801
- // Streaming without content — show thinking/step indicator
802
- activeThinkingText ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-start", children: [
803
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 mt-0.5 payman-agent-thinking-spinner animate-spin shrink-0" }),
804
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm leading-relaxed min-w-0 break-words payman-agent-thinking-text whitespace-pre-wrap flex-1", children: [
805
- activeThinkingText,
806
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-0.5 h-3.5 payman-agent-thinking-cursor animate-pulse ml-0.5 align-text-bottom" })
807
- ] })
808
- ] }) : currentStep ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5 items-start", children: [
809
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 shrink-0", children: renderStepIcon(currentStep, true) }),
810
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm leading-relaxed min-w-0 break-words shimmer-text font-medium flex-1", children: currentStep.message })
811
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2.5 items-start", children: [
812
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-4 h-4 mt-0.5 payman-agent-thinking-spinner animate-spin shrink-0" }),
813
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm payman-agent-thinking-text flex-1", children: currentMessage || "Thinking..." })
814
- ] })
815
- ) : isCancelled && !content ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2.5 items-start", children: [
816
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4 mt-0.5 payman-agent-cancelled-icon shrink-0" }),
817
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm payman-agent-cancelled-text italic flex-1", children: currentMessage || "Request was stopped." })
818
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
819
- "div",
820
- {
821
- className: cn(
822
- "payman-markdown payman-agent-markdown prose prose-sm dark:prose-invert max-w-none min-w-0 w-full break-words overflow-wrap-anywhere",
823
- isError && "payman-agent-markdown--error",
824
- isStreaming && content && "streaming-cursor"
825
- ),
826
- children: /* @__PURE__ */ jsxRuntime.jsx(
827
- ReactMarkdown__default.default,
828
- {
829
- remarkPlugins: [remarkGfm__default.default],
830
- components: markdownRenderers,
831
- children: isError ? conflictErrorMessage ?? FRIENDLY_ERROR_MESSAGE : content || (isStreaming ? "Thinking..." : isCancelled ? "Request was stopped." : "")
832
- }
833
- )
834
- }
835
- )
836
- }
837
- )
838
- ]
839
- }
840
- )
841
- ]
2696
+ /* @__PURE__ */ jsxRuntime.jsx(
2697
+ "div",
2698
+ {
2699
+ className: cn(
2700
+ "text-sm leading-relaxed min-w-0 w-full break-words overflow-wrap-anywhere",
2701
+ showAgentName && "mt-1"
2702
+ ),
2703
+ children: isStreaming && !content ? currentStep ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5 items-start", children: [
2704
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 shrink-0", children: renderStepIcon(currentStep, true) }),
2705
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm leading-relaxed min-w-0 break-words shimmer-text font-medium flex-1", children: currentStep.message })
2706
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2.5 items-start", children: [
2707
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-4 h-4 mt-0.5 payman-agent-thinking-spinner animate-spin shrink-0" }),
2708
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm payman-agent-thinking-text flex-1", children: currentMessage || "Thinking..." })
2709
+ ] }) : isCancelled && !content ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2.5 items-start", children: [
2710
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4 mt-0.5 payman-agent-cancelled-icon shrink-0" }),
2711
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm payman-agent-cancelled-text italic flex-1", children: currentMessage || "Request was stopped." })
2712
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2713
+ "div",
2714
+ {
2715
+ className: cn(
2716
+ "payman-markdown payman-agent-markdown prose prose-sm dark:prose-invert max-w-none min-w-0 w-full break-words overflow-wrap-anywhere",
2717
+ isError && "payman-agent-markdown--error",
2718
+ isStreaming && content && "streaming-cursor"
2719
+ ),
2720
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2721
+ ReactMarkdown__default.default,
2722
+ {
2723
+ remarkPlugins: [remarkGfm__default.default],
2724
+ components: markdownRenderers,
2725
+ children: isError ? conflictErrorMessage ?? FRIENDLY_ERROR_MESSAGE2 : content || (isStreaming ? "Thinking..." : isCancelled ? "Request was stopped." : "")
2726
+ }
2727
+ )
2728
+ }
2729
+ )
2730
+ }
2731
+ )
2732
+ ]
2733
+ }
2734
+ )
842
2735
  }
843
2736
  )
844
2737
  ] }),
@@ -1682,7 +3575,62 @@ function MarkdownImageV2({
1682
3575
  }
1683
3576
  ) });
1684
3577
  }
1685
- function buildComponents(onImageClick, isResolvingRef) {
3578
+ function PdfBlockV2({ title, href, onOpen }) {
3579
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3580
+ "button",
3581
+ {
3582
+ type: "button",
3583
+ className: "payman-v2-pdf-block",
3584
+ onClick: (e) => {
3585
+ e.preventDefault();
3586
+ onOpen(href, title);
3587
+ },
3588
+ "aria-label": `Open PDF: ${title}`,
3589
+ children: [
3590
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-block-icon-area", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 18, strokeWidth: 1.5 }) }),
3591
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-block-body", children: [
3592
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-pdf-block-name", children: title }),
3593
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-pdf-block-sub", children: "PDF \xB7 Click to preview" })
3594
+ ] })
3595
+ ]
3596
+ }
3597
+ );
3598
+ }
3599
+ function isPdfUrl(href) {
3600
+ if (!href) return false;
3601
+ try {
3602
+ const url = new URL(href);
3603
+ const filename = url.searchParams.get("filename");
3604
+ if (filename?.toLowerCase().endsWith(".pdf")) return true;
3605
+ return url.pathname.toLowerCase().endsWith(".pdf");
3606
+ } catch {
3607
+ return href.toLowerCase().endsWith(".pdf");
3608
+ }
3609
+ }
3610
+ function getPdfTitleFromUrl(href) {
3611
+ try {
3612
+ const url = new URL(href);
3613
+ const filename = url.searchParams.get("filename");
3614
+ if (filename) {
3615
+ return filename.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
3616
+ }
3617
+ const parts = url.pathname.split("/").filter(Boolean);
3618
+ const last = parts[parts.length - 1];
3619
+ if (last) return decodeURIComponent(last).replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
3620
+ } catch {
3621
+ }
3622
+ return "Document";
3623
+ }
3624
+ function childrenToText(children) {
3625
+ if (typeof children === "string") return children;
3626
+ if (typeof children === "number") return String(children);
3627
+ if (Array.isArray(children)) return children.map(childrenToText).join("");
3628
+ if (children && typeof children === "object" && "props" in children) {
3629
+ return childrenToText(children.props.children);
3630
+ }
3631
+ return "";
3632
+ }
3633
+ function buildComponents(onImageClick, isResolvingRef, onPdfClick) {
1686
3634
  return {
1687
3635
  p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { children }),
1688
3636
  code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
@@ -1697,7 +3645,15 @@ function buildComponents(onImageClick, isResolvingRef) {
1697
3645
  em: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("em", { children }),
1698
3646
  blockquote: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("blockquote", { children }),
1699
3647
  hr: () => /* @__PURE__ */ jsxRuntime.jsx("hr", {}),
1700
- a: ({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children }),
3648
+ a: ({ href, children }) => {
3649
+ const url = href ?? "";
3650
+ if (onPdfClick && isPdfUrl(url)) {
3651
+ const linkText = childrenToText(children).trim();
3652
+ const title = linkText || getPdfTitleFromUrl(url);
3653
+ return /* @__PURE__ */ jsxRuntime.jsx(PdfBlockV2, { href: url, title, onOpen: onPdfClick });
3654
+ }
3655
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children });
3656
+ },
1701
3657
  img: ({ src, alt }) => /* @__PURE__ */ jsxRuntime.jsx(
1702
3658
  MarkdownImageV2,
1703
3659
  {
@@ -1718,13 +3674,14 @@ function MarkdownRendererV2({
1718
3674
  content,
1719
3675
  isStreaming,
1720
3676
  isResolvingImages,
1721
- onImageClick
3677
+ onImageClick,
3678
+ onPdfClick
1722
3679
  }) {
1723
3680
  const isResolvingRef = react.useRef(isResolvingImages);
1724
3681
  isResolvingRef.current = isResolvingImages;
1725
3682
  const components = react.useMemo(
1726
- () => buildComponents(onImageClick, isResolvingRef),
1727
- [onImageClick]
3683
+ () => buildComponents(onImageClick, isResolvingRef, onPdfClick),
3684
+ [onImageClick, onPdfClick]
1728
3685
  );
1729
3686
  return /* @__PURE__ */ jsxRuntime.jsx(
1730
3687
  "div",
@@ -1950,44 +3907,237 @@ function FeedbackReasonModal({
1950
3907
  }
1951
3908
  ),
1952
3909
  /* @__PURE__ */ jsxRuntime.jsx(
1953
- "textarea",
3910
+ "textarea",
3911
+ {
3912
+ className: "payman-v2-feedback-modal-textarea",
3913
+ placeholder: "Add details (optional)",
3914
+ value: details,
3915
+ maxLength: MAX_DETAILS_CHARS,
3916
+ onChange: (e) => setDetails(e.target.value),
3917
+ disabled: submitting
3918
+ }
3919
+ ),
3920
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-feedback-modal-error", children: error }) : null,
3921
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-feedback-modal-actions", children: [
3922
+ /* @__PURE__ */ jsxRuntime.jsx(
3923
+ "button",
3924
+ {
3925
+ type: "button",
3926
+ onClick: onClose,
3927
+ disabled: submitting,
3928
+ className: "payman-v2-feedback-modal-btn payman-v2-feedback-modal-btn-secondary",
3929
+ children: "Cancel"
3930
+ }
3931
+ ),
3932
+ /* @__PURE__ */ jsxRuntime.jsx(
3933
+ "button",
3934
+ {
3935
+ type: "button",
3936
+ onClick: handleSubmit,
3937
+ disabled: submitting,
3938
+ className: "payman-v2-feedback-modal-btn payman-v2-feedback-modal-btn-primary",
3939
+ children: submitting ? "Sending\u2026" : "Submit"
3940
+ }
3941
+ )
3942
+ ] })
3943
+ ]
3944
+ }
3945
+ )
3946
+ }
3947
+ ) : null });
3948
+ }
3949
+ var MIN_WIDTH = 320;
3950
+ var MAX_WIDTH_RATIO = 0.92;
3951
+ var DEFAULT_WIDTH = 680;
3952
+ function PdfSheetV2({ src, title, onClose }) {
3953
+ const [isMounted, setIsMounted] = react.useState(false);
3954
+ const [isLoaded, setIsLoaded] = react.useState(false);
3955
+ const [width, setWidth] = react.useState(DEFAULT_WIDTH);
3956
+ const panelRef = react.useRef(null);
3957
+ const isDragging = react.useRef(false);
3958
+ const dragStartX = react.useRef(0);
3959
+ const dragStartWidth = react.useRef(0);
3960
+ react.useEffect(() => {
3961
+ setIsMounted(true);
3962
+ return () => setIsMounted(false);
3963
+ }, []);
3964
+ react.useEffect(() => {
3965
+ setIsLoaded(false);
3966
+ }, [src]);
3967
+ react.useEffect(() => {
3968
+ const onResize = () => {
3969
+ const max = Math.floor(window.innerWidth * MAX_WIDTH_RATIO);
3970
+ setWidth((w) => Math.min(w, max));
3971
+ };
3972
+ window.addEventListener("resize", onResize);
3973
+ return () => window.removeEventListener("resize", onResize);
3974
+ }, []);
3975
+ const handleKeyDown = react.useCallback(
3976
+ (e) => {
3977
+ if (e.key === "Escape") onClose();
3978
+ },
3979
+ [onClose]
3980
+ );
3981
+ react.useEffect(() => {
3982
+ if (!src || typeof document === "undefined") return;
3983
+ document.addEventListener("keydown", handleKeyDown);
3984
+ const previousOverflow = document.body.style.overflow;
3985
+ document.body.style.overflow = "hidden";
3986
+ return () => {
3987
+ document.removeEventListener("keydown", handleKeyDown);
3988
+ document.body.style.overflow = previousOverflow;
3989
+ };
3990
+ }, [src, handleKeyDown]);
3991
+ const handleDownload = () => {
3992
+ if (!src || typeof document === "undefined") return;
3993
+ const a = document.createElement("a");
3994
+ a.href = src;
3995
+ a.download = title ? `${title}.pdf` : "document.pdf";
3996
+ document.body.appendChild(a);
3997
+ a.click();
3998
+ a.remove();
3999
+ };
4000
+ const onResizeMouseDown = (e) => {
4001
+ e.preventDefault();
4002
+ isDragging.current = true;
4003
+ dragStartX.current = e.clientX;
4004
+ dragStartWidth.current = panelRef.current?.offsetWidth ?? width;
4005
+ document.body.style.cursor = "ew-resize";
4006
+ document.body.style.userSelect = "none";
4007
+ };
4008
+ react.useEffect(() => {
4009
+ const onMouseMove = (e) => {
4010
+ if (!isDragging.current || !panelRef.current) return;
4011
+ const delta = dragStartX.current - e.clientX;
4012
+ const maxW = Math.floor(window.innerWidth * MAX_WIDTH_RATIO);
4013
+ const newW = Math.max(MIN_WIDTH, Math.min(maxW, dragStartWidth.current + delta));
4014
+ panelRef.current.style.width = `${newW}px`;
4015
+ };
4016
+ const onMouseUp = () => {
4017
+ if (!isDragging.current) return;
4018
+ isDragging.current = false;
4019
+ document.body.style.cursor = "";
4020
+ document.body.style.userSelect = "";
4021
+ if (panelRef.current) {
4022
+ setWidth(panelRef.current.offsetWidth);
4023
+ }
4024
+ };
4025
+ document.addEventListener("mousemove", onMouseMove);
4026
+ document.addEventListener("mouseup", onMouseUp);
4027
+ return () => {
4028
+ document.removeEventListener("mousemove", onMouseMove);
4029
+ document.removeEventListener("mouseup", onMouseUp);
4030
+ };
4031
+ }, []);
4032
+ if (!isMounted || typeof document === "undefined") return null;
4033
+ return reactDom.createPortal(
4034
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: src ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4035
+ /* @__PURE__ */ jsxRuntime.jsx(
4036
+ framerMotion.motion.div,
4037
+ {
4038
+ className: "payman-v2-pdf-sheet-overlay",
4039
+ initial: { opacity: 0 },
4040
+ animate: { opacity: 1 },
4041
+ exit: { opacity: 0 },
4042
+ transition: { duration: 0.22 },
4043
+ onClick: onClose,
4044
+ "aria-hidden": "true"
4045
+ },
4046
+ "pdf-sheet-backdrop"
4047
+ ),
4048
+ /* @__PURE__ */ jsxRuntime.jsxs(
4049
+ framerMotion.motion.div,
4050
+ {
4051
+ ref: panelRef,
4052
+ className: "payman-v2-pdf-sheet",
4053
+ style: { width },
4054
+ initial: { x: "100%" },
4055
+ animate: { x: 0 },
4056
+ exit: { x: "100%" },
4057
+ transition: {
4058
+ type: "spring",
4059
+ stiffness: 340,
4060
+ damping: 34,
4061
+ mass: 0.85
4062
+ },
4063
+ role: "dialog",
4064
+ "aria-modal": "true",
4065
+ "aria-label": title || "PDF Preview",
4066
+ children: [
4067
+ /* @__PURE__ */ jsxRuntime.jsx(
4068
+ "div",
1954
4069
  {
1955
- className: "payman-v2-feedback-modal-textarea",
1956
- placeholder: "Add details (optional)",
1957
- value: details,
1958
- maxLength: MAX_DETAILS_CHARS,
1959
- onChange: (e) => setDetails(e.target.value),
1960
- disabled: submitting
4070
+ className: "payman-v2-pdf-sheet-resize-handle",
4071
+ onMouseDown: onResizeMouseDown,
4072
+ "aria-hidden": "true",
4073
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-resize-grip" })
1961
4074
  }
1962
4075
  ),
1963
- error ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-feedback-modal-error", children: error }) : null,
1964
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-feedback-modal-actions", children: [
1965
- /* @__PURE__ */ jsxRuntime.jsx(
1966
- "button",
1967
- {
1968
- type: "button",
1969
- onClick: onClose,
1970
- disabled: submitting,
1971
- className: "payman-v2-feedback-modal-btn payman-v2-feedback-modal-btn-secondary",
1972
- children: "Cancel"
1973
- }
1974
- ),
4076
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header", children: [
4077
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header-left", children: [
4078
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-file-icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 14, strokeWidth: 1.75 }) }),
4079
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-pdf-sheet-title", title, children: title || "Document" })
4080
+ ] }),
4081
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header-actions", children: [
4082
+ /* @__PURE__ */ jsxRuntime.jsxs(
4083
+ "button",
4084
+ {
4085
+ type: "button",
4086
+ className: "payman-v2-pdf-sheet-download-btn",
4087
+ "aria-label": "Download PDF",
4088
+ onClick: handleDownload,
4089
+ children: [
4090
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 13, strokeWidth: 2 }),
4091
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Download" })
4092
+ ]
4093
+ }
4094
+ ),
4095
+ /* @__PURE__ */ jsxRuntime.jsx(
4096
+ "button",
4097
+ {
4098
+ type: "button",
4099
+ className: "payman-v2-pdf-sheet-close-btn",
4100
+ "aria-label": "Close preview",
4101
+ onClick: onClose,
4102
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 15, strokeWidth: 2.25 })
4103
+ }
4104
+ )
4105
+ ] })
4106
+ ] }),
4107
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-body", children: [
4108
+ !isLoaded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-loading", children: [
4109
+ /* @__PURE__ */ jsxRuntime.jsx(
4110
+ lucideReact.Loader2,
4111
+ {
4112
+ size: 20,
4113
+ strokeWidth: 2,
4114
+ style: {
4115
+ animation: "payman-v2-spin 0.65s linear infinite",
4116
+ color: "var(--payman-v2-text-3)"
4117
+ }
4118
+ }
4119
+ ),
4120
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-pdf-sheet-loading-text", children: "Loading PDF\u2026" })
4121
+ ] }),
1975
4122
  /* @__PURE__ */ jsxRuntime.jsx(
1976
- "button",
4123
+ "iframe",
1977
4124
  {
1978
- type: "button",
1979
- onClick: handleSubmit,
1980
- disabled: submitting,
1981
- className: "payman-v2-feedback-modal-btn payman-v2-feedback-modal-btn-primary",
1982
- children: submitting ? "Sending\u2026" : "Submit"
1983
- }
4125
+ src,
4126
+ title: title || "PDF Preview",
4127
+ className: "payman-v2-pdf-sheet-iframe",
4128
+ style: { opacity: isLoaded ? 1 : 0, transition: "opacity 0.3s ease" },
4129
+ onLoad: () => setIsLoaded(true)
4130
+ },
4131
+ src
1984
4132
  )
1985
4133
  ] })
1986
4134
  ]
1987
- }
4135
+ },
4136
+ "pdf-sheet-panel"
1988
4137
  )
1989
- }
1990
- ) : null });
4138
+ ] }) : null }),
4139
+ document.body
4140
+ );
1991
4141
  }
1992
4142
  var RESPONSE_SPEED = {
1993
4143
  normal: [2, 4],
@@ -2116,6 +4266,10 @@ function AssistantMessageV2({
2116
4266
  () => getFeedbackState(message)
2117
4267
  );
2118
4268
  const [reasonModalOpen, setReasonModalOpen] = react.useState(false);
4269
+ const [pdfSheet, setPdfSheet] = react.useState(null);
4270
+ const handlePdfClick = react.useCallback((href, title) => {
4271
+ setPdfSheet({ href, title });
4272
+ }, []);
2119
4273
  const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
2120
4274
  const [toast, setToast] = react.useState(null);
2121
4275
  const copyResetTimerRef = react.useRef(null);
@@ -2313,7 +4467,8 @@ function AssistantMessageV2({
2313
4467
  content: displayContent,
2314
4468
  isStreaming: message.isStreaming && !isCancelled || isResponseTyping,
2315
4469
  isResolvingImages: message.isResolvingImages,
2316
- onImageClick
4470
+ onImageClick,
4471
+ onPdfClick: handlePdfClick
2317
4472
  }
2318
4473
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
2319
4474
  isCancelled && message.isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
@@ -2411,6 +4566,14 @@ function AssistantMessageV2({
2411
4566
  showToast("Thank you for your feedback", "success");
2412
4567
  }
2413
4568
  }
4569
+ ),
4570
+ /* @__PURE__ */ jsxRuntime.jsx(
4571
+ PdfSheetV2,
4572
+ {
4573
+ src: pdfSheet?.href ?? null,
4574
+ title: pdfSheet?.title ?? "",
4575
+ onClose: () => setPdfSheet(null)
4576
+ }
2414
4577
  )
2415
4578
  ] });
2416
4579
  }
@@ -2509,56 +4672,61 @@ function OtpInputV2({
2509
4672
  i
2510
4673
  )) });
2511
4674
  }
4675
+ var DEFAULT_CODE_LEN = 6;
2512
4676
  var RESEND_COOLDOWN_S = 30;
2513
- var OTP_LEN = 6;
2514
- function VerificationCardV2({
2515
- messageId,
2516
- action,
2517
- status,
2518
- clearOtpTrigger = 0,
2519
- onApprove,
2520
- onReject,
4677
+ function codeLengthFromSchema(prompt) {
4678
+ const field = prompt.requestedSchema?.properties?.verificationCode;
4679
+ const max = typeof field?.maxLength === "number" ? field.maxLength : void 0;
4680
+ const min = typeof field?.minLength === "number" ? field.minLength : void 0;
4681
+ return max ?? min ?? DEFAULT_CODE_LEN;
4682
+ }
4683
+ function VerificationInline({
4684
+ prompt,
4685
+ secondsLeft,
4686
+ expired,
4687
+ onSubmit,
4688
+ onCancel,
2521
4689
  onResend
2522
4690
  }) {
2523
- const [otp, setOtp] = react.useState("");
2524
- const [otpErrored, setOtpErrored] = react.useState(false);
4691
+ const isNumeric = prompt.verificationType !== "ALPHANUMERIC_CODE";
4692
+ const codeLen = react.useMemo(() => codeLengthFromSchema(prompt), [prompt]);
4693
+ const [code, setCode] = react.useState("");
4694
+ const [errored, setErrored] = react.useState(false);
2525
4695
  const [resendSec, setResendSec] = react.useState(0);
2526
- const [localPending, setLocalPending] = react.useState(false);
2527
4696
  const lastSubmittedRef = react.useRef(null);
2528
4697
  const resendTimerRef = react.useRef(void 0);
4698
+ const status = prompt.status;
4699
+ const busy = status === "submitting";
4700
+ const stale = status === "stale";
4701
+ const locked = busy || stale || expired;
2529
4702
  react.useEffect(() => {
2530
- if (clearOtpTrigger <= 0) return;
2531
- setOtpErrored(true);
2532
- const t = window.setTimeout(() => {
2533
- setOtp("");
2534
- setOtpErrored(false);
2535
- setLocalPending(false);
2536
- lastSubmittedRef.current = null;
2537
- }, 600);
2538
- return () => window.clearTimeout(t);
2539
- }, [clearOtpTrigger]);
2540
- react.useEffect(() => {
2541
- if (otp.length < OTP_LEN) {
4703
+ if (prompt.subAction === "SubmissionInvalid") {
4704
+ setErrored(true);
4705
+ setCode("");
2542
4706
  lastSubmittedRef.current = null;
2543
4707
  }
2544
- }, [otp]);
4708
+ }, [prompt.subAction, prompt.userActionId]);
2545
4709
  react.useEffect(() => {
2546
- if (status !== "pending") {
2547
- setLocalPending(false);
2548
- }
2549
- }, [status]);
4710
+ if (code.length < codeLen) lastSubmittedRef.current = null;
4711
+ }, [code, codeLen]);
4712
+ const doSubmit = react.useCallback(
4713
+ (value) => {
4714
+ if (locked || !value) return;
4715
+ if (lastSubmittedRef.current === value) return;
4716
+ lastSubmittedRef.current = value;
4717
+ void onSubmit(prompt.userActionId, { verificationCode: value }).catch(() => {
4718
+ lastSubmittedRef.current = null;
4719
+ setErrored(true);
4720
+ });
4721
+ },
4722
+ [locked, onSubmit, prompt.userActionId]
4723
+ );
2550
4724
  react.useEffect(() => {
2551
- if (otp.length !== OTP_LEN || !/^\d+$/.test(otp) || status !== "pending") {
2552
- return;
4725
+ if (!isNumeric || locked) return;
4726
+ if (code.length === codeLen && /^\d+$/.test(code)) {
4727
+ doSubmit(code);
2553
4728
  }
2554
- if (lastSubmittedRef.current === otp) return;
2555
- lastSubmittedRef.current = otp;
2556
- setLocalPending(true);
2557
- void onApprove(messageId, otp).catch(() => {
2558
- lastSubmittedRef.current = null;
2559
- setLocalPending(false);
2560
- });
2561
- }, [messageId, onApprove, otp, status]);
4729
+ }, [code, codeLen, doSubmit, isNumeric, locked]);
2562
4730
  react.useEffect(() => {
2563
4731
  return () => {
2564
4732
  if (typeof resendTimerRef.current === "number") {
@@ -2585,94 +4753,309 @@ function VerificationCardV2({
2585
4753
  }, 1e3);
2586
4754
  }, []);
2587
4755
  const handleResend = react.useCallback(async () => {
2588
- if (resendSec > 0 || status === "verifying" || localPending) return;
2589
- setLocalPending(true);
4756
+ if (locked || resendSec > 0) return;
4757
+ setErrored(false);
4758
+ setCode("");
4759
+ lastSubmittedRef.current = null;
2590
4760
  try {
2591
- await onResend(messageId);
4761
+ await onResend(prompt.userActionId);
2592
4762
  startResendCooldown();
2593
- } finally {
2594
- setLocalPending(false);
2595
- }
2596
- }, [localPending, messageId, onResend, resendSec, startResendCooldown, status]);
2597
- const handleCancel = react.useCallback(async () => {
2598
- if (status === "verifying" || localPending) return;
2599
- setLocalPending(true);
2600
- try {
2601
- await onReject(messageId);
2602
- } finally {
2603
- setLocalPending(false);
4763
+ } catch {
2604
4764
  }
2605
- }, [localPending, messageId, onReject, status]);
2606
- const busy = status === "verifying" || localPending;
2607
- if (status === "approved" || status === "rejected") {
2608
- return null;
2609
- }
2610
- return /* @__PURE__ */ jsxRuntime.jsxs(
2611
- framerMotion.motion.div,
2612
- {
2613
- className: "payman-v2-verification",
2614
- initial: { opacity: 0, y: 10 },
2615
- animate: { opacity: 1, y: 0 },
2616
- transition: { type: "spring", stiffness: 320, damping: 28 },
2617
- children: [
2618
- /* @__PURE__ */ jsxRuntime.jsxs(
2619
- "div",
4765
+ }, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
4766
+ const handleCancel = react.useCallback(() => {
4767
+ if (busy) return;
4768
+ void onCancel(prompt.userActionId);
4769
+ }, [busy, onCancel, prompt.userActionId]);
4770
+ const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4771
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
4772
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
4773
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
4774
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Verification required" }),
4775
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4776
+ ] }),
4777
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: description }),
4778
+ stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4779
+ isNumeric ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-field", children: /* @__PURE__ */ jsxRuntime.jsx(
4780
+ OtpInputV2,
4781
+ {
4782
+ value: code,
4783
+ onChange: (v) => {
4784
+ setErrored(false);
4785
+ setCode(v);
4786
+ },
4787
+ maxLength: codeLen,
4788
+ disabled: locked,
4789
+ error: errored
4790
+ }
4791
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-field", children: /* @__PURE__ */ jsxRuntime.jsx(
4792
+ "input",
4793
+ {
4794
+ type: "text",
4795
+ className: cn("payman-v2-ua-input", errored && "payman-v2-ua-input-error"),
4796
+ value: code,
4797
+ disabled: locked,
4798
+ autoComplete: "one-time-code",
4799
+ placeholder: "Verification code",
4800
+ onChange: (e) => {
4801
+ setErrored(false);
4802
+ setCode(e.target.value);
4803
+ },
4804
+ onKeyDown: (e) => {
4805
+ if (e.key === "Enter") {
4806
+ e.preventDefault();
4807
+ doSubmit(code.trim());
4808
+ }
4809
+ }
4810
+ }
4811
+ ) }),
4812
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-actions", children: [
4813
+ !isNumeric && /* @__PURE__ */ jsxRuntime.jsx(
4814
+ "button",
2620
4815
  {
2621
- className: cn(
2622
- "payman-v2-verification-card",
2623
- busy && "payman-v2-verification-card-busy"
2624
- ),
2625
- children: [
2626
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-verification-header", children: [
2627
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-verification-header-row", children: [
2628
- /* @__PURE__ */ jsxRuntime.jsx(
2629
- lucideReact.ShieldCheck,
2630
- {
2631
- className: "payman-v2-verification-icon",
2632
- size: 18,
2633
- strokeWidth: 1.75,
2634
- "aria-hidden": true
2635
- }
2636
- ),
2637
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-verification-title", children: "Verify" }),
2638
- action.amount != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-verification-amount", children: action.amount }) : action.payeeName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-verification-payee", children: action.payeeName }) : null
2639
- ] }),
2640
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-verification-description", children: "Enter the 6-digit code sent to your phone" }),
2641
- /* @__PURE__ */ jsxRuntime.jsx(
2642
- OtpInputV2,
2643
- {
2644
- value: otp,
2645
- onChange: setOtp,
2646
- maxLength: OTP_LEN,
2647
- disabled: busy,
2648
- error: otpErrored
2649
- }
2650
- )
2651
- ] }),
2652
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-verification-actions", children: /* @__PURE__ */ jsxRuntime.jsx(
2653
- "button",
2654
- {
2655
- type: "button",
2656
- className: "payman-v2-verification-cancel-btn",
2657
- disabled: busy,
2658
- onClick: () => void handleCancel(),
2659
- children: "Cancel"
2660
- }
2661
- ) })
2662
- ]
4816
+ type: "button",
4817
+ className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
4818
+ disabled: locked || !code.trim(),
4819
+ onClick: () => doSubmit(code.trim()),
4820
+ children: busy ? "Submitting\u2026" : "Submit"
2663
4821
  }
2664
4822
  ),
2665
4823
  /* @__PURE__ */ jsxRuntime.jsx(
2666
4824
  "button",
2667
4825
  {
2668
4826
  type: "button",
2669
- className: "payman-v2-verification-resend-link",
2670
- disabled: busy || resendSec > 0,
4827
+ className: "payman-v2-ua-link",
4828
+ disabled: locked || resendSec > 0,
2671
4829
  onClick: () => void handleResend(),
2672
4830
  children: resendSec > 0 ? `Resend (${resendSec}s)` : "Resend"
2673
4831
  }
4832
+ ),
4833
+ /* @__PURE__ */ jsxRuntime.jsx(
4834
+ "button",
4835
+ {
4836
+ type: "button",
4837
+ className: "payman-v2-ua-link payman-v2-ua-link-danger",
4838
+ disabled: busy,
4839
+ onClick: handleCancel,
4840
+ children: "Cancel"
4841
+ }
2674
4842
  )
2675
- ]
4843
+ ] })
4844
+ ] })
4845
+ ] });
4846
+ }
4847
+ function SchemaFormInline({
4848
+ prompt,
4849
+ secondsLeft,
4850
+ expired,
4851
+ onSubmit,
4852
+ onCancel
4853
+ }) {
4854
+ const schema = prompt.requestedSchema;
4855
+ const fields = react.useMemo(() => renderableFields(schema), [schema]);
4856
+ const [values, setValues] = react.useState(() => {
4857
+ const init2 = {};
4858
+ for (const [key, field] of fields) init2[key] = defaultValueFor(field);
4859
+ return init2;
4860
+ });
4861
+ const [errors, setErrors] = react.useState({});
4862
+ const status = prompt.status;
4863
+ const busy = status === "submitting";
4864
+ const stale = status === "stale";
4865
+ const locked = busy || stale || expired;
4866
+ const setValue = (key, value) => {
4867
+ setValues((prev) => ({ ...prev, [key]: value }));
4868
+ setErrors((prev) => {
4869
+ if (!prev[key]) return prev;
4870
+ const next = { ...prev };
4871
+ delete next[key];
4872
+ return next;
4873
+ });
4874
+ };
4875
+ const handleSubmit = () => {
4876
+ if (locked) return;
4877
+ const validation = validateForm(schema, values);
4878
+ if (Object.keys(validation).length > 0) {
4879
+ setErrors(validation);
4880
+ return;
4881
+ }
4882
+ void onSubmit(prompt.userActionId, buildContent(schema, values)).catch(() => {
4883
+ });
4884
+ };
4885
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Action required", children: [
4886
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
4887
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "payman-v2-ua-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4888
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4889
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4890
+ ] }),
4891
+ prompt.message?.trim() && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: prompt.message }),
4892
+ stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: "This action has no inputs to fill." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
4893
+ const widget = classifyField(field);
4894
+ const label = field.title || key;
4895
+ const required = isRequired(schema, key);
4896
+ const err = errors[key];
4897
+ const fieldId = `ua-${prompt.userActionId}-${key}`;
4898
+ if (widget === "boolean") {
4899
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payman-v2-ua-check", children: [
4900
+ /* @__PURE__ */ jsxRuntime.jsx(
4901
+ "input",
4902
+ {
4903
+ type: "checkbox",
4904
+ checked: Boolean(values[key]),
4905
+ disabled: locked,
4906
+ onChange: (e) => setValue(key, e.target.checked)
4907
+ }
4908
+ ),
4909
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4910
+ label,
4911
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-req", children: "*" })
4912
+ ] })
4913
+ ] }, key);
4914
+ }
4915
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-row", children: [
4916
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: fieldId, className: "payman-v2-ua-label", children: [
4917
+ label,
4918
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-req", children: "*" })
4919
+ ] }),
4920
+ field.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-hint", children: field.description }),
4921
+ widget === "select" ? /* @__PURE__ */ jsxRuntime.jsxs(
4922
+ "select",
4923
+ {
4924
+ id: fieldId,
4925
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4926
+ value: String(values[key] ?? ""),
4927
+ disabled: locked,
4928
+ onChange: (e) => setValue(key, e.target.value),
4929
+ children: [
4930
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select\u2026" }),
4931
+ getOptions(field).map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.const, children: opt.title || opt.const }, opt.const))
4932
+ ]
4933
+ }
4934
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
4935
+ "input",
4936
+ {
4937
+ id: fieldId,
4938
+ type: widget === "integer" || widget === "decimal" ? "number" : "text",
4939
+ inputMode: widget === "integer" ? "numeric" : widget === "decimal" ? "decimal" : void 0,
4940
+ step: widget === "decimal" ? "any" : widget === "integer" ? "1" : void 0,
4941
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4942
+ value: String(values[key] ?? ""),
4943
+ disabled: locked,
4944
+ placeholder: field.description ? void 0 : label,
4945
+ onChange: (e) => setValue(key, e.target.value),
4946
+ onKeyDown: (e) => {
4947
+ if (e.key === "Enter") {
4948
+ e.preventDefault();
4949
+ handleSubmit();
4950
+ }
4951
+ }
4952
+ }
4953
+ ),
4954
+ err && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-error", children: err })
4955
+ ] }, key);
4956
+ }) }),
4957
+ !stale && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-actions", children: [
4958
+ /* @__PURE__ */ jsxRuntime.jsx(
4959
+ "button",
4960
+ {
4961
+ type: "button",
4962
+ className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
4963
+ disabled: locked,
4964
+ onClick: handleSubmit,
4965
+ children: busy ? "Submitting\u2026" : "Submit"
4966
+ }
4967
+ ),
4968
+ /* @__PURE__ */ jsxRuntime.jsx(
4969
+ "button",
4970
+ {
4971
+ type: "button",
4972
+ className: "payman-v2-ua-link payman-v2-ua-link-danger",
4973
+ disabled: busy,
4974
+ onClick: () => void onCancel(prompt.userActionId),
4975
+ children: "Cancel"
4976
+ }
4977
+ )
4978
+ ] })
4979
+ ] });
4980
+ }
4981
+ function NotificationInline({ notification, onDismiss }) {
4982
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-note", role: "status", children: [
4983
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "payman-v2-ua-note-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4984
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-note-text", children: notification.message }),
4985
+ onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
4986
+ "button",
4987
+ {
4988
+ type: "button",
4989
+ className: "payman-v2-ua-note-dismiss",
4990
+ "aria-label": "Dismiss notification",
4991
+ onClick: () => onDismiss(notification.id),
4992
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 13, strokeWidth: 2 })
4993
+ }
4994
+ )
4995
+ ] });
4996
+ }
4997
+ function useExpiryCountdown(prompt) {
4998
+ const initial = typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0 ? Math.floor(prompt.expirySeconds) : void 0;
4999
+ const [secondsLeft, setSecondsLeft] = react.useState(initial);
5000
+ react.useEffect(() => {
5001
+ if (initial === void 0) {
5002
+ setSecondsLeft(void 0);
5003
+ return;
5004
+ }
5005
+ setSecondsLeft(initial);
5006
+ const id = window.setInterval(() => {
5007
+ setSecondsLeft((s) => {
5008
+ if (s === void 0) return s;
5009
+ if (s <= 1) {
5010
+ window.clearInterval(id);
5011
+ return 0;
5012
+ }
5013
+ return s - 1;
5014
+ });
5015
+ }, 1e3);
5016
+ return () => window.clearInterval(id);
5017
+ }, [prompt.userActionId, prompt.subAction, initial]);
5018
+ const expired = initial !== void 0 && secondsLeft === 0;
5019
+ return [secondsLeft, expired];
5020
+ }
5021
+ function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
5022
+ const [secondsLeft, expired] = useExpiryCountdown(prompt);
5023
+ let body;
5024
+ if (prompt.kind === "verification") {
5025
+ body = /* @__PURE__ */ jsxRuntime.jsx(
5026
+ VerificationInline,
5027
+ {
5028
+ prompt,
5029
+ secondsLeft,
5030
+ expired,
5031
+ onSubmit,
5032
+ onCancel,
5033
+ onResend
5034
+ }
5035
+ );
5036
+ } else if (prompt.kind === "notification") {
5037
+ const note = { id: prompt.userActionId, message: prompt.message ?? "" };
5038
+ body = /* @__PURE__ */ jsxRuntime.jsx(NotificationInline, { notification: note });
5039
+ } else {
5040
+ body = /* @__PURE__ */ jsxRuntime.jsx(
5041
+ SchemaFormInline,
5042
+ {
5043
+ prompt,
5044
+ secondsLeft,
5045
+ expired,
5046
+ onSubmit,
5047
+ onCancel
5048
+ }
5049
+ );
5050
+ }
5051
+ return /* @__PURE__ */ jsxRuntime.jsx(
5052
+ framerMotion.motion.div,
5053
+ {
5054
+ className: "payman-v2-ua-wrap",
5055
+ initial: { opacity: 0, y: 8 },
5056
+ animate: { opacity: 1, y: 0 },
5057
+ transition: { type: "spring", stiffness: 320, damping: 28 },
5058
+ children: body
2676
5059
  }
2677
5060
  );
2678
5061
  }
@@ -2688,13 +5071,17 @@ var MessageListV2 = react.forwardRef(
2688
5071
  onExecutionTraceClick,
2689
5072
  messageActions,
2690
5073
  retryDisabled = false,
2691
- userAction,
2692
- onApproveAction,
2693
- onRejectAction,
2694
- onResendAction,
5074
+ userActionPrompts,
5075
+ notifications,
5076
+ onSubmitUserAction,
5077
+ onCancelUserAction,
5078
+ onResendUserAction,
5079
+ onDismissNotification,
2695
5080
  onSubmitFeedback,
2696
5081
  typingSpeed = 4
2697
5082
  }, ref) {
5083
+ const noop = react.useCallback(async () => {
5084
+ }, []);
2698
5085
  const scrollRef = react.useRef(null);
2699
5086
  const scrollInnerRef = react.useRef(null);
2700
5087
  const isNearBottomRef = react.useRef(true);
@@ -2837,20 +5224,24 @@ var MessageListV2 = react.forwardRef(
2837
5224
  typingSpeed
2838
5225
  }
2839
5226
  ) }, message.id)),
2840
- userAction && userAction.status !== "approved" && userAction.status !== "rejected" && /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
2841
- VerificationCardV2,
5227
+ notifications?.map((note) => /* @__PURE__ */ jsxRuntime.jsx(
5228
+ NotificationInline,
2842
5229
  {
2843
- messageId: userAction.messageId,
2844
- action: userAction.action,
2845
- status: userAction.status,
2846
- onApprove: onApproveAction ?? (async () => {
2847
- }),
2848
- onReject: onRejectAction ?? (async () => {
2849
- }),
2850
- onResend: onResendAction ?? (async () => {
2851
- })
2852
- }
2853
- ) })
5230
+ notification: note,
5231
+ onDismiss: onDismissNotification
5232
+ },
5233
+ note.id
5234
+ )),
5235
+ userActionPrompts?.map((prompt) => /* @__PURE__ */ jsxRuntime.jsx(
5236
+ UserActionInline,
5237
+ {
5238
+ prompt,
5239
+ onSubmit: onSubmitUserAction ?? noop,
5240
+ onCancel: onCancelUserAction ?? noop,
5241
+ onResend: onResendUserAction ?? noop
5242
+ },
5243
+ prompt.toolCallId || prompt.userActionId
5244
+ ))
2854
5245
  ]
2855
5246
  }
2856
5247
  )
@@ -3911,10 +6302,13 @@ function TimelineBars({
3911
6302
  ] });
3912
6303
  }
3913
6304
  var DEFAULT_USER_ACTION_STATE = {
3914
- request: null,
3915
- result: null};
6305
+ prompts: [],
6306
+ notifications: []
6307
+ };
3916
6308
  var NOOP_ASYNC = async () => {
3917
6309
  };
6310
+ var NOOP = () => {
6311
+ };
3918
6312
  function useSentryChatCallbacks(callbacks, config) {
3919
6313
  const sentryCtxRef = react.useRef({});
3920
6314
  const callbacksRef = react.useRef(callbacks);
@@ -3984,7 +6378,7 @@ function useSentryChatCallbacks(callbacks, config) {
3984
6378
  onAttachFileClick: () => callbacksRef.current?.onAttachFileClick?.(),
3985
6379
  onMessageFeedback: (data) => callbacksRef.current?.onMessageFeedback?.(data),
3986
6380
  onUserActionRequired: (req) => callbacksRef.current?.onUserActionRequired?.(req),
3987
- onUserActionEvent: (evType, msg) => callbacksRef.current?.onUserActionEvent?.(evType, msg),
6381
+ onUserNotification: (note) => callbacksRef.current?.onUserNotification?.(note),
3988
6382
  onStatusMessage: (msg) => callbacksRef.current?.onStatusMessage?.(msg),
3989
6383
  onStepsUpdate: (steps) => callbacksRef.current?.onStepsUpdate?.(steps)
3990
6384
  }),
@@ -4052,10 +6446,11 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
4052
6446
  }
4053
6447
  }, [editingMessageId, messages]);
4054
6448
  const userActionState = chat.userActionState ?? DEFAULT_USER_ACTION_STATE;
4055
- const approveUserAction = chat.approveUserAction ?? NOOP_ASYNC;
4056
- const rejectUserAction = chat.rejectUserAction ?? NOOP_ASYNC;
4057
- const resendOtp = chat.resendOtp ?? NOOP_ASYNC;
4058
- const isUserActionSupported = typeof chat.approveUserAction === "function" && typeof chat.rejectUserAction === "function" && typeof chat.resendOtp === "function";
6449
+ const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
6450
+ const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
6451
+ const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6452
+ const dismissNotification = chat.dismissNotification ?? NOOP;
6453
+ const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
4059
6454
  const {
4060
6455
  transcribedText,
4061
6456
  isAvailable: voiceAvailable,
@@ -4063,7 +6458,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
4063
6458
  startRecording,
4064
6459
  stopRecording,
4065
6460
  clearTranscript
4066
- } = paymanTypescriptAskSdk.useVoice(
6461
+ } = useVoice(
4067
6462
  {
4068
6463
  lang: config.voiceLang || "en-US",
4069
6464
  interimResults: config.voiceInterimResults !== false,
@@ -4296,23 +6691,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
4296
6691
  setLightboxSrc(src);
4297
6692
  setLightboxAlt(alt);
4298
6693
  };
4299
- const v2UserAction = react.useMemo(() => {
4300
- if (!isUserActionSupported || !userActionState.request) return null;
4301
- const req = userActionState.request;
4302
- let status = "pending";
4303
- if (userActionState.result === "approved") status = "approved";
4304
- else if (userActionState.result === "rejected") status = "rejected";
4305
- return {
4306
- messageId: `ua-${req.userActionId || Date.now()}`,
4307
- action: {
4308
- type: req.userActionType || "generic",
4309
- message: req.message || "Verify this action",
4310
- amount: req.metadata?.amount,
4311
- payeeName: req.metadata?.payeeName
4312
- },
4313
- status
4314
- };
4315
- }, [isUserActionSupported, userActionState.request, userActionState.result]);
6694
+ const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
6695
+ const notifications = userActionState.notifications;
4316
6696
  const handleV2Send = (text) => {
4317
6697
  if (isRecording) stopRecording();
4318
6698
  if (text.trim() && !disableInput && isSessionParamsConfigured) {
@@ -4365,7 +6745,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
4365
6745
  style,
4366
6746
  children: [
4367
6747
  children,
4368
- isChatDisabled ? disabledComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "1rem" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", color: "var(--payman-v2-text-3)", fontSize: "0.875rem" }, children: "Chat is currently disabled" }) }) : /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsxRuntime.jsx(
6748
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsxRuntime.jsx(
4369
6749
  framerMotion.motion.div,
4370
6750
  {
4371
6751
  initial: { opacity: 1 },
@@ -4464,16 +6844,12 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
4464
6844
  messageActions,
4465
6845
  retryDisabled: isWaitingForResponse,
4466
6846
  typingSpeed: config.typingSpeed ?? 4,
4467
- userAction: v2UserAction,
4468
- onApproveAction: isUserActionSupported ? async (_msgId, otp) => {
4469
- await approveUserAction(otp);
4470
- } : void 0,
4471
- onRejectAction: isUserActionSupported ? async () => {
4472
- await rejectUserAction();
4473
- } : void 0,
4474
- onResendAction: isUserActionSupported ? async () => {
4475
- await resendOtp();
4476
- } : void 0,
6847
+ userActionPrompts,
6848
+ notifications,
6849
+ onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6850
+ onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6851
+ onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6852
+ onDismissNotification: dismissNotification,
4477
6853
  onSubmitFeedback: handleSubmitFeedback
4478
6854
  }
4479
6855
  ),
@@ -4551,56 +6927,22 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
4551
6927
  var PaymanChat = react.forwardRef(
4552
6928
  function PaymanChat2(props, ref) {
4553
6929
  const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
4554
- const chat = paymanTypescriptAskSdk.useChatV2(props.config, mergedCallbacks);
6930
+ const chat = useChatV2(props.config, mergedCallbacks);
4555
6931
  return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatInner, { ...props, chat, ref });
4556
6932
  }
4557
6933
  );
4558
6934
 
4559
- Object.defineProperty(exports, "buildFormattedThinking", {
4560
- enumerable: true,
4561
- get: function () { return paymanTypescriptAskSdk.buildFormattedThinking; }
4562
- });
4563
- Object.defineProperty(exports, "cancelUserAction", {
4564
- enumerable: true,
4565
- get: function () { return paymanTypescriptAskSdk.cancelUserAction; }
4566
- });
4567
- Object.defineProperty(exports, "createInitialV2State", {
4568
- enumerable: true,
4569
- get: function () { return paymanTypescriptAskSdk.createInitialV2State; }
4570
- });
4571
- Object.defineProperty(exports, "generateId", {
4572
- enumerable: true,
4573
- get: function () { return paymanTypescriptAskSdk.generateId; }
4574
- });
4575
- Object.defineProperty(exports, "processStreamEventV2", {
4576
- enumerable: true,
4577
- get: function () { return paymanTypescriptAskSdk.processStreamEventV2; }
4578
- });
4579
- Object.defineProperty(exports, "resendUserAction", {
4580
- enumerable: true,
4581
- get: function () { return paymanTypescriptAskSdk.resendUserAction; }
4582
- });
4583
- Object.defineProperty(exports, "streamWorkflowEvents", {
4584
- enumerable: true,
4585
- get: function () { return paymanTypescriptAskSdk.streamWorkflowEvents; }
4586
- });
4587
- Object.defineProperty(exports, "submitUserAction", {
4588
- enumerable: true,
4589
- get: function () { return paymanTypescriptAskSdk.submitUserAction; }
4590
- });
4591
- Object.defineProperty(exports, "useChatV2", {
4592
- enumerable: true,
4593
- get: function () { return paymanTypescriptAskSdk.useChatV2; }
4594
- });
4595
- Object.defineProperty(exports, "useVoice", {
4596
- enumerable: true,
4597
- get: function () { return paymanTypescriptAskSdk.useVoice; }
4598
- });
4599
6935
  exports.PaymanChat = PaymanChat;
4600
6936
  exports.PaymanChatContext = PaymanChatContext;
6937
+ exports.UserActionStaleError = UserActionStaleError;
6938
+ exports.cancelUserAction = cancelUserAction;
4601
6939
  exports.captureSentryError = captureSentryError;
4602
6940
  exports.cn = cn;
4603
6941
  exports.formatDate = formatDate;
6942
+ exports.resendUserAction = resendUserAction;
6943
+ exports.submitUserAction = submitUserAction;
6944
+ exports.useChatV2 = useChatV2;
4604
6945
  exports.usePaymanChat = usePaymanChat;
6946
+ exports.useVoice = useVoice;
4605
6947
  //# sourceMappingURL=index.js.map
4606
6948
  //# sourceMappingURL=index.js.map