@paymanai/payman-ask-sdk 4.0.5 → 4.0.6

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
@@ -157,11 +157,17 @@ var DEFAULT_SLASH_COMMANDS = [
157
157
  requiredAnyPermissions: ["vault:author", "vault:publish"]
158
158
  }
159
159
  ];
160
+ function hasCommandPermission(granted, requiredPermission) {
161
+ if (granted.has("*") || granted.has(requiredPermission)) return true;
162
+ if (!requiredPermission.includes(".")) return false;
163
+ const wildcard = requiredPermission.substring(0, requiredPermission.lastIndexOf(".")) + ".*";
164
+ return granted.has(wildcard);
165
+ }
160
166
  function filterSlashCommands(commands, permissions) {
161
167
  const granted = new Set(permissions ?? []);
162
168
  return commands.filter((command) => {
163
169
  const required = command.requiredAnyPermissions ?? [];
164
- return required.length === 0 || required.some((permission) => granted.has(permission));
170
+ return required.length === 0 || required.some((permission) => hasCommandPermission(granted, permission));
165
171
  });
166
172
  }
167
173
  function parseSlashCommand(content) {
@@ -174,9 +180,19 @@ function parseSlashCommand(content) {
174
180
  body: match[2] ?? ""
175
181
  };
176
182
  }
177
- function slashCommandBodyIsEmpty(content) {
183
+ function getSlashCommandValidationHint(content) {
178
184
  const parsed = parseSlashCommand(content);
179
- return parsed?.command === "/draft-knowledge" && parsed.body.trim().length === 0;
185
+ if (parsed?.command !== "/draft-knowledge") return null;
186
+ const lines = parsed.body.split(/\r?\n/);
187
+ const title = lines[0]?.trim() ?? "";
188
+ const markdownContent = lines.slice(1).join("\n").trim();
189
+ if (!title) {
190
+ return "Add a title after /draft-knowledge, then add markdown content on the next line.";
191
+ }
192
+ if (!markdownContent) {
193
+ return "Add markdown content on a new line below the title.";
194
+ }
195
+ return null;
180
196
  }
181
197
 
182
198
  // src/utils/errorMessages.ts
@@ -231,6 +247,43 @@ function initSentryIfNeeded(dsn) {
231
247
  }
232
248
  });
233
249
  }
250
+
251
+ // src/utils/feedbackClient.ts
252
+ var NEGATIVE_FEEDBACK_REASONS = [
253
+ { value: "NOT_FACTUALLY_CORRECT", label: "Not factually correct" },
254
+ { value: "INCOMPLETE_RESPONSE", label: "Incomplete response" },
255
+ { value: "DID_NOT_FOLLOW_REQUEST", label: "Did not follow my request" },
256
+ { value: "OVERACTIVE_REFUSAL", label: "Refused unnecessarily" },
257
+ { value: "ISSUE_WITH_THOUGHT_PROCESS", label: "Issue with reasoning" },
258
+ { value: "REPORT_CONTENT", label: "Report content" },
259
+ { value: "OTHER", label: "Other" }
260
+ ];
261
+ async function submitAskFeedback({
262
+ baseUrl,
263
+ headers,
264
+ executionId,
265
+ feedback,
266
+ details,
267
+ signal
268
+ }) {
269
+ const base = baseUrl.replace(/\/+$/, "");
270
+ const resp = await fetch(
271
+ `${base}/api/ask/executions/${encodeURIComponent(executionId)}/feedback`,
272
+ {
273
+ method: "POST",
274
+ headers: {
275
+ "Content-Type": "application/json",
276
+ Accept: "application/json",
277
+ ...headers ?? {}
278
+ },
279
+ body: JSON.stringify(details ? { feedback, details } : { feedback }),
280
+ signal
281
+ }
282
+ );
283
+ if (!resp.ok) {
284
+ throw new Error(`Feedback failed: ${resp.status} ${resp.statusText}`);
285
+ }
286
+ }
234
287
  function ImageLightbox({
235
288
  src,
236
289
  alt = "",
@@ -1693,6 +1746,143 @@ function ThinkingBlockV2({
1693
1746
  ) }) })
1694
1747
  ] });
1695
1748
  }
