@superatomai/sdk-node 0.0.3 → 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);
@@ -2728,33 +3064,47 @@ var BaseLLM = class {
2728
3064
  if (classification.visualizations.length > 1) {
2729
3065
  const multiMsg = `Matching ${classification.visualizations.length} components for types: ${classification.visualizations.join(", ")}`;
2730
3066
  logCollector?.info(multiMsg);
2731
- const matchedComponents = [];
2732
- for (const vizType of classification.visualizations) {
3067
+ const componentPromises = classification.visualizations.map((vizType) => {
2733
3068
  logCollector?.info(`Matching component for type: ${vizType}`);
2734
- const result = await this.generateAnalyticalComponent(
3069
+ return this.generateAnalyticalComponent(
2735
3070
  userPrompt,
2736
3071
  components,
2737
3072
  vizType,
2738
3073
  apiKey,
2739
3074
  logCollector,
2740
3075
  conversationHistory
2741
- );
2742
- if (result.component) {
2743
- matchedComponents.push(result.component);
2744
- logCollector?.info(`Matched: ${result.component.name}`);
3076
+ ).then((result) => ({ vizType, result }));
3077
+ });
3078
+ const settledResults = await Promise.allSettled(componentPromises);
3079
+ const matchedComponents = [];
3080
+ for (const settledResult of settledResults) {
3081
+ if (settledResult.status === "fulfilled") {
3082
+ const { vizType, result } = settledResult.value;
3083
+ if (result.component) {
3084
+ matchedComponents.push(result.component);
3085
+ logCollector?.info(`Matched: ${result.component.name}`);
3086
+ logger.info("Component : ", result.component.name, " props: ", result.component.props);
3087
+ } else {
3088
+ logCollector?.warn(`Failed to match component for type: ${vizType}`);
3089
+ }
2745
3090
  } else {
2746
- logCollector?.warn(`Failed to match component for type: ${vizType}`);
3091
+ logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
2747
3092
  }
2748
3093
  }
3094
+ logger.debug(`[${this.getProviderName()}] Matched ${matchedComponents.length} components for multi-component container`);
2749
3095
  if (matchedComponents.length === 0) {
2750
3096
  return {
2751
- component: null,
2752
- reasoning: "Failed to match any components for the requested visualization types",
2753
- method: "classification-multi-failed",
2754
- questionType: classification.questionType,
2755
- needsMultipleComponents: true,
2756
- propsModified: false,
2757
- 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: []
2758
3108
  };
2759
3109
  }
2760
3110
  logCollector?.info("Generating container metadata...");
@@ -2784,38 +3134,50 @@ var BaseLLM = class {
2784
3134
  };
2785
3135
  logCollector?.info(`Created multi-component container with ${matchedComponents.length} components: "${containerMetadata.title}"`);
2786
3136
  return {
2787
- component: containerComponent,
2788
- reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
2789
- method: "classification-multi-generated",
2790
- questionType: classification.questionType,
2791
- needsMultipleComponents: true,
2792
- propsModified: false,
2793
- 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: []
2794
3148
  };
2795
3149
  } else if (classification.visualizations.length === 1) {
2796
3150
  const vizType = classification.visualizations[0];
2797
3151
  logCollector?.info(`Matching single component for type: ${vizType}`);
2798
3152
  const result = await this.generateAnalyticalComponent(userPrompt, components, vizType, apiKey, logCollector, conversationHistory);
2799
3153
  return {
2800
- component: result.component,
2801
- reasoning: result.reasoning,
2802
- method: "classification-generated",
2803
- questionType: classification.questionType,
2804
- needsMultipleComponents: false,
2805
- propsModified: false,
2806
- 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: []
2807
3165
  };
2808
3166
  } else {
2809
3167
  logCollector?.info("No specific visualization type - matching from all components");
2810
3168
  const result = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2811
3169
  return {
2812
- component: result.component,
2813
- reasoning: result.reasoning,
2814
- method: "classification-generated-auto",
2815
- questionType: classification.questionType,
2816
- needsMultipleComponents: false,
2817
- propsModified: false,
2818
- 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: []
2819
3181
  };
2820
3182
  }
2821
3183
  } else if (classification.questionType === "data_modification" || classification.questionType === "general") {
@@ -2823,28 +3185,109 @@ var BaseLLM = class {
2823
3185
  logCollector?.info(matchMsg);
2824
3186
  const matchResult = await this.matchComponent(userPrompt, components, apiKey, logCollector, conversationHistory);
2825
3187
  return {
2826
- component: matchResult.component,
2827
- reasoning: matchResult.reasoning,
2828
- method: "classification-matched",
2829
- questionType: classification.questionType,
2830
- needsMultipleComponents: false,
2831
- propsModified: matchResult.propsModified,
2832
- 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: []
2833
3199
  };
2834
3200
  } else {
2835
3201
  logCollector?.info("General question - no component needed");
2836
3202
  return {
2837
- component: null,
2838
- reasoning: "General question - no component needed",
2839
- method: "classification-general",
2840
- questionType: classification.questionType,
2841
- 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: []
2842
3214
  };
2843
3215
  }
2844
3216
  } catch (error) {
2845
- logCollector?.error(`Error handling user request: ${error.message}`);
2846
- 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
+ };
2847
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
+ };
2848
3291
  }
