@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.js CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __commonJS = (cb, mod) => function __require() {
9
12
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
13
  };
@@ -102,7 +105,7 @@ var require_package = __commonJS({
102
105
  var require_main = __commonJS({
103
106
  "node_modules/dotenv/lib/main.js"(exports2, module2) {
104
107
  "use strict";
105
- var fs7 = require("fs");
108
+ var fs8 = require("fs");
106
109
  var path7 = require("path");
107
110
  var os4 = require("os");
108
111
  var crypto2 = require("crypto");
@@ -244,7 +247,7 @@ var require_main = __commonJS({
244
247
  if (options && options.path && options.path.length > 0) {
245
248
  if (Array.isArray(options.path)) {
246
249
  for (const filepath of options.path) {
247
- if (fs7.existsSync(filepath)) {
250
+ if (fs8.existsSync(filepath)) {
248
251
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
249
252
  }
250
253
  }
@@ -254,7 +257,7 @@ var require_main = __commonJS({
254
257
  } else {
255
258
  possibleVaultPath = path7.resolve(process.cwd(), ".env.vault");
256
259
  }
257
- if (fs7.existsSync(possibleVaultPath)) {
260
+ if (fs8.existsSync(possibleVaultPath)) {
258
261
  return possibleVaultPath;
259
262
  }
260
263
  return null;
@@ -307,7 +310,7 @@ var require_main = __commonJS({
307
310
  const parsedAll = {};
308
311
  for (const path8 of optionPaths) {
309
312
  try {
310
- const parsed = DotenvModule.parse(fs7.readFileSync(path8, { encoding }));
313
+ const parsed = DotenvModule.parse(fs8.readFileSync(path8, { encoding }));
311
314
  DotenvModule.populate(parsedAll, parsed, options);
312
315
  } catch (e) {
313
316
  if (debug) {
@@ -428,6 +431,128 @@ var require_main = __commonJS({
428
431
  }
429
432
  });
430
433
 
434
+ // src/userResponse/utils.ts
435
+ var utils_exports = {};
436
+ __export(utils_exports, {
437
+ convertTopToLimit: () => convertTopToLimit,
438
+ ensureQueryLimit: () => ensureQueryLimit,
439
+ fixScalarSubqueries: () => fixScalarSubqueries,
440
+ getJsonSizeInBytes: () => getJsonSizeInBytes,
441
+ validateMessageSize: () => validateMessageSize
442
+ });
443
+ function convertTopToLimit(query) {
444
+ if (!query || query.trim().length === 0) {
445
+ return query;
446
+ }
447
+ let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
448
+ if (modifiedQuery !== query) {
449
+ console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
450
+ }
451
+ return modifiedQuery;
452
+ }
453
+ function ensureQueryLimit(query, defaultLimit = 32, maxLimit = 32) {
454
+ if (!query || query.trim().length === 0) {
455
+ return query;
456
+ }
457
+ let trimmedQuery = query.trim();
458
+ const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
459
+ if (!isSelectQuery) {
460
+ return query;
461
+ }
462
+ trimmedQuery = convertTopToLimit(trimmedQuery);
463
+ const hadSemicolon = trimmedQuery.endsWith(";");
464
+ if (hadSemicolon) {
465
+ trimmedQuery = trimmedQuery.slice(0, -1).trim();
466
+ }
467
+ const limitMatches = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/gi);
468
+ if (limitMatches && limitMatches.length > 0) {
469
+ if (limitMatches.length > 1) {
470
+ console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
471
+ trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
472
+ } else {
473
+ const existingLimitMatch = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/i);
474
+ if (existingLimitMatch) {
475
+ const existingLimit = parseInt(existingLimitMatch[1], 10);
476
+ if (existingLimit <= maxLimit) {
477
+ if (hadSemicolon) {
478
+ trimmedQuery += ";";
479
+ }
480
+ return trimmedQuery;
481
+ }
482
+ console.warn(`\u26A0\uFE0F Query LIMIT ${existingLimit} exceeds maximum of ${maxLimit}. Reducing to ${maxLimit}...`);
483
+ trimmedQuery = trimmedQuery.replace(/\bLIMIT\s+\d+\b/i, `LIMIT ${maxLimit}`);
484
+ if (hadSemicolon) {
485
+ trimmedQuery += ";";
486
+ }
487
+ return trimmedQuery;
488
+ }
489
+ }
490
+ }
491
+ trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
492
+ if (hadSemicolon) {
493
+ trimmedQuery += ";";
494
+ }
495
+ return trimmedQuery;
496
+ }
497
+ function fixScalarSubqueries(query) {
498
+ if (!query || query.trim().length === 0) {
499
+ return query;
500
+ }
501
+ let modifiedQuery = query;
502
+ let hasChanges = false;
503
+ const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
504
+ const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
505
+ for (let i = matches.length - 1; i >= 0; i--) {
506
+ const match = matches[i];
507
+ const startPos = match.index + match[0].length - "SELECT ".length;
508
+ let parenDepth = 1;
509
+ let endPos = startPos;
510
+ let foundEnd = false;
511
+ for (let j = startPos; j < modifiedQuery.length; j++) {
512
+ const char = modifiedQuery[j];
513
+ if (char === "(") parenDepth++;
514
+ if (char === ")") {
515
+ parenDepth--;
516
+ if (parenDepth === 0) {
517
+ endPos = j;
518
+ foundEnd = true;
519
+ break;
520
+ }
521
+ }
522
+ }
523
+ if (!foundEnd) continue;
524
+ const subquery = modifiedQuery.substring(startPos, endPos);
525
+ if (/\bLIMIT\s+\d+/i.test(subquery)) {
526
+ continue;
527
+ }
528
+ const fixedSubquery = subquery.trim() + " LIMIT 1";
529
+ modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
530
+ hasChanges = true;
531
+ console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
532
+ }
533
+ if (hasChanges) {
534
+ console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
535
+ }
536
+ return modifiedQuery;
537
+ }
538
+ function getJsonSizeInBytes(obj) {
539
+ const jsonString = JSON.stringify(obj);
540
+ return Buffer.byteLength(jsonString, "utf8");
541
+ }
542
+ function validateMessageSize(message, maxSize = 1048576) {
543
+ const size = getJsonSizeInBytes(message);
544
+ return {
545
+ isValid: size <= maxSize,
546
+ size,
547
+ maxSize
548
+ };
549
+ }
550
+ var init_utils = __esm({
551
+ "src/userResponse/utils.ts"() {
552
+ "use strict";
553
+ }
554
+ });
555
+
431
556
  // src/index.ts
432
557
  var index_exports = {};
433
558
  __export(index_exports, {
@@ -441,7 +566,8 @@ __export(index_exports, {
441
566
  ThreadManager: () => ThreadManager,
442
567
  UIBlock: () => UIBlock,
443
568
  UILogCollector: () => UILogCollector,
444
- UserManager: () => UserManager
569
+ UserManager: () => UserManager,
570
+ logger: () => logger
445
571
  });
446
572
  module.exports = __toCommonJS(index_exports);
447
573
 
@@ -641,6 +767,18 @@ var DSLRendererPropsSchema2 = import_zod2.z.object({
641
767
  });
642
768
 
643
769
  // src/types.ts
770
+ var UserSchema = import_zod3.z.object({
771
+ username: import_zod3.z.string().min(1, "Username is required"),
772
+ email: import_zod3.z.string().email("Invalid email format").optional(),
773
+ password: import_zod3.z.string().min(1, "Password is required"),
774
+ fullname: import_zod3.z.string().optional(),
775
+ role: import_zod3.z.string().optional(),
776
+ wsIds: import_zod3.z.array(import_zod3.z.string()).optional()
777
+ // Only in memory, not persisted to file
778
+ });
779
+ var UsersDataSchema = import_zod3.z.object({
780
+ users: import_zod3.z.array(UserSchema)
781
+ });
644
782
  var MessageParticipantSchema = import_zod3.z.object({
645
783
  id: import_zod3.z.string().optional(),
646
784
  type: import_zod3.z.string().optional()
@@ -694,7 +832,8 @@ var UserPromptRequestPayloadSchema = import_zod3.z.object({
694
832
  SA_RUNTIME: import_zod3.z.object({
695
833
  threadId: import_zod3.z.string(),
696
834
  uiBlockId: import_zod3.z.string()
697
- }).optional()
835
+ }).optional(),
836
+ responseMode: import_zod3.z.enum(["component", "text"]).optional()
698
837
  });
699
838
  var UserPromptRequestMessageSchema = import_zod3.z.object({
700
839
  id: import_zod3.z.string(),
@@ -741,7 +880,10 @@ var UsersRequestPayloadSchema = import_zod3.z.object({
741
880
  operation: import_zod3.z.enum(["create", "update", "delete", "getAll", "getOne"]),
742
881
  data: import_zod3.z.object({
743
882
  username: import_zod3.z.string().optional(),
744
- password: import_zod3.z.string().optional()
883
+ email: import_zod3.z.string().email("Invalid email format").optional(),
884
+ password: import_zod3.z.string().optional(),
885
+ fullname: import_zod3.z.string().optional(),
886
+ role: import_zod3.z.string().optional()
745
887
  }).optional()
746
888
  });
747
889
  var UsersRequestMessageSchema = import_zod3.z.object({
@@ -806,21 +948,97 @@ var ReportsRequestMessageSchema = import_zod3.z.object({
806
948
  });
807
949
 
808
950
  // src/utils/logger.ts
951
+ var import_fs = __toESM(require("fs"));
809
952
  var PREFIX = "[SuperatomSDK]";
810
- var logger = {
811
- info: (...args) => {
812
- console.log(PREFIX, ...args);
813
- },
814
- error: (...args) => {
815
- console.error(PREFIX, ...args);
816
- },
817
- warn: (...args) => {
818
- console.warn(PREFIX, ...args);
819
- },
820
- debug: (...args) => {
821
- console.log(PREFIX, "[DEBUG]", ...args);
953
+ var LOGSTREAM = import_fs.default.createWriteStream("superatom-sdk.log", { flags: "a" });
954
+ var LOG_LEVEL_PRIORITY = {
955
+ errors: 0,
956
+ warnings: 1,
957
+ info: 2,
958
+ verbose: 3
959
+ };
960
+ var MESSAGE_LEVEL_PRIORITY = {
961
+ error: 0,
962
+ warn: 1,
963
+ info: 2,
964
+ debug: 3
965
+ };
966
+ var Logger = class {
967
+ constructor() {
968
+ const envLevel = (process.env.SUPERATOM_LOG_LEVEL || "info").toLowerCase();
969
+ if (this.isValidLogLevel(envLevel)) {
970
+ this.currentLevel = envLevel;
971
+ } else {
972
+ this.currentLevel = "info";
973
+ console.warn(
974
+ `${PREFIX} Invalid log level "${envLevel}". Using default "info". Valid levels: errors, warnings, info, verbose`
975
+ );
976
+ }
977
+ this.currentLevelPriority = LOG_LEVEL_PRIORITY[this.currentLevel];
978
+ }
979
+ /**
980
+ * Check if a string is a valid log level
981
+ */
982
+ isValidLogLevel(level) {
983
+ return level === "errors" || level === "warnings" || level === "info" || level === "verbose";
984
+ }
985
+ /**
986
+ * Check if a message should be logged based on current log level
987
+ */
988
+ shouldLog(messageLevel) {
989
+ const messagePriority = MESSAGE_LEVEL_PRIORITY[messageLevel];
990
+ return messagePriority <= this.currentLevelPriority;
991
+ }
992
+ /**
993
+ * Get current log level
994
+ */
995
+ getLogLevel() {
996
+ return this.currentLevel;
997
+ }
998
+ /**
999
+ * Set log level programmatically
1000
+ */
1001
+ setLogLevel(level) {
1002
+ this.currentLevel = level;
1003
+ this.currentLevelPriority = LOG_LEVEL_PRIORITY[level];
1004
+ }
1005
+ /**
1006
+ * Log info message (shown for info and verbose levels)
1007
+ */
1008
+ info(...args) {
1009
+ if (this.shouldLog("info")) {
1010
+ console.log(PREFIX, ...args);
1011
+ }
1012
+ }
1013
+ /**
1014
+ * Log error message (shown for all levels)
1015
+ */
1016
+ error(...args) {
1017
+ if (this.shouldLog("error")) {
1018
+ console.error(PREFIX, ...args);
1019
+ }
1020
+ }
1021
+ /**
1022
+ * Log warning message (shown for warnings, info, and verbose levels)
1023
+ */
1024
+ warn(...args) {
1025
+ if (this.shouldLog("warn")) {
1026
+ console.warn(PREFIX, ...args);
1027
+ }
1028
+ }
1029
+ /**
1030
+ * Log debug message (only shown for verbose level)
1031
+ */
1032
+ debug(...args) {
1033
+ if (this.shouldLog("debug")) {
1034
+ console.log(PREFIX, "[DEBUG]", ...args);
1035
+ }
1036
+ }
1037
+ file(...args) {
1038
+ LOGSTREAM.write(args.join(" ") + "\n");
822
1039
  }
823
1040
  };
1041
+ var logger = new Logger();
824
1042
 
825
1043
  // src/threads/uiblock.ts
826
1044
  var import_crypto = require("crypto");
@@ -855,13 +1073,15 @@ var UIBlock = class {
855
1073
  * @param generatedComponentMetadata - Optional metadata about the generated component
856
1074
  * @param actions - Optional array of available actions
857
1075
  * @param id - Optional custom ID, generates UUID if not provided
1076
+ * @param textResponse - Optional text response from LLM
858
1077
  */
859
- constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id) {
1078
+ constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id, textResponse = null) {
860
1079
  this.id = id || (0, import_crypto.randomUUID)();
861
1080
  this.userQuestion = userQuestion;
862
1081
  this.componentData = componentData;
863
1082
  this.generatedComponentMetadata = generatedComponentMetadata;
864
1083
  this.actions = actions;
1084
+ this.textResponse = textResponse;
865
1085
  this.createdAt = /* @__PURE__ */ new Date();
866
1086
  }
867
1087
  /**
@@ -888,6 +1108,9 @@ var UIBlock = class {
888
1108
  getComponentMetadata() {
889
1109
  return this.generatedComponentMetadata;
890
1110
  }
1111
+ getTextResponse() {
1112
+ return this.textResponse || "";
1113
+ }
891
1114
  /**
892
1115
  * Set or update component metadata
893
1116
  */
@@ -980,6 +1203,12 @@ var UIBlock = class {
980
1203
  const processedData = this.processDataForStorage(data);
981
1204
  this.componentData = { ...this.componentData, ...processedData };
982
1205
  }
1206
+ /**
1207
+ * Set or update text response
1208
+ */
1209
+ setTextResponse(textResponse) {
1210
+ this.textResponse = textResponse;
1211
+ }
983
1212
  /**
984
1213
  * Get all actions (only if they are resolved, not if fetching)
985
1214
  */
@@ -1063,6 +1292,7 @@ var UIBlock = class {
1063
1292
  userQuestion: this.userQuestion,
1064
1293
  generatedComponentMetadata: this.generatedComponentMetadata,
1065
1294
  componentData: this.componentData,
1295
+ textResponse: this.textResponse,
1066
1296
  actions: actionsValue,
1067
1297
  isFetchingActions: this.actions instanceof Promise,
1068
1298
  createdAt: this.createdAt.toISOString()
@@ -1163,8 +1393,11 @@ var Thread = class {
1163
1393
  const questionNum = index + 1;
1164
1394
  const question = block.getUserQuestion();
1165
1395
  const metadata = block.getComponentMetadata();
1166
- let componentSummary = "No component generated";
1167
- if (metadata && Object.keys(metadata).length > 0) {
1396
+ const textResponse = block.getTextResponse();
1397
+ let assistantResponse = "";
1398
+ const hasComponent = metadata && Object.keys(metadata).length > 0 && metadata.type;
1399
+ const hasTextResponse = textResponse && textResponse.trim().length > 0;
1400
+ if (hasComponent) {
1168
1401
  const parts = [];
1169
1402
  if (metadata.type) {
1170
1403
  parts.push(`Component Type: ${metadata.type}`);
@@ -1184,11 +1417,17 @@ var Thread = class {
1184
1417
  const componentTypes = metadata.props.config.components.map((c) => c.type).join(", ");
1185
1418
  parts.push(`Multi-component with: ${componentTypes}`);
1186
1419
  }
1187
- componentSummary = parts.join(", ");
1420
+ assistantResponse = parts.join(", ");
1421
+ } else if (hasTextResponse) {
1422
+ assistantResponse = textResponse;
1423
+ } else {
1424
+ assistantResponse = "No response generated";
1188
1425
  }
1189
- contextLines.push(`Q${questionNum}: ${question}`);
1190
- contextLines.push(`A${questionNum}: ${componentSummary}`);
1191
- contextLines.push("");
1426
+ contextLines.push(`User:
1427
+ ${question}`);
1428
+ contextLines.push(`Assistant:
1429
+ ${assistantResponse}`);
1430
+ contextLines.push("---");
1192
1431
  });
1193
1432
  return contextLines.join("\n").trim();
1194
1433
  }
@@ -1364,7 +1603,7 @@ function sendDataResponse(id, collection, op, data, meta, sendMessage) {
1364
1603
  }
1365
1604
 
1366
1605
  // src/bundle.ts
1367
- var fs = __toESM(require("fs"));
1606
+ var fs2 = __toESM(require("fs"));
1368
1607
  var path = __toESM(require("path"));
1369
1608
  function getBundleDir(configDir) {
1370
1609
  const bundleDir = configDir || process.env.SA_BUNDLE_DIR;
@@ -1377,16 +1616,16 @@ function getBundleDir(configDir) {
1377
1616
  }
1378
1617
  function getJS(bundleDir) {
1379
1618
  try {
1380
- if (!fs.existsSync(bundleDir)) {
1619
+ if (!fs2.existsSync(bundleDir)) {
1381
1620
  throw new Error(`Bundle directory does not exist: ${bundleDir}`);
1382
1621
  }
1383
- const stats = fs.statSync(bundleDir);
1622
+ const stats = fs2.statSync(bundleDir);
1384
1623
  if (!stats.isDirectory()) {
1385
1624
  throw new Error(`Bundle path is not a directory: ${bundleDir}`);
1386
1625
  }
1387
1626
  let files;
1388
1627
  try {
1389
- files = fs.readdirSync(bundleDir);
1628
+ files = fs2.readdirSync(bundleDir);
1390
1629
  } catch (error) {
1391
1630
  const errorMessage = error instanceof Error ? error.message : String(error);
1392
1631
  throw new Error(`Failed to read bundle directory: ${errorMessage}`);
@@ -1401,7 +1640,7 @@ function getJS(bundleDir) {
1401
1640
  const filePath = path.join(bundleDir, indexFile);
1402
1641
  logger.info(`Loading bundle from ${filePath}`);
1403
1642
  try {
1404
- return fs.readFileSync(filePath, "utf8");
1643
+ return fs2.readFileSync(filePath, "utf8");
1405
1644
  } catch (error) {
1406
1645
  const errorMessage = error instanceof Error ? error.message : String(error);
1407
1646
  throw new Error(`Failed to read bundle file: ${errorMessage}`);
@@ -1497,9 +1736,9 @@ function getUserManager() {
1497
1736
  }
1498
1737
  return currentUserManager;
1499
1738
  }
1500
- function findUserByUsername(username) {
1739
+ function findUserByUsernameOrEmail(identifier) {
1501
1740
  const manager = getUserManager();
1502
- const user = manager.getUser(username);
1741
+ const user = manager.getUserByUsernameOrEmail(identifier);
1503
1742
  return user || null;
1504
1743
  }
1505
1744
  function addWsIdToUser(username, wsId) {
@@ -1521,60 +1760,81 @@ async function cleanupUserStorage() {
1521
1760
 
1522
1761
  // src/auth/validator.ts
1523
1762
  function validateUser(credentials) {
1524
- const { username, password } = credentials;
1525
- if (!username || !password) {
1526
- logger.warn("Validation failed: Username and password are required");
1763
+ const { username, email, password } = credentials;
1764
+ const identifier = username || email;
1765
+ logger.debug("[validateUser] Starting user validation");
1766
+ logger.debug(`[validateUser] Username provided: ${username ? "\u2713" : "\u2717"}, Email provided: ${email ? "\u2713" : "\u2717"}, Password provided: ${password ? "\u2713" : "\u2717"}`);
1767
+ if (!identifier || !password) {
1768
+ logger.warn("[validateUser] Validation failed: Username/email and password are required");
1527
1769
  return {
1528
1770
  success: false,
1529
- error: "Username and password are required"
1771
+ error: "Username or email and password are required"
1530
1772
  };
1531
1773
  }
1532
- const user = findUserByUsername(username);
1774
+ logger.debug(`[validateUser] Looking up user by identifier: ${identifier}`);
1775
+ const user = findUserByUsernameOrEmail(identifier);
1533
1776
  if (!user) {
1534
- logger.warn(`Validation failed: User not found - ${username}`);
1777
+ logger.warn(`[validateUser] Validation failed: User not found - ${identifier}`);
1535
1778
  return {
1536
1779
  success: false,
1537
- error: "Invalid username"
1780
+ error: "Invalid username or email"
1538
1781
  };
1539
1782
  }
1783
+ logger.debug(`[validateUser] User found: ${user.username}, verifying password`);
1540
1784
  const hashedPassword = hashPassword(user.password);
1541
1785
  if (hashedPassword !== password) {
1542
- logger.warn(`Validation failed: Invalid password for user - ${username}`);
1786
+ logger.warn(`[validateUser] Validation failed: Invalid password for user - ${user.username}`);
1787
+ logger.debug(`[validateUser] Password hash mismatch for user: ${user.username}`);
1543
1788
  return {
1544
1789
  success: false,
1545
1790
  error: "Invalid password"
1546
1791
  };
1547
1792
  }
1548
- logger.debug(`User validated successfully: ${username}`);
1793
+ logger.info(`[validateUser] \u2713 User validated successfully: ${user.username}`);
1794
+ logger.debug(`[validateUser] Returning user data for: ${user.username}`);
1549
1795
  return {
1550
1796
  success: true,
1551
- data: user.username
1797
+ data: user.username,
1798
+ username: user.username
1552
1799
  };
1553
1800
  }
1554
1801
  function authenticateAndStoreWsId(credentials, wsId) {
1802
+ const identifier = credentials.username || credentials.email;
1803
+ logger.debug("[authenticateAndStoreWsId] Starting authentication and WebSocket ID storage");
1804
+ logger.debug("[authenticateAndStoreWsId] Validating user credentials");
1555
1805
  const validationResult = validateUser(credentials);
1556
1806
  if (!validationResult.success) {
1807
+ logger.warn(`[authenticateAndStoreWsId] User validation failed for: ${identifier}`);
1557
1808
  return validationResult;
1558
1809
  }
1559
- const stored = addWsIdToUser(credentials.username, wsId);
1560
- if (!stored) {
1561
- logger.error(`Failed to store WebSocket ID for user: ${credentials.username}`);
1562
- return {
1563
- success: false,
1564
- error: "Failed to store user session"
1565
- };
1566
- }
1567
- logger.info(`WebSocket ID stored for user: ${credentials.username}`);
1810
+ const username = validationResult.username;
1811
+ logger.info(`[authenticateAndStoreWsId] User ${username} validated, storing WebSocket ID`);
1812
+ logger.debug(`[authenticateAndStoreWsId] Calling addWsIdToUser for ${username}`);
1813
+ addWsIdToUser(username, wsId);
1814
+ logger.debug(`[authenticateAndStoreWsId] WebSocket ID ${wsId} associated with user ${username}`);
1568
1815
  return validationResult;
1569
1816
  }
1570
1817
  function verifyAuthToken(authToken) {
1571
1818
  try {
1819
+ logger.debug("[verifyAuthToken] Starting token verification");
1820
+ logger.debug("[verifyAuthToken] Decoding base64 token");
1572
1821
  const decodedString = Buffer.from(authToken, "base64").toString("utf-8");
1822
+ logger.debug("[verifyAuthToken] Parsing decoded token as JSON");
1573
1823
  const credentials = JSON.parse(decodedString);
1574
- logger.debug("Token decoded and parsed successfully");
1575
- return validateUser(credentials);
1824
+ logger.debug("[verifyAuthToken] Token decoded and parsed successfully");
1825
+ logger.debug(`[verifyAuthToken] Token contains username: ${credentials.username ? "\u2713" : "\u2717"}`);
1826
+ logger.debug("[verifyAuthToken] Validating credentials from token");
1827
+ const result = validateUser(credentials);
1828
+ if (result.success) {
1829
+ logger.info(`[verifyAuthToken] \u2713 Token verified successfully for user: ${credentials.username || "unknown"}`);
1830
+ } else {
1831
+ logger.warn(`[verifyAuthToken] Token verification failed: ${result.error}`);
1832
+ }
1833
+ return result;
1576
1834
  } catch (error) {
1577
- logger.error("Failed to verify auth token:", error);
1835
+ const errorMsg = error instanceof Error ? error.message : String(error);
1836
+ logger.error(`[verifyAuthToken] Failed to verify auth token: ${errorMsg}`);
1837
+ logger.debug("[verifyAuthToken] Token verification error details:", error);
1578
1838
  return {
1579
1839
  success: false,
1580
1840
  error: "Invalid token format"
@@ -1585,36 +1845,49 @@ function verifyAuthToken(authToken) {
1585
1845
  // src/handlers/auth-login-requests.ts
1586
1846
  async function handleAuthLoginRequest(data, sendMessage) {
1587
1847
  try {
1848
+ logger.debug("[AUTH_LOGIN_REQ] Parsing incoming auth login request");
1588
1849
  const authRequest = AuthLoginRequestMessageSchema.parse(data);
1589
1850
  const { id, payload } = authRequest;
1590
1851
  const login_data = payload.login_data;
1591
1852
  const wsId = authRequest.from.id;
1853
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Processing auth login request from client: ${wsId}`);
1854
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data present: ${!!login_data}`);
1592
1855
  if (!login_data) {
1856
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Login data not found in request`);
1593
1857
  sendDataResponse2(id, {
1594
1858
  success: false,
1595
1859
  error: "Login data not found"
1596
1860
  }, sendMessage, wsId);
1597
1861
  return;
1598
1862
  }
1863
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Decoding base64 login data`);
1599
1864
  let loginData;
1600
1865
  try {
1601
1866
  loginData = decodeBase64ToJson(login_data);
1867
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data decoded successfully`);
1602
1868
  } catch (error) {
1869
+ const errorMsg = error instanceof Error ? error.message : String(error);
1870
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Failed to decode login data: ${errorMsg}`);
1871
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Decode error details:`, error);
1603
1872
  sendDataResponse2(id, {
1604
1873
  success: false,
1605
1874
  error: "Invalid login data format"
1606
1875
  }, sendMessage, wsId);
1607
1876
  return;
1608
1877
  }
1609
- const { username, password } = loginData;
1610
- if (!username) {
1878
+ const { username, email, password } = loginData;
1879
+ const identifier = username || email;
1880
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Validating credentials - username: ${username ? "\u2713" : "\u2717"}, email: ${email ? "\u2713" : "\u2717"}, password: ${password ? "\u2713" : "\u2717"}`);
1881
+ if (!identifier) {
1882
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Username or email not found in login data`);
1611
1883
  sendDataResponse2(id, {
1612
1884
  success: false,
1613
- error: "Username not found in login data"
1885
+ error: "Username or email is required"
1614
1886
  }, sendMessage, wsId);
1615
1887
  return;
1616
1888
  }
1617
1889
  if (!password) {
1890
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Password not found in login data`);
1618
1891
  sendDataResponse2(id, {
1619
1892
  success: false,
1620
1893
  error: "Password not found in login data"
@@ -1622,20 +1895,46 @@ async function handleAuthLoginRequest(data, sendMessage) {
1622
1895
  return;
1623
1896
  }
1624
1897
  if (!wsId) {
1898
+ logger.error(`[AUTH_LOGIN_REQ ${id}] WebSocket ID not found in request`);
1625
1899
  sendDataResponse2(id, {
1626
1900
  success: false,
1627
1901
  error: "WebSocket ID not found"
1628
1902
  }, sendMessage, wsId);
1629
1903
  return;
1630
1904
  }
1905
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Credentials validated, authenticating user: ${identifier}`);
1906
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] WebSocket ID: ${wsId}`);
1907
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Calling authenticateAndStoreWsId for user: ${identifier}`);
1631
1908
  const authResult = authenticateAndStoreWsId(
1632
- { username, password },
1909
+ { username, email, password },
1633
1910
  wsId
1634
1911
  );
1912
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Authentication result for ${identifier}: ${authResult.success ? "success" : "failed"}`);
1913
+ if (!authResult.success) {
1914
+ logger.warn(`[AUTH_LOGIN_REQ ${id}] Authentication failed for ${identifier}: ${authResult.error}`);
1915
+ } else {
1916
+ logger.info(`[AUTH_LOGIN_REQ ${id}] User ${authResult.username || identifier} authenticated successfully`);
1917
+ }
1918
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Sending auth response to client`);
1635
1919
  sendDataResponse2(id, authResult, sendMessage, wsId);
1920
+ logger.info(`[AUTH_LOGIN_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth login request completed`);
1636
1921
  return;
1637
1922
  } catch (error) {
1638
- logger.error("Failed to handle auth login request:", error);
1923
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1924
+ const errorStack = error instanceof Error ? error.stack : void 0;
1925
+ logger.error(`[AUTH_LOGIN_REQ] Failed to handle auth login request: ${errorMessage}`);
1926
+ logger.debug(`[AUTH_LOGIN_REQ] Error stack trace:`, errorStack);
1927
+ try {
1928
+ const parsedData = data;
1929
+ if (parsedData?.id) {
1930
+ sendDataResponse2(parsedData.id, {
1931
+ success: false,
1932
+ error: `Internal error: ${errorMessage}`
1933
+ }, sendMessage, parsedData.from?.id);
1934
+ }
1935
+ } catch (sendError) {
1936
+ logger.error("[AUTH_LOGIN_REQ] Failed to send error response:", sendError);
1937
+ }
1639
1938
  }
1640
1939
  }
1641
1940
  function sendDataResponse2(id, res, sendMessage, clientId) {
@@ -1651,17 +1950,27 @@ function sendDataResponse2(id, res, sendMessage, clientId) {
1651
1950
  ...res
1652
1951
  }
1653
1952
  };
1953
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Sending ${res.success ? "successful" : "failed"} auth response to client: ${clientId}`);
1954
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
1955
+ if (res.error) {
1956
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Error message: ${res.error}`);
1957
+ }
1654
1958
  sendMessage(response);
1655
1959
  }
1656
1960
 
1657
1961
  // src/handlers/auth-verify-request.ts
1658
1962
  async function handleAuthVerifyRequest(data, sendMessage) {
1659
1963
  try {
1964
+ logger.debug("[AUTH_VERIFY_REQ] Parsing incoming auth verify request");
1660
1965
  const authRequest = AuthVerifyRequestMessageSchema.parse(data);
1661
1966
  const { id, payload } = authRequest;
1662
1967
  const token = payload.token;
1663
1968
  const wsId = authRequest.from.id;
1969
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Processing auth verify request from client: ${wsId}`);
1970
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Token present: ${!!token}`);
1971
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Token length: ${token ? token.length : 0} characters`);
1664
1972
  if (!token) {
1973
+ logger.error(`[AUTH_VERIFY_REQ ${id}] Token not found in request`);
1665
1974
  sendDataResponse3(id, {
1666
1975
  success: false,
1667
1976
  error: "Token not found"
@@ -1669,17 +1978,45 @@ async function handleAuthVerifyRequest(data, sendMessage) {
1669
1978
  return;
1670
1979
  }
1671
1980
  if (!wsId) {
1981
+ logger.error(`[AUTH_VERIFY_REQ ${id}] WebSocket ID not found in request`);
1672
1982
  sendDataResponse3(id, {
1673
1983
  success: false,
1674
1984
  error: "WebSocket ID not found"
1675
1985
  }, sendMessage, wsId);
1676
1986
  return;
1677
1987
  }
1988
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token validation starting`);
1989
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] WebSocket ID: ${wsId}`);
1990
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Calling verifyAuthToken`);
1991
+ const startTime = Date.now();
1678
1992
  const authResult = verifyAuthToken(token);
1993
+ const verificationTime = Date.now() - startTime;
1994
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token verification completed in ${verificationTime}ms - ${authResult.success ? "valid" : "invalid"}`);
1995
+ if (!authResult.success) {
1996
+ logger.warn(`[AUTH_VERIFY_REQ ${id}] Token verification failed: ${authResult.error}`);
1997
+ } else {
1998
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token verified successfully for user: ${authResult.data || "unknown"}`);
1999
+ }
2000
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Sending verification response to client`);
1679
2001
  sendDataResponse3(id, authResult, sendMessage, wsId);
2002
+ logger.info(`[AUTH_VERIFY_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth verify request completed`);
1680
2003
  return;
1681
2004
  } catch (error) {
1682
- logger.error("Failed to handle auth verify request:", error);
2005
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
2006
+ const errorStack = error instanceof Error ? error.stack : void 0;
2007
+ logger.error(`[AUTH_VERIFY_REQ] Failed to handle auth verify request: ${errorMessage}`);
2008
+ logger.debug(`[AUTH_VERIFY_REQ] Error stack trace:`, errorStack);
2009
+ try {
2010
+ const parsedData = data;
2011
+ if (parsedData?.id) {
2012
+ sendDataResponse3(parsedData.id, {
2013
+ success: false,
2014
+ error: `Internal error: ${errorMessage}`
2015
+ }, sendMessage, parsedData.from?.id);
2016
+ }
2017
+ } catch (sendError) {
2018
+ logger.error("[AUTH_VERIFY_REQ] Failed to send error response:", sendError);
2019
+ }
1683
2020
  }
1684
2021
  }
1685
2022
  function sendDataResponse3(id, res, sendMessage, clientId) {
@@ -1695,59 +2032,26 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
1695
2032
  ...res
1696
2033
  }
1697
2034
  };
2035
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Sending ${res.success ? "successful" : "failed"} verification response to client: ${clientId}`);
2036
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
2037
+ if (res.error) {
2038
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Error message: ${res.error}`);
2039
+ }
2040
+ if (res.data) {
2041
+ logger.debug(`[AUTH_VERIFY_RES ${id}] User verified: ${res.data}`);
2042
+ }
1698
2043
  sendMessage(response);
1699
2044
  }
1700
2045
 
1701
2046
  // src/userResponse/groq.ts
1702
2047
  var import_dotenv = __toESM(require_main());
1703
2048
 
1704
- // src/userResponse/utils.ts
1705
- function convertTopToLimit(query) {
1706
- if (!query || query.trim().length === 0) {
1707
- return query;
1708
- }
1709
- let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
1710
- if (modifiedQuery !== query) {
1711
- console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
1712
- }
1713
- return modifiedQuery;
1714
- }
1715
- function ensureQueryLimit(query, defaultLimit = 50) {
1716
- if (!query || query.trim().length === 0) {
1717
- return query;
1718
- }
1719
- let trimmedQuery = query.trim();
1720
- const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
1721
- if (!isSelectQuery) {
1722
- return query;
1723
- }
1724
- trimmedQuery = convertTopToLimit(trimmedQuery);
1725
- const hadSemicolon = trimmedQuery.endsWith(";");
1726
- if (hadSemicolon) {
1727
- trimmedQuery = trimmedQuery.slice(0, -1).trim();
1728
- }
1729
- const limitMatches = trimmedQuery.match(/\bLIMIT\s+\d+\b/gi);
1730
- if (limitMatches && limitMatches.length > 0) {
1731
- if (limitMatches.length > 1) {
1732
- console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
1733
- trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
1734
- } else {
1735
- if (hadSemicolon) {
1736
- trimmedQuery += ";";
1737
- }
1738
- return trimmedQuery;
1739
- }
1740
- }
1741
- trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
1742
- if (hadSemicolon) {
1743
- trimmedQuery += ";";
1744
- }
1745
- return trimmedQuery;
1746
- }
2049
+ // src/userResponse/base-llm.ts
2050
+ init_utils();
1747
2051
 
1748
2052
  // src/userResponse/schema.ts
1749
2053
  var import_path = __toESM(require("path"));
1750
- var import_fs = __toESM(require("fs"));
2054
+ var import_fs2 = __toESM(require("fs"));
1751
2055
  var Schema = class {
1752
2056
  constructor(schemaFilePath) {
1753
2057
  this.cachedSchema = null;
@@ -1761,11 +2065,11 @@ var Schema = class {
1761
2065
  logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
1762
2066
  try {
1763
2067
  const dir = import_path.default.dirname(this.schemaFilePath);
1764
- if (!import_fs.default.existsSync(dir)) {
2068
+ if (!import_fs2.default.existsSync(dir)) {
1765
2069
  logger.info(`Creating directory structure: ${dir}`);
1766
- import_fs.default.mkdirSync(dir, { recursive: true });
2070
+ import_fs2.default.mkdirSync(dir, { recursive: true });
1767
2071
  }
1768
- if (!import_fs.default.existsSync(this.schemaFilePath)) {
2072
+ if (!import_fs2.default.existsSync(this.schemaFilePath)) {
1769
2073
  logger.info(`Schema file does not exist at ${this.schemaFilePath}, creating with empty schema`);
1770
2074
  const initialSchema = {
1771
2075
  database: "",
@@ -1774,11 +2078,11 @@ var Schema = class {
1774
2078
  tables: [],
1775
2079
  relationships: []
1776
2080
  };
1777
- import_fs.default.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
2081
+ import_fs2.default.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
1778
2082
  this.cachedSchema = initialSchema;
1779
2083
  return initialSchema;
1780
2084
  }
1781
- const fileContent = import_fs.default.readFileSync(this.schemaFilePath, "utf-8");
2085
+ const fileContent = import_fs2.default.readFileSync(this.schemaFilePath, "utf-8");
1782
2086
  const schema2 = JSON.parse(fileContent);
1783
2087
  this.cachedSchema = schema2;
1784
2088
  return schema2;
@@ -1879,7 +2183,7 @@ var Schema = class {
1879
2183
  var schema = new Schema();
1880
2184
 
1881
2185
  // src/userResponse/prompt-loader.ts
1882
- var import_fs2 = __toESM(require("fs"));
2186
+ var import_fs3 = __toESM(require("fs"));
1883
2187
  var import_path2 = __toESM(require("path"));
1884
2188
  var PromptLoader = class {
1885
2189
  constructor(config) {
@@ -1906,7 +2210,9 @@ var PromptLoader = class {
1906
2210
  "single-component",
1907
2211
  "mutli-component",
1908
2212
  "actions",
1909
- "container-metadata"
2213
+ "container-metadata",
2214
+ "text-response",
2215
+ "match-text-components"
1910
2216
  ];
1911
2217
  for (const promptType of promptTypes) {
1912
2218
  try {
@@ -1931,9 +2237,9 @@ var PromptLoader = class {
1931
2237
  try {
1932
2238
  const systemPath = import_path2.default.join(dir, promptName, "system.md");
1933
2239
  const userPath = import_path2.default.join(dir, promptName, "user.md");
1934
- if (import_fs2.default.existsSync(systemPath) && import_fs2.default.existsSync(userPath)) {
1935
- const system = import_fs2.default.readFileSync(systemPath, "utf-8");
1936
- const user = import_fs2.default.readFileSync(userPath, "utf-8");
2240
+ if (import_fs3.default.existsSync(systemPath) && import_fs3.default.existsSync(userPath)) {
2241
+ const system = import_fs3.default.readFileSync(systemPath, "utf-8");
2242
+ const user = import_fs3.default.readFileSync(userPath, "utf-8");
1937
2243
  logger.debug(`Loaded prompt '${promptName}' from ${dir}`);
1938
2244
  return { system, user };
1939
2245
  }
@@ -2055,6 +2361,14 @@ var LLM = class {
2055
2361
  throw new Error(`Unsupported provider: ${provider}. Use "anthropic" or "groq"`);
2056
2362
  }
2057
2363
  }
2364
+ /* Stream response with tool calling support (Anthropic only for now) */
2365
+ static async streamWithTools(messages, tools, toolHandler, options = {}, maxIterations = 3) {
2366
+ const [provider, modelName] = this._parseModel(options.model);
2367
+ if (provider !== "anthropic") {
2368
+ throw new Error(`Tool calling is only supported for Anthropic models`);
2369
+ }
2370
+ return this._anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations);
2371
+ }
2058
2372
  // ============================================================
2059
2373
  // PRIVATE HELPER METHODS
2060
2374
  // ============================================================
@@ -2132,6 +2446,86 @@ var LLM = class {
2132
2446
  }
2133
2447
  return fullText;
2134
2448
  }
2449
+ static async _anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
2450
+ const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
2451
+ const client = new import_sdk.default({
2452
+ apiKey
2453
+ });
2454
+ const conversationMessages = [{
2455
+ role: "user",
2456
+ content: messages.user
2457
+ }];
2458
+ let iterations = 0;
2459
+ let finalText = "";
2460
+ while (iterations < maxIterations) {
2461
+ iterations++;
2462
+ const response = await client.messages.create({
2463
+ model: modelName,
2464
+ max_tokens: options.maxTokens || 4e3,
2465
+ temperature: options.temperature,
2466
+ system: messages.sys,
2467
+ messages: conversationMessages,
2468
+ tools
2469
+ });
2470
+ if (response.stop_reason === "end_turn") {
2471
+ const textBlock = response.content.find((block) => block.type === "text");
2472
+ if (textBlock && textBlock.type === "text") {
2473
+ finalText = textBlock.text;
2474
+ if (options.partial) {
2475
+ options.partial(finalText);
2476
+ }
2477
+ }
2478
+ break;
2479
+ }
2480
+ if (response.stop_reason === "tool_use") {
2481
+ const toolUses = response.content.filter((block) => block.type === "tool_use");
2482
+ if (toolUses.length === 0) {
2483
+ break;
2484
+ }
2485
+ conversationMessages.push({
2486
+ role: "assistant",
2487
+ content: response.content
2488
+ });
2489
+ const toolResults = {
2490
+ role: "user",
2491
+ content: []
2492
+ };
2493
+ for (const toolUse of toolUses) {
2494
+ if (toolUse.type === "tool_use") {
2495
+ try {
2496
+ const result = await toolHandler(toolUse.name, toolUse.input);
2497
+ toolResults.content.push({
2498
+ type: "tool_result",
2499
+ tool_use_id: toolUse.id,
2500
+ content: typeof result === "string" ? result : JSON.stringify(result)
2501
+ });
2502
+ } catch (error) {
2503
+ toolResults.content.push({
2504
+ type: "tool_result",
2505
+ tool_use_id: toolUse.id,
2506
+ content: error instanceof Error ? error.message : String(error),
2507
+ is_error: true
2508
+ });
2509
+ }
2510
+ }
2511
+ }
2512
+ conversationMessages.push(toolResults);
2513
+ } else {
2514
+ const textBlock = response.content.find((block) => block.type === "text");
2515
+ if (textBlock && textBlock.type === "text") {
2516
+ finalText = textBlock.text;
2517
+ if (options.partial) {
2518
+ options.partial(finalText);
2519
+ }
2520
+ }
2521
+ break;
2522
+ }
2523
+ }
2524
+ if (iterations >= maxIterations) {
2525
+ throw new Error(`Max iterations (${maxIterations}) reached in tool calling loop`);
2526
+ }
2527
+ return finalText;
2528
+ }
2135
2529
  // ============================================================
2136
2530
  // GROQ IMPLEMENTATION
2137
2531
  // ============================================================
@@ -2258,7 +2652,9 @@ var BaseLLM = class {
2258
2652
  needsMultipleComponents: result.needsMultipleComponents || false
2259
2653
  };
2260
2654
  } catch (error) {
2261
- console.error("Error classifying user question:", error);
2655
+ const errorMsg = error instanceof Error ? error.message : String(error);
2656
+ logger.error(`[${this.getProviderName()}] Error classifying user question: ${errorMsg}`);
2657
+ logger.debug(`[${this.getProviderName()}] Classification error details:`, error);
2262
2658
  throw error;
2263
2659
  }
2264
2660
  }
@@ -2296,6 +2692,7 @@ var BaseLLM = class {
2296
2692
  );
2297
2693
  const props = result.props || originalProps;
2298
2694
  if (props && props.query) {
2695
+ props.query = fixScalarSubqueries(props.query);
2299
2696
  props.query = ensureQueryLimit(props.query, this.defaultLimit);
2300
2697
  }
2301
2698
  if (props && props.query) {
@@ -2322,7 +2719,9 @@ var BaseLLM = class {
2322
2719
  modifications: result.modifications || []
2323
2720
  };
2324
2721
  } catch (error) {
2325
- console.error(`Error validating/modifying props with ${this.getProviderName()}:`, error);
2722
+ const errorMsg = error instanceof Error ? error.message : String(error);
2723
+ logger.error(`[${this.getProviderName()}] Error validating/modifying props: ${errorMsg}`);
2724
+ logger.debug(`[${this.getProviderName()}] Props validation error details:`, error);
2326
2725
  throw error;
2327
2726
  }
2328
2727
  }
@@ -2442,7 +2841,9 @@ var BaseLLM = class {
2442
2841
  isGenerated: true
2443
2842
  };
2444
2843
  } catch (error) {
2445
- console.error("Error generating analytical component:", error);
2844
+ const errorMsg = error instanceof Error ? error.message : String(error);
2845
+ logger.error(`[${this.getProviderName()}] Error generating analytical component: ${errorMsg}`);
2846
+ logger.debug(`[${this.getProviderName()}] Analytical component generation error details:`, error);
2446
2847
  throw error;
2447
2848
  }
2448
2849
  }
@@ -2484,7 +2885,9 @@ var BaseLLM = class {
2484
2885
  description: result.description || `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
2485
2886
  };
2486
2887
  } catch (error) {
2487
- console.error("Error generating container metadata:", error);
2888
+ const errorMsg = error instanceof Error ? error.message : String(error);
2889
+ logger.error(`[${this.getProviderName()}] Error generating container metadata: ${errorMsg}`);
2890
+ logger.debug(`[${this.getProviderName()}] Container metadata error details:`, error);
2488
2891
  return {
2489
2892
  title: `${userPrompt} - Dashboard`,
2490
2893
  description: `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
@@ -2536,24 +2939,24 @@ var BaseLLM = class {
2536
2939
  component = components[componentIndex - 1];
2537
2940
  }
2538
2941
  const matchedMsg = `${this.getProviderName()} matched component: ${component?.name || "None"}`;
2539
- console.log("\u2713", matchedMsg);
2942
+ logger.info(`[${this.getProviderName()}] \u2713 ${matchedMsg}`);
2540
2943
  logCollector?.info(matchedMsg);
2541
2944
  if (result.alternativeMatches && result.alternativeMatches.length > 0) {
2542
- console.log(" Alternative matches:");
2945
+ logger.debug(`[${this.getProviderName()}] Alternative matches found: ${result.alternativeMatches.length}`);
2543
2946
  const altMatches = result.alternativeMatches.map(
2544
2947
  (alt) => `${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`
2545
2948
  ).join(" | ");
2546
2949
  logCollector?.info(`Alternative matches: ${altMatches}`);
2547
2950
  result.alternativeMatches.forEach((alt) => {
2548
- console.log(` - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
2951
+ logger.debug(`[${this.getProviderName()}] - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
2549
2952
  });
2550
2953
  }
2551
2954
  if (!component) {
2552
2955
  const noMatchMsg = `No matching component found (confidence: ${confidence}%)`;
2553
- console.log("\u2717", noMatchMsg);
2956
+ logger.warn(`[${this.getProviderName()}] \u2717 ${noMatchMsg}`);
2554
2957
  logCollector?.warn(noMatchMsg);
2555
2958
  const genMsg = "Attempting to match component from analytical question...";
2556
- console.log("\u2713", genMsg);
2959
+ logger.info(`[${this.getProviderName()}] \u2713 ${genMsg}`);
2557
2960
  logCollector?.info(genMsg);
2558
2961
  const generatedResult = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2559
2962
  if (generatedResult.component) {
@@ -2614,8 +3017,10 @@ var BaseLLM = class {
2614
3017
  confidence
2615
3018
  };
2616
3019
  } catch (error) {
2617
- console.error(`Error matching component with ${this.getProviderName()}:`, error);
2618
- logCollector?.error(`Error matching component: ${error.message}`);
3020
+ const errorMsg = error instanceof Error ? error.message : String(error);
3021
+ logger.error(`[${this.getProviderName()}] Error matching component: ${errorMsg}`);
3022
+ logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
3023
+ logCollector?.error(`Error matching component: ${errorMsg}`);
2619
3024
  throw error;
2620
3025
  }
2621
3026
  }
@@ -2646,7 +3051,9 @@ var BaseLLM = class {
2646
3051
  isGenerated: true
2647
3052
  };
2648
3053
  } catch (error) {
2649
- console.error("Error matching multiple analytical components:", error);
3054
+ const errorMsg = error instanceof Error ? error.message : String(error);
3055
+ logger.error(`[${this.getProviderName()}] Error matching multiple analytical components: ${errorMsg}`);
3056
+ logger.debug(`[${this.getProviderName()}] Multiple components matching error details:`, error);
2650
3057
  return {
2651
3058
  components: [],
2652
3059
  reasoning: "Error occurred while matching components",
@@ -2725,76 +3132,551 @@ var BaseLLM = class {
2725
3132
  isGenerated: true
2726
3133
  };
2727
3134
  } catch (error) {
2728
- console.error("Error generating multi-component response:", error);
3135
+ const errorMsg = error instanceof Error ? error.message : String(error);
3136
+ logger.error(`[${this.getProviderName()}] Error generating multi-component response: ${errorMsg}`);
3137
+ logger.debug(`[${this.getProviderName()}] Multi-component response error details:`, error);
2729
3138
  throw error;
2730
3139
  }
2731
3140
  }
2732
3141
  /**
2733
- * Main orchestration function that classifies question and routes to appropriate handler
2734
- * This is the NEW recommended entry point for handling user requests
2735
- * ALWAYS returns a SINGLE component (wraps multiple in MultiComponentContainer)
3142
+ * Match components from text response suggestions
3143
+ * Takes a text response with component suggestions (c1:type format) and matches with available components
3144
+ * @param textResponse - The text response containing component suggestions
3145
+ * @param components - List of available components
3146
+ * @param apiKey - Optional API key
3147
+ * @param logCollector - Optional log collector
3148
+ * @returns Object containing matched components, selected layout, and reasoning
2736
3149
  */
2737
- async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory) {
3150
+ async matchComponentsFromTextResponse(textResponse, components, apiKey, logCollector) {
2738
3151
  try {
2739
- const classifyMsg = "Classifying user question...";
2740
- logCollector?.info(classifyMsg);
2741
- const classification = await this.classifyUserQuestion(userPrompt, apiKey, logCollector, conversationHistory);
2742
- const classInfo = `Question type: ${classification.questionType}, Visualizations: ${classification.visualizations.join(", ") || "None"}, Multiple components: ${classification.needsMultipleComponents}`;
2743
- logCollector?.info(classInfo);
2744
- if (classification.questionType === "analytical") {
2745
- if (classification.visualizations.length > 1) {
2746
- const multiMsg = `Matching ${classification.visualizations.length} components for types: ${classification.visualizations.join(", ")}`;
2747
- logCollector?.info(multiMsg);
2748
- const componentPromises = classification.visualizations.map((vizType) => {
2749
- logCollector?.info(`Matching component for type: ${vizType}`);
2750
- return this.generateAnalyticalComponent(
2751
- userPrompt,
2752
- components,
2753
- vizType,
2754
- apiKey,
2755
- logCollector,
2756
- conversationHistory
2757
- ).then((result) => ({ vizType, result }));
2758
- });
2759
- const settledResults = await Promise.allSettled(componentPromises);
2760
- const matchedComponents = [];
2761
- for (const settledResult of settledResults) {
2762
- if (settledResult.status === "fulfilled") {
2763
- const { vizType, result } = settledResult.value;
2764
- if (result.component) {
2765
- matchedComponents.push(result.component);
2766
- logCollector?.info(`Matched: ${result.component.name}`);
2767
- logger.info("Component : ", result.component.name, " props: ", result.component.props);
2768
- } else {
2769
- logCollector?.warn(`Failed to match component for type: ${vizType}`);
2770
- }
2771
- } else {
2772
- logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
2773
- }
2774
- }
2775
- console.log("matched components: after multi comp", matchedComponents.length);
2776
- if (matchedComponents.length === 0) {
2777
- return {
2778
- component: null,
2779
- reasoning: "Failed to match any components for the requested visualization types",
2780
- method: "classification-multi-failed",
2781
- questionType: classification.questionType,
2782
- needsMultipleComponents: true,
2783
- propsModified: false,
2784
- queryModified: false
2785
- };
2786
- }
2787
- logCollector?.info("Generating container metadata...");
2788
- const containerMetadata = await this.generateContainerMetadata(
2789
- userPrompt,
2790
- classification.visualizations,
2791
- apiKey,
2792
- logCollector,
2793
- conversationHistory
2794
- );
2795
- const containerComponent = {
2796
- id: `multi_container_${Date.now()}`,
2797
- name: "MultiComponentContainer",
3152
+ logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
3153
+ let availableComponentsText = "No components available";
3154
+ if (components && components.length > 0) {
3155
+ availableComponentsText = components.map((comp, idx) => {
3156
+ const keywords = comp.keywords ? comp.keywords.join(", ") : "";
3157
+ const propsPreview = comp.props ? JSON.stringify(comp.props, null, 2) : "No props";
3158
+ return `${idx + 1}. ID: ${comp.id}
3159
+ Name: ${comp.name}
3160
+ Type: ${comp.type}
3161
+ Description: ${comp.description || "No description"}
3162
+ Keywords: ${keywords}
3163
+ Props Structure: ${propsPreview}`;
3164
+ }).join("\n\n");
3165
+ }
3166
+ const schemaDoc = schema.generateSchemaDocumentation();
3167
+ const prompts = await promptLoader.loadPrompts("match-text-components", {
3168
+ TEXT_RESPONSE: textResponse,
3169
+ AVAILABLE_COMPONENTS: availableComponentsText,
3170
+ SCHEMA_DOC: schemaDoc
3171
+ });
3172
+ logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
3173
+ logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
3174
+ logCollector?.info("Matching components from text response...");
3175
+ const rawResponse = await LLM.stream(
3176
+ {
3177
+ sys: prompts.system,
3178
+ user: prompts.user
3179
+ },
3180
+ {
3181
+ model: this.model,
3182
+ maxTokens: 3e3,
3183
+ temperature: 0.2,
3184
+ apiKey: this.getApiKey(apiKey)
3185
+ },
3186
+ false
3187
+ // Don't parse as JSON yet, get raw response
3188
+ );
3189
+ logger.debug(`[${this.getProviderName()}] Raw component matching response length: ${rawResponse?.length || 0}`);
3190
+ logger.file(`[${this.getProviderName()}] Component matching raw response:`, rawResponse);
3191
+ let result;
3192
+ try {
3193
+ let cleanedResponse = rawResponse || "";
3194
+ cleanedResponse = cleanedResponse.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
3195
+ const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
3196
+ if (jsonMatch) {
3197
+ cleanedResponse = jsonMatch[0];
3198
+ }
3199
+ result = JSON.parse(cleanedResponse);
3200
+ } catch (parseError) {
3201
+ logger.error(`[${this.getProviderName()}] Failed to parse component matching JSON response`);
3202
+ const errorMsg = parseError instanceof Error ? parseError.message : String(parseError);
3203
+ const posMatch = errorMsg.match(/position (\d+)/);
3204
+ const errorPos = posMatch ? parseInt(posMatch[1]) : -1;
3205
+ if (errorPos > 0 && rawResponse) {
3206
+ const start = Math.max(0, errorPos - 200);
3207
+ const end = Math.min(rawResponse.length, errorPos + 200);
3208
+ logger.debug(`[${this.getProviderName()}] Error context (position ${errorPos}):`);
3209
+ logger.debug(rawResponse.substring(start, end));
3210
+ logger.debug(" ".repeat(Math.min(200, errorPos - start)) + "^--- Error here");
3211
+ }
3212
+ logger.debug(`[${this.getProviderName()}] Raw response (first 2000 chars):`, rawResponse?.substring(0, 2e3));
3213
+ logger.debug(`[${this.getProviderName()}] Parse error:`, parseError);
3214
+ logCollector?.error(`Failed to parse component matching response: ${errorMsg}`);
3215
+ try {
3216
+ logger.info(`[${this.getProviderName()}] Attempting aggressive JSON cleanup...`);
3217
+ let aggressive = rawResponse || "";
3218
+ aggressive = aggressive.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
3219
+ const match = aggressive.match(/\{[\s\S]*\}/);
3220
+ if (match) {
3221
+ aggressive = match[0];
3222
+ }
3223
+ aggressive = aggressive.replace(/,(\s*[}\]])/g, "$1");
3224
+ result = JSON.parse(aggressive);
3225
+ logger.info(`[${this.getProviderName()}] Aggressive cleanup succeeded!`);
3226
+ } catch (secondError) {
3227
+ logger.error(`[${this.getProviderName()}] Aggressive cleanup also failed`);
3228
+ return {
3229
+ components: [],
3230
+ selectedLayout: "MultiComponentContainer",
3231
+ selectedLayoutId: "",
3232
+ selectedLayoutComponent: null,
3233
+ layoutReasoning: "No layout selected"
3234
+ };
3235
+ }
3236
+ }
3237
+ const matchedComponents = result.matchedComponents || [];
3238
+ const selectedLayout = result.selectedLayout || "MultiComponentContainer";
3239
+ const selectedLayoutId = result.selectedLayoutId || "";
3240
+ const layoutReasoning = result.layoutReasoning || "No layout reasoning provided";
3241
+ let selectedLayoutComponent = null;
3242
+ if (selectedLayoutId) {
3243
+ selectedLayoutComponent = components.find((c) => c.id === selectedLayoutId) || null;
3244
+ if (!selectedLayoutComponent) {
3245
+ logger.warn(`[${this.getProviderName()}] Layout component ${selectedLayoutId} not found in available components`);
3246
+ }
3247
+ }
3248
+ logger.info(`[${this.getProviderName()}] Matched ${matchedComponents.length} components from text response`);
3249
+ logger.info(`[${this.getProviderName()}] Selected layout: ${selectedLayout} (ID: ${selectedLayoutId})`);
3250
+ logger.info(`[${this.getProviderName()}] Layout reasoning: ${layoutReasoning}`);
3251
+ if (matchedComponents.length > 0) {
3252
+ logCollector?.info(`Matched ${matchedComponents.length} components for visualization using ${selectedLayout}`);
3253
+ logCollector?.info(`Layout reasoning: ${layoutReasoning}`);
3254
+ matchedComponents.forEach((comp, idx) => {
3255
+ logCollector?.info(` ${idx + 1}. ${comp.componentName} (${comp.componentType}): ${comp.reasoning}`);
3256
+ if (comp.props?.query) {
3257
+ logCollector?.logQuery(
3258
+ `Component ${idx + 1} query`,
3259
+ comp.props.query,
3260
+ { componentName: comp.componentName, title: comp.props.title }
3261
+ );
3262
+ }
3263
+ });
3264
+ }
3265
+ const finalComponents = matchedComponents.map((mc) => {
3266
+ const originalComponent = components.find((c) => c.id === mc.componentId);
3267
+ if (!originalComponent) {
3268
+ logger.warn(`[${this.getProviderName()}] Component ${mc.componentId} not found in available components`);
3269
+ return null;
3270
+ }
3271
+ return {
3272
+ ...originalComponent,
3273
+ props: {
3274
+ ...originalComponent.props,
3275
+ ...mc.props
3276
+ }
3277
+ };
3278
+ }).filter(Boolean);
3279
+ return {
3280
+ components: finalComponents,
3281
+ selectedLayout,
3282
+ selectedLayoutId,
3283
+ selectedLayoutComponent,
3284
+ layoutReasoning
3285
+ };
3286
+ } catch (error) {
3287
+ const errorMsg = error instanceof Error ? error.message : String(error);
3288
+ logger.error(`[${this.getProviderName()}] Error matching components from text response: ${errorMsg}`);
3289
+ logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
3290
+ logCollector?.error(`Error matching components: ${errorMsg}`);
3291
+ return {
3292
+ components: [],
3293
+ selectedLayout: "MultiComponentContainer",
3294
+ selectedLayoutId: "",
3295
+ selectedLayoutComponent: null,
3296
+ layoutReasoning: "Error occurred during component matching"
3297
+ };
3298
+ }
3299
+ }
3300
+ /**
3301
+ * Generate text-based response for user question
3302
+ * This provides conversational text responses instead of component generation
3303
+ * Supports tool calling for query execution with automatic retry on errors (max 3 attempts)
3304
+ * After generating text response, if components are provided, matches suggested components
3305
+ * @param streamCallback - Optional callback function to receive text chunks as they stream
3306
+ * @param collections - Collection registry for executing database queries via database.execute
3307
+ * @param components - Optional list of available components for matching suggestions
3308
+ */
3309
+ async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory, streamCallback, collections, components) {
3310
+ const errors = [];
3311
+ logger.debug(`[${this.getProviderName()}] Starting text response generation`);
3312
+ logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
3313
+ try {
3314
+ const schemaDoc = schema.generateSchemaDocumentation();
3315
+ const prompts = await promptLoader.loadPrompts("text-response", {
3316
+ USER_PROMPT: userPrompt,
3317
+ CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
3318
+ SCHEMA_DOC: schemaDoc
3319
+ });
3320
+ logger.file("\n=============================\nsystem prompt:", prompts.system);
3321
+ logger.file("\n=============================\nuser prompt:", prompts.user);
3322
+ logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
3323
+ logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
3324
+ logCollector?.info("Generating text response with query execution capability...");
3325
+ const tools = [{
3326
+ name: "execute_query",
3327
+ 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.",
3328
+ input_schema: {
3329
+ type: "object",
3330
+ properties: {
3331
+ query: {
3332
+ type: "string",
3333
+ description: "The SQL query to execute. Must be valid SQL syntax using table and column names from the schema."
3334
+ },
3335
+ reasoning: {
3336
+ type: "string",
3337
+ description: "Brief explanation of what this query does and why it answers the user's question."
3338
+ }
3339
+ },
3340
+ required: ["query"]
3341
+ }
3342
+ }];
3343
+ const queryAttempts = /* @__PURE__ */ new Map();
3344
+ const MAX_QUERY_ATTEMPTS = 6;
3345
+ let maxAttemptsReached = false;
3346
+ let fullStreamedText = "";
3347
+ const wrappedStreamCallback = streamCallback ? (chunk) => {
3348
+ fullStreamedText += chunk;
3349
+ streamCallback(chunk);
3350
+ } : void 0;
3351
+ const toolHandler = async (toolName, toolInput) => {
3352
+ if (toolName === "execute_query") {
3353
+ let query = toolInput.query;
3354
+ const reasoning = toolInput.reasoning;
3355
+ const { ensureQueryLimit: ensureQueryLimit2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
3356
+ query = ensureQueryLimit2(query, 32, 32);
3357
+ const queryKey = query.toLowerCase().replace(/\s+/g, " ").trim();
3358
+ const attempts = (queryAttempts.get(queryKey) || 0) + 1;
3359
+ queryAttempts.set(queryKey, attempts);
3360
+ logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${query.substring(0, 100)}...`);
3361
+ if (reasoning) {
3362
+ logCollector?.info(`Query reasoning: ${reasoning}`);
3363
+ }
3364
+ if (attempts > MAX_QUERY_ATTEMPTS) {
3365
+ const errorMsg = `Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`;
3366
+ logger.error(`[${this.getProviderName()}] ${errorMsg}`);
3367
+ logCollector?.error(errorMsg);
3368
+ maxAttemptsReached = true;
3369
+ if (wrappedStreamCallback) {
3370
+ wrappedStreamCallback(`
3371
+
3372
+ \u274C ${errorMsg}
3373
+
3374
+ Please try rephrasing your question or simplifying your request.
3375
+
3376
+ `);
3377
+ }
3378
+ throw new Error(errorMsg);
3379
+ }
3380
+ try {
3381
+ if (wrappedStreamCallback) {
3382
+ if (attempts === 1) {
3383
+ wrappedStreamCallback(`
3384
+
3385
+ \u{1F50D} **Analyzing your question...**
3386
+
3387
+ `);
3388
+ if (reasoning) {
3389
+ wrappedStreamCallback(`\u{1F4AD} ${reasoning}
3390
+
3391
+ `);
3392
+ }
3393
+ wrappedStreamCallback(`\u{1F4DD} **Generated SQL Query:**
3394
+ \`\`\`sql
3395
+ ${query}
3396
+ \`\`\`
3397
+
3398
+ `);
3399
+ wrappedStreamCallback(`\u26A1 **Executing query...**
3400
+
3401
+ `);
3402
+ } else {
3403
+ wrappedStreamCallback(`
3404
+
3405
+ \u{1F504} **Retrying with corrected query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS})...**
3406
+
3407
+ `);
3408
+ if (reasoning) {
3409
+ wrappedStreamCallback(`\u{1F4AD} ${reasoning}
3410
+
3411
+ `);
3412
+ }
3413
+ wrappedStreamCallback(`\u{1F4DD} **Corrected SQL Query:**
3414
+ \`\`\`sql
3415
+ ${query}
3416
+ \`\`\`
3417
+
3418
+ `);
3419
+ wrappedStreamCallback(`\u26A1 **Executing query...**
3420
+
3421
+ `);
3422
+ }
3423
+ }
3424
+ logCollector?.logQuery(
3425
+ `Executing SQL query (attempt ${attempts})`,
3426
+ query,
3427
+ { reasoning, attempt: attempts }
3428
+ );
3429
+ if (!collections || !collections["database"] || !collections["database"]["execute"]) {
3430
+ throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
3431
+ }
3432
+ const result2 = await collections["database"]["execute"]({ sql: query });
3433
+ const data = result2?.data || result2;
3434
+ const rowCount = result2?.count ?? (Array.isArray(data) ? data.length : "N/A");
3435
+ logger.info(`[${this.getProviderName()}] Query executed successfully, rows returned: ${rowCount}`);
3436
+ logCollector?.info(`Query successful, returned ${rowCount} rows`);
3437
+ if (wrappedStreamCallback) {
3438
+ wrappedStreamCallback(`\u2705 **Query executed successfully!**
3439
+
3440
+ `);
3441
+ if (Array.isArray(data) && data.length > 0) {
3442
+ const firstRow = data[0];
3443
+ const columns = Object.keys(firstRow);
3444
+ if (data.length === 1 && columns.length === 1) {
3445
+ const value = firstRow[columns[0]];
3446
+ wrappedStreamCallback(`**Result:** ${value}
3447
+
3448
+ `);
3449
+ } else if (data.length > 0) {
3450
+ wrappedStreamCallback(`**Retrieved ${rowCount} rows**
3451
+
3452
+ `);
3453
+ wrappedStreamCallback(`<DataTable>${JSON.stringify(data)}</DataTable>
3454
+
3455
+ `);
3456
+ }
3457
+ } else if (Array.isArray(data) && data.length === 0) {
3458
+ wrappedStreamCallback(`**No rows returned.**
3459
+
3460
+ `);
3461
+ }
3462
+ wrappedStreamCallback(`\u{1F4CA} **Analyzing results...**
3463
+
3464
+ `);
3465
+ }
3466
+ return JSON.stringify(data, null, 2);
3467
+ } catch (error) {
3468
+ const errorMsg = error instanceof Error ? error.message : String(error);
3469
+ logger.error(`[${this.getProviderName()}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
3470
+ logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
3471
+ if (wrappedStreamCallback) {
3472
+ wrappedStreamCallback(`\u274C **Query execution failed:**
3473
+ \`\`\`
3474
+ ${errorMsg}
3475
+ \`\`\`
3476
+
3477
+ `);
3478
+ if (attempts < MAX_QUERY_ATTEMPTS) {
3479
+ wrappedStreamCallback(`\u{1F527} **Generating corrected query...**
3480
+
3481
+ `);
3482
+ }
3483
+ }
3484
+ throw new Error(`Query execution failed: ${errorMsg}`);
3485
+ }
3486
+ }
3487
+ throw new Error(`Unknown tool: ${toolName}`);
3488
+ };
3489
+ const result = await LLM.streamWithTools(
3490
+ {
3491
+ sys: prompts.system,
3492
+ user: prompts.user
3493
+ },
3494
+ tools,
3495
+ toolHandler,
3496
+ {
3497
+ model: this.model,
3498
+ maxTokens: 4e3,
3499
+ temperature: 0.7,
3500
+ apiKey: this.getApiKey(apiKey),
3501
+ partial: wrappedStreamCallback
3502
+ // Pass the wrapped streaming callback to LLM
3503
+ },
3504
+ 10
3505
+ // max iterations: allows for 6 retries + final response + buffer
3506
+ );
3507
+ logger.info(`[${this.getProviderName()}] Text response stream completed`);
3508
+ const textResponse = fullStreamedText || result || "I apologize, but I was unable to generate a response.";
3509
+ if (maxAttemptsReached) {
3510
+ logger.warn(`[${this.getProviderName()}] Max query attempts reached, returning failure response`);
3511
+ logCollector?.error("Failed to generate valid query after maximum attempts");
3512
+ return {
3513
+ success: false,
3514
+ errors: [`Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`],
3515
+ data: {
3516
+ text: textResponse,
3517
+ // Include the streamed text showing all attempts
3518
+ matchedComponents: [],
3519
+ method: `${this.getProviderName()}-text-response-max-attempts`
3520
+ }
3521
+ };
3522
+ }
3523
+ logCollector?.info(`Text response: ${textResponse.substring(0, 100)}${textResponse.length > 100 ? "..." : ""}`);
3524
+ logCollector?.logExplanation(
3525
+ "Text response generated",
3526
+ "Generated plain text response with component suggestions",
3527
+ {
3528
+ textLength: textResponse.length
3529
+ }
3530
+ );
3531
+ let matchedComponents = [];
3532
+ let selectedLayoutComponent = null;
3533
+ let layoutReasoning = "No layout selected";
3534
+ if (components && components.length > 0) {
3535
+ logger.info(`[${this.getProviderName()}] Matching components from text response...`);
3536
+ const matchResult = await this.matchComponentsFromTextResponse(
3537
+ textResponse,
3538
+ components,
3539
+ apiKey,
3540
+ logCollector
3541
+ );
3542
+ matchedComponents = matchResult.components;
3543
+ selectedLayoutComponent = matchResult.selectedLayoutComponent;
3544
+ layoutReasoning = matchResult.layoutReasoning;
3545
+ }
3546
+ let container_componet = null;
3547
+ if (matchedComponents.length > 0) {
3548
+ if (selectedLayoutComponent) {
3549
+ container_componet = {
3550
+ ...selectedLayoutComponent,
3551
+ id: `${selectedLayoutComponent.id}_${Date.now()}`,
3552
+ description: layoutReasoning,
3553
+ props: {
3554
+ ...selectedLayoutComponent.props,
3555
+ config: {
3556
+ ...selectedLayoutComponent.props?.config || {},
3557
+ components: matchedComponents
3558
+ }
3559
+ }
3560
+ };
3561
+ logger.info(`[${this.getProviderName()}] Created ${selectedLayoutComponent.name} (${selectedLayoutComponent.type}) container with ${matchedComponents.length} components`);
3562
+ logCollector?.info(`Created ${selectedLayoutComponent.name} with ${matchedComponents.length} components: ${layoutReasoning}`);
3563
+ } else {
3564
+ container_componet = {
3565
+ id: `multi_container_${Date.now()}`,
3566
+ name: "MultiComponentContainer",
3567
+ type: "Container",
3568
+ description: layoutReasoning,
3569
+ category: "dynamic",
3570
+ keywords: ["dashboard", "layout", "container"],
3571
+ props: {
3572
+ config: {
3573
+ components: matchedComponents
3574
+ }
3575
+ }
3576
+ };
3577
+ logger.info(`[${this.getProviderName()}] Created fallback MultiComponentContainer with ${matchedComponents.length} components`);
3578
+ logCollector?.info(`Created MultiComponentContainer with ${matchedComponents.length} components: ${layoutReasoning}`);
3579
+ }
3580
+ }
3581
+ return {
3582
+ success: true,
3583
+ data: {
3584
+ text: textResponse,
3585
+ matchedComponents,
3586
+ component: container_componet,
3587
+ layoutReasoning,
3588
+ method: `${this.getProviderName()}-text-response`
3589
+ },
3590
+ errors: []
3591
+ };
3592
+ } catch (error) {
3593
+ const errorMsg = error instanceof Error ? error.message : String(error);
3594
+ logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
3595
+ logCollector?.error(`Error generating text response: ${errorMsg}`);
3596
+ errors.push(errorMsg);
3597
+ return {
3598
+ success: false,
3599
+ errors,
3600
+ data: {
3601
+ text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
3602
+ matchedComponents: [],
3603
+ method: `${this.getProviderName()}-text-response-error`
3604
+ }
3605
+ };
3606
+ }
3607
+ }
3608
+ /**
3609
+ * Generate component response for user question
3610
+ * This provides conversational component suggestions based on user question
3611
+ * Supports component generation and matching
3612
+ */
3613
+ async generateComponentResponse(userPrompt, components, apiKey, logCollector, conversationHistory) {
3614
+ const errors = [];
3615
+ try {
3616
+ logger.info(`[${this.getProviderName()}] Using component response mode`);
3617
+ const classifyMsg = "Classifying user question...";
3618
+ logCollector?.info(classifyMsg);
3619
+ const classification = await this.classifyUserQuestion(userPrompt, apiKey, logCollector, conversationHistory);
3620
+ const classInfo = `Question type: ${classification.questionType}, Visualizations: ${classification.visualizations.join(", ") || "None"}, Multiple components: ${classification.needsMultipleComponents}`;
3621
+ logCollector?.info(classInfo);
3622
+ if (classification.questionType === "analytical") {
3623
+ if (classification.visualizations.length > 1) {
3624
+ const multiMsg = `Matching ${classification.visualizations.length} components for types: ${classification.visualizations.join(", ")}`;
3625
+ logCollector?.info(multiMsg);
3626
+ const componentPromises = classification.visualizations.map((vizType) => {
3627
+ logCollector?.info(`Matching component for type: ${vizType}`);
3628
+ return this.generateAnalyticalComponent(
3629
+ userPrompt,
3630
+ components,
3631
+ vizType,
3632
+ apiKey,
3633
+ logCollector,
3634
+ conversationHistory
3635
+ ).then((result) => ({ vizType, result }));
3636
+ });
3637
+ const settledResults = await Promise.allSettled(componentPromises);
3638
+ const matchedComponents = [];
3639
+ for (const settledResult of settledResults) {
3640
+ if (settledResult.status === "fulfilled") {
3641
+ const { vizType, result } = settledResult.value;
3642
+ if (result.component) {
3643
+ matchedComponents.push(result.component);
3644
+ logCollector?.info(`Matched: ${result.component.name}`);
3645
+ logger.info("Component : ", result.component.name, " props: ", result.component.props);
3646
+ } else {
3647
+ logCollector?.warn(`Failed to match component for type: ${vizType}`);
3648
+ }
3649
+ } else {
3650
+ logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
3651
+ }
3652
+ }
3653
+ logger.debug(`[${this.getProviderName()}] Matched ${matchedComponents.length} components for multi-component container`);
3654
+ if (matchedComponents.length === 0) {
3655
+ return {
3656
+ success: true,
3657
+ data: {
3658
+ component: null,
3659
+ reasoning: "Failed to match any components for the requested visualization types",
3660
+ method: "classification-multi-failed",
3661
+ questionType: classification.questionType,
3662
+ needsMultipleComponents: true,
3663
+ propsModified: false,
3664
+ queryModified: false
3665
+ },
3666
+ errors: []
3667
+ };
3668
+ }
3669
+ logCollector?.info("Generating container metadata...");
3670
+ const containerMetadata = await this.generateContainerMetadata(
3671
+ userPrompt,
3672
+ classification.visualizations,
3673
+ apiKey,
3674
+ logCollector,
3675
+ conversationHistory
3676
+ );
3677
+ const containerComponent = {
3678
+ id: `multi_container_${Date.now()}`,
3679
+ name: "MultiComponentContainer",
2798
3680
  type: "Container",
2799
3681
  description: containerMetadata.description,
2800
3682
  category: "dynamic",
@@ -2811,38 +3693,50 @@ var BaseLLM = class {
2811
3693
  };
2812
3694
  logCollector?.info(`Created multi-component container with ${matchedComponents.length} components: "${containerMetadata.title}"`);
2813
3695
  return {
2814
- component: containerComponent,
2815
- reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
2816
- method: "classification-multi-generated",
2817
- questionType: classification.questionType,
2818
- needsMultipleComponents: true,
2819
- propsModified: false,
2820
- queryModified: false
3696
+ success: true,
3697
+ data: {
3698
+ component: containerComponent,
3699
+ reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
3700
+ method: "classification-multi-generated",
3701
+ questionType: classification.questionType,
3702
+ needsMultipleComponents: true,
3703
+ propsModified: false,
3704
+ queryModified: false
3705
+ },
3706
+ errors: []
2821
3707
  };
2822
3708
  } else if (classification.visualizations.length === 1) {
2823
3709
  const vizType = classification.visualizations[0];
2824
3710
  logCollector?.info(`Matching single component for type: ${vizType}`);
2825
3711
  const result = await this.generateAnalyticalComponent(userPrompt, components, vizType, apiKey, logCollector, conversationHistory);
2826
3712
  return {
2827
- component: result.component,
2828
- reasoning: result.reasoning,
2829
- method: "classification-generated",
2830
- questionType: classification.questionType,
2831
- needsMultipleComponents: false,
2832
- propsModified: false,
2833
- queryModified: false
3713
+ success: true,
3714
+ data: {
3715
+ component: result.component,
3716
+ reasoning: result.reasoning,
3717
+ method: "classification-generated",
3718
+ questionType: classification.questionType,
3719
+ needsMultipleComponents: false,
3720
+ propsModified: false,
3721
+ queryModified: false
3722
+ },
3723
+ errors: []
2834
3724
  };
2835
3725
  } else {
2836
3726
  logCollector?.info("No specific visualization type - matching from all components");
2837
3727
  const result = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2838
3728
  return {
2839
- component: result.component,
2840
- reasoning: result.reasoning,
2841
- method: "classification-generated-auto",
2842
- questionType: classification.questionType,
2843
- needsMultipleComponents: false,
2844
- propsModified: false,
2845
- queryModified: false
3729
+ success: true,
3730
+ data: {
3731
+ component: result.component,
3732
+ reasoning: result.reasoning,
3733
+ method: "classification-generated-auto",
3734
+ questionType: classification.questionType,
3735
+ needsMultipleComponents: false,
3736
+ propsModified: false,
3737
+ queryModified: false
3738
+ },
3739
+ errors: []
2846
3740
  };
2847
3741
  }
2848
3742
  } else if (classification.questionType === "data_modification" || classification.questionType === "general") {
@@ -2850,28 +3744,103 @@ var BaseLLM = class {
2850
3744
  logCollector?.info(matchMsg);
2851
3745
  const matchResult = await this.matchComponent(userPrompt, components, apiKey, logCollector, conversationHistory);
2852
3746
  return {
2853
- component: matchResult.component,
2854
- reasoning: matchResult.reasoning,
2855
- method: "classification-matched",
2856
- questionType: classification.questionType,
2857
- needsMultipleComponents: false,
2858
- propsModified: matchResult.propsModified,
2859
- queryModified: matchResult.queryModified
3747
+ success: true,
3748
+ data: {
3749
+ component: matchResult.component,
3750
+ reasoning: matchResult.reasoning,
3751
+ method: "classification-matched",
3752
+ questionType: classification.questionType,
3753
+ needsMultipleComponents: false,
3754
+ propsModified: matchResult.propsModified,
3755
+ queryModified: matchResult.queryModified
3756
+ },
3757
+ errors: []
2860
3758
  };
2861
3759
  } else {
2862
3760
  logCollector?.info("General question - no component needed");
2863
3761
  return {
2864
- component: null,
2865
- reasoning: "General question - no component needed",
2866
- method: "classification-general",
2867
- questionType: classification.questionType,
2868
- needsMultipleComponents: false
3762
+ success: true,
3763
+ data: {
3764
+ component: null,
3765
+ reasoning: "General question - no component needed",
3766
+ method: "classification-general",
3767
+ questionType: classification.questionType,
3768
+ needsMultipleComponents: false,
3769
+ propsModified: false,
3770
+ queryModified: false
3771
+ },
3772
+ errors: []
2869
3773
  };
2870
3774
  }
2871
3775
  } catch (error) {
2872
- logCollector?.error(`Error handling user request: ${error.message}`);
2873
- throw error;
3776
+ const errorMsg = error instanceof Error ? error.message : String(error);
3777
+ logger.error(`[${this.getProviderName()}] Error generating component response: ${errorMsg}`);
3778
+ logger.debug(`[${this.getProviderName()}] Component response generation error details:`, error);
3779
+ logCollector?.error(`Error generating component response: ${errorMsg}`);
3780
+ errors.push(errorMsg);
3781
+ return {
3782
+ success: false,
3783
+ errors,
3784
+ data: void 0
3785
+ };
3786
+ }
3787
+ }
3788
+ /**
3789
+ * Main orchestration function that classifies question and routes to appropriate handler
3790
+ * This is the NEW recommended entry point for handling user requests
3791
+ * Supports both component generation and text response modes
3792
+ *
3793
+ * @param responseMode - 'component' for component generation (default), 'text' for text responses
3794
+ * @param streamCallback - Optional callback function to receive text chunks as they stream (only for text mode)
3795
+ * @param collections - Collection registry for executing database queries (required for text mode)
3796
+ */
3797
+ async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) {
3798
+ const startTime = Date.now();
3799
+ logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
3800
+ if (responseMode === "text") {
3801
+ logger.info(`[${this.getProviderName()}] Using text response mode`);
3802
+ logCollector?.info("Generating text response...");
3803
+ const textResponse = await this.generateTextResponse(
3804
+ userPrompt,
3805
+ apiKey,
3806
+ logCollector,
3807
+ conversationHistory,
3808
+ streamCallback,
3809
+ collections,
3810
+ components
3811
+ );
3812
+ if (!textResponse.success) {
3813
+ const elapsedTime3 = Date.now() - startTime;
3814
+ logger.error(`[${this.getProviderName()}] Text response generation failed`);
3815
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
3816
+ logCollector?.info(`Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
3817
+ return textResponse;
3818
+ }
3819
+ const elapsedTime2 = Date.now() - startTime;
3820
+ logger.info(`[${this.getProviderName()}] Text response generated successfully`);
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 textResponse;
3824
+ }
3825
+ const componentResponse = await this.generateComponentResponse(
3826
+ userPrompt,
3827
+ components,
3828
+ apiKey,
3829
+ logCollector,
3830
+ conversationHistory
3831
+ );
3832
+ if (!componentResponse.success) {
3833
+ const elapsedTime2 = Date.now() - startTime;
3834
+ logger.error(`[${this.getProviderName()}] Component response generation failed`);
3835
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3836
+ logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3837
+ return componentResponse;
2874
3838
  }
3839
+ const elapsedTime = Date.now() - startTime;
3840
+ logger.info(`[${this.getProviderName()}] Component response generated successfully`);
3841
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
3842
+ logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
3843
+ return componentResponse;
2875
3844
  }
2876
3845
  /**
2877
3846
  * Generate next questions that the user might ask based on the original prompt and generated component
@@ -2917,8 +3886,10 @@ var BaseLLM = class {
2917
3886
  );
2918
3887
  return nextQuestions;
2919
3888
  } catch (error) {
2920
- console.error(`Error generating next questions with ${this.getProviderName()}:`, error);
2921
- logCollector?.error(`Error generating next questions: ${error.message}`);
3889
+ const errorMsg = error instanceof Error ? error.message : String(error);
3890
+ logger.error(`[${this.getProviderName()}] Error generating next questions: ${errorMsg}`);
3891
+ logger.debug(`[${this.getProviderName()}] Next questions generation error details:`, error);
3892
+ logCollector?.error(`Error generating next questions: ${errorMsg}`);
2922
3893
  return [];
2923
3894
  }
2924
3895
  }
@@ -2982,112 +3953,130 @@ function getLLMProviders() {
2982
3953
  return DEFAULT_PROVIDERS;
2983
3954
  }
2984
3955
  }
2985
- var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2986
- const msg = "Using Anthropic Claude matching method...";
2987
- console.log(msg);
3956
+ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3957
+ logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
3958
+ logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
3959
+ const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
2988
3960
  logCollector?.info(msg);
2989
- if (components.length === 0) {
3961
+ if (responseMode === "component" && components.length === 0) {
2990
3962
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3963
+ logger.error("[useAnthropicMethod] No components available");
2991
3964
  logCollector?.error(emptyMsg);
2992
- return { success: false, reason: emptyMsg };
2993
- }
2994
- try {
2995
- const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
2996
- return { success: true, data: matchResult };
2997
- } catch (error) {
2998
- const errorMsg = error instanceof Error ? error.message : String(error);
2999
- logCollector?.error(`Anthropic method failed: ${errorMsg}`);
3000
- throw error;
3965
+ return { success: false, errors: [emptyMsg] };
3001
3966
  }
3967
+ logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
3968
+ const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3969
+ logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
3970
+ return matchResult;
3002
3971
  };
3003
- var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
3004
- const msg = "Using Groq LLM matching method...";
3005
- console.log(msg);
3972
+ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3973
+ logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
3974
+ logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
3975
+ const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
3976
+ logger.info(msg);
3006
3977
  logCollector?.info(msg);
3007
- if (components.length === 0) {
3978
+ if (responseMode === "component" && components.length === 0) {
3008
3979
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3980
+ logger.error("[useGroqMethod] No components available");
3009
3981
  logCollector?.error(emptyMsg);
3010
- return { success: false, reason: emptyMsg };
3011
- }
3012
- try {
3013
- const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
3014
- return { success: true, data: matchResult };
3015
- } catch (error) {
3016
- const errorMsg = error instanceof Error ? error.message : String(error);
3017
- logCollector?.error(`Groq method failed: ${errorMsg}`);
3018
- throw error;
3982
+ return { success: false, errors: [emptyMsg] };
3019
3983
  }
3984
+ logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
3985
+ const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3986
+ logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
3987
+ return matchResult;
3020
3988
  };
3021
3989
  var getUserResponseFromCache = async (prompt) => {
3022
3990
  return false;
3023
3991
  };
3024
- var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
3992
+ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3993
+ logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
3994
+ logger.debug(`[get_user_response] Response mode: ${responseMode}`);
3995
+ logger.debug("[get_user_response] Checking cache for existing response");
3025
3996
  const userResponse = await getUserResponseFromCache(prompt);
3026
3997
  if (userResponse) {
3998
+ logger.info("[get_user_response] User response found in cache - returning cached result");
3027
3999
  logCollector?.info("User response found in cache");
3028
4000
  return {
3029
4001
  success: true,
3030
- data: userResponse
4002
+ data: userResponse,
4003
+ errors: []
3031
4004
  };
3032
4005
  }
4006
+ logger.debug("[get_user_response] No cached response found, proceeding with LLM providers");
3033
4007
  const providers = llmProviders || getLLMProviders();
3034
4008
  const errors = [];
3035
4009
  const providerOrder = providers.join(", ");
3036
4010
  logCollector?.info(`LLM Provider order: [${providerOrder}]`);
3037
4011
  if (conversationHistory && conversationHistory.length > 0) {
3038
- logCollector?.info(`Using conversation history with ${conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length} previous exchanges`);
4012
+ const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
4013
+ logger.debug(`[get_user_response] Using conversation history with ${exchangeCount} previous exchanges`);
4014
+ logCollector?.info(`Using conversation history with ${exchangeCount} previous exchanges`);
4015
+ } else {
4016
+ logger.debug("[get_user_response] No conversation history available");
3039
4017
  }
3040
4018
  for (let i = 0; i < providers.length; i++) {
3041
4019
  const provider = providers[i];
3042
4020
  const isLastProvider = i === providers.length - 1;
3043
- try {
3044
- const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
3045
- logCollector?.info(attemptMsg);
3046
- let result;
3047
- if (provider === "anthropic") {
3048
- result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory);
3049
- } else if (provider === "groq") {
3050
- result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory);
3051
- } else {
3052
- continue;
3053
- }
3054
- if (result.success) {
3055
- const successMsg = `Success with provider: ${provider}`;
3056
- logCollector?.info(successMsg);
3057
- return result;
3058
- } else {
3059
- errors.push({ provider, error: result.reason || "Unknown error" });
3060
- const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.reason}`;
3061
- logCollector?.warn(warnMsg);
3062
- }
3063
- } catch (error) {
3064
- const errorMessage = error.message;
3065
- errors.push({ provider, error: errorMessage });
3066
- const errorMsg = `Provider ${provider} failed: ${errorMessage}`;
3067
- logCollector?.error(errorMsg);
4021
+ const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
4022
+ logCollector?.info(attemptMsg);
4023
+ let result;
4024
+ if (provider === "anthropic") {
4025
+ result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
4026
+ } else if (provider === "groq") {
4027
+ result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
4028
+ } else {
4029
+ logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
4030
+ errors.push(`Unknown provider: ${provider}`);
4031
+ continue;
4032
+ }
4033
+ if (result.success) {
4034
+ const successMsg = `Success with provider: ${provider}`;
4035
+ logger.info(`${successMsg}`);
4036
+ logCollector?.info(successMsg);
4037
+ return result;
4038
+ } else {
4039
+ const providerErrors = result.errors.map((err) => `${provider}: ${err}`);
4040
+ errors.push(...providerErrors);
4041
+ const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.errors.join(", ")}`;
4042
+ logger.warn(`[get_user_response] ${warnMsg}`);
4043
+ logCollector?.warn(warnMsg);
3068
4044
  if (!isLastProvider) {
3069
4045
  const fallbackMsg = "Falling back to next provider...";
4046
+ logger.info(`[get_user_response] ${fallbackMsg}`);
3070
4047
  logCollector?.info(fallbackMsg);
3071
- continue;
3072
4048
  }
3073
4049
  }
3074
4050
  }
3075
- const errorSummary = errors.map((e) => `${e.provider}: ${e.error}`).join("; ");
3076
- const failureMsg = `All LLM providers failed. Errors: ${errorSummary}`;
3077
- logCollector?.error(failureMsg);
4051
+ const failureMsg = `All LLM providers failed`;
4052
+ logger.error(`[get_user_response] ${failureMsg}. Errors: ${errors.join("; ")}`);
4053
+ logCollector?.error(`${failureMsg}. Errors: ${errors.join("; ")}`);
3078
4054
  return {
3079
4055
  success: false,
3080
- reason: failureMsg
4056
+ errors
3081
4057
  };
3082
4058
  };
3083
4059
 
3084
4060
  // src/utils/log-collector.ts
4061
+ var LOG_LEVEL_PRIORITY2 = {
4062
+ errors: 0,
4063
+ warnings: 1,
4064
+ info: 2,
4065
+ verbose: 3
4066
+ };
4067
+ var MESSAGE_LEVEL_PRIORITY2 = {
4068
+ error: 0,
4069
+ warn: 1,
4070
+ info: 2,
4071
+ debug: 3
4072
+ };
3085
4073
  var UILogCollector = class {
3086
4074
  constructor(clientId, sendMessage, uiBlockId) {
3087
4075
  this.logs = [];
3088
4076
  this.uiBlockId = uiBlockId || null;
3089
4077
  this.clientId = clientId;
3090
4078
  this.sendMessage = sendMessage;
4079
+ this.currentLogLevel = logger.getLogLevel();
3091
4080
  }
3092
4081
  /**
3093
4082
  * Check if logging is enabled (uiBlockId is provided)
@@ -3095,10 +4084,22 @@ var UILogCollector = class {
3095
4084
  isEnabled() {
3096
4085
  return this.uiBlockId !== null;
3097
4086
  }
4087
+ /**
4088
+ * Check if a message should be logged based on current log level
4089
+ */
4090
+ shouldLog(messageLevel) {
4091
+ const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
4092
+ const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
4093
+ return messagePriority <= currentLevelPriority;
4094
+ }
3098
4095
  /**
3099
4096
  * Add a log entry with timestamp and immediately send to runtime
4097
+ * Only logs that pass the log level filter are captured and sent
3100
4098
  */
3101
4099
  addLog(level, message, type, data) {
4100
+ if (!this.shouldLog(level)) {
4101
+ return;
4102
+ }
3102
4103
  const log = {
3103
4104
  timestamp: Date.now(),
3104
4105
  level,
@@ -3108,7 +4109,20 @@ var UILogCollector = class {
3108
4109
  };
3109
4110
  this.logs.push(log);
3110
4111
  this.sendLogImmediately(log);
3111
- console.log("UILogCollector:", log);
4112
+ switch (level) {
4113
+ case "error":
4114
+ logger.error("UILogCollector:", log);
4115
+ break;
4116
+ case "warn":
4117
+ logger.warn("UILogCollector:", log);
4118
+ break;
4119
+ case "info":
4120
+ logger.info("UILogCollector:", log);
4121
+ break;
4122
+ case "debug":
4123
+ logger.debug("UILogCollector:", log);
4124
+ break;
4125
+ }
3112
4126
  }
3113
4127
  /**
3114
4128
  * Send a single log to runtime immediately
@@ -3238,103 +4252,153 @@ var CONTEXT_CONFIG = {
3238
4252
  };
3239
4253
 
3240
4254
  // src/handlers/user-prompt-request.ts
3241
- var processedMessageIds = /* @__PURE__ */ new Set();
3242
- async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3243
- try {
3244
- const userPromptRequest = UserPromptRequestMessageSchema.parse(data);
3245
- const { id, payload } = userPromptRequest;
3246
- const prompt = payload.prompt;
3247
- const SA_RUNTIME = payload.SA_RUNTIME;
3248
- const wsId = userPromptRequest.from.id || "unknown";
3249
- logger.info(`[REQUEST ${id}] Processing user prompt: "${prompt.substring(0, 50)}..."`);
3250
- if (processedMessageIds.has(id)) {
3251
- logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3252
- return;
3253
- }
3254
- processedMessageIds.add(id);
3255
- if (processedMessageIds.size > 100) {
3256
- const firstId = processedMessageIds.values().next().value;
3257
- if (firstId) {
3258
- processedMessageIds.delete(firstId);
4255
+ var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) => {
4256
+ const errors = [];
4257
+ logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
4258
+ const parseResult = UserPromptRequestMessageSchema.safeParse(data);
4259
+ if (!parseResult.success) {
4260
+ const zodError = parseResult.error;
4261
+ zodError.errors.forEach((err) => {
4262
+ errors.push(`${err.path.join(".")}: ${err.message}`);
4263
+ });
4264
+ return { success: false, errors };
4265
+ }
4266
+ const userPromptRequest = parseResult.data;
4267
+ const { id, payload } = userPromptRequest;
4268
+ const prompt = payload.prompt;
4269
+ const SA_RUNTIME = payload.SA_RUNTIME;
4270
+ const wsId = userPromptRequest.from.id || "unknown";
4271
+ if (!SA_RUNTIME) {
4272
+ errors.push("SA_RUNTIME is required");
4273
+ }
4274
+ const threadId = SA_RUNTIME?.threadId;
4275
+ const existingUiBlockId = SA_RUNTIME?.uiBlockId;
4276
+ if (!threadId) {
4277
+ errors.push("threadId in SA_RUNTIME is required");
4278
+ }
4279
+ if (!existingUiBlockId) {
4280
+ errors.push("uiBlockId in SA_RUNTIME is required");
4281
+ }
4282
+ if (!prompt) {
4283
+ errors.push("Prompt not found");
4284
+ }
4285
+ logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
4286
+ if (errors.length > 0) {
4287
+ return { success: false, errors, id, wsId };
4288
+ }
4289
+ const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
4290
+ const threadManager = ThreadManager.getInstance();
4291
+ let thread = threadManager.getThread(threadId);
4292
+ if (!thread) {
4293
+ thread = threadManager.createThread(threadId);
4294
+ logger.info(`Created new thread: ${threadId}`);
4295
+ }
4296
+ logCollector.info(`Starting user prompt request with ${components.length} components`);
4297
+ const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
4298
+ logger.info("conversationHistory", conversationHistory);
4299
+ const responseMode = payload.responseMode || "component";
4300
+ logger.info("responseMode", responseMode);
4301
+ let streamCallback;
4302
+ let accumulatedStreamResponse = "";
4303
+ if (responseMode === "text") {
4304
+ streamCallback = (chunk) => {
4305
+ accumulatedStreamResponse += chunk;
4306
+ logger.debug(`[STREAM] Sending chunk (${chunk.length} chars): "${chunk.substring(0, 20)}..."`);
4307
+ const streamMessage = {
4308
+ id: `stream_${existingUiBlockId}`,
4309
+ // Different ID pattern for streaming
4310
+ type: "USER_PROMPT_STREAM",
4311
+ from: { type: "data-agent" },
4312
+ to: {
4313
+ type: "runtime",
4314
+ id: wsId
4315
+ },
4316
+ payload: {
4317
+ uiBlockId: existingUiBlockId,
4318
+ chunk
4319
+ }
4320
+ };
4321
+ sendMessage(streamMessage);
4322
+ logger.debug(`[STREAM] Chunk sent to wsId: ${wsId}`);
4323
+ };
4324
+ }
4325
+ const userResponse = await get_user_response(
4326
+ prompt,
4327
+ components,
4328
+ anthropicApiKey,
4329
+ groqApiKey,
4330
+ llmProviders,
4331
+ logCollector,
4332
+ conversationHistory,
4333
+ responseMode,
4334
+ streamCallback,
4335
+ collections
4336
+ );
4337
+ logCollector.info("User prompt request completed");
4338
+ const uiBlockId = existingUiBlockId;
4339
+ if (!userResponse.success) {
4340
+ logger.error(`User prompt request failed with errors: ${userResponse.errors.join(", ")}`);
4341
+ return {
4342
+ success: false,
4343
+ data: userResponse.data,
4344
+ errors: userResponse.errors,
4345
+ uiBlockId,
4346
+ threadId,
4347
+ id,
4348
+ wsId
4349
+ };
4350
+ }
4351
+ let component = null;
4352
+ let textResponse = null;
4353
+ if (userResponse.data) {
4354
+ if (typeof userResponse.data === "object") {
4355
+ if ("component" in userResponse.data) {
4356
+ component = userResponse.data.component;
4357
+ }
4358
+ if ("textResponse" in userResponse.data) {
4359
+ textResponse = userResponse.data.text;
3259
4360
  }
3260
4361
  }
3261
- if (!SA_RUNTIME) {
3262
- sendDataResponse4(id, {
3263
- success: false,
3264
- error: "SA_RUNTIME is required"
3265
- }, sendMessage, wsId);
3266
- return;
3267
- }
3268
- const threadId = SA_RUNTIME.threadId;
3269
- const existingUiBlockId = SA_RUNTIME.uiBlockId;
3270
- if (!threadId) {
3271
- sendDataResponse4(id, {
3272
- success: false,
3273
- error: "threadId in SA_RUNTIME is required"
3274
- }, sendMessage, wsId);
3275
- return;
3276
- }
3277
- if (!existingUiBlockId) {
3278
- sendDataResponse4(id, {
3279
- success: false,
3280
- error: "uiBlockId in SA_RUNTIME is required"
3281
- }, sendMessage, wsId);
3282
- return;
3283
- }
3284
- const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
3285
- if (!prompt) {
3286
- sendDataResponse4(id, {
3287
- success: false,
3288
- error: "Prompt not found"
3289
- }, sendMessage, wsId);
3290
- return;
3291
- }
3292
- if (!components || components.length === 0) {
3293
- sendDataResponse4(id, {
3294
- success: false,
3295
- error: "Components not found"
3296
- }, sendMessage, wsId);
3297
- return;
3298
- }
3299
- const threadManager = ThreadManager.getInstance();
3300
- let thread = threadManager.getThread(threadId);
3301
- if (!thread) {
3302
- thread = threadManager.createThread(threadId);
3303
- logger.info(`Created new thread: ${threadId}`);
3304
- }
3305
- logCollector.info(`Starting user prompt request with ${components.length} components`);
3306
- const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3307
- logger.info("conversationHistory", conversationHistory);
3308
- const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3309
- logger.info("llm userResponse", userResponse);
3310
- logCollector.info("User prompt request completed");
3311
- if (userResponse.success && userResponse.data && typeof userResponse.data === "object" && "component" in userResponse.data) {
3312
- const component = userResponse.data.component;
3313
- const uiBlockId = existingUiBlockId;
3314
- const uiBlock = new UIBlock(
3315
- prompt,
3316
- {},
3317
- // componentData: initially empty, will be filled later
3318
- component || {},
3319
- // generatedComponentMetadata: full component object (ComponentSchema)
3320
- [],
3321
- // actions: empty initially
3322
- uiBlockId
3323
- );
3324
- thread.addUIBlock(uiBlock);
3325
- logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
3326
- sendDataResponse4(id, {
3327
- ...userResponse,
3328
- uiBlockId,
3329
- threadId
3330
- }, sendMessage, wsId);
3331
- } else {
3332
- sendDataResponse4(id, userResponse, sendMessage, wsId);
3333
- }
3334
- return;
3335
- } catch (error) {
3336
- logger.error("Failed to handle user prompt request:", error);
3337
4362
  }
4363
+ const finalTextResponse = responseMode === "text" && accumulatedStreamResponse ? accumulatedStreamResponse : textResponse;
4364
+ const uiBlock = new UIBlock(
4365
+ prompt,
4366
+ {},
4367
+ // componentData: initially empty, will be filled later
4368
+ component,
4369
+ // generatedComponentMetadata: full component object (ComponentSchema)
4370
+ [],
4371
+ // actions: empty initially
4372
+ uiBlockId,
4373
+ finalTextResponse
4374
+ // textResponse: FULL streaming response including all intermediate messages
4375
+ );
4376
+ thread.addUIBlock(uiBlock);
4377
+ logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
4378
+ return {
4379
+ success: userResponse.success,
4380
+ data: userResponse.data,
4381
+ errors: userResponse.errors,
4382
+ uiBlockId,
4383
+ threadId,
4384
+ id,
4385
+ wsId
4386
+ };
4387
+ };
4388
+ async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) {
4389
+ const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections);
4390
+ sendDataResponse4(
4391
+ response.id || data.id,
4392
+ {
4393
+ success: response.success,
4394
+ errors: response.errors,
4395
+ data: response.data,
4396
+ uiBlockId: response.uiBlockId,
4397
+ threadId: response.threadId
4398
+ },
4399
+ sendMessage,
4400
+ response.wsId || data.from?.id
4401
+ );
3338
4402
  }
3339
4403
  function sendDataResponse4(id, res, sendMessage, clientId) {
3340
4404
  const response = {
@@ -3447,12 +4511,27 @@ function sendResponse(id, res, sendMessage, clientId) {
3447
4511
  // src/userResponse/next-questions.ts
3448
4512
  async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) {
3449
4513
  try {
4514
+ logger.debug("[generateNextQuestions] Starting next questions generation");
4515
+ logger.debug(`[generateNextQuestions] User prompt: "${originalUserPrompt?.substring(0, 50)}..."`);
4516
+ logger.debug(`[generateNextQuestions] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
4517
+ logger.debug(`[generateNextQuestions] Component data available: ${componentData ? "yes" : "no"}`);
3450
4518
  const providers = llmProviders || ["anthropic"];
3451
- for (const provider of providers) {
4519
+ logger.info(`[generateNextQuestions] Using LLM providers: [${providers.join(", ")}]`);
4520
+ if (conversationHistory && conversationHistory.length > 0) {
4521
+ const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
4522
+ logger.debug(`[generateNextQuestions] Using conversation history with ${exchangeCount} previous exchanges`);
4523
+ } else {
4524
+ logger.debug("[generateNextQuestions] No conversation history available");
4525
+ }
4526
+ for (let i = 0; i < providers.length; i++) {
4527
+ const provider = providers[i];
4528
+ const isLastProvider = i === providers.length - 1;
3452
4529
  try {
3453
- logger.info(`Generating next questions using provider: ${provider}`);
4530
+ logger.info(`[generateNextQuestions] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
4531
+ logCollector?.info(`Generating questions with ${provider}...`);
3454
4532
  let result = [];
3455
4533
  if (provider === "groq") {
4534
+ logger.debug("[generateNextQuestions] Using Groq LLM for next questions");
3456
4535
  result = await groqLLM.generateNextQuestions(
3457
4536
  originalUserPrompt,
3458
4537
  component,
@@ -3462,6 +4541,7 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3462
4541
  conversationHistory
3463
4542
  );
3464
4543
  } else {
4544
+ logger.debug("[generateNextQuestions] Using Anthropic LLM for next questions");
3465
4545
  result = await anthropicLLM.generateNextQuestions(
3466
4546
  originalUserPrompt,
3467
4547
  component,
@@ -3472,21 +4552,39 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3472
4552
  );
3473
4553
  }
3474
4554
  if (result && result.length > 0) {
3475
- logger.info(`Successfully generated ${result.length} questions with ${provider}`);
4555
+ logger.info(`[generateNextQuestions] Successfully generated ${result.length} questions with ${provider}`);
4556
+ logger.debug(`[generateNextQuestions] Questions: ${JSON.stringify(result)}`);
4557
+ logCollector?.info(`Generated ${result.length} follow-up questions`);
3476
4558
  return result;
3477
4559
  }
3478
- logger.warn(`No questions generated from ${provider}, trying next provider...`);
4560
+ const warnMsg = `No questions generated from ${provider}${!isLastProvider ? ", trying next provider..." : ""}`;
4561
+ logger.warn(`[generateNextQuestions] ${warnMsg}`);
4562
+ if (!isLastProvider) {
4563
+ logCollector?.warn(warnMsg);
4564
+ }
3479
4565
  } catch (providerError) {
3480
- logger.warn(`Provider ${provider} failed:`, providerError);
3481
- logCollector?.warn(`Provider ${provider} failed, trying next provider...`);
4566
+ const errorMsg = providerError instanceof Error ? providerError.message : String(providerError);
4567
+ logger.error(`[generateNextQuestions] Provider ${provider} failed: ${errorMsg}`);
4568
+ logger.debug(`[generateNextQuestions] Provider error details:`, providerError);
4569
+ if (!isLastProvider) {
4570
+ const fallbackMsg = `Provider ${provider} failed, trying next provider...`;
4571
+ logger.info(`[generateNextQuestions] ${fallbackMsg}`);
4572
+ logCollector?.warn(fallbackMsg);
4573
+ } else {
4574
+ logCollector?.error(`Failed to generate questions with ${provider}`);
4575
+ }
3482
4576
  continue;
3483
4577
  }
3484
4578
  }
3485
- logger.warn("All providers failed or returned no questions");
4579
+ logger.warn("[generateNextQuestions] All providers failed or returned no questions");
4580
+ logCollector?.warn("Unable to generate follow-up questions");
3486
4581
  return [];
3487
4582
  } catch (error) {
3488
- logger.error("Error generating next questions:", error);
3489
- logCollector?.error(`Error generating next questions: ${error.message}`);
4583
+ const errorMsg = error instanceof Error ? error.message : String(error);
4584
+ const errorStack = error instanceof Error ? error.stack : void 0;
4585
+ logger.error(`[generateNextQuestions] Error generating next questions: ${errorMsg}`);
4586
+ logger.debug("[generateNextQuestions] Error stack trace:", errorStack);
4587
+ logCollector?.error(`Error generating next questions: ${errorMsg}`);
3490
4588
  return [];
3491
4589
  }
3492
4590
  }
@@ -3494,11 +4592,15 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3494
4592
  // src/handlers/actions-request.ts
3495
4593
  async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3496
4594
  try {
4595
+ logger.debug("[ACTIONS_REQ] Parsing incoming actions request");
3497
4596
  const actionsRequest = ActionsRequestMessageSchema.parse(data);
3498
4597
  const { id, payload } = actionsRequest;
3499
4598
  const { SA_RUNTIME } = payload;
3500
4599
  const wsId = actionsRequest.from.id || "unknown";
4600
+ logger.info(`[ACTIONS_REQ ${id}] Processing actions request from client: ${wsId}`);
4601
+ logger.debug(`[ACTIONS_REQ ${id}] Request payload:`, JSON.stringify(payload, null, 2).substring(0, 200));
3501
4602
  if (!SA_RUNTIME) {
4603
+ logger.error(`[ACTIONS_REQ ${id}] SA_RUNTIME missing from request`);
3502
4604
  sendResponse2(id, {
3503
4605
  success: false,
3504
4606
  error: "SA_RUNTIME with threadId and uiBlockId is required"
@@ -3507,31 +4609,54 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3507
4609
  }
3508
4610
  const uiBlockId = SA_RUNTIME.uiBlockId;
3509
4611
  const threadId = SA_RUNTIME.threadId;
4612
+ logger.debug(`[ACTIONS_REQ ${id}] SA_RUNTIME validated - threadId: ${threadId}, uiBlockId: ${uiBlockId}`);
4613
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving thread: ${threadId}`);
3510
4614
  const threadManager = ThreadManager.getInstance();
3511
4615
  const thread = threadManager.getThread(threadId);
3512
4616
  if (!thread) {
4617
+ logger.error(`[ACTIONS_REQ ${id}] Thread '${threadId}' not found`);
3513
4618
  sendResponse2(id, {
3514
4619
  success: false,
3515
4620
  error: `Thread '${threadId}' not found`
3516
4621
  }, sendMessage, wsId);
3517
4622
  return;
3518
4623
  }
4624
+ logger.debug(`[ACTIONS_REQ ${id}] Thread found with ${thread.getUIBlocks().length} UIBlocks`);
4625
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving UIBlock: ${uiBlockId}`);
3519
4626
  const uiBlock = thread.getUIBlock(uiBlockId);
3520
4627
  if (!uiBlock) {
4628
+ logger.error(`[ACTIONS_REQ ${id}] UIBlock '${uiBlockId}' not found in thread '${threadId}'`);
3521
4629
  sendResponse2(id, {
3522
4630
  success: false,
3523
4631
  error: `UIBlock '${uiBlockId}' not found in thread '${threadId}'`
3524
4632
  }, sendMessage, wsId);
3525
4633
  return;
3526
4634
  }
4635
+ logger.info(`[ACTIONS_REQ ${id}] UIBlock retrieved successfully`);
4636
+ logger.debug(`[ACTIONS_REQ ${id}] Creating UILogCollector for uiBlockId: ${uiBlockId}`);
3527
4637
  const logCollector = new UILogCollector(wsId, sendMessage, uiBlockId);
4638
+ logger.info(`[ACTIONS_REQ ${id}] UILogCollector initialized`);
4639
+ logger.debug(`[ACTIONS_REQ ${id}] Extracting data from UIBlock`);
3528
4640
  const userQuestion = uiBlock.getUserQuestion();
3529
4641
  const component = uiBlock.getComponentMetadata();
3530
4642
  const componentData = uiBlock.getComponentData();
4643
+ logger.debug(`[ACTIONS_REQ ${id}] User question: "${userQuestion?.substring(0, 50)}..."`);
4644
+ logger.debug(`[ACTIONS_REQ ${id}] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
4645
+ logger.debug(`[ACTIONS_REQ ${id}] Component data available: ${componentData ? "yes" : "no"}`);
4646
+ logger.debug(`[ACTIONS_REQ ${id}] Extracting conversation history (max ${CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS} blocks)`);
3531
4647
  const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, uiBlockId);
4648
+ const historyLineCount = conversationHistory.split("\n").filter((l) => l.trim()).length;
4649
+ logger.info(`[ACTIONS_REQ ${id}] Conversation history extracted: ${historyLineCount} lines`);
4650
+ logger.debug(`[ACTIONS_REQ ${id}] Conversation history preview:
4651
+ ${conversationHistory.substring(0, 200)}...`);
3532
4652
  logCollector.info(`Generating actions for UIBlock: ${uiBlockId}`);
3533
- logger.info(`Generating actions for component: ${component?.name || "unknown"}`);
4653
+ logger.info(`[ACTIONS_REQ ${id}] Generating actions for component: ${component?.name || "unknown"}`);
4654
+ logger.debug(`[ACTIONS_REQ ${id}] Checking if actions are already cached`);
4655
+ const startTime = Date.now();
3534
4656
  const actions = await uiBlock.getOrFetchActions(async () => {
4657
+ logger.info(`[ACTIONS_REQ ${id}] Actions not cached, generating new actions...`);
4658
+ logCollector.info("Generating follow-up questions...");
4659
+ logger.info(`[ACTIONS_REQ ${id}] Starting next questions generation with ${llmProviders?.join(", ") || "default"} providers`);
3535
4660
  const nextQuestions = await generateNextQuestions(
3536
4661
  userQuestion,
3537
4662
  component,
@@ -3542,14 +4667,28 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3542
4667
  logCollector,
3543
4668
  conversationHistory
3544
4669
  );
3545
- return nextQuestions.map((question, index) => ({
4670
+ logger.info(`[ACTIONS_REQ ${id}] Generated ${nextQuestions.length} questions`);
4671
+ logger.debug(`[ACTIONS_REQ ${id}] Questions: ${JSON.stringify(nextQuestions)}`);
4672
+ logger.debug(`[ACTIONS_REQ ${id}] Converting questions to actions format`);
4673
+ const convertedActions = nextQuestions.map((question, index) => ({
3546
4674
  id: `action_${index}_${Date.now()}`,
3547
4675
  name: question,
3548
4676
  type: "next_question",
3549
4677
  question
3550
4678
  }));
4679
+ logger.debug(`[ACTIONS_REQ ${id}] Converted ${convertedActions.length} actions`);
4680
+ return convertedActions;
3551
4681
  });
3552
- logCollector.info(`Generated ${actions.length} actions successfully`);
4682
+ const processingTime = Date.now() - startTime;
4683
+ logger.info(`[ACTIONS_REQ ${id}] Actions retrieved in ${processingTime}ms - ${actions.length} actions total`);
4684
+ if (actions.length > 0) {
4685
+ logCollector.info(`Generated ${actions.length} follow-up questions successfully`);
4686
+ logger.debug(`[ACTIONS_REQ ${id}] Actions: ${actions.map((a) => a.name).join(", ")}`);
4687
+ } else {
4688
+ logger.warn(`[ACTIONS_REQ ${id}] No actions generated`);
4689
+ logCollector.warn("No follow-up questions could be generated");
4690
+ }
4691
+ logger.debug(`[ACTIONS_REQ ${id}] Sending successful response to client`);
3553
4692
  sendResponse2(id, {
3554
4693
  success: true,
3555
4694
  data: {
@@ -3560,12 +4699,26 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3560
4699
  threadId
3561
4700
  }
3562
4701
  }, sendMessage, wsId);
4702
+ logger.info(`[ACTIONS_REQ ${id}] \u2713 Actions request completed successfully`);
3563
4703
  } catch (error) {
3564
- logger.error("Failed to handle actions request:", error);
4704
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
4705
+ const errorStack = error instanceof Error ? error.stack : void 0;
4706
+ logger.error(`[ACTIONS_REQ] Failed to handle actions request: ${errorMessage}`);
4707
+ logger.debug(`[ACTIONS_REQ] Error stack trace:`, errorStack);
4708
+ try {
4709
+ const parsedData = data;
4710
+ if (parsedData?.id && parsedData?.from?.id) {
4711
+ const logCollector = parsedData?.payload?.SA_RUNTIME?.uiBlockId ? new UILogCollector(parsedData.from.id, sendMessage, parsedData.payload.SA_RUNTIME.uiBlockId) : void 0;
4712
+ logCollector?.error(`Failed to generate actions: ${errorMessage}`);
4713
+ }
4714
+ } catch (logError) {
4715
+ logger.debug("[ACTIONS_REQ] Failed to send error logs to UI:", logError);
4716
+ }
3565
4717
  sendResponse2(null, {
3566
4718
  success: false,
3567
- error: error instanceof Error ? error.message : "Unknown error occurred"
4719
+ error: errorMessage
3568
4720
  }, sendMessage);
4721
+ logger.info("[ACTIONS_REQ] \u2717 Actions request completed with errors");
3569
4722
  }
3570
4723
  }
3571
4724
  function sendResponse2(id, res, sendMessage, clientId) {
@@ -3581,6 +4734,11 @@ function sendResponse2(id, res, sendMessage, clientId) {
3581
4734
  ...res
3582
4735
  }
3583
4736
  };
4737
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.success ? "successful" : "failed"} response to client`);
4738
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Response payload size: ${JSON.stringify(response).length} bytes`);
4739
+ if (res.data?.actions) {
4740
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.data.actions.length} actions`);
4741
+ }
3584
4742
  sendMessage(response);
3585
4743
  }
3586
4744
 
@@ -3609,7 +4767,10 @@ async function handleUsersRequest(data, sendMessage) {
3609
4767
  const { id, payload, from } = request;
3610
4768
  const { operation, data: requestData } = payload;
3611
4769
  const username = requestData?.username;
4770
+ const email = requestData?.email;
3612
4771
  const password = requestData?.password;
4772
+ const fullname = requestData?.fullname;
4773
+ const role = requestData?.role;
3613
4774
  if (from.type !== "admin") {
3614
4775
  sendResponse3(id, {
3615
4776
  success: false,
@@ -3621,10 +4782,10 @@ async function handleUsersRequest(data, sendMessage) {
3621
4782
  const userManager = getUserManager();
3622
4783
  switch (operation) {
3623
4784
  case "create":
3624
- await handleCreate(id, username, password, userManager, sendMessage, from.id);
4785
+ await handleCreate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3625
4786
  break;
3626
4787
  case "update":
3627
- await handleUpdate(id, username, password, userManager, sendMessage, from.id);
4788
+ await handleUpdate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3628
4789
  break;
3629
4790
  case "delete":
3630
4791
  await handleDelete(id, username, userManager, sendMessage, from.id);
@@ -3649,7 +4810,8 @@ async function handleUsersRequest(data, sendMessage) {
3649
4810
  }, sendMessage);
3650
4811
  }
3651
4812
  }
3652
- async function handleCreate(id, username, password, userManager, sendMessage, clientId) {
4813
+ async function handleCreate(id, userData, userManager, sendMessage, clientId) {
4814
+ const { username, email, password, fullname, role } = userData;
3653
4815
  if (!username || username.trim().length === 0) {
3654
4816
  sendResponse3(id, {
3655
4817
  success: false,
@@ -3664,6 +4826,16 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3664
4826
  }, sendMessage, clientId);
3665
4827
  return;
3666
4828
  }
4829
+ if (email && email.trim().length > 0) {
4830
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4831
+ if (!emailRegex.test(email)) {
4832
+ sendResponse3(id, {
4833
+ success: false,
4834
+ error: "Invalid email format"
4835
+ }, sendMessage, clientId);
4836
+ return;
4837
+ }
4838
+ }
3667
4839
  if (userManager.userExists(username)) {
3668
4840
  sendResponse3(id, {
3669
4841
  success: false,
@@ -3671,25 +4843,41 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3671
4843
  }, sendMessage, clientId);
3672
4844
  return;
3673
4845
  }
3674
- let wsIds = [];
3675
- if (clientId) {
3676
- wsIds.push(clientId);
4846
+ if (email && userManager.getUserByEmail(email)) {
4847
+ sendResponse3(id, {
4848
+ success: false,
4849
+ error: `User with email '${email}' already exists`
4850
+ }, sendMessage, clientId);
4851
+ return;
3677
4852
  }
3678
- const newUser = userManager.createUser({
4853
+ const newUserData = {
3679
4854
  username,
3680
- password,
3681
- wsIds
3682
- });
3683
- logger.info(`User created by admin: ${username}`);
4855
+ password
4856
+ };
4857
+ if (email && email.trim().length > 0) {
4858
+ newUserData.email = email.trim();
4859
+ }
4860
+ if (fullname && fullname.trim().length > 0) {
4861
+ newUserData.fullname = fullname.trim();
4862
+ }
4863
+ if (role && role.trim().length > 0) {
4864
+ newUserData.role = role.trim();
4865
+ }
4866
+ const newUser = userManager.createUser(newUserData);
4867
+ logger.info(`User created by admin: ${username}${email ? ` (${email})` : ""}`);
3684
4868
  sendResponse3(id, {
3685
4869
  success: true,
3686
4870
  data: {
3687
4871
  username: newUser.username,
4872
+ email: newUser.email,
4873
+ fullname: newUser.fullname,
4874
+ role: newUser.role,
3688
4875
  message: `User '${username}' created successfully`
3689
4876
  }
3690
4877
  }, sendMessage, clientId);
3691
4878
  }
3692
- async function handleUpdate(id, username, password, userManager, sendMessage, clientId) {
4879
+ async function handleUpdate(id, userData, userManager, sendMessage, clientId) {
4880
+ const { username, email, password, fullname, role } = userData;
3693
4881
  if (!username || username.trim().length === 0) {
3694
4882
  sendResponse3(id, {
3695
4883
  success: false,
@@ -3705,13 +4893,42 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3705
4893
  return;
3706
4894
  }
3707
4895
  const updates = {};
3708
- if (password && password.trim().length > 0) {
3709
- updates.password = password;
4896
+ if (email !== void 0) {
4897
+ if (email.trim().length > 0) {
4898
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4899
+ if (!emailRegex.test(email)) {
4900
+ sendResponse3(id, {
4901
+ success: false,
4902
+ error: "Invalid email format"
4903
+ }, sendMessage, clientId);
4904
+ return;
4905
+ }
4906
+ const existingUser = userManager.getUserByEmail(email);
4907
+ if (existingUser && existingUser.username !== username) {
4908
+ sendResponse3(id, {
4909
+ success: false,
4910
+ error: `Email '${email}' is already used by another user`
4911
+ }, sendMessage, clientId);
4912
+ return;
4913
+ }
4914
+ updates.email = email.trim();
4915
+ } else {
4916
+ updates.email = void 0;
4917
+ }
4918
+ }
4919
+ if (password !== void 0 && password.trim().length > 0) {
4920
+ updates.password = password.trim();
4921
+ }
4922
+ if (fullname !== void 0) {
4923
+ updates.fullname = fullname.trim().length > 0 ? fullname.trim() : void 0;
4924
+ }
4925
+ if (role !== void 0) {
4926
+ updates.role = role.trim().length > 0 ? role.trim() : void 0;
3710
4927
  }
3711
4928
  if (Object.keys(updates).length === 0) {
3712
4929
  sendResponse3(id, {
3713
4930
  success: false,
3714
- error: "No fields to update. Please provide password or other valid fields."
4931
+ error: "No fields to update. Please provide at least one field to update."
3715
4932
  }, sendMessage, clientId);
3716
4933
  return;
3717
4934
  }
@@ -3721,6 +4938,9 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3721
4938
  success: true,
3722
4939
  data: {
3723
4940
  username: updatedUser.username,
4941
+ email: updatedUser.email,
4942
+ fullname: updatedUser.fullname,
4943
+ role: updatedUser.role,
3724
4944
  message: `User '${username}' updated successfully`
3725
4945
  }
3726
4946
  }, sendMessage, clientId);
@@ -3761,6 +4981,9 @@ async function handleGetAll(id, userManager, sendMessage, clientId) {
3761
4981
  const users = userManager.getAllUsers();
3762
4982
  const sanitizedUsers = users.map((user) => ({
3763
4983
  username: user.username,
4984
+ email: user.email,
4985
+ fullname: user.fullname,
4986
+ role: user.role,
3764
4987
  wsIds: user.wsIds || []
3765
4988
  }));
3766
4989
  logger.info(`Admin retrieved all users (count: ${sanitizedUsers.length})`);
@@ -3791,6 +5014,9 @@ async function handleGetOne(id, username, userManager, sendMessage, clientId) {
3791
5014
  const user = userManager.getUser(username);
3792
5015
  const sanitizedUser = {
3793
5016
  username: user.username,
5017
+ email: user.email,
5018
+ fullname: user.fullname,
5019
+ role: user.role,
3794
5020
  wsIds: user.wsIds || []
3795
5021
  };
3796
5022
  logger.info(`Admin retrieved user: ${username}`);
@@ -4236,7 +5462,7 @@ function sendResponse5(id, res, sendMessage, clientId) {
4236
5462
  }
4237
5463
 
4238
5464
  // src/auth/user-manager.ts
4239
- var import_fs3 = __toESM(require("fs"));
5465
+ var import_fs4 = __toESM(require("fs"));
4240
5466
  var import_path3 = __toESM(require("path"));
4241
5467
  var import_os = __toESM(require("os"));
4242
5468
  var UserManager = class {
@@ -4276,21 +5502,22 @@ var UserManager = class {
4276
5502
  async loadUsersFromFile() {
4277
5503
  try {
4278
5504
  const dir = import_path3.default.dirname(this.filePath);
4279
- if (!import_fs3.default.existsSync(dir)) {
5505
+ if (!import_fs4.default.existsSync(dir)) {
4280
5506
  logger.info(`Creating directory structure: ${dir}`);
4281
- import_fs3.default.mkdirSync(dir, { recursive: true });
5507
+ import_fs4.default.mkdirSync(dir, { recursive: true });
4282
5508
  }
4283
- if (!import_fs3.default.existsSync(this.filePath)) {
5509
+ if (!import_fs4.default.existsSync(this.filePath)) {
4284
5510
  logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
4285
5511
  const initialData = { users: [] };
4286
- import_fs3.default.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
5512
+ import_fs4.default.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
4287
5513
  this.users = [];
4288
5514
  this.hasChanged = false;
4289
5515
  return;
4290
5516
  }
4291
- const fileContent = import_fs3.default.readFileSync(this.filePath, "utf-8");
4292
- const data = JSON.parse(fileContent);
4293
- this.users = Array.isArray(data.users) ? data.users : [];
5517
+ const fileContent = import_fs4.default.readFileSync(this.filePath, "utf-8");
5518
+ const rawData = JSON.parse(fileContent);
5519
+ const validatedData = UsersDataSchema.parse(rawData);
5520
+ this.users = validatedData.users;
4294
5521
  this.hasChanged = false;
4295
5522
  logger.debug(`Loaded ${this.users.length} users from file`);
4296
5523
  } catch (error) {
@@ -4307,13 +5534,17 @@ var UserManager = class {
4307
5534
  }
4308
5535
  try {
4309
5536
  const dir = import_path3.default.dirname(this.filePath);
4310
- if (!import_fs3.default.existsSync(dir)) {
4311
- import_fs3.default.mkdirSync(dir, { recursive: true });
5537
+ if (!import_fs4.default.existsSync(dir)) {
5538
+ import_fs4.default.mkdirSync(dir, { recursive: true });
4312
5539
  }
4313
- const data = { users: this.users };
4314
- import_fs3.default.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
5540
+ const usersToSave = this.users.map((user) => {
5541
+ const { wsIds, ...userWithoutWsIds } = user;
5542
+ return userWithoutWsIds;
5543
+ });
5544
+ const data = { users: usersToSave };
5545
+ import_fs4.default.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
4315
5546
  this.hasChanged = false;
4316
- logger.debug(`Synced ${this.users.length} users to file`);
5547
+ logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
4317
5548
  } catch (error) {
4318
5549
  logger.error("Failed to save users to file:", error);
4319
5550
  throw new Error(`Failed to save users to file: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -4360,13 +5591,17 @@ var UserManager = class {
4360
5591
  * @returns The created user
4361
5592
  */
4362
5593
  createUser(user) {
4363
- if (this.users.some((u) => u.username === user.username)) {
4364
- throw new Error(`User with username ${user.username} already exists`);
5594
+ const validatedUser = UserSchema.parse(user);
5595
+ if (this.users.some((u) => u.username === validatedUser.username)) {
5596
+ throw new Error(`User with username ${validatedUser.username} already exists`);
5597
+ }
5598
+ if (validatedUser.email && this.users.some((u) => u.email === validatedUser.email)) {
5599
+ throw new Error(`User with email ${validatedUser.email} already exists`);
4365
5600
  }
4366
- this.users.push(user);
5601
+ this.users.push(validatedUser);
4367
5602
  this.hasChanged = true;
4368
- logger.debug(`User created: ${user.username}`);
4369
- return user;
5603
+ logger.debug(`User created: ${validatedUser.username}`);
5604
+ return validatedUser;
4370
5605
  }
4371
5606
  /**
4372
5607
  * Read a user by username
@@ -4376,6 +5611,22 @@ var UserManager = class {
4376
5611
  getUser(username) {
4377
5612
  return this.users.find((u) => u.username === username);
4378
5613
  }
5614
+ /**
5615
+ * Read a user by email
5616
+ * @param email - Email to retrieve
5617
+ * @returns The user if found, undefined otherwise
5618
+ */
5619
+ getUserByEmail(email) {
5620
+ return this.users.find((u) => u.email === email);
5621
+ }
5622
+ /**
5623
+ * Find user by username or email
5624
+ * @param identifier - Username or email to search for
5625
+ * @returns The user if found, undefined otherwise
5626
+ */
5627
+ getUserByUsernameOrEmail(identifier) {
5628
+ return this.users.find((u) => u.username === identifier || u.email === identifier);
5629
+ }
4379
5630
  /**
4380
5631
  * Read all users
4381
5632
  * @returns Array of all users
@@ -4464,7 +5715,6 @@ var UserManager = class {
4464
5715
  }
4465
5716
  if (!user.wsIds.includes(wsId)) {
4466
5717
  user.wsIds.push(wsId);
4467
- this.hasChanged = true;
4468
5718
  logger.debug(`WebSocket ID added to user ${username}: ${wsId}`);
4469
5719
  }
4470
5720
  return true;
@@ -4486,7 +5736,6 @@ var UserManager = class {
4486
5736
  const initialLength = user.wsIds.length;
4487
5737
  user.wsIds = user.wsIds.filter((id) => id !== wsId);
4488
5738
  if (user.wsIds.length < initialLength) {
4489
- this.hasChanged = true;
4490
5739
  logger.debug(`WebSocket ID removed from user ${username}: ${wsId}`);
4491
5740
  }
4492
5741
  return true;
@@ -4511,7 +5760,7 @@ var UserManager = class {
4511
5760
  };
4512
5761
 
4513
5762
  // src/dashboards/dashboard-manager.ts
4514
- var import_fs4 = __toESM(require("fs"));
5763
+ var import_fs5 = __toESM(require("fs"));
4515
5764
  var import_path4 = __toESM(require("path"));
4516
5765
  var import_os2 = __toESM(require("os"));
4517
5766
  var DashboardManager = class {
@@ -4546,12 +5795,12 @@ var DashboardManager = class {
4546
5795
  createDashboard(dashboardId, dashboard) {
4547
5796
  const dashboardPath = this.getDashboardPath(dashboardId);
4548
5797
  const dashboardDir = import_path4.default.dirname(dashboardPath);
4549
- if (import_fs4.default.existsSync(dashboardPath)) {
5798
+ if (import_fs5.default.existsSync(dashboardPath)) {
4550
5799
  throw new Error(`Dashboard '${dashboardId}' already exists`);
4551
5800
  }
4552
5801
  const validated = DSLRendererPropsSchema.parse(dashboard);
4553
- import_fs4.default.mkdirSync(dashboardDir, { recursive: true });
4554
- import_fs4.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5802
+ import_fs5.default.mkdirSync(dashboardDir, { recursive: true });
5803
+ import_fs5.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
4555
5804
  logger.info(`Dashboard created: ${dashboardId}`);
4556
5805
  return validated;
4557
5806
  }
@@ -4562,12 +5811,12 @@ var DashboardManager = class {
4562
5811
  */
4563
5812
  getDashboard(dashboardId) {
4564
5813
  const dashboardPath = this.getDashboardPath(dashboardId);
4565
- if (!import_fs4.default.existsSync(dashboardPath)) {
5814
+ if (!import_fs5.default.existsSync(dashboardPath)) {
4566
5815
  logger.warn(`Dashboard not found: ${dashboardId}`);
4567
5816
  return null;
4568
5817
  }
4569
5818
  try {
4570
- const fileContent = import_fs4.default.readFileSync(dashboardPath, "utf-8");
5819
+ const fileContent = import_fs5.default.readFileSync(dashboardPath, "utf-8");
4571
5820
  const dashboard = JSON.parse(fileContent);
4572
5821
  const validated = DSLRendererPropsSchema.parse(dashboard);
4573
5822
  return validated;
@@ -4581,16 +5830,16 @@ var DashboardManager = class {
4581
5830
  * @returns Array of dashboard objects with their IDs
4582
5831
  */
4583
5832
  getAllDashboards() {
4584
- if (!import_fs4.default.existsSync(this.dashboardsBasePath)) {
4585
- import_fs4.default.mkdirSync(this.dashboardsBasePath, { recursive: true });
5833
+ if (!import_fs5.default.existsSync(this.dashboardsBasePath)) {
5834
+ import_fs5.default.mkdirSync(this.dashboardsBasePath, { recursive: true });
4586
5835
  return [];
4587
5836
  }
4588
5837
  const dashboards = [];
4589
5838
  try {
4590
- const dashboardDirs = import_fs4.default.readdirSync(this.dashboardsBasePath);
5839
+ const dashboardDirs = import_fs5.default.readdirSync(this.dashboardsBasePath);
4591
5840
  for (const dashboardId of dashboardDirs) {
4592
5841
  const dashboardPath = this.getDashboardPath(dashboardId);
4593
- if (import_fs4.default.existsSync(dashboardPath)) {
5842
+ if (import_fs5.default.existsSync(dashboardPath)) {
4594
5843
  const dashboard = this.getDashboard(dashboardId);
4595
5844
  if (dashboard) {
4596
5845
  dashboards.push({ dashboardId, dashboard });
@@ -4612,13 +5861,13 @@ var DashboardManager = class {
4612
5861
  */
4613
5862
  updateDashboard(dashboardId, dashboard) {
4614
5863
  const dashboardPath = this.getDashboardPath(dashboardId);
4615
- if (!import_fs4.default.existsSync(dashboardPath)) {
5864
+ if (!import_fs5.default.existsSync(dashboardPath)) {
4616
5865
  logger.warn(`Dashboard not found for update: ${dashboardId}`);
4617
5866
  return null;
4618
5867
  }
4619
5868
  try {
4620
5869
  const validated = DSLRendererPropsSchema.parse(dashboard);
4621
- import_fs4.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5870
+ import_fs5.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
4622
5871
  logger.info(`Dashboard updated: ${dashboardId}`);
4623
5872
  return validated;
4624
5873
  } catch (error) {
@@ -4634,12 +5883,12 @@ var DashboardManager = class {
4634
5883
  deleteDashboard(dashboardId) {
4635
5884
  const dashboardPath = this.getDashboardPath(dashboardId);
4636
5885
  const dashboardDir = import_path4.default.dirname(dashboardPath);
4637
- if (!import_fs4.default.existsSync(dashboardPath)) {
5886
+ if (!import_fs5.default.existsSync(dashboardPath)) {
4638
5887
  logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
4639
5888
  return false;
4640
5889
  }
4641
5890
  try {
4642
- import_fs4.default.rmSync(dashboardDir, { recursive: true, force: true });
5891
+ import_fs5.default.rmSync(dashboardDir, { recursive: true, force: true });
4643
5892
  logger.info(`Dashboard deleted: ${dashboardId}`);
4644
5893
  return true;
4645
5894
  } catch (error) {
@@ -4654,21 +5903,21 @@ var DashboardManager = class {
4654
5903
  */
4655
5904
  dashboardExists(dashboardId) {
4656
5905
  const dashboardPath = this.getDashboardPath(dashboardId);
4657
- return import_fs4.default.existsSync(dashboardPath);
5906
+ return import_fs5.default.existsSync(dashboardPath);
4658
5907
  }
4659
5908
  /**
4660
5909
  * Get dashboard count
4661
5910
  * @returns Number of dashboards
4662
5911
  */
4663
5912
  getDashboardCount() {
4664
- if (!import_fs4.default.existsSync(this.dashboardsBasePath)) {
5913
+ if (!import_fs5.default.existsSync(this.dashboardsBasePath)) {
4665
5914
  return 0;
4666
5915
  }
4667
5916
  try {
4668
- const dashboardDirs = import_fs4.default.readdirSync(this.dashboardsBasePath);
5917
+ const dashboardDirs = import_fs5.default.readdirSync(this.dashboardsBasePath);
4669
5918
  return dashboardDirs.filter((dir) => {
4670
5919
  const dashboardPath = this.getDashboardPath(dir);
4671
- return import_fs4.default.existsSync(dashboardPath);
5920
+ return import_fs5.default.existsSync(dashboardPath);
4672
5921
  }).length;
4673
5922
  } catch (error) {
4674
5923
  logger.error("Failed to get dashboard count:", error);
@@ -4678,7 +5927,7 @@ var DashboardManager = class {
4678
5927
  };
4679
5928
 
4680
5929
  // src/reports/report-manager.ts
4681
- var import_fs5 = __toESM(require("fs"));
5930
+ var import_fs6 = __toESM(require("fs"));
4682
5931
  var import_path5 = __toESM(require("path"));
4683
5932
  var import_os3 = __toESM(require("os"));
4684
5933
  var ReportManager = class {
@@ -4713,12 +5962,12 @@ var ReportManager = class {
4713
5962
  createReport(reportId, report) {
4714
5963
  const reportPath = this.getReportPath(reportId);
4715
5964
  const reportDir = import_path5.default.dirname(reportPath);
4716
- if (import_fs5.default.existsSync(reportPath)) {
5965
+ if (import_fs6.default.existsSync(reportPath)) {
4717
5966
  throw new Error(`Report '${reportId}' already exists`);
4718
5967
  }
4719
5968
  const validated = DSLRendererPropsSchema2.parse(report);
4720
- import_fs5.default.mkdirSync(reportDir, { recursive: true });
4721
- import_fs5.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
5969
+ import_fs6.default.mkdirSync(reportDir, { recursive: true });
5970
+ import_fs6.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
4722
5971
  logger.info(`Report created: ${reportId}`);
4723
5972
  return validated;
4724
5973
  }
@@ -4729,12 +5978,12 @@ var ReportManager = class {
4729
5978
  */
4730
5979
  getReport(reportId) {
4731
5980
  const reportPath = this.getReportPath(reportId);
4732
- if (!import_fs5.default.existsSync(reportPath)) {
5981
+ if (!import_fs6.default.existsSync(reportPath)) {
4733
5982
  logger.warn(`Report not found: ${reportId}`);
4734
5983
  return null;
4735
5984
  }
4736
5985
  try {
4737
- const fileContent = import_fs5.default.readFileSync(reportPath, "utf-8");
5986
+ const fileContent = import_fs6.default.readFileSync(reportPath, "utf-8");
4738
5987
  const report = JSON.parse(fileContent);
4739
5988
  const validated = DSLRendererPropsSchema2.parse(report);
4740
5989
  return validated;
@@ -4748,16 +5997,16 @@ var ReportManager = class {
4748
5997
  * @returns Array of report objects with their IDs
4749
5998
  */
4750
5999
  getAllReports() {
4751
- if (!import_fs5.default.existsSync(this.reportsBasePath)) {
4752
- import_fs5.default.mkdirSync(this.reportsBasePath, { recursive: true });
6000
+ if (!import_fs6.default.existsSync(this.reportsBasePath)) {
6001
+ import_fs6.default.mkdirSync(this.reportsBasePath, { recursive: true });
4753
6002
  return [];
4754
6003
  }
4755
6004
  const reports = [];
4756
6005
  try {
4757
- const reportDirs = import_fs5.default.readdirSync(this.reportsBasePath);
6006
+ const reportDirs = import_fs6.default.readdirSync(this.reportsBasePath);
4758
6007
  for (const reportId of reportDirs) {
4759
6008
  const reportPath = this.getReportPath(reportId);
4760
- if (import_fs5.default.existsSync(reportPath)) {
6009
+ if (import_fs6.default.existsSync(reportPath)) {
4761
6010
  const report = this.getReport(reportId);
4762
6011
  if (report) {
4763
6012
  reports.push({ reportId, report });
@@ -4779,13 +6028,13 @@ var ReportManager = class {
4779
6028
  */
4780
6029
  updateReport(reportId, report) {
4781
6030
  const reportPath = this.getReportPath(reportId);
4782
- if (!import_fs5.default.existsSync(reportPath)) {
6031
+ if (!import_fs6.default.existsSync(reportPath)) {
4783
6032
  logger.warn(`Report not found for update: ${reportId}`);
4784
6033
  return null;
4785
6034
  }
4786
6035
  try {
4787
6036
  const validated = DSLRendererPropsSchema2.parse(report);
4788
- import_fs5.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
6037
+ import_fs6.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
4789
6038
  logger.info(`Report updated: ${reportId}`);
4790
6039
  return validated;
4791
6040
  } catch (error) {
@@ -4801,12 +6050,12 @@ var ReportManager = class {
4801
6050
  deleteReport(reportId) {
4802
6051
  const reportPath = this.getReportPath(reportId);
4803
6052
  const reportDir = import_path5.default.dirname(reportPath);
4804
- if (!import_fs5.default.existsSync(reportPath)) {
6053
+ if (!import_fs6.default.existsSync(reportPath)) {
4805
6054
  logger.warn(`Report not found for deletion: ${reportId}`);
4806
6055
  return false;
4807
6056
  }
4808
6057
  try {
4809
- import_fs5.default.rmSync(reportDir, { recursive: true, force: true });
6058
+ import_fs6.default.rmSync(reportDir, { recursive: true, force: true });
4810
6059
  logger.info(`Report deleted: ${reportId}`);
4811
6060
  return true;
4812
6061
  } catch (error) {
@@ -4821,21 +6070,21 @@ var ReportManager = class {
4821
6070
  */
4822
6071
  reportExists(reportId) {
4823
6072
  const reportPath = this.getReportPath(reportId);
4824
- return import_fs5.default.existsSync(reportPath);
6073
+ return import_fs6.default.existsSync(reportPath);
4825
6074
  }
4826
6075
  /**
4827
6076
  * Get report count
4828
6077
  * @returns Number of reports
4829
6078
  */
4830
6079
  getReportCount() {
4831
- if (!import_fs5.default.existsSync(this.reportsBasePath)) {
6080
+ if (!import_fs6.default.existsSync(this.reportsBasePath)) {
4832
6081
  return 0;
4833
6082
  }
4834
6083
  try {
4835
- const reportDirs = import_fs5.default.readdirSync(this.reportsBasePath);
6084
+ const reportDirs = import_fs6.default.readdirSync(this.reportsBasePath);
4836
6085
  return reportDirs.filter((dir) => {
4837
6086
  const reportPath = this.getReportPath(dir);
4838
- return import_fs5.default.existsSync(reportPath);
6087
+ return import_fs6.default.existsSync(reportPath);
4839
6088
  }).length;
4840
6089
  } catch (error) {
4841
6090
  logger.error("Failed to get report count:", error);
@@ -5036,6 +6285,9 @@ var SuperatomSDK = class {
5036
6285
  this.maxReconnectAttempts = 5;
5037
6286
  this.collections = {};
5038
6287
  this.components = [];
6288
+ if (config.logLevel) {
6289
+ logger.setLogLevel(config.logLevel);
6290
+ }
5039
6291
  this.apiKey = config.apiKey;
5040
6292
  this.projectId = config.projectId;
5041
6293
  this.userId = config.userId || "anonymous";
@@ -5056,9 +6308,6 @@ var SuperatomSDK = class {
5056
6308
  });
5057
6309
  this.initializeDashboardManager();
5058
6310
  this.initializeReportManager();
5059
- this.connect().catch((error) => {
5060
- logger.error("Failed to connect to Superatom:", error);
5061
- });
5062
6311
  }
5063
6312
  /**
5064
6313
  * Initialize PromptLoader and load prompts into memory
@@ -5124,6 +6373,10 @@ var SuperatomSDK = class {
5124
6373
  * Connect to the Superatom WebSocket service
5125
6374
  */
5126
6375
  async connect() {
6376
+ if (this.connected && this.ws && this.ws.readyState === this.ws.OPEN) {
6377
+ logger.info("Already connected to WebSocket");
6378
+ return Promise.resolve();
6379
+ }
5127
6380
  return new Promise((resolve, reject) => {
5128
6381
  try {
5129
6382
  const url = new URL(this.url);
@@ -5186,7 +6439,7 @@ var SuperatomSDK = class {
5186
6439
  });
5187
6440
  break;
5188
6441
  case "USER_PROMPT_REQ":
5189
- handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders).catch((error) => {
6442
+ handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders, this.collections).catch((error) => {
5190
6443
  logger.error("Failed to handle user prompt request:", error);
5191
6444
  });
5192
6445
  break;
@@ -5339,6 +6592,7 @@ var SuperatomSDK = class {
5339
6592
  ThreadManager,
5340
6593
  UIBlock,
5341
6594
  UILogCollector,
5342
- UserManager
6595
+ UserManager,
6596
+ logger
5343
6597
  });
5344
6598
  //# sourceMappingURL=index.js.map