1749
+ var MAX_DETAILS_CHARS = 2e3;
1750
+ function FeedbackReasonModal({
1751
+ open,
1752
+ onClose,
1753
+ onSubmit
1754
+ }) {
1755
+ const [reason, setReason] = react.useState(
1756
+ NEGATIVE_FEEDBACK_REASONS[0].value
1757
+ );
1758
+ const [details, setDetails] = react.useState("");
1759
+ const [submitting, setSubmitting] = react.useState(false);
1760
+ const [error, setError] = react.useState(null);
1761
+ react.useEffect(() => {
1762
+ if (open) {
1763
+ setReason(NEGATIVE_FEEDBACK_REASONS[0].value);
1764
+ setDetails("");
1765
+ setError(null);
1766
+ setSubmitting(false);
1767
+ }
1768
+ }, [open]);
1769
+ const handleKeyDown = react.useCallback(
1770
+ (event) => {
1771
+ if (event.key === "Escape") onClose();
1772
+ },
1773
+ [onClose]
1774
+ );
1775
+ react.useEffect(() => {
1776
+ if (!open || typeof document === "undefined") return;
1777
+ document.addEventListener("keydown", handleKeyDown);
1778
+ const previousOverflow = document.body.style.overflow;
1779
+ document.body.style.overflow = "hidden";
1780
+ return () => {
1781
+ document.removeEventListener("keydown", handleKeyDown);
1782
+ document.body.style.overflow = previousOverflow;
1783
+ };
1784
+ }, [handleKeyDown, open]);
1785
+ const handleSubmit = async () => {
1786
+ setSubmitting(true);
1787
+ setError(null);
1788
+ try {
1789
+ await onSubmit(reason, details.trim() ? details.trim() : void 0);
1790
+ onClose();
1791
+ } catch (e) {
1792
+ setError(e instanceof Error ? e.message : "Could not send feedback");
1793
+ setSubmitting(false);
1794
+ }
1795
+ };
1796
+ return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: open ? /* @__PURE__ */ jsxRuntime.jsx(
1797
+ framerMotion.motion.div,
1798
+ {
1799
+ className: "payman-v2-feedback-modal-backdrop",
1800
+ initial: { opacity: 0 },
1801
+ animate: { opacity: 1 },
1802
+ exit: { opacity: 0 },
1803
+ transition: { duration: 0.18 },
1804
+ onClick: onClose,
1805
+ role: "presentation",
1806
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1807
+ framerMotion.motion.div,
1808
+ {
1809
+ initial: { opacity: 0, y: 12, scale: 0.98 },
1810
+ animate: { opacity: 1, y: 0, scale: 1 },
1811
+ exit: { opacity: 0, y: 8, scale: 0.98 },
1812
+ transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
1813
+ className: "payman-v2-feedback-modal-dialog",
1814
+ onClick: (event) => event.stopPropagation(),
1815
+ role: "dialog",
1816
+ "aria-modal": "true",
1817
+ "aria-labelledby": "payman-v2-feedback-title",
1818
+ children: [
1819
+ /* @__PURE__ */ jsxRuntime.jsx(
1820
+ "h2",
1821
+ {
1822
+ id: "payman-v2-feedback-title",
1823
+ className: "payman-v2-feedback-modal-title",
1824
+ children: "Tell us what went wrong"
1825
+ }
1826
+ ),
1827
+ /* @__PURE__ */ jsxRuntime.jsx(
1828
+ "label",
1829
+ {
1830
+ className: "payman-v2-feedback-modal-label",
1831
+ htmlFor: "payman-v2-feedback-reason",
1832
+ children: "What was the issue?"
1833
+ }
1834
+ ),
1835
+ /* @__PURE__ */ jsxRuntime.jsx(
1836
+ "select",
1837
+ {
1838
+ id: "payman-v2-feedback-reason",
1839
+ className: "payman-v2-feedback-modal-select",
1840
+ value: reason,
1841
+ onChange: (e) => setReason(e.target.value),
1842
+ disabled: submitting,
1843
+ children: NEGATIVE_FEEDBACK_REASONS.map((r) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: r.value, children: r.label }, r.value))
1844
+ }
1845
+ ),
1846
+ /* @__PURE__ */ jsxRuntime.jsx(
1847
+ "textarea",
1848
+ {
1849
+ className: "payman-v2-feedback-modal-textarea",
1850
+ placeholder: "Add details (optional)",
1851
+ value: details,
1852
+ maxLength: MAX_DETAILS_CHARS,
1853
+ onChange: (e) => setDetails(e.target.value),
1854
+ disabled: submitting
1855
+ }
1856
+ ),
1857
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-feedback-modal-error", children: error }) : null,
1858
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-feedback-modal-actions", children: [
1859
+ /* @__PURE__ */ jsxRuntime.jsx(
1860
+ "button",
1861
+ {
1862
+ type: "button",
1863
+ onClick: onClose,
1864
+ disabled: submitting,
1865
+ className: "payman-v2-feedback-modal-btn payman-v2-feedback-modal-btn-secondary",
1866
+ children: "Cancel"
1867
+ }
1868
+ ),
1869
+ /* @__PURE__ */ jsxRuntime.jsx(
1870
+ "button",
1871
+ {
1872
+ type: "button",
1873
+ onClick: handleSubmit,
1874
+ disabled: submitting,
1875
+ className: "payman-v2-feedback-modal-btn payman-v2-feedback-modal-btn-primary",
1876
+ children: submitting ? "Sending\u2026" : "Submit"
1877
+ }
1878
+ )
1879
+ ] })
1880
+ ]
1881
+ }
1882
+ )
1883
+ }
1884
+ ) : null });
1885
+ }
1696
1886
  var RESPONSE_SPEED = {
1697
1887
  normal: [2, 4],
1698
1888
  fast: 1,
@@ -1775,12 +1965,26 @@ function AssistantMessageV2({
1775
1965
  message,
1776
1966
  onImageClick,
1777
1967
  onExecutionTraceClick,
1778
- onFeedback,
1968
+ onSubmitFeedback,
1779
1969
  actions,
1780
1970
  typingSpeed = 4
1781
1971
  }) {
1782
1972
  const [copied, setCopied] = react.useState(false);
1783
1973
  const [activeFeedback, setActiveFeedback] = react.useState(null);
1974
+ const [reasonModalOpen, setReasonModalOpen] = react.useState(false);
1975
+ const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
1976
+ const handlePositiveFeedback = () => {
1977
+ if (!canSubmitFeedback || activeFeedback === "up") return;
1978
+ const previous = activeFeedback;
1979
+ setActiveFeedback("up");
1980
+ Promise.resolve(
1981
+ onSubmitFeedback?.({
1982
+ messageId: message.id,
1983
+ executionId: message.executionId,
1984
+ feedback: "POSITIVE"
1985
+ })
1986
+ ).catch(() => setActiveFeedback(previous));
1987
+ };
1784
1988
  const [toast, setToast] = react.useState(null);
1785
1989
  const copyResetTimerRef = react.useRef(null);
1786
1990
  const toastTimerRef = react.useRef(null);
@@ -2020,14 +2224,10 @@ function AssistantMessageV2({
2020
2224
  children: copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { style: { width: 16, height: 16 } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { style: { width: 16, height: 16 } })
2021
2225
  }
2022
2226
  ) }),
