@paymanai/payman-ask-sdk 4.0.26 → 4.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as React from 'react';
2
2
  import { createContext, forwardRef, useCallback, useRef, useState, useMemo, useEffect, useImperativeHandle, useLayoutEffect, useContext } from 'react';
3
- import { AnimatePresence, motion } from 'framer-motion';
3
+ import { AnimatePresence, motion, useMotionValue, useSpring, useInView } from 'framer-motion';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import * as Sentry from '@sentry/react';
7
- import { ArrowDown, Pencil, X, RotateCcw, Telescope, Zap, Plus, ImagePlus, Paperclip, Mic, ArrowUp, Check, AlertCircle, Copy, WifiOff, ThumbsUp, ThumbsDown, Binoculars, Info, Download, Loader2, ChevronDown, User, Clock, Sparkles, ImageOff, Eye, ChevronRight, ShieldCheck } from 'lucide-react';
7
+ import { ArrowDown, Pencil, X, RotateCcw, Telescope, Zap, ImagePlus, Paperclip, ArrowUp, Check, AlertCircle, Copy, WifiOff, ThumbsUp, ThumbsDown, Binoculars, Info, PanelLeft, Download, Loader2, ChevronDown, MoreHorizontal, User, Clock, Sparkles, ImageOff, Eye, ChevronRight, ShieldCheck } from 'lucide-react';
8
8
  import ReactMarkdown from 'react-markdown';
9
9
  import remarkGfm from 'remark-gfm';
10
10
  import { createPortal } from 'react-dom';
@@ -1128,6 +1128,9 @@ function getUserActionSecondsLeft(prompt) {
1128
1128
  return void 0;
1129
1129
  }
1130
1130
  var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
