@superatomai/sdk-node 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -10,9 +10,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
10
  if (typeof require !== "undefined") return require.apply(this, arguments);
11
11
  throw Error('Dynamic require of "' + x + '" is not supported');
12
12
  });
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ };
13
16
  var __commonJS = (cb, mod) => function __require2() {
14
17
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
15
18
  };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, { get: all[name], enumerable: true });
22
+ };
16
23
  var __copyProps = (to, from, except, desc) => {
17
24
  if (from && typeof from === "object" || typeof from === "function") {
18
25
  for (let key of __getOwnPropNames(from))
@@ -102,7 +109,7 @@ var require_package = __commonJS({
102
109
  var require_main = __commonJS({
103
110
  "node_modules/dotenv/lib/main.js"(exports, module) {
104
111
  "use strict";
105
- var fs7 = __require("fs");
112
+ var fs8 = __require("fs");
106
113
  var path7 = __require("path");
107
114
  var os4 = __require("os");
108
115
  var crypto2 = __require("crypto");
@@ -244,7 +251,7 @@ var require_main = __commonJS({
244
251
  if (options && options.path && options.path.length > 0) {
245
252
  if (Array.isArray(options.path)) {
246
253
  for (const filepath of options.path) {
247
- if (fs7.existsSync(filepath)) {
254
+ if (fs8.existsSync(filepath)) {
248
255
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
249
256
  }
250
257
  }
@@ -254,7 +261,7 @@ var require_main = __commonJS({
254
261
  } else {
255
262
  possibleVaultPath = path7.resolve(process.cwd(), ".env.vault");
256
263
  }
257
- if (fs7.existsSync(possibleVaultPath)) {
264
+ if (fs8.existsSync(possibleVaultPath)) {
258
265
  return possibleVaultPath;
259
266
  }
260
267
  return null;
@@ -307,7 +314,7 @@ var require_main = __commonJS({
307
314
  const parsedAll = {};
308
315
  for (const path8 of optionPaths) {
309
316
  try {
310
- const parsed = DotenvModule.parse(fs7.readFileSync(path8, { encoding }));
317
+ const parsed = DotenvModule.parse(fs8.readFileSync(path8, { encoding }));
311
318
  DotenvModule.populate(parsedAll, parsed, options);
312
319
  } catch (e) {
313
320
  if (debug) {
@@ -428,6 +435,128 @@ var require_main = __commonJS({
428
435
  }
429
436
  });
430
437
 
438
+ // src/userResponse/utils.ts
439
+ var utils_exports = {};
440
+ __export(utils_exports, {
441
+ convertTopToLimit: () => convertTopToLimit,
442
+ ensureQueryLimit: () => ensureQueryLimit,
443
+ fixScalarSubqueries: () => fixScalarSubqueries,
444
+ getJsonSizeInBytes: () => getJsonSizeInBytes,
445
+ validateMessageSize: () => validateMessageSize
446
+ });
447
+ function convertTopToLimit(query) {
448
+ if (!query || query.trim().length === 0) {
449
+ return query;
450
+ }
451
+ let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
452
+ if (modifiedQuery !== query) {
453
+ console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
454
+ }
455
+ return modifiedQuery;
456
+ }
457
+ function ensureQueryLimit(query, defaultLimit = 32, maxLimit = 32) {
458
+ if (!query || query.trim().length === 0) {
459
+ return query;
460
+ }
461
+ let trimmedQuery = query.trim();
462
+ const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
463
+ if (!isSelectQuery) {
464
+ return query;
465
+ }
466
+ trimmedQuery = convertTopToLimit(trimmedQuery);
467
+ const hadSemicolon = trimmedQuery.endsWith(";");
468
+ if (hadSemicolon) {
469
+ trimmedQuery = trimmedQuery.slice(0, -1).trim();
470
+ }
471
+ const limitMatches = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/gi);
472
+ if (limitMatches && limitMatches.length > 0) {
473
+ if (limitMatches.length > 1) {
474
+ console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
475
+ trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
476
+ } else {
477
+ const existingLimitMatch = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/i);
478
+ if (existingLimitMatch) {
479
+ const existingLimit = parseInt(existingLimitMatch[1], 10);
480
+ if (existingLimit <= maxLimit) {
481
+ if (hadSemicolon) {
482
+ trimmedQuery += ";";
483
+ }
484
+ return trimmedQuery;
485
+ }
486
+ console.warn(`\u26A0\uFE0F Query LIMIT ${existingLimit} exceeds maximum of ${maxLimit}. Reducing to ${maxLimit}...`);
487
+ trimmedQuery = trimmedQuery.replace(/\bLIMIT\s+\d+\b/i, `LIMIT ${maxLimit}`);
488
+ if (hadSemicolon) {
489
+ trimmedQuery += ";";
490
+ }
491
+ return trimmedQuery;
492
+ }
493
+ }
494
+ }
495
+ trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
496
+ if (hadSemicolon) {
497
+ trimmedQuery += ";";
498
+ }
499
+ return trimmedQuery;
500
+ }
501
+ function fixScalarSubqueries(query) {
502
+ if (!query || query.trim().length === 0) {
503
+ return query;
504
+ }
505
+ let modifiedQuery = query;
506
+ let hasChanges = false;
507
+ const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
508
+ const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
509
+ for (let i = matches.length - 1; i >= 0; i--) {
510
+ const match = matches[i];
511
+ const startPos = match.index + match[0].length - "SELECT ".length;
512
+ let parenDepth = 1;
513
+ let endPos = startPos;
514
+ let foundEnd = false;
515
+ for (let j = startPos; j < modifiedQuery.length; j++) {
516
+ const char = modifiedQuery[j];
517
+ if (char === "(") parenDepth++;
518
+ if (char === ")") {
519
+ parenDepth--;
520
+ if (parenDepth === 0) {
521
+ endPos = j;
522
+ foundEnd = true;
523
+ break;
524
+ }
525
+ }
526
+ }
527
+ if (!foundEnd) continue;
528
+ const subquery = modifiedQuery.substring(startPos, endPos);
529
+ if (/\bLIMIT\s+\d+/i.test(subquery)) {
530
+ continue;
531
+ }
532
+ const fixedSubquery = subquery.trim() + " LIMIT 1";
533
+ modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
534
+ hasChanges = true;
535
+ console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
536
+ }
537
+ if (hasChanges) {
538
+ console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
539
+ }
540
+ return modifiedQuery;
541
+ }
542
+ function getJsonSizeInBytes(obj) {
543
+ const jsonString = JSON.stringify(obj);
544
+ return Buffer.byteLength(jsonString, "utf8");
545
+ }
546
+ function validateMessageSize(message, maxSize = 1048576) {
547
+ const size = getJsonSizeInBytes(message);
548
+ return {
549
+ isValid: size <= maxSize,
550
+ size,
551
+ maxSize
552
+ };
553
+ }
554
+ var init_utils = __esm({
555
+ "src/userResponse/utils.ts"() {
556
+ "use strict";
557
+ }
558
+ });
559
+
431
560
  // src/websocket.ts
432
561
  import WebSocket from "ws";
433
562
  function createWebSocket(url) {
@@ -624,6 +753,18 @@ var DSLRendererPropsSchema2 = z2.object({
624
753
  });
625
754
 
626
755
  // src/types.ts
756
+ var UserSchema = z3.object({
757
+ username: z3.string().min(1, "Username is required"),
758
+ email: z3.string().email("Invalid email format").optional(),
759
+ password: z3.string().min(1, "Password is required"),
760
+ fullname: z3.string().optional(),
761
+ role: z3.string().optional(),
762
+ wsIds: z3.array(z3.string()).optional()
763
+ // Only in memory, not persisted to file
764
+ });
765
+ var UsersDataSchema = z3.object({
766
+ users: z3.array(UserSchema)
767
+ });
627
768
  var MessageParticipantSchema = z3.object({
628
769
  id: z3.string().optional(),
629
770
  type: z3.string().optional()
@@ -677,7 +818,8 @@ var UserPromptRequestPayloadSchema = z3.object({
677
818
  SA_RUNTIME: z3.object({
678
819
  threadId: z3.string(),
679
820
  uiBlockId: z3.string()
680
- }).optional()
821
+ }).optional(),
822
+ responseMode: z3.enum(["component", "text"]).optional()
681
823
  });
682
824
  var UserPromptRequestMessageSchema = z3.object({
683
825
  id: z3.string(),
@@ -724,7 +866,10 @@ var UsersRequestPayloadSchema = z3.object({
724
866
  operation: z3.enum(["create", "update", "delete", "getAll", "getOne"]),
725
867
  data: z3.object({
726
868
  username: z3.string().optional(),
727
- password: z3.string().optional()
869
+ email: z3.string().email("Invalid email format").optional(),
870
+ password: z3.string().optional(),
871
+ fullname: z3.string().optional(),
872
+ role: z3.string().optional()
728
873
  }).optional()
729
874
  });
730
875
  var UsersRequestMessageSchema = z3.object({
@@ -789,21 +934,97 @@ var ReportsRequestMessageSchema = z3.object({
789
934
  });
790
935
 
791
936
  // src/utils/logger.ts
937
+ import fs from "fs";
792
938
  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);
939
+ var LOGSTREAM = fs.createWriteStream("superatom-sdk.log", { flags: "a" });
940
+ var LOG_LEVEL_PRIORITY = {
941
+ errors: 0,
942
+ warnings: 1,
943
+ info: 2,
944
+ verbose: 3
945
+ };
946
+ var MESSAGE_LEVEL_PRIORITY = {
947
+ error: 0,
948
+ warn: 1,
949
+ info: 2,
950
+ debug: 3
951
+ };
952
+ var Logger = class {
953
+ constructor() {
954
+ const envLevel = (process.env.SUPERATOM_LOG_LEVEL || "info").toLowerCase();
955
+ if (this.isValidLogLevel(envLevel)) {
956
+ this.currentLevel = envLevel;
957
+ } else {
958
+ this.currentLevel = "info";
959
+ console.warn(
960
+ `${PREFIX} Invalid log level "${envLevel}". Using default "info". Valid levels: errors, warnings, info, verbose`
961
+ );
962
+ }
963
+ this.currentLevelPriority = LOG_LEVEL_PRIORITY[this.currentLevel];
964
+ }
965
+ /**
966
+ * Check if a string is a valid log level
967
+ */
968
+ isValidLogLevel(level) {
969
+ return level === "errors" || level === "warnings" || level === "info" || level === "verbose";
970
+ }
971
+ /**
972
+ * Check if a message should be logged based on current log level
973
+ */
974
+ shouldLog(messageLevel) {
975
+ const messagePriority = MESSAGE_LEVEL_PRIORITY[messageLevel];
976
+ return messagePriority <= this.currentLevelPriority;
977
+ }
978
+ /**
979
+ * Get current log level
980
+ */
981
+ getLogLevel() {
982
+ return this.currentLevel;
983
+ }
984
+ /**
985
+ * Set log level programmatically
986
+ */
987
+ setLogLevel(level) {
988
+ this.currentLevel = level;
989
+ this.currentLevelPriority = LOG_LEVEL_PRIORITY[level];
990
+ }
991
+ /**
992
+ * Log info message (shown for info and verbose levels)
993
+ */
994
+ info(...args) {
995
+ if (this.shouldLog("info")) {
996
+ console.log(PREFIX, ...args);
997
+ }
998
+ }
999
+ /**
1000
+ * Log error message (shown for all levels)
1001
+ */
1002
+ error(...args) {
1003
+ if (this.shouldLog("error")) {
1004
+ console.error(PREFIX, ...args);
1005
+ }
1006
+ }
1007
+ /**
1008
+ * Log warning message (shown for warnings, info, and verbose levels)
1009
+ */
1010
+ warn(...args) {
1011
+ if (this.shouldLog("warn")) {
1012
+ console.warn(PREFIX, ...args);
1013
+ }
1014
+ }
1015
+ /**
1016
+ * Log debug message (only shown for verbose level)
1017
+ */
1018
+ debug(...args) {
1019
+ if (this.shouldLog("debug")) {
1020
+ console.log(PREFIX, "[DEBUG]", ...args);
1021
+ }
1022
+ }
1023
+ file(...args) {
1024
+ LOGSTREAM.write(args.join(" ") + "\n");
805
1025
  }
806
1026
  };
1027
+ var logger = new Logger();
807
1028
 
808
1029
  // src/threads/uiblock.ts
809
1030
  import { randomUUID } from "crypto";
@@ -838,13 +1059,15 @@ var UIBlock = class {
838
1059
  * @param generatedComponentMetadata - Optional metadata about the generated component
839
1060
  * @param actions - Optional array of available actions
840
1061
  * @param id - Optional custom ID, generates UUID if not provided
1062
+ * @param textResponse - Optional text response from LLM
841
1063
  */
842
- constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id) {
1064
+ constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id, textResponse = null) {
843
1065
  this.id = id || randomUUID();
844
1066
  this.userQuestion = userQuestion;
845
1067
  this.componentData = componentData;
846
1068
  this.generatedComponentMetadata = generatedComponentMetadata;
847
1069
  this.actions = actions;
1070
+ this.textResponse = textResponse;
848
1071
  this.createdAt = /* @__PURE__ */ new Date();
849
1072
  }
850
1073
  /**
@@ -871,6 +1094,9 @@ var UIBlock = class {
871
1094
  getComponentMetadata() {
872
1095
  return this.generatedComponentMetadata;
873
1096
  }
1097
+ getTextResponse() {
1098
+ return this.textResponse || "";
1099
+ }
874
1100
  /**
875
1101
  * Set or update component metadata
876
1102
  */
@@ -963,6 +1189,12 @@ var UIBlock = class {
963
1189
  const processedData = this.processDataForStorage(data);
964
1190
  this.componentData = { ...this.componentData, ...processedData };
965
1191
  }
1192
+ /**
1193
+ * Set or update text response
1194
+ */
1195
+ setTextResponse(textResponse) {
1196
+ this.textResponse = textResponse;
1197
+ }
966
1198
  /**
967
1199
  * Get all actions (only if they are resolved, not if fetching)
968
1200
  */
@@ -1046,6 +1278,7 @@ var UIBlock = class {
1046
1278
  userQuestion: this.userQuestion,
1047
1279
  generatedComponentMetadata: this.generatedComponentMetadata,
1048
1280
  componentData: this.componentData,
1281
+ textResponse: this.textResponse,
1049
1282
  actions: actionsValue,
1050
1283
  isFetchingActions: this.actions instanceof Promise,
1051
1284
  createdAt: this.createdAt.toISOString()
@@ -1146,8 +1379,11 @@ var Thread = class {
1146
1379
  const questionNum = index + 1;
1147
1380
  const question = block.getUserQuestion();
1148
1381
  const metadata = block.getComponentMetadata();
1149
- let componentSummary = "No component generated";
1150
- if (metadata && Object.keys(metadata).length > 0) {
1382
+ const textResponse = block.getTextResponse();
1383
+ let assistantResponse = "";
1384
+ const hasComponent = metadata && Object.keys(metadata).length > 0 && metadata.type;
1385
+ const hasTextResponse = textResponse && textResponse.trim().length > 0;
1386
+ if (hasComponent) {
1151
1387
  const parts = [];
1152
1388
  if (metadata.type) {
1153
1389
  parts.push(`Component Type: ${metadata.type}`);
@@ -1167,11 +1403,17 @@ var Thread = class {
1167
1403
  const componentTypes = metadata.props.config.components.map((c) => c.type).join(", ");
1168
1404
  parts.push(`Multi-component with: ${componentTypes}`);
1169
1405
  }
1170
- componentSummary = parts.join(", ");
1406
+ assistantResponse = parts.join(", ");
1407
+ } else if (hasTextResponse) {
1408
+ assistantResponse = textResponse;
1409
+ } else {
1410
+ assistantResponse = "No response generated";
1171
1411
  }
1172
- contextLines.push(`Q${questionNum}: ${question}`);
1173
- contextLines.push(`A${questionNum}: ${componentSummary}`);
1174
- contextLines.push("");
1412
+ contextLines.push(`User:
1413
+ ${question}`);
1414
+ contextLines.push(`Assistant:
1415
+ ${assistantResponse}`);
1416
+ contextLines.push("---");
1175
1417
  });
1176
1418
  return contextLines.join("\n").trim();
1177
1419
  }
@@ -1347,7 +1589,7 @@ function sendDataResponse(id, collection, op, data, meta, sendMessage) {
1347
1589
  }
1348
1590
 
1349
1591
  // src/bundle.ts
1350
- import * as fs from "fs";
1592
+ import * as fs2 from "fs";
1351
1593
  import * as path from "path";
1352
1594
  function getBundleDir(configDir) {
1353
1595
  const bundleDir = configDir || process.env.SA_BUNDLE_DIR;
@@ -1360,16 +1602,16 @@ function getBundleDir(configDir) {
1360
1602
  }
1361
1603
  function getJS(bundleDir) {
1362
1604
  try {
1363
- if (!fs.existsSync(bundleDir)) {
1605
+ if (!fs2.existsSync(bundleDir)) {
1364
1606
  throw new Error(`Bundle directory does not exist: ${bundleDir}`);
1365
1607
  }
1366
- const stats = fs.statSync(bundleDir);
1608
+ const stats = fs2.statSync(bundleDir);
1367
1609
  if (!stats.isDirectory()) {
1368
1610
  throw new Error(`Bundle path is not a directory: ${bundleDir}`);
1369
1611
  }
1370
1612
  let files;
1371
1613
  try {
1372
- files = fs.readdirSync(bundleDir);
1614
+ files = fs2.readdirSync(bundleDir);
1373
1615
  } catch (error) {
1374
1616
  const errorMessage = error instanceof Error ? error.message : String(error);
1375
1617
  throw new Error(`Failed to read bundle directory: ${errorMessage}`);
@@ -1384,7 +1626,7 @@ function getJS(bundleDir) {
1384
1626
  const filePath = path.join(bundleDir, indexFile);
1385
1627
  logger.info(`Loading bundle from ${filePath}`);
1386
1628
  try {
1387
- return fs.readFileSync(filePath, "utf8");
1629
+ return fs2.readFileSync(filePath, "utf8");
1388
1630
  } catch (error) {
1389
1631
  const errorMessage = error instanceof Error ? error.message : String(error);
1390
1632
  throw new Error(`Failed to read bundle file: ${errorMessage}`);
@@ -1480,9 +1722,9 @@ function getUserManager() {
1480
1722
  }
1481
1723
  return currentUserManager;
1482
1724
  }
1483
- function findUserByUsername(username) {
1725
+ function findUserByUsernameOrEmail(identifier) {
1484
1726
  const manager = getUserManager();
1485
- const user = manager.getUser(username);
1727
+ const user = manager.getUserByUsernameOrEmail(identifier);
1486
1728
  return user || null;
1487
1729
  }
1488
1730
  function addWsIdToUser(username, wsId) {
@@ -1504,60 +1746,81 @@ async function cleanupUserStorage() {
1504
1746
 
1505
1747
  // src/auth/validator.ts
1506
1748
  function validateUser(credentials) {
1507
- const { username, password } = credentials;
1508
- if (!username || !password) {
1509
- logger.warn("Validation failed: Username and password are required");
1749
+ const { username, email, password } = credentials;
1750
+ const identifier = username || email;
1751
+ logger.debug("[validateUser] Starting user validation");
1752
+ logger.debug(`[validateUser] Username provided: ${username ? "\u2713" : "\u2717"}, Email provided: ${email ? "\u2713" : "\u2717"}, Password provided: ${password ? "\u2713" : "\u2717"}`);
1753
+ if (!identifier || !password) {
1754
+ logger.warn("[validateUser] Validation failed: Username/email and password are required");
1510
1755
  return {
1511
1756
  success: false,
1512
- error: "Username and password are required"
1757
+ error: "Username or email and password are required"
1513
1758
  };
1514
1759
  }
1515
- const user = findUserByUsername(username);
1760
+ logger.debug(`[validateUser] Looking up user by identifier: ${identifier}`);
1761
+ const user = findUserByUsernameOrEmail(identifier);
1516
1762
  if (!user) {
1517
- logger.warn(`Validation failed: User not found - ${username}`);
1763
+ logger.warn(`[validateUser] Validation failed: User not found - ${identifier}`);
1518
1764
  return {
1519
1765
  success: false,
1520
- error: "Invalid username"
1766
+ error: "Invalid username or email"
1521
1767
  };
1522
1768
  }
1769
+ logger.debug(`[validateUser] User found: ${user.username}, verifying password`);
1523
1770
  const hashedPassword = hashPassword(user.password);
1524
1771
  if (hashedPassword !== password) {
1525
- logger.warn(`Validation failed: Invalid password for user - ${username}`);
1772
+ logger.warn(`[validateUser] Validation failed: Invalid password for user - ${user.username}`);
1773
+ logger.debug(`[validateUser] Password hash mismatch for user: ${user.username}`);
1526
1774
  return {
1527
1775
  success: false,
1528
1776
  error: "Invalid password"
1529
1777
  };
1530
1778
  }
1531
- logger.debug(`User validated successfully: ${username}`);
1779
+ logger.info(`[validateUser] \u2713 User validated successfully: ${user.username}`);
1780
+ logger.debug(`[validateUser] Returning user data for: ${user.username}`);
1532
1781
  return {
1533
1782
  success: true,
1534
- data: user.username
1783
+ data: user.username,
1784
+ username: user.username
1535
1785
  };
1536
1786
  }
1537
1787
  function authenticateAndStoreWsId(credentials, wsId) {
1788
+ const identifier = credentials.username || credentials.email;
1789
+ logger.debug("[authenticateAndStoreWsId] Starting authentication and WebSocket ID storage");
1790
+ logger.debug("[authenticateAndStoreWsId] Validating user credentials");
1538
1791
  const validationResult = validateUser(credentials);
1539
1792
  if (!validationResult.success) {
1793
+ logger.warn(`[authenticateAndStoreWsId] User validation failed for: ${identifier}`);
1540
1794
  return validationResult;
1541
1795
  }
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}`);
1796
+ const username = validationResult.username;
1797
+ logger.info(`[authenticateAndStoreWsId] User ${username} validated, storing WebSocket ID`);
1798
+ logger.debug(`[authenticateAndStoreWsId] Calling addWsIdToUser for ${username}`);
1799
+ addWsIdToUser(username, wsId);
1800
+ logger.debug(`[authenticateAndStoreWsId] WebSocket ID ${wsId} associated with user ${username}`);
1551
1801
  return validationResult;
1552
1802
  }
1553
1803
  function verifyAuthToken(authToken) {
1554
1804
  try {
1805
+ logger.debug("[verifyAuthToken] Starting token verification");
1806
+ logger.debug("[verifyAuthToken] Decoding base64 token");
1555
1807
  const decodedString = Buffer.from(authToken, "base64").toString("utf-8");
1808
+ logger.debug("[verifyAuthToken] Parsing decoded token as JSON");
1556
1809
  const credentials = JSON.parse(decodedString);
1557
- logger.debug("Token decoded and parsed successfully");
1558
- return validateUser(credentials);
1810
+ logger.debug("[verifyAuthToken] Token decoded and parsed successfully");
1811
+ logger.debug(`[verifyAuthToken] Token contains username: ${credentials.username ? "\u2713" : "\u2717"}`);
1812
+ logger.debug("[verifyAuthToken] Validating credentials from token");
1813
+ const result = validateUser(credentials);
1814
+ if (result.success) {
1815
+ logger.info(`[verifyAuthToken] \u2713 Token verified successfully for user: ${credentials.username || "unknown"}`);
1816
+ } else {
1817
+ logger.warn(`[verifyAuthToken] Token verification failed: ${result.error}`);
1818
+ }
1819
+ return result;
1559
1820
  } catch (error) {
1560
- logger.error("Failed to verify auth token:", error);
1821
+ const errorMsg = error instanceof Error ? error.message : String(error);
1822
+ logger.error(`[verifyAuthToken] Failed to verify auth token: ${errorMsg}`);
1823
+ logger.debug("[verifyAuthToken] Token verification error details:", error);
1561
1824
  return {
1562
1825
  success: false,
1563
1826
  error: "Invalid token format"
@@ -1568,36 +1831,49 @@ function verifyAuthToken(authToken) {
1568
1831
  // src/handlers/auth-login-requests.ts
1569
1832
  async function handleAuthLoginRequest(data, sendMessage) {
1570
1833
  try {
1834
+ logger.debug("[AUTH_LOGIN_REQ] Parsing incoming auth login request");
1571
1835
  const authRequest = AuthLoginRequestMessageSchema.parse(data);
1572
1836
  const { id, payload } = authRequest;
1573
1837
  const login_data = payload.login_data;
1574
1838
  const wsId = authRequest.from.id;
1839
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Processing auth login request from client: ${wsId}`);
1840
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data present: ${!!login_data}`);
1575
1841
  if (!login_data) {
1842
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Login data not found in request`);
1576
1843
  sendDataResponse2(id, {
1577
1844
  success: false,
1578
1845
  error: "Login data not found"
1579
1846
  }, sendMessage, wsId);
1580
1847
  return;
1581
1848
  }
1849
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Decoding base64 login data`);
1582
1850
  let loginData;
1583
1851
  try {
1584
1852
  loginData = decodeBase64ToJson(login_data);
1853
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data decoded successfully`);
1585
1854
  } catch (error) {
1855
+ const errorMsg = error instanceof Error ? error.message : String(error);
1856
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Failed to decode login data: ${errorMsg}`);
1857
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Decode error details:`, error);
1586
1858
  sendDataResponse2(id, {
1587
1859
  success: false,
1588
1860
  error: "Invalid login data format"
1589
1861
  }, sendMessage, wsId);
1590
1862
  return;
1591
1863
  }
1592
- const { username, password } = loginData;
1593
- if (!username) {
1864
+ const { username, email, password } = loginData;
1865
+ const identifier = username || email;
1866
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Validating credentials - username: ${username ? "\u2713" : "\u2717"}, email: ${email ? "\u2713" : "\u2717"}, password: ${password ? "\u2713" : "\u2717"}`);
1867
+ if (!identifier) {
1868
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Username or email not found in login data`);
1594
1869
  sendDataResponse2(id, {
1595
1870
  success: false,
1596
- error: "Username not found in login data"
1871
+ error: "Username or email is required"
1597
1872
  }, sendMessage, wsId);
1598
1873
  return;
1599
1874
  }
1600
1875
  if (!password) {
1876
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Password not found in login data`);
1601
1877
  sendDataResponse2(id, {
1602
1878
  success: false,
1603
1879
  error: "Password not found in login data"
@@ -1605,20 +1881,46 @@ async function handleAuthLoginRequest(data, sendMessage) {
1605
1881
  return;
1606
1882
  }
1607
1883
  if (!wsId) {
1884
+ logger.error(`[AUTH_LOGIN_REQ ${id}] WebSocket ID not found in request`);
1608
1885
  sendDataResponse2(id, {
1609
1886
  success: false,
1610
1887
  error: "WebSocket ID not found"
1611
1888
  }, sendMessage, wsId);
1612
1889
  return;
1613
1890
  }
1891
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Credentials validated, authenticating user: ${identifier}`);
1892
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] WebSocket ID: ${wsId}`);
1893
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Calling authenticateAndStoreWsId for user: ${identifier}`);
1614
1894
  const authResult = authenticateAndStoreWsId(
1615
- { username, password },
1895
+ { username, email, password },
1616
1896
  wsId
1617
1897
  );
1898
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Authentication result for ${identifier}: ${authResult.success ? "success" : "failed"}`);
1899
+ if (!authResult.success) {
1900
+ logger.warn(`[AUTH_LOGIN_REQ ${id}] Authentication failed for ${identifier}: ${authResult.error}`);
1901
+ } else {
1902
+ logger.info(`[AUTH_LOGIN_REQ ${id}] User ${authResult.username || identifier} authenticated successfully`);
1903
+ }
1904
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Sending auth response to client`);
1618
1905
  sendDataResponse2(id, authResult, sendMessage, wsId);
1906
+ logger.info(`[AUTH_LOGIN_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth login request completed`);
1619
1907
  return;
1620
1908
  } catch (error) {
1621
- logger.error("Failed to handle auth login request:", error);
1909
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1910
+ const errorStack = error instanceof Error ? error.stack : void 0;
1911
+ logger.error(`[AUTH_LOGIN_REQ] Failed to handle auth login request: ${errorMessage}`);
1912
+ logger.debug(`[AUTH_LOGIN_REQ] Error stack trace:`, errorStack);
1913
+ try {
1914
+ const parsedData = data;
1915
+ if (parsedData?.id) {
1916
+ sendDataResponse2(parsedData.id, {
1917
+ success: false,
1918
+ error: `Internal error: ${errorMessage}`
1919
+ }, sendMessage, parsedData.from?.id);
1920
+ }
1921
+ } catch (sendError) {
1922
+ logger.error("[AUTH_LOGIN_REQ] Failed to send error response:", sendError);
1923
+ }
1622
1924
  }
1623
1925
  }
1624
1926
  function sendDataResponse2(id, res, sendMessage, clientId) {
@@ -1634,17 +1936,27 @@ function sendDataResponse2(id, res, sendMessage, clientId) {
1634
1936
  ...res
1635
1937
  }
1636
1938
  };
1939
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Sending ${res.success ? "successful" : "failed"} auth response to client: ${clientId}`);
1940
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
1941
+ if (res.error) {
1942
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Error message: ${res.error}`);
1943
+ }
1637
1944
  sendMessage(response);
1638
1945
  }
1639
1946
 
1640
1947
  // src/handlers/auth-verify-request.ts
1641
1948
  async function handleAuthVerifyRequest(data, sendMessage) {
1642
1949
  try {
1950
+ logger.debug("[AUTH_VERIFY_REQ] Parsing incoming auth verify request");
1643
1951
  const authRequest = AuthVerifyRequestMessageSchema.parse(data);
1644
1952
  const { id, payload } = authRequest;
1645
1953
  const token = payload.token;
1646
1954
  const wsId = authRequest.from.id;
1955
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Processing auth verify request from client: ${wsId}`);
1956
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Token present: ${!!token}`);
1957
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Token length: ${token ? token.length : 0} characters`);
1647
1958
  if (!token) {
1959
+ logger.error(`[AUTH_VERIFY_REQ ${id}] Token not found in request`);
1648
1960
  sendDataResponse3(id, {
1649
1961
  success: false,
1650
1962
  error: "Token not found"
@@ -1652,17 +1964,45 @@ async function handleAuthVerifyRequest(data, sendMessage) {
1652
1964
  return;
1653
1965
  }
1654
1966
  if (!wsId) {
1967
+ logger.error(`[AUTH_VERIFY_REQ ${id}] WebSocket ID not found in request`);
1655
1968
  sendDataResponse3(id, {
1656
1969
  success: false,
1657
1970
  error: "WebSocket ID not found"
1658
1971
  }, sendMessage, wsId);
1659
1972
  return;
1660
1973
  }
1974
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token validation starting`);
1975
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] WebSocket ID: ${wsId}`);
1976
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Calling verifyAuthToken`);
1977
+ const startTime = Date.now();
1661
1978
  const authResult = verifyAuthToken(token);
1979
+ const verificationTime = Date.now() - startTime;
1980
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token verification completed in ${verificationTime}ms - ${authResult.success ? "valid" : "invalid"}`);
1981
+ if (!authResult.success) {
1982
+ logger.warn(`[AUTH_VERIFY_REQ ${id}] Token verification failed: ${authResult.error}`);
1983
+ } else {
1984
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token verified successfully for user: ${authResult.data || "unknown"}`);
1985
+ }
1986
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Sending verification response to client`);
1662
1987
  sendDataResponse3(id, authResult, sendMessage, wsId);
1988
+ logger.info(`[AUTH_VERIFY_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth verify request completed`);
1663
1989
  return;
1664
1990
  } catch (error) {
1665
- logger.error("Failed to handle auth verify request:", error);
1991
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1992
+ const errorStack = error instanceof Error ? error.stack : void 0;
1993
+ logger.error(`[AUTH_VERIFY_REQ] Failed to handle auth verify request: ${errorMessage}`);
1994
+ logger.debug(`[AUTH_VERIFY_REQ] Error stack trace:`, errorStack);
1995
+ try {
1996
+ const parsedData = data;
1997
+ if (parsedData?.id) {
1998
+ sendDataResponse3(parsedData.id, {
1999
+ success: false,
2000
+ error: `Internal error: ${errorMessage}`
2001
+ }, sendMessage, parsedData.from?.id);
2002
+ }
2003
+ } catch (sendError) {
2004
+ logger.error("[AUTH_VERIFY_REQ] Failed to send error response:", sendError);
2005
+ }
1666
2006
  }
1667
2007
  }
1668
2008
  function sendDataResponse3(id, res, sendMessage, clientId) {
@@ -1678,59 +2018,26 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
1678
2018
  ...res
1679
2019
  }
1680
2020
  };
2021
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Sending ${res.success ? "successful" : "failed"} verification response to client: ${clientId}`);
2022
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
2023
+ if (res.error) {
2024
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Error message: ${res.error}`);
2025
+ }
2026
+ if (res.data) {
2027
+ logger.debug(`[AUTH_VERIFY_RES ${id}] User verified: ${res.data}`);
2028
+ }
1681
2029
  sendMessage(response);
1682
2030
  }
1683
2031
 
1684
2032
  // src/userResponse/groq.ts
1685
2033
  var import_dotenv = __toESM(require_main());
1686
2034
 
1687
- // src/userResponse/utils.ts
1688
- function convertTopToLimit(query) {
1689
- if (!query || query.trim().length === 0) {
1690
- return query;
1691
- }
1692
- let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
1693
- if (modifiedQuery !== query) {
1694
- console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
1695
- }
1696
- return modifiedQuery;
1697
- }
1698
- function ensureQueryLimit(query, defaultLimit = 50) {
1699
- if (!query || query.trim().length === 0) {
1700
- return query;
1701
- }
1702
- let trimmedQuery = query.trim();
1703
- const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
1704
- if (!isSelectQuery) {
1705
- return query;
1706
- }
1707
- trimmedQuery = convertTopToLimit(trimmedQuery);
1708
- const hadSemicolon = trimmedQuery.endsWith(";");
1709
- if (hadSemicolon) {
1710
- trimmedQuery = trimmedQuery.slice(0, -1).trim();
1711
- }
1712
- const limitMatches = trimmedQuery.match(/\bLIMIT\s+\d+\b/gi);
1713
- if (limitMatches && limitMatches.length > 0) {
1714
- if (limitMatches.length > 1) {
1715
- console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
1716
- trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
1717
- } else {
1718
- if (hadSemicolon) {
1719
- trimmedQuery += ";";
1720
- }
1721
- return trimmedQuery;
1722
- }
1723
- }
1724
- trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
1725
- if (hadSemicolon) {
1726
- trimmedQuery += ";";
1727
- }
1728
- return trimmedQuery;
1729
- }
2035
+ // src/userResponse/base-llm.ts
2036
+ init_utils();
1730
2037
 
1731
2038
  // src/userResponse/schema.ts
1732
2039
  import path2 from "path";
1733
- import fs2 from "fs";
2040
+ import fs3 from "fs";
1734
2041
  var Schema = class {
1735
2042
  constructor(schemaFilePath) {
1736
2043
  this.cachedSchema = null;
@@ -1744,11 +2051,11 @@ var Schema = class {
1744
2051
  logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
1745
2052
  try {
1746
2053
  const dir = path2.dirname(this.schemaFilePath);
1747
- if (!fs2.existsSync(dir)) {
2054
+ if (!fs3.existsSync(dir)) {
1748
2055
  logger.info(`Creating directory structure: ${dir}`);
1749
- fs2.mkdirSync(dir, { recursive: true });
2056
+ fs3.mkdirSync(dir, { recursive: true });
1750
2057
  }
1751
- if (!fs2.existsSync(this.schemaFilePath)) {
2058
+ if (!fs3.existsSync(this.schemaFilePath)) {
1752
2059
  logger.info(`Schema file does not exist at ${this.schemaFilePath}, creating with empty schema`);
1753
2060
  const initialSchema = {
1754
2061
  database: "",
@@ -1757,11 +2064,11 @@ var Schema = class {
1757
2064
  tables: [],
1758
2065
  relationships: []
1759
2066
  };
1760
- fs2.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
2067
+ fs3.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
1761
2068
  this.cachedSchema = initialSchema;
1762
2069
  return initialSchema;
1763
2070
  }
1764
- const fileContent = fs2.readFileSync(this.schemaFilePath, "utf-8");
2071
+ const fileContent = fs3.readFileSync(this.schemaFilePath, "utf-8");
1765
2072
  const schema2 = JSON.parse(fileContent);
1766
2073
  this.cachedSchema = schema2;
1767
2074
  return schema2;
@@ -1862,7 +2169,7 @@ var Schema = class {
1862
2169
  var schema = new Schema();
1863
2170
 
1864
2171
  // src/userResponse/prompt-loader.ts
1865
- import fs3 from "fs";
2172
+ import fs4 from "fs";
1866
2173
  import path3 from "path";
1867
2174
  var PromptLoader = class {
1868
2175
  constructor(config) {
@@ -1889,7 +2196,9 @@ var PromptLoader = class {
1889
2196
  "single-component",
1890
2197
  "mutli-component",
1891
2198
  "actions",
1892
- "container-metadata"
2199
+ "container-metadata",
2200
+ "text-response",
2201
+ "match-text-components"
1893
2202
  ];
1894
2203
  for (const promptType of promptTypes) {
1895
2204
  try {
@@ -1914,9 +2223,9 @@ var PromptLoader = class {
1914
2223
  try {
1915
2224
  const systemPath = path3.join(dir, promptName, "system.md");
1916
2225
  const userPath = path3.join(dir, promptName, "user.md");
1917
- if (fs3.existsSync(systemPath) && fs3.existsSync(userPath)) {
1918
- const system = fs3.readFileSync(systemPath, "utf-8");
1919
- const user = fs3.readFileSync(userPath, "utf-8");
2226
+ if (fs4.existsSync(systemPath) && fs4.existsSync(userPath)) {
2227
+ const system = fs4.readFileSync(systemPath, "utf-8");
2228
+ const user = fs4.readFileSync(userPath, "utf-8");
1920
2229
  logger.debug(`Loaded prompt '${promptName}' from ${dir}`);
1921
2230
  return { system, user };
1922
2231
  }
@@ -2038,6 +2347,14 @@ var LLM = class {
2038
2347
  throw new Error(`Unsupported provider: ${provider}. Use "anthropic" or "groq"`);
2039
2348
  }
2040
2349
  }
2350
+ /* Stream response with tool calling support (Anthropic only for now) */
2351
+ static async streamWithTools(messages, tools, toolHandler, options = {}, maxIterations = 3) {
2352
+ const [provider, modelName] = this._parseModel(options.model);
2353
+ if (provider !== "anthropic") {
2354
+ throw new Error(`Tool calling is only supported for Anthropic models`);
2355
+ }
2356
+ return this._anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations);
2357
+ }
2041
2358
  // ============================================================
2042
2359
  // PRIVATE HELPER METHODS
2043
2360
  // ============================================================
@@ -2115,6 +2432,86 @@ var LLM = class {
2115
2432
  }
2116
2433
  return fullText;
2117
2434
  }
2435
+ static async _anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
2436
+ const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
2437
+ const client = new Anthropic({
2438
+ apiKey
2439
+ });
2440
+ const conversationMessages = [{
2441
+ role: "user",
2442
+ content: messages.user
2443
+ }];
2444
+ let iterations = 0;
2445
+ let finalText = "";
2446
+ while (iterations < maxIterations) {
2447
+ iterations++;
2448
+ const response = await client.messages.create({
2449
+ model: modelName,
2450
+ max_tokens: options.maxTokens || 4e3,
2451
+ temperature: options.temperature,
2452
+ system: messages.sys,
2453
+ messages: conversationMessages,
2454
+ tools
2455
+ });
2456
+ if (response.stop_reason === "end_turn") {
2457
+ const textBlock = response.content.find((block) => block.type === "text");
2458
+ if (textBlock && textBlock.type === "text") {
2459
+ finalText = textBlock.text;
2460
+ if (options.partial) {
2461
+ options.partial(finalText);
2462
+ }
2463
+ }
2464
+ break;
2465
+ }
2466
+ if (response.stop_reason === "tool_use") {
2467
+ const toolUses = response.content.filter((block) => block.type === "tool_use");
2468
+ if (toolUses.length === 0) {
2469
+ break;
2470
+ }
2471
+ conversationMessages.push({
2472
+ role: "assistant",
2473
+ content: response.content
2474
+ });
2475
+ const toolResults = {
2476
+ role: "user",
2477
+ content: []
2478
+ };
2479
+ for (const toolUse of toolUses) {
2480
+ if (toolUse.type === "tool_use") {
2481
+ try {
2482
+ const result = await toolHandler(toolUse.name, toolUse.input);
2483
+ toolResults.content.push({
2484
+ type: "tool_result",
2485
+ tool_use_id: toolUse.id,
2486
+ content: typeof result === "string" ? result : JSON.stringify(result)
2487
+ });
2488
+ } catch (error) {
2489
+ toolResults.content.push({
2490
+ type: "tool_result",
2491
+ tool_use_id: toolUse.id,
2492
+ content: error instanceof Error ? error.message : String(error),
2493
+ is_error: true
2494
+ });
2495
+ }
2496
+ }
2497
+ }
2498
+ conversationMessages.push(toolResults);
2499
+ } else {
2500
+ const textBlock = response.content.find((block) => block.type === "text");
2501
+ if (textBlock && textBlock.type === "text") {
2502
+ finalText = textBlock.text;
2503
+ if (options.partial) {
2504
+ options.partial(finalText);
2505
+ }
2506
+ }
2507
+ break;
2508
+ }
2509
+ }
2510
+ if (iterations >= maxIterations) {
2511
+ throw new Error(`Max iterations (${maxIterations}) reached in tool calling loop`);
2512
+ }
2513
+ return finalText;
2514
+ }
2118
2515
  // ============================================================
2119
2516
  // GROQ IMPLEMENTATION
2120
2517
  // ============================================================
@@ -2241,7 +2638,9 @@ var BaseLLM = class {
2241
2638
  needsMultipleComponents: result.needsMultipleComponents || false
2242
2639
  };
2243
2640
  } catch (error) {
2244
- console.error("Error classifying user question:", error);
2641
+ const errorMsg = error instanceof Error ? error.message : String(error);
2642
+ logger.error(`[${this.getProviderName()}] Error classifying user question: ${errorMsg}`);
2643
+ logger.debug(`[${this.getProviderName()}] Classification error details:`, error);
2245
2644
  throw error;
2246
2645
  }
2247
2646
  }
@@ -2279,6 +2678,7 @@ var BaseLLM = class {
2279
2678
  );
2280
2679
  const props = result.props || originalProps;
2281
2680
  if (props && props.query) {
2681
+ props.query = fixScalarSubqueries(props.query);
2282
2682
  props.query = ensureQueryLimit(props.query, this.defaultLimit);
2283
2683
  }
2284
2684
  if (props && props.query) {
@@ -2305,7 +2705,9 @@ var BaseLLM = class {
2305
2705
  modifications: result.modifications || []
2306
2706
  };
2307
2707
  } catch (error) {
2308
- console.error(`Error validating/modifying props with ${this.getProviderName()}:`, error);
2708
+ const errorMsg = error instanceof Error ? error.message : String(error);
2709
+ logger.error(`[${this.getProviderName()}] Error validating/modifying props: ${errorMsg}`);
2710
+ logger.debug(`[${this.getProviderName()}] Props validation error details:`, error);
2309
2711
  throw error;
2310
2712
  }
2311
2713
  }
@@ -2425,7 +2827,9 @@ var BaseLLM = class {
2425
2827
  isGenerated: true
2426
2828
  };
2427
2829
  } catch (error) {
2428
- console.error("Error generating analytical component:", error);
2830
+ const errorMsg = error instanceof Error ? error.message : String(error);
2831
+ logger.error(`[${this.getProviderName()}] Error generating analytical component: ${errorMsg}`);
2832
+ logger.debug(`[${this.getProviderName()}] Analytical component generation error details:`, error);
2429
2833
  throw error;
2430
2834
  }
2431
2835
  }
@@ -2467,7 +2871,9 @@ var BaseLLM = class {
2467
2871
  description: result.description || `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
2468
2872
  };
2469
2873
  } catch (error) {
2470
- console.error("Error generating container metadata:", error);
2874
+ const errorMsg = error instanceof Error ? error.message : String(error);
2875
+ logger.error(`[${this.getProviderName()}] Error generating container metadata: ${errorMsg}`);
2876
+ logger.debug(`[${this.getProviderName()}] Container metadata error details:`, error);
2471
2877
  return {
2472
2878
  title: `${userPrompt} - Dashboard`,
2473
2879
  description: `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
@@ -2519,24 +2925,24 @@ var BaseLLM = class {
2519
2925
  component = components[componentIndex - 1];
2520
2926
  }
2521
2927
  const matchedMsg = `${this.getProviderName()} matched component: ${component?.name || "None"}`;
2522
- console.log("\u2713", matchedMsg);
2928
+ logger.info(`[${this.getProviderName()}] \u2713 ${matchedMsg}`);
2523
2929
  logCollector?.info(matchedMsg);
2524
2930
  if (result.alternativeMatches && result.alternativeMatches.length > 0) {
2525
- console.log(" Alternative matches:");
2931
+ logger.debug(`[${this.getProviderName()}] Alternative matches found: ${result.alternativeMatches.length}`);
2526
2932
  const altMatches = result.alternativeMatches.map(
2527
2933
  (alt) => `${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`
2528
2934
  ).join(" | ");
2529
2935
  logCollector?.info(`Alternative matches: ${altMatches}`);
2530
2936
  result.alternativeMatches.forEach((alt) => {
2531
- console.log(` - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
2937
+ logger.debug(`[${this.getProviderName()}] - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
2532
2938
  });
2533
2939
  }
2534
2940
  if (!component) {
2535
2941
  const noMatchMsg = `No matching component found (confidence: ${confidence}%)`;
2536
- console.log("\u2717", noMatchMsg);
2942
+ logger.warn(`[${this.getProviderName()}] \u2717 ${noMatchMsg}`);
2537
2943
  logCollector?.warn(noMatchMsg);
2538
2944
  const genMsg = "Attempting to match component from analytical question...";
2539
- console.log("\u2713", genMsg);
2945
+ logger.info(`[${this.getProviderName()}] \u2713 ${genMsg}`);
2540
2946
  logCollector?.info(genMsg);
2541
2947
  const generatedResult = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2542
2948
  if (generatedResult.component) {
@@ -2597,8 +3003,10 @@ var BaseLLM = class {
2597
3003
  confidence
2598
3004
  };
2599
3005
  } catch (error) {
2600
- console.error(`Error matching component with ${this.getProviderName()}:`, error);
2601
- logCollector?.error(`Error matching component: ${error.message}`);
3006
+ const errorMsg = error instanceof Error ? error.message : String(error);
3007
+ logger.error(`[${this.getProviderName()}] Error matching component: ${errorMsg}`);
3008
+ logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
3009
+ logCollector?.error(`Error matching component: ${errorMsg}`);
2602
3010
  throw error;
2603
3011
  }
2604
3012
  }
@@ -2629,7 +3037,9 @@ var BaseLLM = class {
2629
3037
  isGenerated: true
2630
3038
  };
2631
3039
  } catch (error) {
2632
- console.error("Error matching multiple analytical components:", error);
3040
+ const errorMsg = error instanceof Error ? error.message : String(error);
3041
+ logger.error(`[${this.getProviderName()}] Error matching multiple analytical components: ${errorMsg}`);
3042
+ logger.debug(`[${this.getProviderName()}] Multiple components matching error details:`, error);
2633
3043
  return {
2634
3044
  components: [],
2635
3045
  reasoning: "Error occurred while matching components",
@@ -2708,63 +3118,538 @@ var BaseLLM = class {
2708
3118
  isGenerated: true
2709
3119
  };
2710
3120
  } catch (error) {
2711
- console.error("Error generating multi-component response:", error);
3121
+ const errorMsg = error instanceof Error ? error.message : String(error);
3122
+ logger.error(`[${this.getProviderName()}] Error generating multi-component response: ${errorMsg}`);
3123
+ logger.debug(`[${this.getProviderName()}] Multi-component response error details:`, error);
2712
3124
  throw error;
2713
3125
  }
2714
3126
  }
2715
3127
  /**
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)
3128
+ * Match components from text response suggestions
3129
+ * Takes a text response with component suggestions (c1:type format) and matches with available components
3130
+ * @param textResponse - The text response containing component suggestions
3131
+ * @param components - List of available components
3132
+ * @param apiKey - Optional API key
3133
+ * @param logCollector - Optional log collector
3134
+ * @returns Object containing matched components, selected layout, and reasoning
2719
3135
  */
2720
- async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory) {
3136
+ async matchComponentsFromTextResponse(textResponse, components, apiKey, logCollector) {
2721
3137
  try {
2722
- const classifyMsg = "Classifying user question...";
2723
- logCollector?.info(classifyMsg);
2724
- const classification = await this.classifyUserQuestion(userPrompt, apiKey, logCollector, conversationHistory);
2725
- const classInfo = `Question type: ${classification.questionType}, Visualizations: ${classification.visualizations.join(", ") || "None"}, Multiple components: ${classification.needsMultipleComponents}`;
2726
- logCollector?.info(classInfo);
2727
- if (classification.questionType === "analytical") {
2728
- if (classification.visualizations.length > 1) {
2729
- const multiMsg = `Matching ${classification.visualizations.length} components for types: ${classification.visualizations.join(", ")}`;
2730
- logCollector?.info(multiMsg);
2731
- const componentPromises = classification.visualizations.map((vizType) => {
2732
- logCollector?.info(`Matching component for type: ${vizType}`);
2733
- return this.generateAnalyticalComponent(
2734
- userPrompt,
2735
- components,
2736
- vizType,
2737
- apiKey,
2738
- logCollector,
2739
- conversationHistory
2740
- ).then((result) => ({ vizType, result }));
2741
- });
2742
- const settledResults = await Promise.allSettled(componentPromises);
2743
- const matchedComponents = [];
2744
- for (const settledResult of settledResults) {
2745
- if (settledResult.status === "fulfilled") {
2746
- const { vizType, result } = settledResult.value;
2747
- if (result.component) {
2748
- matchedComponents.push(result.component);
2749
- logCollector?.info(`Matched: ${result.component.name}`);
2750
- logger.info("Component : ", result.component.name, " props: ", result.component.props);
2751
- } else {
2752
- logCollector?.warn(`Failed to match component for type: ${vizType}`);
2753
- }
2754
- } else {
2755
- logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
2756
- }
3138
+ logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
3139
+ let availableComponentsText = "No components available";
3140
+ if (components && components.length > 0) {
3141
+ availableComponentsText = components.map((comp, idx) => {
3142
+ const keywords = comp.keywords ? comp.keywords.join(", ") : "";
3143
+ const propsPreview = comp.props ? JSON.stringify(comp.props, null, 2) : "No props";
3144
+ return `${idx + 1}. ID: ${comp.id}
3145
+ Name: ${comp.name}
3146
+ Type: ${comp.type}
3147
+ Description: ${comp.description || "No description"}
3148
+ Keywords: ${keywords}
3149
+ Props Structure: ${propsPreview}`;
3150
+ }).join("\n\n");
3151
+ }
3152
+ const schemaDoc = schema.generateSchemaDocumentation();
3153
+ const prompts = await promptLoader.loadPrompts("match-text-components", {
3154
+ TEXT_RESPONSE: textResponse,
3155
+ AVAILABLE_COMPONENTS: availableComponentsText,
3156
+ SCHEMA_DOC: schemaDoc
3157
+ });
3158
+ logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
3159
+ logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
3160
+ logCollector?.info("Matching components from text response...");
3161
+ const rawResponse = await LLM.stream(
3162
+ {
3163
+ sys: prompts.system,
3164
+ user: prompts.user
3165
+ },
3166
+ {
3167
+ model: this.model,
3168
+ maxTokens: 3e3,
3169
+ temperature: 0.2,
3170
+ apiKey: this.getApiKey(apiKey)
3171
+ },
3172
+ false
3173
+ // Don't parse as JSON yet, get raw response
3174
+ );
3175
+ logger.debug(`[${this.getProviderName()}] Raw component matching response length: ${rawResponse?.length || 0}`);
3176
+ logger.file(`[${this.getProviderName()}] Component matching raw response:`, rawResponse);
3177
+ let result;
3178
+ try {
3179
+ let cleanedResponse = rawResponse || "";
3180
+ cleanedResponse = cleanedResponse.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
3181
+ const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
3182
+ if (jsonMatch) {
3183
+ cleanedResponse = jsonMatch[0];
3184
+ }
3185
+ result = JSON.parse(cleanedResponse);
3186
+ } catch (parseError) {
3187
+ logger.error(`[${this.getProviderName()}] Failed to parse component matching JSON response`);
3188
+ const errorMsg = parseError instanceof Error ? parseError.message : String(parseError);
3189
+ const posMatch = errorMsg.match(/position (\d+)/);
3190
+ const errorPos = posMatch ? parseInt(posMatch[1]) : -1;
3191
+ if (errorPos > 0 && rawResponse) {
3192
+ const start = Math.max(0, errorPos - 200);
3193
+ const end = Math.min(rawResponse.length, errorPos + 200);
3194
+ logger.debug(`[${this.getProviderName()}] Error context (position ${errorPos}):`);
3195
+ logger.debug(rawResponse.substring(start, end));
3196
+ logger.debug(" ".repeat(Math.min(200, errorPos - start)) + "^--- Error here");
3197
+ }
3198
+ logger.debug(`[${this.getProviderName()}] Raw response (first 2000 chars):`, rawResponse?.substring(0, 2e3));
3199
+ logger.debug(`[${this.getProviderName()}] Parse error:`, parseError);
3200
+ logCollector?.error(`Failed to parse component matching response: ${errorMsg}`);
3201
+ try {
3202
+ logger.info(`[${this.getProviderName()}] Attempting aggressive JSON cleanup...`);
3203
+ let aggressive = rawResponse || "";
3204
+ aggressive = aggressive.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
3205
+ const match = aggressive.match(/\{[\s\S]*\}/);
3206
+ if (match) {
3207
+ aggressive = match[0];
2757
3208
  }
2758
- console.log("matched components: after multi comp", matchedComponents.length);
3209
+ aggressive = aggressive.replace(/,(\s*[}\]])/g, "$1");
3210
+ result = JSON.parse(aggressive);
3211
+ logger.info(`[${this.getProviderName()}] Aggressive cleanup succeeded!`);
3212
+ } catch (secondError) {
3213
+ logger.error(`[${this.getProviderName()}] Aggressive cleanup also failed`);
3214
+ return {
3215
+ components: [],
3216
+ selectedLayout: "MultiComponentContainer",
3217
+ selectedLayoutId: "",
3218
+ selectedLayoutComponent: null,
3219
+ layoutReasoning: "No layout selected"
3220
+ };
3221
+ }
3222
+ }
3223
+ const matchedComponents = result.matchedComponents || [];
3224
+ const selectedLayout = result.selectedLayout || "MultiComponentContainer";
3225
+ const selectedLayoutId = result.selectedLayoutId || "";
3226
+ const layoutReasoning = result.layoutReasoning || "No layout reasoning provided";
3227
+ let selectedLayoutComponent = null;
3228
+ if (selectedLayoutId) {
3229
+ selectedLayoutComponent = components.find((c) => c.id === selectedLayoutId) || null;
3230
+ if (!selectedLayoutComponent) {
3231
+ logger.warn(`[${this.getProviderName()}] Layout component ${selectedLayoutId} not found in available components`);
3232
+ }
3233
+ }
3234
+ logger.info(`[${this.getProviderName()}] Matched ${matchedComponents.length} components from text response`);
3235
+ logger.info(`[${this.getProviderName()}] Selected layout: ${selectedLayout} (ID: ${selectedLayoutId})`);
3236
+ logger.info(`[${this.getProviderName()}] Layout reasoning: ${layoutReasoning}`);
3237
+ if (matchedComponents.length > 0) {
3238
+ logCollector?.info(`Matched ${matchedComponents.length} components for visualization using ${selectedLayout}`);
3239
+ logCollector?.info(`Layout reasoning: ${layoutReasoning}`);
3240
+ matchedComponents.forEach((comp, idx) => {
3241
+ logCollector?.info(` ${idx + 1}. ${comp.componentName} (${comp.componentType}): ${comp.reasoning}`);
3242
+ if (comp.props?.query) {
3243
+ logCollector?.logQuery(
3244
+ `Component ${idx + 1} query`,
3245
+ comp.props.query,
3246
+ { componentName: comp.componentName, title: comp.props.title }
3247
+ );
3248
+ }
3249
+ });
3250
+ }
3251
+ const finalComponents = matchedComponents.map((mc) => {
3252
+ const originalComponent = components.find((c) => c.id === mc.componentId);
3253
+ if (!originalComponent) {
3254
+ logger.warn(`[${this.getProviderName()}] Component ${mc.componentId} not found in available components`);
3255
+ return null;
3256
+ }
3257
+ return {
3258
+ ...originalComponent,
3259
+ props: {
3260
+ ...originalComponent.props,
3261
+ ...mc.props
3262
+ }
3263
+ };
3264
+ }).filter(Boolean);
3265
+ return {
3266
+ components: finalComponents,
3267
+ selectedLayout,
3268
+ selectedLayoutId,
3269
+ selectedLayoutComponent,
3270
+ layoutReasoning
3271
+ };
3272
+ } catch (error) {
3273
+ const errorMsg = error instanceof Error ? error.message : String(error);
3274
+ logger.error(`[${this.getProviderName()}] Error matching components from text response: ${errorMsg}`);
3275
+ logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
3276
+ logCollector?.error(`Error matching components: ${errorMsg}`);
3277
+ return {
3278
+ components: [],
3279
+ selectedLayout: "MultiComponentContainer",
3280
+ selectedLayoutId: "",
3281
+ selectedLayoutComponent: null,
3282
+ layoutReasoning: "Error occurred during component matching"
3283
+ };
3284
+ }
3285
+ }
3286
+ /**
3287
+ * Generate text-based response for user question
3288
+ * This provides conversational text responses instead of component generation
3289
+ * Supports tool calling for query execution with automatic retry on errors (max 3 attempts)
3290
+ * After generating text response, if components are provided, matches suggested components
3291
+ * @param streamCallback - Optional callback function to receive text chunks as they stream
3292
+ * @param collections - Collection registry for executing database queries via database.execute
3293
+ * @param components - Optional list of available components for matching suggestions
3294
+ */
3295
+ async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory, streamCallback, collections, components) {
3296
+ const errors = [];
3297
+ logger.debug(`[${this.getProviderName()}] Starting text response generation`);
3298
+ logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
3299
+ try {
3300
+ const schemaDoc = schema.generateSchemaDocumentation();
3301
+ const prompts = await promptLoader.loadPrompts("text-response", {
3302
+ USER_PROMPT: userPrompt,
3303
+ CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
3304
+ SCHEMA_DOC: schemaDoc
3305
+ });
3306
+ logger.file("\n=============================\nsystem prompt:", prompts.system);
3307
+ logger.file("\n=============================\nuser prompt:", prompts.user);
3308
+ logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
3309
+ logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
3310
+ logCollector?.info("Generating text response with query execution capability...");
3311
+ const tools = [{
3312
+ name: "execute_query",
3313
+ description: "Executes a SQL query against the database and returns the results. Use this when the user asks for data. If the query fails, you will receive the error and can retry with a corrected query.",
3314
+ input_schema: {
3315
+ type: "object",
3316
+ properties: {
3317
+ query: {
3318
+ type: "string",
3319
+ description: "The SQL query to execute. Must be valid SQL syntax using table and column names from the schema."
3320
+ },
3321
+ reasoning: {
3322
+ type: "string",
3323
+ description: "Brief explanation of what this query does and why it answers the user's question."
3324
+ }
3325
+ },
3326
+ required: ["query"]
3327
+ }
3328
+ }];
3329
+ const queryAttempts = /* @__PURE__ */ new Map();
3330
+ const MAX_QUERY_ATTEMPTS = 6;
3331
+ let maxAttemptsReached = false;
3332
+ let fullStreamedText = "";
3333
+ const wrappedStreamCallback = streamCallback ? (chunk) => {
3334
+ fullStreamedText += chunk;
3335
+ streamCallback(chunk);
3336
+ } : void 0;
3337
+ const toolHandler = async (toolName, toolInput) => {
3338
+ if (toolName === "execute_query") {
3339
+ let query = toolInput.query;
3340
+ const reasoning = toolInput.reasoning;
3341
+ const { ensureQueryLimit: ensureQueryLimit2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
3342
+ query = ensureQueryLimit2(query, 32, 32);
3343
+ const queryKey = query.toLowerCase().replace(/\s+/g, " ").trim();
3344
+ const attempts = (queryAttempts.get(queryKey) || 0) + 1;
3345
+ queryAttempts.set(queryKey, attempts);
3346
+ logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${query.substring(0, 100)}...`);
3347
+ if (reasoning) {
3348
+ logCollector?.info(`Query reasoning: ${reasoning}`);
3349
+ }
3350
+ if (attempts > MAX_QUERY_ATTEMPTS) {
3351
+ const errorMsg = `Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`;
3352
+ logger.error(`[${this.getProviderName()}] ${errorMsg}`);
3353
+ logCollector?.error(errorMsg);
3354
+ maxAttemptsReached = true;
3355
+ if (wrappedStreamCallback) {
3356
+ wrappedStreamCallback(`
3357
+
3358
+ \u274C ${errorMsg}
3359
+
3360
+ Please try rephrasing your question or simplifying your request.
3361
+
3362
+ `);
3363
+ }
3364
+ throw new Error(errorMsg);
3365
+ }
3366
+ try {
3367
+ if (wrappedStreamCallback) {
3368
+ if (attempts === 1) {
3369
+ wrappedStreamCallback(`
3370
+
3371
+ \u{1F50D} **Analyzing your question...**
3372
+
3373
+ `);
3374
+ if (reasoning) {
3375
+ wrappedStreamCallback(`\u{1F4AD} ${reasoning}
3376
+
3377
+ `);
3378
+ }
3379
+ wrappedStreamCallback(`\u{1F4DD} **Generated SQL Query:**
3380
+ \`\`\`sql
3381
+ ${query}
3382
+ \`\`\`
3383
+
3384
+ `);
3385
+ wrappedStreamCallback(`\u26A1 **Executing query...**
3386
+
3387
+ `);
3388
+ } else {
3389
+ wrappedStreamCallback(`
3390
+
3391
+ \u{1F504} **Retrying with corrected query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS})...**
3392
+
3393
+ `);
3394
+ if (reasoning) {
3395
+ wrappedStreamCallback(`\u{1F4AD} ${reasoning}
3396
+
3397
+ `);
3398
+ }
3399
+ wrappedStreamCallback(`\u{1F4DD} **Corrected SQL Query:**
3400
+ \`\`\`sql
3401
+ ${query}
3402
+ \`\`\`
3403
+
3404
+ `);
3405
+ wrappedStreamCallback(`\u26A1 **Executing query...**
3406
+
3407
+ `);
3408
+ }
3409
+ }
3410
+ logCollector?.logQuery(
3411
+ `Executing SQL query (attempt ${attempts})`,
3412
+ query,
3413
+ { reasoning, attempt: attempts }
3414
+ );
3415
+ if (!collections || !collections["database"] || !collections["database"]["execute"]) {
3416
+ throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
3417
+ }
3418
+ const result2 = await collections["database"]["execute"]({ sql: query });
3419
+ const data = result2?.data || result2;
3420
+ const rowCount = result2?.count ?? (Array.isArray(data) ? data.length : "N/A");
3421
+ logger.info(`[${this.getProviderName()}] Query executed successfully, rows returned: ${rowCount}`);
3422
+ logCollector?.info(`Query successful, returned ${rowCount} rows`);
3423
+ if (wrappedStreamCallback) {
3424
+ wrappedStreamCallback(`\u2705 **Query executed successfully!**
3425
+
3426
+ `);
3427
+ if (Array.isArray(data) && data.length > 0) {
3428
+ const firstRow = data[0];
3429
+ const columns = Object.keys(firstRow);
3430
+ if (data.length === 1 && columns.length === 1) {
3431
+ const value = firstRow[columns[0]];
3432
+ wrappedStreamCallback(`**Result:** ${value}
3433
+
3434
+ `);
3435
+ } else if (data.length > 0) {
3436
+ wrappedStreamCallback(`**Retrieved ${rowCount} rows**
3437
+
3438
+ `);
3439
+ wrappedStreamCallback(`<DataTable>${JSON.stringify(data)}</DataTable>
3440
+
3441
+ `);
3442
+ }
3443
+ } else if (Array.isArray(data) && data.length === 0) {
3444
+ wrappedStreamCallback(`**No rows returned.**
3445
+
3446
+ `);
3447
+ }
3448
+ wrappedStreamCallback(`\u{1F4CA} **Analyzing results...**
3449
+
3450
+ `);
3451
+ }
3452
+ return JSON.stringify(data, null, 2);
3453
+ } catch (error) {
3454
+ const errorMsg = error instanceof Error ? error.message : String(error);
3455
+ logger.error(`[${this.getProviderName()}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
3456
+ logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
3457
+ if (wrappedStreamCallback) {
3458
+ wrappedStreamCallback(`\u274C **Query execution failed:**
3459
+ \`\`\`
3460
+ ${errorMsg}
3461
+ \`\`\`
3462
+
3463
+ `);
3464
+ if (attempts < MAX_QUERY_ATTEMPTS) {
3465
+ wrappedStreamCallback(`\u{1F527} **Generating corrected query...**
3466
+
3467
+ `);
3468
+ }
3469
+ }
3470
+ throw new Error(`Query execution failed: ${errorMsg}`);
3471
+ }
3472
+ }
3473
+ throw new Error(`Unknown tool: ${toolName}`);
3474
+ };
3475
+ const result = await LLM.streamWithTools(
3476
+ {
3477
+ sys: prompts.system,
3478
+ user: prompts.user
3479
+ },
3480
+ tools,
3481
+ toolHandler,
3482
+ {
3483
+ model: this.model,
3484
+ maxTokens: 4e3,
3485
+ temperature: 0.7,
3486
+ apiKey: this.getApiKey(apiKey),
3487
+ partial: wrappedStreamCallback
3488
+ // Pass the wrapped streaming callback to LLM
3489
+ },
3490
+ 10
3491
+ // max iterations: allows for 6 retries + final response + buffer
3492
+ );
3493
+ logger.info(`[${this.getProviderName()}] Text response stream completed`);
3494
+ const textResponse = fullStreamedText || result || "I apologize, but I was unable to generate a response.";
3495
+ if (maxAttemptsReached) {
3496
+ logger.warn(`[${this.getProviderName()}] Max query attempts reached, returning failure response`);
3497
+ logCollector?.error("Failed to generate valid query after maximum attempts");
3498
+ return {
3499
+ success: false,
3500
+ errors: [`Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`],
3501
+ data: {
3502
+ text: textResponse,
3503
+ // Include the streamed text showing all attempts
3504
+ matchedComponents: [],
3505
+ method: `${this.getProviderName()}-text-response-max-attempts`
3506
+ }
3507
+ };
3508
+ }
3509
+ logCollector?.info(`Text response: ${textResponse.substring(0, 100)}${textResponse.length > 100 ? "..." : ""}`);
3510
+ logCollector?.logExplanation(
3511
+ "Text response generated",
3512
+ "Generated plain text response with component suggestions",
3513
+ {
3514
+ textLength: textResponse.length
3515
+ }
3516
+ );
3517
+ let matchedComponents = [];
3518
+ let selectedLayoutComponent = null;
3519
+ let layoutReasoning = "No layout selected";
3520
+ if (components && components.length > 0) {
3521
+ logger.info(`[${this.getProviderName()}] Matching components from text response...`);
3522
+ const matchResult = await this.matchComponentsFromTextResponse(
3523
+ textResponse,
3524
+ components,
3525
+ apiKey,
3526
+ logCollector
3527
+ );
3528
+ matchedComponents = matchResult.components;
3529
+ selectedLayoutComponent = matchResult.selectedLayoutComponent;
3530
+ layoutReasoning = matchResult.layoutReasoning;
3531
+ }
3532
+ let container_componet = null;
3533
+ if (matchedComponents.length > 0) {
3534
+ if (selectedLayoutComponent) {
3535
+ container_componet = {
3536
+ ...selectedLayoutComponent,
3537
+ id: `${selectedLayoutComponent.id}_${Date.now()}`,
3538
+ description: layoutReasoning,
3539
+ props: {
3540
+ ...selectedLayoutComponent.props,
3541
+ config: {
3542
+ ...selectedLayoutComponent.props?.config || {},
3543
+ components: matchedComponents
3544
+ }
3545
+ }
3546
+ };
3547
+ logger.info(`[${this.getProviderName()}] Created ${selectedLayoutComponent.name} (${selectedLayoutComponent.type}) container with ${matchedComponents.length} components`);
3548
+ logCollector?.info(`Created ${selectedLayoutComponent.name} with ${matchedComponents.length} components: ${layoutReasoning}`);
3549
+ } else {
3550
+ container_componet = {
3551
+ id: `multi_container_${Date.now()}`,
3552
+ name: "MultiComponentContainer",
3553
+ type: "Container",
3554
+ description: layoutReasoning,
3555
+ category: "dynamic",
3556
+ keywords: ["dashboard", "layout", "container"],
3557
+ props: {
3558
+ config: {
3559
+ components: matchedComponents
3560
+ }
3561
+ }
3562
+ };
3563
+ logger.info(`[${this.getProviderName()}] Created fallback MultiComponentContainer with ${matchedComponents.length} components`);
3564
+ logCollector?.info(`Created MultiComponentContainer with ${matchedComponents.length} components: ${layoutReasoning}`);
3565
+ }
3566
+ }
3567
+ return {
3568
+ success: true,
3569
+ data: {
3570
+ text: textResponse,
3571
+ matchedComponents,
3572
+ component: container_componet,
3573
+ layoutReasoning,
3574
+ method: `${this.getProviderName()}-text-response`
3575
+ },
3576
+ errors: []
3577
+ };
3578
+ } catch (error) {
3579
+ const errorMsg = error instanceof Error ? error.message : String(error);
3580
+ logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
3581
+ logCollector?.error(`Error generating text response: ${errorMsg}`);
3582
+ errors.push(errorMsg);
3583
+ return {
3584
+ success: false,
3585
+ errors,
3586
+ data: {
3587
+ text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
3588
+ matchedComponents: [],
3589
+ method: `${this.getProviderName()}-text-response-error`
3590
+ }
3591
+ };
3592
+ }
3593
+ }
3594
+ /**
3595
+ * Generate component response for user question
3596
+ * This provides conversational component suggestions based on user question
3597
+ * Supports component generation and matching
3598
+ */
3599
+ async generateComponentResponse(userPrompt, components, apiKey, logCollector, conversationHistory) {
3600
+ const errors = [];
3601
+ try {
3602
+ logger.info(`[${this.getProviderName()}] Using component response mode`);
3603
+ const classifyMsg = "Classifying user question...";
3604
+ logCollector?.info(classifyMsg);
3605
+ const classification = await this.classifyUserQuestion(userPrompt, apiKey, logCollector, conversationHistory);
3606
+ const classInfo = `Question type: ${classification.questionType}, Visualizations: ${classification.visualizations.join(", ") || "None"}, Multiple components: ${classification.needsMultipleComponents}`;
3607
+ logCollector?.info(classInfo);
3608
+ if (classification.questionType === "analytical") {
3609
+ if (classification.visualizations.length > 1) {
3610
+ const multiMsg = `Matching ${classification.visualizations.length} components for types: ${classification.visualizations.join(", ")}`;
3611
+ logCollector?.info(multiMsg);
3612
+ const componentPromises = classification.visualizations.map((vizType) => {
3613
+ logCollector?.info(`Matching component for type: ${vizType}`);
3614
+ return this.generateAnalyticalComponent(
3615
+ userPrompt,
3616
+ components,
3617
+ vizType,
3618
+ apiKey,
3619
+ logCollector,
3620
+ conversationHistory
3621
+ ).then((result) => ({ vizType, result }));
3622
+ });
3623
+ const settledResults = await Promise.allSettled(componentPromises);
3624
+ const matchedComponents = [];
3625
+ for (const settledResult of settledResults) {
3626
+ if (settledResult.status === "fulfilled") {
3627
+ const { vizType, result } = settledResult.value;
3628
+ if (result.component) {
3629
+ matchedComponents.push(result.component);
3630
+ logCollector?.info(`Matched: ${result.component.name}`);
3631
+ logger.info("Component : ", result.component.name, " props: ", result.component.props);
3632
+ } else {
3633
+ logCollector?.warn(`Failed to match component for type: ${vizType}`);
3634
+ }
3635
+ } else {
3636
+ logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
3637
+ }
3638
+ }
3639
+ logger.debug(`[${this.getProviderName()}] Matched ${matchedComponents.length} components for multi-component container`);
2759
3640
  if (matchedComponents.length === 0) {
2760
3641
  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
3642
+ success: true,
3643
+ data: {
3644
+ component: null,
3645
+ reasoning: "Failed to match any components for the requested visualization types",
3646
+ method: "classification-multi-failed",
3647
+ questionType: classification.questionType,
3648
+ needsMultipleComponents: true,
3649
+ propsModified: false,
3650
+ queryModified: false
3651
+ },
3652
+ errors: []
2768
3653
  };
2769
3654
  }
2770
3655
  logCollector?.info("Generating container metadata...");
@@ -2794,38 +3679,50 @@ var BaseLLM = class {
2794
3679
  };
2795
3680
  logCollector?.info(`Created multi-component container with ${matchedComponents.length} components: "${containerMetadata.title}"`);
2796
3681
  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
3682
+ success: true,
3683
+ data: {
3684
+ component: containerComponent,
3685
+ reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
3686
+ method: "classification-multi-generated",
3687
+ questionType: classification.questionType,
3688
+ needsMultipleComponents: true,
3689
+ propsModified: false,
3690
+ queryModified: false
3691
+ },
3692
+ errors: []
2804
3693
  };
2805
3694
  } else if (classification.visualizations.length === 1) {
2806
3695
  const vizType = classification.visualizations[0];
2807
3696
  logCollector?.info(`Matching single component for type: ${vizType}`);
2808
3697
  const result = await this.generateAnalyticalComponent(userPrompt, components, vizType, apiKey, logCollector, conversationHistory);
2809
3698
  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
3699
+ success: true,
3700
+ data: {
3701
+ component: result.component,
3702
+ reasoning: result.reasoning,
3703
+ method: "classification-generated",
3704
+ questionType: classification.questionType,
3705
+ needsMultipleComponents: false,
3706
+ propsModified: false,
3707
+ queryModified: false
3708
+ },
3709
+ errors: []
2817
3710
  };
2818
3711
  } else {
2819
3712
  logCollector?.info("No specific visualization type - matching from all components");
2820
3713
  const result = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2821
3714
  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
3715
+ success: true,
3716
+ data: {
3717
+ component: result.component,
3718
+ reasoning: result.reasoning,
3719
+ method: "classification-generated-auto",
3720
+ questionType: classification.questionType,
3721
+ needsMultipleComponents: false,
3722
+ propsModified: false,
3723
+ queryModified: false
3724
+ },
3725
+ errors: []
2829
3726
  };
2830
3727
  }
2831
3728
  } else if (classification.questionType === "data_modification" || classification.questionType === "general") {
@@ -2833,29 +3730,104 @@ var BaseLLM = class {
2833
3730
  logCollector?.info(matchMsg);
2834
3731
  const matchResult = await this.matchComponent(userPrompt, components, apiKey, logCollector, conversationHistory);
2835
3732
  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
3733
+ success: true,
3734
+ data: {
3735
+ component: matchResult.component,
3736
+ reasoning: matchResult.reasoning,
3737
+ method: "classification-matched",
3738
+ questionType: classification.questionType,
3739
+ needsMultipleComponents: false,
3740
+ propsModified: matchResult.propsModified,
3741
+ queryModified: matchResult.queryModified
3742
+ },
3743
+ errors: []
2843
3744
  };
2844
3745
  } else {
2845
3746
  logCollector?.info("General question - no component needed");
2846
3747
  return {
2847
- component: null,
2848
- reasoning: "General question - no component needed",
2849
- method: "classification-general",
2850
- questionType: classification.questionType,
2851
- needsMultipleComponents: false
3748
+ success: true,
3749
+ data: {
3750
+ component: null,
3751
+ reasoning: "General question - no component needed",
3752
+ method: "classification-general",
3753
+ questionType: classification.questionType,
3754
+ needsMultipleComponents: false,
3755
+ propsModified: false,
3756
+ queryModified: false
3757
+ },
3758
+ errors: []
2852
3759
  };
2853
3760
  }
2854
3761
  } catch (error) {
2855
- logCollector?.error(`Error handling user request: ${error.message}`);
2856
- throw error;
3762
+ const errorMsg = error instanceof Error ? error.message : String(error);
3763
+ logger.error(`[${this.getProviderName()}] Error generating component response: ${errorMsg}`);
3764
+ logger.debug(`[${this.getProviderName()}] Component response generation error details:`, error);
3765
+ logCollector?.error(`Error generating component response: ${errorMsg}`);
3766
+ errors.push(errorMsg);
3767
+ return {
3768
+ success: false,
3769
+ errors,
3770
+ data: void 0
3771
+ };
2857
3772
  }
2858
3773
  }
3774
+ /**
3775
+ * Main orchestration function that classifies question and routes to appropriate handler
3776
+ * This is the NEW recommended entry point for handling user requests
3777
+ * Supports both component generation and text response modes
3778
+ *
3779
+ * @param responseMode - 'component' for component generation (default), 'text' for text responses
3780
+ * @param streamCallback - Optional callback function to receive text chunks as they stream (only for text mode)
3781
+ * @param collections - Collection registry for executing database queries (required for text mode)
3782
+ */
3783
+ async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) {
3784
+ const startTime = Date.now();
3785
+ logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
3786
+ if (responseMode === "text") {
3787
+ logger.info(`[${this.getProviderName()}] Using text response mode`);
3788
+ logCollector?.info("Generating text response...");
3789
+ const textResponse = await this.generateTextResponse(
3790
+ userPrompt,
3791
+ apiKey,
3792
+ logCollector,
3793
+ conversationHistory,
3794
+ streamCallback,
3795
+ collections,
3796
+ components
3797
+ );
3798
+ if (!textResponse.success) {
3799
+ const elapsedTime3 = Date.now() - startTime;
3800
+ logger.error(`[${this.getProviderName()}] Text response generation failed`);
3801
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
3802
+ logCollector?.info(`Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
3803
+ return textResponse;
3804
+ }
3805
+ const elapsedTime2 = Date.now() - startTime;
3806
+ logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3807
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3808
+ logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3809
+ return textResponse;
3810
+ }
3811
+ const componentResponse = await this.generateComponentResponse(
3812
+ userPrompt,
3813
+ components,
3814
+ apiKey,
3815
+ logCollector,
3816
+ conversationHistory
3817
+ );
3818
+ if (!componentResponse.success) {
3819
+ const elapsedTime2 = Date.now() - startTime;
3820
+ logger.error(`[${this.getProviderName()}] Component response generation failed`);
3821
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3822
+ logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3823
+ return componentResponse;
3824
+ }
3825
+ const elapsedTime = Date.now() - startTime;
3826
+ logger.info(`[${this.getProviderName()}] Component response generated successfully`);
3827
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
3828
+ logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
3829
+ return componentResponse;
3830
+ }
2859
3831
  /**
2860
3832
  * Generate next questions that the user might ask based on the original prompt and generated component
2861
3833
  * This helps provide intelligent suggestions for follow-up queries
@@ -2900,8 +3872,10 @@ var BaseLLM = class {
2900
3872
  );
2901
3873
  return nextQuestions;
2902
3874
  } catch (error) {
2903
- console.error(`Error generating next questions with ${this.getProviderName()}:`, error);
2904
- logCollector?.error(`Error generating next questions: ${error.message}`);
3875
+ const errorMsg = error instanceof Error ? error.message : String(error);
3876
+ logger.error(`[${this.getProviderName()}] Error generating next questions: ${errorMsg}`);
3877
+ logger.debug(`[${this.getProviderName()}] Next questions generation error details:`, error);
3878
+ logCollector?.error(`Error generating next questions: ${errorMsg}`);
2905
3879
  return [];
2906
3880
  }
2907
3881
  }
@@ -2965,112 +3939,130 @@ function getLLMProviders() {
2965
3939
  return DEFAULT_PROVIDERS;
2966
3940
  }
2967
3941
  }
2968
- var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2969
- const msg = "Using Anthropic Claude matching method...";
2970
- console.log(msg);
3942
+ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3943
+ logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
3944
+ logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
3945
+ const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
2971
3946
  logCollector?.info(msg);
2972
- if (components.length === 0) {
3947
+ if (responseMode === "component" && components.length === 0) {
2973
3948
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3949
+ logger.error("[useAnthropicMethod] No components available");
2974
3950
  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;
3951
+ return { success: false, errors: [emptyMsg] };
2984
3952
  }
3953
+ logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
3954
+ const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3955
+ logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
3956
+ return matchResult;
2985
3957
  };
2986
- var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2987
- const msg = "Using Groq LLM matching method...";
2988
- console.log(msg);
3958
+ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3959
+ logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
3960
+ logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
3961
+ const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
3962
+ logger.info(msg);
2989
3963
  logCollector?.info(msg);
2990
- if (components.length === 0) {
3964
+ if (responseMode === "component" && components.length === 0) {
2991
3965
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3966
+ logger.error("[useGroqMethod] No components available");
2992
3967
  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;
3968
+ return { success: false, errors: [emptyMsg] };
3002
3969
  }
3970
+ logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
3971
+ const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3972
+ logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
3973
+ return matchResult;
3003
3974
  };
3004
3975
  var getUserResponseFromCache = async (prompt) => {
3005
3976
  return false;
3006
3977
  };
3007
- var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
3978
+ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3979
+ logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
3980
+ logger.debug(`[get_user_response] Response mode: ${responseMode}`);
3981
+ logger.debug("[get_user_response] Checking cache for existing response");
3008
3982
  const userResponse = await getUserResponseFromCache(prompt);
3009
3983
  if (userResponse) {
3984
+ logger.info("[get_user_response] User response found in cache - returning cached result");
3010
3985
  logCollector?.info("User response found in cache");
3011
3986
  return {
3012
3987
  success: true,
3013
- data: userResponse
3988
+ data: userResponse,
3989
+ errors: []
3014
3990
  };
3015
3991
  }
3992
+ logger.debug("[get_user_response] No cached response found, proceeding with LLM providers");
3016
3993
  const providers = llmProviders || getLLMProviders();
3017
3994
  const errors = [];
3018
3995
  const providerOrder = providers.join(", ");
3019
3996
  logCollector?.info(`LLM Provider order: [${providerOrder}]`);
3020
3997
  if (conversationHistory && conversationHistory.length > 0) {
3021
- logCollector?.info(`Using conversation history with ${conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length} previous exchanges`);
3998
+ const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
3999
+ logger.debug(`[get_user_response] Using conversation history with ${exchangeCount} previous exchanges`);
4000
+ logCollector?.info(`Using conversation history with ${exchangeCount} previous exchanges`);
4001
+ } else {
4002
+ logger.debug("[get_user_response] No conversation history available");
3022
4003
  }
3023
4004
  for (let i = 0; i < providers.length; i++) {
3024
4005
  const provider = providers[i];
3025
4006
  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);
4007
+ const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
4008
+ logCollector?.info(attemptMsg);
4009
+ let result;
4010
+ if (provider === "anthropic") {
4011
+ result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
4012
+ } else if (provider === "groq") {
4013
+ result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
4014
+ } else {
4015
+ logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
4016
+ errors.push(`Unknown provider: ${provider}`);
4017
+ continue;
4018
+ }
4019
+ if (result.success) {
4020
+ const successMsg = `Success with provider: ${provider}`;
4021
+ logger.info(`${successMsg}`);
4022
+ logCollector?.info(successMsg);
4023
+ return result;
4024
+ } else {
4025
+ const providerErrors = result.errors.map((err) => `${provider}: ${err}`);
4026
+ errors.push(...providerErrors);
4027
+ const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.errors.join(", ")}`;
4028
+ logger.warn(`[get_user_response] ${warnMsg}`);
4029
+ logCollector?.warn(warnMsg);
3051
4030
  if (!isLastProvider) {
3052
4031
  const fallbackMsg = "Falling back to next provider...";
4032
+ logger.info(`[get_user_response] ${fallbackMsg}`);
3053
4033
  logCollector?.info(fallbackMsg);
3054
- continue;
3055
4034
  }
3056
4035
  }
3057
4036
  }
3058
- const errorSummary = errors.map((e) => `${e.provider}: ${e.error}`).join("; ");
3059
- const failureMsg = `All LLM providers failed. Errors: ${errorSummary}`;
3060
- logCollector?.error(failureMsg);
4037
+ const failureMsg = `All LLM providers failed`;
4038
+ logger.error(`[get_user_response] ${failureMsg}. Errors: ${errors.join("; ")}`);
4039
+ logCollector?.error(`${failureMsg}. Errors: ${errors.join("; ")}`);
3061
4040
  return {
3062
4041
  success: false,
3063
- reason: failureMsg
4042
+ errors
3064
4043
  };
3065
4044
  };
3066
4045
 
3067
4046
  // src/utils/log-collector.ts
4047
+ var LOG_LEVEL_PRIORITY2 = {
4048
+ errors: 0,
4049
+ warnings: 1,
4050
+ info: 2,
4051
+ verbose: 3
4052
+ };
4053
+ var MESSAGE_LEVEL_PRIORITY2 = {
4054
+ error: 0,
4055
+ warn: 1,
4056
+ info: 2,
4057
+ debug: 3
4058
+ };
3068
4059
  var UILogCollector = class {
3069
4060
  constructor(clientId, sendMessage, uiBlockId) {
3070
4061
  this.logs = [];
3071
4062
  this.uiBlockId = uiBlockId || null;
3072
4063
  this.clientId = clientId;
3073
4064
  this.sendMessage = sendMessage;
4065
+ this.currentLogLevel = logger.getLogLevel();
3074
4066
  }
3075
4067
  /**
3076
4068
  * Check if logging is enabled (uiBlockId is provided)
@@ -3078,10 +4070,22 @@ var UILogCollector = class {
3078
4070
  isEnabled() {
3079
4071
  return this.uiBlockId !== null;
3080
4072
  }
4073
+ /**
4074
+ * Check if a message should be logged based on current log level
4075
+ */
4076
+ shouldLog(messageLevel) {
4077
+ const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
4078
+ const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
4079
+ return messagePriority <= currentLevelPriority;
4080
+ }
3081
4081
  /**
3082
4082
  * Add a log entry with timestamp and immediately send to runtime
4083
+ * Only logs that pass the log level filter are captured and sent
3083
4084
  */
3084
4085
  addLog(level, message, type, data) {
4086
+ if (!this.shouldLog(level)) {
4087
+ return;
4088
+ }
3085
4089
  const log = {
3086
4090
  timestamp: Date.now(),
3087
4091
  level,
@@ -3091,7 +4095,20 @@ var UILogCollector = class {
3091
4095
  };
3092
4096
  this.logs.push(log);
3093
4097
  this.sendLogImmediately(log);
3094
- console.log("UILogCollector:", log);
4098
+ switch (level) {
4099
+ case "error":
4100
+ logger.error("UILogCollector:", log);
4101
+ break;
4102
+ case "warn":
4103
+ logger.warn("UILogCollector:", log);
4104
+ break;
4105
+ case "info":
4106
+ logger.info("UILogCollector:", log);
4107
+ break;
4108
+ case "debug":
4109
+ logger.debug("UILogCollector:", log);
4110
+ break;
4111
+ }
3095
4112
  }
3096
4113
  /**
3097
4114
  * Send a single log to runtime immediately
@@ -3221,103 +4238,153 @@ var CONTEXT_CONFIG = {
3221
4238
  };
3222
4239
 
3223
4240
  // src/handlers/user-prompt-request.ts
3224
- 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);
4241
+ var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) => {
4242
+ const errors = [];
4243
+ logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
4244
+ const parseResult = UserPromptRequestMessageSchema.safeParse(data);
4245
+ if (!parseResult.success) {
4246
+ const zodError = parseResult.error;
4247
+ zodError.errors.forEach((err) => {
4248
+ errors.push(`${err.path.join(".")}: ${err.message}`);
4249
+ });
4250
+ return { success: false, errors };
4251
+ }
4252
+ const userPromptRequest = parseResult.data;
4253
+ const { id, payload } = userPromptRequest;
4254
+ const prompt = payload.prompt;
4255
+ const SA_RUNTIME = payload.SA_RUNTIME;
4256
+ const wsId = userPromptRequest.from.id || "unknown";
4257
+ if (!SA_RUNTIME) {
4258
+ errors.push("SA_RUNTIME is required");
4259
+ }
4260
+ const threadId = SA_RUNTIME?.threadId;
4261
+ const existingUiBlockId = SA_RUNTIME?.uiBlockId;
4262
+ if (!threadId) {
4263
+ errors.push("threadId in SA_RUNTIME is required");
4264
+ }
4265
+ if (!existingUiBlockId) {
4266
+ errors.push("uiBlockId in SA_RUNTIME is required");
4267
+ }
4268
+ if (!prompt) {
4269
+ errors.push("Prompt not found");
4270
+ }
4271
+ logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
4272
+ if (errors.length > 0) {
4273
+ return { success: false, errors, id, wsId };
4274
+ }
4275
+ const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
4276
+ const threadManager = ThreadManager.getInstance();
4277
+ let thread = threadManager.getThread(threadId);
4278
+ if (!thread) {
4279
+ thread = threadManager.createThread(threadId);
4280
+ logger.info(`Created new thread: ${threadId}`);
4281
+ }
4282
+ logCollector.info(`Starting user prompt request with ${components.length} components`);
4283
+ const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
4284
+ logger.info("conversationHistory", conversationHistory);
4285
+ const responseMode = payload.responseMode || "component";
4286
+ logger.info("responseMode", responseMode);
4287
+ let streamCallback;
4288
+ let accumulatedStreamResponse = "";
4289
+ if (responseMode === "text") {
4290
+ streamCallback = (chunk) => {
4291
+ accumulatedStreamResponse += chunk;
4292
+ logger.debug(`[STREAM] Sending chunk (${chunk.length} chars): "${chunk.substring(0, 20)}..."`);
4293
+ const streamMessage = {
4294
+ id: `stream_${existingUiBlockId}`,
4295
+ // Different ID pattern for streaming
4296
+ type: "USER_PROMPT_STREAM",
4297
+ from: { type: "data-agent" },
4298
+ to: {
4299
+ type: "runtime",
4300
+ id: wsId
4301
+ },
4302
+ payload: {
4303
+ uiBlockId: existingUiBlockId,
4304
+ chunk
4305
+ }
4306
+ };
4307
+ sendMessage(streamMessage);
4308
+ logger.debug(`[STREAM] Chunk sent to wsId: ${wsId}`);
4309
+ };
4310
+ }
4311
+ const userResponse = await get_user_response(
4312
+ prompt,
4313
+ components,
4314
+ anthropicApiKey,
4315
+ groqApiKey,
4316
+ llmProviders,
4317
+ logCollector,
4318
+ conversationHistory,
4319
+ responseMode,
4320
+ streamCallback,
4321
+ collections
4322
+ );
4323
+ logCollector.info("User prompt request completed");
4324
+ const uiBlockId = existingUiBlockId;
4325
+ if (!userResponse.success) {
4326
+ logger.error(`User prompt request failed with errors: ${userResponse.errors.join(", ")}`);
4327
+ return {
4328
+ success: false,
4329
+ data: userResponse.data,
4330
+ errors: userResponse.errors,
4331
+ uiBlockId,
4332
+ threadId,
4333
+ id,
4334
+ wsId
4335
+ };
4336
+ }
4337
+ let component = null;
4338
+ let textResponse = null;
4339
+ if (userResponse.data) {
4340
+ if (typeof userResponse.data === "object") {
4341
+ if ("component" in userResponse.data) {
4342
+ component = userResponse.data.component;
4343
+ }
4344
+ if ("textResponse" in userResponse.data) {
4345
+ textResponse = userResponse.data.text;
3242
4346
  }
3243
4347
  }
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
4348
  }
4349
+ const finalTextResponse = responseMode === "text" && accumulatedStreamResponse ? accumulatedStreamResponse : textResponse;
4350
+ const uiBlock = new UIBlock(
4351
+ prompt,
4352
+ {},
4353
+ // componentData: initially empty, will be filled later
4354
+ component,
4355
+ // generatedComponentMetadata: full component object (ComponentSchema)
4356
+ [],
4357
+ // actions: empty initially
4358
+ uiBlockId,
4359
+ finalTextResponse
4360
+ // textResponse: FULL streaming response including all intermediate messages
4361
+ );
4362
+ thread.addUIBlock(uiBlock);
4363
+ logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
4364
+ return {
4365
+ success: userResponse.success,
4366
+ data: userResponse.data,
4367
+ errors: userResponse.errors,
4368
+ uiBlockId,
4369
+ threadId,
4370
+ id,
4371
+ wsId
4372
+ };
4373
+ };
4374
+ async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) {
4375
+ const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections);
4376
+ sendDataResponse4(
4377
+ response.id || data.id,
4378
+ {
4379
+ success: response.success,
4380
+ errors: response.errors,
4381
+ data: response.data,
4382
+ uiBlockId: response.uiBlockId,
4383
+ threadId: response.threadId
4384
+ },
4385
+ sendMessage,
4386
+ response.wsId || data.from?.id
4387
+ );
3321
4388
  }
3322
4389
  function sendDataResponse4(id, res, sendMessage, clientId) {
3323
4390
  const response = {
@@ -3430,12 +4497,27 @@ function sendResponse(id, res, sendMessage, clientId) {
3430
4497
  // src/userResponse/next-questions.ts
3431
4498
  async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) {
3432
4499
  try {
4500
+ logger.debug("[generateNextQuestions] Starting next questions generation");
4501
+ logger.debug(`[generateNextQuestions] User prompt: "${originalUserPrompt?.substring(0, 50)}..."`);
4502
+ logger.debug(`[generateNextQuestions] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
4503
+ logger.debug(`[generateNextQuestions] Component data available: ${componentData ? "yes" : "no"}`);
3433
4504
  const providers = llmProviders || ["anthropic"];
3434
- for (const provider of providers) {
4505
+ logger.info(`[generateNextQuestions] Using LLM providers: [${providers.join(", ")}]`);
4506
+ if (conversationHistory && conversationHistory.length > 0) {
4507
+ const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
4508
+ logger.debug(`[generateNextQuestions] Using conversation history with ${exchangeCount} previous exchanges`);
4509
+ } else {
4510
+ logger.debug("[generateNextQuestions] No conversation history available");
4511
+ }
4512
+ for (let i = 0; i < providers.length; i++) {
4513
+ const provider = providers[i];
4514
+ const isLastProvider = i === providers.length - 1;
3435
4515
  try {
3436
- logger.info(`Generating next questions using provider: ${provider}`);
4516
+ logger.info(`[generateNextQuestions] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
4517
+ logCollector?.info(`Generating questions with ${provider}...`);
3437
4518
  let result = [];
3438
4519
  if (provider === "groq") {
4520
+ logger.debug("[generateNextQuestions] Using Groq LLM for next questions");
3439
4521
  result = await groqLLM.generateNextQuestions(
3440
4522
  originalUserPrompt,
3441
4523
  component,
@@ -3445,6 +4527,7 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3445
4527
  conversationHistory
3446
4528
  );
3447
4529
  } else {
4530
+ logger.debug("[generateNextQuestions] Using Anthropic LLM for next questions");
3448
4531
  result = await anthropicLLM.generateNextQuestions(
3449
4532
  originalUserPrompt,
3450
4533
  component,
@@ -3455,21 +4538,39 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3455
4538
  );
3456
4539
  }
3457
4540
  if (result && result.length > 0) {
3458
- logger.info(`Successfully generated ${result.length} questions with ${provider}`);
4541
+ logger.info(`[generateNextQuestions] Successfully generated ${result.length} questions with ${provider}`);
4542
+ logger.debug(`[generateNextQuestions] Questions: ${JSON.stringify(result)}`);
4543
+ logCollector?.info(`Generated ${result.length} follow-up questions`);
3459
4544
  return result;
3460
4545
  }
3461
- logger.warn(`No questions generated from ${provider}, trying next provider...`);
4546
+ const warnMsg = `No questions generated from ${provider}${!isLastProvider ? ", trying next provider..." : ""}`;
4547
+ logger.warn(`[generateNextQuestions] ${warnMsg}`);
4548
+ if (!isLastProvider) {
4549
+ logCollector?.warn(warnMsg);
4550
+ }
3462
4551
  } catch (providerError) {
3463
- logger.warn(`Provider ${provider} failed:`, providerError);
3464
- logCollector?.warn(`Provider ${provider} failed, trying next provider...`);
4552
+ const errorMsg = providerError instanceof Error ? providerError.message : String(providerError);
4553
+ logger.error(`[generateNextQuestions] Provider ${provider} failed: ${errorMsg}`);
4554
+ logger.debug(`[generateNextQuestions] Provider error details:`, providerError);
4555
+ if (!isLastProvider) {
4556
+ const fallbackMsg = `Provider ${provider} failed, trying next provider...`;
4557
+ logger.info(`[generateNextQuestions] ${fallbackMsg}`);
4558
+ logCollector?.warn(fallbackMsg);
4559
+ } else {
4560
+ logCollector?.error(`Failed to generate questions with ${provider}`);
4561
+ }
3465
4562
  continue;
3466
4563
  }
3467
4564
  }
3468
- logger.warn("All providers failed or returned no questions");
4565
+ logger.warn("[generateNextQuestions] All providers failed or returned no questions");
4566
+ logCollector?.warn("Unable to generate follow-up questions");
3469
4567
  return [];
3470
4568
  } catch (error) {
3471
- logger.error("Error generating next questions:", error);
3472
- logCollector?.error(`Error generating next questions: ${error.message}`);
4569
+ const errorMsg = error instanceof Error ? error.message : String(error);
4570
+ const errorStack = error instanceof Error ? error.stack : void 0;
4571
+ logger.error(`[generateNextQuestions] Error generating next questions: ${errorMsg}`);
4572
+ logger.debug("[generateNextQuestions] Error stack trace:", errorStack);
4573
+ logCollector?.error(`Error generating next questions: ${errorMsg}`);
3473
4574
  return [];
3474
4575
  }
3475
4576
  }
@@ -3477,11 +4578,15 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3477
4578
  // src/handlers/actions-request.ts
3478
4579
  async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3479
4580
  try {
4581
+ logger.debug("[ACTIONS_REQ] Parsing incoming actions request");
3480
4582
  const actionsRequest = ActionsRequestMessageSchema.parse(data);
3481
4583
  const { id, payload } = actionsRequest;
3482
4584
  const { SA_RUNTIME } = payload;
3483
4585
  const wsId = actionsRequest.from.id || "unknown";
4586
+ logger.info(`[ACTIONS_REQ ${id}] Processing actions request from client: ${wsId}`);
4587
+ logger.debug(`[ACTIONS_REQ ${id}] Request payload:`, JSON.stringify(payload, null, 2).substring(0, 200));
3484
4588
  if (!SA_RUNTIME) {
4589
+ logger.error(`[ACTIONS_REQ ${id}] SA_RUNTIME missing from request`);
3485
4590
  sendResponse2(id, {
3486
4591
  success: false,
3487
4592
  error: "SA_RUNTIME with threadId and uiBlockId is required"
@@ -3490,31 +4595,54 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3490
4595
  }
3491
4596
  const uiBlockId = SA_RUNTIME.uiBlockId;
3492
4597
  const threadId = SA_RUNTIME.threadId;
4598
+ logger.debug(`[ACTIONS_REQ ${id}] SA_RUNTIME validated - threadId: ${threadId}, uiBlockId: ${uiBlockId}`);
4599
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving thread: ${threadId}`);
3493
4600
  const threadManager = ThreadManager.getInstance();
3494
4601
  const thread = threadManager.getThread(threadId);
3495
4602
  if (!thread) {
4603
+ logger.error(`[ACTIONS_REQ ${id}] Thread '${threadId}' not found`);
3496
4604
  sendResponse2(id, {
3497
4605
  success: false,
3498
4606
  error: `Thread '${threadId}' not found`
3499
4607
  }, sendMessage, wsId);
3500
4608
  return;
3501
4609
  }
4610
+ logger.debug(`[ACTIONS_REQ ${id}] Thread found with ${thread.getUIBlocks().length} UIBlocks`);
4611
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving UIBlock: ${uiBlockId}`);
3502
4612
  const uiBlock = thread.getUIBlock(uiBlockId);
3503
4613
  if (!uiBlock) {
4614
+ logger.error(`[ACTIONS_REQ ${id}] UIBlock '${uiBlockId}' not found in thread '${threadId}'`);
3504
4615
  sendResponse2(id, {
3505
4616
  success: false,
3506
4617
  error: `UIBlock '${uiBlockId}' not found in thread '${threadId}'`
3507
4618
  }, sendMessage, wsId);
3508
4619
  return;
3509
4620
  }
4621
+ logger.info(`[ACTIONS_REQ ${id}] UIBlock retrieved successfully`);
4622
+ logger.debug(`[ACTIONS_REQ ${id}] Creating UILogCollector for uiBlockId: ${uiBlockId}`);
3510
4623
  const logCollector = new UILogCollector(wsId, sendMessage, uiBlockId);
4624
+ logger.info(`[ACTIONS_REQ ${id}] UILogCollector initialized`);
4625
+ logger.debug(`[ACTIONS_REQ ${id}] Extracting data from UIBlock`);
3511
4626
  const userQuestion = uiBlock.getUserQuestion();
3512
4627
  const component = uiBlock.getComponentMetadata();
3513
4628
  const componentData = uiBlock.getComponentData();
4629
+ logger.debug(`[ACTIONS_REQ ${id}] User question: "${userQuestion?.substring(0, 50)}..."`);
4630
+ logger.debug(`[ACTIONS_REQ ${id}] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
4631
+ logger.debug(`[ACTIONS_REQ ${id}] Component data available: ${componentData ? "yes" : "no"}`);
4632
+ logger.debug(`[ACTIONS_REQ ${id}] Extracting conversation history (max ${CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS} blocks)`);
3514
4633
  const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, uiBlockId);
4634
+ const historyLineCount = conversationHistory.split("\n").filter((l) => l.trim()).length;
4635
+ logger.info(`[ACTIONS_REQ ${id}] Conversation history extracted: ${historyLineCount} lines`);
4636
+ logger.debug(`[ACTIONS_REQ ${id}] Conversation history preview:
4637
+ ${conversationHistory.substring(0, 200)}...`);
3515
4638
  logCollector.info(`Generating actions for UIBlock: ${uiBlockId}`);
3516
- logger.info(`Generating actions for component: ${component?.name || "unknown"}`);
4639
+ logger.info(`[ACTIONS_REQ ${id}] Generating actions for component: ${component?.name || "unknown"}`);
4640
+ logger.debug(`[ACTIONS_REQ ${id}] Checking if actions are already cached`);
4641
+ const startTime = Date.now();
3517
4642
  const actions = await uiBlock.getOrFetchActions(async () => {
4643
+ logger.info(`[ACTIONS_REQ ${id}] Actions not cached, generating new actions...`);
4644
+ logCollector.info("Generating follow-up questions...");
4645
+ logger.info(`[ACTIONS_REQ ${id}] Starting next questions generation with ${llmProviders?.join(", ") || "default"} providers`);
3518
4646
  const nextQuestions = await generateNextQuestions(
3519
4647
  userQuestion,
3520
4648
  component,
@@ -3525,14 +4653,28 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3525
4653
  logCollector,
3526
4654
  conversationHistory
3527
4655
  );
3528
- return nextQuestions.map((question, index) => ({
4656
+ logger.info(`[ACTIONS_REQ ${id}] Generated ${nextQuestions.length} questions`);
4657
+ logger.debug(`[ACTIONS_REQ ${id}] Questions: ${JSON.stringify(nextQuestions)}`);
4658
+ logger.debug(`[ACTIONS_REQ ${id}] Converting questions to actions format`);
4659
+ const convertedActions = nextQuestions.map((question, index) => ({
3529
4660
  id: `action_${index}_${Date.now()}`,
3530
4661
  name: question,
3531
4662
  type: "next_question",
3532
4663
  question
3533
4664
  }));
4665
+ logger.debug(`[ACTIONS_REQ ${id}] Converted ${convertedActions.length} actions`);
4666
+ return convertedActions;
3534
4667
  });
3535
- logCollector.info(`Generated ${actions.length} actions successfully`);
4668
+ const processingTime = Date.now() - startTime;
4669
+ logger.info(`[ACTIONS_REQ ${id}] Actions retrieved in ${processingTime}ms - ${actions.length} actions total`);
4670
+ if (actions.length > 0) {
4671
+ logCollector.info(`Generated ${actions.length} follow-up questions successfully`);
4672
+ logger.debug(`[ACTIONS_REQ ${id}] Actions: ${actions.map((a) => a.name).join(", ")}`);
4673
+ } else {
4674
+ logger.warn(`[ACTIONS_REQ ${id}] No actions generated`);
4675
+ logCollector.warn("No follow-up questions could be generated");
4676
+ }
4677
+ logger.debug(`[ACTIONS_REQ ${id}] Sending successful response to client`);
3536
4678
  sendResponse2(id, {
3537
4679
  success: true,
3538
4680
  data: {
@@ -3543,12 +4685,26 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3543
4685
  threadId
3544
4686
  }
3545
4687
  }, sendMessage, wsId);
4688
+ logger.info(`[ACTIONS_REQ ${id}] \u2713 Actions request completed successfully`);
3546
4689
  } catch (error) {
3547
- logger.error("Failed to handle actions request:", error);
4690
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
4691
+ const errorStack = error instanceof Error ? error.stack : void 0;
4692
+ logger.error(`[ACTIONS_REQ] Failed to handle actions request: ${errorMessage}`);
4693
+ logger.debug(`[ACTIONS_REQ] Error stack trace:`, errorStack);
4694
+ try {
4695
+ const parsedData = data;
4696
+ if (parsedData?.id && parsedData?.from?.id) {
4697
+ const logCollector = parsedData?.payload?.SA_RUNTIME?.uiBlockId ? new UILogCollector(parsedData.from.id, sendMessage, parsedData.payload.SA_RUNTIME.uiBlockId) : void 0;
4698
+ logCollector?.error(`Failed to generate actions: ${errorMessage}`);
4699
+ }
4700
+ } catch (logError) {
4701
+ logger.debug("[ACTIONS_REQ] Failed to send error logs to UI:", logError);
4702
+ }
3548
4703
  sendResponse2(null, {
3549
4704
  success: false,
3550
- error: error instanceof Error ? error.message : "Unknown error occurred"
4705
+ error: errorMessage
3551
4706
  }, sendMessage);
4707
+ logger.info("[ACTIONS_REQ] \u2717 Actions request completed with errors");
3552
4708
  }
3553
4709
  }
3554
4710
  function sendResponse2(id, res, sendMessage, clientId) {
@@ -3564,6 +4720,11 @@ function sendResponse2(id, res, sendMessage, clientId) {
3564
4720
  ...res
3565
4721
  }
3566
4722
  };
4723
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.success ? "successful" : "failed"} response to client`);
4724
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Response payload size: ${JSON.stringify(response).length} bytes`);
4725
+ if (res.data?.actions) {
4726
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.data.actions.length} actions`);
4727
+ }
3567
4728
  sendMessage(response);
3568
4729
  }
3569
4730
 
@@ -3592,7 +4753,10 @@ async function handleUsersRequest(data, sendMessage) {
3592
4753
  const { id, payload, from } = request;
3593
4754
  const { operation, data: requestData } = payload;
3594
4755
  const username = requestData?.username;
4756
+ const email = requestData?.email;
3595
4757
  const password = requestData?.password;
4758
+ const fullname = requestData?.fullname;
4759
+ const role = requestData?.role;
3596
4760
  if (from.type !== "admin") {
3597
4761
  sendResponse3(id, {
3598
4762
  success: false,
@@ -3604,10 +4768,10 @@ async function handleUsersRequest(data, sendMessage) {
3604
4768
  const userManager = getUserManager();
3605
4769
  switch (operation) {
3606
4770
  case "create":
3607
- await handleCreate(id, username, password, userManager, sendMessage, from.id);
4771
+ await handleCreate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3608
4772
  break;
3609
4773
  case "update":
3610
- await handleUpdate(id, username, password, userManager, sendMessage, from.id);
4774
+ await handleUpdate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3611
4775
  break;
3612
4776
  case "delete":
3613
4777
  await handleDelete(id, username, userManager, sendMessage, from.id);
@@ -3632,7 +4796,8 @@ async function handleUsersRequest(data, sendMessage) {
3632
4796
  }, sendMessage);
3633
4797
  }
3634
4798
  }
3635
- async function handleCreate(id, username, password, userManager, sendMessage, clientId) {
4799
+ async function handleCreate(id, userData, userManager, sendMessage, clientId) {
4800
+ const { username, email, password, fullname, role } = userData;
3636
4801
  if (!username || username.trim().length === 0) {
3637
4802
  sendResponse3(id, {
3638
4803
  success: false,
@@ -3647,6 +4812,16 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3647
4812
  }, sendMessage, clientId);
3648
4813
  return;
3649
4814
  }
4815
+ if (email && email.trim().length > 0) {
4816
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4817
+ if (!emailRegex.test(email)) {
4818
+ sendResponse3(id, {
4819
+ success: false,
4820
+ error: "Invalid email format"
4821
+ }, sendMessage, clientId);
4822
+ return;
4823
+ }
4824
+ }
3650
4825
  if (userManager.userExists(username)) {
3651
4826
  sendResponse3(id, {
3652
4827
  success: false,
@@ -3654,25 +4829,41 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3654
4829
  }, sendMessage, clientId);
3655
4830
  return;
3656
4831
  }
3657
- let wsIds = [];
3658
- if (clientId) {
3659
- wsIds.push(clientId);
4832
+ if (email && userManager.getUserByEmail(email)) {
4833
+ sendResponse3(id, {
4834
+ success: false,
4835
+ error: `User with email '${email}' already exists`
4836
+ }, sendMessage, clientId);
4837
+ return;
3660
4838
  }
3661
- const newUser = userManager.createUser({
4839
+ const newUserData = {
3662
4840
  username,
3663
- password,
3664
- wsIds
3665
- });
3666
- logger.info(`User created by admin: ${username}`);
4841
+ password
4842
+ };
4843
+ if (email && email.trim().length > 0) {
4844
+ newUserData.email = email.trim();
4845
+ }
4846
+ if (fullname && fullname.trim().length > 0) {
4847
+ newUserData.fullname = fullname.trim();
4848
+ }
4849
+ if (role && role.trim().length > 0) {
4850
+ newUserData.role = role.trim();
4851
+ }
4852
+ const newUser = userManager.createUser(newUserData);
4853
+ logger.info(`User created by admin: ${username}${email ? ` (${email})` : ""}`);
3667
4854
  sendResponse3(id, {
3668
4855
  success: true,
3669
4856
  data: {
3670
4857
  username: newUser.username,
4858
+ email: newUser.email,
4859
+ fullname: newUser.fullname,
4860
+ role: newUser.role,
3671
4861
  message: `User '${username}' created successfully`
3672
4862
  }
3673
4863
  }, sendMessage, clientId);
3674
4864
  }
3675
- async function handleUpdate(id, username, password, userManager, sendMessage, clientId) {
4865
+ async function handleUpdate(id, userData, userManager, sendMessage, clientId) {
4866
+ const { username, email, password, fullname, role } = userData;
3676
4867
  if (!username || username.trim().length === 0) {
3677
4868
  sendResponse3(id, {
3678
4869
  success: false,
@@ -3688,13 +4879,42 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3688
4879
  return;
3689
4880
  }
3690
4881
  const updates = {};
3691
- if (password && password.trim().length > 0) {
3692
- updates.password = password;
4882
+ if (email !== void 0) {
4883
+ if (email.trim().length > 0) {
4884
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4885
+ if (!emailRegex.test(email)) {
4886
+ sendResponse3(id, {
4887
+ success: false,
4888
+ error: "Invalid email format"
4889
+ }, sendMessage, clientId);
4890
+ return;
4891
+ }
4892
+ const existingUser = userManager.getUserByEmail(email);
4893
+ if (existingUser && existingUser.username !== username) {
4894
+ sendResponse3(id, {
4895
+ success: false,
4896
+ error: `Email '${email}' is already used by another user`
4897
+ }, sendMessage, clientId);
4898
+ return;
4899
+ }
4900
+ updates.email = email.trim();
4901
+ } else {
4902
+ updates.email = void 0;
4903
+ }
4904
+ }
4905
+ if (password !== void 0 && password.trim().length > 0) {
4906
+ updates.password = password.trim();
4907
+ }
4908
+ if (fullname !== void 0) {
4909
+ updates.fullname = fullname.trim().length > 0 ? fullname.trim() : void 0;
4910
+ }
4911
+ if (role !== void 0) {
4912
+ updates.role = role.trim().length > 0 ? role.trim() : void 0;
3693
4913
  }
3694
4914
  if (Object.keys(updates).length === 0) {
3695
4915
  sendResponse3(id, {
3696
4916
  success: false,
3697
- error: "No fields to update. Please provide password or other valid fields."
4917
+ error: "No fields to update. Please provide at least one field to update."
3698
4918
  }, sendMessage, clientId);
3699
4919
  return;
3700
4920
  }
@@ -3704,6 +4924,9 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3704
4924
  success: true,
3705
4925
  data: {
3706
4926
  username: updatedUser.username,
4927
+ email: updatedUser.email,
4928
+ fullname: updatedUser.fullname,
4929
+ role: updatedUser.role,
3707
4930
  message: `User '${username}' updated successfully`
3708
4931
  }
3709
4932
  }, sendMessage, clientId);
@@ -3744,6 +4967,9 @@ async function handleGetAll(id, userManager, sendMessage, clientId) {
3744
4967
  const users = userManager.getAllUsers();
3745
4968
  const sanitizedUsers = users.map((user) => ({
3746
4969
  username: user.username,
4970
+ email: user.email,
4971
+ fullname: user.fullname,
4972
+ role: user.role,
3747
4973
  wsIds: user.wsIds || []
3748
4974
  }));
3749
4975
  logger.info(`Admin retrieved all users (count: ${sanitizedUsers.length})`);
@@ -3774,6 +5000,9 @@ async function handleGetOne(id, username, userManager, sendMessage, clientId) {
3774
5000
  const user = userManager.getUser(username);
3775
5001
  const sanitizedUser = {
3776
5002
  username: user.username,
5003
+ email: user.email,
5004
+ fullname: user.fullname,
5005
+ role: user.role,
3777
5006
  wsIds: user.wsIds || []
3778
5007
  };
3779
5008
  logger.info(`Admin retrieved user: ${username}`);
@@ -4219,7 +5448,7 @@ function sendResponse5(id, res, sendMessage, clientId) {
4219
5448
  }
4220
5449
 
4221
5450
  // src/auth/user-manager.ts
4222
- import fs4 from "fs";
5451
+ import fs5 from "fs";
4223
5452
  import path4 from "path";
4224
5453
  import os from "os";
4225
5454
  var UserManager = class {
@@ -4259,21 +5488,22 @@ var UserManager = class {
4259
5488
  async loadUsersFromFile() {
4260
5489
  try {
4261
5490
  const dir = path4.dirname(this.filePath);
4262
- if (!fs4.existsSync(dir)) {
5491
+ if (!fs5.existsSync(dir)) {
4263
5492
  logger.info(`Creating directory structure: ${dir}`);
4264
- fs4.mkdirSync(dir, { recursive: true });
5493
+ fs5.mkdirSync(dir, { recursive: true });
4265
5494
  }
4266
- if (!fs4.existsSync(this.filePath)) {
5495
+ if (!fs5.existsSync(this.filePath)) {
4267
5496
  logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
4268
5497
  const initialData = { users: [] };
4269
- fs4.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
5498
+ fs5.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
4270
5499
  this.users = [];
4271
5500
  this.hasChanged = false;
4272
5501
  return;
4273
5502
  }
4274
- const fileContent = fs4.readFileSync(this.filePath, "utf-8");
4275
- const data = JSON.parse(fileContent);
4276
- this.users = Array.isArray(data.users) ? data.users : [];
5503
+ const fileContent = fs5.readFileSync(this.filePath, "utf-8");
5504
+ const rawData = JSON.parse(fileContent);
5505
+ const validatedData = UsersDataSchema.parse(rawData);
5506
+ this.users = validatedData.users;
4277
5507
  this.hasChanged = false;
4278
5508
  logger.debug(`Loaded ${this.users.length} users from file`);
4279
5509
  } catch (error) {
@@ -4290,13 +5520,17 @@ var UserManager = class {
4290
5520
  }
4291
5521
  try {
4292
5522
  const dir = path4.dirname(this.filePath);
4293
- if (!fs4.existsSync(dir)) {
4294
- fs4.mkdirSync(dir, { recursive: true });
5523
+ if (!fs5.existsSync(dir)) {
5524
+ fs5.mkdirSync(dir, { recursive: true });
4295
5525
  }
4296
- const data = { users: this.users };
4297
- fs4.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
5526
+ const usersToSave = this.users.map((user) => {
5527
+ const { wsIds, ...userWithoutWsIds } = user;
5528
+ return userWithoutWsIds;
5529
+ });
5530
+ const data = { users: usersToSave };
5531
+ fs5.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
4298
5532
  this.hasChanged = false;
4299
- logger.debug(`Synced ${this.users.length} users to file`);
5533
+ logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
4300
5534
  } catch (error) {
4301
5535
  logger.error("Failed to save users to file:", error);
4302
5536
  throw new Error(`Failed to save users to file: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -4343,13 +5577,17 @@ var UserManager = class {
4343
5577
  * @returns The created user
4344
5578
  */
4345
5579
  createUser(user) {
4346
- if (this.users.some((u) => u.username === user.username)) {
4347
- throw new Error(`User with username ${user.username} already exists`);
5580
+ const validatedUser = UserSchema.parse(user);
5581
+ if (this.users.some((u) => u.username === validatedUser.username)) {
5582
+ throw new Error(`User with username ${validatedUser.username} already exists`);
5583
+ }
5584
+ if (validatedUser.email && this.users.some((u) => u.email === validatedUser.email)) {
5585
+ throw new Error(`User with email ${validatedUser.email} already exists`);
4348
5586
  }
4349
- this.users.push(user);
5587
+ this.users.push(validatedUser);
4350
5588
  this.hasChanged = true;
4351
- logger.debug(`User created: ${user.username}`);
4352
- return user;
5589
+ logger.debug(`User created: ${validatedUser.username}`);
5590
+ return validatedUser;
4353
5591
  }
4354
5592
  /**
4355
5593
  * Read a user by username
@@ -4359,6 +5597,22 @@ var UserManager = class {
4359
5597
  getUser(username) {
4360
5598
  return this.users.find((u) => u.username === username);
4361
5599
  }
5600
+ /**
5601
+ * Read a user by email
5602
+ * @param email - Email to retrieve
5603
+ * @returns The user if found, undefined otherwise
5604
+ */
5605
+ getUserByEmail(email) {
5606
+ return this.users.find((u) => u.email === email);
5607
+ }
5608
+ /**
5609
+ * Find user by username or email
5610
+ * @param identifier - Username or email to search for
5611
+ * @returns The user if found, undefined otherwise
5612
+ */
5613
+ getUserByUsernameOrEmail(identifier) {
5614
+ return this.users.find((u) => u.username === identifier || u.email === identifier);
5615
+ }
4362
5616
  /**
4363
5617
  * Read all users
4364
5618
  * @returns Array of all users
@@ -4447,7 +5701,6 @@ var UserManager = class {
4447
5701
  }
4448
5702
  if (!user.wsIds.includes(wsId)) {
4449
5703
  user.wsIds.push(wsId);
4450
- this.hasChanged = true;
4451
5704
  logger.debug(`WebSocket ID added to user ${username}: ${wsId}`);
4452
5705
  }
4453
5706
  return true;
@@ -4469,7 +5722,6 @@ var UserManager = class {
4469
5722
  const initialLength = user.wsIds.length;
4470
5723
  user.wsIds = user.wsIds.filter((id) => id !== wsId);
4471
5724
  if (user.wsIds.length < initialLength) {
4472
- this.hasChanged = true;
4473
5725
  logger.debug(`WebSocket ID removed from user ${username}: ${wsId}`);
4474
5726
  }
4475
5727
  return true;
@@ -4494,7 +5746,7 @@ var UserManager = class {
4494
5746
  };
4495
5747
 
4496
5748
  // src/dashboards/dashboard-manager.ts
4497
- import fs5 from "fs";
5749
+ import fs6 from "fs";
4498
5750
  import path5 from "path";
4499
5751
  import os2 from "os";
4500
5752
  var DashboardManager = class {
@@ -4529,12 +5781,12 @@ var DashboardManager = class {
4529
5781
  createDashboard(dashboardId, dashboard) {
4530
5782
  const dashboardPath = this.getDashboardPath(dashboardId);
4531
5783
  const dashboardDir = path5.dirname(dashboardPath);
4532
- if (fs5.existsSync(dashboardPath)) {
5784
+ if (fs6.existsSync(dashboardPath)) {
4533
5785
  throw new Error(`Dashboard '${dashboardId}' already exists`);
4534
5786
  }
4535
5787
  const validated = DSLRendererPropsSchema.parse(dashboard);
4536
- fs5.mkdirSync(dashboardDir, { recursive: true });
4537
- fs5.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5788
+ fs6.mkdirSync(dashboardDir, { recursive: true });
5789
+ fs6.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
4538
5790
  logger.info(`Dashboard created: ${dashboardId}`);
4539
5791
  return validated;
4540
5792
  }
@@ -4545,12 +5797,12 @@ var DashboardManager = class {
4545
5797
  */
4546
5798
  getDashboard(dashboardId) {
4547
5799
  const dashboardPath = this.getDashboardPath(dashboardId);
4548
- if (!fs5.existsSync(dashboardPath)) {
5800
+ if (!fs6.existsSync(dashboardPath)) {
4549
5801
  logger.warn(`Dashboard not found: ${dashboardId}`);
4550
5802
  return null;
4551
5803
  }
4552
5804
  try {
4553
- const fileContent = fs5.readFileSync(dashboardPath, "utf-8");
5805
+ const fileContent = fs6.readFileSync(dashboardPath, "utf-8");
4554
5806
  const dashboard = JSON.parse(fileContent);
4555
5807
  const validated = DSLRendererPropsSchema.parse(dashboard);
4556
5808
  return validated;
@@ -4564,16 +5816,16 @@ var DashboardManager = class {
4564
5816
  * @returns Array of dashboard objects with their IDs
4565
5817
  */
4566
5818
  getAllDashboards() {
4567
- if (!fs5.existsSync(this.dashboardsBasePath)) {
4568
- fs5.mkdirSync(this.dashboardsBasePath, { recursive: true });
5819
+ if (!fs6.existsSync(this.dashboardsBasePath)) {
5820
+ fs6.mkdirSync(this.dashboardsBasePath, { recursive: true });
4569
5821
  return [];
4570
5822
  }
4571
5823
  const dashboards = [];
4572
5824
  try {
4573
- const dashboardDirs = fs5.readdirSync(this.dashboardsBasePath);
5825
+ const dashboardDirs = fs6.readdirSync(this.dashboardsBasePath);
4574
5826
  for (const dashboardId of dashboardDirs) {
4575
5827
  const dashboardPath = this.getDashboardPath(dashboardId);
4576
- if (fs5.existsSync(dashboardPath)) {
5828
+ if (fs6.existsSync(dashboardPath)) {
4577
5829
  const dashboard = this.getDashboard(dashboardId);
4578
5830
  if (dashboard) {
4579
5831
  dashboards.push({ dashboardId, dashboard });
@@ -4595,13 +5847,13 @@ var DashboardManager = class {
4595
5847
  */
4596
5848
  updateDashboard(dashboardId, dashboard) {
4597
5849
  const dashboardPath = this.getDashboardPath(dashboardId);
4598
- if (!fs5.existsSync(dashboardPath)) {
5850
+ if (!fs6.existsSync(dashboardPath)) {
4599
5851
  logger.warn(`Dashboard not found for update: ${dashboardId}`);
4600
5852
  return null;
4601
5853
  }
4602
5854
  try {
4603
5855
  const validated = DSLRendererPropsSchema.parse(dashboard);
4604
- fs5.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5856
+ fs6.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
4605
5857
  logger.info(`Dashboard updated: ${dashboardId}`);
4606
5858
  return validated;
4607
5859
  } catch (error) {
@@ -4617,12 +5869,12 @@ var DashboardManager = class {
4617
5869
  deleteDashboard(dashboardId) {
4618
5870
  const dashboardPath = this.getDashboardPath(dashboardId);
4619
5871
  const dashboardDir = path5.dirname(dashboardPath);
4620
- if (!fs5.existsSync(dashboardPath)) {
5872
+ if (!fs6.existsSync(dashboardPath)) {
4621
5873
  logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
4622
5874
  return false;
4623
5875
  }
4624
5876
  try {
4625
- fs5.rmSync(dashboardDir, { recursive: true, force: true });
5877
+ fs6.rmSync(dashboardDir, { recursive: true, force: true });
4626
5878
  logger.info(`Dashboard deleted: ${dashboardId}`);
4627
5879
  return true;
4628
5880
  } catch (error) {
@@ -4637,21 +5889,21 @@ var DashboardManager = class {
4637
5889
  */
4638
5890
  dashboardExists(dashboardId) {
4639
5891
  const dashboardPath = this.getDashboardPath(dashboardId);
4640
- return fs5.existsSync(dashboardPath);
5892
+ return fs6.existsSync(dashboardPath);
4641
5893
  }
4642
5894
  /**
4643
5895
  * Get dashboard count
4644
5896
  * @returns Number of dashboards
4645
5897
  */
4646
5898
  getDashboardCount() {
4647
- if (!fs5.existsSync(this.dashboardsBasePath)) {
5899
+ if (!fs6.existsSync(this.dashboardsBasePath)) {
4648
5900
  return 0;
4649
5901
  }
4650
5902
  try {
4651
- const dashboardDirs = fs5.readdirSync(this.dashboardsBasePath);
5903
+ const dashboardDirs = fs6.readdirSync(this.dashboardsBasePath);
4652
5904
  return dashboardDirs.filter((dir) => {
4653
5905
  const dashboardPath = this.getDashboardPath(dir);
4654
- return fs5.existsSync(dashboardPath);
5906
+ return fs6.existsSync(dashboardPath);
4655
5907
  }).length;
4656
5908
  } catch (error) {
4657
5909
  logger.error("Failed to get dashboard count:", error);
@@ -4661,7 +5913,7 @@ var DashboardManager = class {
4661
5913
  };
4662
5914
 
4663
5915
  // src/reports/report-manager.ts
4664
- import fs6 from "fs";
5916
+ import fs7 from "fs";
4665
5917
  import path6 from "path";
4666
5918
  import os3 from "os";
4667
5919
  var ReportManager = class {
@@ -4696,12 +5948,12 @@ var ReportManager = class {
4696
5948
  createReport(reportId, report) {
4697
5949
  const reportPath = this.getReportPath(reportId);
4698
5950
  const reportDir = path6.dirname(reportPath);
4699
- if (fs6.existsSync(reportPath)) {
5951
+ if (fs7.existsSync(reportPath)) {
4700
5952
  throw new Error(`Report '${reportId}' already exists`);
4701
5953
  }
4702
5954
  const validated = DSLRendererPropsSchema2.parse(report);
4703
- fs6.mkdirSync(reportDir, { recursive: true });
4704
- fs6.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
5955
+ fs7.mkdirSync(reportDir, { recursive: true });
5956
+ fs7.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
4705
5957
  logger.info(`Report created: ${reportId}`);
4706
5958
  return validated;
4707
5959
  }
@@ -4712,12 +5964,12 @@ var ReportManager = class {
4712
5964
  */
4713
5965
  getReport(reportId) {
4714
5966
  const reportPath = this.getReportPath(reportId);
4715
- if (!fs6.existsSync(reportPath)) {
5967
+ if (!fs7.existsSync(reportPath)) {
4716
5968
  logger.warn(`Report not found: ${reportId}`);
4717
5969
  return null;
4718
5970
  }
4719
5971
  try {
4720
- const fileContent = fs6.readFileSync(reportPath, "utf-8");
5972
+ const fileContent = fs7.readFileSync(reportPath, "utf-8");
4721
5973
  const report = JSON.parse(fileContent);
4722
5974
  const validated = DSLRendererPropsSchema2.parse(report);
4723
5975
  return validated;
@@ -4731,16 +5983,16 @@ var ReportManager = class {
4731
5983
  * @returns Array of report objects with their IDs
4732
5984
  */
4733
5985
  getAllReports() {
4734
- if (!fs6.existsSync(this.reportsBasePath)) {
4735
- fs6.mkdirSync(this.reportsBasePath, { recursive: true });
5986
+ if (!fs7.existsSync(this.reportsBasePath)) {
5987
+ fs7.mkdirSync(this.reportsBasePath, { recursive: true });
4736
5988
  return [];
4737
5989
  }
4738
5990
  const reports = [];
4739
5991
  try {
4740
- const reportDirs = fs6.readdirSync(this.reportsBasePath);
5992
+ const reportDirs = fs7.readdirSync(this.reportsBasePath);
4741
5993
  for (const reportId of reportDirs) {
4742
5994
  const reportPath = this.getReportPath(reportId);
4743
- if (fs6.existsSync(reportPath)) {
5995
+ if (fs7.existsSync(reportPath)) {
4744
5996
  const report = this.getReport(reportId);
4745
5997
  if (report) {
4746
5998
  reports.push({ reportId, report });
@@ -4762,13 +6014,13 @@ var ReportManager = class {
4762
6014
  */
4763
6015
  updateReport(reportId, report) {
4764
6016
  const reportPath = this.getReportPath(reportId);
4765
- if (!fs6.existsSync(reportPath)) {
6017
+ if (!fs7.existsSync(reportPath)) {
4766
6018
  logger.warn(`Report not found for update: ${reportId}`);
4767
6019
  return null;
4768
6020
  }
4769
6021
  try {
4770
6022
  const validated = DSLRendererPropsSchema2.parse(report);
4771
- fs6.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
6023
+ fs7.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
4772
6024
  logger.info(`Report updated: ${reportId}`);
4773
6025
  return validated;
4774
6026
  } catch (error) {
@@ -4784,12 +6036,12 @@ var ReportManager = class {
4784
6036
  deleteReport(reportId) {
4785
6037
  const reportPath = this.getReportPath(reportId);
4786
6038
  const reportDir = path6.dirname(reportPath);
4787
- if (!fs6.existsSync(reportPath)) {
6039
+ if (!fs7.existsSync(reportPath)) {
4788
6040
  logger.warn(`Report not found for deletion: ${reportId}`);
4789
6041
  return false;
4790
6042
  }
4791
6043
  try {
4792
- fs6.rmSync(reportDir, { recursive: true, force: true });
6044
+ fs7.rmSync(reportDir, { recursive: true, force: true });
4793
6045
  logger.info(`Report deleted: ${reportId}`);
4794
6046
  return true;
4795
6047
  } catch (error) {
@@ -4804,21 +6056,21 @@ var ReportManager = class {
4804
6056
  */
4805
6057
  reportExists(reportId) {
4806
6058
  const reportPath = this.getReportPath(reportId);
4807
- return fs6.existsSync(reportPath);
6059
+ return fs7.existsSync(reportPath);
4808
6060
  }
4809
6061
  /**
4810
6062
  * Get report count
4811
6063
  * @returns Number of reports
4812
6064
  */
4813
6065
  getReportCount() {
4814
- if (!fs6.existsSync(this.reportsBasePath)) {
6066
+ if (!fs7.existsSync(this.reportsBasePath)) {
4815
6067
  return 0;
4816
6068
  }
4817
6069
  try {
4818
- const reportDirs = fs6.readdirSync(this.reportsBasePath);
6070
+ const reportDirs = fs7.readdirSync(this.reportsBasePath);
4819
6071
  return reportDirs.filter((dir) => {
4820
6072
  const reportPath = this.getReportPath(dir);
4821
- return fs6.existsSync(reportPath);
6073
+ return fs7.existsSync(reportPath);
4822
6074
  }).length;
4823
6075
  } catch (error) {
4824
6076
  logger.error("Failed to get report count:", error);
@@ -5019,6 +6271,9 @@ var SuperatomSDK = class {
5019
6271
  this.maxReconnectAttempts = 5;
5020
6272
  this.collections = {};
5021
6273
  this.components = [];
6274
+ if (config.logLevel) {
6275
+ logger.setLogLevel(config.logLevel);
6276
+ }
5022
6277
  this.apiKey = config.apiKey;
5023
6278
  this.projectId = config.projectId;
5024
6279
  this.userId = config.userId || "anonymous";
@@ -5039,9 +6294,6 @@ var SuperatomSDK = class {
5039
6294
  });
5040
6295
  this.initializeDashboardManager();
5041
6296
  this.initializeReportManager();
5042
- this.connect().catch((error) => {
5043
- logger.error("Failed to connect to Superatom:", error);
5044
- });
5045
6297
  }
5046
6298
  /**
5047
6299
  * Initialize PromptLoader and load prompts into memory
@@ -5107,6 +6359,10 @@ var SuperatomSDK = class {
5107
6359
  * Connect to the Superatom WebSocket service
5108
6360
  */
5109
6361
  async connect() {
6362
+ if (this.connected && this.ws && this.ws.readyState === this.ws.OPEN) {
6363
+ logger.info("Already connected to WebSocket");
6364
+ return Promise.resolve();
6365
+ }
5110
6366
  return new Promise((resolve, reject) => {
5111
6367
  try {
5112
6368
  const url = new URL(this.url);
@@ -5169,7 +6425,7 @@ var SuperatomSDK = class {
5169
6425
  });
5170
6426
  break;
5171
6427
  case "USER_PROMPT_REQ":
5172
- handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders).catch((error) => {
6428
+ handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders, this.collections).catch((error) => {
5173
6429
  logger.error("Failed to handle user prompt request:", error);
5174
6430
  });
5175
6431
  break;
@@ -5321,6 +6577,7 @@ export {
5321
6577
  ThreadManager,
5322
6578
  UIBlock,
5323
6579
  UILogCollector,
5324
- UserManager
6580
+ UserManager,
6581
+ logger
5325
6582
  };
5326
6583
  //# sourceMappingURL=index.mjs.map