2023
- showThumbsUp && /* @__PURE__ */ jsxRuntime.jsx(ActionTooltipV2, { label: "Good response", children: /* @__PURE__ */ jsxRuntime.jsx(
2227
+ showThumbsUp && canSubmitFeedback && /* @__PURE__ */ jsxRuntime.jsx(ActionTooltipV2, { label: "Good response", children: /* @__PURE__ */ jsxRuntime.jsx(
2024
2228
  "button",
2025
2229
  {
2026
- onClick: () => {
2027
- const next = activeFeedback === "up" ? null : "up";
2028
- setActiveFeedback(next);
2029
- if (next) onFeedback?.({ messageId: message.id, feedback: "up" });
2030
- },
2230
+ onClick: handlePositiveFeedback,
2031
2231
  className: cn(
2032
2232
  "payman-v2-assistant-msg-action-btn",
2033
2233
  activeFeedback === "up" && "payman-v2-assistant-msg-action-btn-active"
@@ -2036,14 +2236,10 @@ function AssistantMessageV2({
2036
2236
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ThumbsUp, { style: { width: 15, height: 15 } })
2037
2237
  }
2038
2238
  ) }),
2039
- showThumbsDown && /* @__PURE__ */ jsxRuntime.jsx(ActionTooltipV2, { label: "Bad response", children: /* @__PURE__ */ jsxRuntime.jsx(
2239
+ showThumbsDown && canSubmitFeedback && /* @__PURE__ */ jsxRuntime.jsx(ActionTooltipV2, { label: "Bad response", children: /* @__PURE__ */ jsxRuntime.jsx(
2040
2240
  "button",
2041
2241
  {
2042
- onClick: () => {
2043
- const next = activeFeedback === "down" ? null : "down";
2044
- setActiveFeedback(next);
2045
- if (next) onFeedback?.({ messageId: message.id, feedback: "down" });
2046
- },
2242
+ onClick: () => setReasonModalOpen(true),
2047
2243
  className: cn(
2048
2244
  "payman-v2-assistant-msg-action-btn",
2049
2245
  activeFeedback === "down" && "payman-v2-assistant-msg-action-btn-active"
@@ -2063,7 +2259,23 @@ function AssistantMessageV2({
2063
2259
  ) }),
2064
2260
  totalElapsedLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-elapsed", children: totalElapsedLabel })
2065
2261
  ] })
2066
- ] })
2262
+ ] }),
2263
+ /* @__PURE__ */ jsxRuntime.jsx(
2264
+ FeedbackReasonModal,
2265
+ {
2266
+ open: reasonModalOpen,
2267
+ onClose: () => setReasonModalOpen(false),
2268
+ onSubmit: async (reason, details) => {
2269
+ await onSubmitFeedback?.({
2270
+ messageId: message.id,
2271
+ executionId: message.executionId,
2272
+ feedback: reason,
2273
+ details
2274
+ });
2275
+ setActiveFeedback("down");
2276
+ }
2277
+ }
2278
+ )
2067
2279
  ] });