2849
3292
  /**
2850
3293
  * Generate next questions that the user might ask based on the original prompt and generated component
@@ -2890,8 +3333,10 @@ var BaseLLM = class {
2890
3333
  );
2891
3334
  return nextQuestions;
2892
3335
  } catch (error) {
2893
- console.error(`Error generating next questions with ${this.getProviderName()}:`, error);
2894
- 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}`);
2895
3340
  return [];
2896
3341
  }
2897
3342
  }
@@ -2955,112 +3400,130 @@ function getLLMProviders() {
2955
3400
  return DEFAULT_PROVIDERS;
2956
3401
  }
2957
3402
  }
2958
- var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2959
- const msg = "Using Anthropic Claude matching method...";
2960
- 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...`;
2961
3407
  logCollector?.info(msg);
2962
- if (components.length === 0) {
3408
+ if (responseMode === "component" && components.length === 0) {
2963
3409
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3410
+ logger.error("[useAnthropicMethod] No components available");
2964
3411
  logCollector?.error(emptyMsg);
2965
- return { success: false, reason: emptyMsg };
2966
- }
2967
- try {
2968
- const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
2969
- return { success: true, data: matchResult };
2970
- } catch (error) {
2971
- const errorMsg = error instanceof Error ? error.message : String(error);
2972
- logCollector?.error(`Anthropic method failed: ${errorMsg}`);
2973
- throw error;
3412
+ return { success: false, errors: [emptyMsg] };
2974
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;
2975
3418
  };
2976
- var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2977
- const msg = "Using Groq LLM matching method...";
2978
- 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);
2979
3424
  logCollector?.info(msg);
2980
- if (components.length === 0) {
3425
+ if (responseMode === "component" && components.length === 0) {
2981
3426
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3427
+ logger.error("[useGroqMethod] No components available");
2982
3428
  logCollector?.error(emptyMsg);
2983
- return { success: false, reason: emptyMsg };
2984
- }
2985
- try {
2986
- const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
2987
- return { success: true, data: matchResult };
2988
- } catch (error) {
2989
- const errorMsg = error instanceof Error ? error.message : String(error);
2990
- logCollector?.error(`Groq method failed: ${errorMsg}`);
2991
- throw error;
3429
+ return { success: false, errors: [emptyMsg] };
2992
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;
2993
3435
  };
2994
3436
  var getUserResponseFromCache = async (prompt) => {
2995
3437
  return false;
2996
3438
  };
2997
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");
2998
3443
  const userResponse = await getUserResponseFromCache(prompt);
2999
3444
  if (userResponse) {
3445
+ logger.info("[get_user_response] User response found in cache - returning cached result");
3000
3446
  logCollector?.info("User response found in cache");
3001
3447
  return {
3002
3448
  success: true,
3003
- data: userResponse
3449
+ data: userResponse,
3450
+ errors: []
3004
3451
  };
3005
3452
  }
3453
+ logger.debug("[get_user_response] No cached response found, proceeding with LLM providers");
3006
3454
  const providers = llmProviders || getLLMProviders();
3007
3455
  const errors = [];
3008
3456
  const providerOrder = providers.join(", ");
3009
3457
  logCollector?.info(`LLM Provider order: [${providerOrder}]`);
3010
3458
  if (conversationHistory && conversationHistory.length > 0) {
3011
- 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");
3012
3464
  }
3013
3465
  for (let i = 0; i < providers.length; i++) {
3014
3466
  const provider = providers[i];
3015
3467
  const isLastProvider = i === providers.length - 1;
3016
- try {
3017
- const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
3018
- logCollector?.info(attemptMsg);
3019
- let result;
3020
- if (provider === "anthropic") {
3021
- result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory);
3022
- } else if (provider === "groq") {
3023
- result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory);
3024
- } else {
3025
- continue;
3026
- }
3027
- if (result.success) {
3028
- const successMsg = `Success with provider: ${provider}`;
3029
- logCollector?.info(successMsg);
3030
- return result;
3031
- } else {
3032
- errors.push({ provider, error: result.reason || "Unknown error" });
3033
- const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.reason}`;
3034
- logCollector?.warn(warnMsg);
3035
- }
3036
- } catch (error) {
3037
- const errorMessage = error.message;
3038
- errors.push({ provider, error: errorMessage });
3039
- const errorMsg = `Provider ${provider} failed: ${errorMessage}`;
3040
- 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);
3041
3491
  if (!isLastProvider) {
3042
3492
  const fallbackMsg = "Falling back to next provider...";
3493
+ logger.info(`[get_user_response] ${fallbackMsg}`);
3043
3494
  logCollector?.info(fallbackMsg);
3044
- continue;
3045
3495
  }
3046
3496
  }
3047
3497
  }
3048
- const errorSummary = errors.map((e) => `${e.provider}: ${e.error}`).join("; ");
3049
- const failureMsg = `All LLM providers failed. Errors: ${errorSummary}`;
3050
- 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("; ")}`);
3051
3501
  return {
3052
3502
  success: false,
3053
- reason: failureMsg
3503
+ errors
3054
3504
  };
3055
3505
  };
3056
3506
 
3057
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
+ };
3058
3520
  var UILogCollector = class {
3059
3521
  constructor(clientId, sendMessage, uiBlockId) {
3060
3522
  this.logs = [];
3061
3523
  this.uiBlockId = uiBlockId || null;
3062
3524
  this.clientId = clientId;
3063
3525
  this.sendMessage = sendMessage;
3526
+ this.currentLogLevel = logger.getLogLevel();
3064
3527
  }
3065
3528
  /**
3066
3529
  * Check if logging is enabled (uiBlockId is provided)
@@ -3068,10 +3531,22 @@ var UILogCollector = class {
3068
3531
  isEnabled() {
3069
3532
  return this.uiBlockId !== null;
3070
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
+ }
3071
3542
  /**
3072
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
3073
3545
  */