1131
+ function promptSlotKey(p) {
1132
+ return p.toolCallId || p.userActionId;
1133
+ }
1131
1134
  function upsertPrompt(prompts, req) {
1132
1135
  const idx = prompts.findIndex(
1133
1136
  (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
@@ -1174,6 +1177,7 @@ function useChatV2(config, callbacks = {}) {
1174
1177
  configRef.current = config;
1175
1178
  const messagesRef = useRef(messages);
1176
1179
  messagesRef.current = messages;
1180
+ const pendingSubmissionsRef = useRef(/* @__PURE__ */ new Map());
1177
1181
  const storeAwareSetMessages = useCallback(
1178
1182
  (updater) => {
1179
1183
  const streamUserId = streamUserIdRef.current;
@@ -1208,6 +1212,8 @@ function useChatV2(config, callbacks = {}) {
1208
1212
  if (!config.userId) return EMPTY_USER_ACTION_STATE2;
1209
1213
  return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
1210
1214
  });
1215
+ const userActionStateRef = useRef(userActionState);
1216
+ userActionStateRef.current = userActionState;
1211
1217
  const storeAwareSetUserActionState = useCallback(
1212
1218
  (updater) => {
1213
1219
  const streamUserId = streamUserIdRef.current;
@@ -1235,6 +1241,12 @@ function useChatV2(config, callbacks = {}) {
1235
1241
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1236
1242
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1237
1243
  onUserActionRequired: (request) => {
1244
+ const requestSlotKey = promptSlotKey(request);
1245
+ for (const [pendingId, slotKey] of pendingSubmissionsRef.current) {
1246
+ if (slotKey === requestSlotKey) {
1247
+ pendingSubmissionsRef.current.delete(pendingId);
1248
+ }
1249
+ }
1238
1250
  storeAwareSetUserActionState((prev) => ({
1239
1251
  ...prev,
1240
1252
  prompts: upsertPrompt(prev.prompts, request)
@@ -1246,6 +1258,28 @@ function useChatV2(config, callbacks = {}) {
1246
1258
  (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1247
1259
  );
1248
1260
  callbacksRef.current.onUserNotification?.(notification);
1261
+ },
1262
+ // A submitted-but-unconfirmed prompt (see `submitUserAction`) is only
1263
+ // safe to drop once the stream has demonstrably moved on. `onEvent`
1264
+ // in useStreamManagerV2 calls `onStepsUpdate` for *every* processed
1265
+ // stream event, unconditionally — so the first time this fires
1266
+ // after a submission, the event in hand is either the rejection
1267
+ // retry (a fresh `USER_ACTION_REQUIRED`, which the handler above
1268
+ // already deleted this pending entry for, earlier in the very same
1269
+ // event) or genuine forward progress (RUN_IN_PROGRESS, a tool call,
1270
+ // content deltas, RUN_COMPLETED, ...). Anything still pending by
1271
+ // the time we get here therefore means the run resumed normally —
1272
+ // clear it.
1273
+ onStepsUpdate: (steps) => {
1274
+ if (pendingSubmissionsRef.current.size > 0) {
1275
+ const resolved = [...pendingSubmissionsRef.current.keys()];
1276
+ pendingSubmissionsRef.current.clear();
1277
+ storeAwareSetUserActionState((prev) => ({
1278
+ ...prev,
1279
+ prompts: prev.prompts.filter((p) => !resolved.includes(p.userActionId))
1280
+ }));
1281
+ }
1282
+ callbacksRef.current.onStepsUpdate?.(steps);
1249
1283
  }
1250
1284
  // eslint-disable-next-line react-hooks/exhaustive-deps
1251
1285
  }), []);
@@ -1381,6 +1415,7 @@ function useChatV2(config, callbacks = {}) {
1381
1415
  [storeAwareSetUserActionState]
1382
1416
  );
1383
1417
  const removePrompt = useCallback((userActionId) => {
1418
+ pendingSubmissionsRef.current.delete(userActionId);
1384
1419
  storeAwareSetUserActionState((prev) => ({
1385
1420
  ...prev,
1386
1421
  prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
@@ -1391,7 +1426,10 @@ function useChatV2(config, callbacks = {}) {
1391
1426
  setPromptStatus(userActionId, "submitting");
1392
1427
  try {
1393
1428
  await submitUserAction(configRef.current, userActionId, content);
1394
- removePrompt(userActionId);
1429
+ const prompt = userActionStateRef.current.prompts.find(
1430
+ (p) => p.userActionId === userActionId
1431
+ );
1432
+ pendingSubmissionsRef.current.set(userActionId, prompt ? promptSlotKey(prompt) : userActionId);
1395
1433
  } catch (error) {
1396
1434
  if (error instanceof UserActionStaleError) {
1397
1435
  setPromptStatus(userActionId, "stale");
@@ -1402,7 +1440,7 @@ function useChatV2(config, callbacks = {}) {
1402
1440
  throw error;
1403
1441
  }
1404
1442
  },
1405
- [removePrompt, setPromptStatus]
1443
+ [setPromptStatus]
1406
1444
  );
1407
1445
  const cancelUserAction2 = useCallback(
1408
1446
  async (userActionId) => {
@@ -1756,6 +1794,284 @@ function useVoice(config = {}, callbacks = {}) {
1756
1794
  reset
1757
1795
  };
1758
1796
  }
1797
+ var DEFAULT_PAGE_SIZE = 20;
1798
+ var DEFAULT_MESSAGES_PAGE_SIZE = 100;
1799
+ function defaultParseSessions(json) {
1800
+ const body = json;
1801
+ const rows = Array.isArray(body?.data) ? body.data : [];
1802
+ const data = rows.map((row) => ({ ...row, title: row.title ?? row.sessionTitle ?? null }));
1803
+ return { data, total: body?.total };
1804
+ }
1805
+ function defaultParseMessages(json) {
1806
+ const body = json;
1807
+ return { data: Array.isArray(body?.data) ? body.data : [] };
1808
+ }
1809
+ function withQuery(path, params) {
1810
+ const qs = Object.entries(params).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
1811
+ if (!qs) return path;
1812
+ return `${path}${path.includes("?") ? "&" : "?"}${qs}`;
1813
+ }
1814
+ function toFeedbackState(feedback) {
1815
+ const value = feedback?.feedback;
1816
+ if (!value) return null;
1817
+ return value === "POSITIVE" ? "up" : "down";
1818
+ }
1819
+ function toMessageDisplay(rows, sessionId) {
1820
+ return rows.flatMap((row) => {
1821
+ const timestamp = row.startTime || row.endTime || (/* @__PURE__ */ new Date()).toISOString();
1822
+ const out = [
1823
+ {
1824
+ id: `${row.id}:user`,
1825
+ sessionId,
1826
+ role: "user",
1827
+ content: row.sessionUserIntent,
1828
+ timestamp
1829
+ }
1830
+ ];
1831
+ if (row.agentResponse) {
1832
+ out.push({
1833
+ id: `${row.id}:assistant`,
1834
+ sessionId,
1835
+ role: "assistant",
1836
+ content: row.agentResponse,
1837
+ timestamp: row.endTime || timestamp,
1838
+ executionId: row.id,
1839
+ isError: row.status === "FAILED",
1840
+ feedback: toFeedbackState(row.feedback)
1841
+ });
1842
+ }
1843
+ return out;
1844
+ });
1845
+ }
1846
+ function useChatSessions({
1847
+ api,
1848
+ sessions,
1849
+ initialSessionId
1850
+ }) {
1851
+ const enabled = sessions?.enabled === true;
1852
+ const listEndpoint = sessions?.listEndpoint ?? "/api/conversations/sessions";
1853
+ const messagesEndpointTemplate = sessions?.messagesEndpoint ?? "/api/conversations/sessions/{sessionId}/messages";
1854
+ const pageSize = sessions?.pageSize ?? DEFAULT_PAGE_SIZE;
1855
+ const messagesPageSize = sessions?.messagesPageSize ?? DEFAULT_MESSAGES_PAGE_SIZE;
1856
+ const parseSessionsResponse = sessions?.parseSessionsResponse ?? defaultParseSessions;
1857
+ const parseMessagesResponse = sessions?.parseMessagesResponse ?? defaultParseMessages;
1858
+ const [sessionsList, setSessionsList] = useState([]);
1859
+ const [isLoadingList, setIsLoadingList] = useState(enabled);
1860
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
1861
+ const [listError, setListError] = useState(null);
1862
+ const hasLoadedOnceRef = useRef(false);
1863
+ const [page, setPage] = useState(0);
1864
+ const [total, setTotal] = useState(null);
1865
+ const [lastPageFull, setLastPageFull] = useState(false);
1866
+ const [selectedSessionId, setSelectedSessionId] = useState(
1867
+ initialSessionId ?? null
1868
+ );
1869
+ const [draftSessionId, setDraftSessionId] = useState(generateId);
1870
+ const [selectedMessages, setSelectedMessages] = useState([]);
1871
+ const [isLoadingMessages, setIsLoadingMessages] = useState(
1872
+ enabled && initialSessionId != null
1873
+ );
1874
+ const [messagesError, setMessagesError] = useState(null);
1875
+ const [refetchTick, setRefetchTick] = useState(0);
1876
+ const baseUrl = (api.baseUrl || "").replace(/\/+$/, "");
1877
+ const apiHeaders = api.headers;
1878
+ const authToken = api.authToken;
1879
+ const headersKey = JSON.stringify(apiHeaders ?? {});
1880
+ const requestHeaders = useMemo(() => {
1881
+ const headers = { Accept: "application/json", ...apiHeaders ?? {} };
1882
+ if (authToken && !headers.Authorization) headers.Authorization = `Bearer ${authToken}`;
1883
+ return headers;
1884
+ }, [headersKey, authToken]);
1885
+ const hasMore = total != null ? sessionsList.length < total : lastPageFull;
1886
+ useEffect(() => {
1887
+ setPage(0);
1888
+ hasLoadedOnceRef.current = false;
1889
+ }, [enabled, baseUrl, listEndpoint, pageSize, requestHeaders]);
1890
+ useEffect(() => {
1891
+ setPage(0);
1892
+ }, [refetchTick]);
1893
+ useEffect(() => {
1894
+ if (!enabled) return;
1895
+ const ctrl = new AbortController();
1896
+ const isFirstPage = page === 0;
1897
+ if (isFirstPage && !hasLoadedOnceRef.current) setIsLoadingList(true);
1898
+ else if (!isFirstPage) setIsLoadingMore(true);
1899
+ setListError(null);
1900
+ fetch(withQuery(`${baseUrl}${listEndpoint}`, { page, limit: pageSize }), {
1901
+ method: "GET",
1902
+ headers: requestHeaders,
1903
+ signal: ctrl.signal
1904
+ }).then(async (res) => {
1905
+ if (!res.ok) throw new Error(`Failed to load chats (${res.status})`);
1906
+ return res.json();
1907
+ }).then((json) => {
1908
+ const parsed = parseSessionsResponse(json);
1909
+ setSessionsList((prev) => isFirstPage ? parsed.data : [...prev, ...parsed.data]);
1910
+ setTotal(parsed.total ?? null);
1911
+ setLastPageFull(parsed.data.length >= pageSize);
1912
+ hasLoadedOnceRef.current = true;
1913
+ }).catch((e) => {
1914
+ if (e.name === "AbortError") return;
1915
+ setListError(e instanceof Error ? e.message : "Failed to load chats");
1916
+ }).finally(() => {
1917
+ setIsLoadingList(false);
1918
+ setIsLoadingMore(false);
1919
+ });
1920
+ return () => ctrl.abort();
1921
+ }, [enabled, baseUrl, listEndpoint, pageSize, requestHeaders, refetchTick, page]);
1922
+ useEffect(() => {
1923
+ if (!enabled || !selectedSessionId) {
1924
+ setSelectedMessages([]);
1925
+ return;
1926
+ }
1927
+ const ctrl = new AbortController();
1928
+ setIsLoadingMessages(true);
1929
+ setMessagesError(null);
1930
+ const path = messagesEndpointTemplate.replace(
1931
+ "{sessionId}",
1932
+ encodeURIComponent(selectedSessionId)
1933
+ );
1934
+ fetch(withQuery(`${baseUrl}${path}`, { limit: messagesPageSize }), {
1935
+ method: "GET",
1936
+ headers: requestHeaders,
1937
+ signal: ctrl.signal
1938
+ }).then(async (res) => {
1939
+ if (!res.ok) throw new Error(`Failed to load messages (${res.status})`);
1940
+ return res.json();
1941
+ }).then(
1942
+ (json) => setSelectedMessages(
1943
+ toMessageDisplay(parseMessagesResponse(json).data, selectedSessionId)
1944
+ )
1945
+ ).catch((e) => {
1946
+ if (e.name === "AbortError") return;
1947
+ setMessagesError(e instanceof Error ? e.message : "Failed to load messages");
1948
+ setSelectedMessages([]);
1949
+ }).finally(() => setIsLoadingMessages(false));
1950
+ return () => ctrl.abort();
1951
+ }, [enabled, selectedSessionId, baseUrl, messagesEndpointTemplate, messagesPageSize, requestHeaders]);
1952
+ const loadMore = useCallback(() => {
1953
+ setPage((prev) => {
1954
+ if (isLoadingList || isLoadingMore || !hasMore) return prev;
1955
+ return prev + 1;
1956
+ });
1957
+ }, [isLoadingList, isLoadingMore, hasMore]);
1958
+ const selectSession = useCallback((sessionId) => {
1959
+ setSelectedSessionId(sessionId);
1960
+ setSelectedMessages([]);
1961
+ }, []);
1962
+ const startNewSession = useCallback(() => {
1963
+ setSelectedSessionId(null);
1964
+ setDraftSessionId(generateId());
1965
+ setSelectedMessages([]);
1966
+ }, []);
1967
+ const refetch = useCallback(() => setRefetchTick((tick) => tick + 1), []);
1968
+ const noteSentMessage = useCallback(
1969
+ (text) => {
1970
+ const id = selectedSessionId ?? draftSessionId;
1971
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1972
+ const trimmed = text.trim();
1973
+ setSessionsList((prev) => {
1974
+ const idx = prev.findIndex((s) => s.sessionId === id);
1975
+ if (idx >= 0) {
1976
+ const existing = prev[idx];
1977
+ const bumped = {
1978
+ ...existing,
1979
+ lastMessageAt: now,
1980
+ messageCount: (existing.messageCount ?? 0) + 1,
1981
+ title: existing.title?.trim() ? existing.title : trimmed
1982
+ };
1983
+ return [bumped, ...prev.slice(0, idx), ...prev.slice(idx + 1)];
1984
+ }
1985
+ const fresh = {
1986
+ sessionId: id,
1987
+ title: trimmed,
1988
+ messageCount: 1,
1989
+ lastMessageAt: now,
1990
+ status: "PROCESSING"
1991
+ };
1992
+ return [fresh, ...prev];
1993
+ });
1994
+ },
1995
+ [selectedSessionId, draftSessionId]
1996
+ );
1997
+ return {
1998
+ enabled,
1999
+ sessionsList,
2000
+ isLoadingList,
2001
+ listError,
2002
+ total,
2003
+ hasMore,
2004
+ isLoadingMore,
2005
+ loadMore,
2006
+ noteSentMessage,
2007
+ selectedSessionId,
2008
+ activeSessionId: selectedSessionId ?? draftSessionId,
2009
+ selectSession,
2010
+ startNewSession,
2011
+ selectedMessages,
2012
+ isLoadingMessages,
2013
+ messagesError,
2014
+ refetch
2015
+ };
2016
+ }
2017
+ function deriveStatus(enabled, blocked) {
2018
+ if (blocked === true) return "blocked";
2019
+ if (enabled === false) return "disabled";
2020
+ if (enabled === true) return "available";
2021
+ return "unknown";
2022
+ }
2023
+ function defaultParse(json) {
2024
+ const body = json;
2025
+ return {
2026
+ available: body?.enabled === true && body?.blocked !== true,
2027
+ status: deriveStatus(body?.enabled, body?.blocked),
2028
+ config: body ?? void 0
2029
+ };
2030
+ }
2031
+ function useAgentConfig({ api, availability }) {
2032
+ const enabled = availability?.enabled === true;
2033
+ const endpoint = availability?.endpoint ?? "/api/agent/config";
2034
+ const parseResponse = availability?.parseResponse ?? defaultParse;
2035
+ const [available, setAvailable] = useState(null);
2036
+ const [status, setStatus] = useState("unknown");
2037
+ const [config, setConfig] = useState(void 0);
2038
+ const [isLoading, setIsLoading] = useState(enabled);
2039
+ const [error, setError] = useState(null);
2040
+ const [refetchTick, setRefetchTick] = useState(0);
2041
+ const baseUrl = (api.baseUrl || "").replace(/\/+$/, "");
2042
+ const apiHeaders = api.headers;
2043
+ const authToken = api.authToken;
2044
+ const requestHeaders = useMemo(() => {
2045
+ const headers = { Accept: "application/json", ...apiHeaders ?? {} };
2046
+ if (authToken && !headers.Authorization) headers.Authorization = `Bearer ${authToken}`;
2047
+ return headers;
2048
+ }, [apiHeaders, authToken]);
2049
+ useEffect(() => {
2050
+ if (!enabled) return;
2051
+ const ctrl = new AbortController();
2052
+ setIsLoading(true);
2053
+ setError(null);
2054
+ fetch(`${baseUrl}${endpoint}`, {
2055
+ method: "GET",
2056
+ headers: requestHeaders,
2057
+ signal: ctrl.signal
2058
+ }).then(async (res) => {
2059
+ if (!res.ok) throw new Error(`Failed to check availability (${res.status})`);
2060
+ return res.json();
2061
+ }).then((json) => {
2062
+ const parsed = parseResponse(json);
2063
+ setAvailable(parsed.available);
2064
+ setStatus(parsed.status ?? deriveStatus(parsed.available, void 0));
2065
+ setConfig(parsed.config);
2066
+ }).catch((e) => {
2067
+ if (e.name === "AbortError") return;
2068
+ setError(e instanceof Error ? e.message : "Failed to check availability");
2069
+ }).finally(() => setIsLoading(false));
2070
+ return () => ctrl.abort();
2071
+ }, [enabled, baseUrl, endpoint, requestHeaders, refetchTick]);
2072
+ const refetch = useCallback(() => setRefetchTick((tick) => tick + 1), []);
2073
+ return { enabled, available, status, config, isLoading, error, refetch };
2074
+ }
1759
2075
  function classifyField(field) {
1760
2076
  if (!field) return "text";
1761
2077
  if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
@@ -3212,11 +3528,10 @@ function UserMessageV2({
3212
3528
  ),
3213
3529
  style: { borderRadius: 9999 },
3214
3530
  children: [
3215
- toast.tone === "success" ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5", style: { color: "#059669" } }) : /* @__PURE__ */ jsx(
3531
+ toast.tone === "success" ? /* @__PURE__ */ jsx(Check, { style: { width: 14, height: 14, color: "#059669" } }) : /* @__PURE__ */ jsx(
3216
3532
  AlertCircle,
3217
3533
  {
3218
- className: "h-3.5 w-3.5",
3219
- style: { color: "#ef4444" }
3534
+ style: { width: 14, height: 14, color: "#ef4444" }
3220
3535
  }
3221
3536
  ),
3222
3537
  /* @__PURE__ */ jsx("span", { children: toast.label })
@@ -3240,8 +3555,13 @@ function UserMessageV2({
3240
3555
  /* @__PURE__ */ jsx(
3241
3556
  AlertCircle,
3242
3557
  {
3243
- className: "h-3.5 w-3.5 shrink-0",
3244
- style: { color: "rgba(239, 68, 68, 0.7)", marginTop: "2px" }
3558
+ style: {
3559
+ width: 14,
3560
+ height: 14,
3561
+ flexShrink: 0,
3562
+ color: "rgba(239, 68, 68, 0.7)",
3563
+ marginTop: "2px"
3564
+ }
3245
3565
  }
3246
3566
  ),
3247
3567
  /* @__PURE__ */ jsx("p", { className: "payman-v2-user-msg-error-text", children: resolvedError })
@@ -3259,10 +3579,9 @@ function UserMessageV2({
3259
3579
  children: copied ? /* @__PURE__ */ jsx(
3260
3580
  Check,
3261
3581
  {
3262
- className: "h-3.5 w-3.5",
3263
- style: { color: "#059669" }
3582
+ style: { width: 14, height: 14, color: "#059669" }
3264
3583
  }
3265
- ) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" })
3584
+ ) : /* @__PURE__ */ jsx(Copy, { style: { width: 14, height: 14 } })
3266
3585
  }
3267
3586
  ) }),
3268
3587
  showEditAction && onEdit && /* @__PURE__ */ jsx(ActionTooltipV2, { label: "Edit", children: /* @__PURE__ */ jsx(
@@ -3272,7 +3591,7 @@ function UserMessageV2({
3272
3591
  onClick: () => onEdit(message.id),
3273
3592
  className: "payman-v2-user-msg-action-btn",
3274
3593
  "aria-label": "Edit message",
3275
- children: /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" })
3594
+ children: /* @__PURE__ */ jsx(Pencil, { style: { width: 14, height: 14 } })
3276
3595
  }
3277
3596
  ) }),
3278
3597
  showRetryAction && onRetry && /* @__PURE__ */ jsx(ActionTooltipV2, { label: "Retry", children: /* @__PURE__ */ jsx(
@@ -3283,7 +3602,7 @@ function UserMessageV2({
3283
3602
  disabled: retryDisabled,
3284
3603
  className: "payman-v2-user-msg-action-btn",
3285
3604
  "aria-label": "Retry message",
3286
- children: /* @__PURE__ */ jsx(RotateCcw, { className: "h-3.5 w-3.5" })
3605
+ children: /* @__PURE__ */ jsx(RotateCcw, { style: { width: 14, height: 14 } })
3287
3606
  }
3288
3607
  ) })
3289
3608
  ] })
@@ -4082,6 +4401,8 @@ function AssistantMessageV2({
4082
4401
  void 0,
4083
4402
  typingSpeed
4084
4403
  );
4404
+ const displayContentPending = Boolean(rawResponseContent) && !displayContent;
4405
+ const showThinkingBlock = isThinkingStreaming || displayContentPending;
4085
4406
  const stickyLabel = (() => {
4086
4407
  const steps = message.steps;
4087
4408
  if (!steps || steps.length === 0) return void 0;
@@ -4195,14 +4516,12 @@ function AssistantMessageV2({
4195
4516
  toast.tone === "success" ? /* @__PURE__ */ jsx(
4196
4517
  Check,
4197
4518
  {
4198
- className: "h-3.5 w-3.5",
4199
- style: { color: "#059669" }
4519
+ style: { width: 14, height: 14, color: "#059669" }
4200
4520
  }
4201
4521
  ) : /* @__PURE__ */ jsx(
4202
4522
  AlertCircle,
4203
4523
  {
4204
- className: "h-3.5 w-3.5",
4205
- style: { color: "#ef4444" }
4524
+ style: { width: 14, height: 14, color: "#ef4444" }
4206
4525
  }
4207
4526
  ),
4208
4527
  /* @__PURE__ */ jsx("span", { children: toast.label })
@@ -4216,7 +4535,7 @@ function AssistantMessageV2({
4216
4535
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4217
4536
  toastPortal,
4218
4537
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-assistant-msg payman-v2-fade-in", children: [
4219
- /* @__PURE__ */ jsx(AnimatePresence, { children: isThinkingStreaming && /* @__PURE__ */ jsx(
4538
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showThinkingBlock && /* @__PURE__ */ jsx(
4220
4539
  motion.div,
4221
4540
  {
4222
4541
  initial: { opacity: 0, height: 0 },
@@ -4246,6 +4565,10 @@ function AssistantMessageV2({
4246
4565
  isResolvingImages: message.isResolvingImages,
4247
4566
  onImageClick
4248
4567
  }
4568
+ ) : displayContentPending ? (
4569
+ // The intent/status line above is still bridging this
4570
+ // gap (see `showThinkingBlock`) — nothing to add here.
4571
+ null
4249
4572
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
4250
4573
  /* @__PURE__ */ jsx(AnimatePresence, { children: showTrailingProgress && /* @__PURE__ */ jsx(
4251
4574
  motion.div,
@@ -4366,6 +4689,112 @@ function AssistantMessageV2({
4366
4689
  )
4367
4690
  ] });
4368
4691
  }
4692
+ var CHAR_VARIANTS = {
4693
+ fade: {
4694
+ initial: { opacity: 0, scale: 0.7 },
4695
+ animate: { opacity: 1, scale: 1 },
4696
+ exit: { opacity: 0, scale: 0.7 },
4697
+ transition: { duration: 0.14, ease: "easeOut" },
4698
+ overflow: "hidden"
4699
+ },
4700
+ blur: {
4701
+ initial: { opacity: 0, filter: "blur(8px)", y: -8 },
4702
+ animate: { opacity: 1, filter: "blur(0px)", y: 0 },
4703
+ exit: { opacity: 0, filter: "blur(8px)", y: 8 },
4704
+ transition: { duration: 0.18, ease: "easeOut" },
4705
+ overflow: "visible"
4706
+ }
4707
+ };
4708
+ function CharSlot({
4709
+ char,
4710
+ charKey,
4711
+ effect
4712
+ }) {
4713
+ if (!/\d/.test(char)) return /* @__PURE__ */ jsx("span", { style: { display: "inline-block" }, children: char });
4714
+ const v = CHAR_VARIANTS[effect];
4715
+ return /* @__PURE__ */ jsx("span", { style: { position: "relative", display: "inline-block", overflow: v.overflow }, children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "popLayout", initial: false, children: /* @__PURE__ */ jsx(
4716
+ motion.span,
4717
+ {
4718
+ initial: v.initial,
4719
+ animate: v.animate,
4720
+ exit: v.exit,
4721
+ transition: v.transition,
4722
+ style: { display: "inline-block" },
4723
+ children: char
4724
+ },
4725
+ charKey
4726
+ ) }) });
4727
+ }
4728
+ function CountUp({
4729
+ to,
4730
+ from = 0,
4731
+ duration = 2,
4732
+ delay = 0,
4733
+ digitEffect = "none",
4734
+ className,
4735
+ startWhen = true,
4736
+ separator = ""
4737
+ }) {
4738
+ const ref = useRef(null);
4739
+ const motionValue = useMotionValue(from);
4740
+ const damping = 20 + 40 * (1 / duration);
4741
+ const stiffness = 100 * (1 / duration);
4742
+ const springValue = useSpring(motionValue, { damping, stiffness });
4743
+ const isInView = useInView(ref, { once: true, margin: "0px" });
4744
+ const decimals = useMemo(() => {
4745
+ const d = (n) => {
4746
+ const s = n.toString();
4747
+ return s.includes(".") ? s.split(".")[1].length : 0;
4748
+ };
4749
+ return Math.max(d(from), d(to));
4750
+ }, [from, to]);
4751
+ const formatValue = useCallback(
4752
+ (latest) => {
4753
+ const formatted = Intl.NumberFormat("en-US", {
4754
+ useGrouping: !!separator,
4755
+ minimumFractionDigits: decimals,
4756
+ maximumFractionDigits: decimals
4757
+ }).format(latest);
4758
+ return separator ? formatted.replace(/,/g, separator) : formatted;
4759
+ },
4760
+ [decimals, separator]
4761
+ );
4762
+ const [chars, setChars] = useState(formatValue(from).split(""));
4763
+ useEffect(() => {
4764
+ if (isInView && startWhen) {
4765
+ const t = setTimeout(() => motionValue.set(to), delay * 1e3);
4766
+ return () => clearTimeout(t);
4767
+ }
4768
+ }, [isInView, startWhen, motionValue, to, delay]);
4769
+ useEffect(() => {
4770
+ const unsub = springValue.on("change", (latest) => {
4771
+ if (digitEffect === "none") {
4772
+ if (ref.current) ref.current.textContent = formatValue(latest);
4773
+ } else {
4774
+ setChars(formatValue(latest).split(""));
4775
+ }
4776
+ });
4777
+ return () => unsub();
4778
+ }, [springValue, formatValue, digitEffect]);
4779
+ if (digitEffect === "none") {
4780
+ return /* @__PURE__ */ jsx("span", { ref, className: cn(className), children: formatValue(from) });
4781
+ }
4782
+ return /* @__PURE__ */ jsx("span", { ref, className: cn("inline-flex items-center", className), children: chars.map((char, i) => /* @__PURE__ */ jsx(CharSlot, { char, charKey: `${i}-${char}`, effect: digitEffect }, i)) });
4783
+ }
4784
+ function AnimatedDuration({ seconds, className }) {
4785
+ const total = Math.max(0, seconds);
4786
+ const m = Math.floor(total / 60);
4787
+ const s = total % 60;
4788
+ return /* @__PURE__ */ jsxs("span", { className: cn("tabular-nums", className), children: [
4789
+ m > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
4790
+ /* @__PURE__ */ jsx(CountUp, { to: m, duration: 0.9, digitEffect: "blur", className: "tabular-nums" }),
4791
+ "m",
4792
+ " "
4793
+ ] }),
4794
+ /* @__PURE__ */ jsx(CountUp, { to: s, duration: 0.9, digitEffect: "blur", className: "tabular-nums" }),
4795
+ "s"
4796
+ ] });
4797
+ }
4369
4798
  var DEFAULT_MAX_LENGTH = 6;
4370
4799
  var MAX_SUPPORTED_LENGTH = 12;
4371
4800
  var AUTO_FOCUS_DELAY_MS = 250;
@@ -4492,7 +4921,6 @@ function VerificationInline({
4492
4921
  useEffect(() => {
4493
4922
  if (prompt.subAction === "SubmissionInvalid") {
4494
4923
  setErrored(true);
4495
- setCode("");
4496
4924
  lastSubmittedRef.current = null;
4497
4925
  }
4498
4926
  }, [prompt.subAction, prompt.userActionId]);
@@ -4571,7 +4999,7 @@ function VerificationInline({
4571
4999
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4572
5000
  /* @__PURE__ */ jsx(ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
4573
5001
  /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Verification required" }),
4574
- typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
5002
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : /* @__PURE__ */ jsx(AnimatedDuration, { seconds: secondsLeft }) })
4575
5003
  ] }),
4576
5004
  renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsx(MarkdownRendererV2, { content: description }) }) : /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: description }),
4577
5005
  stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -4643,6 +5071,228 @@ function VerificationInline({
4643
5071
  ] })
4644
5072
  ] });
4645
5073
  }
5074
+ function AmountFieldV2({
5075
+ id,
5076
+ value,
5077
+ onChange,
5078
+ disabled,
5079
+ className,
5080
+ onEnter
5081
+ }) {
5082
+ const inputRef = useRef(null);
5083
+ const digits = digitsFromValue(value);
5084
+ const display = formatDigits(digits);
5085
+ useLayoutEffect(() => {
5086
+ const el = inputRef.current;
5087
+ if (el && document.activeElement === el) {
5088
+ el.setSelectionRange(el.value.length, el.value.length);
5089
+ }
5090
+ }, [display]);
5091
+ return /* @__PURE__ */ jsx(
5092
+ "input",
5093
+ {
5094
+ ref: inputRef,
5095
+ id,
5096
+ type: "text",
5097
+ inputMode: "decimal",
5098
+ autoComplete: "off",
5099
+ className,
5100
+ value: display,
5101
+ placeholder: "0.00",
5102
+ disabled,
5103
+ onChange: (e) => {
5104
+ onChange(valueFromDigits(digitsFromDisplay(e.target.value)));
5105
+ },
5106
+ onKeyDown: (e) => {
5107
+ if (e.key === "Enter") {
5108
+ e.preventDefault();
5109
+ onEnter?.();
5110
+ }
5111
+ },
5112
+ onFocus: (e) => {
5113
+ const el = e.currentTarget;
5114
+ requestAnimationFrame(() => el.setSelectionRange(el.value.length, el.value.length));
5115
+ }
5116
+ }
5117
+ );
5118
+ }
5119
+ function digitsFromDisplay(raw) {
5120
+ return raw.replace(/[^0-9]/g, "").replace(/^0+(?=\d)/, "").slice(-9);
5121
+ }
5122
+ function digitsFromValue(value) {
5123
+ const num = Number(value);
5124
+ if (!value || !Number.isFinite(num) || num <= 0) return "";
5125
+ return String(Math.round(num * 100));
5126
+ }
5127
+ function formatDigits(digits) {
5128
+ if (!digits) return "";
5129
+ const padded = digits.padStart(3, "0");
5130
+ const cents = padded.slice(-2);
5131
+ const whole = padded.slice(0, -2).replace(/^0+(?=\d)/, "") || "0";
5132
+ const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
5133
+ return `${grouped}.${cents}`;
5134
+ }
5135
+ function valueFromDigits(digits) {
5136
+ if (!digits) return "";
5137
+ return (Number(digits) / 100).toString();
5138
+ }
5139
+ function findRootClassName(el) {
5140
+ const root = el?.closest(".payman-v2-root");
5141
+ return root instanceof HTMLElement ? root.className : "";
5142
+ }
5143
+ var MENU_MAX_HEIGHT = 240;
5144
+ var OPTION_HEIGHT = 34;
5145
+ var VIEWPORT_MARGIN = 8;
5146
+ function SelectFieldV2({
5147
+ id,
5148
+ value,
5149
+ options,
5150
+ onChange,
5151
+ disabled,
5152
+ error,
5153
+ placeholder = "Select\u2026"
5154
+ }) {
5155
+ const triggerRef = useRef(null);
5156
+ const listRef = useRef(null);
5157
+ const [open, setOpen] = useState(false);
5158
+ const [rect, setRect] = useState(null);
5159
+ const [activeIndex, setActiveIndex] = useState(-1);
5160
+ const [rootClassName, setRootClassName] = useState("");
5161
+ const selected = options.find((o) => o.const === value);
5162
+ useLayoutEffect(() => {
5163
+ if (!open) return;
5164
+ const place = () => {
5165
+ const el = triggerRef.current;
5166
+ if (!el) return;
5167
+ const r = el.getBoundingClientRect();
5168
+ const viewportH = window.innerHeight;
5169
+ const estimatedMenuH = Math.min(
5170
+ options.length * OPTION_HEIGHT + VIEWPORT_MARGIN,
5171
+ MENU_MAX_HEIGHT
5172
+ );
5173
+ const spaceBelow = viewportH - r.bottom;
5174
+ const spaceAbove = r.top;
5175
+ const openUp = spaceBelow < estimatedMenuH && spaceAbove > spaceBelow;
5176
+ setRect({ top: r.bottom, bottom: viewportH - r.top, left: r.left, width: r.width, openUp });
5177
+ setRootClassName(findRootClassName(el));
5178
+ };
5179
+ place();
5180
+ window.addEventListener("resize", place);
5181
+ window.addEventListener("scroll", place, true);
5182
+ return () => {
5183
+ window.removeEventListener("resize", place);
5184
+ window.removeEventListener("scroll", place, true);
5185
+ };
5186
+ }, [open, options.length]);
5187
+ useEffect(() => {
5188
+ if (!open) return;
5189
+ setActiveIndex(Math.max(0, options.findIndex((o) => o.const === value)));
5190
+ listRef.current?.focus();
5191
+ }, [open]);
5192
+ useEffect(() => {
5193
+ if (!open) return;
5194
+ const onDocDown = (e) => {
5195
+ const target = e.target;
5196
+ if (triggerRef.current?.contains(target) || listRef.current?.contains(target)) return;
5197
+ setOpen(false);
5198
+ };
5199
+ document.addEventListener("mousedown", onDocDown);
5200
+ return () => document.removeEventListener("mousedown", onDocDown);
5201
+ }, [open]);
5202
+ const commit = (opt) => {
5203
+ onChange(opt.const);
5204
+ setOpen(false);
5205
+ triggerRef.current?.focus();
5206
+ };
5207
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
5208
+ /* @__PURE__ */ jsxs(
5209
+ "button",
5210
+ {
5211
+ ref: triggerRef,
5212
+ id,
5213
+ type: "button",
5214
+ disabled,
5215
+ className: cn("payman-v2-ua-select-trigger", error && "payman-v2-ua-input-error"),
5216
+ "aria-haspopup": "listbox",
5217
+ "aria-expanded": open,
5218
+ onClick: () => setOpen((v) => !v),
5219
+ onKeyDown: (e) => {
5220
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
5221
+ e.preventDefault();
5222
+ setOpen(true);
5223
+ }
5224
+ },
5225
+ children: [
5226
+ /* @__PURE__ */ jsx(
5227
+ "span",
5228
+ {
5229
+ className: cn(
5230
+ "payman-v2-ua-select-value",
5231
+ !selected && "payman-v2-ua-select-placeholder"
5232
+ ),
5233
+ children: selected ? selected.title || selected.const : placeholder
5234
+ }
5235
+ ),
5236
+ /* @__PURE__ */ jsx(ChevronDown, { size: 14, className: "payman-v2-ua-select-chevron", "aria-hidden": true })
5237
+ ]
5238
+ }
5239
+ ),
5240
+ open && rect && typeof document !== "undefined" && createPortal(
5241
+ /* @__PURE__ */ jsx(
5242
+ "div",
5243
+ {
5244
+ ref: listRef,
5245
+ role: "listbox",
5246
+ className: cn(rootClassName, "payman-v2-ua-select-menu"),
5247
+ tabIndex: -1,
5248
+ style: {
5249
+ position: "fixed",
5250
+ left: rect.left,
5251
+ width: rect.width,
5252
+ height: "fit-content",
5253
+ maxHeight: MENU_MAX_HEIGHT,
5254
+ ...rect.openUp ? { bottom: rect.bottom } : { top: rect.top }
5255
+ },
5256
+ onKeyDown: (e) => {
5257
+ if (e.key === "ArrowDown") {
5258
+ e.preventDefault();
5259
+ setActiveIndex((i) => Math.min(options.length - 1, i + 1));
5260
+ } else if (e.key === "ArrowUp") {
5261
+ e.preventDefault();
5262
+ setActiveIndex((i) => Math.max(0, i - 1));
5263
+ } else if (e.key === "Enter" || e.key === " ") {
5264
+ e.preventDefault();
5265
+ if (activeIndex >= 0) commit(options[activeIndex]);
5266
+ } else if (e.key === "Escape" || e.key === "Tab") {
5267
+ setOpen(false);
5268
+ triggerRef.current?.focus();
5269
+ }
5270
+ },
5271
+ children: options.map((opt, i) => /* @__PURE__ */ jsxs(
5272
+ "div",
5273
+ {
5274
+ role: "option",
5275
+ "aria-selected": opt.const === value,
5276
+ className: cn(
5277
+ "payman-v2-ua-select-option",
5278
+ i === activeIndex && "payman-v2-ua-select-option-active",
5279
+ opt.const === value && "payman-v2-ua-select-option-selected"
5280
+ ),
5281
+ onMouseEnter: () => setActiveIndex(i),
5282
+ onClick: () => commit(opt),
5283
+ children: [
5284
+ /* @__PURE__ */ jsx("span", { children: opt.title || opt.const }),
5285
+ opt.const === value && /* @__PURE__ */ jsx(Check, { size: 13, "aria-hidden": true })
5286
+ ]
5287
+ },
5288
+ opt.const
5289
+ ))
5290
+ }
5291
+ ),
5292
+ document.body
5293
+ )
5294
+ ] });
5295
+ }
4646
5296
  function SchemaFormInline({
4647
5297
  prompt,
4648
5298
  secondsLeft,
@@ -4688,7 +5338,7 @@ function SchemaFormInline({
4688
5338
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4689
5339
  /* @__PURE__ */ jsx(Pencil, { className: "payman-v2-ua-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4690
5340
  /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4691
- typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
5341
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : /* @__PURE__ */ jsx(AnimatedDuration, { seconds: secondsLeft }) })
4692
5342
  ] }),
4693
5343
  prompt.message?.trim() && (renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsx(MarkdownRendererV2, { content: prompt.message }) }) : /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: prompt.message })),
4694
5344
  stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
@@ -4720,26 +5370,33 @@ function SchemaFormInline({
4720
5370
  required && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-req", children: "*" })
4721
5371
  ] }),
4722
5372
  field.description && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-hint", children: field.description }),
4723
- widget === "select" ? /* @__PURE__ */ jsxs(
4724
- "select",
5373
+ widget === "select" ? /* @__PURE__ */ jsx(
5374
+ SelectFieldV2,
4725
5375
  {
4726
5376
  id: fieldId,
4727
- className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4728
5377
  value: String(values[key] ?? ""),
5378
+ options: getOptions(field),
4729
5379
  disabled: locked,
4730
- onChange: (e) => setValue(key, e.target.value),
4731
- children: [
4732
- /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select\u2026" }),
4733
- getOptions(field).map((opt) => /* @__PURE__ */ jsx("option", { value: opt.const, children: opt.title || opt.const }, opt.const))
4734
- ]
5380
+ error: Boolean(err),
5381
+ onChange: (v) => setValue(key, v)
5382
+ }
5383
+ ) : widget === "decimal" ? /* @__PURE__ */ jsx(
5384
+ AmountFieldV2,
5385
+ {
5386
+ id: fieldId,
5387
+ value: String(values[key] ?? ""),
5388
+ disabled: locked,
5389
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
5390
+ onChange: (v) => setValue(key, v),
5391
+ onEnter: handleSubmit
4735
5392
  }
4736
5393
  ) : /* @__PURE__ */ jsx(
4737
5394
  "input",
4738
5395
  {
4739
5396
  id: fieldId,
4740
- type: widget === "integer" || widget === "decimal" ? "number" : "text",
4741
- inputMode: widget === "integer" ? "numeric" : widget === "decimal" ? "decimal" : void 0,
4742
- step: widget === "decimal" ? "any" : widget === "integer" ? "1" : void 0,
5397
+ type: widget === "integer" ? "number" : "text",
5398
+ inputMode: widget === "integer" ? "numeric" : void 0,
5399
+ step: widget === "integer" ? "1" : void 0,
4743
5400
  className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4744
5401
  value: String(values[key] ?? ""),
4745
5402
  disabled: locked,
@@ -4836,12 +5493,16 @@ function UserActionInline({
4836
5493
  onSubmit,
4837
5494
  onCancel,
4838
5495
  onResend,
4839
- onExpired
5496
+ onExpired,
5497
+ suppressExpiredNote
4840
5498
  }) {
4841
5499
  const [secondsLeft, expired] = useExpiryCountdown(prompt);
4842
5500
  useEffect(() => {
4843
5501
  if (expired && prompt.kind !== "notification") onExpired?.();
4844
5502
  }, [expired, onExpired, prompt.kind]);
5503
+ if (expired && prompt.kind !== "notification" && suppressExpiredNote) {
5504
+ return null;
5505
+ }
4845
5506
  let body;
4846
5507
  if (expired && prompt.kind !== "notification") {
4847
5508
  const note = {
@@ -4902,6 +5563,9 @@ function getPromptViewKey(prompt) {
4902
5563
  prompt.message ?? ""
4903
5564
  ].join(PROMPT_KEY_SEPARATOR);
4904
5565
  }
5566
+ function getPromptMountKey(prompt) {
5567
+ return [getPromptSlotKey(prompt), prompt.userActionId].join(PROMPT_KEY_SEPARATOR);
5568
+ }
4905
5569
  var MessageListV2 = forwardRef(
4906
5570
  function MessageListV22({
4907
5571
  messages,
@@ -4913,6 +5577,7 @@ var MessageListV2 = forwardRef(
4913
5577
  messageActions,
4914
5578
  retryDisabled = false,
4915
5579
  userActionPrompts,
5580
+ dockedUserActionId,
4916
5581
  notifications,
4917
5582
  onSubmitUserAction,
4918
5583
  onCancelUserAction,
@@ -5021,10 +5686,11 @@ var MessageListV2 = forwardRef(
5021
5686
  }, [userActionPrompts]);
5022
5687
  const visibleUserActionPrompts = useMemo(
5023
5688
  () => userActionPrompts?.filter((prompt) => {
5689
+ if (prompt.userActionId === dockedUserActionId) return false;
5024
5690
  const promptKey = getPromptViewKey(prompt);
5025
5691
  return !expiredPromptViewState[promptKey]?.hidden;
5026
5692
  }),
5027
- [expiredPromptViewState, userActionPrompts]
5693
+ [dockedUserActionId, expiredPromptViewState, userActionPrompts]
5028
5694
  );
5029
5695
  const pinToBottom = useCallback((behavior = "instant") => {
5030
5696
  const el = scrollRef.current;
@@ -5173,7 +5839,7 @@ var MessageListV2 = forwardRef(
5173
5839
  onResend: onResendUserAction ?? noop,
5174
5840
  onExpired: () => handleUserActionExpired(promptKey, prompt.userActionId)
5175
5841
  },
5176
- promptKey
5842
+ getPromptMountKey(prompt)
5177
5843
  );
5178
5844
  })
5179
5845
  ]
@@ -5205,6 +5871,62 @@ var MessageListV2 = forwardRef(
5205
5871
  ] });
5206
5872
  }
5207
5873
  );
5874
+ function IconNewChat({ size = 16, className, style }) {
5875
+ return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsx(
5876
+ "path",
5877
+ {
5878
+ d: "M11.9991 14.25V12M11.9991 12V9.75M11.9991 12H9.74936M11.9991 12H14.2489M20.9983 12C20.9983 13.2938 20.7253 14.5238 20.2338 15.6356L21 20.9991L16.4039 19.85C15.1019 20.5823 13.5993 21 11.9991 21C7.02906 21 3 16.9706 3 12C3 7.02944 7.02906 3 11.9991 3C16.9692 3 20.9983 7.02944 20.9983 12Z",
5879
+ stroke: "currentColor",
5880
+ strokeWidth: 2,
5881
+ strokeLinecap: "round",
5882
+ strokeLinejoin: "round"
5883
+ }
5884
+ ) });
5885
+ }
5886
+ function IconPlus({ size = 16, className, style }) {
5887
+ return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsx(
5888
+ "path",
5889
+ {
5890
+ d: "M12.0001 4.7998L12 19.1998M19.2 11.9998L4.80005 11.9998",
5891
+ stroke: "currentColor",
5892
+ strokeWidth: 2,
5893
+ strokeLinecap: "round"
5894
+ }
5895
+ ) });
5896
+ }
5897
+ function IconMic({ size = 18, className, style }) {
5898
+ return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsx(
5899
+ "path",
5900
+ {
5901
+ d: "M2.49292 13.3764C2.82536 15.6606 3.96908 17.7487 5.71484 19.2588C7.4606 20.7688 9.69168 21.5999 11.9999 21.5999C14.3082 21.5999 16.5393 20.7688 18.285 19.2588C20.0308 17.7487 21.1745 15.6606 21.5069 13.3764M12.0013 2.3999C10.9096 2.3999 9.86265 2.83357 9.09072 3.6055C8.31878 4.37744 7.88512 5.4244 7.88512 6.51608V12.0043C7.88512 13.096 8.31878 14.143 9.09072 14.9149C9.86265 15.6868 10.9096 16.1205 12.0013 16.1205C13.093 16.1205 14.1399 15.6868 14.9119 14.9149C15.6838 14.143 16.1175 13.096 16.1175 12.0043V6.51608C16.1175 5.4244 15.6838 4.37744 14.9119 3.6055C14.1399 2.83357 13.093 2.3999 12.0013 2.3999Z",
5902
+ stroke: "currentColor",
5903
+ strokeWidth: 2,
5904
+ strokeLinecap: "round",
5905
+ strokeLinejoin: "round"
5906
+ }
5907
+ ) });
5908
+ }
5909
+ function IconClock({ size = 12, className, style }) {
5910
+ return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 25 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsx(
5911
+ "path",
5912
+ {
5913
+ d: "M15.5588 15.1987C16.0827 15.3733 16.649 15.0902 16.8237 14.5662C16.9983 14.0423 16.7152 13.476 16.1912 13.3013L15.5588 15.1987ZM12.5 13.125H11.5C11.5 13.5554 11.7754 13.9376 12.1838 14.0737L12.5 13.125ZM13.5 8.42087C13.5 7.86858 13.0523 7.42087 12.5 7.42087C11.9477 7.42087 11.5 7.86858 11.5 8.42087H13.5ZM16.1912 13.3013L12.8162 12.1763L12.1838 14.0737L15.5588 15.1987L16.1912 13.3013ZM13.5 13.125V8.42087H11.5V13.125H13.5ZM20.5 12C20.5 16.4183 16.9183 20 12.5 20V22C18.0228 22 22.5 17.5228 22.5 12H20.5ZM12.5 20C8.08172 20 4.5 16.4183 4.5 12H2.5C2.5 17.5228 6.97715 22 12.5 22V20ZM4.5 12C4.5 7.58172 8.08172 4 12.5 4V2C6.97715 2 2.5 6.47715 2.5 12H4.5ZM12.5 4C16.9183 4 20.5 7.58172 20.5 12H22.5C22.5 6.47715 18.0228 2 12.5 2V4Z",
5914
+ fill: "currentColor"
5915
+ }
5916
+ ) });
5917
+ }
5918
+ function IconMessage({ size = 12, className, style }) {
5919
+ return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsx(
5920
+ "path",
5921
+ {
5922
+ d: "M8.39917 8.40002H15.5992M8.39917 13.2H12.5992M21.5992 12C21.5992 13.3801 21.308 14.6921 20.7837 15.878L21.601 21.5991L16.6981 20.3734C15.3091 21.1545 13.7062 21.6 11.9992 21.6C6.69724 21.6 2.39917 17.302 2.39917 12C2.39917 6.69809 6.69724 2.40002 11.9992 2.40002C17.3011 2.40002 21.5992 6.69809 21.5992 12Z",
5923
+ stroke: "currentColor",
5924
+ strokeWidth: 2,
5925
+ strokeLinecap: "round",
5926
+ strokeLinejoin: "round"
5927
+ }
5928
+ ) });
5929
+ }
5208
5930
  var ChatInputV2 = forwardRef(
5209
5931
  function ChatInputV22({
5210
5932
  onSend,
@@ -5595,12 +6317,7 @@ var ChatInputV2 = forwardRef(
5595
6317
  showActions && "payman-v2-input-attach-btn-active"
5596
6318
  ),
5597
6319
  "aria-label": "Attach",
5598
- children: /* @__PURE__ */ jsx(
5599
- Plus,
5600
- {
5601
- style: { width: 20, height: 20, strokeWidth: 2 }
5602
- }
5603
- )
6320
+ children: /* @__PURE__ */ jsx(IconPlus, { size: 20 })
5604
6321
  }
5605
6322
  ),
5606
6323
  /* @__PURE__ */ jsx(AnimatePresence, { children: showActions && /* @__PURE__ */ jsxs(
@@ -5683,7 +6400,7 @@ var ChatInputV2 = forwardRef(
5683
6400
  ),
5684
6401
  "aria-label": isRecording ? "Stop recording" : "Voice input",
5685
6402
  children: [
5686
- /* @__PURE__ */ jsx(Mic, { style: { width: 18, height: 18 } }),
6403
+ /* @__PURE__ */ jsx(IconMic, { size: 18 }),
5687
6404
  isRecording && /* @__PURE__ */ jsx("span", { className: "payman-v2-input-mic-indicator" })
5688
6405
  ]
5689
6406
  }
@@ -5719,66 +6436,577 @@ var ChatInputV2 = forwardRef(
5719
6436
  ] });
5720
6437
  }