2068
2280
  }
2069
2281
  var DEFAULT_MAX_LENGTH = 6;
@@ -2344,7 +2556,7 @@ var MessageListV2 = react.forwardRef(
2344
2556
  onApproveAction,
2345
2557
  onRejectAction,
2346
2558
  onResendAction,
2347
- onMessageFeedback,
2559
+ onSubmitFeedback,
2348
2560
  typingSpeed = 4
2349
2561
  }, ref) {
2350
2562
  const scrollRef = react.useRef(null);
@@ -2484,7 +2696,7 @@ var MessageListV2 = react.forwardRef(
2484
2696
  message,
2485
2697
  onImageClick,
2486
2698
  onExecutionTraceClick,
2487
- onFeedback: onMessageFeedback,
2699
+ onSubmitFeedback,
2488
2700
  actions: messageActions?.assistantMessageActions,
2489
2701
  typingSpeed
2490
2702
  }
@@ -2648,8 +2860,9 @@ var ChatInputV2 = react.forwardRef(
2648
2860
  }, [isRecording, transcribedText]);
2649
2861
  const handleSend = react.useCallback(() => {
2650
2862
  if (!value.trim() || disabled) return;
2651
- if (slashCommandBodyIsEmpty(value)) {
2652
- setInlineHint("Add markdown content below the command line.");
2863
+ const commandHint = getSlashCommandValidationHint(value);
2864
+ if (commandHint) {
2865
+ setInlineHint(commandHint);
2653
2866
  return;
2654
2867
  }
2655
2868
  voiceDraftSyncActiveRef.current = false;
@@ -3251,6 +3464,316 @@ function ImageLightboxV2({ src, alt, onClose }) {
3251
3464
  document.body
3252
3465
  );
3253
3466
  }
3467
+ var PRE_PIPELINE_STEPS = /* @__PURE__ */ new Set([
3468
+ "record_execution",
3469
+ "resolve_provider",
3470
+ "resolve_mcp_servers",
3471
+ "build_pipeline",
3472
+ "load_skill_index"
3473
+ ]);
3474
+ function stepColor(step) {
3475
+ if (step.status === "failed") return "#ef4444";
3476
+ if (PRE_PIPELINE_STEPS.has(step.step)) return "#f59e0b";
3477
+ return "#3b82f6";
3478
+ }
3479
+ function formatMs(ms) {
3480
+ if (ms == null) return "\u2014";
3481
+ if (ms < 1e3) return `${ms} ms`;
3482
+ return `${(ms / 1e3).toFixed(2)} s`;
3483
+ }
3484
+ function TraceTimelineModal({
3485
+ open,
3486
+ onClose,
3487
+ executionId,
3488
+ apiBaseUrl,
3489
+ apiHeaders
3490
+ }) {
3491
+ const [trace, setTrace] = react.useState(null);
3492
+ const [loading, setLoading] = react.useState(false);
3493
+ const [error, setError] = react.useState(null);
3494
+ react.useEffect(() => {
3495
+ if (!open || !executionId) return;
3496
+ const ctrl = new AbortController();
3497
+ setLoading(true);
3498
+ setError(null);
3499
+ setTrace(null);
3500
+ const base = apiBaseUrl.replace(/\/+$/, "");
3501
+ fetch(`${base}/api/ask/executions/${encodeURIComponent(executionId)}/trace`, {
3502
+ method: "GET",
3503
+ headers: { Accept: "application/json", ...apiHeaders },
3504
+ signal: ctrl.signal
3505
+ }).then(async (resp) => {
3506
+ if (!resp.ok) {
3507
+ throw new Error(`Trace fetch failed: ${resp.status} ${resp.statusText}`);
3508
+ }
3509
+ return resp.json();
3510
+ }).then((data) => setTrace(data)).catch((e) => {
3511
+ if (e.name === "AbortError") return;
3512
+ setError(e instanceof Error ? e.message : "Trace fetch failed");
3513
+ }).finally(() => setLoading(false));
3514
+ return () => ctrl.abort();
3515
+ }, [open, executionId, apiBaseUrl, apiHeaders]);
3516
+ const totalMs = react.useMemo(() => {
3517
+ if (!trace) return 0;
3518
+ const meta = trace.runMetadata?.totalTimeMs;
3519
+ if (typeof meta === "number" && meta > 0) return meta;
3520
+ const starts = trace.pipelineSteps.map((s) => s.startTime);
3521
+ const ends = trace.pipelineSteps.map((s) => s.endTime ?? s.startTime).filter((t) => typeof t === "number");
3522
+ if (starts.length === 0 || ends.length === 0) return 0;
3523
+ return Math.max(...ends) - Math.min(...starts);
3524
+ }, [trace]);
3525
+ const accountedMs = react.useMemo(
3526
+ () => (trace?.pipelineSteps ?? []).reduce(
3527
+ (sum, s) => sum + (s.durationMs ?? 0),
3528
+ 0
3529
+ ),
3530
+ [trace]
3531
+ );
3532
+ const unaccountedMs = Math.max(totalMs - accountedMs, 0);
3533
+ if (typeof document === "undefined") return null;
3534
+ return reactDom.createPortal(
3535
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: open && /* @__PURE__ */ jsxRuntime.jsx(
3536
+ framerMotion.motion.div,
3537
+ {
3538
+ initial: { opacity: 0 },
3539
+ animate: { opacity: 1 },
3540
+ exit: { opacity: 0 },
3541
+ transition: { duration: 0.15 },
3542
+ onClick: onClose,
3543
+ style: {
3544
+ position: "fixed",
3545
+ inset: 0,
3546
+ background: "rgba(0,0,0,0.45)",
3547
+ zIndex: 9999,
3548
+ display: "flex",
3549
+ alignItems: "center",
3550
+ justifyContent: "center",
3551
+ padding: 24
3552
+ },
3553
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
3554
+ framerMotion.motion.div,
3555
+ {
3556
+ initial: { y: 12, opacity: 0 },
3557
+ animate: { y: 0, opacity: 1 },
3558
+ exit: { y: 12, opacity: 0 },
3559
+ transition: { duration: 0.18 },
3560
+ onClick: (e) => e.stopPropagation(),
3561
+ style: {
3562
+ background: "#0b1220",
3563
+ color: "#e5e7eb",
3564
+ borderRadius: 12,
3565
+ width: "100%",
3566
+ maxWidth: 760,
3567
+ maxHeight: "85vh",
3568
+ overflow: "auto",
3569
+ boxShadow: "0 20px 60px rgba(0,0,0,0.45)",
3570
+ fontFamily: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
3571
+ },
3572
+ children: [
3573
+ /* @__PURE__ */ jsxRuntime.jsxs(
3574
+ "header",
3575
+ {
3576
+ style: {
3577
+ display: "flex",
3578
+ alignItems: "center",
3579
+ justifyContent: "space-between",
3580
+ padding: "14px 18px",
3581
+ borderBottom: "1px solid rgba(255,255,255,0.08)"
3582
+ },
3583
+ children: [
3584
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3585
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 14, fontWeight: 600 }, children: "Execution trace" }),
3586
+ /* @__PURE__ */ jsxRuntime.jsx(
3587
+ "div",
3588
+ {
3589
+ style: {
3590
+ fontSize: 11,
3591
+ opacity: 0.6,
3592
+ marginTop: 2,
3593
+ fontFamily: "ui-monospace, SFMono-Regular, monospace"
3594
+ },
3595
+ children: executionId ?? ""
3596
+ }
3597
+ )
3598
+ ] }),
3599
+ /* @__PURE__ */ jsxRuntime.jsx(
3600
+ "button",
3601
+ {
3602
+ onClick: onClose,
3603
+ "aria-label": "Close",
3604
+ style: {
3605
+ background: "transparent",
3606
+ border: 0,
3607
+ color: "#e5e7eb",
3608
+ cursor: "pointer",
3609
+ padding: 6,
3610
+ borderRadius: 6
3611
+ },
3612
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { style: { width: 18, height: 18 } })
3613
+ }
3614
+ )
3615
+ ]
3616
+ }
3617
+ ),
3618
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: 18 }, children: [
3619
+ loading && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { opacity: 0.7, fontSize: 13 }, children: "Loading trace\u2026" }),
3620
+ error && /* @__PURE__ */ jsxRuntime.jsx(
3621
+ "div",
3622
+ {
3623
+ style: {
3624
+ color: "#fca5a5",
3625
+ fontSize: 13,
3626
+ whiteSpace: "pre-wrap"
3627
+ },
3628
+ children: error
3629
+ }
3630
+ ),
3631
+ trace && !loading && !error && /* @__PURE__ */ jsxRuntime.jsx(
3632
+ TimelineBars,
3633
+ {
3634
+ trace,
3635
+ totalMs,
3636
+ unaccountedMs
3637
+ }
3638
+ )
3639
+ ] })
3640
+ ]
3641
+ }
3642
+ )
3643
+ }
3644
+ ) }),
3645
+ document.body
3646
+ );
3647
+ }
3648
+ function TimelineBars({
3649
+ trace,
3650
+ totalMs,
3651
+ unaccountedMs
3652
+ }) {
3653
+ const bars = trace.pipelineSteps.map((step) => {
3654
+ const duration = step.durationMs ?? 0;
3655
+ const widthPct = totalMs > 0 ? duration / totalMs * 100 : 0;
3656
+ const llmMs = step.llmCall?.durationMs ?? null;
3657
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 8 }, children: [
3658
+ /* @__PURE__ */ jsxRuntime.jsxs(
3659
+ "div",
3660
+ {
3661
+ style: {
3662
+ display: "flex",
3663
+ justifyContent: "space-between",
3664
+ fontSize: 12,
3665
+ marginBottom: 4,
3666
+ fontFamily: "ui-monospace, SFMono-Regular, monospace"
3667
+ },
3668
+ children: [
3669
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3670
+ step.step,
3671
+ step.status === "failed" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#fca5a5", marginLeft: 6 }, children: "(failed)" })
3672
+ ] }),
3673
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { opacity: 0.75 }, children: [
3674
+ formatMs(duration),
3675
+ llmMs != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { marginLeft: 8, opacity: 0.6 }, children: [
3676
+ "(LLM ",
3677
+ formatMs(llmMs),
3678
+ ")"
3679
+ ] })
3680
+ ] })
3681
+ ]
3682
+ }
3683
+ ),
3684
+ /* @__PURE__ */ jsxRuntime.jsx(
3685
+ "div",
3686
+ {
3687
+ style: {
3688
+ height: 8,
3689
+ background: "rgba(255,255,255,0.06)",
3690
+ borderRadius: 4,
3691
+ overflow: "hidden"
3692
+ },
3693
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3694
+ "div",
3695
+ {
3696
+ style: {
3697
+ height: "100%",
3698
+ width: `${Math.max(widthPct, 0.3)}%`,
3699
+ background: stepColor(step),
3700
+ transition: "width 0.2s ease"
3701
+ }
3702
+ }
3703
+ )
3704
+ }
3705
+ )
3706
+ ] }, `${step.step}-${step.startTime}`);
3707
+ });
3708
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3709
+ bars,
3710
+ unaccountedMs > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 8 }, children: [
3711
+ /* @__PURE__ */ jsxRuntime.jsxs(
3712
+ "div",
3713
+ {
3714
+ style: {
3715
+ display: "flex",
3716
+ justifyContent: "space-between",
3717
+ fontSize: 12,
3718
+ marginBottom: 4,
3719
+ fontFamily: "ui-monospace, SFMono-Regular, monospace",
3720
+ opacity: 0.7
3721
+ },
3722
+ children: [
3723
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "(unaccounted)" }),
3724
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatMs(unaccountedMs) })
3725
+ ]
3726
+ }
3727
+ ),
3728
+ /* @__PURE__ */ jsxRuntime.jsx(
3729
+ "div",
3730
+ {
3731
+ style: {
3732
+ height: 8,
3733
+ background: "rgba(255,255,255,0.06)",
3734
+ borderRadius: 4,
3735
+ overflow: "hidden"
3736
+ },
3737
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3738
+ "div",
3739
+ {
3740
+ style: {
3741
+ height: "100%",
3742
+ width: `${unaccountedMs / Math.max(totalMs, 1) * 100}%`,
3743
+ background: "repeating-linear-gradient(45deg, rgba(255,255,255,0.18) 0 4px, transparent 4px 8px)"
3744
+ }
3745
+ }
3746
+ )
3747
+ }
3748
+ )
3749
+ ] }),
3750
+ /* @__PURE__ */ jsxRuntime.jsxs(
3751
+ "div",
3752
+ {
3753
+ style: {
3754
+ marginTop: 14,
3755
+ paddingTop: 12,
3756
+ borderTop: "1px solid rgba(255,255,255,0.08)",
3757
+ fontSize: 12,
3758
+ display: "flex",
3759
+ justifyContent: "space-between",
3760
+ opacity: 0.85
3761
+ },
3762
+ children: [
3763
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3764
+ trace.pipelineSteps.length,
3765
+ " step",
3766
+ trace.pipelineSteps.length === 1 ? "" : "s"
3767
+ ] }),
3768
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontFamily: "ui-monospace, SFMono-Regular, monospace" }, children: [
3769
+ "total: ",
3770
+ formatMs(totalMs)
3771
+ ] })
3772
+ ]
3773
+ }
3774
+ )
3775
+ ] });
3776
+ }
3254
3777
  var DEFAULT_USER_ACTION_STATE = {
3255
3778
  request: null,
3256
3779
  result: null};