3074
3546
  addLog(level, message, type, data) {
3547
+ if (!this.shouldLog(level)) {
3548
+ return;
3549
+ }
3075
3550
  const log = {
3076
3551
  timestamp: Date.now(),
3077
3552
  level,
@@ -3081,6 +3556,20 @@ var UILogCollector = class {
3081
3556
  };
3082
3557
  this.logs.push(log);
3083
3558
  this.sendLogImmediately(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
+ }
3084
3573
  }
3085
3574
  /**
3086
3575
  * Send a single log to runtime immediately
@@ -3211,100 +3700,131 @@ var CONTEXT_CONFIG = {
3211
3700
 
3212
3701
  // src/handlers/user-prompt-request.ts
3213
3702
  var processedMessageIds = /* @__PURE__ */ new Set();
3214
- async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3215
- try {
3216
- const userPromptRequest = UserPromptRequestMessageSchema.parse(data);
3217
- const { id, payload } = userPromptRequest;
3218
- const prompt = payload.prompt;
3219
- const SA_RUNTIME = payload.SA_RUNTIME;
3220
- const wsId = userPromptRequest.from.id || "unknown";
3221
- logger.info(`[REQUEST ${id}] Processing user prompt: "${prompt.substring(0, 50)}..."`);
3222
- if (processedMessageIds.has(id)) {
3223
- logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3224
- return;
3225
- }
3226
- processedMessageIds.add(id);
3227
- if (processedMessageIds.size > 100) {
3228
- const firstId = processedMessageIds.values().next().value;
3229
- if (firstId) {
3230
- 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;
3231
3787
  }
3232
3788
  }
3233
- if (!SA_RUNTIME) {
3234
- sendDataResponse4(id, {
3235
- success: false,
3236
- error: "SA_RUNTIME is required"
3237
- }, sendMessage, wsId);
3238
- return;
3239
- }
3240
- const threadId = SA_RUNTIME.threadId;
3241
- const existingUiBlockId = SA_RUNTIME.uiBlockId;
3242
- if (!threadId) {
3243
- sendDataResponse4(id, {
3244
- success: false,
3245
- error: "threadId in SA_RUNTIME is required"
3246
- }, sendMessage, wsId);
3247
- return;
3248
- }
3249
- if (!existingUiBlockId) {
3250
- sendDataResponse4(id, {
3251
- success: false,
3252
- error: "uiBlockId in SA_RUNTIME is required"
3253
- }, sendMessage, wsId);
3254
- return;
3255
- }
3256
- const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
3257
- if (!prompt) {
3258
- sendDataResponse4(id, {
3259
- success: false,
3260
- error: "Prompt not found"
3261
- }, sendMessage, wsId);
3262
- return;
3263
- }
3264
- if (!components || components.length === 0) {
3265
- sendDataResponse4(id, {
3266
- success: false,
3267
- error: "Components not found"
3268
- }, sendMessage, wsId);
3269
- return;
3270
- }
3271
- logCollector.info(`Starting user prompt request with ${components.length} components`);
3272
- const threadManager = ThreadManager.getInstance();
3273
- let thread = threadManager.getThread(threadId);
3274
- if (!thread) {
3275
- thread = threadManager.createThread(threadId);
3276
- logger.info(`Created new thread: ${threadId}`);
3277
- }
3278
- const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3279
- const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3280
- logCollector.info("User prompt request completed");
3281
- if (userResponse.success && userResponse.data && typeof userResponse.data === "object" && "component" in userResponse.data) {
3282
- const component = userResponse.data.component;
3283
- const uiBlockId = existingUiBlockId;
3284
- const uiBlock = new UIBlock(
3285
- prompt,
3286
- {},
3287
- // componentData: initially empty, will be filled later
3288
- component || {},
3289
- // generatedComponentMetadata: full component object (ComponentSchema)
3290
- [],
3291
- // actions: empty initially
3292
- uiBlockId
3293
- );
3294
- thread.addUIBlock(uiBlock);
3295
- logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
3296
- sendDataResponse4(id, {
3297
- ...userResponse,
3298
- uiBlockId,
3299
- threadId
3300
- }, sendMessage, wsId);
3301
- } else {
3302
- sendDataResponse4(id, userResponse, sendMessage, wsId);
3303
- }
3304
- return;
3305
- } catch (error) {
3306
- logger.error("Failed to handle user prompt request:", error);
3307
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
+ );
3308
3828
  }
