@superatomai/sdk-node 0.0.5 → 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, {
@@ -707,7 +832,8 @@ var UserPromptRequestPayloadSchema = import_zod3.z.object({
707
832
  SA_RUNTIME: import_zod3.z.object({
708
833
  threadId: import_zod3.z.string(),
709
834
  uiBlockId: import_zod3.z.string()
710
- }).optional()
835
+ }).optional(),
836
+ responseMode: import_zod3.z.enum(["component", "text"]).optional()
711
837
  });
712
838
  var UserPromptRequestMessageSchema = import_zod3.z.object({
713
839
  id: import_zod3.z.string(),
@@ -822,7 +948,9 @@ var ReportsRequestMessageSchema = import_zod3.z.object({
822
948
  });
823
949
 
824
950
  // src/utils/logger.ts
951
+ var import_fs = __toESM(require("fs"));
825
952
  var PREFIX = "[SuperatomSDK]";
953
+ var LOGSTREAM = import_fs.default.createWriteStream("superatom-sdk.log", { flags: "a" });
826
954
  var LOG_LEVEL_PRIORITY = {
827
955
  errors: 0,
828
956
  warnings: 1,
@@ -906,6 +1034,9 @@ var Logger = class {
906
1034
  console.log(PREFIX, "[DEBUG]", ...args);
907
1035
  }
908
1036
  }
1037
+ file(...args) {
1038
+ LOGSTREAM.write(args.join(" ") + "\n");
1039
+ }
909
1040
  };
910
1041
  var logger = new Logger();
911
1042
 
@@ -977,6 +1108,9 @@ var UIBlock = class {
977
1108
  getComponentMetadata() {
978
1109
  return this.generatedComponentMetadata;
979
1110
  }
1111
+ getTextResponse() {
1112
+ return this.textResponse || "";
1113
+ }
980
1114
  /**
981
1115
  * Set or update component metadata
982
1116
  */
@@ -1069,12 +1203,6 @@ var UIBlock = class {
1069
1203
  const processedData = this.processDataForStorage(data);
1070
1204
  this.componentData = { ...this.componentData, ...processedData };
1071
1205
  }
1072
- /**
1073
- * Get text response
1074
- */
1075
- getTextResponse() {
1076
- return this.textResponse;
1077
- }
1078
1206
  /**
1079
1207
  * Set or update text response
1080
1208
  */
@@ -1265,8 +1393,11 @@ var Thread = class {
1265
1393
  const questionNum = index + 1;
1266
1394
  const question = block.getUserQuestion();
1267
1395
  const metadata = block.getComponentMetadata();
1268
- let componentSummary = "No component generated";
1269
- 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) {
1270
1401
  const parts = [];
1271
1402
  if (metadata.type) {
1272
1403
  parts.push(`Component Type: ${metadata.type}`);
@@ -1286,11 +1417,17 @@ var Thread = class {
1286
1417
  const componentTypes = metadata.props.config.components.map((c) => c.type).join(", ");
1287
1418
  parts.push(`Multi-component with: ${componentTypes}`);
1288
1419
  }
1289
- componentSummary = parts.join(", ");
1420
+ assistantResponse = parts.join(", ");
1421
+ } else if (hasTextResponse) {
1422
+ assistantResponse = textResponse;
1423
+ } else {
1424
+ assistantResponse = "No response generated";
1290
1425
  }
1291
- contextLines.push(`Q${questionNum}: ${question}`);
1292
- contextLines.push(`A${questionNum}: ${componentSummary}`);
1293
- contextLines.push("");
1426
+ contextLines.push(`User:
1427
+ ${question}`);
1428
+ contextLines.push(`Assistant:
1429
+ ${assistantResponse}`);
1430
+ contextLines.push("---");
1294
1431
  });
1295
1432
  return contextLines.join("\n").trim();
1296
1433
  }
@@ -1466,7 +1603,7 @@ function sendDataResponse(id, collection, op, data, meta, sendMessage) {
1466
1603
  }
1467
1604
 
1468
1605
  // src/bundle.ts
1469
- var fs = __toESM(require("fs"));
1606
+ var fs2 = __toESM(require("fs"));
1470
1607
  var path = __toESM(require("path"));