@@ -3441,12 +3964,39 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3441
3964
  ]
3442
3965
  );
3443
3966
  const {
3444
- onExecutionTraceClick,
3967
+ onExecutionTraceClick: rawOnExecutionTraceClick,
3445
3968
  onResetSession,
3446
3969
  onUploadImageClick,
3447
3970
  onAttachFileClick,
3448
3971
  onMessageFeedback
3449
3972
  } = callbacks;
3973
+ const [debugTraceExecutionId, setDebugTraceExecutionId] = react.useState(null);
3974
+ const handleSubmitFeedback = react.useCallback(
3975
+ async ({ messageId, executionId, feedback, details }) => {
3976
+ if (!executionId) {
3977
+ throw new Error("Cannot submit feedback before the response completes");
3978
+ }
3979
+ await submitAskFeedback({
3980
+ baseUrl: config.api.baseUrl,
3981
+ headers: config.api.headers,
3982
+ executionId,
3983
+ feedback,
3984
+ details
3985
+ });
3986
+ onMessageFeedback?.({
3987
+ messageId,
3988
+ feedback: feedback === "POSITIVE" ? "up" : "down"
3989
+ });
3990
+ },
3991
+ [config.api.baseUrl, config.api.headers, onMessageFeedback]
3992
+ );
3993
+ const onExecutionTraceClick = react.useMemo(() => {
3994
+ if (!config.debug) return rawOnExecutionTraceClick;
3995
+ return (data) => {
3996
+ rawOnExecutionTraceClick?.(data);
3997
+ if (data.executionId) setDebugTraceExecutionId(data.executionId);
3998
+ };
3999
+ }, [config.debug, rawOnExecutionTraceClick]);
3450
4000
  const performResetSession = react.useCallback(() => {
3451
4001
  resetToEmptyStateRef.current = true;
3452
4002
  setEditingMessageId(null);
@@ -3504,6 +4054,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3504
4054
  showEmptyStateIcon = true,
3505
4055
  emptyStateComponent,
3506
4056
  showResetSession = false,
4057
+ enableDeepModeToggle = true,
3507
4058
  showAttachmentButton = true,
3508
4059
  showUploadImageButton = true,
3509
4060
  showAttachFileButton = true,
@@ -3559,6 +4110,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3559
4110
  stopRecording();
3560
4111
  };
3561
4112
  const isV2InputDisabled = !isSessionParamsConfigured || disableInput;
4113
+ const effectiveAnalysisMode = enableDeepModeToggle ? analysisMode : "fast";
3562
4114
  const isEmpty = messages.length === 0;
3563
4115
  if (isChatDisabled) {
3564
4116
  if (disabledComponent) {
@@ -3617,7 +4169,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3617
4169
  if (isRecording) stopRecording();
3618
4170
  if (text.trim() && !disableInput && isSessionParamsConfigured) {
3619
4171
  setEditingMessageId(null);
3620
- void sendMessage(text.trim(), { analysisMode });
4172
+ void sendMessage(text.trim(), { analysisMode: effectiveAnalysisMode });
3621
4173
  }
3622
4174
  };
3623
4175
  const handleVoicePress = react.useCallback(async () => {
@@ -3644,7 +4196,9 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3644
4196
  (message) => message.id === messageId && message.role === "user"
3645
4197
  );
3646
4198
  if (!targetMessage?.content.trim()) return;
3647
- void sendMessage(targetMessage.content.trim(), { analysisMode });
4199
+ void sendMessage(targetMessage.content.trim(), {
4200
+ analysisMode: effectiveAnalysisMode
4201
+ });
3648
4202
  const bump = () => messageListV2Ref.current?.scrollToBottom("instant");
3649
4203
  requestAnimationFrame(() => {
3650
4204
  bump();
@@ -3652,7 +4206,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3652
4206
  window.setTimeout(bump, 120);
3653
4207
  });
3654
4208
  });
3655
- }, [isWaitingForResponse, messages, sendMessage, analysisMode]);
4209
+ }, [isWaitingForResponse, messages, sendMessage, effectiveAnalysisMode]);
3656
4210
  const handleClearEditing = react.useCallback(() => {
3657
4211
  setEditingMessageId(null);
3658
4212
  }, []);