3309
3829
  function sendDataResponse4(id, res, sendMessage, clientId) {
3310
3830
  const response = {
@@ -3319,6 +3839,7 @@ function sendDataResponse4(id, res, sendMessage, clientId) {
3319
3839
  ...res
3320
3840
  }
3321
3841
  };
3842
+ logger.info("sending user prompt response", response);
3322
3843
  sendMessage(response);
3323
3844
  }
3324
3845
 
@@ -3416,12 +3937,27 @@ function sendResponse(id, res, sendMessage, clientId) {
3416
3937
  // src/userResponse/next-questions.ts
3417
3938
  async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) {
3418
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"}`);
3419
3944
  const providers = llmProviders || ["anthropic"];
3420
- 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;
3421
3955
  try {
3422
- 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}...`);
3423
3958
  let result = [];
3424
3959
  if (provider === "groq") {
3960
+ logger.debug("[generateNextQuestions] Using Groq LLM for next questions");
3425
3961
  result = await groqLLM.generateNextQuestions(
3426
3962
  originalUserPrompt,
3427
3963
  component,
@@ -3431,6 +3967,7 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3431
3967
  conversationHistory
3432
3968
  );
3433
3969
  } else {
3970
+ logger.debug("[generateNextQuestions] Using Anthropic LLM for next questions");
3434
3971
  result = await anthropicLLM.generateNextQuestions(
3435
3972
  originalUserPrompt,
3436
3973
  component,
@@ -3441,21 +3978,39 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3441
3978
  );
3442
3979
  }
3443
3980
  if (result && result.length > 0) {
3444
- 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`);
3445
3984
  return result;