5721
6438
  );
5722
-
5723
- // src/assets/thinking-atom.lottie
5724
- var thinking_atom_default = "data:application/zip;base64,UEsDBBQAAAAIAMWrkFzFrMWzUgAAAF4AAAANAAAAbWFuaWZlc3QuanNvbqtWKkstKs7Mz1OyUjJS0lFKT81LLUosyS8C8h1S8kty8ktKMlP14SzdrGIHQz0zPZDaxLzM3MQSoN5iJavoaqXMFKAe38TMPIXgZKApSrWxtQBQSwMEFAAAAAgAxauQXLRghFpXBAAA1RwAABEAAABhL01haW4gU2NlbmUuanNvbu1YWW/jNhD+KwafJVWkqMtPbdEDfdiiQIq+GMZCcei1GvmApLRdGP7v/YYiddqbGNumR4wcomZGHM7B76N0ZLstm7N3Wb6b3a3UTjGHPTw8sLnvsA2bCx/X3811q+qMzY/sAx74stjXda7WeaGqL1alyup9OeNeEnmcnRxWZB9VWbH54sjqj2weOI2bH5+KYibhoirZnONSa0f7AzykGOQYkGP4X2dFpbq13ONx0mTVu6x6bNXZXosf4erI9OLwjwRw7Tv4WWLSP+ALa7I2Rs0RkvkzRhEZQdWz8iHCms48Z54RMEAs2gABkcGRYVFHBqXv8Sh2GBKgRzDNW00SIClag1GzugVNShk5OUd9zxMj4cI/2UhoSdUwVBJpn53EGPMTOd0hh8hAUwuodC1UoVZ1ud+9UjWog+jvb6hIv9D9egxT8aK8kfte5tD5kiTYCfpaqvUPyABb7beH9/57zlUi4zBwE+FnrsyUdDOfC/c+iNVqzVOVKoH06vwLLDsr1Y6q+VZLgX7+dxQDW29SDGmKsVNPr1iLSyB1Bkui0CDGAEdkEmmpeXghQwe/Td0slnRzyDQ5NwdH8L05htXHLGFqIWk0fQ+YPhc9P3+3VpvsoDrSYR9KFNHWpKvWdgfdV998/e3sF2w70Nb35f7pAFNdfn0z49QpNLHDVnqh0FL9IUXU1kG1eZmDO1rYzB06+imrN30/hNG9JmkiPLIVm9flE2ZFuRZoFzf0hIyXzkJfqXcgtLJGCSHyi1SR/URF9pNJYP8b2adeaEwwoqc6AQ2XS+obXTi7bdi6eGmWs8MmXyEN3+UFPaOzQGOdBcQ52BeeDGXqgB3jRNIlirBe7Vs2TYNkXeiGsFtcTS0w3XLPAp8x0MQ86uluEVPse66t9R4ZdbVd88Vw4tNpqTcZIZekEUrFQi/2fAS3hqPIwBMXSIqBp6yqVN1shjHH5NejpvUyOtdZzNyX93lNVfwPn+vGrHjDpiuxSXAvFIHDhSdFRNghvcSPnMQLhKRbo3dbg0bvNgYWsZ4xcz7txuIYrGOfOzz0/JiTUZB4acwdVwiNa43abfVGrbVnMA79fCXG3WGvPdLLlM5nc6czWiBpKFXxq07ptqBOudg7hDI49vRUOAJYuJggZiQDrqFSxA6CMgj2/8PCNqDty6oCjqlViaL8XOb0iC4JjWfU51Xb6CiLunz8CiPop0cnnIb6Ryd7YGpf4bqTUmhPSpSSYXxuSsBtk3ztGkQwPAJS7QevkWdOa2SLNLSn378smfqMTZF0yRzV8Ez4SToNf4i5drmWByfHdk1B5PxGQW+Xgix5jMlhTB5jchlS0CeNOiK74GREQROOGVLQhKFuFHSjoH+CgkSELwM3CrqCgjDJGQoKbhT0pikIH0oE+CAMgOmpF3HAof580grxHaWRdt9opjr9wWUyj6UWHnvSD4yZ1N9pBiIXshuF3Cjktd9izmHo61CIxeSAPpWd/gRQSwECFAAUAAAACADFq5BcxazFs1IAAABeAAAADQAAAAAAAAAAAAAAAAAAAAAAbWFuaWZlc3QuanNvblBLAQIUABQAAAAIAMWrkFy0YIRaVwQAANUcAAARAAAAAAAAAAAAAAAAAH0AAABhL01haW4gU2NlbmUuanNvblBLBQYAAAAAAgACAHoAAAADBQAAAAA=";
5725
- var DEFAULT_SIZE = 36;
5726
- function useElapsedSeconds(isStreaming) {
5727
- const [elapsed, setElapsed] = useState(0);
5728
- const startRef = useRef(null);
5729
- useEffect(() => {
5730
- if (!isStreaming) {
5731
- startRef.current = null;
5732
- setElapsed(0);
5733
- return;
5734
- }
5735
- startRef.current = performance.now();
5736
- setElapsed(0);
5737
- const id = setInterval(() => {
5738
- if (startRef.current == null) return;
5739
- const secs = Math.floor((performance.now() - startRef.current) / 1e3);
5740
- setElapsed(secs);
5741
- }, 100);
5742
- return () => clearInterval(id);
5743
- }, [isStreaming]);
5744
- return elapsed;
6439
+ function dockKey(prompt) {
6440
+ return `${prompt.toolCallId || prompt.userActionId}:${prompt.userActionId}`;
5745
6441
  }
5746
- function StreamingIndicatorV2({
5747
- isStreaming,
5748
- loadingAnimation
6442
+ function UserActionDock({
6443
+ prompt,
6444
+ onSubmit,
6445
+ onCancel,
6446
+ onResend,
6447
+ onExpired,
6448
+ children
5749
6449
  }) {
5750
- const size = loadingAnimation?.size ?? DEFAULT_SIZE;
5751
- const elapsed = useElapsedSeconds(isStreaming);
5752
- return /* @__PURE__ */ jsx(AnimatePresence, { children: isStreaming && /* @__PURE__ */ jsxs(
6450
+ return /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-dock", children: /* @__PURE__ */ jsx(motion.div, { layout: "size", transition: { duration: 0.3, ease: [0.2, 0, 0, 1] }, children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", initial: false, children: prompt ? /* @__PURE__ */ jsx(
5753
6451
  motion.div,
5754
6452
  {
5755
- className: "payman-v2-streaming-indicator",
5756
- initial: { opacity: 0, y: 4 },
5757
- animate: { opacity: 1, y: 0 },
5758
- exit: { opacity: 0, y: 4 },
5759
- transition: { duration: 0.2 },
5760
- "aria-hidden": "true",
5761
- children: [
5762
- /* @__PURE__ */ jsx(
5763
- "div",
5764
- {
5765
- className: "payman-v2-streaming-indicator-glyph",
5766
- style: { width: size, height: size },
5767
- children: /* @__PURE__ */ jsx(
5768
- DotLottieReact,
5769
- {
5770
- src: loadingAnimation?.src ?? thinking_atom_default,
5771
- loop: true,
5772
- autoplay: true,
5773
- style: { width: "100%", height: "100%" }
5774
- }
6453
+ initial: { opacity: 0 },
6454
+ animate: { opacity: 1 },
6455
+ exit: { opacity: 0 },
6456
+ transition: { duration: 0.15 },
6457
+ className: "payman-v2-ua-dock-panel",
6458
+ children: /* @__PURE__ */ jsx(
6459
+ UserActionInline,
6460
+ {
6461
+ prompt,
6462
+ onSubmit,
6463
+ onCancel,
6464
+ onResend,
6465
+ onExpired: () => onExpired?.(prompt.userActionId),
6466
+ suppressExpiredNote: true
6467
+ }
6468
+ )
6469
+ },
6470
+ dockKey(prompt)
6471
+ ) : /* @__PURE__ */ jsx(
6472
+ motion.div,
6473
+ {
6474
+ initial: { opacity: 0 },
6475
+ animate: { opacity: 1 },
6476
+ exit: { opacity: 0 },
6477
+ transition: { duration: 0.15 },
6478
+ children
6479
+ },
6480
+ "composer"
6481
+ ) }) }) });
6482
+ }
6483
+ function clamp(value) {
6484
+ return Math.max(0, Math.min(1, value));
6485
+ }
6486
+ function ensureFrameSize(frame, rows, cols) {
6487
+ const result = [];
6488
+ for (let r = 0; r < rows; r++) {
6489
+ const row = frame[r] || [];
6490
+ result.push([]);
6491
+ for (let c = 0; c < cols; c++) {
6492
+ result[r][c] = row[c] ?? 0;
6493
+ }
6494
+ }
6495
+ return result;
6496
+ }
6497
+ function useAnimation(frames, options) {
6498
+ const [frameIndex, setFrameIndex] = useState(0);
6499
+ const [isPlaying, setIsPlaying] = useState(options.autoplay);
6500
+ const frameIdRef = useRef(void 0);
6501
+ const lastTimeRef = useRef(0);
6502
+ const accumulatorRef = useRef(0);
6503
+ useEffect(() => {
6504
+ if (!frames || frames.length === 0 || !isPlaying) {
6505
+ return;
6506
+ }
6507
+ const frameInterval = 1e3 / options.fps;
6508
+ const animate = (currentTime) => {
6509
+ if (lastTimeRef.current === 0) {
6510
+ lastTimeRef.current = currentTime;
6511
+ }
6512
+ const deltaTime = currentTime - lastTimeRef.current;
6513
+ lastTimeRef.current = currentTime;
6514
+ accumulatorRef.current += deltaTime;
6515
+ if (accumulatorRef.current >= frameInterval) {
6516
+ accumulatorRef.current -= frameInterval;
6517
+ setFrameIndex((prev) => {
6518
+ const next = prev + 1;
6519
+ if (next >= frames.length) {
6520
+ if (options.loop) {
6521
+ options.onFrame?.(0);
6522
+ return 0;
6523
+ } else {
6524
+ setIsPlaying(false);
6525
+ return prev;
6526
+ }
6527
+ }
6528
+ options.onFrame?.(next);
6529
+ return next;
6530
+ });
6531
+ }
6532
+ frameIdRef.current = requestAnimationFrame(animate);
6533
+ };
6534
+ frameIdRef.current = requestAnimationFrame(animate);
6535
+ return () => {
6536
+ if (frameIdRef.current) {
6537
+ cancelAnimationFrame(frameIdRef.current);
6538
+ }
6539
+ };
6540
+ }, [frames, isPlaying, options.fps, options.loop, options.onFrame]);
6541
+ useEffect(() => {
6542
+ setFrameIndex(0);
6543
+ setIsPlaying(options.autoplay);
6544
+ lastTimeRef.current = 0;
6545
+ accumulatorRef.current = 0;
6546
+ }, [frames, options.autoplay]);
6547
+ return { frameIndex, isPlaying };
6548
+ }
6549
+ function emptyFrame(rows, cols) {
6550
+ return Array.from({ length: rows }, () => Array(cols).fill(0));
6551
+ }
6552
+ function setPixel(frame, row, col, value) {
6553
+ if (row >= 0 && row < frame.length && col >= 0 && col < frame[0].length) {
6554
+ frame[row][col] = value;
6555
+ }
6556
+ }
6557
+ var loader = (() => {
6558
+ const frames = [];
6559
+ const size = 7;
6560
+ const center = 3;
6561
+ const radius = 2.5;
6562
+ for (let frame = 0; frame < 12; frame++) {
6563
+ const f = emptyFrame(size, size);
6564
+ for (let i = 0; i < 8; i++) {
6565
+ const angle = frame / 12 * Math.PI * 2 + i / 8 * Math.PI * 2;
6566
+ const x = Math.round(center + Math.cos(angle) * radius);
6567
+ const y = Math.round(center + Math.sin(angle) * radius);
6568
+ const brightness = 1 - i / 10;
6569
+ setPixel(f, y, x, Math.max(0.2, brightness));
6570
+ }
6571
+ frames.push(f);
6572
+ }
6573
+ return frames;
6574
+ })();
6575
+ var pulse = (() => {
6576
+ const frames = [];
6577
+ const size = 7;
6578
+ const center = 3;
6579
+ for (let frame = 0; frame < 16; frame++) {
6580
+ const f = emptyFrame(size, size);
6581
+ const phase = frame / 16 * Math.PI * 2;
6582
+ const intensity = (Math.sin(phase) + 1) / 2;
6583
+ setPixel(f, center, center, 1);
6584
+ const radius = Math.floor((1 - intensity) * 3) + 1;
6585
+ for (let dy = -radius; dy <= radius; dy++) {
6586
+ for (let dx = -radius; dx <= radius; dx++) {
6587
+ const dist = Math.sqrt(dx * dx + dy * dy);
6588
+ if (Math.abs(dist - radius) < 0.7) {
6589
+ setPixel(f, center + dy, center + dx, intensity * 0.6);
6590
+ }
6591
+ }
6592
+ }
6593
+ frames.push(f);
6594
+ }
6595
+ return frames;
6596
+ })();
6597
+ function vu(columns, levels) {
6598
+ const rows = 7;
6599
+ const frame = emptyFrame(rows, columns);
6600
+ for (let col = 0; col < Math.min(columns, levels.length); col++) {
6601
+ const level = Math.max(0, Math.min(1, levels[col]));
6602
+ const height = Math.floor(level * rows);
6603
+ for (let row = 0; row < rows; row++) {
6604
+ const rowFromBottom = rows - 1 - row;
6605
+ if (rowFromBottom < height) {
6606
+ let brightness = 1;
6607
+ if (row < rows * 0.3) {
6608
+ brightness = 1;
6609
+ } else if (row < rows * 0.6) {
6610
+ brightness = 0.8;
6611
+ } else {
6612
+ brightness = 0.6;
6613
+ }
6614
+ frame[row][col] = brightness;
6615
+ }
6616
+ }
6617
+ }
6618
+ return frame;
6619
+ }
6620
+ var wave = (() => {
6621
+ const frames = [];
6622
+ const rows = 7;
6623
+ const cols = 7;
6624
+ for (let frame = 0; frame < 24; frame++) {
6625
+ const f = emptyFrame(rows, cols);
6626
+ const phase = frame / 24 * Math.PI * 2;
6627
+ for (let col = 0; col < cols; col++) {
6628
+ const colPhase = col / cols * Math.PI * 2;
6629
+ const height = Math.sin(phase + colPhase) * 2.5 + 3.5;
6630
+ const row = Math.floor(height);
6631
+ if (row >= 0 && row < rows) {
6632
+ setPixel(f, row, col, 1);
6633
+ const frac = height - row;
6634
+ if (row > 0) setPixel(f, row - 1, col, 1 - frac);
6635
+ if (row < rows - 1) setPixel(f, row + 1, col, frac);
6636
+ }
6637
+ }
6638
+ frames.push(f);
6639
+ }
6640
+ return frames;
6641
+ })();
6642
+ var snake = (() => {
6643
+ const frames = [];
6644
+ const rows = 7;
6645
+ const cols = 7;
6646
+ const path = [];
6647
+ let x = 0;
6648
+ let y = 0;
6649
+ let dx = 1;
6650
+ let dy = 0;
6651
+ const visited = /* @__PURE__ */ new Set();
6652
+ while (path.length < rows * cols) {
6653
+ path.push([y, x]);
6654
+ visited.add(`${y},${x}`);
6655
+ const nextX = x + dx;
6656
+ const nextY = y + dy;
6657
+ if (nextX >= 0 && nextX < cols && nextY >= 0 && nextY < rows && !visited.has(`${nextY},${nextX}`)) {
6658
+ x = nextX;
6659
+ y = nextY;
6660
+ } else {
6661
+ const newDx = -dy;
6662
+ const newDy = dx;
6663
+ dx = newDx;
6664
+ dy = newDy;
6665
+ const nextX2 = x + dx;
6666
+ const nextY2 = y + dy;
6667
+ if (nextX2 >= 0 && nextX2 < cols && nextY2 >= 0 && nextY2 < rows && !visited.has(`${nextY2},${nextX2}`)) {
6668
+ x = nextX2;
6669
+ y = nextY2;
6670
+ } else {
6671
+ break;
6672
+ }
6673
+ }
6674
+ }
6675
+ const snakeLength = 5;
6676
+ for (let frame = 0; frame < path.length; frame++) {
6677
+ const f = emptyFrame(rows, cols);
6678
+ for (let i = 0; i < snakeLength; i++) {
6679
+ const idx = frame - i;
6680
+ if (idx >= 0 && idx < path.length) {
6681
+ const [y2, x2] = path[idx];
6682
+ const brightness = 1 - i / snakeLength;
6683
+ setPixel(f, y2, x2, brightness);
6684
+ }
6685
+ }
6686
+ frames.push(f);
6687
+ }
6688
+ return frames;
6689
+ })();
6690
+ var Matrix = React.forwardRef(
6691
+ ({
6692
+ rows,
6693
+ cols,
6694
+ pattern,
6695
+ frames,
6696
+ fps = 12,
6697
+ autoplay = true,
6698
+ loop = true,
6699
+ size = 10,
6700
+ gap = 2,
6701
+ palette = {
6702
+ on: "currentColor",
6703
+ off: "var(--muted-foreground)"
6704
+ },
6705
+ brightness = 1,
6706
+ ariaLabel,
6707
+ onFrame,
6708
+ mode = "default",
6709
+ levels,
6710
+ className,
6711
+ ...props
6712
+ }, ref) => {
6713
+ const { frameIndex } = useAnimation(frames, {
6714
+ fps,
6715
+ autoplay: autoplay && !pattern,
6716
+ loop,
6717
+ onFrame
6718
+ });
6719
+ const currentFrame = useMemo(() => {
6720
+ if (mode === "vu" && levels && levels.length > 0) {
6721
+ return ensureFrameSize(vu(cols, levels), rows, cols);
6722
+ }
6723
+ if (pattern) {
6724
+ return ensureFrameSize(pattern, rows, cols);
6725
+ }
6726
+ if (frames && frames.length > 0) {
6727
+ return ensureFrameSize(frames[frameIndex] || frames[0], rows, cols);
6728
+ }
6729
+ return ensureFrameSize([], rows, cols);
6730
+ }, [pattern, frames, frameIndex, rows, cols, mode, levels]);
6731
+ const cellPositions = useMemo(() => {
6732
+ const positions = [];
6733
+ for (let row = 0; row < rows; row++) {
6734
+ positions[row] = [];
6735
+ for (let col = 0; col < cols; col++) {
6736
+ positions[row][col] = {
6737
+ x: col * (size + gap),
6738
+ y: row * (size + gap)
6739
+ };
6740
+ }
6741
+ }
6742
+ return positions;
6743
+ }, [rows, cols, size, gap]);
6744
+ const svgDimensions = useMemo(() => {
6745
+ return {
6746
+ width: cols * (size + gap) - gap,
6747
+ height: rows * (size + gap) - gap
6748
+ };
6749
+ }, [rows, cols, size, gap]);
6750
+ const isAnimating = !pattern && frames && frames.length > 0;
6751
+ return /* @__PURE__ */ jsx(
6752
+ "div",
6753
+ {
6754
+ ref,
6755
+ role: "img",
6756
+ "aria-label": ariaLabel ?? "matrix display",
6757
+ "aria-live": isAnimating ? "polite" : void 0,
6758
+ className: cn("relative inline-block", className),
6759
+ style: {
6760
+ "--matrix-on": palette.on,
6761
+ "--matrix-off": palette.off,
6762
+ "--matrix-gap": `${gap}px`,
6763
+ "--matrix-size": `${size}px`
6764
+ },
6765
+ ...props,
6766
+ children: /* @__PURE__ */ jsxs(
6767
+ "svg",
6768
+ {
6769
+ width: svgDimensions.width,
6770
+ height: svgDimensions.height,
6771
+ viewBox: `0 0 ${svgDimensions.width} ${svgDimensions.height}`,
6772
+ xmlns: "http://www.w3.org/2000/svg",
6773
+ className: "block",
6774
+ style: { overflow: "visible" },
6775
+ children: [
6776
+ /* @__PURE__ */ jsxs("defs", { children: [
6777
+ /* @__PURE__ */ jsxs("radialGradient", { id: "matrix-pixel-on", cx: "50%", cy: "50%", r: "50%", children: [
6778
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "var(--matrix-on)", stopOpacity: "1" }),
6779
+ /* @__PURE__ */ jsx(
6780
+ "stop",
6781
+ {
6782
+ offset: "70%",
6783
+ stopColor: "var(--matrix-on)",
6784
+ stopOpacity: "0.85"
6785
+ }
6786
+ ),
6787
+ /* @__PURE__ */ jsx(
6788
+ "stop",
6789
+ {
6790
+ offset: "100%",
6791
+ stopColor: "var(--matrix-on)",
6792
+ stopOpacity: "0.6"
6793
+ }
6794
+ )
6795
+ ] }),
6796
+ /* @__PURE__ */ jsxs("radialGradient", { id: "matrix-pixel-off", cx: "50%", cy: "50%", r: "50%", children: [
6797
+ /* @__PURE__ */ jsx(
6798
+ "stop",
6799
+ {
6800
+ offset: "0%",
6801
+ stopColor: "var(--muted-foreground)",
6802
+ stopOpacity: "1"
6803
+ }
6804
+ ),
6805
+ /* @__PURE__ */ jsx(
6806
+ "stop",
6807
+ {
6808
+ offset: "100%",
6809
+ stopColor: "var(--muted-foreground)",
6810
+ stopOpacity: "0.7"
6811
+ }
6812
+ )
6813
+ ] }),
6814
+ /* @__PURE__ */ jsxs(
6815
+ "filter",
6816
+ {
6817
+ id: "matrix-glow",
6818
+ x: "-50%",
6819
+ y: "-50%",
6820
+ width: "200%",
6821
+ height: "200%",
6822
+ children: [
6823
+ /* @__PURE__ */ jsx("feGaussianBlur", { stdDeviation: "2", result: "blur" }),
6824
+ /* @__PURE__ */ jsx("feComposite", { in: "SourceGraphic", in2: "blur", operator: "over" })
6825
+ ]
6826
+ }
6827
+ )
6828
+ ] }),
6829
+ /* @__PURE__ */ jsx("style", { children: `
6830
+ .matrix-pixel {
6831
+ transition: opacity 300ms ease-out, transform 150ms ease-out;
6832
+ transform-origin: center;
6833
+ transform-box: fill-box;
6834
+ }
6835
+ .matrix-pixel-active {
6836
+ filter: url(#matrix-glow);
6837
+ }
6838
+ ` }),
6839
+ currentFrame.map(
6840
+ (row, rowIndex) => row.map((value, colIndex) => {
6841
+ const pos = cellPositions[rowIndex]?.[colIndex];
6842
+ if (!pos) return null;
6843
+ const opacity = clamp(brightness * value);
6844
+ const isActive = opacity > 0.5;
6845
+ const isOn = opacity > 0.05;
6846
+ const fill = isOn ? "url(#matrix-pixel-on)" : "url(#matrix-pixel-off)";
6847
+ const scale = isActive ? 1.1 : 1;
6848
+ const radius = size / 2 * 0.9;
6849
+ return /* @__PURE__ */ jsx(
6850
+ "circle",
6851
+ {
6852
+ className: cn(
6853
+ "matrix-pixel",
6854
+ isActive && "matrix-pixel-active",
6855
+ !isOn && "opacity-20 dark:opacity-[0.1]"
6856
+ ),
6857
+ cx: pos.x + size / 2,
6858
+ cy: pos.y + size / 2,
6859
+ r: radius,
6860
+ fill,
6861
+ opacity: isOn ? opacity : 0.1,
6862
+ style: {
6863
+ transform: `scale(${scale})`
6864
+ }
6865
+ },
6866
+ `${rowIndex}-${colIndex}`
6867
+ );
6868
+ })
6869
+ )
6870
+ ]
6871
+ }
6872
+ )
6873
+ }
6874
+ );
6875
+ }
6876
+ );
6877
+ Matrix.displayName = "Matrix";
6878
+ var DEFAULT_SIZE = 22;
6879
+ var MATRIX_GRID = 7;
6880
+ var MATRIX_ON = "var(--payman-v2-matrix-color)";
6881
+ var MATRIX_PRESETS = [
6882
+ { frames: loader, fps: 8 },
6883
+ { frames: wave, fps: 14 },
6884
+ { frames: snake, fps: 16 },
6885
+ { frames: pulse, fps: 12 }
6886
+ ];
6887
+ var PRESET_ROTATE_MS = 1e4;
6888
+ function useElapsedSeconds(isStreaming) {
6889
+ const [elapsed, setElapsed] = useState(0);
6890
+ const startRef = useRef(null);
6891
+ useEffect(() => {
6892
+ if (!isStreaming) {
6893
+ startRef.current = null;
6894
+ setElapsed(0);
6895
+ return;
6896
+ }
6897
+ startRef.current = performance.now();
6898
+ setElapsed(0);
6899
+ const id = setInterval(() => {
6900
+ if (startRef.current == null) return;
6901
+ const secs = Math.floor((performance.now() - startRef.current) / 1e3);
6902
+ setElapsed(secs);
6903
+ }, 100);
6904
+ return () => clearInterval(id);
6905
+ }, [isStreaming]);
6906
+ return elapsed;
6907
+ }
6908
+ function useMatrixPresetIndex(isStreaming) {
6909
+ const [index, setIndex] = useState(0);
6910
+ useEffect(() => {
6911
+ if (!isStreaming) return;
6912
+ setIndex(0);
6913
+ const id = setInterval(() => {
6914
+ setIndex((prev) => (prev + 1) % MATRIX_PRESETS.length);
6915
+ }, PRESET_ROTATE_MS);
6916
+ return () => clearInterval(id);
6917
+ }, [isStreaming]);
6918
+ return index;
6919
+ }
6920
+ function formatElapsed(totalSeconds) {
6921
+ if (totalSeconds < 60) return `${totalSeconds}s`;
6922
+ const minutes = Math.floor(totalSeconds / 60);
6923
+ const seconds = totalSeconds % 60;
6924
+ return `${minutes}m ${seconds}s`;
6925
+ }
6926
+ function StreamingIndicatorV2({
6927
+ isStreaming,
6928
+ loadingAnimation
6929
+ }) {
6930
+ const size = loadingAnimation?.size ?? DEFAULT_SIZE;
6931
+ const elapsed = useElapsedSeconds(isStreaming);
6932
+ const presetIndex = useMatrixPresetIndex(isStreaming);
6933
+ const preset = MATRIX_PRESETS[presetIndex];
6934
+ const gap = 1;
6935
+ const cell = useMemo(
6936
+ () => Math.max(2, Math.round((size - (MATRIX_GRID - 1) * gap) / MATRIX_GRID)),
6937
+ [size]
6938
+ );
6939
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: isStreaming && /* @__PURE__ */ jsxs(
6940
+ motion.div,
6941
+ {
6942
+ className: "payman-v2-streaming-indicator",
6943
+ initial: { opacity: 0, y: 4 },
6944
+ animate: { opacity: 1, y: 0 },
6945
+ exit: { opacity: 0, y: 4 },
6946
+ transition: { duration: 0.2 },
6947
+ "aria-hidden": "true",
6948
+ children: [
6949
+ /* @__PURE__ */ jsx(
6950
+ "div",
6951
+ {
6952
+ className: "payman-v2-streaming-indicator-glyph",
6953
+ style: { width: size, height: size },
6954
+ children: loadingAnimation?.src ? (
6955
+ // Consumer override: render their Lottie animation.
6956
+ /* @__PURE__ */ jsx(
6957
+ DotLottieReact,
6958
+ {
6959
+ src: loadingAnimation.src,
6960
+ loop: true,
6961
+ autoplay: true,
6962
+ style: { width: "100%", height: "100%" }
6963
+ }
6964
+ )
6965
+ ) : (
6966
+ // Default: dot-matrix glyph, cycling presets +
6967
+ // Payman brand tint. Each preset lives in its own
6968
+ // keyed motion layer so swapping presets crossfades
6969
+ // (old scales/fades out while the new scales/fades
6970
+ // in) instead of hard-cutting. Keying the layer on
6971
+ // `presetIndex` also remounts the Matrix, restarting
6972
+ // its frame counter cleanly at frame 0.
6973
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: /* @__PURE__ */ jsx(
6974
+ motion.div,
6975
+ {
6976
+ className: "payman-v2-streaming-indicator-matrix",
6977
+ initial: { opacity: 0, scale: 0.8 },
6978
+ animate: { opacity: 1, scale: 1 },
6979
+ exit: { opacity: 0, scale: 0.8 },
6980
+ transition: { duration: 0.45, ease: [0.2, 0, 0, 1] },
6981
+ children: /* @__PURE__ */ jsx(
6982
+ Matrix,
6983
+ {
6984
+ rows: MATRIX_GRID,
6985
+ cols: MATRIX_GRID,
6986
+ frames: preset.frames,
6987
+ fps: preset.fps,
6988
+ size: cell,
6989
+ gap,
6990
+ palette: {
6991
+ on: MATRIX_ON,
6992
+ // Unlit dots use the v2 theme's own muted-text
6993
+ // token (light/dark-aware), not the bare
6994
+ // `--muted-foreground` the vendored Matrix
6995
+ // component falls back to internally — that
6996
+ // variable isn't defined under `.payman-v2-root`
6997
+ // and would otherwise resolve to black.
6998
+ off: "var(--payman-v2-matrix-dot-off)"
6999
+ },
7000
+ ariaLabel: "Working\u2026"
7001
+ }
7002
+ )
7003
+ },
7004
+ presetIndex
7005
+ ) })
5775
7006
  )
5776
7007
  }
5777
7008
  ),
5778
- elapsed > 0 && /* @__PURE__ */ jsxs("span", { className: "payman-v2-streaming-indicator-elapsed", children: [
5779
- elapsed,
5780
- "s"
5781
- ] })
7009
+ elapsed > 0 && /* @__PURE__ */ jsx("span", { className: "payman-v2-streaming-indicator-elapsed", children: formatElapsed(elapsed) })
5782
7010
  ]
5783
7011
  },
5784
7012
  "streaming-indicator"
@@ -6154,82 +7382,660 @@ function TimelineBars({
6154
7382
  children: /* @__PURE__ */ jsx(
6155
7383
  "div",
6156
7384
  {
6157
- style: {
6158
- height: "100%",
6159
- width: `${Math.max(widthPct, 0.3)}%`,
6160
- background: stepColor(step2),
6161
- transition: "width 0.2s ease"
6162
- }
7385
+ style: {
7386
+ height: "100%",
7387
+ width: `${Math.max(widthPct, 0.3)}%`,
7388
+ background: stepColor(step2),
7389
+ transition: "width 0.2s ease"
7390
+ }
7391
+ }
7392
+ )
7393
+ }
7394
+ )
7395
+ ] }, `${step2.step}-${step2.startTime}`);
7396
+ });
7397
+ return /* @__PURE__ */ jsxs("div", { children: [
7398
+ bars,
7399
+ unaccountedMs > 0 && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 8 }, children: [
7400
+ /* @__PURE__ */ jsxs(
7401
+ "div",
7402
+ {
7403
+ style: {
7404
+ display: "flex",
7405
+ justifyContent: "space-between",
7406
+ fontSize: 12,
7407
+ marginBottom: 4,
7408
+ fontFamily: "ui-monospace, SFMono-Regular, monospace",
7409
+ opacity: 0.7
7410
+ },
7411
+ children: [
7412
+ /* @__PURE__ */ jsx("span", { children: "(unaccounted)" }),
7413
+ /* @__PURE__ */ jsx("span", { children: formatMs(unaccountedMs) })
7414
+ ]
7415
+ }
7416
+ ),
7417
+ /* @__PURE__ */ jsx(
7418
+ "div",
7419
+ {
7420
+ style: {
7421
+ height: 8,
7422
+ background: "rgba(255,255,255,0.06)",
7423
+ borderRadius: 4,
7424
+ overflow: "hidden"
7425
+ },
7426
+ children: /* @__PURE__ */ jsx(
7427
+ "div",
7428
+ {
7429
+ style: {
7430
+ height: "100%",
7431
+ width: `${unaccountedMs / Math.max(totalMs, 1) * 100}%`,
7432
+ background: "repeating-linear-gradient(45deg, rgba(255,255,255,0.18) 0 4px, transparent 4px 8px)"
7433
+ }
7434
+ }
7435
+ )
7436
+ }
7437
+ )
7438
+ ] }),
7439
+ /* @__PURE__ */ jsxs(
7440
+ "div",
7441
+ {
7442
+ style: {
7443
+ marginTop: 14,
7444
+ paddingTop: 12,
7445
+ borderTop: "1px solid rgba(255,255,255,0.08)",
7446
+ fontSize: 12,
7447
+ display: "flex",
7448
+ justifyContent: "space-between",
7449
+ opacity: 0.85
7450
+ },
7451
+ children: [
7452
+ /* @__PURE__ */ jsxs("span", { children: [
7453
+ trace.pipelineSteps.length,
7454
+ " step",
7455
+ trace.pipelineSteps.length === 1 ? "" : "s"
7456
+ ] }),
7457
+ /* @__PURE__ */ jsxs("span", { style: { fontFamily: "ui-monospace, SFMono-Regular, monospace" }, children: [
7458
+ "total: ",
7459
+ formatMs(totalMs)
7460
+ ] })
7461
+ ]
7462
+ }
7463
+ )
7464
+ ] });
7465
+ }
7466
+ var MIN_SIDEBAR_WIDTH = 240;
7467
+ var MAX_SIDEBAR_WIDTH = 520;
7468
+ var DEFAULT_SIDEBAR_WIDTH = 264;
7469
+ var WIDTH_STORAGE_KEY = "payman-v2-sessions-width";
7470
+ function readStoredWidth() {
7471
+ if (typeof window === "undefined") return DEFAULT_SIDEBAR_WIDTH;
7472
+ const raw = Number(window.localStorage?.getItem(WIDTH_STORAGE_KEY));
7473
+ if (Number.isFinite(raw) && raw >= MIN_SIDEBAR_WIDTH && raw <= MAX_SIDEBAR_WIDTH) return raw;
7474
+ return DEFAULT_SIDEBAR_WIDTH;
7475
+ }
7476
+ function formatRelativeTime(iso) {
7477
+ if (!iso) return "";
7478
+ const date = new Date(iso);
7479
+ if (Number.isNaN(date.getTime())) return "";
7480
+ const diffSec = Math.round((Date.now() - date.getTime()) / 1e3);
7481
+ if (diffSec < 60) return "just now";
7482
+ const diffMin = Math.round(diffSec / 60);
7483
+ if (diffMin < 60) return `${diffMin} minute${diffMin === 1 ? "" : "s"} ago`;
7484
+ const diffHr = Math.round(diffMin / 60);
7485
+ if (diffHr < 24) return `${diffHr} hour${diffHr === 1 ? "" : "s"} ago`;
7486
+ const diffDay = Math.round(diffHr / 24);
7487
+ if (diffDay < 7) return `${diffDay} day${diffDay === 1 ? "" : "s"} ago`;
7488
+ return date.toLocaleDateString(void 0, { month: "short", day: "numeric" });
7489
+ }
7490
+ function SessionsSidebarV2({
7491
+ open,
7492
+ onClose,
7493
+ title,
7494
+ newChatLabel,
7495
+ sessions,
7496
+ isLoading,
7497
+ error,
7498
+ onRetry,
7499
+ selectedSessionId,
7500
+ onSelectSession,
7501
+ onNewSession,
7502
+ actions,
7503
+ hasMore,
7504
+ isLoadingMore,
7505
+ onLoadMore
7506
+ }) {
7507
+ const [width, setWidth] = useState(readStoredWidth);
7508
+ const [isResizing, setIsResizing] = useState(false);
7509
+ const dragState = useRef(null);
7510
+ const onResizeMove = useCallback((event) => {
7511
+ if (!dragState.current) return;
7512
+ const delta = event.clientX - dragState.current.startX;
7513
+ const next = Math.min(
7514
+ MAX_SIDEBAR_WIDTH,
7515
+ Math.max(MIN_SIDEBAR_WIDTH, dragState.current.startWidth + delta)
7516
+ );
7517
+ setWidth(next);
7518
+ }, []);
7519
+ const onResizeEnd = useCallback(() => {
7520
+ dragState.current = null;
7521
+ setIsResizing(false);
7522
+ document.removeEventListener("mousemove", onResizeMove);
7523
+ document.removeEventListener("mouseup", onResizeEnd);
7524
+ document.body.style.removeProperty("user-select");
7525
+ setWidth((w) => {
7526
+ if (typeof window !== "undefined") window.localStorage?.setItem(WIDTH_STORAGE_KEY, String(w));
7527
+ return w;
7528
+ });
7529
+ }, [onResizeMove]);
7530
+ const onResizeStart = useCallback(
7531
+ (event) => {
7532
+ event.preventDefault();
7533
+ dragState.current = { startX: event.clientX, startWidth: width };
7534
+ setIsResizing(true);
7535
+ document.body.style.userSelect = "none";
7536
+ document.addEventListener("mousemove", onResizeMove);
7537
+ document.addEventListener("mouseup", onResizeEnd);
7538
+ },
7539
+ [width, onResizeMove, onResizeEnd]
7540
+ );
7541
+ useEffect(
7542
+ () => () => {
7543
+ document.removeEventListener("mousemove", onResizeMove);
7544
+ document.removeEventListener("mouseup", onResizeEnd);
7545
+ },
7546
+ [onResizeMove, onResizeEnd]
7547
+ );
7548
+ return /* @__PURE__ */ jsxs(
7549
+ "div",
7550
+ {
7551
+ className: cn(
7552
+ "payman-v2-sessions-sidebar",
7553
+ !open && "payman-v2-sessions-sidebar-collapsed",
7554
+ isResizing && "payman-v2-sessions-sidebar-resizing"
7555
+ ),
7556
+ role: "complementary",
7557
+ "aria-label": title,
7558
+ "aria-hidden": !open,
7559
+ style: { "--payman-v2-sessions-width": `${width}px` },
7560
+ children: [
7561
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-sessions-sidebar-inner", children: [
7562
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-sessions-header", children: [
7563
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-sessions-title", children: title }),
7564
+ onClose && /* @__PURE__ */ jsx(
7565
+ "button",
7566
+ {
7567
+ type: "button",
7568
+ className: "payman-v2-sessions-close",
7569
+ onClick: onClose,
7570
+ "aria-label": "Close chats panel",
7571
+ tabIndex: open ? 0 : -1,
7572
+ children: /* @__PURE__ */ jsx(X, { size: 16 })
7573
+ }
7574
+ )
7575
+ ] }),
7576
+ /* @__PURE__ */ jsxs(
7577
+ "button",
7578
+ {
7579
+ type: "button",
7580
+ className: "payman-v2-sessions-new-btn",
7581
+ onClick: onNewSession,
7582
+ tabIndex: open ? 0 : -1,
7583
+ children: [
7584
+ /* @__PURE__ */ jsx(IconNewChat, { size: 16 }),
7585
+ newChatLabel
7586
+ ]
7587
+ }
7588
+ ),
7589
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-sessions-divider" }),
7590
+ /* @__PURE__ */ jsx(
7591
+ SessionsListBody,
7592
+ {
7593
+ sessions,
7594
+ isLoading,
7595
+ error,
7596
+ onRetry,
7597
+ selectedSessionId,
7598
+ onSelectSession,
7599
+ actions,
7600
+ hasMore,
7601
+ isLoadingMore,
7602
+ onLoadMore
7603
+ }
7604
+ )
7605
+ ] }),
7606
+ open && /* @__PURE__ */ jsx(
7607
+ "div",
7608
+ {
7609
+ className: "payman-v2-sessions-resizer",
7610
+ onMouseDown: onResizeStart,
7611
+ role: "separator",
7612
+ "aria-orientation": "vertical",
7613
+ "aria-label": "Resize chats panel"
7614
+ }
7615
+ )
7616
+ ]
7617
+ }
7618
+ );
7619
+ }
7620
+ function SessionsListBody({
7621
+ sessions,
7622
+ isLoading,
7623
+ error,
7624
+ onRetry,
7625
+ selectedSessionId,
7626
+ onSelectSession,
7627
+ actions,
7628
+ compact = false,
7629
+ hasMore,
7630
+ isLoadingMore,
7631
+ onLoadMore
7632
+ }) {
7633
+ const [openMenuId, setOpenMenuId] = useState(null);
7634
+ const listRef = useRef(null);
7635
+ const [shadow, setShadow] = useState({ top: false, bottom: false });
7636
+ const updateShadow = useCallback(() => {
7637
+ const el = listRef.current;
7638
+ if (!el) return;
7639
+ const top = el.scrollTop > 1;
7640
+ const bottom = Math.ceil(el.scrollTop + el.clientHeight) < el.scrollHeight - 1;
7641
+ setShadow((s) => s.top === top && s.bottom === bottom ? s : { top, bottom });
7642
+ }, []);
7643
+ useEffect(() => {
7644
+ updateShadow();
7645
+ const el = listRef.current;
7646
+ if (!el) return;
7647
+ const ro = new ResizeObserver(updateShadow);
7648
+ ro.observe(el);
7649
+ return () => ro.disconnect();
7650
+ }, [sessions, isLoading, updateShadow]);
7651
+ return /* @__PURE__ */ jsx(
7652
+ "div",
7653
+ {
7654
+ className: cn(
7655
+ "payman-v2-sessions-list-wrap",
7656
+ shadow.top && "payman-v2-scroll-shadow-top",
7657
+ shadow.bottom && "payman-v2-scroll-shadow-bottom"
7658
+ ),
7659
+ children: /* @__PURE__ */ jsx(
7660
+ "div",
7661
+ {
7662
+ ref: listRef,
7663
+ onScroll: updateShadow,
7664
+ className: cn("payman-v2-sessions-list", compact && "payman-v2-sessions-list-compact"),
7665
+ children: isLoading ? /* @__PURE__ */ jsx(SessionsListSkeleton, {}) : error ? /* @__PURE__ */ jsxs("div", { className: "payman-v2-sessions-error", children: [
7666
+ /* @__PURE__ */ jsx(AlertCircle, { size: 18 }),
7667
+ /* @__PURE__ */ jsx("p", { children: error }),
7668
+ onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "payman-v2-sessions-retry-btn", onClick: onRetry, children: "Try again" })
7669
+ ] }) : sessions.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "payman-v2-sessions-empty", children: [
7670
+ /* @__PURE__ */ jsx(IconMessage, { size: 20 }),
7671
+ /* @__PURE__ */ jsx("p", { children: "No chats yet" })
7672
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7673
+ sessions.map((session) => /* @__PURE__ */ jsx(
7674
+ SessionRow,
7675
+ {
7676
+ session,
7677
+ isSelected: session.sessionId === selectedSessionId,
7678
+ isMenuOpen: openMenuId === session.sessionId,
7679
+ onSelect: () => onSelectSession(session.sessionId),
7680
+ onToggleMenu: () => setOpenMenuId((id) => id === session.sessionId ? null : session.sessionId),
7681
+ onCloseMenu: () => setOpenMenuId(null),
7682
+ actions
7683
+ },
7684
+ session.sessionId
7685
+ )),
7686
+ hasMore && onLoadMore && /* @__PURE__ */ jsx(
7687
+ "button",
7688
+ {
7689
+ type: "button",
7690
+ className: "payman-v2-sessions-load-more-btn",
7691
+ onClick: onLoadMore,
7692
+ disabled: isLoadingMore,
7693
+ children: isLoadingMore ? /* @__PURE__ */ jsx("span", { className: "payman-v2-spinner payman-v2-spinner-sm" }) : "Load more"
7694
+ }
7695
+ )
7696
+ ] })
7697
+ }
7698
+ )
7699
+ }
7700
+ );
7701
+ }
7702
+ function SessionsListSkeleton() {
7703
+ return /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "payman-v2-sessions-skeleton", children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsxs("div", { className: "payman-v2-sessions-skeleton-row", children: [
7704
+ /* @__PURE__ */ jsx(
7705
+ "div",
7706
+ {
7707
+ className: "payman-v2-sessions-skeleton-line payman-v2-sessions-skeleton-line-title",
7708
+ style: { animationDelay: `${i * 90}ms`, width: `${60 + i * 13 % 30}%` }
7709
+ }
7710
+ ),
7711
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-sessions-skeleton-meta", children: [
7712
+ /* @__PURE__ */ jsx(
7713
+ "div",
7714
+ {
7715
+ className: "payman-v2-sessions-skeleton-line payman-v2-sessions-skeleton-pill",
7716
+ style: { animationDelay: `${i * 90 + 60}ms` }
7717
+ }
7718
+ ),
7719
+ /* @__PURE__ */ jsx(
7720
+ "div",
7721
+ {
7722
+ className: "payman-v2-sessions-skeleton-line payman-v2-sessions-skeleton-pill",
7723
+ style: { animationDelay: `${i * 90 + 120}ms` }
7724
+ }
7725
+ )
7726
+ ] })
7727
+ ] }, i)) });
7728
+ }
7729
+ function SessionRow({
7730
+ session,
7731
+ isSelected,
7732
+ isMenuOpen,
7733
+ onSelect,
7734
+ onToggleMenu,
7735
+ onCloseMenu,
7736
+ actions
7737
+ }) {
7738
+ const menuRef = useRef(null);
7739
+ const clipRef = useRef(null);
7740
+ const textRef = useRef(null);
7741
+ const [overflowPx, setOverflowPx] = useState(0);
7742
+ const [scrolling, setScrolling] = useState(false);
7743
+ useEffect(() => {
7744
+ if (!isMenuOpen) return;
7745
+ const handlePointerDown = (event) => {
7746
+ if (menuRef.current && !menuRef.current.contains(event.target)) {
7747
+ onCloseMenu();
7748
+ }
7749
+ };
7750
+ document.addEventListener("mousedown", handlePointerDown);
7751
+ return () => document.removeEventListener("mousedown", handlePointerDown);
7752
+ }, [isMenuOpen, onCloseMenu]);
7753
+ useEffect(() => {
7754
+ const clip = clipRef.current;
7755
+ const text = textRef.current;
7756
+ if (!clip || !text) return;
7757
+ const measure = () => setOverflowPx(Math.max(0, text.scrollWidth - clip.clientWidth));
7758
+ measure();
7759
+ const ro = new ResizeObserver(measure);
7760
+ ro.observe(clip);
7761
+ return () => ro.disconnect();
7762
+ }, [session.title]);
7763
+ const truncated = overflowPx > 2;
7764
+ const messageCount = session.messageCount ?? 0;
7765
+ const title = session.title?.trim() || "New chat";
7766
+ const relativeTime = formatRelativeTime(session.lastMessageAt);
7767
+ return /* @__PURE__ */ jsxs(
7768
+ "div",
7769
+ {
7770
+ className: cn(
7771
+ "payman-v2-session-row",
7772
+ isSelected && "payman-v2-session-row-selected"
7773
+ ),
7774
+ children: [
7775
+ /* @__PURE__ */ jsxs(
7776
+ "button",
7777
+ {
7778
+ type: "button",
7779
+ className: "payman-v2-session-row-main",
7780
+ onClick: onSelect,
7781
+ title,
7782
+ onMouseEnter: () => truncated && setScrolling(true),
7783
+ onMouseLeave: () => setScrolling(false),
7784
+ children: [
7785
+ /* @__PURE__ */ jsx(
7786
+ "span",
7787
+ {
7788
+ ref: clipRef,
7789
+ className: "payman-v2-session-row-title",
7790
+ "data-truncated": truncated ? "true" : void 0,
7791
+ "data-scrolling": scrolling ? "true" : void 0,
7792
+ children: /* @__PURE__ */ jsx(
7793
+ "span",
7794
+ {
7795
+ ref: textRef,
7796
+ className: "payman-v2-session-row-title-text",
7797
+ style: scrolling ? {
7798
+ // +16 so the last chars clear the right-edge fade mask.
7799
+ transform: `translateX(-${overflowPx + 16}px)`,
7800
+ // Constant reading pace (~38px/s), so it moves at the same
7801
+ // speed from start to end rather than easing in/out.
7802
+ transitionDuration: `${Math.max(0.9, (overflowPx + 16) / 38)}s`
7803
+ } : void 0,
7804
+ children: title
7805
+ }
7806
+ )
7807
+ }
7808
+ ),
7809
+ /* @__PURE__ */ jsxs("span", { className: "payman-v2-session-row-meta", children: [
7810
+ /* @__PURE__ */ jsxs("span", { className: "payman-v2-session-row-meta-item", children: [
7811
+ /* @__PURE__ */ jsx(IconMessage, { size: 12 }),
7812
+ messageCount,
7813
+ " ",
7814
+ messageCount === 1 ? "message" : "messages"
7815
+ ] }),
7816
+ relativeTime && /* @__PURE__ */ jsxs("span", { className: "payman-v2-session-row-meta-item", children: [
7817
+ /* @__PURE__ */ jsx(IconClock, { size: 12 }),
7818
+ relativeTime
7819
+ ] })
7820
+ ] })
7821
+ ]
7822
+ }
7823
+ ),
7824
+ actions && actions.length > 0 && /* @__PURE__ */ jsxs("div", { className: "payman-v2-session-row-menu-wrap", ref: menuRef, children: [
7825
+ /* @__PURE__ */ jsx(
7826
+ "button",
7827
+ {
7828
+ type: "button",
7829
+ className: cn(
7830
+ "payman-v2-session-row-menu-btn",
7831
+ isMenuOpen && "payman-v2-session-row-menu-btn-active"
7832
+ ),
7833
+ onClick: onToggleMenu,
7834
+ "aria-label": "Chat options",
7835
+ "aria-expanded": isMenuOpen,
7836
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { size: 14 })
7837
+ }
7838
+ ),
7839
+ isMenuOpen && /* @__PURE__ */ jsx("div", { className: "payman-v2-session-row-menu", role: "menu", children: actions.map((action) => /* @__PURE__ */ jsx(
7840
+ "button",
7841
+ {
7842
+ type: "button",
7843
+ role: "menuitem",
7844
+ className: cn(
7845
+ "payman-v2-session-row-menu-item",
7846
+ action.destructive && "payman-v2-session-row-menu-item-destructive"
7847
+ ),
7848
+ onClick: () => {
7849
+ onCloseMenu();
7850
+ void action.onSelect(session);
7851
+ },
7852
+ children: action.label
7853
+ },
7854
+ action.key
7855
+ )) })
7856
+ ] })
7857
+ ]
7858
+ }
7859
+ );
7860
+ }
7861
+ var CLOSE_DELAY_MS = 150;
7862
+ var HOVER_OPEN_DELAY_MS = 500;
7863
+ function SessionsPeekPopoverV2({
7864
+ title,
7865
+ newChatLabel,
7866
+ sessions,
7867
+ isLoading,
7868
+ error,
7869
+ onRetry,
7870
+ selectedSessionId,
7871
+ onSelectSession,
7872
+ onNewSession,
7873
+ onExpand,
7874
+ actions
7875
+ }) {
7876
+ const [open, setOpen] = useState(false);
7877
+ const closeTimerRef = useRef(null);
7878
+ const openTimerRef = useRef(null);
7879
+ const cancelClose = useCallback(() => {
7880
+ if (closeTimerRef.current) {
7881
+ clearTimeout(closeTimerRef.current);
7882
+ closeTimerRef.current = null;
7883
+ }
7884
+ }, []);
7885
+ const cancelOpen = useCallback(() => {
7886
+ if (openTimerRef.current) {
7887
+ clearTimeout(openTimerRef.current);
7888
+ openTimerRef.current = null;
7889
+ }
7890
+ }, []);
7891
+ const scheduleClose = useCallback(() => {
7892
+ cancelOpen();
7893
+ cancelClose();
7894
+ closeTimerRef.current = setTimeout(() => setOpen(false), CLOSE_DELAY_MS);
7895
+ }, [cancelOpen, cancelClose]);
7896
+ const handleEnter = useCallback(() => {
7897
+ cancelClose();
7898
+ cancelOpen();
7899
+ openTimerRef.current = setTimeout(() => setOpen(true), HOVER_OPEN_DELAY_MS);
7900
+ }, [cancelClose, cancelOpen]);
7901
+ useEffect(
7902
+ () => () => {
7903
+ cancelOpen();
7904
+ cancelClose();
7905
+ },
7906
+ [cancelOpen, cancelClose]
7907
+ );
7908
+ return /* @__PURE__ */ jsxs(
7909
+ "div",
7910
+ {
7911
+ className: "payman-v2-sessions-peek-anchor",
7912
+ onMouseEnter: handleEnter,
7913
+ onMouseLeave: scheduleClose,
7914
+ children: [
7915
+ /* @__PURE__ */ jsx(
7916
+ "button",
7917
+ {
7918
+ type: "button",
7919
+ className: "payman-v2-sessions-reopen-btn",
7920
+ onClick: onExpand,
7921
+ "aria-label": "Show chats",
7922
+ title: "Show chats",
7923
+ children: /* @__PURE__ */ jsx(PanelLeft, { size: 16 })
7924
+ }
7925
+ ),
7926
+ open && /* @__PURE__ */ jsxs("div", { className: "payman-v2-sessions-peek", role: "dialog", "aria-label": title, children: [
7927
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-sessions-peek-header", children: /* @__PURE__ */ jsx("span", { className: "payman-v2-sessions-peek-title", children: title }) }),
7928
+ /* @__PURE__ */ jsxs(
7929
+ "button",
7930
+ {
7931
+ type: "button",
7932
+ className: "payman-v2-sessions-new-btn payman-v2-sessions-peek-new-btn",
7933
+ onClick: () => {
7934
+ onNewSession();
7935
+ setOpen(false);
7936
+ },
7937
+ children: [
7938
+ /* @__PURE__ */ jsx(IconNewChat, { size: 14 }),
7939
+ newChatLabel
7940
+ ]
6163
7941
  }
6164
- )
6165
- }
6166
- )
6167
- ] }, `${step2.step}-${step2.startTime}`);
6168
- });
6169
- return /* @__PURE__ */ jsxs("div", { children: [
6170
- bars,
6171
- unaccountedMs > 0 && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 8 }, children: [
6172
- /* @__PURE__ */ jsxs(
6173
- "div",
6174
- {
6175
- style: {
6176
- display: "flex",
6177
- justifyContent: "space-between",
6178
- fontSize: 12,
6179
- marginBottom: 4,
6180
- fontFamily: "ui-monospace, SFMono-Regular, monospace",
6181
- opacity: 0.7
6182
- },
6183
- children: [
6184
- /* @__PURE__ */ jsx("span", { children: "(unaccounted)" }),
6185
- /* @__PURE__ */ jsx("span", { children: formatMs(unaccountedMs) })
6186
- ]
6187
- }
6188
- ),
6189
- /* @__PURE__ */ jsx(
6190
- "div",
6191
- {
6192
- style: {
6193
- height: 8,
6194
- background: "rgba(255,255,255,0.06)",
6195
- borderRadius: 4,
6196
- overflow: "hidden"
6197
- },
6198
- children: /* @__PURE__ */ jsx(
6199
- "div",
7942
+ ),
7943
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-sessions-divider" }),
7944
+ /* @__PURE__ */ jsx(
7945
+ SessionsListBody,
6200
7946
  {
6201
- style: {
6202
- height: "100%",
6203
- width: `${unaccountedMs / Math.max(totalMs, 1) * 100}%`,
6204
- background: "repeating-linear-gradient(45deg, rgba(255,255,255,0.18) 0 4px, transparent 4px 8px)"
6205
- }
7947
+ sessions,
7948
+ isLoading,
7949
+ error,
7950
+ onRetry,
7951
+ selectedSessionId,
7952
+ onSelectSession: (sessionId) => {
7953
+ onSelectSession(sessionId);
7954
+ setOpen(false);
7955
+ },
7956
+ actions,
7957
+ compact: true
6206
7958
  }
6207
7959
  )
6208
- }
6209
- )
6210
- ] }),
7960
+ ] })
7961
+ ]
7962
+ }
7963
+ );
7964
+ }
7965
+ function SessionsBottomSheetV2({
7966
+ title,
7967
+ newChatLabel,
7968
+ sessions,
7969
+ isLoading,
7970
+ error,
7971
+ onRetry,
7972
+ selectedSessionId,
7973
+ onSelectSession,
7974
+ onNewSession,
7975
+ onClose,
7976
+ actions,
7977
+ hasMore,
7978
+ isLoadingMore,
7979
+ onLoadMore
7980
+ }) {
7981
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
7982
+ /* @__PURE__ */ jsx(
7983
+ motion.div,
7984
+ {
7985
+ className: "payman-v2-sessions-sheet-backdrop",
7986
+ initial: { opacity: 0 },
7987
+ animate: { opacity: 1 },
7988
+ exit: { opacity: 0 },
7989
+ transition: { duration: 0.2 },
7990
+ onClick: onClose
7991
+ }
7992
+ ),
6211
7993
  /* @__PURE__ */ jsxs(
6212
- "div",
7994
+ motion.div,
6213
7995
  {
6214
- style: {
6215
- marginTop: 14,
6216
- paddingTop: 12,
6217
- borderTop: "1px solid rgba(255,255,255,0.08)",
6218
- fontSize: 12,
6219
- display: "flex",
6220
- justifyContent: "space-between",
6221
- opacity: 0.85
6222
- },
7996
+ className: "payman-v2-sessions-sheet",
7997
+ role: "dialog",
7998
+ "aria-modal": "true",
7999
+ "aria-label": title,
8000
+ initial: { y: "100%" },
8001
+ animate: { y: 0 },
8002
+ exit: { y: "100%" },
8003
+ transition: { type: "spring", damping: 32, stiffness: 320 },
6223
8004
  children: [
6224
- /* @__PURE__ */ jsxs("span", { children: [
6225
- trace.pipelineSteps.length,
6226
- " step",
6227
- trace.pipelineSteps.length === 1 ? "" : "s"
8005
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-sessions-sheet-grabber", "aria-hidden": true }),
8006
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-sessions-header", children: [
8007
+ /* @__PURE__ */ jsx("span", { className: "payman-v2-sessions-title", children: title }),
8008
+ /* @__PURE__ */ jsx(
8009
+ "button",
8010
+ {
8011
+ type: "button",
8012
+ className: "payman-v2-sessions-close",
8013
+ onClick: onClose,
8014
+ "aria-label": "Close chats panel",
8015
+ children: /* @__PURE__ */ jsx(X, { size: 16 })
8016
+ }
8017
+ )
6228
8018
  ] }),
6229
- /* @__PURE__ */ jsxs("span", { style: { fontFamily: "ui-monospace, SFMono-Regular, monospace" }, children: [
6230
- "total: ",
6231
- formatMs(totalMs)
6232
- ] })
8019
+ /* @__PURE__ */ jsxs("button", { type: "button", className: "payman-v2-sessions-new-btn", onClick: onNewSession, children: [
8020
+ /* @__PURE__ */ jsx(IconNewChat, { size: 16 }),
8021
+ newChatLabel
8022
+ ] }),
8023
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-sessions-divider" }),
8024
+ /* @__PURE__ */ jsx(
8025
+ SessionsListBody,
8026
+ {
8027
+ sessions,
8028
+ isLoading,
8029
+ error,
8030
+ onRetry,
8031
+ selectedSessionId,
8032
+ onSelectSession,
8033
+ actions,
8034
+ hasMore,
8035
+ isLoadingMore,
8036
+ onLoadMore
8037
+ }
8038
+ )
6233
8039
  ]
6234
8040
  }
6235
8041
  )
@@ -6329,7 +8135,8 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6329
8135
  onLoadMoreMessages,
6330
8136
  isLoadingMoreMessages = false,
6331
8137
  hasMoreMessages = false,
6332
- chat
8138
+ chat,
8139
+ sessionsPanel
6333
8140
  }, ref) {
6334
8141
  const [inputValue, setInputValue] = useState("");
6335
8142
  const prevInputValueRef = useRef(inputValue);
@@ -6342,6 +8149,25 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6342
8149
  const chatInputV2Ref = useRef(null);
6343
8150
  const messageListV2Ref = useRef(null);
6344
8151
  const resetToEmptyStateRef = useRef(false);
8152
+ const rootRef = useRef(null);
8153
+ const [isCompact, setIsCompact] = useState(false);
8154
+ useEffect(() => {
8155
+ const el = rootRef.current;
8156
+ if (!el) return;
8157
+ const ro = new ResizeObserver((entries) => {
8158
+ const width = entries[0]?.contentRect.width;
8159
+ if (width != null) setIsCompact(width < COMPACT_BREAKPOINT_PX);
8160
+ });
8161
+ ro.observe(el);
8162
+ return () => ro.disconnect();
8163
+ }, []);
8164
+ const wasCompactRef = useRef(isCompact);
8165
+ useEffect(() => {
8166
+ if (isCompact && !wasCompactRef.current && sessionsPanel?.open) {
8167
+ sessionsPanel.onToggle();
8168
+ }
8169
+ wasCompactRef.current = isCompact;
8170
+ }, [isCompact, sessionsPanel]);
6345
8171
  useEffect(() => {
6346
8172
  if (config.sentryDsn) {
6347
8173
  initSentryIfNeeded(config.sentryDsn);
@@ -6386,6 +8212,15 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6386
8212
  const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
6387
8213
  const dismissNotification = chat.dismissNotification ?? NOOP;
6388
8214
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
8215
+ const expiredUserActionIdsRef = useRef(/* @__PURE__ */ new Set());
8216
+ const expireUserActionOnce = useCallback(
8217
+ (userActionId) => {
8218
+ if (expiredUserActionIdsRef.current.has(userActionId)) return;
8219
+ expiredUserActionIdsRef.current.add(userActionId);
8220
+ void expireUserAction2(userActionId);
8221
+ },
8222
+ [expireUserAction2]
8223
+ );
6389
8224
  const {
6390
8225
  transcribedText,
6391
8226
  isAvailable: voiceAvailable,
@@ -6487,13 +8322,20 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6487
8322
  if (isRecording) {
6488
8323
  stopRecording();
6489
8324
  }
6490
- resetSession();
8325
+ if (sessionsPanel?.onNewSession) {
8326
+ sessionsPanel.onNewSession();
8327
+ if (isCompact && sessionsPanel.open) sessionsPanel.onToggle();
8328
+ } else {
8329
+ resetSession();
8330
+ }
6491
8331
  onResetSession?.();
6492
8332
  }, [
6493
8333
  clearTranscript,
6494
8334
  isRecording,
8335
+ isCompact,
6495
8336
  onResetSession,
6496
8337
  resetSession,
8338
+ sessionsPanel,
6497
8339
  stopRecording
6498
8340
  ]);
6499
8341
  const requestResetSession = useCallback(() => {
@@ -6507,8 +8349,9 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6507
8349
  clearMessages,
6508
8350
  cancelStream,
6509
8351
  getSessionId,
6510
- getMessages
6511
- }), [performResetSession, clearMessages, cancelStream, getSessionId, getMessages]);
8352
+ getMessages,
8353
+ refreshSessions: sessionsPanel?.refetch ?? NOOP
8354
+ }), [performResetSession, clearMessages, cancelStream, getSessionId, getMessages, sessionsPanel?.refetch]);
6512
8355
  const {
6513
8356
  placeholder = "Type your message...",
6514
8357
  emptyStateText = "What can I help with?",
@@ -6628,6 +8471,13 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6628
8471
  };
