@paymanai/payman-typescript-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.
@@ -1169,6 +1169,9 @@ function isUserActionExpired(prompt) {
1169
1169
 
1170
1170
  // src/hooks/useChatV2.ts
1171
1171
  var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
1172
+ function promptSlotKey(p) {
1173
+ return p.toolCallId || p.userActionId;
1174
+ }
1172
1175
  function upsertPrompt(prompts, req) {
1173
1176
  const idx = prompts.findIndex(
1174
1177
  (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
@@ -1215,6 +1218,7 @@ function useChatV2(config, callbacks = {}) {
1215
1218
  configRef.current = config;
1216
1219
  const messagesRef = react.useRef(messages);
1217
1220
  messagesRef.current = messages;
1221
+ const pendingSubmissionsRef = react.useRef(/* @__PURE__ */ new Map());
1218
1222
  const storeAwareSetMessages = react.useCallback(
1219
1223
  (updater) => {
1220
1224
  const streamUserId = streamUserIdRef.current;
@@ -1249,6 +1253,8 @@ function useChatV2(config, callbacks = {}) {
1249
1253
  if (!config.userId) return EMPTY_USER_ACTION_STATE2;
1250
1254
  return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
1251
1255
  });
1256
+ const userActionStateRef = react.useRef(userActionState);
1257
+ userActionStateRef.current = userActionState;
1252
1258
  const storeAwareSetUserActionState = react.useCallback(
1253
1259
  (updater) => {
1254
1260
  const streamUserId = streamUserIdRef.current;
@@ -1276,6 +1282,12 @@ function useChatV2(config, callbacks = {}) {
1276
1282
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1277
1283
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1278
1284
  onUserActionRequired: (request) => {
1285
+ const requestSlotKey = promptSlotKey(request);
1286
+ for (const [pendingId, slotKey] of pendingSubmissionsRef.current) {
1287
+ if (slotKey === requestSlotKey) {
1288
+ pendingSubmissionsRef.current.delete(pendingId);
1289
+ }
1290
+ }
1279
1291
  storeAwareSetUserActionState((prev) => ({
1280
1292
  ...prev,
1281
1293
  prompts: upsertPrompt(prev.prompts, request)
@@ -1287,6 +1299,28 @@ function useChatV2(config, callbacks = {}) {
1287
1299
  (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1288
1300
  );
1289
1301
  callbacksRef.current.onUserNotification?.(notification);
1302
+ },
1303
+ // A submitted-but-unconfirmed prompt (see `submitUserAction`) is only
1304
+ // safe to drop once the stream has demonstrably moved on. `onEvent`
1305
+ // in useStreamManagerV2 calls `onStepsUpdate` for *every* processed
1306
+ // stream event, unconditionally — so the first time this fires
1307
+ // after a submission, the event in hand is either the rejection
1308
+ // retry (a fresh `USER_ACTION_REQUIRED`, which the handler above
1309
+ // already deleted this pending entry for, earlier in the very same
1310
+ // event) or genuine forward progress (RUN_IN_PROGRESS, a tool call,
1311
+ // content deltas, RUN_COMPLETED, ...). Anything still pending by
1312
+ // the time we get here therefore means the run resumed normally —
1313
+ // clear it.
1314
+ onStepsUpdate: (steps) => {
1315
+ if (pendingSubmissionsRef.current.size > 0) {
1316
+ const resolved = [...pendingSubmissionsRef.current.keys()];
1317
+ pendingSubmissionsRef.current.clear();
1318
+ storeAwareSetUserActionState((prev) => ({
1319
+ ...prev,
1320
+ prompts: prev.prompts.filter((p) => !resolved.includes(p.userActionId))
1321
+ }));
1322
+ }
1323
+ callbacksRef.current.onStepsUpdate?.(steps);
1290
1324
  }
1291
1325
  // eslint-disable-next-line react-hooks/exhaustive-deps
1292
1326
  }), []);
@@ -1422,6 +1456,7 @@ function useChatV2(config, callbacks = {}) {
1422
1456
  [storeAwareSetUserActionState]
1423
1457
  );
1424
1458
  const removePrompt = react.useCallback((userActionId) => {
1459
+ pendingSubmissionsRef.current.delete(userActionId);
1425
1460
  storeAwareSetUserActionState((prev) => ({
1426
1461
  ...prev,
1427
1462
  prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
@@ -1432,7 +1467,10 @@ function useChatV2(config, callbacks = {}) {
1432
1467
  setPromptStatus(userActionId, "submitting");
1433
1468
  try {
1434
1469
  await submitUserAction(configRef.current, userActionId, content);
1435
- removePrompt(userActionId);
1470
+ const prompt = userActionStateRef.current.prompts.find(
1471
+ (p) => p.userActionId === userActionId
1472
+ );
1473
+ pendingSubmissionsRef.current.set(userActionId, prompt ? promptSlotKey(prompt) : userActionId);
1436
1474
  } catch (error) {
1437
1475
  if (error instanceof UserActionStaleError) {
1438
1476
  setPromptStatus(userActionId, "stale");
@@ -1443,7 +1481,7 @@ function useChatV2(config, callbacks = {}) {
1443
1481
  throw error;
1444
1482
  }
1445
1483
  },
1446
- [removePrompt, setPromptStatus]
1484
+ [setPromptStatus]
1447
1485
  );
1448
1486
  const cancelUserAction2 = react.useCallback(
1449
1487
  async (userActionId) => {
@@ -1697,6 +1735,284 @@ function useVoice() {
1697
1735
  reset
1698
1736
  };
1699
1737
  }
1738
+ var DEFAULT_PAGE_SIZE = 20;
1739
+ var DEFAULT_MESSAGES_PAGE_SIZE = 100;
1740
+ function defaultParseSessions(json) {
1741
+ const body = json;
1742
+ const rows = Array.isArray(body?.data) ? body.data : [];
1743
+ const data = rows.map((row) => ({ ...row, title: row.title ?? row.sessionTitle ?? null }));
1744
+ return { data, total: body?.total };
1745
+ }
1746
+ function defaultParseMessages(json) {
1747
+ const body = json;
1748
+ return { data: Array.isArray(body?.data) ? body.data : [] };
1749
+ }
1750
+ function withQuery(path, params) {
1751
+ const qs = Object.entries(params).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
1752
+ if (!qs) return path;
1753
+ return `${path}${path.includes("?") ? "&" : "?"}${qs}`;
1754
+ }
1755
+ function toFeedbackState(feedback) {
1756
+ const value = feedback?.feedback;
1757
+ if (!value) return null;
1758
+ return value === "POSITIVE" ? "up" : "down";
1759
+ }
1760
+ function toMessageDisplay(rows, sessionId) {
1761
+ return rows.flatMap((row) => {
1762
+ const timestamp = row.startTime || row.endTime || (/* @__PURE__ */ new Date()).toISOString();
1763
+ const out = [
1764
+ {
1765
+ id: `${row.id}:user`,
1766
+ sessionId,
1767
+ role: "user",
1768
+ content: row.sessionUserIntent,
1769
+ timestamp
1770
+ }
1771
+ ];
1772
+ if (row.agentResponse) {
1773
+ out.push({
1774
+ id: `${row.id}:assistant`,
1775
+ sessionId,
1776
+ role: "assistant",
1777
+ content: row.agentResponse,
1778
+ timestamp: row.endTime || timestamp,
1779
+ executionId: row.id,
1780
+ isError: row.status === "FAILED",
1781
+ feedback: toFeedbackState(row.feedback)
1782
+ });
1783
+ }
1784
+ return out;
1785
+ });
1786
+ }
1787
+ function useChatSessions({
1788
+ api,
1789
+ sessions,
1790
+ initialSessionId
1791
+ }) {
1792
+ const enabled = sessions?.enabled === true;
1793
+ const listEndpoint = sessions?.listEndpoint ?? "/api/conversations/sessions";
1794
+ const messagesEndpointTemplate = sessions?.messagesEndpoint ?? "/api/conversations/sessions/{sessionId}/messages";
1795
+ const pageSize = sessions?.pageSize ?? DEFAULT_PAGE_SIZE;
1796
+ const messagesPageSize = sessions?.messagesPageSize ?? DEFAULT_MESSAGES_PAGE_SIZE;
1797
+ const parseSessionsResponse = sessions?.parseSessionsResponse ?? defaultParseSessions;
1798
+ const parseMessagesResponse = sessions?.parseMessagesResponse ?? defaultParseMessages;
1799
+ const [sessionsList, setSessionsList] = react.useState([]);
1800
+ const [isLoadingList, setIsLoadingList] = react.useState(enabled);
1801
+ const [isLoadingMore, setIsLoadingMore] = react.useState(false);
1802
+ const [listError, setListError] = react.useState(null);
1803
+ const hasLoadedOnceRef = react.useRef(false);
1804
+ const [page, setPage] = react.useState(0);
1805
+ const [total, setTotal] = react.useState(null);
1806
+ const [lastPageFull, setLastPageFull] = react.useState(false);
1807
+ const [selectedSessionId, setSelectedSessionId] = react.useState(
1808
+ initialSessionId ?? null
1809
+ );
1810
+ const [draftSessionId, setDraftSessionId] = react.useState(generateId);
1811
+ const [selectedMessages, setSelectedMessages] = react.useState([]);
1812
+ const [isLoadingMessages, setIsLoadingMessages] = react.useState(
1813
+ enabled && initialSessionId != null
1814
+ );
1815
+ const [messagesError, setMessagesError] = react.useState(null);
1816
+ const [refetchTick, setRefetchTick] = react.useState(0);
1817
+ const baseUrl = (api.baseUrl || "").replace(/\/+$/, "");
1818
+ const apiHeaders = api.headers;
1819
+ const authToken = api.authToken;
1820
+ const headersKey = JSON.stringify(apiHeaders ?? {});
1821
+ const requestHeaders = react.useMemo(() => {
1822
+ const headers = { Accept: "application/json", ...apiHeaders ?? {} };
1823
+ if (authToken && !headers.Authorization) headers.Authorization = `Bearer ${authToken}`;
1824
+ return headers;
1825
+ }, [headersKey, authToken]);
1826
+ const hasMore = total != null ? sessionsList.length < total : lastPageFull;
1827
+ react.useEffect(() => {
1828
+ setPage(0);
1829
+ hasLoadedOnceRef.current = false;
1830
+ }, [enabled, baseUrl, listEndpoint, pageSize, requestHeaders]);
1831
+ react.useEffect(() => {
1832
+ setPage(0);
1833
+ }, [refetchTick]);
1834
+ react.useEffect(() => {
1835
+ if (!enabled) return;
1836
+ const ctrl = new AbortController();
1837
+ const isFirstPage = page === 0;
1838
+ if (isFirstPage && !hasLoadedOnceRef.current) setIsLoadingList(true);
1839
+ else if (!isFirstPage) setIsLoadingMore(true);
1840
+ setListError(null);
1841
+ fetch(withQuery(`${baseUrl}${listEndpoint}`, { page, limit: pageSize }), {
1842
+ method: "GET",
1843
+ headers: requestHeaders,
1844
+ signal: ctrl.signal
1845
+ }).then(async (res) => {
1846
+ if (!res.ok) throw new Error(`Failed to load chats (${res.status})`);
1847
+ return res.json();
1848
+ }).then((json) => {
1849
+ const parsed = parseSessionsResponse(json);
1850
+ setSessionsList((prev) => isFirstPage ? parsed.data : [...prev, ...parsed.data]);
1851
+ setTotal(parsed.total ?? null);
1852
+ setLastPageFull(parsed.data.length >= pageSize);
1853
+ hasLoadedOnceRef.current = true;
1854
+ }).catch((e) => {
1855
+ if (e.name === "AbortError") return;
1856
+ setListError(e instanceof Error ? e.message : "Failed to load chats");
1857
+ }).finally(() => {
1858
+ setIsLoadingList(false);
1859
+ setIsLoadingMore(false);
1860
+ });
1861
+ return () => ctrl.abort();
1862
+ }, [enabled, baseUrl, listEndpoint, pageSize, requestHeaders, refetchTick, page]);
1863
+ react.useEffect(() => {
1864
+ if (!enabled || !selectedSessionId) {
1865
+ setSelectedMessages([]);
1866
+ return;
1867
+ }
1868
+ const ctrl = new AbortController();
1869
+ setIsLoadingMessages(true);
1870
+ setMessagesError(null);
1871
+ const path = messagesEndpointTemplate.replace(
1872
+ "{sessionId}",
1873
+ encodeURIComponent(selectedSessionId)
1874
+ );
1875
+ fetch(withQuery(`${baseUrl}${path}`, { limit: messagesPageSize }), {
1876
+ method: "GET",
1877
+ headers: requestHeaders,
1878
+ signal: ctrl.signal
1879
+ }).then(async (res) => {
1880
+ if (!res.ok) throw new Error(`Failed to load messages (${res.status})`);
1881
+ return res.json();
1882
+ }).then(
1883
+ (json) => setSelectedMessages(
1884
+ toMessageDisplay(parseMessagesResponse(json).data, selectedSessionId)
1885
+ )
1886
+ ).catch((e) => {
1887
+ if (e.name === "AbortError") return;
1888
+ setMessagesError(e instanceof Error ? e.message : "Failed to load messages");
1889
+ setSelectedMessages([]);
1890
+ }).finally(() => setIsLoadingMessages(false));
1891
+ return () => ctrl.abort();
1892
+ }, [enabled, selectedSessionId, baseUrl, messagesEndpointTemplate, messagesPageSize, requestHeaders]);
1893
+ const loadMore = react.useCallback(() => {
1894
+ setPage((prev) => {
1895
+ if (isLoadingList || isLoadingMore || !hasMore) return prev;
1896
+ return prev + 1;
1897
+ });
1898
+ }, [isLoadingList, isLoadingMore, hasMore]);
1899
+ const selectSession = react.useCallback((sessionId) => {
1900
+ setSelectedSessionId(sessionId);
1901
+ setSelectedMessages([]);
1902
+ }, []);
1903
+ const startNewSession = react.useCallback(() => {
1904
+ setSelectedSessionId(null);
1905
+ setDraftSessionId(generateId());
1906
+ setSelectedMessages([]);
1907
+ }, []);
1908
+ const refetch = react.useCallback(() => setRefetchTick((tick) => tick + 1), []);
1909
+ const noteSentMessage = react.useCallback(
1910
+ (text) => {
1911
+ const id = selectedSessionId ?? draftSessionId;
1912
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1913
+ const trimmed = text.trim();
1914
+ setSessionsList((prev) => {
1915
+ const idx = prev.findIndex((s) => s.sessionId === id);
1916
+ if (idx >= 0) {
1917
+ const existing = prev[idx];
1918
+ const bumped = {
1919
+ ...existing,
1920
+ lastMessageAt: now,
1921
+ messageCount: (existing.messageCount ?? 0) + 1,
1922
+ title: existing.title?.trim() ? existing.title : trimmed
1923
+ };
1924
+ return [bumped, ...prev.slice(0, idx), ...prev.slice(idx + 1)];
1925
+ }
1926
+ const fresh = {
1927
+ sessionId: id,
1928
+ title: trimmed,
1929
+ messageCount: 1,
1930
+ lastMessageAt: now,
1931
+ status: "PROCESSING"
1932
+ };
1933
+ return [fresh, ...prev];
1934
+ });
1935
+ },
1936
+ [selectedSessionId, draftSessionId]
1937
+ );
1938
+ return {
1939
+ enabled,
1940
+ sessionsList,
1941
+ isLoadingList,
1942
+ listError,
1943
+ total,
1944
+ hasMore,
1945
+ isLoadingMore,
1946
+ loadMore,
1947
+ noteSentMessage,
1948
+ selectedSessionId,
1949
+ activeSessionId: selectedSessionId ?? draftSessionId,
1950
+ selectSession,
1951
+ startNewSession,
1952
+ selectedMessages,
1953
+ isLoadingMessages,
1954
+ messagesError,
1955
+ refetch
1956
+ };
1957
+ }
1958
+ function deriveStatus(enabled, blocked) {
1959
+ if (blocked === true) return "blocked";
1960
+ if (enabled === false) return "disabled";
1961
+ if (enabled === true) return "available";
1962
+ return "unknown";
1963
+ }
1964
+ function defaultParse(json) {
1965
+ const body = json;
1966
+ return {
1967
+ available: body?.enabled === true && body?.blocked !== true,
1968
+ status: deriveStatus(body?.enabled, body?.blocked),
1969
+ config: body ?? void 0
1970
+ };
1971
+ }
1972
+ function useAgentConfig({ api, availability }) {
1973
+ const enabled = availability?.enabled === true;
1974
+ const endpoint = availability?.endpoint ?? "/api/agent/config";
1975
+ const parseResponse = availability?.parseResponse ?? defaultParse;
1976
+ const [available, setAvailable] = react.useState(null);
1977
+ const [status, setStatus] = react.useState("unknown");
1978
+ const [config, setConfig] = react.useState(void 0);
1979
+ const [isLoading, setIsLoading] = react.useState(enabled);
1980
+ const [error, setError] = react.useState(null);
1981
+ const [refetchTick, setRefetchTick] = react.useState(0);
1982
+ const baseUrl = (api.baseUrl || "").replace(/\/+$/, "");
1983
+ const apiHeaders = api.headers;
1984
+ const authToken = api.authToken;
1985
+ const requestHeaders = react.useMemo(() => {
1986
+ const headers = { Accept: "application/json", ...apiHeaders ?? {} };
1987
+ if (authToken && !headers.Authorization) headers.Authorization = `Bearer ${authToken}`;
1988
+ return headers;
1989
+ }, [apiHeaders, authToken]);
1990
+ react.useEffect(() => {
1991
+ if (!enabled) return;
1992
+ const ctrl = new AbortController();
1993
+ setIsLoading(true);
1994
+ setError(null);
1995
+ fetch(`${baseUrl}${endpoint}`, {
1996
+ method: "GET",
1997
+ headers: requestHeaders,
1998
+ signal: ctrl.signal
1999
+ }).then(async (res) => {
2000
+ if (!res.ok) throw new Error(`Failed to check availability (${res.status})`);
2001
+ return res.json();
2002
+ }).then((json) => {
2003
+ const parsed = parseResponse(json);
2004
+ setAvailable(parsed.available);
2005
+ setStatus(parsed.status ?? deriveStatus(parsed.available, void 0));
2006
+ setConfig(parsed.config);
2007
+ }).catch((e) => {
2008
+ if (e.name === "AbortError") return;
2009
+ setError(e instanceof Error ? e.message : "Failed to check availability");
2010
+ }).finally(() => setIsLoading(false));
2011
+ return () => ctrl.abort();
2012
+ }, [enabled, baseUrl, endpoint, requestHeaders, refetchTick]);
2013
+ const refetch = react.useCallback(() => setRefetchTick((tick) => tick + 1), []);
2014
+ return { enabled, available, status, config, isLoading, error, refetch };
2015
+ }
1700
2016
 
1701
2017
  // src/utils/jsonSchemaForm.ts
1702
2018
  function classifyField(field) {
@@ -1843,6 +2159,8 @@ exports.resendUserAction = resendUserAction;
1843
2159
  exports.resolveUserActionExpiresAt = resolveUserActionExpiresAt;
1844
2160
  exports.streamWorkflowEvents = streamWorkflowEvents;
1845
2161
  exports.submitUserAction = submitUserAction;
2162
+ exports.useAgentConfig = useAgentConfig;
2163
+ exports.useChatSessions = useChatSessions;
1846
2164
  exports.useChatV2 = useChatV2;
1847
2165
  exports.useVoice = useVoice;
1848
2166
  exports.validateField = validateField;