3446
3985
  }
3447
- 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
+ }
3448
3991
  } catch (providerError) {
3449
- logger.warn(`Provider ${provider} failed:`, providerError);
3450
- 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
+ }
3451
4002
  continue;
3452
4003
  }
3453
4004
  }
3454
- 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");
3455
4007
  return [];
3456
4008
  } catch (error) {
3457
- logger.error("Error generating next questions:", error);
3458
- 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}`);
3459
4014
  return [];
3460
4015
  }
3461
4016
  }
@@ -3463,11 +4018,15 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3463
4018
  // src/handlers/actions-request.ts
3464
4019
  async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3465
4020
  try {
4021
+ logger.debug("[ACTIONS_REQ] Parsing incoming actions request");
3466
4022
  const actionsRequest = ActionsRequestMessageSchema.parse(data);
3467
4023
  const { id, payload } = actionsRequest;
3468
4024
  const { SA_RUNTIME } = payload;
3469
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));
3470
4028
  if (!SA_RUNTIME) {
4029
+ logger.error(`[ACTIONS_REQ ${id}] SA_RUNTIME missing from request`);
3471
4030
  sendResponse2(id, {
3472
4031
  success: false,
3473
4032
  error: "SA_RUNTIME with threadId and uiBlockId is required"
@@ -3476,31 +4035,54 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3476
4035
  }
3477
4036
  const uiBlockId = SA_RUNTIME.uiBlockId;
3478
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}`);
3479
4040
  const threadManager = ThreadManager.getInstance();
3480
4041
  const thread = threadManager.getThread(threadId);
3481
4042
  if (!thread) {
4043
+ logger.error(`[ACTIONS_REQ ${id}] Thread '${threadId}' not found`);
3482
4044
  sendResponse2(id, {
3483
4045
  success: false,
3484
4046
  error: `Thread '${threadId}' not found`
3485
4047
  }, sendMessage, wsId);
3486
4048
  return;
3487
4049
  }