6629
8472
  const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
6630
8473
  const notifications = userActionState.notifications;
8474
+ const dockableUserActionPrompts = userActionPrompts?.filter(
8475
+ (prompt) => prompt.kind !== "notification" && (prompt.status === "pending" || prompt.status === "submitting" || prompt.status === "stale")
8476
+ );
8477
+ const dockedPendingPrompts = dockableUserActionPrompts?.filter(
8478
+ (prompt) => prompt.status === "pending"
8479
+ );
8480
+ const dockedPrompt = dockedPendingPrompts?.[dockedPendingPrompts.length - 1] ?? dockableUserActionPrompts?.[dockableUserActionPrompts.length - 1];
6631
8481
  const handleV2Send = (text) => {
6632
8482
  if (isRecording) stopRecording();
6633
8483
  if (text.trim() && !disableInput && isSessionParamsConfigured) {
@@ -6676,160 +8526,236 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6676
8526
  return /* @__PURE__ */ jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(
6677
8527
  "div",
6678
8528
  {
8529
+ ref: rootRef,
6679
8530
  className: cn("payman-v2-root", className),
6680
8531
  style,
6681
8532
  children: [
6682
8533
  children,
6683
- /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsx(
6684
- motion.div,
6685
- {
6686
- initial: { opacity: 1 },
6687
- exit: { opacity: 0 },
6688
- transition: { duration: 0.3 },
6689
- className: "payman-v2-chat-layout",
6690
- style: { justifyContent: "center", alignItems: "center" },
6691
- children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
6692
- /* @__PURE__ */ jsx(
6693
- MessageList,
6694
- {
6695
- messages,
6696
- isLoading: false,
6697
- emptyStateText,
6698
- showEmptyStateIcon,
6699
- emptyStateComponent,
6700
- layout,
6701
- showTimestamps,
6702
- stage: config.stage || "DEVELOPMENT",
6703
- animated,
6704
- showAgentName,
6705
- agentName,
6706
- showAvatars,
6707
- showUserAvatar,
6708
- showAssistantAvatar,
6709
- showExecutionSteps,
6710
- showStreamingDot,
6711
- streamingStepsText,
6712
- completedStepsText,
6713
- onExecutionTraceClick,
6714
- onLoadMoreMessages,
6715
- isLoadingMoreMessages,
6716
- hasMoreMessages
6717
- }
6718
- ),
6719
- /* @__PURE__ */ jsx(
6720
- motion.div,
6721
- {
6722
- initial: { opacity: 0, y: 12 },
6723
- animate: { opacity: 1, y: 0 },
6724
- transition: { delay: 0.2, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
6725
- style: { width: "100%" },
6726
- children: hasAskPermission && /* @__PURE__ */ jsx(
6727
- ChatInputV2,
8534
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-body", children: [
8535
+ sessionsPanel && !isCompact && /* @__PURE__ */ jsx(
8536
+ SessionsSidebarV2,
8537
+ {
8538
+ open: sessionsPanel.open,
8539
+ onClose: sessionsPanel.onToggle,
8540
+ title: sessionsPanel.title,
8541
+ newChatLabel: sessionsPanel.newChatLabel,
8542
+ sessions: sessionsPanel.sessionsList,
8543
+ isLoading: sessionsPanel.isLoadingList,
8544
+ error: sessionsPanel.listError,
8545
+ onRetry: sessionsPanel.refetch,
8546
+ selectedSessionId: sessionsPanel.activeSessionId,
8547
+ onSelectSession: sessionsPanel.onSelectSession,
8548
+ onNewSession: requestResetSession,
8549
+ actions: sessionsPanel.actions,
8550
+ hasMore: sessionsPanel.hasMore,
8551
+ isLoadingMore: sessionsPanel.isLoadingMore,
8552
+ onLoadMore: sessionsPanel.onLoadMore
8553
+ }
8554
+ ),
8555
+ /* @__PURE__ */ jsx(AnimatePresence, { children: sessionsPanel && isCompact && sessionsPanel.open && /* @__PURE__ */ jsx(
8556
+ SessionsBottomSheetV2,
8557
+ {
8558
+ title: sessionsPanel.title,
8559
+ newChatLabel: sessionsPanel.newChatLabel,
8560
+ sessions: sessionsPanel.sessionsList,
8561
+ isLoading: sessionsPanel.isLoadingList,
8562
+ error: sessionsPanel.listError,
8563
+ onRetry: sessionsPanel.refetch,
8564
+ selectedSessionId: sessionsPanel.activeSessionId,
8565
+ onSelectSession: (sessionId) => {
8566
+ sessionsPanel.onSelectSession(sessionId);
8567
+ sessionsPanel.onToggle();
8568
+ },
8569
+ onNewSession: requestResetSession,
8570
+ onClose: sessionsPanel.onToggle,
8571
+ actions: sessionsPanel.actions,
8572
+ hasMore: sessionsPanel.hasMore,
8573
+ isLoadingMore: sessionsPanel.isLoadingMore,
8574
+ onLoadMore: sessionsPanel.onLoadMore
8575
+ },
8576
+ "sessions-bottom-sheet"
8577
+ ) }),
8578
+ /* @__PURE__ */ jsxs("div", { className: "payman-v2-chat-column", children: [
8579
+ sessionsPanel?.isLoadingMessages && /* @__PURE__ */ jsx("div", { className: "payman-v2-messages-loading-overlay", children: /* @__PURE__ */ jsx("span", { className: "payman-v2-spinner" }) }),
8580
+ sessionsPanel && !sessionsPanel.open && /* @__PURE__ */ jsx(
8581
+ SessionsPeekPopoverV2,
8582
+ {
8583
+ title: sessionsPanel.title,
8584
+ newChatLabel: sessionsPanel.newChatLabel,
8585
+ sessions: sessionsPanel.sessionsList,
8586
+ isLoading: sessionsPanel.isLoadingList,
8587
+ error: sessionsPanel.listError,
8588
+ onRetry: sessionsPanel.refetch,
8589
+ selectedSessionId: sessionsPanel.activeSessionId,
8590
+ onSelectSession: sessionsPanel.onSelectSession,
8591
+ onNewSession: requestResetSession,
8592
+ onExpand: sessionsPanel.onToggle,
8593
+ actions: sessionsPanel.actions
8594
+ }
8595
+ ),
8596
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsx(
8597
+ motion.div,
8598
+ {
8599
+ initial: { opacity: 1 },
8600
+ exit: { opacity: 0 },
8601
+ transition: { duration: 0.3 },
8602
+ className: "payman-v2-chat-layout",
8603
+ style: { justifyContent: "center", alignItems: "center" },
8604
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
8605
+ /* @__PURE__ */ jsx(
8606
+ MessageList,
8607
+ {
8608
+ messages,
8609
+ isLoading: false,
8610
+ emptyStateText,
8611
+ showEmptyStateIcon,
8612
+ emptyStateComponent,
8613
+ layout,
8614
+ showTimestamps,
8615
+ stage: config.stage || "DEVELOPMENT",
8616
+ animated,
8617
+ showAgentName,
8618
+ agentName,
8619
+ showAvatars,
8620
+ showUserAvatar,
8621
+ showAssistantAvatar,
8622
+ showExecutionSteps,
8623
+ showStreamingDot,
8624
+ streamingStepsText,
8625
+ completedStepsText,
8626
+ onExecutionTraceClick,
8627
+ onLoadMoreMessages,
8628
+ isLoadingMoreMessages,
8629
+ hasMoreMessages
8630
+ }
8631
+ ),
8632
+ /* @__PURE__ */ jsx(
8633
+ motion.div,
8634
+ {
8635
+ initial: { opacity: 0 },
8636
+ animate: { opacity: 1 },
8637
+ transition: { duration: 0.18 },
8638
+ style: { width: "100%" },
8639
+ children: hasAskPermission && /* @__PURE__ */ jsx(
8640
+ ChatInputV2,
8641
+ {
8642
+ ref: chatInputV2Ref,
8643
+ onSend: handleV2Send,
8644
+ onCancel: cancelStream,
8645
+ disabled: isV2InputDisabled,
8646
+ isStreaming: isWaitingForResponse,
8647
+ placeholder: isRecording ? "Listening..." : placeholder,
8648
+ enableVoice: config.enableVoice === true,
8649
+ transcribedText: config.enableVoice === true ? transcribedText : "",
8650
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
8651
+ isRecording,
8652
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
8653
+ onCancelRecording: handleCancelRecording,
8654
+ onConfirmRecording: handleConfirmRecording,
8655
+ showResetSession,
8656
+ onResetSession: requestResetSession,
8657
+ showAttachmentButton,
8658
+ showUploadImageButton,
8659
+ showAttachFileButton,
8660
+ onUploadImageClick,
8661
+ onAttachFileClick,
8662
+ editingMessageId,
8663
+ onClearEditing: handleClearEditing,
8664
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
8665
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
8666
+ slashCommands
8667
+ }
8668
+ )
8669
+ }
8670
+ )
8671
+ ] })
8672
+ },
8673
+ "v2-empty"
8674
+ ) : /* @__PURE__ */ jsxs(
8675
+ motion.div,
8676
+ {
8677
+ initial: { opacity: 0 },
8678
+ animate: { opacity: 1 },
8679
+ transition: { duration: 0.25 },
8680
+ className: "payman-v2-chat-layout",
8681
+ children: [
8682
+ /* @__PURE__ */ jsx(
8683
+ MessageListV2,
8684
+ {
8685
+ ref: messageListV2Ref,
8686
+ messages,
8687
+ isStreaming: isWaitingForResponse,
8688
+ onEditUserMessage: handleEditMessageDraft,
8689
+ onRetryUserMessage: handleRetryUserMessage,
8690
+ onImageClick: handleImageClick,
8691
+ onExecutionTraceClick,
8692
+ messageActions,
8693
+ retryDisabled: isWaitingForResponse,
8694
+ typingSpeed: config.typingSpeed ?? 4,
8695
+ userActionPrompts,
8696
+ dockedUserActionId: dockedPrompt?.userActionId,
8697
+ notifications,
8698
+ onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
8699
+ onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
8700
+ onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
8701
+ onExpireUserAction: isUserActionSupported ? async (id) => expireUserActionOnce(id) : void 0,
8702
+ onDismissNotification: dismissNotification,
8703
+ onSubmitFeedback: handleSubmitFeedback
8704
+ }
8705
+ ),
8706
+ /* @__PURE__ */ jsx(
8707
+ StreamingIndicatorV2,
6728
8708
  {
6729
- ref: chatInputV2Ref,
6730
- onSend: handleV2Send,
6731
- onCancel: cancelStream,
6732
- disabled: isV2InputDisabled,
6733
8709
  isStreaming: isWaitingForResponse,
6734
- placeholder: isRecording ? "Listening..." : placeholder,
6735
- enableVoice: config.enableVoice === true,
6736
- transcribedText: config.enableVoice === true ? transcribedText : "",
6737
- voiceAvailable: config.enableVoice === true && voiceAvailable,
6738
- isRecording,
6739
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6740
- onCancelRecording: handleCancelRecording,
6741
- onConfirmRecording: handleConfirmRecording,
6742
- showResetSession,
6743
- onResetSession: requestResetSession,
6744
- showAttachmentButton,
6745
- showUploadImageButton,
6746
- showAttachFileButton,
6747
- onUploadImageClick,
6748
- onAttachFileClick,
6749
- editingMessageId,
6750
- onClearEditing: handleClearEditing,
6751
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6752
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6753
- slashCommands
8710
+ loadingAnimation: config.loadingAnimation
8711
+ }
8712
+ ),
8713
+ hasAskPermission && /* @__PURE__ */ jsx(
8714
+ UserActionDock,
8715
+ {
8716
+ prompt: isUserActionSupported ? dockedPrompt : void 0,
8717
+ onSubmit: submitUserAction2,
8718
+ onCancel: cancelUserAction2,
8719
+ onResend: resendUserAction2,
8720
+ onExpired: expireUserActionOnce,
8721
+ children: /* @__PURE__ */ jsx(
8722
+ ChatInputV2,
8723
+ {
8724
+ ref: chatInputV2Ref,
8725
+ onSend: handleV2Send,
8726
+ onCancel: cancelStream,
8727
+ disabled: isV2InputDisabled,
8728
+ isStreaming: isWaitingForResponse,
8729
+ placeholder: isRecording ? "Listening..." : placeholder,
8730
+ enableVoice: config.enableVoice === true,
8731
+ transcribedText: config.enableVoice === true ? transcribedText : "",
8732
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
8733
+ isRecording,
8734
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
8735
+ onCancelRecording: handleCancelRecording,
8736
+ onConfirmRecording: handleConfirmRecording,
8737
+ showResetSession,
8738
+ onResetSession: requestResetSession,
8739
+ showAttachmentButton,
8740
+ showUploadImageButton,
8741
+ showAttachFileButton,
8742
+ onUploadImageClick,
8743
+ onAttachFileClick,
8744
+ editingMessageId,
8745
+ onClearEditing: handleClearEditing,
8746
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
8747
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
8748
+ slashCommands
8749
+ }
8750
+ )
6754
8751
  }
6755
8752
  )
6756
- }
6757
- )
6758
- ] })
6759
- },
6760
- "v2-empty"
6761
- ) : /* @__PURE__ */ jsxs(
6762
- motion.div,
6763
- {
6764
- initial: hasEverSentMessage ? { opacity: 0 } : false,
6765
- animate: { opacity: 1 },
6766
- transition: { duration: 0.3 },
6767
- className: "payman-v2-chat-layout",
6768
- children: [
6769
- /* @__PURE__ */ jsx(
6770
- MessageListV2,
6771
- {
6772
- ref: messageListV2Ref,
6773
- messages,
6774
- isStreaming: isWaitingForResponse,
6775
- onEditUserMessage: handleEditMessageDraft,
6776
- onRetryUserMessage: handleRetryUserMessage,
6777
- onImageClick: handleImageClick,
6778
- onExecutionTraceClick,
6779
- messageActions,
6780
- retryDisabled: isWaitingForResponse,
6781
- typingSpeed: config.typingSpeed ?? 4,
6782
- userActionPrompts,
6783
- notifications,
6784
- onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6785
- onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6786
- onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6787
- onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
6788
- onDismissNotification: dismissNotification,
6789
- onSubmitFeedback: handleSubmitFeedback
6790
- }
6791
- ),
6792
- /* @__PURE__ */ jsx(
6793
- StreamingIndicatorV2,
6794
- {
6795
- isStreaming: isWaitingForResponse,
6796
- loadingAnimation: config.loadingAnimation
6797
- }
6798
- ),
6799
- hasAskPermission && /* @__PURE__ */ jsx(
6800
- ChatInputV2,
6801
- {
6802
- ref: chatInputV2Ref,
6803
- onSend: handleV2Send,
6804
- onCancel: cancelStream,
6805
- disabled: isV2InputDisabled,
6806
- isStreaming: isWaitingForResponse,
6807
- placeholder: isRecording ? "Listening..." : placeholder,
6808
- enableVoice: config.enableVoice === true,
6809
- transcribedText: config.enableVoice === true ? transcribedText : "",
6810
- voiceAvailable: config.enableVoice === true && voiceAvailable,
6811
- isRecording,
6812
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6813
- onCancelRecording: handleCancelRecording,
6814
- onConfirmRecording: handleConfirmRecording,
6815
- showResetSession,
6816
- onResetSession: requestResetSession,
6817
- showAttachmentButton,
6818
- showUploadImageButton,
6819
- showAttachFileButton,
6820
- onUploadImageClick,
6821
- onAttachFileClick,
6822
- editingMessageId,
6823
- onClearEditing: handleClearEditing,
6824
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6825
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6826
- slashCommands
6827
- }
6828
- )
6829
- ]
6830
- },
6831
- "v2-chat"
6832
- ) }),
8753
+ ]
8754
+ },
8755
+ "v2-chat"
8756
+ ) })
8757
+ ] })
8758
+ ] }),
6833
8759
  /* @__PURE__ */ jsx(
6834
8760
  ImageLightboxV2,
6835
8761
  {
@@ -6860,14 +8786,163 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6860
8786
  }
6861
8787
  ) });