1471
1608
  function getBundleDir(configDir) {
1472
1609
  const bundleDir = configDir || process.env.SA_BUNDLE_DIR;
@@ -1479,16 +1616,16 @@ function getBundleDir(configDir) {
1479
1616
  }
1480
1617
  function getJS(bundleDir) {
1481
1618
  try {
1482
- if (!fs.existsSync(bundleDir)) {
1619
+ if (!fs2.existsSync(bundleDir)) {
1483
1620
  throw new Error(`Bundle directory does not exist: ${bundleDir}`);
1484
1621
  }
1485
- const stats = fs.statSync(bundleDir);
1622
+ const stats = fs2.statSync(bundleDir);
1486
1623
  if (!stats.isDirectory()) {
1487
1624
  throw new Error(`Bundle path is not a directory: ${bundleDir}`);
1488
1625
  }
1489
1626
  let files;
1490
1627
  try {
1491
- files = fs.readdirSync(bundleDir);
1628
+ files = fs2.readdirSync(bundleDir);
1492
1629
  } catch (error) {
1493
1630
  const errorMessage = error instanceof Error ? error.message : String(error);
1494
1631
  throw new Error(`Failed to read bundle directory: ${errorMessage}`);
@@ -1503,7 +1640,7 @@ function getJS(bundleDir) {
1503
1640
  const filePath = path.join(bundleDir, indexFile);
1504
1641
  logger.info(`Loading bundle from ${filePath}`);
1505
1642
  try {
1506
- return fs.readFileSync(filePath, "utf8");
1643
+ return fs2.readFileSync(filePath, "utf8");
1507
1644
  } catch (error) {
1508
1645
  const errorMessage = error instanceof Error ? error.message : String(error);
1509
1646
  throw new Error(`Failed to read bundle file: ${errorMessage}`);
@@ -1909,94 +2046,12 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
1909
2046
  // src/userResponse/groq.ts
1910
2047
  var import_dotenv = __toESM(require_main());
1911
2048
 
1912
- // src/userResponse/utils.ts
1913
- function convertTopToLimit(query) {
1914
- if (!query || query.trim().length === 0) {
1915
- return query;
1916
- }
1917
- let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
1918
- if (modifiedQuery !== query) {
1919
- console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
1920
- }
1921
- return modifiedQuery;
1922
- }
1923
- function ensureQueryLimit(query, defaultLimit = 50) {
1924
- if (!query || query.trim().length === 0) {
1925
- return query;
1926
- }
1927
- let trimmedQuery = query.trim();
1928
- const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
1929
- if (!isSelectQuery) {
1930
- return query;
1931
- }
1932
- trimmedQuery = convertTopToLimit(trimmedQuery);
1933
- const hadSemicolon = trimmedQuery.endsWith(";");
1934
- if (hadSemicolon) {
1935
- trimmedQuery = trimmedQuery.slice(0, -1).trim();
1936
- }
1937
- const limitMatches = trimmedQuery.match(/\bLIMIT\s+\d+\b/gi);
1938
- if (limitMatches && limitMatches.length > 0) {
1939
- if (limitMatches.length > 1) {
1940
- console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
1941
- trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
1942
- } else {
1943
- if (hadSemicolon) {
1944
- trimmedQuery += ";";
1945
- }
1946
- return trimmedQuery;
1947
- }
1948
- }
1949
- trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
1950
- if (hadSemicolon) {
1951
- trimmedQuery += ";";
1952
- }
1953
- return trimmedQuery;
1954
- }
1955
- function fixScalarSubqueries(query) {
1956
- if (!query || query.trim().length === 0) {
1957
- return query;
1958
- }
1959
- let modifiedQuery = query;
1960
- let hasChanges = false;
1961
- const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
1962
- const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
1963
- for (let i = matches.length - 1; i >= 0; i--) {
1964
- const match = matches[i];
1965
- const startPos = match.index + match[0].length - "SELECT ".length;
1966
- let parenDepth = 1;
1967
- let endPos = startPos;
1968
- let foundEnd = false;
1969
- for (let j = startPos; j < modifiedQuery.length; j++) {
1970
- const char = modifiedQuery[j];
1971
- if (char === "(") parenDepth++;
1972
- if (char === ")") {
1973
- parenDepth--;
1974
- if (parenDepth === 0) {
1975
- endPos = j;
1976
- foundEnd = true;
1977
- break;
1978
- }
1979
- }
1980
- }
1981
- if (!foundEnd) continue;
1982
- const subquery = modifiedQuery.substring(startPos, endPos);
1983
- if (/\bLIMIT\s+\d+/i.test(subquery)) {
1984
- continue;
1985
- }
1986
- const fixedSubquery = subquery.trim() + " LIMIT 1";
1987
- modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
1988
- hasChanges = true;
1989
- console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
1990
- }
1991
- if (hasChanges) {
1992
- console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
1993
- }
1994
- return modifiedQuery;
1995
- }
2049
+ // src/userResponse/base-llm.ts
2050
+ init_utils();
1996
2051
 
1997
2052
  // src/userResponse/schema.ts
1998
2053
  var import_path = __toESM(require("path"));
1999
- var import_fs = __toESM(require("fs"));
2054
+ var import_fs2 = __toESM(require("fs"));
2000
2055
  var Schema = class {
2001
2056
  constructor(schemaFilePath) {
2002
2057
  this.cachedSchema = null;
@@ -2010,11 +2065,11 @@ var Schema = class {
2010
2065
  logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
2011
2066
  try {
2012
2067
  const dir = import_path.default.dirname(this.schemaFilePath);
2013
- if (!import_fs.default.existsSync(dir)) {
2068
+ if (!import_fs2.default.existsSync(dir)) {
2014
2069
  logger.info(`Creating directory structure: ${dir}`);
2015
- import_fs.default.mkdirSync(dir, { recursive: true });
2070
+ import_fs2.default.mkdirSync(dir, { recursive: true });
2016
2071
  }
2017
- if (!import_fs.default.existsSync(this.schemaFilePath)) {
2072
+ if (!import_fs2.default.existsSync(this.schemaFilePath)) {
2018
2073
  logger.info(`Schema file does not exist at ${this.schemaFilePath}, creating with empty schema`);
2019
2074
  const initialSchema = {
2020
2075
  database: "",
@@ -2023,11 +2078,11 @@ var Schema = class {
2023
2078
  tables: [],
2024
2079
  relationships: []
2025
2080
  };
2026
- import_fs.default.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
2081
+ import_fs2.default.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
2027
2082
  this.cachedSchema = initialSchema;
2028
2083
  return initialSchema;
2029
2084
  }
2030
- const fileContent = import_fs.default.readFileSync(this.schemaFilePath, "utf-8");
2085
+ const fileContent = import_fs2.default.readFileSync(this.schemaFilePath, "utf-8");
2031
2086
  const schema2 = JSON.parse(fileContent);
2032
2087
  this.cachedSchema = schema2;
2033
2088
  return schema2;
@@ -2128,7 +2183,7 @@ var Schema = class {
2128
2183
  var schema = new Schema();
2129
2184
 
2130
2185
  // src/userResponse/prompt-loader.ts
2131
- var import_fs2 = __toESM(require("fs"));
2186
+ var import_fs3 = __toESM(require("fs"));
2132
2187
  var import_path2 = __toESM(require("path"));
2133
2188
  var PromptLoader = class {
2134
2189
  constructor(config) {
@@ -2156,7 +2211,8 @@ var PromptLoader = class {
2156
2211
  "mutli-component",
2157
2212
  "actions",
2158
2213
  "container-metadata",
2159
- "text-response"
2214
+ "text-response",
2215
+ "match-text-components"
2160
2216
  ];
2161
2217
  for (const promptType of promptTypes) {
2162
2218
  try {
@@ -2181,9 +2237,9 @@ var PromptLoader = class {
2181
2237
  try {
2182
2238
  const systemPath = import_path2.default.join(dir, promptName, "system.md");
2183
2239
  const userPath = import_path2.default.join(dir, promptName, "user.md");
2184
- if (import_fs2.default.existsSync(systemPath) && import_fs2.default.existsSync(userPath)) {
2185
- const system = import_fs2.default.readFileSync(systemPath, "utf-8");
2186
- 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");
2187
2243
  logger.debug(`Loaded prompt '${promptName}' from ${dir}`);
2188
2244
  return { system, user };
2189
2245
  }
@@ -2305,6 +2361,14 @@ var LLM = class {
2305
2361
  throw new Error(`Unsupported provider: ${provider}. Use "anthropic" or "groq"`);
2306
2362
  }
2307
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
+ }
2308
2372
  // ============================================================
2309
2373
  // PRIVATE HELPER METHODS
2310
2374
  // ============================================================
@@ -2382,6 +2446,86 @@ var LLM = class {
2382
2446
  }
2383
2447
  return fullText;
2384
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
+ }
2385
2529
  // ============================================================
2386
2530
  // GROQ IMPLEMENTATION
2387
2531
  // ============================================================
@@ -2994,62 +3138,460 @@ var BaseLLM = class {
2994
3138
  throw error;
2995
3139
  }
2996
3140
  }
3141
+ /**
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
3149
+ */
3150
+ async matchComponentsFromTextResponse(textResponse, components, apiKey, logCollector) {
3151
+ try {
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
+ }
2997
3300
  /**
2998
3301
  * Generate text-based response for user question
2999
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
3000
3308
  */
3001
- async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory) {
3309
+ async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory, streamCallback, collections, components) {
3002
3310
  const errors = [];
3003
3311
  logger.debug(`[${this.getProviderName()}] Starting text response generation`);
3004
3312
  logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
3005
3313
  try {
3314
+ const schemaDoc = schema.generateSchemaDocumentation();
3006
3315
  const prompts = await promptLoader.loadPrompts("text-response", {
3007
3316
  USER_PROMPT: userPrompt,
3008
- CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
3317
+ CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
3318
+ SCHEMA_DOC: schemaDoc
3009
3319
  });
3010
- logger.debug(`[${this.getProviderName()}] Loaded text-response prompts`);
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`);
3011
3323
  logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
3012
- logCollector?.info("Generating text response...");
3013
- const result = await LLM.stream(
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(
3014
3490
  {
3015
3491
  sys: prompts.system,
3016
3492
  user: prompts.user
3017
3493
  },
3494
+ tools,
3495
+ toolHandler,
3018
3496
  {
3019
3497
  model: this.model,
3020
- maxTokens: 1e3,
3498
+ maxTokens: 4e3,
3021
3499
  temperature: 0.7,
3022
- apiKey: this.getApiKey(apiKey)
3500
+ apiKey: this.getApiKey(apiKey),
3501
+ partial: wrappedStreamCallback
3502
+ // Pass the wrapped streaming callback to LLM
3023
3503
  },
3024
- true
3025
- // Parse as JSON
3504
+ 10
3505
+ // max iterations: allows for 6 retries + final response + buffer
3026
3506
  );
3027
- logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3028
- logger.debug(`[${this.getProviderName()}] Response type: ${result.responseType}, Confidence: ${result.confidence}`);
3029
- logCollector?.info(`Text response: ${result.text?.substring(0, 100)}${result.text?.length > 100 ? "..." : ""}`);
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 ? "..." : ""}`);
3030
3524
  logCollector?.logExplanation(
3031
3525
  "Text response generated",
3032
- result.reasoning || "Generated text response for user question",
3526
+ "Generated plain text response with component suggestions",
3033
3527
  {
3034
- responseType: result.responseType,
3035
- confidence: result.confidence,
3036
- textLength: result.text?.length || 0
3528
+ textLength: textResponse.length
3037
3529
  }
3038
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
+ }
3039
3581
  return {
3040
3582
  success: true,
3041
3583
  data: {
3042
- text: result.text || "I apologize, but I was unable to generate a response.",
3043
- responseType: result.responseType || "answer",
3044
- confidence: result.confidence || 0.5,
3045
- reasoning: result.reasoning || "No reasoning provided"
3584
+ text: textResponse,
3585
+ matchedComponents,
3586
+ component: container_componet,
3587
+ layoutReasoning,
3588
+ method: `${this.getProviderName()}-text-response`
3046
3589
  },
3047
3590
  errors: []
3048
3591
  };
3049
3592
  } catch (error) {
3050
3593
  const errorMsg = error instanceof Error ? error.message : String(error);
3051
3594
  logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
3052
- logger.debug(`[${this.getProviderName()}] Text response generation error details:`, error);
3053
3595
  logCollector?.error(`Error generating text response: ${errorMsg}`);
3054
3596
  errors.push(errorMsg);
3055
3597
  return {
@@ -3057,9 +3599,8 @@ var BaseLLM = class {
3057
3599
  errors,
3058
3600
  data: {
3059
3601
  text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
3060
- responseType: "error",
3061
- confidence: 0,
3062
- reasoning: `Error: ${errorMsg}`
3602
+ matchedComponents: [],
3603
+ method: `${this.getProviderName()}-text-response-error`
3063
3604
  }
3064
3605
  };
3065
3606
  }
@@ -3250,8 +3791,11 @@ var BaseLLM = class {
3250
3791
  * Supports both component generation and text response modes
3251
3792
  *
3252
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)
3253
3796
  */
3254
- async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") {
3797
+ async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) {
3798
+ const startTime = Date.now();
3255
3799
  logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
3256
3800
  if (responseMode === "text") {
3257
3801
  logger.info(`[${this.getProviderName()}] Using text response mode`);
@@ -3260,25 +3804,23 @@ var BaseLLM = class {
3260
3804
  userPrompt,
3261
3805
  apiKey,
3262
3806
  logCollector,
3263
- conversationHistory
3807
+ conversationHistory,
3808
+ streamCallback,
3809
+ collections,
3810
+ components
3264
3811
  );
3265
3812
  if (!textResponse.success) {
3813
+ const elapsedTime3 = Date.now() - startTime;
3266
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)`);
3267
3817
  return textResponse;
3268
3818
  }
3819
+ const elapsedTime2 = Date.now() - startTime;
3269
3820
  logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3270
- return {
3271
- success: true,
3272
- data: {
3273
- textResponse: textResponse.data.text,
3274
- text: textResponse.data.text,
3275
- responseType: textResponse.data.responseType,
3276
- confidence: textResponse.data.confidence,
3277
- reasoning: textResponse.data.reasoning,
3278
- method: `${this.getProviderName()}-text-response`
3279
- },
3280
- errors: []
3281
- };
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;
3282
3824
  }
3283
3825
  const componentResponse = await this.generateComponentResponse(
3284
3826
  userPrompt,
@@ -3288,24 +3830,17 @@ var BaseLLM = class {
3288
3830
  conversationHistory
3289
3831
  );
3290
3832
  if (!componentResponse.success) {
3833
+ const elapsedTime2 = Date.now() - startTime;
3291
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)`);
3292
3837
  return componentResponse;
3293
3838
  }
3839
+ const elapsedTime = Date.now() - startTime;
3294
3840
  logger.info(`[${this.getProviderName()}] Component response generated successfully`);
3295
- return {
3296
- success: true,
3297
- data: {
3298
- component: componentResponse.data.component,
3299
- reasoning: componentResponse.data.reasoning,
3300
- method: componentResponse.data.method,
3301
- // Preserve the original method name
3302
- questionType: componentResponse.data.questionType,
3303
- needsMultipleComponents: componentResponse.data.needsMultipleComponents,
3304
- propsModified: componentResponse.data.propsModified,
3305
- queryModified: componentResponse.data.queryModified
3306
- },
3307
- errors: []
3308
- };
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;
3309
3844
  }
3310
3845
  /**
3311
3846
  * Generate next questions that the user might ask based on the original prompt and generated component
@@ -3418,7 +3953,7 @@ function getLLMProviders() {
3418
3953
  return DEFAULT_PROVIDERS;
3419
3954
  }
3420
3955
  }
3421
- var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
3956
+ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3422
3957
  logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
3423
3958
  logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
3424
3959
  const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
@@ -3430,11 +3965,11 @@ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conver
3430
3965
  return { success: false, errors: [emptyMsg] };
3431
3966
  }
3432
3967
  logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
3433
- const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
3968
+ const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3434
3969
  logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
3435
3970
  return matchResult;
3436
3971
  };
3437
- var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
3972
+ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3438
3973
  logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
3439
3974
  logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
3440
3975
  const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
@@ -3447,16 +3982,16 @@ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversatio
3447
3982
  return { success: false, errors: [emptyMsg] };
3448
3983
  }
3449
3984
  logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
3450
- const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
3985
+ const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3451
3986
  logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
3452
3987
  return matchResult;
3453
3988
  };
3454
3989
  var getUserResponseFromCache = async (prompt) => {
3455
3990
  return false;
3456
3991
  };
3457
- var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
3458
- const responseMode = "component";
3992
+ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3459
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}`);
3460
3995
  logger.debug("[get_user_response] Checking cache for existing response");
3461
3996
  const userResponse = await getUserResponseFromCache(prompt);
3462
3997
  if (userResponse) {
@@ -3487,16 +4022,16 @@ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey,
3487
4022
  logCollector?.info(attemptMsg);
3488
4023
  let result;
3489
4024
  if (provider === "anthropic") {
3490
- result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode);
4025
+ result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3491
4026
  } else if (provider === "groq") {
3492
- result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode);
4027
+ result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3493
4028
  } else {
3494
4029
  logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
3495
4030
  errors.push(`Unknown provider: ${provider}`);
3496
4031
  continue;
3497
4032
  }
3498
4033
  if (result.success) {
3499
- const successMsg = `Success with provider: ${provider} result: ${JSON.stringify(result)}`;
4034
+ const successMsg = `Success with provider: ${provider}`;
3500
4035
  logger.info(`${successMsg}`);
3501
4036
  logCollector?.info(successMsg);
3502
4037
  return result;
@@ -3717,8 +4252,7 @@ var CONTEXT_CONFIG = {
3717
4252
  };
3718
4253
 
3719
4254
  // src/handlers/user-prompt-request.ts
3720
- var processedMessageIds = /* @__PURE__ */ new Set();
3721
- var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) => {
4255
+ var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) => {
3722
4256
  const errors = [];
3723
4257
  logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
3724
4258
  const parseResult = UserPromptRequestMessageSchema.safeParse(data);
@@ -3734,19 +4268,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3734
4268
  const prompt = payload.prompt;
3735
4269
  const SA_RUNTIME = payload.SA_RUNTIME;
3736
4270
  const wsId = userPromptRequest.from.id || "unknown";
3737
- logger.debug(`[REQUEST ${id}] Full request details - wsId: ${wsId}, prompt length: ${prompt.length}`);
3738
- if (processedMessageIds.has(id)) {
3739
- logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3740
- }
3741
- processedMessageIds.add(id);
3742
- logger.debug(`[REQUEST ${id}] Message ID marked as processed (${processedMessageIds.size} total)`);
3743
- if (processedMessageIds.size > 100) {
3744
- const firstId = processedMessageIds.values().next().value;
3745
- if (firstId) {
3746
- processedMessageIds.delete(firstId);
3747
- logger.debug(`[REQUEST ${id}] Cleaned up old message ID from cache`);
3748
- }
3749
- }
3750
4271
  if (!SA_RUNTIME) {
3751
4272
  errors.push("SA_RUNTIME is required");
3752
4273
  }
@@ -3761,9 +4282,7 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3761
4282
  if (!prompt) {
3762
4283
  errors.push("Prompt not found");
3763
4284
  }
3764
- if (!components || components.length === 0) {
3765
- errors.push("Components not found");
3766
- }
4285
+ logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
3767
4286
  if (errors.length > 0) {
3768
4287
  return { success: false, errors, id, wsId };
3769
4288
  }
@@ -3777,8 +4296,44 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3777
4296
  logCollector.info(`Starting user prompt request with ${components.length} components`);
3778
4297
  const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3779
4298
  logger.info("conversationHistory", conversationHistory);
3780
- const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3781
- logger.info("llm userResponse", userResponse);
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
+ );
3782
4337
  logCollector.info("User prompt request completed");
3783
4338
  const uiBlockId = existingUiBlockId;
3784
4339
  if (!userResponse.success) {
@@ -3801,10 +4356,11 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3801
4356
  component = userResponse.data.component;
3802
4357
  }
3803
4358
  if ("textResponse" in userResponse.data) {
3804
- textResponse = userResponse.data.textResponse;
4359
+ textResponse = userResponse.data.text;
3805
4360
  }
3806
4361
  }
3807
4362
  }
4363
+ const finalTextResponse = responseMode === "text" && accumulatedStreamResponse ? accumulatedStreamResponse : textResponse;
3808
4364
  const uiBlock = new UIBlock(
3809
4365
  prompt,
3810
4366
  {},
@@ -3814,8 +4370,8 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3814
4370
  [],
3815
4371
  // actions: empty initially
3816
4372
  uiBlockId,
3817
- textResponse
3818
- // textResponse: text response from LLM
4373
+ finalTextResponse
4374
+ // textResponse: FULL streaming response including all intermediate messages
3819
4375
  );
3820
4376
  thread.addUIBlock(uiBlock);
3821
4377
  logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
@@ -3829,8 +4385,8 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3829
4385
  wsId
3830
4386
  };
3831
4387
  };
3832
- async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3833
- const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders);
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);
3834
4390
  sendDataResponse4(
3835
4391
  response.id || data.id,
3836
4392
  {
@@ -4906,7 +5462,7 @@ function sendResponse5(id, res, sendMessage, clientId) {
4906
5462
  }
4907
5463
 
4908
5464
  // src/auth/user-manager.ts
4909
- var import_fs3 = __toESM(require("fs"));
5465
+ var import_fs4 = __toESM(require("fs"));
4910
5466
  var import_path3 = __toESM(require("path"));
4911
5467
  var import_os = __toESM(require("os"));
4912
5468
  var UserManager = class {
@@ -4946,19 +5502,19 @@ var UserManager = class {
4946
5502
  async loadUsersFromFile() {
4947
5503
  try {
4948
5504
  const dir = import_path3.default.dirname(this.filePath);
4949
- if (!import_fs3.default.existsSync(dir)) {
5505
+ if (!import_fs4.default.existsSync(dir)) {
4950
5506
  logger.info(`Creating directory structure: ${dir}`);
4951
- import_fs3.default.mkdirSync(dir, { recursive: true });
5507
+ import_fs4.default.mkdirSync(dir, { recursive: true });
4952
5508
  }
4953
- if (!import_fs3.default.existsSync(this.filePath)) {
5509
+ if (!import_fs4.default.existsSync(this.filePath)) {
4954
5510
  logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
4955
5511
  const initialData = { users: [] };
4956
- import_fs3.default.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
5512
+ import_fs4.default.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
4957
5513
  this.users = [];
4958
5514
  this.hasChanged = false;
4959
5515
  return;
4960
5516
  }
4961
- const fileContent = import_fs3.default.readFileSync(this.filePath, "utf-8");
5517
+ const fileContent = import_fs4.default.readFileSync(this.filePath, "utf-8");
4962
5518
  const rawData = JSON.parse(fileContent);
4963
5519
  const validatedData = UsersDataSchema.parse(rawData);
4964
5520
  this.users = validatedData.users;
@@ -4978,15 +5534,15 @@ var UserManager = class {
4978
5534
  }
4979
5535
  try {
4980
5536
  const dir = import_path3.default.dirname(this.filePath);
4981
- if (!import_fs3.default.existsSync(dir)) {
4982
- import_fs3.default.mkdirSync(dir, { recursive: true });
5537
+ if (!import_fs4.default.existsSync(dir)) {
5538
+ import_fs4.default.mkdirSync(dir, { recursive: true });
4983
5539
  }
4984
5540
  const usersToSave = this.users.map((user) => {
4985
5541
  const { wsIds, ...userWithoutWsIds } = user;
4986
5542
  return userWithoutWsIds;
4987
5543
  });
4988
5544
  const data = { users: usersToSave };
4989
- import_fs3.default.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
5545
+ import_fs4.default.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
4990
5546
  this.hasChanged = false;
4991
5547
  logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
4992
5548
  } catch (error) {
@@ -5204,7 +5760,7 @@ var UserManager = class {
5204
5760
  };
5205
5761
 
5206
5762
  // src/dashboards/dashboard-manager.ts
5207
- var import_fs4 = __toESM(require("fs"));
5763
+ var import_fs5 = __toESM(require("fs"));
5208
5764
  var import_path4 = __toESM(require("path"));
5209
5765
  var import_os2 = __toESM(require("os"));
5210
5766
  var DashboardManager = class {
@@ -5239,12 +5795,12 @@ var DashboardManager = class {
5239
5795
  createDashboard(dashboardId, dashboard) {
5240
5796
  const dashboardPath = this.getDashboardPath(dashboardId);
5241
5797
  const dashboardDir = import_path4.default.dirname(dashboardPath);
5242
- if (import_fs4.default.existsSync(dashboardPath)) {
5798
+ if (import_fs5.default.existsSync(dashboardPath)) {
5243
5799
  throw new Error(`Dashboard '${dashboardId}' already exists`);
5244
5800
  }
5245
5801
  const validated = DSLRendererPropsSchema.parse(dashboard);
5246
- import_fs4.default.mkdirSync(dashboardDir, { recursive: true });
5247
- 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));
5248
5804
  logger.info(`Dashboard created: ${dashboardId}`);
5249
5805
  return validated;
5250
5806
  }
@@ -5255,12 +5811,12 @@ var DashboardManager = class {
5255
5811
  */
5256
5812
  getDashboard(dashboardId) {
5257
5813
  const dashboardPath = this.getDashboardPath(dashboardId);
5258
- if (!import_fs4.default.existsSync(dashboardPath)) {
5814
+ if (!import_fs5.default.existsSync(dashboardPath)) {
5259
5815
  logger.warn(`Dashboard not found: ${dashboardId}`);
5260
5816
  return null;
5261
5817
  }
5262
5818
  try {
5263
- const fileContent = import_fs4.default.readFileSync(dashboardPath, "utf-8");
5819
+ const fileContent = import_fs5.default.readFileSync(dashboardPath, "utf-8");
5264
5820
  const dashboard = JSON.parse(fileContent);
5265
5821
  const validated = DSLRendererPropsSchema.parse(dashboard);
5266
5822
  return validated;
@@ -5274,16 +5830,16 @@ var DashboardManager = class {
5274
5830
  * @returns Array of dashboard objects with their IDs
5275
5831
  */
5276
5832
  getAllDashboards() {
5277
- if (!import_fs4.default.existsSync(this.dashboardsBasePath)) {
5278
- 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 });
5279
5835
  return [];
5280
5836
  }
5281
5837
  const dashboards = [];
5282
5838
  try {
5283
- const dashboardDirs = import_fs4.default.readdirSync(this.dashboardsBasePath);
5839
+ const dashboardDirs = import_fs5.default.readdirSync(this.dashboardsBasePath);
5284
5840
  for (const dashboardId of dashboardDirs) {
5285
5841
  const dashboardPath = this.getDashboardPath(dashboardId);
5286
- if (import_fs4.default.existsSync(dashboardPath)) {
5842
+ if (import_fs5.default.existsSync(dashboardPath)) {
5287
5843
  const dashboard = this.getDashboard(dashboardId);
5288
5844
  if (dashboard) {
5289
5845
  dashboards.push({ dashboardId, dashboard });
@@ -5305,13 +5861,13 @@ var DashboardManager = class {
5305
5861
  */
5306
5862
  updateDashboard(dashboardId, dashboard) {
5307
5863
  const dashboardPath = this.getDashboardPath(dashboardId);
5308
- if (!import_fs4.default.existsSync(dashboardPath)) {
5864
+ if (!import_fs5.default.existsSync(dashboardPath)) {
5309
5865
  logger.warn(`Dashboard not found for update: ${dashboardId}`);
5310
5866
  return null;
5311
5867
  }
5312
5868
  try {
5313
5869
  const validated = DSLRendererPropsSchema.parse(dashboard);
5314
- import_fs4.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5870
+ import_fs5.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5315
5871
  logger.info(`Dashboard updated: ${dashboardId}`);
5316
5872
  return validated;
5317
5873
  } catch (error) {
@@ -5327,12 +5883,12 @@ var DashboardManager = class {
5327
5883
  deleteDashboard(dashboardId) {
5328
5884
  const dashboardPath = this.getDashboardPath(dashboardId);
5329
5885
  const dashboardDir = import_path4.default.dirname(dashboardPath);
5330
- if (!import_fs4.default.existsSync(dashboardPath)) {
5886
+ if (!import_fs5.default.existsSync(dashboardPath)) {
5331
5887
  logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
5332
5888
  return false;
5333
5889
  }
5334
5890
  try {
5335
- import_fs4.default.rmSync(dashboardDir, { recursive: true, force: true });
5891
+ import_fs5.default.rmSync(dashboardDir, { recursive: true, force: true });
5336
5892
  logger.info(`Dashboard deleted: ${dashboardId}`);
5337
5893
  return true;
5338
5894
  } catch (error) {
@@ -5347,21 +5903,21 @@ var DashboardManager = class {
5347
5903
  */
5348
5904
  dashboardExists(dashboardId) {
5349
5905
  const dashboardPath = this.getDashboardPath(dashboardId);
5350
- return import_fs4.default.existsSync(dashboardPath);
5906
+ return import_fs5.default.existsSync(dashboardPath);
5351
5907
  }
5352
5908
  /**
5353
5909
  * Get dashboard count
5354
5910
  * @returns Number of dashboards
5355
5911
  */
5356
5912
  getDashboardCount() {
5357
- if (!import_fs4.default.existsSync(this.dashboardsBasePath)) {
5913
+ if (!import_fs5.default.existsSync(this.dashboardsBasePath)) {
5358
5914
  return 0;
5359
5915
  }
5360
5916
  try {
5361
- const dashboardDirs = import_fs4.default.readdirSync(this.dashboardsBasePath);
5917
+ const dashboardDirs = import_fs5.default.readdirSync(this.dashboardsBasePath);
5362
5918
  return dashboardDirs.filter((dir) => {
5363
5919
  const dashboardPath = this.getDashboardPath(dir);
5364
- return import_fs4.default.existsSync(dashboardPath);
5920
+ return import_fs5.default.existsSync(dashboardPath);
5365
5921
  }).length;
5366
5922
  } catch (error) {
5367
5923
  logger.error("Failed to get dashboard count:", error);
@@ -5371,7 +5927,7 @@ var DashboardManager = class {
5371
5927
  };
5372
5928
 
5373
5929
  // src/reports/report-manager.ts
5374
- var import_fs5 = __toESM(require("fs"));
5930
+ var import_fs6 = __toESM(require("fs"));
5375
5931
  var import_path5 = __toESM(require("path"));
5376
5932
  var import_os3 = __toESM(require("os"));
5377
5933
  var ReportManager = class {
@@ -5406,12 +5962,12 @@ var ReportManager = class {
5406
5962
  createReport(reportId, report) {
5407
5963
  const reportPath = this.getReportPath(reportId);
5408
5964
  const reportDir = import_path5.default.dirname(reportPath);
5409
- if (import_fs5.default.existsSync(reportPath)) {
5965
+ if (import_fs6.default.existsSync(reportPath)) {
5410
5966
  throw new Error(`Report '${reportId}' already exists`);
5411
5967
  }
5412
5968
  const validated = DSLRendererPropsSchema2.parse(report);
5413
- import_fs5.default.mkdirSync(reportDir, { recursive: true });
5414
- 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));
5415
5971
  logger.info(`Report created: ${reportId}`);
5416
5972
  return validated;
5417
5973
  }
@@ -5422,12 +5978,12 @@ var ReportManager = class {
5422
5978
  */
5423
5979
  getReport(reportId) {
5424
5980
  const reportPath = this.getReportPath(reportId);
5425
- if (!import_fs5.default.existsSync(reportPath)) {
5981
+ if (!import_fs6.default.existsSync(reportPath)) {
5426
5982
  logger.warn(`Report not found: ${reportId}`);
5427
5983
  return null;
5428
5984
  }
5429
5985
  try {
5430
- const fileContent = import_fs5.default.readFileSync(reportPath, "utf-8");
5986
+ const fileContent = import_fs6.default.readFileSync(reportPath, "utf-8");
5431
5987
  const report = JSON.parse(fileContent);
5432
5988
  const validated = DSLRendererPropsSchema2.parse(report);
5433
5989
  return validated;
@@ -5441,16 +5997,16 @@ var ReportManager = class {
5441
5997
  * @returns Array of report objects with their IDs
5442
5998
  */
5443
5999
  getAllReports() {
5444
- if (!import_fs5.default.existsSync(this.reportsBasePath)) {
5445
- 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 });
5446
6002
  return [];
5447
6003
  }
5448
6004
  const reports = [];
5449
6005
  try {
5450
- const reportDirs = import_fs5.default.readdirSync(this.reportsBasePath);
6006
+ const reportDirs = import_fs6.default.readdirSync(this.reportsBasePath);
5451
6007
  for (const reportId of reportDirs) {
5452
6008
  const reportPath = this.getReportPath(reportId);
5453
- if (import_fs5.default.existsSync(reportPath)) {
6009
+ if (import_fs6.default.existsSync(reportPath)) {
5454
6010
  const report = this.getReport(reportId);
5455
6011
  if (report) {
5456
6012
  reports.push({ reportId, report });
@@ -5472,13 +6028,13 @@ var ReportManager = class {
5472
6028
  */
5473
6029
  updateReport(reportId, report) {
5474
6030
  const reportPath = this.getReportPath(reportId);
5475
- if (!import_fs5.default.existsSync(reportPath)) {
6031
+ if (!import_fs6.default.existsSync(reportPath)) {
5476
6032
  logger.warn(`Report not found for update: ${reportId}`);
5477
6033
  return null;
5478
6034
  }
5479
6035
  try {
5480
6036
  const validated = DSLRendererPropsSchema2.parse(report);
5481
- import_fs5.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
6037
+ import_fs6.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
5482
6038
  logger.info(`Report updated: ${reportId}`);
5483
6039
  return validated;
5484
6040
  } catch (error) {
@@ -5494,12 +6050,12 @@ var ReportManager = class {
5494
6050
  deleteReport(reportId) {
5495
6051
  const reportPath = this.getReportPath(reportId);
5496
6052
  const reportDir = import_path5.default.dirname(reportPath);
5497
- if (!import_fs5.default.existsSync(reportPath)) {
6053
+ if (!import_fs6.default.existsSync(reportPath)) {
5498
6054
  logger.warn(`Report not found for deletion: ${reportId}`);
5499
6055
  return false;
5500
6056
  }
5501
6057
  try {
5502
- import_fs5.default.rmSync(reportDir, { recursive: true, force: true });
6058
+ import_fs6.default.rmSync(reportDir, { recursive: true, force: true });
5503
6059
  logger.info(`Report deleted: ${reportId}`);
5504
6060
  return true;
5505
6061
  } catch (error) {
@@ -5514,21 +6070,21 @@ var ReportManager = class {
5514
6070
  */
5515
6071
  reportExists(reportId) {
5516
6072
  const reportPath = this.getReportPath(reportId);
5517
- return import_fs5.default.existsSync(reportPath);
6073
+ return import_fs6.default.existsSync(reportPath);
5518
6074
  }
5519
6075
  /**
5520
6076
  * Get report count
5521
6077
  * @returns Number of reports
5522
6078
  */
5523
6079
  getReportCount() {
5524
- if (!import_fs5.default.existsSync(this.reportsBasePath)) {
6080
+ if (!import_fs6.default.existsSync(this.reportsBasePath)) {
5525
6081
  return 0;
5526
6082
  }
5527
6083
  try {
5528
- const reportDirs = import_fs5.default.readdirSync(this.reportsBasePath);
6084
+ const reportDirs = import_fs6.default.readdirSync(this.reportsBasePath);
5529
6085
  return reportDirs.filter((dir) => {
5530
6086
  const reportPath = this.getReportPath(dir);
5531
- return import_fs5.default.existsSync(reportPath);
6087
+ return import_fs6.default.existsSync(reportPath);
5532
6088
  }).length;
5533
6089
  } catch (error) {
5534
6090
  logger.error("Failed to get report count:", error);
@@ -5752,9 +6308,6 @@ var SuperatomSDK = class {
5752
6308
  });
5753
6309
  this.initializeDashboardManager();
5754
6310
  this.initializeReportManager();
5755
- this.connect().catch((error) => {
5756
- logger.error("Failed to connect to Superatom:", error);
5757
- });
5758
6311
  }
5759
6312
  /**
5760
6313
  * Initialize PromptLoader and load prompts into memory
@@ -5820,6 +6373,10 @@ var SuperatomSDK = class {
5820
6373
  * Connect to the Superatom WebSocket service
5821
6374
  */
5822
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
+ }
5823
6380
  return new Promise((resolve, reject) => {
5824
6381
  try {
5825
6382
  const url = new URL(this.url);
@@ -5882,7 +6439,7 @@ var SuperatomSDK = class {
5882
6439
  });
5883
6440
  break;
5884
6441
  case "USER_PROMPT_REQ":
5885
- 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) => {
5886
6443
  logger.error("Failed to handle user prompt request:", error);
5887
6444
  });
5888
6445
  break;