4050
+ logger.debug(`[ACTIONS_REQ ${id}] Thread found with ${thread.getUIBlocks().length} UIBlocks`);
4051
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving UIBlock: ${uiBlockId}`);
3488
4052
  const uiBlock = thread.getUIBlock(uiBlockId);
3489
4053
  if (!uiBlock) {
4054
+ logger.error(`[ACTIONS_REQ ${id}] UIBlock '${uiBlockId}' not found in thread '${threadId}'`);
3490
4055
  sendResponse2(id, {
3491
4056
  success: false,
3492
4057
  error: `UIBlock '${uiBlockId}' not found in thread '${threadId}'`
3493
4058
  }, sendMessage, wsId);
3494
4059
  return;
3495
4060
  }
4061
+ logger.info(`[ACTIONS_REQ ${id}] UIBlock retrieved successfully`);
4062
+ logger.debug(`[ACTIONS_REQ ${id}] Creating UILogCollector for uiBlockId: ${uiBlockId}`);
3496
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`);
3497
4066
  const userQuestion = uiBlock.getUserQuestion();
3498
4067
  const component = uiBlock.getComponentMetadata();
3499
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)`);
3500
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)}...`);
3501
4078
  logCollector.info(`Generating actions for UIBlock: ${uiBlockId}`);
3502
- 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();
3503
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`);
3504
4086
  const nextQuestions = await generateNextQuestions(
3505
4087
  userQuestion,
3506
4088
  component,
@@ -3511,14 +4093,28 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3511
4093
  logCollector,
3512
4094
  conversationHistory
3513
4095
  );
3514
- 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) => ({
3515
4100
  id: `action_${index}_${Date.now()}`,
3516
4101
  name: question,
3517
4102
  type: "next_question",
3518
4103
  question
3519
4104
  }));
4105
+ logger.debug(`[ACTIONS_REQ ${id}] Converted ${convertedActions.length} actions`);
4106
+ return convertedActions;
3520
4107
  });
3521
- 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`);
3522
4118
  sendResponse2(id, {
3523
4119
  success: true,
3524
4120
  data: {
@@ -3529,12 +4125,26 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3529
4125
  threadId
3530
4126
  }
3531
4127
  }, sendMessage, wsId);
4128
+ logger.info(`[ACTIONS_REQ ${id}] \u2713 Actions request completed successfully`);
3532
4129
  } catch (error) {
3533
- 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
+ }
3534
4143
  sendResponse2(null, {
3535
4144
  success: false,
3536
- error: error instanceof Error ? error.message : "Unknown error occurred"
4145
+ error: errorMessage
3537
4146
  }, sendMessage);
4147
+ logger.info("[ACTIONS_REQ] \u2717 Actions request completed with errors");
3538
4148
  }
3539
4149
  }
3540
4150
  function sendResponse2(id, res, sendMessage, clientId) {
@@ -3550,6 +4160,11 @@ function sendResponse2(id, res, sendMessage, clientId) {
3550
4160
  ...res
3551
4161
  }
3552
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
+ }
3553
4168
  sendMessage(response);
3554
4169
  }
3555
4170
 