@@ -3731,8 +4285,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3731
4285
  onAttachFileClick,
3732
4286
  editingMessageId,
3733
4287
  onClearEditing: handleClearEditing,
3734
- analysisMode,
3735
- onAnalysisModeChange: setAnalysisMode,
4288
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
4289
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
3736
4290
  slashCommands
3737
4291
  }
3738
4292
  )
@@ -3772,7 +4326,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3772
4326
  onResendAction: isUserActionSupported ? async () => {
3773
4327
  await resendOtp();
3774
4328
  } : void 0,
3775
- onMessageFeedback
4329
+ onSubmitFeedback: handleSubmitFeedback
3776
4330
  }
3777
4331
  ),
3778
4332
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3807,8 +4361,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3807
4361
  onAttachFileClick,
3808
4362
  editingMessageId,
3809
4363
  onClearEditing: handleClearEditing,
3810
- analysisMode,
3811
- onAnalysisModeChange: setAnalysisMode,
4364
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
4365
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
3812
4366
  slashCommands
3813
4367
  }
3814
4368
  )
@@ -3831,6 +4385,16 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
3831
4385
  onClose: closeResetSessionConfirm,
3832
4386
  onConfirm: performResetSession
3833
4387
  }
4388
+ ),
4389
+ config.debug && /* @__PURE__ */ jsxRuntime.jsx(
4390
+ TraceTimelineModal,
4391
+ {
4392
+ open: debugTraceExecutionId !== null,
4393
+ onClose: () => setDebugTraceExecutionId(null),
4394
+ executionId: debugTraceExecutionId,
4395
+ apiBaseUrl: config.api.baseUrl,
4396
+ apiHeaders: config.api.headers
4397
+ }
3834
4398
  )
3835
4399
  ]
3836
4400
  }