@superatomai/sdk-node 0.0.4 → 0.0.5

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
@@ -624,6 +624,18 @@ var DSLRendererPropsSchema2 = z2.object({
624
624
  });
625
625
 
626
626
  // src/types.ts
627
+ var UserSchema = z3.object({
628
+ username: z3.string().min(1, "Username is required"),
629
+ email: z3.string().email("Invalid email format").optional(),
630
+ password: z3.string().min(1, "Password is required"),
631
+ fullname: z3.string().optional(),
632
+ role: z3.string().optional(),
633
+ wsIds: z3.array(z3.string()).optional()
634
+ // Only in memory, not persisted to file
635
+ });
636
+ var UsersDataSchema = z3.object({
637
+ users: z3.array(UserSchema)
638
+ });
627
639
  var MessageParticipantSchema = z3.object({
628
640
  id: z3.string().optional(),
629
641
  type: z3.string().optional()
@@ -724,7 +736,10 @@ var UsersRequestPayloadSchema = z3.object({
724
736
  operation: z3.enum(["create", "update", "delete", "getAll", "getOne"]),
725
737
  data: z3.object({
726
738
  username: z3.string().optional(),
727
- password: z3.string().optional()
739
+ email: z3.string().email("Invalid email format").optional(),
740
+ password: z3.string().optional(),
741
+ fullname: z3.string().optional(),
742
+ role: z3.string().optional()
728
743
  }).optional()
729
744
  });
730
745
  var UsersRequestMessageSchema = z3.object({
@@ -790,20 +805,91 @@ var ReportsRequestMessageSchema = z3.object({
790
805
 
791
806
  // src/utils/logger.ts
792
807
  var PREFIX = "[SuperatomSDK]";
793
- var logger = {
794
- info: (...args) => {
795
- console.log(PREFIX, ...args);
796
- },
797
- error: (...args) => {
798
- console.error(PREFIX, ...args);
799
- },
800
- warn: (...args) => {
801
- console.warn(PREFIX, ...args);
802
- },
803
- debug: (...args) => {
804
- console.log(PREFIX, "[DEBUG]", ...args);
808
+ var LOG_LEVEL_PRIORITY = {
809
+ errors: 0,
810
+ warnings: 1,
811
+ info: 2,
812
+ verbose: 3
813
+ };
814
+ var MESSAGE_LEVEL_PRIORITY = {
815
+ error: 0,
816
+ warn: 1,
817
+ info: 2,
818
+ debug: 3
819
+ };
820
+ var Logger = class {
821
+ constructor() {
822
+ const envLevel = (process.env.SUPERATOM_LOG_LEVEL || "info").toLowerCase();
823
+ if (this.isValidLogLevel(envLevel)) {
824
+ this.currentLevel = envLevel;
825
+ } else {
826
+ this.currentLevel = "info";
827
+ console.warn(
828
+ `${PREFIX} Invalid log level "${envLevel}". Using default "info". Valid levels: errors, warnings, info, verbose`
829
+ );
830
+ }
831
+ this.currentLevelPriority = LOG_LEVEL_PRIORITY[this.currentLevel];
832
+ }
833
+ /**
834
+ * Check if a string is a valid log level
835
+ */
836
+ isValidLogLevel(level) {
837
+ return level === "errors" || level === "warnings" || level === "info" || level === "verbose";
838
+ }
839
+ /**
840
+ * Check if a message should be logged based on current log level
841
+ */
842
+ shouldLog(messageLevel) {
843
+ const messagePriority = MESSAGE_LEVEL_PRIORITY[messageLevel];
844
+ return messagePriority <= this.currentLevelPriority;
845
+ }
846
+ /**
847
+ * Get current log level
848
+ */
849
+ getLogLevel() {
850
+ return this.currentLevel;
851
+ }
852
+ /**
853
+ * Set log level programmatically
854
+ */
855
+ setLogLevel(level) {
856
+ this.currentLevel = level;
857
+ this.currentLevelPriority = LOG_LEVEL_PRIORITY[level];
858
+ }
859
+ /**
860
+ * Log info message (shown for info and verbose levels)
861
+ */
862
+ info(...args) {
863
+ if (this.shouldLog("info")) {
864
+ console.log(PREFIX, ...args);
865
+ }
866
+ }
867
+ /**
868
+ * Log error message (shown for all levels)
869
+ */
870
+ error(...args) {
871
+ if (this.shouldLog("error")) {
872
+ console.error(PREFIX, ...args);
873
+ }
874
+ }
875
+ /**
876
+ * Log warning message (shown for warnings, info, and verbose levels)
877
+ */
878
+ warn(...args) {
879
+ if (this.shouldLog("warn")) {
880
+ console.warn(PREFIX, ...args);
881
+ }
882
+ }
883
+ /**
884
+ * Log debug message (only shown for verbose level)
885
+ */
886
+ debug(...args) {
887
+ if (this.shouldLog("debug")) {
888
+ console.log(PREFIX, "[DEBUG]", ...args);
889
+ }
805
890
  }
806
891
  };
892
+ var logger = new Logger();
807
893
 
808
894
  // src/threads/uiblock.ts
809
895
  import { randomUUID } from "crypto";
@@ -838,13 +924,15 @@ var UIBlock = class {
838
924
  * @param generatedComponentMetadata - Optional metadata about the generated component
839
925
  * @param actions - Optional array of available actions
840
926
  * @param id - Optional custom ID, generates UUID if not provided
927
+ * @param textResponse - Optional text response from LLM
841
928
  */
842
- constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id) {
929
+ constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id, textResponse = null) {
843
930
  this.id = id || randomUUID();
844
931
  this.userQuestion = userQuestion;
845
932
  this.componentData = componentData;
846
933
  this.generatedComponentMetadata = generatedComponentMetadata;
847
934
  this.actions = actions;
935
+ this.textResponse = textResponse;
848
936
  this.createdAt = /* @__PURE__ */ new Date();
849
937
  }
850
938
  /**
@@ -963,6 +1051,18 @@ var UIBlock = class {
963
1051
  const processedData = this.processDataForStorage(data);
964
1052
  this.componentData = { ...this.componentData, ...processedData };
965
1053
  }
1054
+ /**
1055
+ * Get text response
1056
+ */
1057
+ getTextResponse() {
1058
+ return this.textResponse;
1059
+ }
1060
+ /**
1061
+ * Set or update text response
1062
+ */
1063
+ setTextResponse(textResponse) {
1064
+ this.textResponse = textResponse;
1065
+ }
966
1066
  /**
967
1067
  * Get all actions (only if they are resolved, not if fetching)
968
1068
  */
@@ -1046,6 +1146,7 @@ var UIBlock = class {
1046
1146
  userQuestion: this.userQuestion,
1047
1147
  generatedComponentMetadata: this.generatedComponentMetadata,
1048
1148
  componentData: this.componentData,
1149
+ textResponse: this.textResponse,
1049
1150
  actions: actionsValue,
1050
1151
  isFetchingActions: this.actions instanceof Promise,
1051
1152
  createdAt: this.createdAt.toISOString()
@@ -1480,9 +1581,9 @@ function getUserManager() {
1480
1581
  }
1481
1582
  return currentUserManager;
1482
1583
  }
1483
- function findUserByUsername(username) {
1584
+ function findUserByUsernameOrEmail(identifier) {
1484
1585
  const manager = getUserManager();
1485
- const user = manager.getUser(username);
1586
+ const user = manager.getUserByUsernameOrEmail(identifier);
1486
1587
  return user || null;
1487
1588
  }
1488
1589
  function addWsIdToUser(username, wsId) {
@@ -1504,60 +1605,81 @@ async function cleanupUserStorage() {
1504
1605
 
1505
1606
  // src/auth/validator.ts
1506
1607
  function validateUser(credentials) {
1507
- const { username, password } = credentials;
1508
- if (!username || !password) {
1509
- logger.warn("Validation failed: Username and password are required");
1608
+ const { username, email, password } = credentials;
1609
+ const identifier = username || email;
1610
+ logger.debug("[validateUser] Starting user validation");
1611
+ logger.debug(`[validateUser] Username provided: ${username ? "\u2713" : "\u2717"}, Email provided: ${email ? "\u2713" : "\u2717"}, Password provided: ${password ? "\u2713" : "\u2717"}`);
1612
+ if (!identifier || !password) {
1613
+ logger.warn("[validateUser] Validation failed: Username/email and password are required");
1510
1614
  return {
1511
1615
  success: false,
1512
- error: "Username and password are required"
1616
+ error: "Username or email and password are required"
1513
1617
  };
1514
1618
  }
1515
- const user = findUserByUsername(username);
1619
+ logger.debug(`[validateUser] Looking up user by identifier: ${identifier}`);
1620
+ const user = findUserByUsernameOrEmail(identifier);
1516
1621
  if (!user) {
1517
- logger.warn(`Validation failed: User not found - ${username}`);
1622
+ logger.warn(`[validateUser] Validation failed: User not found - ${identifier}`);
1518
1623
  return {
1519
1624
  success: false,
1520
- error: "Invalid username"
1625
+ error: "Invalid username or email"
1521
1626
  };
1522
1627
  }
1628
+ logger.debug(`[validateUser] User found: ${user.username}, verifying password`);
1523
1629
  const hashedPassword = hashPassword(user.password);
1524
1630
  if (hashedPassword !== password) {
1525
- logger.warn(`Validation failed: Invalid password for user - ${username}`);
1631
+ logger.warn(`[validateUser] Validation failed: Invalid password for user - ${user.username}`);
1632
+ logger.debug(`[validateUser] Password hash mismatch for user: ${user.username}`);
1526
1633
  return {
1527
1634
  success: false,
1528
1635
  error: "Invalid password"
1529
1636
  };
1530
1637
  }
1531
- logger.debug(`User validated successfully: ${username}`);
1638
+ logger.info(`[validateUser] \u2713 User validated successfully: ${user.username}`);
1639
+ logger.debug(`[validateUser] Returning user data for: ${user.username}`);
1532
1640
  return {
1533
1641
  success: true,
1534
- data: user.username
1642
+ data: user.username,
1643
+ username: user.username
1535
1644
  };
1536
1645
  }
1537
1646
  function authenticateAndStoreWsId(credentials, wsId) {
1647
+ const identifier = credentials.username || credentials.email;
1648
+ logger.debug("[authenticateAndStoreWsId] Starting authentication and WebSocket ID storage");
1649
+ logger.debug("[authenticateAndStoreWsId] Validating user credentials");
1538
1650
  const validationResult = validateUser(credentials);
1539
1651
  if (!validationResult.success) {
1652
+ logger.warn(`[authenticateAndStoreWsId] User validation failed for: ${identifier}`);
1540
1653
  return validationResult;
1541
1654
  }
1542
- const stored = addWsIdToUser(credentials.username, wsId);
1543
- if (!stored) {
1544
- logger.error(`Failed to store WebSocket ID for user: ${credentials.username}`);
1545
- return {
1546
- success: false,
1547
- error: "Failed to store user session"
1548
- };
1549
- }
1550
- logger.info(`WebSocket ID stored for user: ${credentials.username}`);
1655
+ const username = validationResult.username;
1656
+ logger.info(`[authenticateAndStoreWsId] User ${username} validated, storing WebSocket ID`);
1657
+ logger.debug(`[authenticateAndStoreWsId] Calling addWsIdToUser for ${username}`);
1658
+ addWsIdToUser(username, wsId);
1659
+ logger.debug(`[authenticateAndStoreWsId] WebSocket ID ${wsId} associated with user ${username}`);
1551
1660
  return validationResult;
1552
1661
  }
1553
1662
  function verifyAuthToken(authToken) {
1554
1663
  try {
1664
+ logger.debug("[verifyAuthToken] Starting token verification");
1665
+ logger.debug("[verifyAuthToken] Decoding base64 token");
1555
1666
  const decodedString = Buffer.from(authToken, "base64").toString("utf-8");
1667
+ logger.debug("[verifyAuthToken] Parsing decoded token as JSON");
1556
1668
  const credentials = JSON.parse(decodedString);
1557
- logger.debug("Token decoded and parsed successfully");
1558
- return validateUser(credentials);
1669
+ logger.debug("[verifyAuthToken] Token decoded and parsed successfully");
1670
+ logger.debug(`[verifyAuthToken] Token contains username: ${credentials.username ? "\u2713" : "\u2717"}`);
1671
+ logger.debug("[verifyAuthToken] Validating credentials from token");
1672
+ const result = validateUser(credentials);
1673
+ if (result.success) {
1674
+ logger.info(`[verifyAuthToken] \u2713 Token verified successfully for user: ${credentials.username || "unknown"}`);
1675
+ } else {
1676
+ logger.warn(`[verifyAuthToken] Token verification failed: ${result.error}`);
1677
+ }
1678
+ return result;
1559
1679
  } catch (error) {
1560
- logger.error("Failed to verify auth token:", error);
1680
+ const errorMsg = error instanceof Error ? error.message : String(error);
1681
+ logger.error(`[verifyAuthToken] Failed to verify auth token: ${errorMsg}`);
1682
+ logger.debug("[verifyAuthToken] Token verification error details:", error);
1561
1683
  return {
1562
1684
  success: false,
1563
1685
  error: "Invalid token format"
@@ -1568,36 +1690,49 @@ function verifyAuthToken(authToken) {
1568
1690
  // src/handlers/auth-login-requests.ts
1569
1691
  async function handleAuthLoginRequest(data, sendMessage) {
1570
1692
  try {
1693
+ logger.debug("[AUTH_LOGIN_REQ] Parsing incoming auth login request");
1571
1694
  const authRequest = AuthLoginRequestMessageSchema.parse(data);
1572
1695
  const { id, payload } = authRequest;
1573
1696
  const login_data = payload.login_data;
1574
1697
  const wsId = authRequest.from.id;
1698
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Processing auth login request from client: ${wsId}`);
1699
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data present: ${!!login_data}`);
1575
1700
  if (!login_data) {
1701
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Login data not found in request`);
1576
1702
  sendDataResponse2(id, {
1577
1703
  success: false,
1578
1704
  error: "Login data not found"
1579
1705
  }, sendMessage, wsId);
1580
1706
  return;
1581
1707
  }
1708
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Decoding base64 login data`);
1582
1709
  let loginData;
1583
1710
  try {
1584
1711
  loginData = decodeBase64ToJson(login_data);
1712
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data decoded successfully`);
1585
1713
  } catch (error) {
1714
+ const errorMsg = error instanceof Error ? error.message : String(error);
1715
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Failed to decode login data: ${errorMsg}`);
1716
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Decode error details:`, error);
1586
1717
  sendDataResponse2(id, {
1587
1718
  success: false,
1588
1719
  error: "Invalid login data format"
1589
1720
  }, sendMessage, wsId);
1590
1721
  return;
1591
1722
  }
1592
- const { username, password } = loginData;
1593
- if (!username) {
1723
+ const { username, email, password } = loginData;
1724
+ const identifier = username || email;
1725
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Validating credentials - username: ${username ? "\u2713" : "\u2717"}, email: ${email ? "\u2713" : "\u2717"}, password: ${password ? "\u2713" : "\u2717"}`);
1726
+ if (!identifier) {
1727
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Username or email not found in login data`);
1594
1728
  sendDataResponse2(id, {
1595
1729
  success: false,
1596
- error: "Username not found in login data"
1730
+ error: "Username or email is required"
1597
1731
  }, sendMessage, wsId);
1598
1732
  return;
1599
1733
  }
1600
1734
  if (!password) {
1735
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Password not found in login data`);
1601
1736
  sendDataResponse2(id, {
1602
1737
  success: false,
1603
1738
  error: "Password not found in login data"
@@ -1605,20 +1740,46 @@ async function handleAuthLoginRequest(data, sendMessage) {
1605
1740
  return;
1606
1741
  }
1607
1742
  if (!wsId) {
1743
+ logger.error(`[AUTH_LOGIN_REQ ${id}] WebSocket ID not found in request`);
1608
1744
  sendDataResponse2(id, {
1609
1745
  success: false,
1610
1746
  error: "WebSocket ID not found"
1611
1747
  }, sendMessage, wsId);
1612
1748
  return;
1613
1749
  }
1750
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Credentials validated, authenticating user: ${identifier}`);
1751
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] WebSocket ID: ${wsId}`);
1752
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Calling authenticateAndStoreWsId for user: ${identifier}`);
1614
1753
  const authResult = authenticateAndStoreWsId(
1615
- { username, password },
1754
+ { username, email, password },
1616
1755
  wsId
1617
1756
  );
1757
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Authentication result for ${identifier}: ${authResult.success ? "success" : "failed"}`);
1758
+ if (!authResult.success) {
1759
+ logger.warn(`[AUTH_LOGIN_REQ ${id}] Authentication failed for ${identifier}: ${authResult.error}`);
1760
+ } else {
1761
+ logger.info(`[AUTH_LOGIN_REQ ${id}] User ${authResult.username || identifier} authenticated successfully`);
1762
+ }
1763
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Sending auth response to client`);
1618
1764
  sendDataResponse2(id, authResult, sendMessage, wsId);
1765
+ logger.info(`[AUTH_LOGIN_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth login request completed`);
1619
1766
  return;
1620
1767
  } catch (error) {
1621
- logger.error("Failed to handle auth login request:", error);
1768
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1769
+ const errorStack = error instanceof Error ? error.stack : void 0;
1770
+ logger.error(`[AUTH_LOGIN_REQ] Failed to handle auth login request: ${errorMessage}`);
1771
+ logger.debug(`[AUTH_LOGIN_REQ] Error stack trace:`, errorStack);
1772
+ try {
1773
+ const parsedData = data;
1774
+ if (parsedData?.id) {
1775
+ sendDataResponse2(parsedData.id, {
1776
+ success: false,
1777
+ error: `Internal error: ${errorMessage}`
1778
+ }, sendMessage, parsedData.from?.id);
1779
+ }
1780
+ } catch (sendError) {
1781
+ logger.error("[AUTH_LOGIN_REQ] Failed to send error response:", sendError);
1782
+ }
1622
1783
  }
1623
1784
  }
1624
1785
  function sendDataResponse2(id, res, sendMessage, clientId) {
@@ -1634,17 +1795,27 @@ function sendDataResponse2(id, res, sendMessage, clientId) {
1634
1795
  ...res
1635
1796
  }
1636
1797
  };
1798
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Sending ${res.success ? "successful" : "failed"} auth response to client: ${clientId}`);
1799
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
1800
+ if (res.error) {
1801
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Error message: ${res.error}`);
1802
+ }
1637
1803
  sendMessage(response);
1638
1804
  }
1639
1805
 
1640
1806
  // src/handlers/auth-verify-request.ts
1641
1807
  async function handleAuthVerifyRequest(data, sendMessage) {
1642
1808
  try {
1809
+ logger.debug("[AUTH_VERIFY_REQ] Parsing incoming auth verify request");
1643
1810
  const authRequest = AuthVerifyRequestMessageSchema.parse(data);
1644
1811
  const { id, payload } = authRequest;
1645
1812
  const token = payload.token;
1646
1813
  const wsId = authRequest.from.id;
1814
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Processing auth verify request from client: ${wsId}`);
1815
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Token present: ${!!token}`);
1816
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Token length: ${token ? token.length : 0} characters`);
1647
1817
  if (!token) {
1818
+ logger.error(`[AUTH_VERIFY_REQ ${id}] Token not found in request`);
1648
1819
  sendDataResponse3(id, {
1649
1820
  success: false,
1650
1821
  error: "Token not found"
@@ -1652,17 +1823,45 @@ async function handleAuthVerifyRequest(data, sendMessage) {
1652
1823
  return;
1653
1824
  }
1654
1825
  if (!wsId) {
1826
+ logger.error(`[AUTH_VERIFY_REQ ${id}] WebSocket ID not found in request`);
1655
1827
  sendDataResponse3(id, {
1656
1828
  success: false,
1657
1829
  error: "WebSocket ID not found"
1658
1830
  }, sendMessage, wsId);
1659
1831
  return;
1660
1832
  }
1833
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token validation starting`);
1834
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] WebSocket ID: ${wsId}`);
1835
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Calling verifyAuthToken`);
1836
+ const startTime = Date.now();
1661
1837
  const authResult = verifyAuthToken(token);
1838
+ const verificationTime = Date.now() - startTime;
1839
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token verification completed in ${verificationTime}ms - ${authResult.success ? "valid" : "invalid"}`);
1840
+ if (!authResult.success) {
1841
+ logger.warn(`[AUTH_VERIFY_REQ ${id}] Token verification failed: ${authResult.error}`);
1842
+ } else {
1843
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token verified successfully for user: ${authResult.data || "unknown"}`);
1844
+ }
1845
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Sending verification response to client`);
1662
1846
  sendDataResponse3(id, authResult, sendMessage, wsId);
1847
+ logger.info(`[AUTH_VERIFY_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth verify request completed`);
1663
1848
  return;
1664
1849
  } catch (error) {
1665
- logger.error("Failed to handle auth verify request:", error);
1850
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1851
+ const errorStack = error instanceof Error ? error.stack : void 0;
1852
+ logger.error(`[AUTH_VERIFY_REQ] Failed to handle auth verify request: ${errorMessage}`);
1853
+ logger.debug(`[AUTH_VERIFY_REQ] Error stack trace:`, errorStack);
1854
+ try {
1855
+ const parsedData = data;
1856
+ if (parsedData?.id) {
1857
+ sendDataResponse3(parsedData.id, {
1858
+ success: false,
1859
+ error: `Internal error: ${errorMessage}`
1860
+ }, sendMessage, parsedData.from?.id);
1861
+ }
1862
+ } catch (sendError) {
1863
+ logger.error("[AUTH_VERIFY_REQ] Failed to send error response:", sendError);
1864
+ }
1666
1865
  }
1667
1866
  }
1668
1867
  function sendDataResponse3(id, res, sendMessage, clientId) {
@@ -1678,6 +1877,14 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
1678
1877
  ...res
1679
1878
  }
1680
1879
  };
1880
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Sending ${res.success ? "successful" : "failed"} verification response to client: ${clientId}`);
1881
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
1882
+ if (res.error) {
1883
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Error message: ${res.error}`);
1884
+ }
1885
+ if (res.data) {
1886
+ logger.debug(`[AUTH_VERIFY_RES ${id}] User verified: ${res.data}`);
1887
+ }
1681
1888
  sendMessage(response);
1682
1889
  }
1683
1890
 
@@ -1727,6 +1934,47 @@ function ensureQueryLimit(query, defaultLimit = 50) {
1727
1934
  }
1728
1935
  return trimmedQuery;
1729
1936
  }
1937
+ function fixScalarSubqueries(query) {
1938
+ if (!query || query.trim().length === 0) {
1939
+ return query;
1940
+ }
1941
+ let modifiedQuery = query;
1942
+ let hasChanges = false;
1943
+ const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
1944
+ const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
1945
+ for (let i = matches.length - 1; i >= 0; i--) {
1946
+ const match = matches[i];
1947
+ const startPos = match.index + match[0].length - "SELECT ".length;
1948
+ let parenDepth = 1;
1949
+ let endPos = startPos;
1950
+ let foundEnd = false;
1951
+ for (let j = startPos; j < modifiedQuery.length; j++) {
1952
+ const char = modifiedQuery[j];
1953
+ if (char === "(") parenDepth++;
1954
+ if (char === ")") {
1955
+ parenDepth--;
1956
+ if (parenDepth === 0) {
1957
+ endPos = j;
1958
+ foundEnd = true;
1959
+ break;
1960
+ }
1961
+ }
1962
+ }
1963
+ if (!foundEnd) continue;
1964
+ const subquery = modifiedQuery.substring(startPos, endPos);
1965
+ if (/\bLIMIT\s+\d+/i.test(subquery)) {
1966
+ continue;
1967
+ }
1968
+ const fixedSubquery = subquery.trim() + " LIMIT 1";
1969
+ modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
1970
+ hasChanges = true;
1971
+ console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
1972
+ }
1973
+ if (hasChanges) {
1974
+ console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
1975
+ }
1976
+ return modifiedQuery;
1977
+ }
1730
1978
 
1731
1979
  // src/userResponse/schema.ts
1732
1980
  import path2 from "path";
@@ -1889,7 +2137,8 @@ var PromptLoader = class {
1889
2137
  "single-component",
1890
2138
  "mutli-component",
1891
2139
  "actions",
1892
- "container-metadata"
2140
+ "container-metadata",
2141
+ "text-response"
1893
2142
  ];
1894
2143
  for (const promptType of promptTypes) {
1895
2144
  try {
@@ -2241,7 +2490,9 @@ var BaseLLM = class {
2241
2490
  needsMultipleComponents: result.needsMultipleComponents || false
2242
2491
  };
2243
2492
  } catch (error) {
2244
- console.error("Error classifying user question:", error);
2493
+ const errorMsg = error instanceof Error ? error.message : String(error);
2494
+ logger.error(`[${this.getProviderName()}] Error classifying user question: ${errorMsg}`);
2495
+ logger.debug(`[${this.getProviderName()}] Classification error details:`, error);
2245
2496
  throw error;
2246
2497
  }
2247
2498
  }
@@ -2279,6 +2530,7 @@ var BaseLLM = class {
2279
2530
  );
2280
2531
  const props = result.props || originalProps;
2281
2532
  if (props && props.query) {
2533
+ props.query = fixScalarSubqueries(props.query);
2282
2534
  props.query = ensureQueryLimit(props.query, this.defaultLimit);
2283
2535
  }
2284
2536
  if (props && props.query) {
@@ -2305,7 +2557,9 @@ var BaseLLM = class {
2305
2557
  modifications: result.modifications || []
2306
2558
  };
2307
2559
  } catch (error) {
2308
- console.error(`Error validating/modifying props with ${this.getProviderName()}:`, error);
2560
+ const errorMsg = error instanceof Error ? error.message : String(error);
2561
+ logger.error(`[${this.getProviderName()}] Error validating/modifying props: ${errorMsg}`);
2562
+ logger.debug(`[${this.getProviderName()}] Props validation error details:`, error);
2309
2563
  throw error;
2310
2564
  }
2311
2565
  }
@@ -2425,7 +2679,9 @@ var BaseLLM = class {
2425
2679
  isGenerated: true
2426
2680
  };
2427
2681
  } catch (error) {
2428
- console.error("Error generating analytical component:", error);
2682
+ const errorMsg = error instanceof Error ? error.message : String(error);
2683
+ logger.error(`[${this.getProviderName()}] Error generating analytical component: ${errorMsg}`);
2684
+ logger.debug(`[${this.getProviderName()}] Analytical component generation error details:`, error);
2429
2685
  throw error;
2430
2686
  }
2431
2687
  }
@@ -2467,7 +2723,9 @@ var BaseLLM = class {
2467
2723
  description: result.description || `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
2468
2724
  };
2469
2725
  } catch (error) {
2470
- console.error("Error generating container metadata:", error);
2726
+ const errorMsg = error instanceof Error ? error.message : String(error);
2727
+ logger.error(`[${this.getProviderName()}] Error generating container metadata: ${errorMsg}`);
2728
+ logger.debug(`[${this.getProviderName()}] Container metadata error details:`, error);
2471
2729
  return {
2472
2730
  title: `${userPrompt} - Dashboard`,
2473
2731
  description: `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
@@ -2519,24 +2777,24 @@ var BaseLLM = class {
2519
2777
  component = components[componentIndex - 1];
2520
2778
  }
2521
2779
  const matchedMsg = `${this.getProviderName()} matched component: ${component?.name || "None"}`;
2522
- console.log("\u2713", matchedMsg);
2780
+ logger.info(`[${this.getProviderName()}] \u2713 ${matchedMsg}`);
2523
2781
  logCollector?.info(matchedMsg);
2524
2782
  if (result.alternativeMatches && result.alternativeMatches.length > 0) {
2525
- console.log(" Alternative matches:");
2783
+ logger.debug(`[${this.getProviderName()}] Alternative matches found: ${result.alternativeMatches.length}`);
2526
2784
  const altMatches = result.alternativeMatches.map(
2527
2785
  (alt) => `${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`
2528
2786
  ).join(" | ");
2529
2787
  logCollector?.info(`Alternative matches: ${altMatches}`);
2530
2788
  result.alternativeMatches.forEach((alt) => {
2531
- console.log(` - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
2789
+ logger.debug(`[${this.getProviderName()}] - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
2532
2790
  });
2533
2791
  }
2534
2792
  if (!component) {
2535
2793
  const noMatchMsg = `No matching component found (confidence: ${confidence}%)`;
2536
- console.log("\u2717", noMatchMsg);
2794
+ logger.warn(`[${this.getProviderName()}] \u2717 ${noMatchMsg}`);
2537
2795
  logCollector?.warn(noMatchMsg);
2538
2796
  const genMsg = "Attempting to match component from analytical question...";
2539
- console.log("\u2713", genMsg);
2797
+ logger.info(`[${this.getProviderName()}] \u2713 ${genMsg}`);
2540
2798
  logCollector?.info(genMsg);
2541
2799
  const generatedResult = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2542
2800
  if (generatedResult.component) {
@@ -2597,8 +2855,10 @@ var BaseLLM = class {
2597
2855
  confidence
2598
2856
  };
2599
2857
  } catch (error) {
2600
- console.error(`Error matching component with ${this.getProviderName()}:`, error);
2601
- logCollector?.error(`Error matching component: ${error.message}`);
2858
+ const errorMsg = error instanceof Error ? error.message : String(error);
2859
+ logger.error(`[${this.getProviderName()}] Error matching component: ${errorMsg}`);
2860
+ logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
2861
+ logCollector?.error(`Error matching component: ${errorMsg}`);
2602
2862
  throw error;
2603
2863
  }
2604
2864
  }
@@ -2629,7 +2889,9 @@ var BaseLLM = class {
2629
2889
  isGenerated: true
2630
2890
  };
2631
2891
  } catch (error) {
2632
- console.error("Error matching multiple analytical components:", error);
2892
+ const errorMsg = error instanceof Error ? error.message : String(error);
2893
+ logger.error(`[${this.getProviderName()}] Error matching multiple analytical components: ${errorMsg}`);
2894
+ logger.debug(`[${this.getProviderName()}] Multiple components matching error details:`, error);
2633
2895
  return {
2634
2896
  components: [],
2635
2897
  reasoning: "Error occurred while matching components",
@@ -2708,17 +2970,91 @@ var BaseLLM = class {
2708
2970
  isGenerated: true
2709
2971
  };
2710
2972
  } catch (error) {
2711
- console.error("Error generating multi-component response:", error);
2973
+ const errorMsg = error instanceof Error ? error.message : String(error);
2974
+ logger.error(`[${this.getProviderName()}] Error generating multi-component response: ${errorMsg}`);
2975
+ logger.debug(`[${this.getProviderName()}] Multi-component response error details:`, error);
2712
2976
  throw error;
2713
2977
  }
2714
2978
  }
2715
2979
  /**
2716
- * Main orchestration function that classifies question and routes to appropriate handler
2717
- * This is the NEW recommended entry point for handling user requests
2718
- * ALWAYS returns a SINGLE component (wraps multiple in MultiComponentContainer)
2980
+ * Generate text-based response for user question
2981
+ * This provides conversational text responses instead of component generation
2982
+ */
2983
+ async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory) {
2984
+ const errors = [];
2985
+ logger.debug(`[${this.getProviderName()}] Starting text response generation`);
2986
+ logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
2987
+ try {
2988
+ const prompts = await promptLoader.loadPrompts("text-response", {
2989
+ USER_PROMPT: userPrompt,
2990
+ CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
2991
+ });
2992
+ logger.debug(`[${this.getProviderName()}] Loaded text-response prompts`);
2993
+ logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
2994
+ logCollector?.info("Generating text response...");
2995
+ const result = await LLM.stream(
2996
+ {
2997
+ sys: prompts.system,
2998
+ user: prompts.user
2999
+ },
3000
+ {
3001
+ model: this.model,
3002
+ maxTokens: 1e3,
3003
+ temperature: 0.7,
3004
+ apiKey: this.getApiKey(apiKey)
3005
+ },
3006
+ true
3007
+ // Parse as JSON
3008
+ );
3009
+ logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3010
+ logger.debug(`[${this.getProviderName()}] Response type: ${result.responseType}, Confidence: ${result.confidence}`);
3011
+ logCollector?.info(`Text response: ${result.text?.substring(0, 100)}${result.text?.length > 100 ? "..." : ""}`);
3012
+ logCollector?.logExplanation(
3013
+ "Text response generated",
3014
+ result.reasoning || "Generated text response for user question",
3015
+ {
3016
+ responseType: result.responseType,
3017
+ confidence: result.confidence,
3018
+ textLength: result.text?.length || 0
3019
+ }
3020
+ );
3021
+ return {
3022
+ success: true,
3023
+ data: {
3024
+ text: result.text || "I apologize, but I was unable to generate a response.",
3025
+ responseType: result.responseType || "answer",
3026
+ confidence: result.confidence || 0.5,
3027
+ reasoning: result.reasoning || "No reasoning provided"
3028
+ },
3029
+ errors: []
3030
+ };
3031
+ } catch (error) {
3032
+ const errorMsg = error instanceof Error ? error.message : String(error);
3033
+ logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
3034
+ logger.debug(`[${this.getProviderName()}] Text response generation error details:`, error);
3035
+ logCollector?.error(`Error generating text response: ${errorMsg}`);
3036
+ errors.push(errorMsg);
3037
+ return {
3038
+ success: false,
3039
+ errors,
3040
+ data: {
3041
+ text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
3042
+ responseType: "error",
3043
+ confidence: 0,
3044
+ reasoning: `Error: ${errorMsg}`
3045
+ }
3046
+ };
3047
+ }
3048
+ }
3049
+ /**
3050
+ * Generate component response for user question
3051
+ * This provides conversational component suggestions based on user question
3052
+ * Supports component generation and matching
2719
3053
  */
2720
- async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory) {
3054
+ async generateComponentResponse(userPrompt, components, apiKey, logCollector, conversationHistory) {
3055
+ const errors = [];
2721
3056
  try {
3057
+ logger.info(`[${this.getProviderName()}] Using component response mode`);
2722
3058
  const classifyMsg = "Classifying user question...";
2723
3059
  logCollector?.info(classifyMsg);
2724
3060
  const classification = await this.classifyUserQuestion(userPrompt, apiKey, logCollector, conversationHistory);
@@ -2755,16 +3091,20 @@ var BaseLLM = class {
2755
3091
  logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
2756
3092
  }
2757
3093
  }
2758
- console.log("matched components: after multi comp", matchedComponents.length);
3094
+ logger.debug(`[${this.getProviderName()}] Matched ${matchedComponents.length} components for multi-component container`);
2759
3095
  if (matchedComponents.length === 0) {
2760
3096
  return {
2761
- component: null,
2762
- reasoning: "Failed to match any components for the requested visualization types",
2763
- method: "classification-multi-failed",
2764
- questionType: classification.questionType,
2765
- needsMultipleComponents: true,
2766
- propsModified: false,
2767
- queryModified: false
3097
+ success: true,
3098
+ data: {
3099
+ component: null,
3100
+ reasoning: "Failed to match any components for the requested visualization types",
3101
+ method: "classification-multi-failed",
3102
+ questionType: classification.questionType,
3103
+ needsMultipleComponents: true,
3104
+ propsModified: false,
3105
+ queryModified: false
3106
+ },
3107
+ errors: []
2768
3108
  };
2769
3109
  }
2770
3110
  logCollector?.info("Generating container metadata...");
@@ -2794,38 +3134,50 @@ var BaseLLM = class {
2794
3134
  };
2795
3135
  logCollector?.info(`Created multi-component container with ${matchedComponents.length} components: "${containerMetadata.title}"`);
2796
3136
  return {
2797
- component: containerComponent,
2798
- reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
2799
- method: "classification-multi-generated",
2800
- questionType: classification.questionType,
2801
- needsMultipleComponents: true,
2802
- propsModified: false,
2803
- queryModified: false
3137
+ success: true,
3138
+ data: {
3139
+ component: containerComponent,
3140
+ reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
3141
+ method: "classification-multi-generated",
3142
+ questionType: classification.questionType,
3143
+ needsMultipleComponents: true,
3144
+ propsModified: false,
3145
+ queryModified: false
3146
+ },
3147
+ errors: []
2804
3148
  };
2805
3149
  } else if (classification.visualizations.length === 1) {
2806
3150
  const vizType = classification.visualizations[0];
2807
3151
  logCollector?.info(`Matching single component for type: ${vizType}`);
2808
3152
  const result = await this.generateAnalyticalComponent(userPrompt, components, vizType, apiKey, logCollector, conversationHistory);
2809
3153
  return {
2810
- component: result.component,
2811
- reasoning: result.reasoning,
2812
- method: "classification-generated",
2813
- questionType: classification.questionType,
2814
- needsMultipleComponents: false,
2815
- propsModified: false,
2816
- queryModified: false
3154
+ success: true,
3155
+ data: {
3156
+ component: result.component,
3157
+ reasoning: result.reasoning,
3158
+ method: "classification-generated",
3159
+ questionType: classification.questionType,
3160
+ needsMultipleComponents: false,
3161
+ propsModified: false,
3162
+ queryModified: false
3163
+ },
3164
+ errors: []
2817
3165
  };
2818
3166
  } else {
2819
3167
  logCollector?.info("No specific visualization type - matching from all components");
2820
3168
  const result = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2821
3169
  return {
2822
- component: result.component,
2823
- reasoning: result.reasoning,
2824
- method: "classification-generated-auto",
2825
- questionType: classification.questionType,
2826
- needsMultipleComponents: false,
2827
- propsModified: false,
2828
- queryModified: false
3170
+ success: true,
3171
+ data: {
3172
+ component: result.component,
3173
+ reasoning: result.reasoning,
3174
+ method: "classification-generated-auto",
3175
+ questionType: classification.questionType,
3176
+ needsMultipleComponents: false,
3177
+ propsModified: false,
3178
+ queryModified: false
3179
+ },
3180
+ errors: []
2829
3181
  };
2830
3182
  }
2831
3183
  } else if (classification.questionType === "data_modification" || classification.questionType === "general") {
@@ -2833,28 +3185,109 @@ var BaseLLM = class {
2833
3185
  logCollector?.info(matchMsg);
2834
3186
  const matchResult = await this.matchComponent(userPrompt, components, apiKey, logCollector, conversationHistory);
2835
3187
  return {
2836
- component: matchResult.component,
2837
- reasoning: matchResult.reasoning,
2838
- method: "classification-matched",
2839
- questionType: classification.questionType,
2840
- needsMultipleComponents: false,
2841
- propsModified: matchResult.propsModified,
2842
- queryModified: matchResult.queryModified
3188
+ success: true,
3189
+ data: {
3190
+ component: matchResult.component,
3191
+ reasoning: matchResult.reasoning,
3192
+ method: "classification-matched",
3193
+ questionType: classification.questionType,
3194
+ needsMultipleComponents: false,
3195
+ propsModified: matchResult.propsModified,
3196
+ queryModified: matchResult.queryModified
3197
+ },
3198
+ errors: []
2843
3199
  };
2844
3200
  } else {
2845
3201
  logCollector?.info("General question - no component needed");
2846
3202
  return {
2847
- component: null,
2848
- reasoning: "General question - no component needed",
2849
- method: "classification-general",
2850
- questionType: classification.questionType,
2851
- needsMultipleComponents: false
3203
+ success: true,
3204
+ data: {
3205
+ component: null,
3206
+ reasoning: "General question - no component needed",
3207
+ method: "classification-general",
3208
+ questionType: classification.questionType,
3209
+ needsMultipleComponents: false,
3210
+ propsModified: false,
3211
+ queryModified: false
3212
+ },
3213
+ errors: []
2852
3214
  };
2853
3215
  }
2854
3216
  } catch (error) {
2855
- logCollector?.error(`Error handling user request: ${error.message}`);
2856
- throw error;
3217
+ const errorMsg = error instanceof Error ? error.message : String(error);
3218
+ logger.error(`[${this.getProviderName()}] Error generating component response: ${errorMsg}`);
3219
+ logger.debug(`[${this.getProviderName()}] Component response generation error details:`, error);
3220
+ logCollector?.error(`Error generating component response: ${errorMsg}`);
3221
+ errors.push(errorMsg);
3222
+ return {
3223
+ success: false,
3224
+ errors,
3225
+ data: void 0
3226
+ };
3227
+ }
3228
+ }
3229
+ /**
3230
+ * Main orchestration function that classifies question and routes to appropriate handler
3231
+ * This is the NEW recommended entry point for handling user requests
3232
+ * Supports both component generation and text response modes
3233
+ *
3234
+ * @param responseMode - 'component' for component generation (default), 'text' for text responses
3235
+ */
3236
+ async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") {
3237
+ logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
3238
+ if (responseMode === "text") {
3239
+ logger.info(`[${this.getProviderName()}] Using text response mode`);
3240
+ logCollector?.info("Generating text response...");
3241
+ const textResponse = await this.generateTextResponse(
3242
+ userPrompt,
3243
+ apiKey,
3244
+ logCollector,
3245
+ conversationHistory
3246
+ );
3247
+ if (!textResponse.success) {
3248
+ logger.error(`[${this.getProviderName()}] Text response generation failed`);
3249
+ return textResponse;
3250
+ }
3251
+ logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3252
+ return {
3253
+ success: true,
3254
+ data: {
3255
+ textResponse: textResponse.data.text,
3256
+ text: textResponse.data.text,
3257
+ responseType: textResponse.data.responseType,
3258
+ confidence: textResponse.data.confidence,
3259
+ reasoning: textResponse.data.reasoning,
3260
+ method: `${this.getProviderName()}-text-response`
3261
+ },
3262
+ errors: []
3263
+ };
2857
3264
  }
3265
+ const componentResponse = await this.generateComponentResponse(
3266
+ userPrompt,
3267
+ components,
3268
+ apiKey,
3269
+ logCollector,
3270
+ conversationHistory
3271
+ );
3272
+ if (!componentResponse.success) {
3273
+ logger.error(`[${this.getProviderName()}] Component response generation failed`);
3274
+ return componentResponse;
3275
+ }
3276
+ logger.info(`[${this.getProviderName()}] Component response generated successfully`);
3277
+ return {
3278
+ success: true,
3279
+ data: {
3280
+ component: componentResponse.data.component,
3281
+ reasoning: componentResponse.data.reasoning,
3282
+ method: componentResponse.data.method,
3283
+ // Preserve the original method name
3284
+ questionType: componentResponse.data.questionType,
3285
+ needsMultipleComponents: componentResponse.data.needsMultipleComponents,
3286
+ propsModified: componentResponse.data.propsModified,
3287
+ queryModified: componentResponse.data.queryModified
3288
+ },
3289
+ errors: []
3290
+ };
2858
3291
  }
2859
3292
  /**
2860
3293
  * Generate next questions that the user might ask based on the original prompt and generated component
@@ -2900,8 +3333,10 @@ var BaseLLM = class {
2900
3333
  );
2901
3334
  return nextQuestions;
2902
3335
  } catch (error) {
2903
- console.error(`Error generating next questions with ${this.getProviderName()}:`, error);
2904
- logCollector?.error(`Error generating next questions: ${error.message}`);
3336
+ const errorMsg = error instanceof Error ? error.message : String(error);
3337
+ logger.error(`[${this.getProviderName()}] Error generating next questions: ${errorMsg}`);
3338
+ logger.debug(`[${this.getProviderName()}] Next questions generation error details:`, error);
3339
+ logCollector?.error(`Error generating next questions: ${errorMsg}`);
2905
3340
  return [];
2906
3341
  }
2907
3342
  }
@@ -2965,112 +3400,130 @@ function getLLMProviders() {
2965
3400
  return DEFAULT_PROVIDERS;
2966
3401
  }
2967
3402
  }
2968
- var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2969
- const msg = "Using Anthropic Claude matching method...";
2970
- console.log(msg);
3403
+ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
3404
+ logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
3405
+ logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
3406
+ const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
2971
3407
  logCollector?.info(msg);
2972
- if (components.length === 0) {
3408
+ if (responseMode === "component" && components.length === 0) {
2973
3409
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3410
+ logger.error("[useAnthropicMethod] No components available");
2974
3411
  logCollector?.error(emptyMsg);
2975
- return { success: false, reason: emptyMsg };
2976
- }
2977
- try {
2978
- const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
2979
- return { success: true, data: matchResult };
2980
- } catch (error) {
2981
- const errorMsg = error instanceof Error ? error.message : String(error);
2982
- logCollector?.error(`Anthropic method failed: ${errorMsg}`);
2983
- throw error;
3412
+ return { success: false, errors: [emptyMsg] };
2984
3413
  }
3414
+ logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
3415
+ const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
3416
+ logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
3417
+ return matchResult;
2985
3418
  };
2986
- var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2987
- const msg = "Using Groq LLM matching method...";
2988
- console.log(msg);
3419
+ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
3420
+ logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
3421
+ logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
3422
+ const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
3423
+ logger.info(msg);
2989
3424
  logCollector?.info(msg);
2990
- if (components.length === 0) {
3425
+ if (responseMode === "component" && components.length === 0) {
2991
3426
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3427
+ logger.error("[useGroqMethod] No components available");
2992
3428
  logCollector?.error(emptyMsg);
2993
- return { success: false, reason: emptyMsg };
2994
- }
2995
- try {
2996
- const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
2997
- return { success: true, data: matchResult };
2998
- } catch (error) {
2999
- const errorMsg = error instanceof Error ? error.message : String(error);
3000
- logCollector?.error(`Groq method failed: ${errorMsg}`);
3001
- throw error;
3429
+ return { success: false, errors: [emptyMsg] };
3002
3430
  }
3431
+ logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
3432
+ const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
3433
+ logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
3434
+ return matchResult;
3003
3435
  };
3004
3436
  var getUserResponseFromCache = async (prompt) => {
3005
3437
  return false;
3006
3438
  };
3007
3439
  var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
3440
+ const responseMode = "component";
3441
+ logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
3442
+ logger.debug("[get_user_response] Checking cache for existing response");
3008
3443
  const userResponse = await getUserResponseFromCache(prompt);
3009
3444
  if (userResponse) {
3445
+ logger.info("[get_user_response] User response found in cache - returning cached result");
3010
3446
  logCollector?.info("User response found in cache");
3011
3447
  return {
3012
3448
  success: true,
3013
- data: userResponse
3449
+ data: userResponse,
3450
+ errors: []
3014
3451
  };
3015
3452
  }
3453
+ logger.debug("[get_user_response] No cached response found, proceeding with LLM providers");
3016
3454
  const providers = llmProviders || getLLMProviders();
3017
3455
  const errors = [];
3018
3456
  const providerOrder = providers.join(", ");
3019
3457
  logCollector?.info(`LLM Provider order: [${providerOrder}]`);
3020
3458
  if (conversationHistory && conversationHistory.length > 0) {
3021
- logCollector?.info(`Using conversation history with ${conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length} previous exchanges`);
3459
+ const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
3460
+ logger.debug(`[get_user_response] Using conversation history with ${exchangeCount} previous exchanges`);
3461
+ logCollector?.info(`Using conversation history with ${exchangeCount} previous exchanges`);
3462
+ } else {
3463
+ logger.debug("[get_user_response] No conversation history available");
3022
3464
  }
3023
3465
  for (let i = 0; i < providers.length; i++) {
3024
3466
  const provider = providers[i];
3025
3467
  const isLastProvider = i === providers.length - 1;
3026
- try {
3027
- const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
3028
- logCollector?.info(attemptMsg);
3029
- let result;
3030
- if (provider === "anthropic") {
3031
- result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory);
3032
- } else if (provider === "groq") {
3033
- result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory);
3034
- } else {
3035
- continue;
3036
- }
3037
- if (result.success) {
3038
- const successMsg = `Success with provider: ${provider}`;
3039
- logCollector?.info(successMsg);
3040
- return result;
3041
- } else {
3042
- errors.push({ provider, error: result.reason || "Unknown error" });
3043
- const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.reason}`;
3044
- logCollector?.warn(warnMsg);
3045
- }
3046
- } catch (error) {
3047
- const errorMessage = error.message;
3048
- errors.push({ provider, error: errorMessage });
3049
- const errorMsg = `Provider ${provider} failed: ${errorMessage}`;
3050
- logCollector?.error(errorMsg);
3468
+ const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
3469
+ logCollector?.info(attemptMsg);
3470
+ let result;
3471
+ if (provider === "anthropic") {
3472
+ result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode);
3473
+ } else if (provider === "groq") {
3474
+ result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode);
3475
+ } else {
3476
+ logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
3477
+ errors.push(`Unknown provider: ${provider}`);
3478
+ continue;
3479
+ }
3480
+ if (result.success) {
3481
+ const successMsg = `Success with provider: ${provider} result: ${JSON.stringify(result)}`;
3482
+ logger.info(`${successMsg}`);
3483
+ logCollector?.info(successMsg);
3484
+ return result;
3485
+ } else {
3486
+ const providerErrors = result.errors.map((err) => `${provider}: ${err}`);
3487
+ errors.push(...providerErrors);
3488
+ const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.errors.join(", ")}`;
3489
+ logger.warn(`[get_user_response] ${warnMsg}`);
3490
+ logCollector?.warn(warnMsg);
3051
3491
  if (!isLastProvider) {
3052
3492
  const fallbackMsg = "Falling back to next provider...";
3493
+ logger.info(`[get_user_response] ${fallbackMsg}`);
3053
3494
  logCollector?.info(fallbackMsg);
3054
- continue;
3055
3495
  }
3056
3496
  }
3057
3497
  }
3058
- const errorSummary = errors.map((e) => `${e.provider}: ${e.error}`).join("; ");
3059
- const failureMsg = `All LLM providers failed. Errors: ${errorSummary}`;
3060
- logCollector?.error(failureMsg);
3498
+ const failureMsg = `All LLM providers failed`;
3499
+ logger.error(`[get_user_response] ${failureMsg}. Errors: ${errors.join("; ")}`);
3500
+ logCollector?.error(`${failureMsg}. Errors: ${errors.join("; ")}`);
3061
3501
  return {
3062
3502
  success: false,
3063
- reason: failureMsg
3503
+ errors
3064
3504
  };
3065
3505
  };
3066
3506
 
3067
3507
  // src/utils/log-collector.ts
3508
+ var LOG_LEVEL_PRIORITY2 = {
3509
+ errors: 0,
3510
+ warnings: 1,
3511
+ info: 2,
3512
+ verbose: 3
3513
+ };
3514
+ var MESSAGE_LEVEL_PRIORITY2 = {
3515
+ error: 0,
3516
+ warn: 1,
3517
+ info: 2,
3518
+ debug: 3
3519
+ };
3068
3520
  var UILogCollector = class {
3069
3521
  constructor(clientId, sendMessage, uiBlockId) {
3070
3522
  this.logs = [];
3071
3523
  this.uiBlockId = uiBlockId || null;
3072
3524
  this.clientId = clientId;
3073
3525
  this.sendMessage = sendMessage;
3526
+ this.currentLogLevel = logger.getLogLevel();
3074
3527
  }
3075
3528
  /**
3076
3529
  * Check if logging is enabled (uiBlockId is provided)
@@ -3078,10 +3531,22 @@ var UILogCollector = class {
3078
3531
  isEnabled() {
3079
3532
  return this.uiBlockId !== null;
3080
3533
  }
3534
+ /**
3535
+ * Check if a message should be logged based on current log level
3536
+ */
3537
+ shouldLog(messageLevel) {
3538
+ const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
3539
+ const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
3540
+ return messagePriority <= currentLevelPriority;
3541
+ }
3081
3542
  /**
3082
3543
  * Add a log entry with timestamp and immediately send to runtime
3544
+ * Only logs that pass the log level filter are captured and sent
3083
3545
  */
3084
3546
  addLog(level, message, type, data) {
3547
+ if (!this.shouldLog(level)) {
3548
+ return;
3549
+ }
3085
3550
  const log = {
3086
3551
  timestamp: Date.now(),
3087
3552
  level,
@@ -3091,7 +3556,20 @@ var UILogCollector = class {
3091
3556
  };
3092
3557
  this.logs.push(log);
3093
3558
  this.sendLogImmediately(log);
3094
- console.log("UILogCollector:", log);
3559
+ switch (level) {
3560
+ case "error":
3561
+ logger.error("UILogCollector:", log);
3562
+ break;
3563
+ case "warn":
3564
+ logger.warn("UILogCollector:", log);
3565
+ break;
3566
+ case "info":
3567
+ logger.info("UILogCollector:", log);
3568
+ break;
3569
+ case "debug":
3570
+ logger.debug("UILogCollector:", log);
3571
+ break;
3572
+ }
3095
3573
  }
3096
3574
  /**
3097
3575
  * Send a single log to runtime immediately
@@ -3222,102 +3700,131 @@ var CONTEXT_CONFIG = {
3222
3700
 
3223
3701
  // src/handlers/user-prompt-request.ts
3224
3702
  var processedMessageIds = /* @__PURE__ */ new Set();
3225
- async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3226
- try {
3227
- const userPromptRequest = UserPromptRequestMessageSchema.parse(data);
3228
- const { id, payload } = userPromptRequest;
3229
- const prompt = payload.prompt;
3230
- const SA_RUNTIME = payload.SA_RUNTIME;
3231
- const wsId = userPromptRequest.from.id || "unknown";
3232
- logger.info(`[REQUEST ${id}] Processing user prompt: "${prompt.substring(0, 50)}..."`);
3233
- if (processedMessageIds.has(id)) {
3234
- logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3235
- return;
3236
- }
3237
- processedMessageIds.add(id);
3238
- if (processedMessageIds.size > 100) {
3239
- const firstId = processedMessageIds.values().next().value;
3240
- if (firstId) {
3241
- processedMessageIds.delete(firstId);
3703
+ var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) => {
3704
+ const errors = [];
3705
+ logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
3706
+ const parseResult = UserPromptRequestMessageSchema.safeParse(data);
3707
+ if (!parseResult.success) {
3708
+ const zodError = parseResult.error;
3709
+ zodError.errors.forEach((err) => {
3710
+ errors.push(`${err.path.join(".")}: ${err.message}`);
3711
+ });
3712
+ return { success: false, errors };
3713
+ }
3714
+ const userPromptRequest = parseResult.data;
3715
+ const { id, payload } = userPromptRequest;
3716
+ const prompt = payload.prompt;
3717
+ const SA_RUNTIME = payload.SA_RUNTIME;
3718
+ const wsId = userPromptRequest.from.id || "unknown";
3719
+ logger.debug(`[REQUEST ${id}] Full request details - wsId: ${wsId}, prompt length: ${prompt.length}`);
3720
+ if (processedMessageIds.has(id)) {
3721
+ logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3722
+ }
3723
+ processedMessageIds.add(id);
3724
+ logger.debug(`[REQUEST ${id}] Message ID marked as processed (${processedMessageIds.size} total)`);
3725
+ if (processedMessageIds.size > 100) {
3726
+ const firstId = processedMessageIds.values().next().value;
3727
+ if (firstId) {
3728
+ processedMessageIds.delete(firstId);
3729
+ logger.debug(`[REQUEST ${id}] Cleaned up old message ID from cache`);
3730
+ }
3731
+ }
3732
+ if (!SA_RUNTIME) {
3733
+ errors.push("SA_RUNTIME is required");
3734
+ }
3735
+ const threadId = SA_RUNTIME?.threadId;
3736
+ const existingUiBlockId = SA_RUNTIME?.uiBlockId;
3737
+ if (!threadId) {
3738
+ errors.push("threadId in SA_RUNTIME is required");
3739
+ }
3740
+ if (!existingUiBlockId) {
3741
+ errors.push("uiBlockId in SA_RUNTIME is required");
3742
+ }
3743
+ if (!prompt) {
3744
+ errors.push("Prompt not found");
3745
+ }
3746
+ if (!components || components.length === 0) {
3747
+ errors.push("Components not found");
3748
+ }
3749
+ if (errors.length > 0) {
3750
+ return { success: false, errors, id, wsId };
3751
+ }
3752
+ const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
3753
+ const threadManager = ThreadManager.getInstance();
3754
+ let thread = threadManager.getThread(threadId);
3755
+ if (!thread) {
3756
+ thread = threadManager.createThread(threadId);
3757
+ logger.info(`Created new thread: ${threadId}`);
3758
+ }
3759
+ logCollector.info(`Starting user prompt request with ${components.length} components`);
3760
+ const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3761
+ logger.info("conversationHistory", conversationHistory);
3762
+ const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3763
+ logger.info("llm userResponse", userResponse);
3764
+ logCollector.info("User prompt request completed");
3765
+ const uiBlockId = existingUiBlockId;
3766
+ if (!userResponse.success) {
3767
+ logger.error(`User prompt request failed with errors: ${userResponse.errors.join(", ")}`);
3768
+ return {
3769
+ success: false,
3770
+ data: userResponse.data,
3771
+ errors: userResponse.errors,
3772
+ uiBlockId,
3773
+ threadId,
3774
+ id,
3775
+ wsId
3776
+ };
3777
+ }
3778
+ let component = null;
3779
+ let textResponse = null;
3780
+ if (userResponse.data) {
3781
+ if (typeof userResponse.data === "object") {
3782
+ if ("component" in userResponse.data) {
3783
+ component = userResponse.data.component;
3784
+ }
3785
+ if ("textResponse" in userResponse.data) {
3786
+ textResponse = userResponse.data.textResponse;
3242
3787
  }
3243
3788
  }
3244
- if (!SA_RUNTIME) {
3245
- sendDataResponse4(id, {
3246
- success: false,
3247
- error: "SA_RUNTIME is required"
3248
- }, sendMessage, wsId);
3249
- return;
3250
- }
3251
- const threadId = SA_RUNTIME.threadId;
3252
- const existingUiBlockId = SA_RUNTIME.uiBlockId;
3253
- if (!threadId) {
3254
- sendDataResponse4(id, {
3255
- success: false,
3256
- error: "threadId in SA_RUNTIME is required"
3257
- }, sendMessage, wsId);
3258
- return;
3259
- }
3260
- if (!existingUiBlockId) {
3261
- sendDataResponse4(id, {
3262
- success: false,
3263
- error: "uiBlockId in SA_RUNTIME is required"
3264
- }, sendMessage, wsId);
3265
- return;
3266
- }
3267
- const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
3268
- if (!prompt) {
3269
- sendDataResponse4(id, {
3270
- success: false,
3271
- error: "Prompt not found"
3272
- }, sendMessage, wsId);
3273
- return;
3274
- }
3275
- if (!components || components.length === 0) {
3276
- sendDataResponse4(id, {
3277
- success: false,
3278
- error: "Components not found"
3279
- }, sendMessage, wsId);
3280
- return;
3281
- }
3282
- const threadManager = ThreadManager.getInstance();
3283
- let thread = threadManager.getThread(threadId);
3284
- if (!thread) {
3285
- thread = threadManager.createThread(threadId);
3286
- logger.info(`Created new thread: ${threadId}`);
3287
- }
3288
- logCollector.info(`Starting user prompt request with ${components.length} components`);
3289
- const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3290
- logger.info("conversationHistory", conversationHistory);
3291
- const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3292
- logger.info("llm userResponse", userResponse);
3293
- logCollector.info("User prompt request completed");
3294
- if (userResponse.success && userResponse.data && typeof userResponse.data === "object" && "component" in userResponse.data) {
3295
- const component = userResponse.data.component;
3296
- const uiBlockId = existingUiBlockId;
3297
- const uiBlock = new UIBlock(
3298
- prompt,
3299
- {},
3300
- // componentData: initially empty, will be filled later
3301
- component || {},
3302
- // generatedComponentMetadata: full component object (ComponentSchema)
3303
- [],
3304
- // actions: empty initially
3305
- uiBlockId
3306
- );
3307
- thread.addUIBlock(uiBlock);
3308
- logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
3309
- sendDataResponse4(id, {
3310
- ...userResponse,
3311
- uiBlockId,
3312
- threadId
3313
- }, sendMessage, wsId);
3314
- } else {
3315
- sendDataResponse4(id, userResponse, sendMessage, wsId);
3316
- }
3317
- return;
3318
- } catch (error) {
3319
- logger.error("Failed to handle user prompt request:", error);
3320
3789
  }
3790
+ const uiBlock = new UIBlock(
3791
+ prompt,
3792
+ {},
3793
+ // componentData: initially empty, will be filled later
3794
+ component,
3795
+ // generatedComponentMetadata: full component object (ComponentSchema)
3796
+ [],
3797
+ // actions: empty initially
3798
+ uiBlockId,
3799
+ textResponse
3800
+ // textResponse: text response from LLM
3801
+ );
3802
+ thread.addUIBlock(uiBlock);
3803
+ logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
3804
+ return {
3805
+ success: userResponse.success,
3806
+ data: userResponse.data,
3807
+ errors: userResponse.errors,
3808
+ uiBlockId,
3809
+ threadId,
3810
+ id,
3811
+ wsId
3812
+ };
3813
+ };
3814
+ async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3815
+ const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders);
3816
+ sendDataResponse4(
3817
+ response.id || data.id,
3818
+ {
3819
+ success: response.success,
3820
+ errors: response.errors,
3821
+ data: response.data,
3822
+ uiBlockId: response.uiBlockId,
3823
+ threadId: response.threadId
3824
+ },
3825
+ sendMessage,
3826
+ response.wsId || data.from?.id
3827
+ );
3321
3828
  }
3322
3829
  function sendDataResponse4(id, res, sendMessage, clientId) {
3323
3830
  const response = {
@@ -3430,12 +3937,27 @@ function sendResponse(id, res, sendMessage, clientId) {
3430
3937
  // src/userResponse/next-questions.ts
3431
3938
  async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) {
3432
3939
  try {
3940
+ logger.debug("[generateNextQuestions] Starting next questions generation");
3941
+ logger.debug(`[generateNextQuestions] User prompt: "${originalUserPrompt?.substring(0, 50)}..."`);
3942
+ logger.debug(`[generateNextQuestions] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
3943
+ logger.debug(`[generateNextQuestions] Component data available: ${componentData ? "yes" : "no"}`);
3433
3944
  const providers = llmProviders || ["anthropic"];
3434
- for (const provider of providers) {
3945
+ logger.info(`[generateNextQuestions] Using LLM providers: [${providers.join(", ")}]`);
3946
+ if (conversationHistory && conversationHistory.length > 0) {
3947
+ const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
3948
+ logger.debug(`[generateNextQuestions] Using conversation history with ${exchangeCount} previous exchanges`);
3949
+ } else {
3950
+ logger.debug("[generateNextQuestions] No conversation history available");
3951
+ }
3952
+ for (let i = 0; i < providers.length; i++) {
3953
+ const provider = providers[i];
3954
+ const isLastProvider = i === providers.length - 1;
3435
3955
  try {
3436
- logger.info(`Generating next questions using provider: ${provider}`);
3956
+ logger.info(`[generateNextQuestions] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
3957
+ logCollector?.info(`Generating questions with ${provider}...`);
3437
3958
  let result = [];
3438
3959
  if (provider === "groq") {
3960
+ logger.debug("[generateNextQuestions] Using Groq LLM for next questions");
3439
3961
  result = await groqLLM.generateNextQuestions(
3440
3962
  originalUserPrompt,
3441
3963
  component,
@@ -3445,6 +3967,7 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3445
3967
  conversationHistory
3446
3968
  );
3447
3969
  } else {
3970
+ logger.debug("[generateNextQuestions] Using Anthropic LLM for next questions");
3448
3971
  result = await anthropicLLM.generateNextQuestions(
3449
3972
  originalUserPrompt,
3450
3973
  component,
@@ -3455,21 +3978,39 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3455
3978
  );
3456
3979
  }
3457
3980
  if (result && result.length > 0) {
3458
- logger.info(`Successfully generated ${result.length} questions with ${provider}`);
3981
+ logger.info(`[generateNextQuestions] Successfully generated ${result.length} questions with ${provider}`);
3982
+ logger.debug(`[generateNextQuestions] Questions: ${JSON.stringify(result)}`);
3983
+ logCollector?.info(`Generated ${result.length} follow-up questions`);
3459
3984
  return result;
3460
3985
  }
3461
- logger.warn(`No questions generated from ${provider}, trying next provider...`);
3986
+ const warnMsg = `No questions generated from ${provider}${!isLastProvider ? ", trying next provider..." : ""}`;
3987
+ logger.warn(`[generateNextQuestions] ${warnMsg}`);
3988
+ if (!isLastProvider) {
3989
+ logCollector?.warn(warnMsg);
3990
+ }
3462
3991
  } catch (providerError) {
3463
- logger.warn(`Provider ${provider} failed:`, providerError);
3464
- logCollector?.warn(`Provider ${provider} failed, trying next provider...`);
3992
+ const errorMsg = providerError instanceof Error ? providerError.message : String(providerError);
3993
+ logger.error(`[generateNextQuestions] Provider ${provider} failed: ${errorMsg}`);
3994
+ logger.debug(`[generateNextQuestions] Provider error details:`, providerError);
3995
+ if (!isLastProvider) {
3996
+ const fallbackMsg = `Provider ${provider} failed, trying next provider...`;
3997
+ logger.info(`[generateNextQuestions] ${fallbackMsg}`);
3998
+ logCollector?.warn(fallbackMsg);
3999
+ } else {
4000
+ logCollector?.error(`Failed to generate questions with ${provider}`);
4001
+ }
3465
4002
  continue;
3466
4003
  }
3467
4004
  }
3468
- logger.warn("All providers failed or returned no questions");
4005
+ logger.warn("[generateNextQuestions] All providers failed or returned no questions");
4006
+ logCollector?.warn("Unable to generate follow-up questions");
3469
4007
  return [];
3470
4008
  } catch (error) {
3471
- logger.error("Error generating next questions:", error);
3472
- logCollector?.error(`Error generating next questions: ${error.message}`);
4009
+ const errorMsg = error instanceof Error ? error.message : String(error);
4010
+ const errorStack = error instanceof Error ? error.stack : void 0;
4011
+ logger.error(`[generateNextQuestions] Error generating next questions: ${errorMsg}`);
4012
+ logger.debug("[generateNextQuestions] Error stack trace:", errorStack);
4013
+ logCollector?.error(`Error generating next questions: ${errorMsg}`);
3473
4014
  return [];
3474
4015
  }
3475
4016
  }
@@ -3477,11 +4018,15 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3477
4018
  // src/handlers/actions-request.ts
3478
4019
  async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3479
4020
  try {
4021
+ logger.debug("[ACTIONS_REQ] Parsing incoming actions request");
3480
4022
  const actionsRequest = ActionsRequestMessageSchema.parse(data);
3481
4023
  const { id, payload } = actionsRequest;
3482
4024
  const { SA_RUNTIME } = payload;
3483
4025
  const wsId = actionsRequest.from.id || "unknown";
4026
+ logger.info(`[ACTIONS_REQ ${id}] Processing actions request from client: ${wsId}`);
4027
+ logger.debug(`[ACTIONS_REQ ${id}] Request payload:`, JSON.stringify(payload, null, 2).substring(0, 200));
3484
4028
  if (!SA_RUNTIME) {
4029
+ logger.error(`[ACTIONS_REQ ${id}] SA_RUNTIME missing from request`);
3485
4030
  sendResponse2(id, {
3486
4031
  success: false,
3487
4032
  error: "SA_RUNTIME with threadId and uiBlockId is required"
@@ -3490,31 +4035,54 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3490
4035
  }
3491
4036
  const uiBlockId = SA_RUNTIME.uiBlockId;
3492
4037
  const threadId = SA_RUNTIME.threadId;
4038
+ logger.debug(`[ACTIONS_REQ ${id}] SA_RUNTIME validated - threadId: ${threadId}, uiBlockId: ${uiBlockId}`);
4039
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving thread: ${threadId}`);
3493
4040
  const threadManager = ThreadManager.getInstance();
3494
4041
  const thread = threadManager.getThread(threadId);
3495
4042
  if (!thread) {
4043
+ logger.error(`[ACTIONS_REQ ${id}] Thread '${threadId}' not found`);
3496
4044
  sendResponse2(id, {
3497
4045
  success: false,
3498
4046
  error: `Thread '${threadId}' not found`
3499
4047
  }, sendMessage, wsId);
3500
4048
  return;
3501
4049
  }
4050
+ logger.debug(`[ACTIONS_REQ ${id}] Thread found with ${thread.getUIBlocks().length} UIBlocks`);
4051
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving UIBlock: ${uiBlockId}`);
3502
4052
  const uiBlock = thread.getUIBlock(uiBlockId);
3503
4053
  if (!uiBlock) {
4054
+ logger.error(`[ACTIONS_REQ ${id}] UIBlock '${uiBlockId}' not found in thread '${threadId}'`);
3504
4055
  sendResponse2(id, {
3505
4056
  success: false,
3506
4057
  error: `UIBlock '${uiBlockId}' not found in thread '${threadId}'`
3507
4058
  }, sendMessage, wsId);
3508
4059
  return;
3509
4060
  }
4061
+ logger.info(`[ACTIONS_REQ ${id}] UIBlock retrieved successfully`);
4062
+ logger.debug(`[ACTIONS_REQ ${id}] Creating UILogCollector for uiBlockId: ${uiBlockId}`);
3510
4063
  const logCollector = new UILogCollector(wsId, sendMessage, uiBlockId);
4064
+ logger.info(`[ACTIONS_REQ ${id}] UILogCollector initialized`);
4065
+ logger.debug(`[ACTIONS_REQ ${id}] Extracting data from UIBlock`);
3511
4066
  const userQuestion = uiBlock.getUserQuestion();
3512
4067
  const component = uiBlock.getComponentMetadata();
3513
4068
  const componentData = uiBlock.getComponentData();
4069
+ logger.debug(`[ACTIONS_REQ ${id}] User question: "${userQuestion?.substring(0, 50)}..."`);
4070
+ logger.debug(`[ACTIONS_REQ ${id}] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
4071
+ logger.debug(`[ACTIONS_REQ ${id}] Component data available: ${componentData ? "yes" : "no"}`);
4072
+ logger.debug(`[ACTIONS_REQ ${id}] Extracting conversation history (max ${CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS} blocks)`);
3514
4073
  const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, uiBlockId);
4074
+ const historyLineCount = conversationHistory.split("\n").filter((l) => l.trim()).length;
4075
+ logger.info(`[ACTIONS_REQ ${id}] Conversation history extracted: ${historyLineCount} lines`);
4076
+ logger.debug(`[ACTIONS_REQ ${id}] Conversation history preview:
4077
+ ${conversationHistory.substring(0, 200)}...`);
3515
4078
  logCollector.info(`Generating actions for UIBlock: ${uiBlockId}`);
3516
- logger.info(`Generating actions for component: ${component?.name || "unknown"}`);
4079
+ logger.info(`[ACTIONS_REQ ${id}] Generating actions for component: ${component?.name || "unknown"}`);
4080
+ logger.debug(`[ACTIONS_REQ ${id}] Checking if actions are already cached`);
4081
+ const startTime = Date.now();
3517
4082
  const actions = await uiBlock.getOrFetchActions(async () => {
4083
+ logger.info(`[ACTIONS_REQ ${id}] Actions not cached, generating new actions...`);
4084
+ logCollector.info("Generating follow-up questions...");
4085
+ logger.info(`[ACTIONS_REQ ${id}] Starting next questions generation with ${llmProviders?.join(", ") || "default"} providers`);
3518
4086
  const nextQuestions = await generateNextQuestions(
3519
4087
  userQuestion,
3520
4088
  component,
@@ -3525,14 +4093,28 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3525
4093
  logCollector,
3526
4094
  conversationHistory
3527
4095
  );
3528
- return nextQuestions.map((question, index) => ({
4096
+ logger.info(`[ACTIONS_REQ ${id}] Generated ${nextQuestions.length} questions`);
4097
+ logger.debug(`[ACTIONS_REQ ${id}] Questions: ${JSON.stringify(nextQuestions)}`);
4098
+ logger.debug(`[ACTIONS_REQ ${id}] Converting questions to actions format`);
4099
+ const convertedActions = nextQuestions.map((question, index) => ({
3529
4100
  id: `action_${index}_${Date.now()}`,
3530
4101
  name: question,
3531
4102
  type: "next_question",
3532
4103
  question
3533
4104
  }));
4105
+ logger.debug(`[ACTIONS_REQ ${id}] Converted ${convertedActions.length} actions`);
4106
+ return convertedActions;
3534
4107
  });
3535
- logCollector.info(`Generated ${actions.length} actions successfully`);
4108
+ const processingTime = Date.now() - startTime;
4109
+ logger.info(`[ACTIONS_REQ ${id}] Actions retrieved in ${processingTime}ms - ${actions.length} actions total`);
4110
+ if (actions.length > 0) {
4111
+ logCollector.info(`Generated ${actions.length} follow-up questions successfully`);
4112
+ logger.debug(`[ACTIONS_REQ ${id}] Actions: ${actions.map((a) => a.name).join(", ")}`);
4113
+ } else {
4114
+ logger.warn(`[ACTIONS_REQ ${id}] No actions generated`);
4115
+ logCollector.warn("No follow-up questions could be generated");
4116
+ }
4117
+ logger.debug(`[ACTIONS_REQ ${id}] Sending successful response to client`);
3536
4118
  sendResponse2(id, {
3537
4119
  success: true,
3538
4120
  data: {
@@ -3543,12 +4125,26 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3543
4125
  threadId
3544
4126
  }
3545
4127
  }, sendMessage, wsId);
4128
+ logger.info(`[ACTIONS_REQ ${id}] \u2713 Actions request completed successfully`);
3546
4129
  } catch (error) {
3547
- logger.error("Failed to handle actions request:", error);
4130
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
4131
+ const errorStack = error instanceof Error ? error.stack : void 0;
4132
+ logger.error(`[ACTIONS_REQ] Failed to handle actions request: ${errorMessage}`);
4133
+ logger.debug(`[ACTIONS_REQ] Error stack trace:`, errorStack);
4134
+ try {
4135
+ const parsedData = data;
4136
+ if (parsedData?.id && parsedData?.from?.id) {
4137
+ const logCollector = parsedData?.payload?.SA_RUNTIME?.uiBlockId ? new UILogCollector(parsedData.from.id, sendMessage, parsedData.payload.SA_RUNTIME.uiBlockId) : void 0;
4138
+ logCollector?.error(`Failed to generate actions: ${errorMessage}`);
4139
+ }
4140
+ } catch (logError) {
4141
+ logger.debug("[ACTIONS_REQ] Failed to send error logs to UI:", logError);
4142
+ }
3548
4143
  sendResponse2(null, {
3549
4144
  success: false,
3550
- error: error instanceof Error ? error.message : "Unknown error occurred"
4145
+ error: errorMessage
3551
4146
  }, sendMessage);
4147
+ logger.info("[ACTIONS_REQ] \u2717 Actions request completed with errors");
3552
4148
  }
3553
4149
  }
3554
4150
  function sendResponse2(id, res, sendMessage, clientId) {
@@ -3564,6 +4160,11 @@ function sendResponse2(id, res, sendMessage, clientId) {
3564
4160
  ...res
3565
4161
  }
3566
4162
  };
4163
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.success ? "successful" : "failed"} response to client`);
4164
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Response payload size: ${JSON.stringify(response).length} bytes`);
4165
+ if (res.data?.actions) {
4166
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.data.actions.length} actions`);
4167
+ }
3567
4168
  sendMessage(response);
3568
4169
  }
3569
4170
 
@@ -3592,7 +4193,10 @@ async function handleUsersRequest(data, sendMessage) {
3592
4193
  const { id, payload, from } = request;
3593
4194
  const { operation, data: requestData } = payload;
3594
4195
  const username = requestData?.username;
4196
+ const email = requestData?.email;
3595
4197
  const password = requestData?.password;
4198
+ const fullname = requestData?.fullname;
4199
+ const role = requestData?.role;
3596
4200
  if (from.type !== "admin") {
3597
4201
  sendResponse3(id, {
3598
4202
  success: false,
@@ -3604,10 +4208,10 @@ async function handleUsersRequest(data, sendMessage) {
3604
4208
  const userManager = getUserManager();
3605
4209
  switch (operation) {
3606
4210
  case "create":
3607
- await handleCreate(id, username, password, userManager, sendMessage, from.id);
4211
+ await handleCreate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3608
4212
  break;
3609
4213
  case "update":
3610
- await handleUpdate(id, username, password, userManager, sendMessage, from.id);
4214
+ await handleUpdate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3611
4215
  break;
3612
4216
  case "delete":
3613
4217
  await handleDelete(id, username, userManager, sendMessage, from.id);
@@ -3632,7 +4236,8 @@ async function handleUsersRequest(data, sendMessage) {
3632
4236
  }, sendMessage);
3633
4237
  }
3634
4238
  }
3635
- async function handleCreate(id, username, password, userManager, sendMessage, clientId) {
4239
+ async function handleCreate(id, userData, userManager, sendMessage, clientId) {
4240
+ const { username, email, password, fullname, role } = userData;
3636
4241
  if (!username || username.trim().length === 0) {
3637
4242
  sendResponse3(id, {
3638
4243
  success: false,
@@ -3647,6 +4252,16 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3647
4252
  }, sendMessage, clientId);
3648
4253
  return;
3649
4254
  }
4255
+ if (email && email.trim().length > 0) {
4256
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4257
+ if (!emailRegex.test(email)) {
4258
+ sendResponse3(id, {
4259
+ success: false,
4260
+ error: "Invalid email format"
4261
+ }, sendMessage, clientId);
4262
+ return;
4263
+ }
4264
+ }
3650
4265
  if (userManager.userExists(username)) {
3651
4266
  sendResponse3(id, {
3652
4267
  success: false,
@@ -3654,25 +4269,41 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3654
4269
  }, sendMessage, clientId);
3655
4270
  return;
3656
4271
  }
3657
- let wsIds = [];
3658
- if (clientId) {
3659
- wsIds.push(clientId);
4272
+ if (email && userManager.getUserByEmail(email)) {
4273
+ sendResponse3(id, {
4274
+ success: false,
4275
+ error: `User with email '${email}' already exists`
4276
+ }, sendMessage, clientId);
4277
+ return;
3660
4278
  }
3661
- const newUser = userManager.createUser({
4279
+ const newUserData = {
3662
4280
  username,
3663
- password,
3664
- wsIds
3665
- });
3666
- logger.info(`User created by admin: ${username}`);
4281
+ password
4282
+ };
4283
+ if (email && email.trim().length > 0) {
4284
+ newUserData.email = email.trim();
4285
+ }
4286
+ if (fullname && fullname.trim().length > 0) {
4287
+ newUserData.fullname = fullname.trim();
4288
+ }
4289
+ if (role && role.trim().length > 0) {
4290
+ newUserData.role = role.trim();
4291
+ }
4292
+ const newUser = userManager.createUser(newUserData);
4293
+ logger.info(`User created by admin: ${username}${email ? ` (${email})` : ""}`);
3667
4294
  sendResponse3(id, {
3668
4295
  success: true,
3669
4296
  data: {
3670
4297
  username: newUser.username,
4298
+ email: newUser.email,
4299
+ fullname: newUser.fullname,
4300
+ role: newUser.role,
3671
4301
  message: `User '${username}' created successfully`
3672
4302
  }
3673
4303
  }, sendMessage, clientId);
3674
4304
  }
3675
- async function handleUpdate(id, username, password, userManager, sendMessage, clientId) {
4305
+ async function handleUpdate(id, userData, userManager, sendMessage, clientId) {
4306
+ const { username, email, password, fullname, role } = userData;
3676
4307
  if (!username || username.trim().length === 0) {
3677
4308
  sendResponse3(id, {
3678
4309
  success: false,
@@ -3688,13 +4319,42 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3688
4319
  return;
3689
4320
  }
3690
4321
  const updates = {};
3691
- if (password && password.trim().length > 0) {
3692
- updates.password = password;
4322
+ if (email !== void 0) {
4323
+ if (email.trim().length > 0) {
4324
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4325
+ if (!emailRegex.test(email)) {
4326
+ sendResponse3(id, {
4327
+ success: false,
4328
+ error: "Invalid email format"
4329
+ }, sendMessage, clientId);
4330
+ return;
4331
+ }
4332
+ const existingUser = userManager.getUserByEmail(email);
4333
+ if (existingUser && existingUser.username !== username) {
4334
+ sendResponse3(id, {
4335
+ success: false,
4336
+ error: `Email '${email}' is already used by another user`
4337
+ }, sendMessage, clientId);
4338
+ return;
4339
+ }
4340
+ updates.email = email.trim();
4341
+ } else {
4342
+ updates.email = void 0;
4343
+ }
4344
+ }
4345
+ if (password !== void 0 && password.trim().length > 0) {
4346
+ updates.password = password.trim();
4347
+ }
4348
+ if (fullname !== void 0) {
4349
+ updates.fullname = fullname.trim().length > 0 ? fullname.trim() : void 0;
4350
+ }
4351
+ if (role !== void 0) {
4352
+ updates.role = role.trim().length > 0 ? role.trim() : void 0;
3693
4353
  }
3694
4354
  if (Object.keys(updates).length === 0) {
3695
4355
  sendResponse3(id, {
3696
4356
  success: false,
3697
- error: "No fields to update. Please provide password or other valid fields."
4357
+ error: "No fields to update. Please provide at least one field to update."
3698
4358
  }, sendMessage, clientId);
3699
4359
  return;
3700
4360
  }
@@ -3704,6 +4364,9 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3704
4364
  success: true,
3705
4365
  data: {
3706
4366
  username: updatedUser.username,
4367
+ email: updatedUser.email,
4368
+ fullname: updatedUser.fullname,
4369
+ role: updatedUser.role,
3707
4370
  message: `User '${username}' updated successfully`
3708
4371
  }
3709
4372
  }, sendMessage, clientId);
@@ -3744,6 +4407,9 @@ async function handleGetAll(id, userManager, sendMessage, clientId) {
3744
4407
  const users = userManager.getAllUsers();
3745
4408
  const sanitizedUsers = users.map((user) => ({
3746
4409
  username: user.username,
4410
+ email: user.email,
4411
+ fullname: user.fullname,
4412
+ role: user.role,
3747
4413
  wsIds: user.wsIds || []
3748
4414
  }));
3749
4415
  logger.info(`Admin retrieved all users (count: ${sanitizedUsers.length})`);
@@ -3774,6 +4440,9 @@ async function handleGetOne(id, username, userManager, sendMessage, clientId) {
3774
4440
  const user = userManager.getUser(username);
3775
4441
  const sanitizedUser = {
3776
4442
  username: user.username,
4443
+ email: user.email,
4444
+ fullname: user.fullname,
4445
+ role: user.role,
3777
4446
  wsIds: user.wsIds || []
3778
4447
  };
3779
4448
  logger.info(`Admin retrieved user: ${username}`);
@@ -4272,8 +4941,9 @@ var UserManager = class {
4272
4941
  return;
4273
4942
  }
4274
4943
  const fileContent = fs4.readFileSync(this.filePath, "utf-8");
4275
- const data = JSON.parse(fileContent);
4276
- this.users = Array.isArray(data.users) ? data.users : [];
4944
+ const rawData = JSON.parse(fileContent);
4945
+ const validatedData = UsersDataSchema.parse(rawData);
4946
+ this.users = validatedData.users;
4277
4947
  this.hasChanged = false;
4278
4948
  logger.debug(`Loaded ${this.users.length} users from file`);
4279
4949
  } catch (error) {
@@ -4293,10 +4963,14 @@ var UserManager = class {
4293
4963
  if (!fs4.existsSync(dir)) {
4294
4964
  fs4.mkdirSync(dir, { recursive: true });
4295
4965
  }
4296
- const data = { users: this.users };
4966
+ const usersToSave = this.users.map((user) => {
4967
+ const { wsIds, ...userWithoutWsIds } = user;
4968
+ return userWithoutWsIds;
4969
+ });
4970
+ const data = { users: usersToSave };
4297
4971
  fs4.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
4298
4972
  this.hasChanged = false;
4299
- logger.debug(`Synced ${this.users.length} users to file`);
4973
+ logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
4300
4974
  } catch (error) {
4301
4975
  logger.error("Failed to save users to file:", error);
4302
4976
  throw new Error(`Failed to save users to file: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -4343,13 +5017,17 @@ var UserManager = class {
4343
5017
  * @returns The created user
4344
5018
  */
4345
5019
  createUser(user) {
4346
- if (this.users.some((u) => u.username === user.username)) {
4347
- throw new Error(`User with username ${user.username} already exists`);
5020
+ const validatedUser = UserSchema.parse(user);
5021
+ if (this.users.some((u) => u.username === validatedUser.username)) {
5022
+ throw new Error(`User with username ${validatedUser.username} already exists`);
5023
+ }
5024
+ if (validatedUser.email && this.users.some((u) => u.email === validatedUser.email)) {
5025
+ throw new Error(`User with email ${validatedUser.email} already exists`);
4348
5026
  }
4349
- this.users.push(user);
5027
+ this.users.push(validatedUser);
4350
5028
  this.hasChanged = true;
4351
- logger.debug(`User created: ${user.username}`);
4352
- return user;
5029
+ logger.debug(`User created: ${validatedUser.username}`);
5030
+ return validatedUser;
4353
5031
  }
4354
5032
  /**
4355
5033
  * Read a user by username
@@ -4359,6 +5037,22 @@ var UserManager = class {
4359
5037
  getUser(username) {
4360
5038
  return this.users.find((u) => u.username === username);
4361
5039
  }
5040
+ /**
5041
+ * Read a user by email
5042
+ * @param email - Email to retrieve
5043
+ * @returns The user if found, undefined otherwise
5044
+ */
5045
+ getUserByEmail(email) {
5046
+ return this.users.find((u) => u.email === email);
5047
+ }
5048
+ /**
5049
+ * Find user by username or email
5050
+ * @param identifier - Username or email to search for
5051
+ * @returns The user if found, undefined otherwise
5052
+ */
5053
+ getUserByUsernameOrEmail(identifier) {
5054
+ return this.users.find((u) => u.username === identifier || u.email === identifier);
5055
+ }
4362
5056
  /**
4363
5057
  * Read all users
4364
5058
  * @returns Array of all users
@@ -4447,7 +5141,6 @@ var UserManager = class {
4447
5141
  }
4448
5142
  if (!user.wsIds.includes(wsId)) {
4449
5143
  user.wsIds.push(wsId);
4450
- this.hasChanged = true;
4451
5144
  logger.debug(`WebSocket ID added to user ${username}: ${wsId}`);
4452
5145
  }
4453
5146
  return true;
@@ -4469,7 +5162,6 @@ var UserManager = class {
4469
5162
  const initialLength = user.wsIds.length;
4470
5163
  user.wsIds = user.wsIds.filter((id) => id !== wsId);
4471
5164
  if (user.wsIds.length < initialLength) {
4472
- this.hasChanged = true;
4473
5165
  logger.debug(`WebSocket ID removed from user ${username}: ${wsId}`);
4474
5166
  }
4475
5167
  return true;
@@ -5019,6 +5711,9 @@ var SuperatomSDK = class {
5019
5711
  this.maxReconnectAttempts = 5;
5020
5712
  this.collections = {};
5021
5713
  this.components = [];
5714
+ if (config.logLevel) {
5715
+ logger.setLogLevel(config.logLevel);
5716
+ }
5022
5717
  this.apiKey = config.apiKey;
5023
5718
  this.projectId = config.projectId;
5024
5719
  this.userId = config.userId || "anonymous";
@@ -5321,6 +6016,7 @@ export {
5321
6016
  ThreadManager,
5322
6017
  UIBlock,
5323
6018
  UILogCollector,
5324
- UserManager
6019
+ UserManager,
6020
+ logger
5325
6021
  };
5326
6022
  //# sourceMappingURL=index.mjs.map