6862
8788
  });
6863
- var PaymanChat = forwardRef(
6864
- function PaymanChat2(props, ref) {
8789
+ var PaymanChatWithChat = forwardRef(
8790
+ function PaymanChatWithChat2(props, ref) {
6865
8791
  const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
6866
8792
  const chat = useChatV2(props.config, mergedCallbacks);
6867
8793
  return /* @__PURE__ */ jsx(PaymanChatInner, { ...props, chat, ref });
6868
8794
  }
6869
8795
  );
8796
+ function AvailabilityNotice({ message }) {
8797
+ return /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center p-4", children: /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx("div", { className: "text-muted-foreground text-sm", children: message }) }) });
8798
+ }
8799
+ var DEFAULT_AGENT_DISABLED_MESSAGE = "Chat is currently unavailable. Please try again later.";
8800
+ var DEFAULT_USER_BLOCKED_MESSAGE = "Chat isn't available for your account right now.";
8801
+ var COMPACT_BREAKPOINT_PX = 640;
8802
+ var PaymanChat = forwardRef(
8803
+ function PaymanChat2(props, ref) {
8804
+ const { config } = props;
8805
+ const sessionsConfig = config.sessions;
8806
+ const availabilityData = useAgentConfig({
8807
+ api: config.api,
8808
+ availability: config.availability
8809
+ });
8810
+ const agentUnavailable = availabilityData.enabled && availabilityData.available === false;
8811
+ const availabilityStatus = availabilityData.status;
8812
+ const availabilityGateOpen = !availabilityData.enabled || availabilityData.available === true || availabilityData.available === null && !availabilityData.isLoading;
8813
+ const gatedSessionsConfig = useMemo(
8814
+ () => sessionsConfig ? {
8815
+ ...sessionsConfig,
8816
+ enabled: sessionsConfig.enabled === true && availabilityGateOpen
8817
+ } : sessionsConfig,
8818
+ [sessionsConfig, availabilityGateOpen]
8819
+ );
8820
+ const sessionsData = useChatSessions({
8821
+ api: config.api,
8822
+ sessions: gatedSessionsConfig,
8823
+ initialSessionId: config.initialSessionId
8824
+ });
8825
+ const [sidebarOpen, setSidebarOpen] = useState(
8826
+ sessionsConfig?.defaultOpen !== false
8827
+ );
8828
+ const toggleSidebar = useCallback(() => setSidebarOpen((open) => !open), []);
8829
+ const effectiveConfig = useMemo(() => {
8830
+ if (!sessionsData.enabled) return config;
8831
+ return {
8832
+ ...config,
8833
+ initialSessionId: sessionsData.activeSessionId,
8834
+ initialMessages: sessionsData.selectedMessages,
8835
+ autoGenerateSessionId: false,
8836
+ // Scope the message store per active session. The base userId alone
8837
+ // is shared across every session, so its chatStore entry would leak
8838
+ // the previous chat's messages into a newly-selected or brand-new
8839
+ // session (getStoredOrInitialMessages prefers the store over
8840
+ // initialMessages). Per-session keys give each chat a clean slate.
8841
+ userId: config.userId ? `${config.userId}::${sessionsData.activeSessionId}` : void 0
8842
+ };
8843
+ }, [
8844
+ config,
8845
+ sessionsData.enabled,
8846
+ sessionsData.activeSessionId,
8847
+ sessionsData.selectedMessages
8848
+ ]);
8849
+ const sessionsPanel = useMemo(() => {
8850
+ if (!sessionsData.enabled) return void 0;
8851
+ return {
8852
+ open: sidebarOpen,
8853
+ onToggle: toggleSidebar,
8854
+ title: sessionsConfig?.title ?? "Chats",
8855
+ newChatLabel: sessionsConfig?.newChatLabel ?? "Start New Chat",
8856
+ sessionsList: sessionsData.sessionsList,
8857
+ isLoadingList: sessionsData.isLoadingList,
8858
+ listError: sessionsData.listError,
8859
+ activeSessionId: sessionsData.activeSessionId,
8860
+ onSelectSession: sessionsData.selectSession,
8861
+ onNewSession: sessionsData.startNewSession,
8862
+ actions: sessionsConfig?.actions,
8863
+ refetch: sessionsData.refetch,
8864
+ hasMore: sessionsData.hasMore,
8865
+ isLoadingMore: sessionsData.isLoadingMore,
8866
+ onLoadMore: sessionsData.loadMore,
8867
+ isLoadingMessages: sessionsData.isLoadingMessages
8868
+ };
8869
+ }, [
8870
+ sessionsData.enabled,
8871
+ sessionsData.sessionsList,
8872
+ sessionsData.isLoadingList,
8873
+ sessionsData.listError,
8874
+ sessionsData.activeSessionId,
8875
+ sessionsData.selectSession,
8876
+ sessionsData.startNewSession,
8877
+ sessionsData.refetch,
8878
+ sessionsData.hasMore,
8879
+ sessionsData.isLoadingMore,
8880
+ sessionsData.loadMore,
8881
+ sessionsData.isLoadingMessages,
8882
+ sidebarOpen,
8883
+ toggleSidebar,
8884
+ sessionsConfig?.title,
8885
+ sessionsConfig?.newChatLabel,
8886
+ sessionsConfig?.actions
8887
+ ]);
8888
+ const callbacksWithSessionsRefresh = useMemo(() => {
8889
+ if (!sessionsData.enabled) return props.callbacks;
8890
+ return {
8891
+ ...props.callbacks,
8892
+ // Reflect the message in the sidebar the instant it's sent, before
8893
+ // the server round-trip — the refetch below reconciles on complete.
8894
+ onMessageSent: (message) => {
8895
+ props.callbacks?.onMessageSent?.(message);
8896
+ sessionsData.noteSentMessage(message);
8897
+ },
8898
+ onStreamComplete: (message) => {
8899
+ props.callbacks?.onStreamComplete?.(message);
8900
+ sessionsData.refetch();
8901
+ }
8902
+ };
8903
+ }, [sessionsData.enabled, props.callbacks, sessionsData.refetch, sessionsData.noteSentMessage]);
8904
+ const availabilityPending = availabilityData.enabled && availabilityData.isLoading && availabilityData.available === null;
8905
+ if (availabilityPending) {
8906
+ return /* @__PURE__ */ jsxs(
8907
+ "div",
8908
+ {
8909
+ className: cn("bg-background overflow-hidden flex flex-col flex-[4]", props.className),
8910
+ style: props.style,
8911
+ children: [
8912
+ props.children,
8913
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-chat-loading", children: /* @__PURE__ */ jsx("span", { className: "payman-v2-spinner" }) })
8914
+ ]
8915
+ }
8916
+ );
8917
+ }
8918
+ if (agentUnavailable) {
8919
+ const disabledBody = availabilityStatus === "blocked" ? config.availability?.blockedComponent ?? /* @__PURE__ */ jsx(AvailabilityNotice, { message: DEFAULT_USER_BLOCKED_MESSAGE }) : config.availability?.disabledComponent ?? /* @__PURE__ */ jsx(AvailabilityNotice, { message: DEFAULT_AGENT_DISABLED_MESSAGE });
8920
+ return /* @__PURE__ */ jsxs(
8921
+ "div",
8922
+ {
8923
+ className: cn("bg-background overflow-hidden flex flex-col flex-[4]", props.className),
8924
+ style: props.style,
8925
+ children: [
8926
+ props.children,
8927
+ disabledBody
8928
+ ]
8929
+ }
8930
+ );
8931
+ }
8932
+ return /* @__PURE__ */ jsx(
8933
+ PaymanChatWithChat,
8934
+ {
8935
+ ...props,
8936
+ config: effectiveConfig,
8937
+ callbacks: callbacksWithSessionsRefresh,
8938
+ sessionsPanel,
8939
+ ref
8940
+ },
8941
+ sessionsData.enabled ? sessionsData.activeSessionId : void 0
8942
+ );
8943
+ }
8944
+ );
6870
8945
 
6871
- export { PaymanChat, PaymanChatContext, UserActionStaleError, cancelUserAction, captureSentryError, cn, formatDate, resendUserAction, submitUserAction, useChatV2, usePaymanChat, useVoice };
8946
+ export { PaymanChat, PaymanChatContext, UserActionStaleError, cancelUserAction, captureSentryError, cn, formatDate, resendUserAction, submitUserAction, useAgentConfig, useChatV2, usePaymanChat, useVoice };
6872
8947
  //# sourceMappingURL=index.mjs.map
6873
8948
  //# sourceMappingURL=index.mjs.map