@@ -3578,7 +4193,10 @@ async function handleUsersRequest(data, sendMessage) {
3578
4193
  const { id, payload, from } = request;
3579
4194
  const { operation, data: requestData } = payload;
3580
4195
  const username = requestData?.username;
4196
+ const email = requestData?.email;
3581
4197
  const password = requestData?.password;
4198
+ const fullname = requestData?.fullname;
4199
+ const role = requestData?.role;
3582
4200
  if (from.type !== "admin") {
3583
4201
  sendResponse3(id, {
3584
4202
  success: false,
@@ -3590,10 +4208,10 @@ async function handleUsersRequest(data, sendMessage) {
3590
4208
  const userManager = getUserManager();
3591
4209
  switch (operation) {
3592
4210
  case "create":
3593
- await handleCreate(id, username, password, userManager, sendMessage, from.id);
4211
+ await handleCreate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3594
4212
  break;
3595
4213
  case "update":
3596
- await handleUpdate(id, username, password, userManager, sendMessage, from.id);
4214
+ await handleUpdate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3597
4215
  break;
3598
4216
  case "delete":
3599
4217
  await handleDelete(id, username, userManager, sendMessage, from.id);
@@ -3618,7 +4236,8 @@ async function handleUsersRequest(data, sendMessage) {
3618
4236
  }, sendMessage);
3619
4237
  }
3620
4238
  }
3621
- 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;
3622
4241
  if (!username || username.trim().length === 0) {
3623
4242
  sendResponse3(id, {
3624
4243
  success: false,
@@ -3633,6 +4252,16 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3633
4252
  }, sendMessage, clientId);
3634
4253
  return;
3635
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
+ }
3636
4265
  if (userManager.userExists(username)) {
3637
4266
  sendResponse3(id, {
3638
4267
  success: false,
@@ -3640,25 +4269,41 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3640
4269
  }, sendMessage, clientId);
3641
4270
  return;
3642
4271
  }
3643
- let wsIds = [];
3644
- if (clientId) {
3645
- 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;
3646
4278
  }
3647
- const newUser = userManager.createUser({
4279
+ const newUserData = {
3648
4280
  username,
3649
- password,
3650
- wsIds
3651
- });
3652
- 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})` : ""}`);
3653
4294
  sendResponse3(id, {
3654
4295
  success: true,
3655
4296
  data: {
3656
4297
  username: newUser.username,
4298
+ email: newUser.email,
4299
+ fullname: newUser.fullname,
4300
+ role: newUser.role,
3657
4301
  message: `User '${username}' created successfully`
3658
4302
  }
3659
4303
  }, sendMessage, clientId);
3660
4304
  }
3661
- 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;
3662
4307
  if (!username || username.trim().length === 0) {
3663
4308
  sendResponse3(id, {
3664
4309
  success: false,
@@ -3674,13 +4319,42 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3674
4319
  return;
3675
4320
  }
3676
4321
  const updates = {};
3677
- if (password && password.trim().length > 0) {
3678
- 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;
3679
4353
  }
3680
4354
  if (Object.keys(updates).length === 0) {
3681
4355
  sendResponse3(id, {
3682
4356
  success: false,
3683
- 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."
3684
4358
  }, sendMessage, clientId);
3685
4359
  return;
3686
4360
  }
@@ -3690,6 +4364,9 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3690
4364
  success: true,
3691
4365
  data: {
3692
4366
  username: updatedUser.username,
4367
+ email: updatedUser.email,
4368
+ fullname: updatedUser.fullname,
4369
+ role: updatedUser.role,
3693
4370
  message: `User '${username}' updated successfully`
3694
4371
  }
3695
4372
  }, sendMessage, clientId);
@@ -3730,6 +4407,9 @@ async function handleGetAll(id, userManager, sendMessage, clientId) {
3730
4407
  const users = userManager.getAllUsers();
3731
4408
  const sanitizedUsers = users.map((user) => ({
3732
4409
  username: user.username,
4410
+ email: user.email,
4411
+ fullname: user.fullname,
4412
+ role: user.role,
3733
4413
  wsIds: user.wsIds || []
3734
4414
  }));
3735
4415
  logger.info(`Admin retrieved all users (count: ${sanitizedUsers.length})`);
@@ -3760,6 +4440,9 @@ async function handleGetOne(id, username, userManager, sendMessage, clientId) {
3760
4440
  const user = userManager.getUser(username);
3761
4441
  const sanitizedUser = {
3762
4442
  username: user.username,
4443
+ email: user.email,
4444
+ fullname: user.fullname,
4445
+ role: user.role,
3763
4446
  wsIds: user.wsIds || []
3764
4447
  };
3765
4448
  logger.info(`Admin retrieved user: ${username}`);
@@ -4258,8 +4941,9 @@ var UserManager = class {
4258
4941
  return;
4259
4942
  }
4260
4943
  const fileContent = fs4.readFileSync(this.filePath, "utf-8");
4261
- const data = JSON.parse(fileContent);
4262
- 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;
4263
4947
  this.hasChanged = false;
4264
4948
  logger.debug(`Loaded ${this.users.length} users from file`);
4265
4949
  } catch (error) {
@@ -4279,10 +4963,14 @@ var UserManager = class {
4279
4963
  if (!fs4.existsSync(dir)) {
4280
4964
  fs4.mkdirSync(dir, { recursive: true });
4281
4965
  }
4282
- 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 };
4283
4971
  fs4.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
4284
4972
  this.hasChanged = false;
4285
- logger.debug(`Synced ${this.users.length} users to file`);
4973
+ logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
4286
4974
  } catch (error) {
4287
4975
  logger.error("Failed to save users to file:", error);
4288
4976
  throw new Error(`Failed to save users to file: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -4329,13 +5017,17 @@ var UserManager = class {
4329
5017
  * @returns The created user
4330
5018
  */
4331
5019
  createUser(user) {
4332
- if (this.users.some((u) => u.username === user.username)) {
4333
- 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`);
4334
5023
  }
4335
- this.users.push(user);
5024
+ if (validatedUser.email && this.users.some((u) => u.email === validatedUser.email)) {
5025
+ throw new Error(`User with email ${validatedUser.email} already exists`);
5026
+ }
5027
+ this.users.push(validatedUser);
4336
5028
  this.hasChanged = true;
4337
- logger.debug(`User created: ${user.username}`);
4338
- return user;
5029
+ logger.debug(`User created: ${validatedUser.username}`);
5030
+ return validatedUser;
4339
5031
  }
4340
5032
  /**
4341
5033
  * Read a user by username
@@ -4345,6 +5037,22 @@ var UserManager = class {
4345
5037
  getUser(username) {
4346
5038
  return this.users.find((u) => u.username === username);
4347
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
+ }
4348
5056
  /**
4349
5057
  * Read all users
4350
5058
  * @returns Array of all users
@@ -4433,7 +5141,6 @@ var UserManager = class {
4433
5141
  }
4434
5142
  if (!user.wsIds.includes(wsId)) {
4435
5143
  user.wsIds.push(wsId);
4436
- this.hasChanged = true;
4437
5144
  logger.debug(`WebSocket ID added to user ${username}: ${wsId}`);
4438
5145
  }
4439
5146
  return true;
@@ -4455,7 +5162,6 @@ var UserManager = class {
4455
5162
  const initialLength = user.wsIds.length;
4456
5163
  user.wsIds = user.wsIds.filter((id) => id !== wsId);
4457
5164
  if (user.wsIds.length < initialLength) {
4458
- this.hasChanged = true;
4459
5165
  logger.debug(`WebSocket ID removed from user ${username}: ${wsId}`);
4460
5166
  }
4461
5167
  return true;
@@ -5005,6 +5711,9 @@ var SuperatomSDK = class {
5005
5711
  this.maxReconnectAttempts = 5;
5006
5712
  this.collections = {};
5007
5713
  this.components = [];
5714
+ if (config.logLevel) {
5715
+ logger.setLogLevel(config.logLevel);
5716
+ }
5008
5717
  this.apiKey = config.apiKey;
5009
5718
  this.projectId = config.projectId;
5010
5719
  this.userId = config.userId || "anonymous";
@@ -5307,6 +6016,7 @@ export {
5307
6016
  ThreadManager,
5308
6017
  UIBlock,
5309
6018
  UILogCollector,
5310
- UserManager
6019
+ UserManager,
6020
+ logger
5311
6021
  };
5312
6022
  //# sourceMappingURL=index.mjs.map