@deepagents/text2sql 0.11.0 → 0.12.0

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.
Files changed (49) hide show
  1. package/dist/index.d.ts +3 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1231 -511
  4. package/dist/index.js.map +4 -4
  5. package/dist/lib/adapters/groundings/index.js +1937 -50
  6. package/dist/lib/adapters/groundings/index.js.map +4 -4
  7. package/dist/lib/adapters/groundings/report.grounding.d.ts.map +1 -1
  8. package/dist/lib/adapters/mysql/index.js +1937 -50
  9. package/dist/lib/adapters/mysql/index.js.map +4 -4
  10. package/dist/lib/adapters/postgres/index.js +1937 -50
  11. package/dist/lib/adapters/postgres/index.js.map +4 -4
  12. package/dist/lib/adapters/spreadsheet/index.js +34 -49
  13. package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
  14. package/dist/lib/adapters/sqlite/index.js +1937 -50
  15. package/dist/lib/adapters/sqlite/index.js.map +4 -4
  16. package/dist/lib/adapters/sqlserver/index.js +1937 -50
  17. package/dist/lib/adapters/sqlserver/index.js.map +4 -4
  18. package/dist/lib/agents/developer.agent.d.ts.map +1 -1
  19. package/dist/lib/agents/explainer.agent.d.ts +4 -5
  20. package/dist/lib/agents/explainer.agent.d.ts.map +1 -1
  21. package/dist/lib/agents/question.agent.d.ts.map +1 -1
  22. package/dist/lib/agents/result-tools.d.ts +34 -0
  23. package/dist/lib/agents/result-tools.d.ts.map +1 -0
  24. package/dist/lib/agents/sql.agent.d.ts.map +1 -1
  25. package/dist/lib/agents/teachables.agent.d.ts.map +1 -1
  26. package/dist/lib/agents/text2sql.agent.d.ts +0 -21
  27. package/dist/lib/agents/text2sql.agent.d.ts.map +1 -1
  28. package/dist/lib/checkpoint.d.ts +1 -1
  29. package/dist/lib/checkpoint.d.ts.map +1 -1
  30. package/dist/lib/instructions.d.ts +9 -28
  31. package/dist/lib/instructions.d.ts.map +1 -1
  32. package/dist/lib/sql.d.ts +1 -1
  33. package/dist/lib/sql.d.ts.map +1 -1
  34. package/dist/lib/synthesis/extractors/base-contextual-extractor.d.ts +6 -7
  35. package/dist/lib/synthesis/extractors/base-contextual-extractor.d.ts.map +1 -1
  36. package/dist/lib/synthesis/extractors/last-query-extractor.d.ts.map +1 -1
  37. package/dist/lib/synthesis/extractors/segmented-context-extractor.d.ts +0 -6
  38. package/dist/lib/synthesis/extractors/segmented-context-extractor.d.ts.map +1 -1
  39. package/dist/lib/synthesis/extractors/sql-extractor.d.ts.map +1 -1
  40. package/dist/lib/synthesis/index.js +2478 -2323
  41. package/dist/lib/synthesis/index.js.map +4 -4
  42. package/dist/lib/synthesis/synthesizers/breadth-evolver.d.ts.map +1 -1
  43. package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts.map +1 -1
  44. package/dist/lib/synthesis/synthesizers/persona-generator.d.ts.map +1 -1
  45. package/package.json +9 -15
  46. package/dist/lib/instructions.js +0 -432
  47. package/dist/lib/instructions.js.map +0 -7
  48. package/dist/lib/teach/teachings.d.ts +0 -11
  49. package/dist/lib/teach/teachings.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -387,15 +387,16 @@ function getTablesWithRelated(allTables, relationships, filter) {
387
387
 
388
388
  // packages/text2sql/src/lib/agents/developer.agent.ts
389
389
  import { tool } from "ai";
390
- import dedent2 from "dedent";
390
+ import dedent from "dedent";
391
391
  import z2 from "zod";
392
- import { generate, toState } from "@deepagents/agent";
392
+ import { toState } from "@deepagents/agent";
393
393
 
394
394
  // packages/context/dist/index.js
395
395
  import { encode } from "gpt-tokenizer";
396
396
  import { generateId } from "ai";
397
397
  import pluralize from "pluralize";
398
398
  import { titlecase } from "stringcase";
399
+ import chalk from "chalk";
399
400
  import { defineCommand } from "just-bash";
400
401
  import spawn from "nano-spawn";
401
402
  import "bash-tool";
@@ -405,7 +406,9 @@ import {
405
406
  } from "bash-tool";
406
407
  import YAML from "yaml";
407
408
  import { DatabaseSync } from "node:sqlite";
409
+ import { groq } from "@ai-sdk/groq";
408
410
  import {
411
+ NoSuchToolError,
409
412
  Output,
410
413
  convertToModelMessages,
411
414
  createUIMessageStream,
@@ -415,7 +418,7 @@ import {
415
418
  stepCountIs,
416
419
  streamText
417
420
  } from "ai";
418
- import chalk from "chalk";
421
+ import chalk2 from "chalk";
419
422
  import "zod";
420
423
  import "@deepagents/agent";
421
424
  var defaultTokenizer = {
@@ -547,6 +550,12 @@ function isFragmentObject(data) {
547
550
  function isMessageFragment(fragment2) {
548
551
  return fragment2.type === "message";
549
552
  }
553
+ function fragment(name, ...children) {
554
+ return {
555
+ name,
556
+ data: children
557
+ };
558
+ }
550
559
  function user(content) {
551
560
  const message2 = typeof content === "string" ? {
552
561
  id: generateId(),
@@ -639,6 +648,68 @@ var ContextRenderer = class {
639
648
  }
640
649
  return groups;
641
650
  }
651
+ /**
652
+ * Remove null/undefined from fragments and fragment data recursively.
653
+ * This protects renderers from nullish values and ensures they are ignored
654
+ * consistently across all output formats.
655
+ */
656
+ sanitizeFragments(fragments2) {
657
+ const sanitized = [];
658
+ for (const fragment2 of fragments2) {
659
+ const cleaned = this.sanitizeFragment(fragment2, /* @__PURE__ */ new WeakSet());
660
+ if (cleaned) {
661
+ sanitized.push(cleaned);
662
+ }
663
+ }
664
+ return sanitized;
665
+ }
666
+ sanitizeFragment(fragment2, seen) {
667
+ const data = this.sanitizeData(fragment2.data, seen);
668
+ if (data == null) {
669
+ return null;
670
+ }
671
+ return {
672
+ ...fragment2,
673
+ data
674
+ };
675
+ }
676
+ sanitizeData(data, seen) {
677
+ if (data == null) {
678
+ return void 0;
679
+ }
680
+ if (isFragment(data)) {
681
+ return this.sanitizeFragment(data, seen) ?? void 0;
682
+ }
683
+ if (Array.isArray(data)) {
684
+ if (seen.has(data)) {
685
+ return void 0;
686
+ }
687
+ seen.add(data);
688
+ const cleaned = [];
689
+ for (const item of data) {
690
+ const sanitizedItem = this.sanitizeData(item, seen);
691
+ if (sanitizedItem != null) {
692
+ cleaned.push(sanitizedItem);
693
+ }
694
+ }
695
+ return cleaned;
696
+ }
697
+ if (isFragmentObject(data)) {
698
+ if (seen.has(data)) {
699
+ return void 0;
700
+ }
701
+ seen.add(data);
702
+ const cleaned = {};
703
+ for (const [key, value] of Object.entries(data)) {
704
+ const sanitizedValue = this.sanitizeData(value, seen);
705
+ if (sanitizedValue != null) {
706
+ cleaned[key] = sanitizedValue;
707
+ }
708
+ }
709
+ return cleaned;
710
+ }
711
+ return data;
712
+ }
642
713
  /**
643
714
  * Template method - dispatches value to appropriate handler.
644
715
  */
@@ -666,7 +737,8 @@ var ContextRenderer = class {
666
737
  };
667
738
  var XmlRenderer = class extends ContextRenderer {
668
739
  render(fragments2) {
669
- return fragments2.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
740
+ const sanitized = this.sanitizeFragments(fragments2);
741
+ return sanitized.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
670
742
  }
671
743
  #renderTopLevel(fragment2) {
672
744
  if (this.isPrimitive(fragment2.data)) {
@@ -679,10 +751,13 @@ var XmlRenderer = class extends ContextRenderer {
679
751
  const child = this.renderFragment(fragment2.data, { depth: 1, path: [] });
680
752
  return this.#wrap(fragment2.name, [child]);
681
753
  }
682
- return this.#wrap(
683
- fragment2.name,
684
- this.renderEntries(fragment2.data, { depth: 1, path: [] })
685
- );
754
+ if (isFragmentObject(fragment2.data)) {
755
+ return this.#wrap(
756
+ fragment2.name,
757
+ this.renderEntries(fragment2.data, { depth: 1, path: [] })
758
+ );
759
+ }
760
+ return "";
686
761
  }
687
762
  #renderArray(name, items, depth) {
688
763
  const fragmentItems = items.filter(isFragment);
@@ -690,9 +765,19 @@ var XmlRenderer = class extends ContextRenderer {
690
765
  const children = [];
691
766
  for (const item of nonFragmentItems) {
692
767
  if (item != null) {
693
- children.push(
694
- this.#leaf(pluralize.singular(name), String(item), depth + 1)
695
- );
768
+ if (isFragmentObject(item)) {
769
+ children.push(
770
+ this.#wrapIndented(
771
+ pluralize.singular(name),
772
+ this.renderEntries(item, { depth: depth + 2, path: [] }),
773
+ depth + 1
774
+ )
775
+ );
776
+ } else {
777
+ children.push(
778
+ this.#leaf(pluralize.singular(name), String(item), depth + 1)
779
+ );
780
+ }
696
781
  }
697
782
  }
698
783
  if (this.options.groupFragments && fragmentItems.length > 0) {
@@ -734,8 +819,14 @@ ${this.#indent(safe, 2)}
734
819
  if (Array.isArray(data)) {
735
820
  return this.#renderArrayIndented(name, data, ctx.depth);
736
821
  }
737
- const children = this.renderEntries(data, { ...ctx, depth: ctx.depth + 1 });
738
- return this.#wrapIndented(name, children, ctx.depth);
822
+ if (isFragmentObject(data)) {
823
+ const children = this.renderEntries(data, {
824
+ ...ctx,
825
+ depth: ctx.depth + 1
826
+ });
827
+ return this.#wrapIndented(name, children, ctx.depth);
828
+ }
829
+ return "";
739
830
  }
740
831
  #renderArrayIndented(name, items, depth) {
741
832
  const fragmentItems = items.filter(isFragment);
@@ -743,9 +834,19 @@ ${this.#indent(safe, 2)}
743
834
  const children = [];
744
835
  for (const item of nonFragmentItems) {
745
836
  if (item != null) {
746
- children.push(
747
- this.#leaf(pluralize.singular(name), String(item), depth + 1)
748
- );
837
+ if (isFragmentObject(item)) {
838
+ children.push(
839
+ this.#wrapIndented(
840
+ pluralize.singular(name),
841
+ this.renderEntries(item, { depth: depth + 2, path: [] }),
842
+ depth + 1
843
+ )
844
+ );
845
+ } else {
846
+ children.push(
847
+ this.#leaf(pluralize.singular(name), String(item), depth + 1)
848
+ );
849
+ }
749
850
  }
750
851
  }
751
852
  if (this.options.groupFragments && fragmentItems.length > 0) {
@@ -774,7 +875,19 @@ ${this.#indent(safe, 2)}
774
875
  return "";
775
876
  }
776
877
  const itemTag = pluralize.singular(key);
777
- const children = items.filter((item) => item != null).map((item) => this.#leaf(itemTag, String(item), ctx.depth + 1));
878
+ const children = items.filter((item) => item != null).map((item) => {
879
+ if (isFragment(item)) {
880
+ return this.renderFragment(item, { ...ctx, depth: ctx.depth + 1 });
881
+ }
882
+ if (isFragmentObject(item)) {
883
+ return this.#wrapIndented(
884
+ itemTag,
885
+ this.renderEntries(item, { ...ctx, depth: ctx.depth + 2 }),
886
+ ctx.depth + 1
887
+ );
888
+ }
889
+ return this.#leaf(itemTag, String(item), ctx.depth + 1);
890
+ });
778
891
  return this.#wrapIndented(key, children, ctx.depth);
779
892
  }
780
893
  renderObject(key, obj, ctx) {
@@ -833,6 +946,7 @@ var ContextEngine = class {
833
946
  #pendingMessages = [];
834
947
  #store;
835
948
  #chatId;
949
+ #userId;
836
950
  #branchName;
837
951
  #branch = null;
838
952
  #chatData = null;
@@ -841,9 +955,13 @@ var ContextEngine = class {
841
955
  if (!options.chatId) {
842
956
  throw new Error("chatId is required");
843
957
  }
958
+ if (!options.userId) {
959
+ throw new Error("userId is required");
960
+ }
844
961
  this.#store = options.store;
845
962
  this.#chatId = options.chatId;
846
- this.#branchName = options.branch ?? "main";
963
+ this.#userId = options.userId;
964
+ this.#branchName = "main";
847
965
  }
848
966
  /**
849
967
  * Initialize the chat and branch if they don't exist.
@@ -852,24 +970,11 @@ var ContextEngine = class {
852
970
  if (this.#initialized) {
853
971
  return;
854
972
  }
855
- this.#chatData = await this.#store.upsertChat({ id: this.#chatId });
856
- const existingBranch = await this.#store.getBranch(
857
- this.#chatId,
858
- this.#branchName
859
- );
860
- if (existingBranch) {
861
- this.#branch = existingBranch;
862
- } else {
863
- this.#branch = {
864
- id: crypto.randomUUID(),
865
- chatId: this.#chatId,
866
- name: this.#branchName,
867
- headMessageId: null,
868
- isActive: true,
869
- createdAt: Date.now()
870
- };
871
- await this.#store.createBranch(this.#branch);
872
- }
973
+ this.#chatData = await this.#store.upsertChat({
974
+ id: this.#chatId,
975
+ userId: this.#userId
976
+ });
977
+ this.#branch = await this.#store.getActiveBranch(this.#chatId);
873
978
  this.#initialized = true;
874
979
  }
875
980
  /**
@@ -929,6 +1034,7 @@ var ContextEngine = class {
929
1034
  }
930
1035
  return {
931
1036
  id: this.#chatData.id,
1037
+ userId: this.#chatData.userId,
932
1038
  createdAt: this.#chatData.createdAt,
933
1039
  updatedAt: this.#chatData.updatedAt,
934
1040
  title: this.#chatData.title,
@@ -971,7 +1077,7 @@ var ContextEngine = class {
971
1077
  *
972
1078
  * @example
973
1079
  * ```ts
974
- * const context = new ContextEngine({ store, chatId: 'chat-1' })
1080
+ * const context = new ContextEngine({ store, chatId: 'chat-1', userId: 'user-1' })
975
1081
  * .set(role('You are helpful'), user('Hello'));
976
1082
  *
977
1083
  * const { systemPrompt, messages } = await context.resolve();
@@ -1315,7 +1421,7 @@ var ContextEngine = class {
1315
1421
  }
1316
1422
  /**
1317
1423
  * Inspect the full context state for debugging.
1318
- * Returns a comprehensive JSON-serializable object with all context information.
1424
+ * Returns a JSON-serializable object with context information.
1319
1425
  *
1320
1426
  * @param options - Inspection options (modelId and renderer required)
1321
1427
  * @returns Complete inspection data including estimates, rendered output, fragments, and graph
@@ -1378,6 +1484,26 @@ function guardrail(input) {
1378
1484
  }
1379
1485
  };
1380
1486
  }
1487
+ function explain(input) {
1488
+ return {
1489
+ name: "explain",
1490
+ data: {
1491
+ concept: input.concept,
1492
+ explanation: input.explanation,
1493
+ ...input.therefore && { therefore: input.therefore }
1494
+ }
1495
+ };
1496
+ }
1497
+ function example(input) {
1498
+ return {
1499
+ name: "example",
1500
+ data: {
1501
+ question: input.question,
1502
+ answer: input.answer,
1503
+ ...input.note && { note: input.note }
1504
+ }
1505
+ };
1506
+ }
1381
1507
  function clarification(input) {
1382
1508
  return {
1383
1509
  name: "clarification",
@@ -1399,6 +1525,15 @@ function workflow(input) {
1399
1525
  }
1400
1526
  };
1401
1527
  }
1528
+ function quirk(input) {
1529
+ return {
1530
+ name: "quirk",
1531
+ data: {
1532
+ issue: input.issue,
1533
+ workaround: input.workaround
1534
+ }
1535
+ };
1536
+ }
1402
1537
  function styleGuide(input) {
1403
1538
  return {
1404
1539
  name: "styleGuide",
@@ -1409,12 +1544,40 @@ function styleGuide(input) {
1409
1544
  }
1410
1545
  };
1411
1546
  }
1547
+ function role(content) {
1548
+ return {
1549
+ name: "role",
1550
+ data: content
1551
+ };
1552
+ }
1553
+ function principle(input) {
1554
+ return {
1555
+ name: "principle",
1556
+ data: {
1557
+ title: input.title,
1558
+ description: input.description,
1559
+ ...input.policies?.length && { policies: input.policies }
1560
+ }
1561
+ };
1562
+ }
1563
+ function policy(input) {
1564
+ return {
1565
+ name: "policy",
1566
+ data: {
1567
+ rule: input.rule,
1568
+ ...input.before && { before: input.before },
1569
+ ...input.reason && { reason: input.reason },
1570
+ ...input.policies?.length && { policies: input.policies }
1571
+ }
1572
+ };
1573
+ }
1412
1574
  function persona(input) {
1413
1575
  return {
1414
1576
  name: "persona",
1415
1577
  data: {
1416
1578
  name: input.name,
1417
- role: input.role,
1579
+ ...input.role && { role: input.role },
1580
+ ...input.objective && { objective: input.objective },
1418
1581
  ...input.tone && { tone: input.tone }
1419
1582
  }
1420
1583
  };
@@ -1422,6 +1585,9 @@ function persona(input) {
1422
1585
  function pass(part) {
1423
1586
  return { type: "pass", part };
1424
1587
  }
1588
+ function fail(feedback) {
1589
+ return { type: "fail", feedback };
1590
+ }
1425
1591
  function runGuardrailChain(part, guardrails, context) {
1426
1592
  let currentPart = part;
1427
1593
  for (const guardrail2 of guardrails) {
@@ -1433,11 +1599,77 @@ function runGuardrailChain(part, guardrails, context) {
1433
1599
  }
1434
1600
  return pass(currentPart);
1435
1601
  }
1602
+ var errorRecoveryGuardrail = {
1603
+ id: "error-recovery",
1604
+ name: "API Error Recovery",
1605
+ handle: (part, context) => {
1606
+ if (part.type !== "error") {
1607
+ return pass(part);
1608
+ }
1609
+ const errorText = part.errorText || "";
1610
+ const prefix = chalk.bold.magenta("[ErrorRecovery]");
1611
+ console.log(
1612
+ `${prefix} ${chalk.red("Caught error:")} ${chalk.dim(errorText.slice(0, 150))}`
1613
+ );
1614
+ const logAndFail = (pattern, feedback) => {
1615
+ console.log(
1616
+ `${prefix} ${chalk.yellow("Pattern:")} ${chalk.cyan(pattern)}`
1617
+ );
1618
+ console.log(
1619
+ `${prefix} ${chalk.green("Feedback:")} ${chalk.dim(feedback.slice(0, 80))}...`
1620
+ );
1621
+ return fail(feedback);
1622
+ };
1623
+ if (errorText.includes("Tool choice is none")) {
1624
+ if (context.availableTools.length > 0) {
1625
+ return logAndFail(
1626
+ "Tool choice is none",
1627
+ `I tried to call a tool that doesn't exist. Available tools: ${context.availableTools.join(", ")}. Let me use one of these instead.`
1628
+ );
1629
+ }
1630
+ return logAndFail(
1631
+ "Tool choice is none (no tools)",
1632
+ "I tried to call a tool, but no tools are available. Let me respond with plain text instead."
1633
+ );
1634
+ }
1635
+ if (errorText.includes("not in request.tools") || errorText.includes("tool") && errorText.includes("not found")) {
1636
+ const toolMatch = errorText.match(/tool '([^']+)'/);
1637
+ const toolName = toolMatch ? toolMatch[1] : "unknown";
1638
+ if (context.availableTools.length > 0) {
1639
+ return logAndFail(
1640
+ `Unregistered tool: ${toolName}`,
1641
+ `I tried to call "${toolName}" but it doesn't exist. Available tools: ${context.availableTools.join(", ")}. Let me use one of these instead.`
1642
+ );
1643
+ }
1644
+ return logAndFail(
1645
+ `Unregistered tool: ${toolName} (no tools)`,
1646
+ `I tried to call "${toolName}" but no tools are available. Let me respond with plain text instead.`
1647
+ );
1648
+ }
1649
+ if (errorText.includes("Failed to parse tool call arguments") || errorText.includes("parse tool call") || errorText.includes("invalid JSON")) {
1650
+ return logAndFail(
1651
+ "Malformed JSON arguments",
1652
+ "I generated malformed JSON for the tool arguments. Let me format my tool call properly with valid JSON."
1653
+ );
1654
+ }
1655
+ if (errorText.includes("Parsing failed")) {
1656
+ return logAndFail(
1657
+ "Parsing failed",
1658
+ "My response format was invalid. Let me try again with a properly formatted response."
1659
+ );
1660
+ }
1661
+ return logAndFail(
1662
+ "Unknown error",
1663
+ `An error occurred: ${errorText.slice(0, 100)}. Let me try a different approach.`
1664
+ );
1665
+ }
1666
+ };
1436
1667
  var STORE_DDL = `
1437
1668
  -- Chats table
1438
1669
  -- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
1439
1670
  CREATE TABLE IF NOT EXISTS chats (
1440
1671
  id TEXT PRIMARY KEY,
1672
+ userId TEXT NOT NULL,
1441
1673
  title TEXT,
1442
1674
  metadata TEXT,
1443
1675
  createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
@@ -1445,6 +1677,7 @@ CREATE TABLE IF NOT EXISTS chats (
1445
1677
  );
1446
1678
 
1447
1679
  CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
1680
+ CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
1448
1681
 
1449
1682
  -- Messages table (nodes in the DAG)
1450
1683
  CREATE TABLE IF NOT EXISTS messages (
@@ -1510,37 +1743,67 @@ var SqliteContextStore = class extends ContextStore {
1510
1743
  this.#db.exec("PRAGMA foreign_keys = ON");
1511
1744
  this.#db.exec(STORE_DDL);
1512
1745
  }
1746
+ /**
1747
+ * Execute a function within a transaction.
1748
+ * Automatically commits on success or rolls back on error.
1749
+ */
1750
+ #useTransaction(fn) {
1751
+ this.#db.exec("BEGIN TRANSACTION");
1752
+ try {
1753
+ const result = fn();
1754
+ this.#db.exec("COMMIT");
1755
+ return result;
1756
+ } catch (error) {
1757
+ this.#db.exec("ROLLBACK");
1758
+ throw error;
1759
+ }
1760
+ }
1513
1761
  // ==========================================================================
1514
1762
  // Chat Operations
1515
1763
  // ==========================================================================
1516
1764
  async createChat(chat) {
1517
- this.#db.prepare(
1518
- `INSERT INTO chats (id, title, metadata)
1519
- VALUES (?, ?, ?)`
1520
- ).run(
1521
- chat.id,
1522
- chat.title ?? null,
1523
- chat.metadata ? JSON.stringify(chat.metadata) : null
1524
- );
1765
+ this.#useTransaction(() => {
1766
+ this.#db.prepare(
1767
+ `INSERT INTO chats (id, userId, title, metadata)
1768
+ VALUES (?, ?, ?, ?)`
1769
+ ).run(
1770
+ chat.id,
1771
+ chat.userId,
1772
+ chat.title ?? null,
1773
+ chat.metadata ? JSON.stringify(chat.metadata) : null
1774
+ );
1775
+ this.#db.prepare(
1776
+ `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
1777
+ VALUES (?, ?, 'main', NULL, 1, ?)`
1778
+ ).run(crypto.randomUUID(), chat.id, Date.now());
1779
+ });
1525
1780
  }
1526
1781
  async upsertChat(chat) {
1527
- const row = this.#db.prepare(
1528
- `INSERT INTO chats (id, title, metadata)
1529
- VALUES (?, ?, ?)
1530
- ON CONFLICT(id) DO UPDATE SET id = excluded.id
1531
- RETURNING *`
1532
- ).get(
1533
- chat.id,
1534
- chat.title ?? null,
1535
- chat.metadata ? JSON.stringify(chat.metadata) : null
1536
- );
1537
- return {
1538
- id: row.id,
1539
- title: row.title ?? void 0,
1540
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1541
- createdAt: row.createdAt,
1542
- updatedAt: row.updatedAt
1543
- };
1782
+ return this.#useTransaction(() => {
1783
+ const row = this.#db.prepare(
1784
+ `INSERT INTO chats (id, userId, title, metadata)
1785
+ VALUES (?, ?, ?, ?)
1786
+ ON CONFLICT(id) DO UPDATE SET id = excluded.id
1787
+ RETURNING *`
1788
+ ).get(
1789
+ chat.id,
1790
+ chat.userId,
1791
+ chat.title ?? null,
1792
+ chat.metadata ? JSON.stringify(chat.metadata) : null
1793
+ );
1794
+ this.#db.prepare(
1795
+ `INSERT OR IGNORE INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
1796
+ VALUES (?, ?, 'main', NULL, 1, ?)`
1797
+ ).run(crypto.randomUUID(), chat.id, Date.now());
1798
+ return {
1799
+ id: row.id,
1800
+ userId: row.userId,
1801
+ title: row.title ?? void 0,
1802
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1803
+ createdAt: row.createdAt,
1804
+ updatedAt: row.updatedAt
1805
+ };
1806
+ });
1544
1807
  }
1545
1808
  async getChat(chatId) {
1546
1809
  const row = this.#db.prepare("SELECT * FROM chats WHERE id = ?").get(chatId);
@@ -1549,6 +1812,7 @@ var SqliteContextStore = class extends ContextStore {
1549
1812
  }
1550
1813
  return {
1551
1814
  id: row.id,
1815
+ userId: row.userId,
1552
1816
  title: row.title ?? void 0,
1553
1817
  metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1554
1818
  createdAt: row.createdAt,
@@ -1572,16 +1836,33 @@ var SqliteContextStore = class extends ContextStore {
1572
1836
  ).get(...params);
1573
1837
  return {
1574
1838
  id: row.id,
1839
+ userId: row.userId,
1575
1840
  title: row.title ?? void 0,
1576
1841
  metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1577
1842
  createdAt: row.createdAt,
1578
1843
  updatedAt: row.updatedAt
1579
1844
  };
1580
1845
  }
1581
- async listChats() {
1846
+ async listChats(options) {
1847
+ const params = [];
1848
+ let whereClause = "";
1849
+ let limitClause = "";
1850
+ if (options?.userId) {
1851
+ whereClause = "WHERE c.userId = ?";
1852
+ params.push(options.userId);
1853
+ }
1854
+ if (options?.limit !== void 0) {
1855
+ limitClause = " LIMIT ?";
1856
+ params.push(options.limit);
1857
+ if (options.offset !== void 0) {
1858
+ limitClause += " OFFSET ?";
1859
+ params.push(options.offset);
1860
+ }
1861
+ }
1582
1862
  const rows = this.#db.prepare(
1583
1863
  `SELECT
1584
1864
  c.id,
1865
+ c.userId,
1585
1866
  c.title,
1586
1867
  c.createdAt,
1587
1868
  c.updatedAt,
@@ -1590,11 +1871,13 @@ var SqliteContextStore = class extends ContextStore {
1590
1871
  FROM chats c
1591
1872
  LEFT JOIN messages m ON m.chatId = c.id
1592
1873
  LEFT JOIN branches b ON b.chatId = c.id
1874
+ ${whereClause}
1593
1875
  GROUP BY c.id
1594
- ORDER BY c.updatedAt DESC`
1595
- ).all();
1876
+ ORDER BY c.updatedAt DESC${limitClause}`
1877
+ ).all(...params);
1596
1878
  return rows.map((row) => ({
1597
1879
  id: row.id,
1880
+ userId: row.userId,
1598
1881
  title: row.title ?? void 0,
1599
1882
  messageCount: row.messageCount,
1600
1883
  branchCount: row.branchCount,
@@ -1602,6 +1885,25 @@ var SqliteContextStore = class extends ContextStore {
1602
1885
  updatedAt: row.updatedAt
1603
1886
  }));
1604
1887
  }
1888
+ async deleteChat(chatId, options) {
1889
+ return this.#useTransaction(() => {
1890
+ const messageIds = this.#db.prepare("SELECT id FROM messages WHERE chatId = ?").all(chatId);
1891
+ let sql = "DELETE FROM chats WHERE id = ?";
1892
+ const params = [chatId];
1893
+ if (options?.userId !== void 0) {
1894
+ sql += " AND userId = ?";
1895
+ params.push(options.userId);
1896
+ }
1897
+ const result = this.#db.prepare(sql).run(...params);
1898
+ if (result.changes > 0 && messageIds.length > 0) {
1899
+ const placeholders = messageIds.map(() => "?").join(", ");
1900
+ this.#db.prepare(
1901
+ `DELETE FROM messages_fts WHERE messageId IN (${placeholders})`
1902
+ ).run(...messageIds.map((m) => m.id));
1903
+ }
1904
+ return result.changes > 0;
1905
+ });
1906
+ }
1605
1907
  // ==========================================================================
1606
1908
  // Message Operations (Graph Nodes)
1607
1909
  // ==========================================================================
@@ -1672,6 +1974,17 @@ var SqliteContextStore = class extends ContextStore {
1672
1974
  ).get(messageId);
1673
1975
  return row.hasChildren === 1;
1674
1976
  }
1977
+ async getMessages(chatId) {
1978
+ const chat = await this.getChat(chatId);
1979
+ if (!chat) {
1980
+ throw new Error(`Chat "${chatId}" not found`);
1981
+ }
1982
+ const activeBranch = await this.getActiveBranch(chatId);
1983
+ if (!activeBranch?.headMessageId) {
1984
+ return [];
1985
+ }
1986
+ return this.getMessageChain(activeBranch.headMessageId);
1987
+ }
1675
1988
  // ==========================================================================
1676
1989
  // Branch Operations
1677
1990
  // ==========================================================================
@@ -1933,16 +2246,17 @@ var Agent = class _Agent {
1933
2246
  providerOptions: this.#options.providerOptions,
1934
2247
  model: this.#options.model,
1935
2248
  system: systemPrompt,
1936
- messages: convertToModelMessages(messages),
2249
+ messages: await convertToModelMessages(messages),
1937
2250
  stopWhen: stepCountIs(25),
1938
2251
  tools: this.#options.tools,
1939
2252
  experimental_context: contextVariables,
2253
+ experimental_repairToolCall: repairToolCall,
1940
2254
  toolChoice: this.#options.toolChoice,
1941
2255
  onStepFinish: (step) => {
1942
2256
  const toolCall = step.toolCalls.at(-1);
1943
2257
  if (toolCall) {
1944
2258
  console.log(
1945
- `Debug: ${chalk.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
2259
+ `Debug: ${chalk2.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
1946
2260
  );
1947
2261
  }
1948
2262
  }
@@ -1991,8 +2305,9 @@ var Agent = class _Agent {
1991
2305
  providerOptions: this.#options.providerOptions,
1992
2306
  model: this.#options.model,
1993
2307
  system: systemPrompt,
1994
- messages: convertToModelMessages(messages),
1995
- stopWhen: stepCountIs(25),
2308
+ messages: await convertToModelMessages(messages),
2309
+ experimental_repairToolCall: repairToolCall,
2310
+ stopWhen: stepCountIs(50),
1996
2311
  experimental_transform: config?.transform ?? smoothStream(),
1997
2312
  tools: this.#options.tools,
1998
2313
  experimental_context: contextVariables,
@@ -2001,7 +2316,7 @@ var Agent = class _Agent {
2001
2316
  const toolCall = step.toolCalls.at(-1);
2002
2317
  if (toolCall) {
2003
2318
  console.log(
2004
- `Debug: (${runId}) ${chalk.bold.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
2319
+ `Debug: (${runId}) ${chalk2.bold.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
2005
2320
  );
2006
2321
  }
2007
2322
  }
@@ -2048,7 +2363,7 @@ var Agent = class _Agent {
2048
2363
  guardrailFailed = true;
2049
2364
  failureFeedback = checkResult.feedback;
2050
2365
  console.log(
2051
- chalk.yellow(
2366
+ chalk2.yellow(
2052
2367
  `[${this.#options.name}] Guardrail triggered (attempt ${attempt}/${maxRetries}): ${failureFeedback.slice(0, 50)}...`
2053
2368
  )
2054
2369
  );
@@ -2065,7 +2380,7 @@ var Agent = class _Agent {
2065
2380
  }
2066
2381
  if (attempt >= maxRetries) {
2067
2382
  console.error(
2068
- chalk.red(
2383
+ chalk2.red(
2069
2384
  `[${this.#options.name}] Guardrail retry limit (${maxRetries}) exceeded.`
2070
2385
  )
2071
2386
  );
@@ -2108,14 +2423,10 @@ function structuredOutput(options) {
2108
2423
  return {
2109
2424
  async generate(contextVariables, config) {
2110
2425
  if (!options.context) {
2111
- throw new Error(
2112
- `structuredOutput "${options.name}" is missing a context.`
2113
- );
2426
+ throw new Error(`structuredOutput is missing a context.`);
2114
2427
  }
2115
2428
  if (!options.model) {
2116
- throw new Error(
2117
- `structuredOutput "${options.name}" is missing a model.`
2118
- );
2429
+ throw new Error(`structuredOutput is missing a model.`);
2119
2430
  }
2120
2431
  const { messages, systemPrompt } = await options.context.resolve({
2121
2432
  renderer: new XmlRenderer()
@@ -2125,23 +2436,21 @@ function structuredOutput(options) {
2125
2436
  providerOptions: options.providerOptions,
2126
2437
  model: options.model,
2127
2438
  system: systemPrompt,
2128
- messages: convertToModelMessages(messages),
2439
+ messages: await convertToModelMessages(messages),
2129
2440
  stopWhen: stepCountIs(25),
2441
+ experimental_repairToolCall: repairToolCall,
2130
2442
  experimental_context: contextVariables,
2131
- experimental_output: Output.object({ schema: options.schema })
2443
+ output: Output.object({ schema: options.schema }),
2444
+ tools: options.tools
2132
2445
  });
2133
- return result.experimental_output;
2446
+ return result.output;
2134
2447
  },
2135
2448
  async stream(contextVariables, config) {
2136
2449
  if (!options.context) {
2137
- throw new Error(
2138
- `structuredOutput "${options.name}" is missing a context.`
2139
- );
2450
+ throw new Error(`structuredOutput is missing a context.`);
2140
2451
  }
2141
2452
  if (!options.model) {
2142
- throw new Error(
2143
- `structuredOutput "${options.name}" is missing a model.`
2144
- );
2453
+ throw new Error(`structuredOutput is missing a model.`);
2145
2454
  }
2146
2455
  const { messages, systemPrompt } = await options.context.resolve({
2147
2456
  renderer: new XmlRenderer()
@@ -2151,37 +2460,74 @@ function structuredOutput(options) {
2151
2460
  providerOptions: options.providerOptions,
2152
2461
  model: options.model,
2153
2462
  system: systemPrompt,
2154
- messages: convertToModelMessages(messages),
2155
- stopWhen: stepCountIs(25),
2463
+ experimental_repairToolCall: repairToolCall,
2464
+ messages: await convertToModelMessages(messages),
2465
+ stopWhen: stepCountIs(50),
2156
2466
  experimental_transform: config?.transform ?? smoothStream(),
2157
2467
  experimental_context: contextVariables,
2158
- experimental_output: Output.object({ schema: options.schema })
2468
+ output: Output.object({ schema: options.schema }),
2469
+ tools: options.tools
2159
2470
  });
2160
2471
  }
2161
2472
  };
2162
2473
  }
2474
+ var repairToolCall = async ({
2475
+ toolCall,
2476
+ tools: tools3,
2477
+ inputSchema,
2478
+ error
2479
+ }) => {
2480
+ console.log(
2481
+ `Debug: ${chalk2.yellow("RepairingToolCall")}: ${toolCall.toolName}`,
2482
+ error.name
2483
+ );
2484
+ if (NoSuchToolError.isInstance(error)) {
2485
+ return null;
2486
+ }
2487
+ const tool3 = tools3[toolCall.toolName];
2488
+ const { output } = await generateText({
2489
+ model: groq("openai/gpt-oss-20b"),
2490
+ output: Output.object({ schema: tool3.inputSchema }),
2491
+ prompt: [
2492
+ `The model tried to call the tool "${toolCall.toolName}" with the following inputs:`,
2493
+ JSON.stringify(toolCall.input),
2494
+ `The tool accepts the following schema:`,
2495
+ JSON.stringify(inputSchema(toolCall)),
2496
+ "Please fix the inputs."
2497
+ ].join("\n")
2498
+ });
2499
+ return { ...toolCall, input: JSON.stringify(output) };
2500
+ };
2163
2501
 
2164
2502
  // packages/text2sql/src/lib/agents/explainer.agent.ts
2165
- import { groq } from "@ai-sdk/groq";
2166
- import dedent from "dedent";
2503
+ import { groq as groq2 } from "@ai-sdk/groq";
2167
2504
  import z from "zod";
2168
- import { agent as agent2 } from "@deepagents/agent";
2169
- var explainerAgent = agent2({
2170
- name: "explainer",
2171
- model: groq("openai/gpt-oss-20b"),
2172
- prompt: (state) => dedent`
2173
- You are an expert SQL tutor.
2174
- Explain the following SQL query in plain English to a non-technical user.
2175
- Focus on the intent and logic, not the syntax.
2176
-
2177
- <sql>
2178
- ${state?.sql}
2179
- </sql>
2180
- `,
2181
- output: z.object({
2182
- explanation: z.string().describe("The explanation of the SQL query.")
2183
- })
2505
+ var outputSchema = z.object({
2506
+ explanation: z.string().describe("The explanation of the SQL query.")
2184
2507
  });
2508
+ async function explainSql(sql) {
2509
+ const context = new ContextEngine({
2510
+ store: new InMemoryContextStore(),
2511
+ chatId: `explainer-${crypto.randomUUID()}`,
2512
+ userId: "system"
2513
+ });
2514
+ context.set(
2515
+ persona({
2516
+ name: "explainer",
2517
+ role: "You are an expert SQL tutor.",
2518
+ objective: "Explain SQL queries in plain English that non-technical users understand"
2519
+ }),
2520
+ fragment("sql", sql),
2521
+ fragment("task", "Focus on the intent and logic, not the syntax."),
2522
+ user("Explain this SQL query in plain English to a non-technical user.")
2523
+ );
2524
+ const explainerOutput = structuredOutput({
2525
+ model: groq2("openai/gpt-oss-20b"),
2526
+ context,
2527
+ schema: outputSchema
2528
+ });
2529
+ return explainerOutput.generate();
2530
+ }
2185
2531
 
2186
2532
  // packages/text2sql/src/lib/agents/developer.agent.ts
2187
2533
  var tools = {
@@ -2227,7 +2573,7 @@ var tools = {
2227
2573
  * Get plain-English explanation of a SQL query.
2228
2574
  */
2229
2575
  explain_sql: tool({
2230
- description: dedent2`
2576
+ description: dedent`
2231
2577
  Get a plain-English explanation of a SQL query.
2232
2578
  Use this to help the user understand what a query does.
2233
2579
 
@@ -2237,17 +2583,15 @@ var tools = {
2237
2583
  sql: z2.string().min(1).describe("The SQL query to explain")
2238
2584
  }),
2239
2585
  execute: async ({ sql }) => {
2240
- const { experimental_output } = await generate(explainerAgent, [], {
2241
- sql
2242
- });
2243
- return { explanation: experimental_output.explanation };
2586
+ return explainSql(sql);
2244
2587
  }
2245
2588
  })
2246
2589
  };
2247
2590
  var fragments = [
2248
2591
  persona({
2249
2592
  name: "developer_assistant",
2250
- role: "You are an expert SQL developer assistant helping power users build and refine queries."
2593
+ role: "You are an expert SQL developer assistant helping power users build and refine queries.",
2594
+ objective: "Help power users build and refine SQL queries with precision and clarity"
2251
2595
  }),
2252
2596
  hint("Be transparent: show the SQL you generate before explaining it"),
2253
2597
  hint("Be precise: provide exact column names and table references"),
@@ -2257,137 +2601,442 @@ var fragments = [
2257
2601
  ];
2258
2602
  var developer_agent_default = { tools, fragments };
2259
2603
 
2260
- // packages/text2sql/src/lib/agents/suggestions.agents.ts
2261
- import { groq as groq2 } from "@ai-sdk/groq";
2262
- import dedent3 from "dedent";
2263
- import z3 from "zod";
2264
- import { agent as agent3, thirdPersonPrompt } from "@deepagents/agent";
2265
- var suggestionsAgent = agent3({
2266
- name: "text2sql-suggestions",
2267
- model: groq2("openai/gpt-oss-20b"),
2268
- output: z3.object({
2269
- suggestions: z3.array(
2270
- z3.object({
2271
- question: z3.string().describe("A complex, high-impact business question."),
2272
- sql: z3.string().describe("The SQL statement needed to answer the question."),
2273
- businessValue: z3.string().describe("Why the question matters to stakeholders.")
2274
- })
2275
- ).min(1).max(5).describe("A set of up to two advanced question + SQL pairs.")
2276
- }),
2277
- prompt: (state) => {
2278
- return dedent3`
2279
- ${thirdPersonPrompt()}
2280
-
2281
- <identity>
2282
- You are a senior analytics strategist who proposes ambitious business questions
2283
- and drafts the SQL needed to answer them. You specialize in identifying ideas
2284
- that combine multiple tables, apply segmentation or time analysis, and surface
2285
- metrics that drive executive decisions.
2286
- </identity>
2287
-
2288
-
2289
- <instructions>
2290
- - Recommend one or two UNIQUE questions that go beyond simple counts or listings.
2291
- - Favor questions that require joins, aggregates, time comparisons, cohort analysis,
2292
- or window functions.
2293
- - For each question, explain the business reason stakeholders care about it.
2294
- - Provide the complete SQL query that could answer the question in the given schema.
2295
- - Keep result sets scoped with LIMIT clauses (max 50 rows) when returning raw rows.
2296
- - Ensure table/column names match the provided schema exactly.
2297
- - Use columns marked [LowCardinality: ...] to identify meaningful categorical filters or segmentations.
2298
- - Leverage table [rows / size] hints to determine whether to aggregate (large tables) or inspect detailed data (tiny tables).
2299
- - Reference PK/Indexed annotations and the Indexes list to recommend queries that use efficient join/filter paths.
2300
- - Column annotations may expose ranges/null percentages—use them to suggest realistic thresholds or quality checks.
2301
- - Consult <relationship_examples> to anchor your recommendations in the actual join paths between tables.
2302
- - Output only information grounded in the schema/context provided.
2303
- </instructions>
2604
+ // packages/text2sql/src/lib/agents/result-tools.ts
2605
+ import { createBashTool as createBashTool2 } from "bash-tool";
2606
+ import chalk3 from "chalk";
2607
+ import {
2608
+ Bash,
2609
+ MountableFs,
2610
+ OverlayFs,
2611
+ ReadWriteFs,
2612
+ defineCommand as defineCommand2
2613
+ } from "just-bash";
2614
+ import * as fs from "node:fs/promises";
2615
+ import * as path from "node:path";
2616
+ function createCommand(name, subcommands) {
2617
+ const usageLines = Object.entries(subcommands).map(([, def]) => ` ${name} ${def.usage.padEnd(30)} ${def.description}`).join("\n");
2618
+ return defineCommand2(name, async (args, ctx) => {
2619
+ const subcommand = args[0];
2620
+ const restArgs = args.slice(1);
2621
+ if (subcommand && subcommand in subcommands) {
2622
+ return subcommands[subcommand].handler(restArgs, ctx);
2623
+ }
2624
+ return {
2625
+ stdout: "",
2626
+ stderr: `${name}: ${subcommand ? `unknown subcommand '${subcommand}'` : "missing subcommand"}
2304
2627
 
2305
- <response-format>
2306
- Return valid JSON that satisfies the defined output schema.
2307
- </response-format>
2308
- `;
2628
+ Usage:
2629
+ ${usageLines}`,
2630
+ exitCode: 1
2631
+ };
2632
+ });
2633
+ }
2634
+ function validateReadOnly(query) {
2635
+ const upper = query.toUpperCase().trim();
2636
+ if (!upper.startsWith("SELECT") && !upper.startsWith("WITH")) {
2637
+ return { valid: false, error: "only SELECT or WITH queries allowed" };
2309
2638
  }
2310
- });
2311
-
2312
- // packages/text2sql/src/lib/agents/text2sql.agent.ts
2313
- import { groq as groq3 } from "@ai-sdk/groq";
2314
- import { tool as tool2 } from "ai";
2315
- import z4 from "zod";
2316
- import { toState as toState2 } from "@deepagents/agent";
2317
- import { scratchpad_tool } from "@deepagents/toolbox";
2318
- var tools2 = {
2319
- validate_query: tool2({
2320
- description: `Validate SQL query syntax before execution. Use this to check if your SQL is valid before running db_query. This helps catch errors early and allows you to correct the query if needed.`,
2321
- inputSchema: z4.object({
2322
- sql: z4.string().describe("The SQL query to validate.")
2323
- }),
2324
- execute: async ({ sql }, options) => {
2325
- const state = toState2(options);
2326
- const result = await state.adapter.validate(sql);
2327
- if (typeof result === "string") {
2328
- return `Validation Error: ${result}`;
2639
+ return { valid: true };
2640
+ }
2641
+ function createSqlCommand(adapter) {
2642
+ return createCommand("sql", {
2643
+ run: {
2644
+ usage: 'run "SELECT ..."',
2645
+ description: "Execute query and store results",
2646
+ handler: async (args, ctx) => {
2647
+ const query = args.join(" ").trim();
2648
+ if (!query) {
2649
+ return {
2650
+ stdout: "",
2651
+ stderr: "sql run: no query provided",
2652
+ exitCode: 1
2653
+ };
2654
+ }
2655
+ const validation = validateReadOnly(query);
2656
+ if (!validation.valid) {
2657
+ return {
2658
+ stdout: "",
2659
+ stderr: `sql run: ${validation.error}`,
2660
+ exitCode: 1
2661
+ };
2662
+ }
2663
+ try {
2664
+ const rows = await adapter.execute(query);
2665
+ const rowsArray = Array.isArray(rows) ? rows : [];
2666
+ const filePath = `/results/${crypto.randomUUID()}.json`;
2667
+ await ctx.fs.writeFile(filePath, JSON.stringify(rowsArray, null, 2));
2668
+ const columns = rowsArray.length > 0 ? Object.keys(rowsArray[0]) : [];
2669
+ return {
2670
+ stdout: [
2671
+ filePath,
2672
+ `columns: ${columns.join(", ") || "(none)"}`,
2673
+ `rows: ${rowsArray.length}`
2674
+ ].join("\n") + "\n",
2675
+ stderr: "",
2676
+ exitCode: 0
2677
+ };
2678
+ } catch (error) {
2679
+ return {
2680
+ stdout: "",
2681
+ stderr: `sql run: ${error instanceof Error ? error.message : String(error)}`,
2682
+ exitCode: 1
2683
+ };
2684
+ }
2329
2685
  }
2330
- return "Query is valid.";
2331
- }
2332
- }),
2333
- db_query: tool2({
2334
- description: `Internal tool to fetch data from the store's database. Write a SQL query to retrieve the information needed to answer the user's question. The results will be returned as data that you can then present to the user in natural language.`,
2335
- inputSchema: z4.object({
2336
- reasoning: z4.string().describe(
2337
- "Your reasoning for why this SQL query is relevant to the user request."
2338
- ),
2339
- sql: z4.string().min(1, { message: "SQL query cannot be empty." }).refine(
2340
- (sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
2341
- {
2342
- message: "Only read-only SELECT or WITH queries are allowed."
2686
+ },
2687
+ validate: {
2688
+ usage: 'validate "SELECT ..."',
2689
+ description: "Validate query syntax",
2690
+ handler: (args) => {
2691
+ const query = args.join(" ").trim();
2692
+ if (!query) {
2693
+ return {
2694
+ stdout: "",
2695
+ stderr: "sql validate: no query provided",
2696
+ exitCode: 1
2697
+ };
2343
2698
  }
2344
- ).describe("The SQL query to execute against the database.")
2345
- }),
2346
- execute: ({ sql }, options) => {
2347
- const state = toState2(options);
2348
- return state.adapter.execute(sql);
2699
+ const validation = validateReadOnly(query);
2700
+ if (!validation.valid) {
2701
+ return {
2702
+ stdout: "",
2703
+ stderr: `sql validate: ${validation.error}`,
2704
+ exitCode: 1
2705
+ };
2706
+ }
2707
+ return {
2708
+ stdout: "valid\n",
2709
+ stderr: "",
2710
+ exitCode: 0
2711
+ };
2712
+ }
2713
+ }
2714
+ });
2715
+ }
2716
+ async function createResultTools(options) {
2717
+ const { adapter, chatId, messageId } = options;
2718
+ const sqlCommand = createSqlCommand(adapter);
2719
+ const chatDir = path.join(process.cwd(), "artifacts", chatId);
2720
+ const resultsDir = path.join(chatDir, messageId, "results");
2721
+ await fs.mkdir(resultsDir, { recursive: true });
2722
+ const filesystem = new MountableFs({
2723
+ base: new OverlayFs({ root: process.cwd() }),
2724
+ mounts: [
2725
+ {
2726
+ mountPoint: "/results",
2727
+ filesystem: new ReadWriteFs({ root: resultsDir })
2728
+ },
2729
+ {
2730
+ mountPoint: "/artifacts",
2731
+ filesystem: new ReadWriteFs({ root: chatDir })
2732
+ }
2733
+ ]
2734
+ });
2735
+ const bashInstance = new Bash({
2736
+ customCommands: [sqlCommand],
2737
+ fs: filesystem
2738
+ });
2739
+ const { bash, sandbox } = await createBashTool2({
2740
+ sandbox: bashInstance,
2741
+ destination: "/",
2742
+ uploadDirectory: {
2743
+ source: process.cwd(),
2744
+ include: "packages/text2sql/src/skills/**/*.md"
2745
+ },
2746
+ onBeforeBashCall: ({ command }) => {
2747
+ console.log(chalk3.cyan(`[onBeforeBashCall]: ${command}`));
2748
+ return { command };
2749
+ },
2750
+ onAfterBashCall: ({ result }) => {
2751
+ if (result.exitCode !== 0) {
2752
+ console.log(chalk3.yellow(`[onAfterBashCall]: ${result.exitCode}`));
2753
+ }
2754
+ return { result };
2755
+ }
2756
+ });
2757
+ return { bash, sandbox };
2758
+ }
2759
+
2760
+ // packages/text2sql/src/lib/agents/sql.agent.ts
2761
+ import { groq as groq3 } from "@ai-sdk/groq";
2762
+ import {
2763
+ APICallError,
2764
+ JSONParseError,
2765
+ NoContentGeneratedError,
2766
+ NoObjectGeneratedError,
2767
+ NoOutputGeneratedError,
2768
+ TypeValidationError
2769
+ } from "ai";
2770
+ import { Console } from "node:console";
2771
+ import { createWriteStream } from "node:fs";
2772
+ import pRetry from "p-retry";
2773
+ import z3 from "zod";
2774
+ import "@deepagents/agent";
2775
+ var logger = new Console({
2776
+ stdout: createWriteStream("./sql-agent.log", { flags: "a" }),
2777
+ stderr: createWriteStream("./sql-agent-error.log", { flags: "a" }),
2778
+ inspectOptions: { depth: null }
2779
+ });
2780
+ function extractSql(output) {
2781
+ const match = output.match(/```sql\n?([\s\S]*?)```/);
2782
+ return match ? match[1].trim() : output.trim();
2783
+ }
2784
+ var marker = Symbol("SQLValidationError");
2785
+ var SQLValidationError = class _SQLValidationError extends Error {
2786
+ [marker];
2787
+ constructor(message2) {
2788
+ super(message2);
2789
+ this.name = "SQLValidationError";
2790
+ this[marker] = true;
2791
+ }
2792
+ static isInstance(error) {
2793
+ return error instanceof _SQLValidationError && error[marker] === true;
2794
+ }
2795
+ };
2796
+ var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
2797
+ constructor(message2) {
2798
+ super(message2);
2799
+ this.name = "UnanswerableSQLError";
2800
+ }
2801
+ static isInstance(error) {
2802
+ return error instanceof _UnanswerableSQLError;
2803
+ }
2804
+ };
2805
+ async function toSql(options) {
2806
+ const { maxRetries = 3 } = options;
2807
+ return withRetry(
2808
+ async (attemptNumber, errors, attempts) => {
2809
+ const context = new ContextEngine({
2810
+ store: new InMemoryContextStore(),
2811
+ chatId: `sql-gen-${crypto.randomUUID()}`,
2812
+ userId: "system"
2813
+ });
2814
+ context.set(
2815
+ persona({
2816
+ name: "Freya",
2817
+ role: "You are an expert SQL query generator. You translate natural language questions into precise, efficient SQL queries based on the provided database schema.",
2818
+ objective: "Translate natural language questions into precise, efficient SQL queries"
2819
+ }),
2820
+ ...options.instructions,
2821
+ ...options.schemaFragments
2822
+ );
2823
+ if (errors.length) {
2824
+ context.set(
2825
+ user(options.input),
2826
+ user(
2827
+ `<validation_error>Your previous SQL query had the following error: ${errors.at(-1)?.message}. Please fix the query.</validation_error>`
2828
+ )
2829
+ );
2830
+ } else {
2831
+ context.set(user(options.input));
2832
+ }
2833
+ const sqlOutput = structuredOutput({
2834
+ model: options.model ?? groq3("openai/gpt-oss-20b"),
2835
+ context,
2836
+ schema: z3.union([
2837
+ z3.object({
2838
+ sql: z3.string().describe("The SQL query that answers the question"),
2839
+ reasoning: z3.string().optional().describe("The reasoning steps taken to generate the SQL")
2840
+ }),
2841
+ z3.object({
2842
+ error: z3.string().describe(
2843
+ "Error message explaining why the question cannot be answered with the given schema"
2844
+ )
2845
+ })
2846
+ ])
2847
+ });
2848
+ const output = await sqlOutput.generate();
2849
+ if ("error" in output) {
2850
+ throw new UnanswerableSQLError(output.error);
2851
+ }
2852
+ const sql = extractSql(output.sql);
2853
+ const validationError = await options.adapter.validate(sql);
2854
+ if (validationError) {
2855
+ throw new SQLValidationError(validationError);
2856
+ }
2857
+ return {
2858
+ attempts,
2859
+ sql,
2860
+ errors: errors.length ? errors.map(formatErrorMessage) : void 0
2861
+ };
2862
+ },
2863
+ { retries: maxRetries - 1 }
2864
+ );
2865
+ }
2866
+ function formatErrorMessage(error) {
2867
+ if (APICallError.isInstance(error)) {
2868
+ if (error.message.startsWith("Failed to validate JSON")) {
2869
+ return `Schema validation failed: ${error.message}`;
2870
+ }
2871
+ return error.message;
2872
+ }
2873
+ if (SQLValidationError.isInstance(error)) {
2874
+ return `SQL Validation Error: ${error.message}`;
2875
+ }
2876
+ return error.message;
2877
+ }
2878
+ async function withRetry(computation, options = { retries: 3 }) {
2879
+ const errors = [];
2880
+ let attempts = 0;
2881
+ return pRetry(
2882
+ (attemptNumber) => {
2883
+ return computation(attemptNumber, errors, ++attempts);
2884
+ },
2885
+ {
2886
+ retries: options.retries,
2887
+ shouldRetry: (context) => {
2888
+ if (UnanswerableSQLError.isInstance(context.error)) {
2889
+ return false;
2890
+ }
2891
+ if (SQLValidationError.isInstance(context.error)) {
2892
+ return true;
2893
+ }
2894
+ console.log({
2895
+ NoObjectGeneratedError: NoObjectGeneratedError.isInstance(
2896
+ context.error
2897
+ ),
2898
+ NoOutputGeneratedError: NoOutputGeneratedError.isInstance(
2899
+ context.error
2900
+ ),
2901
+ APICallError: APICallError.isInstance(context.error),
2902
+ JSONParseError: JSONParseError.isInstance(context.error),
2903
+ TypeValidationError: TypeValidationError.isInstance(context.error),
2904
+ NoContentGeneratedError: NoContentGeneratedError.isInstance(
2905
+ context.error
2906
+ )
2907
+ });
2908
+ return APICallError.isInstance(context.error) || JSONParseError.isInstance(context.error) || TypeValidationError.isInstance(context.error) || NoObjectGeneratedError.isInstance(context.error) || NoOutputGeneratedError.isInstance(context.error) || NoContentGeneratedError.isInstance(context.error);
2909
+ },
2910
+ onFailedAttempt(context) {
2911
+ logger.error(`toSQL`, context.error);
2912
+ console.log(
2913
+ `Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`
2914
+ );
2915
+ errors.push(context.error);
2916
+ }
2917
+ }
2918
+ );
2919
+ }
2920
+
2921
+ // packages/text2sql/src/lib/agents/suggestions.agents.ts
2922
+ import { groq as groq4 } from "@ai-sdk/groq";
2923
+ import dedent2 from "dedent";
2924
+ import z4 from "zod";
2925
+ import { agent as agent2, thirdPersonPrompt } from "@deepagents/agent";
2926
+ var suggestionsAgent = agent2({
2927
+ name: "text2sql-suggestions",
2928
+ model: groq4("openai/gpt-oss-20b"),
2929
+ output: z4.object({
2930
+ suggestions: z4.array(
2931
+ z4.object({
2932
+ question: z4.string().describe("A complex, high-impact business question."),
2933
+ sql: z4.string().describe("The SQL statement needed to answer the question."),
2934
+ businessValue: z4.string().describe("Why the question matters to stakeholders.")
2935
+ })
2936
+ ).min(1).max(5).describe("A set of up to two advanced question + SQL pairs.")
2937
+ }),
2938
+ prompt: (state) => {
2939
+ return dedent2`
2940
+ ${thirdPersonPrompt()}
2941
+
2942
+ <identity>
2943
+ You are a senior analytics strategist who proposes ambitious business questions
2944
+ and drafts the SQL needed to answer them. You specialize in identifying ideas
2945
+ that combine multiple tables, apply segmentation or time analysis, and surface
2946
+ metrics that drive executive decisions.
2947
+ </identity>
2948
+
2949
+
2950
+ <instructions>
2951
+ - Recommend one or two UNIQUE questions that go beyond simple counts or listings.
2952
+ - Favor questions that require joins, aggregates, time comparisons, cohort analysis,
2953
+ or window functions.
2954
+ - For each question, explain the business reason stakeholders care about it.
2955
+ - Provide the complete SQL query that could answer the question in the given schema.
2956
+ - Keep result sets scoped with LIMIT clauses (max 50 rows) when returning raw rows.
2957
+ - Ensure table/column names match the provided schema exactly.
2958
+ - Use columns marked [LowCardinality: ...] to identify meaningful categorical filters or segmentations.
2959
+ - Leverage table [rows / size] hints to determine whether to aggregate (large tables) or inspect detailed data (tiny tables).
2960
+ - Reference PK/Indexed annotations and the Indexes list to recommend queries that use efficient join/filter paths.
2961
+ - Column annotations may expose ranges/null percentages—use them to suggest realistic thresholds or quality checks.
2962
+ - Consult <relationship_examples> to anchor your recommendations in the actual join paths between tables.
2963
+ - Output only information grounded in the schema/context provided.
2964
+ </instructions>
2965
+
2966
+ <response-format>
2967
+ Return valid JSON that satisfies the defined output schema.
2968
+ </response-format>
2969
+ `;
2970
+ }
2971
+ });
2972
+
2973
+ // packages/text2sql/src/lib/agents/text2sql.agent.ts
2974
+ import { tool as tool2 } from "ai";
2975
+ import z5 from "zod";
2976
+ import { toState as toState2 } from "@deepagents/agent";
2977
+ import { scratchpad_tool } from "@deepagents/toolbox";
2978
+ var tools2 = {
2979
+ validate_query: tool2({
2980
+ description: `Validate SQL query syntax before execution. Use this to check if your SQL is valid before running db_query. This helps catch errors early and allows you to correct the query if needed.`,
2981
+ inputSchema: z5.object({
2982
+ sql: z5.string().describe("The SQL query to validate.")
2983
+ }),
2984
+ execute: async ({ sql }, options) => {
2985
+ const state = toState2(options);
2986
+ const result = await state.adapter.validate(sql);
2987
+ if (typeof result === "string") {
2988
+ return `Validation Error: ${result}`;
2989
+ }
2990
+ return "Query is valid.";
2991
+ }
2992
+ }),
2993
+ db_query: tool2({
2994
+ description: `Internal tool to fetch data from the store's database. Write a SQL query to retrieve the information needed to answer the user's question. The results will be returned as data that you can then present to the user in natural language.`,
2995
+ inputSchema: z5.object({
2996
+ reasoning: z5.string().describe(
2997
+ "Your reasoning for why this SQL query is relevant to the user request."
2998
+ ),
2999
+ sql: z5.string().min(1, { message: "SQL query cannot be empty." }).refine(
3000
+ (sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
3001
+ {
3002
+ message: "Only read-only SELECT or WITH queries are allowed."
3003
+ }
3004
+ ).describe("The SQL query to execute against the database.")
3005
+ }),
3006
+ execute: ({ sql }, options) => {
3007
+ const state = toState2(options);
3008
+ return state.adapter.execute(sql);
2349
3009
  }
2350
3010
  }),
2351
3011
  scratchpad: scratchpad_tool
2352
3012
  };
2353
- var t_a_g = agent({
2354
- model: groq3("openai/gpt-oss-20b"),
2355
- tools: tools2,
2356
- name: "text2sql"
2357
- // prompt: (state) => {
2358
- // return `
2359
- // ${state?.teachings || ''}
2360
- // ${state?.introspection || ''}
2361
- // ${chainOfThoughtPrompt}
2362
- // ${fewShotExamples}
2363
- // `;
2364
- // },
2365
- });
2366
3013
 
2367
3014
  // packages/text2sql/src/lib/checkpoint.ts
2368
3015
  import { createHash } from "node:crypto";
2369
3016
  import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2370
3017
  import pLimit from "p-limit";
2371
3018
  var Checkpoint = class _Checkpoint {
2372
- constructor(path2, configHash, points) {
2373
- this.path = path2;
2374
- this.configHash = configHash;
3019
+ points;
3020
+ path;
3021
+ configHash;
3022
+ constructor(path3, configHash, points) {
2375
3023
  this.points = points;
3024
+ this.path = path3;
3025
+ this.configHash = configHash;
2376
3026
  }
2377
- points;
2378
3027
  /**
2379
3028
  * Load checkpoint from file, or return empty checkpoint if none exists.
2380
3029
  * Handles corrupted files and config changes gracefully.
2381
3030
  */
2382
3031
  static async load(options) {
2383
- const { path: path2, configHash } = options;
2384
- if (existsSync(path2)) {
3032
+ const { path: path3, configHash } = options;
3033
+ if (existsSync(path3)) {
2385
3034
  try {
2386
- const content = readFileSync(path2, "utf-8");
3035
+ const content = readFileSync(path3, "utf-8");
2387
3036
  const file = JSON.parse(content);
2388
3037
  if (configHash && file.configHash && file.configHash !== configHash) {
2389
3038
  console.log("\u26A0 Config changed, starting fresh");
2390
- return new _Checkpoint(path2, configHash, {});
3039
+ return new _Checkpoint(path3, configHash, {});
2391
3040
  }
2392
3041
  const points = file.points ?? {};
2393
3042
  const totalEntries = Object.values(points).reduce(
@@ -2395,14 +3044,14 @@ var Checkpoint = class _Checkpoint {
2395
3044
  0
2396
3045
  );
2397
3046
  console.log(`\u2713 Resuming from checkpoint (${totalEntries} entries)`);
2398
- return new _Checkpoint(path2, configHash, points);
3047
+ return new _Checkpoint(path3, configHash, points);
2399
3048
  } catch {
2400
3049
  console.log("\u26A0 Checkpoint corrupted, starting fresh");
2401
- return new _Checkpoint(path2, configHash, {});
3050
+ return new _Checkpoint(path3, configHash, {});
2402
3051
  }
2403
3052
  }
2404
3053
  console.log("Starting new checkpoint");
2405
- return new _Checkpoint(path2, configHash, {});
3054
+ return new _Checkpoint(path3, configHash, {});
2406
3055
  }
2407
3056
  /**
2408
3057
  * Run a single computation with checkpointing.
@@ -2487,14 +3136,16 @@ function hash(value) {
2487
3136
  return createHash("md5").update(JSON.stringify(value)).digest("hex");
2488
3137
  }
2489
3138
  var Point = class {
3139
+ #cache;
3140
+ data;
3141
+ persist;
2490
3142
  constructor(data, persist) {
2491
- this.data = data;
2492
- this.persist = persist;
2493
3143
  this.#cache = new Map(
2494
3144
  data.entries.map((e) => [e.inputHash, e.output])
2495
3145
  );
3146
+ this.data = data;
3147
+ this.persist = persist;
2496
3148
  }
2497
- #cache;
2498
3149
  /**
2499
3150
  * Execute computation if input wasn't processed before.
2500
3151
  * Returns cached output if input hash exists, otherwise executes, saves, and returns.
@@ -2534,12 +3185,12 @@ import { createHash as createHash2 } from "node:crypto";
2534
3185
  import { existsSync as existsSync2 } from "node:fs";
2535
3186
  import { readFile, writeFile } from "node:fs/promises";
2536
3187
  import { tmpdir } from "node:os";
2537
- import path from "node:path";
3188
+ import path2 from "node:path";
2538
3189
  var FileCache = class {
2539
3190
  path;
2540
3191
  constructor(watermark, extension = ".txt") {
2541
3192
  const hash2 = createHash2("md5").update(watermark).digest("hex");
2542
- this.path = path.join(tmpdir(), `text2sql-${hash2}${extension}`);
3193
+ this.path = path2.join(tmpdir(), `text2sql-${hash2}${extension}`);
2543
3194
  }
2544
3195
  async get() {
2545
3196
  if (existsSync2(this.path)) {
@@ -2567,302 +3218,328 @@ var JsonCache = class extends FileCache {
2567
3218
  }
2568
3219
  };
2569
3220
 
2570
- // packages/text2sql/src/lib/sql.ts
2571
- import {
2572
- APICallError as APICallError2,
2573
- InvalidToolInputError,
2574
- NoSuchToolError,
2575
- ToolCallRepairError,
2576
- generateId as generateId3
2577
- } from "ai";
2578
- import "@deepagents/agent";
2579
-
2580
- // packages/text2sql/src/lib/agents/sql.agent.ts
2581
- import { groq as groq4 } from "@ai-sdk/groq";
2582
- import {
2583
- APICallError,
2584
- JSONParseError,
2585
- NoContentGeneratedError,
2586
- NoObjectGeneratedError,
2587
- NoOutputGeneratedError,
2588
- TypeValidationError,
2589
- defaultSettingsMiddleware,
2590
- wrapLanguageModel
2591
- } from "ai";
2592
- import { Console } from "node:console";
2593
- import { createWriteStream } from "node:fs";
2594
- import pRetry from "p-retry";
2595
- import z5 from "zod";
2596
- import "@deepagents/agent";
2597
- var logger = new Console({
2598
- stdout: createWriteStream("./sql-agent.log", { flags: "a" }),
2599
- stderr: createWriteStream("./sql-agent-error.log", { flags: "a" }),
2600
- inspectOptions: { depth: null }
2601
- });
2602
- var RETRY_TEMPERATURES = [0, 0.2, 0.3];
2603
- function extractSql(output) {
2604
- const match = output.match(/```sql\n?([\s\S]*?)```/);
2605
- return match ? match[1].trim() : output.trim();
2606
- }
2607
- var marker = Symbol("SQLValidationError");
2608
- var SQLValidationError = class _SQLValidationError extends Error {
2609
- [marker];
2610
- constructor(message2) {
2611
- super(message2);
2612
- this.name = "SQLValidationError";
2613
- this[marker] = true;
2614
- }
2615
- static isInstance(error) {
2616
- return error instanceof _SQLValidationError && error[marker] === true;
2617
- }
2618
- };
2619
- var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
2620
- constructor(message2) {
2621
- super(message2);
2622
- this.name = "UnanswerableSQLError";
2623
- }
2624
- static isInstance(error) {
2625
- return error instanceof _UnanswerableSQLError;
2626
- }
2627
- };
2628
- async function toSql(options) {
2629
- const { maxRetries = 3 } = options;
2630
- return withRetry(
2631
- async (attemptNumber, errors, attempts) => {
2632
- const context = new ContextEngine({
2633
- store: new InMemoryContextStore(),
2634
- chatId: `sql-gen-${crypto.randomUUID()}`
2635
- });
2636
- context.set(
2637
- persona({
2638
- name: "Freya",
2639
- role: "You are an expert SQL query generator. You translate natural language questions into precise, efficient SQL queries based on the provided database schema."
2640
- }),
2641
- ...options.instructions,
2642
- ...options.schemaFragments
2643
- );
2644
- if (errors.length) {
2645
- context.set(
2646
- user(options.input),
2647
- user(
2648
- `<validation_error>Your previous SQL query had the following error: ${errors.at(-1)?.message}. Please fix the query.</validation_error>`
2649
- )
2650
- );
2651
- } else {
2652
- context.set(user(options.input));
2653
- }
2654
- const sqlOutput = structuredOutput({
2655
- name: "text2sql",
2656
- model: wrapLanguageModel({
2657
- model: options.model ?? groq4("openai/gpt-oss-20b"),
2658
- middleware: defaultSettingsMiddleware({
2659
- settings: {
2660
- temperature: RETRY_TEMPERATURES[attemptNumber - 1] ?? 0.3,
2661
- topP: 1
2662
- }
2663
- })
2664
- }),
2665
- context,
2666
- schema: z5.union([
2667
- z5.object({
2668
- sql: z5.string().describe("The SQL query that answers the question"),
2669
- reasoning: z5.string().optional().describe("The reasoning steps taken to generate the SQL")
3221
+ // packages/text2sql/src/lib/instructions.ts
3222
+ function reasoningFramework() {
3223
+ return [
3224
+ role(
3225
+ "You are a very strong reasoner and planner. Use these critical instructions to structure your plans, thoughts, and responses."
3226
+ ),
3227
+ fragment(
3228
+ "Meta-cognitive reasoning framework",
3229
+ hint(
3230
+ "Before taking any action (either tool calls *or* responses to the user), you must proactively, methodically, and independently plan and reason about:"
3231
+ ),
3232
+ // 1) Logical dependencies and constraints
3233
+ principle({
3234
+ title: "Logical dependencies and constraints",
3235
+ description: "Analyze the intended action against the following factors. Resolve conflicts in order of importance:",
3236
+ policies: [
3237
+ policy({
3238
+ rule: "Policy-based rules, mandatory prerequisites, and constraints."
2670
3239
  }),
2671
- z5.object({
2672
- error: z5.string().describe(
2673
- "Error message explaining why the question cannot be answered with the given schema"
2674
- )
3240
+ policy({
3241
+ rule: "Order of operations: Ensure taking an action does not prevent a subsequent necessary action.",
3242
+ policies: [
3243
+ "The user may request actions in a random order, but you may need to reorder operations to maximize successful completion of the task."
3244
+ ]
3245
+ }),
3246
+ policy({
3247
+ rule: "Other prerequisites (information and/or actions needed)."
3248
+ }),
3249
+ policy({ rule: "Explicit user constraints or preferences." })
3250
+ ]
3251
+ }),
3252
+ // 2) Risk assessment
3253
+ principle({
3254
+ title: "Risk assessment",
3255
+ description: "What are the consequences of taking the action? Will the new state cause any future issues?",
3256
+ policies: [
3257
+ "For exploratory tasks (like searches), missing *optional* parameters is a LOW risk. **Prefer calling the tool with the available information over asking the user, unless** your Rule 1 (Logical Dependencies) reasoning determines that optional information is required for a later step in your plan."
3258
+ ]
3259
+ }),
3260
+ // 3) Abductive reasoning and hypothesis exploration
3261
+ principle({
3262
+ title: "Abductive reasoning and hypothesis exploration",
3263
+ description: "At each step, identify the most logical and likely reason for any problem encountered.",
3264
+ policies: [
3265
+ "Look beyond immediate or obvious causes. The most likely reason may not be the simplest and may require deeper inference.",
3266
+ "Hypotheses may require additional research. Each hypothesis may take multiple steps to test.",
3267
+ "Prioritize hypotheses based on likelihood, but do not discard less likely ones prematurely. A low-probability event may still be the root cause."
3268
+ ]
3269
+ }),
3270
+ // 4) Outcome evaluation and adaptability
3271
+ principle({
3272
+ title: "Outcome evaluation and adaptability",
3273
+ description: "Does the previous observation require any changes to your plan?",
3274
+ policies: [
3275
+ "If your initial hypotheses are disproven, actively generate new ones based on the gathered information."
3276
+ ]
3277
+ }),
3278
+ // 5) Information availability
3279
+ principle({
3280
+ title: "Information availability",
3281
+ description: "Incorporate all applicable and alternative sources of information, including:",
3282
+ policies: [
3283
+ "Using available tools and their capabilities",
3284
+ "All policies, rules, checklists, and constraints",
3285
+ "Previous observations and conversation history",
3286
+ "Information only available by asking the user"
3287
+ ]
3288
+ }),
3289
+ // 6) Precision and Grounding
3290
+ principle({
3291
+ title: "Precision and Grounding",
3292
+ description: "Ensure your reasoning is extremely precise and relevant to each exact ongoing situation.",
3293
+ policies: [
3294
+ "Verify your claims by quoting the exact applicable information (including policies) when referring to them."
3295
+ ]
3296
+ }),
3297
+ // 7) Completeness
3298
+ principle({
3299
+ title: "Completeness",
3300
+ description: "Ensure that all requirements, constraints, options, and preferences are exhaustively incorporated into your plan.",
3301
+ policies: [
3302
+ policy({
3303
+ rule: "Resolve conflicts using the order of importance in #1."
3304
+ }),
3305
+ policy({
3306
+ rule: "Avoid premature conclusions: There may be multiple relevant options for a given situation.",
3307
+ policies: [
3308
+ "To check for whether an option is relevant, reason about all information sources from #5.",
3309
+ "You may need to consult the user to even know whether something is applicable. Do not assume it is not applicable without checking."
3310
+ ]
3311
+ }),
3312
+ policy({
3313
+ rule: "Review applicable sources of information from #5 to confirm which are relevant to the current state."
2675
3314
  })
2676
- ])
2677
- });
2678
- const output = await sqlOutput.generate();
2679
- if ("error" in output) {
2680
- throw new UnanswerableSQLError(output.error);
2681
- }
2682
- const sql = extractSql(output.sql);
2683
- const validationError = await options.adapter.validate(sql);
2684
- if (validationError) {
2685
- throw new SQLValidationError(validationError);
2686
- }
2687
- return {
2688
- attempts,
2689
- sql,
2690
- errors: errors.length ? errors.map(formatErrorMessage) : void 0
2691
- };
2692
- },
2693
- { retries: maxRetries - 1 }
2694
- );
2695
- }
2696
- function formatErrorMessage(error) {
2697
- if (APICallError.isInstance(error)) {
2698
- if (error.message.startsWith("Failed to validate JSON")) {
2699
- return `Schema validation failed: ${error.message}`;
2700
- }
2701
- return error.message;
2702
- }
2703
- if (SQLValidationError.isInstance(error)) {
2704
- return `SQL Validation Error: ${error.message}`;
2705
- }
2706
- return error.message;
2707
- }
2708
- async function withRetry(computation, options = { retries: 3 }) {
2709
- const errors = [];
2710
- let attempts = 0;
2711
- return pRetry(
2712
- (attemptNumber) => {
2713
- return computation(attemptNumber, errors, ++attempts);
2714
- },
2715
- {
2716
- retries: options.retries,
2717
- shouldRetry: (context) => {
2718
- if (UnanswerableSQLError.isInstance(context.error)) {
2719
- return false;
2720
- }
2721
- if (SQLValidationError.isInstance(context.error)) {
2722
- return true;
2723
- }
2724
- console.log({
2725
- NoObjectGeneratedError: NoObjectGeneratedError.isInstance(
2726
- context.error
2727
- ),
2728
- NoOutputGeneratedError: NoOutputGeneratedError.isInstance(
2729
- context.error
2730
- ),
2731
- APICallError: APICallError.isInstance(context.error),
2732
- JSONParseError: JSONParseError.isInstance(context.error),
2733
- TypeValidationError: TypeValidationError.isInstance(context.error),
2734
- NoContentGeneratedError: NoContentGeneratedError.isInstance(
2735
- context.error
2736
- )
2737
- });
2738
- return APICallError.isInstance(context.error) || JSONParseError.isInstance(context.error) || TypeValidationError.isInstance(context.error) || NoObjectGeneratedError.isInstance(context.error) || NoOutputGeneratedError.isInstance(context.error) || NoContentGeneratedError.isInstance(context.error);
2739
- },
2740
- onFailedAttempt(context) {
2741
- logger.error(`toSQL`, context.error);
2742
- console.log(
2743
- `Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`
2744
- );
2745
- errors.push(context.error);
2746
- }
2747
- }
2748
- );
3315
+ ]
3316
+ }),
3317
+ // 8) Persistence and patience
3318
+ principle({
3319
+ title: "Persistence and patience",
3320
+ description: "Do not give up unless all the reasoning above is exhausted.",
3321
+ policies: [
3322
+ "Don't be dissuaded by time taken or user frustration.",
3323
+ "This persistence must be intelligent: On *transient* errors (e.g. please try again), you *must* retry **unless an explicit retry limit (e.g., max x tries) has been reached**. If such a limit is hit, you *must* stop. On *other* errors, you must change your strategy or arguments, not repeat the same failed call."
3324
+ ]
3325
+ }),
3326
+ // 9) Inhibit your response
3327
+ principle({
3328
+ title: "Inhibit your response",
3329
+ description: "Only take an action after all the above reasoning is completed. Once you've taken an action, you cannot take it back."
3330
+ })
3331
+ )
3332
+ ];
2749
3333
  }
2750
-
2751
- // packages/text2sql/src/lib/teach/teachings.ts
2752
3334
  function guidelines(options = {}) {
2753
3335
  const { date = "strict" } = options;
2754
3336
  const baseTeachings = [
2755
- // Schema adherence
2756
- hint(
2757
- "Use only tables and columns that exist in the schema. Never reference non-existent entities."
2758
- ),
2759
- hint(
2760
- "If the user asks to show a table or entity without specifying columns, use SELECT *."
2761
- ),
2762
- hint(
2763
- "When showing items associated with another entity, include the item ID and the related details requested."
2764
- ),
2765
- hint(
2766
- 'When asked to "show" items, list them unless the user explicitly asks to count or total.'
2767
- ),
2768
- hint(
2769
- "Use canonical/LowCardinality values verbatim for filtering; [rows/size] hints suggest when to aggregate instead of listing."
2770
- ),
2771
- // Joins and relationships
2772
- hint(
2773
- "Use appropriate JOINs based on the relationships defined in the schema."
2774
- ),
2775
- hint(
2776
- "Favor PK/indexed columns for joins and filters; follow relationship metadata for join direction and cardinality."
2777
- ),
2778
- // Aggregations and calculations
2779
- hint(
2780
- "Apply proper aggregations (COUNT, SUM, AVG, etc.) when the question implies summarization."
2781
- ),
2782
- hint(
2783
- 'When asked "how many X are there" about types/categories/statuses (e.g., "how many statuses are there?"), use COUNT(DISTINCT column). This asks about variety, not row count.'
3337
+ // Include the meta-cognitive reasoning framework
3338
+ ...reasoningFramework(),
3339
+ // Prerequisite policies (must do X before Y)
3340
+ fragment(
3341
+ "Prerequisite policies",
3342
+ policy({
3343
+ rule: "YOU MUST inspect schema structure and available tables",
3344
+ before: "generating ANY SQL query",
3345
+ reason: "NEVER generate SQL without knowing valid tables, columns, and relationships"
3346
+ }),
3347
+ policy({
3348
+ rule: "YOU MUST resolve ambiguous business terms with the user",
3349
+ before: "making ANY assumptions about terminology meaning",
3350
+ reason: "NEVER guess domain-specific language\u2014ask for clarification"
3351
+ }),
3352
+ policy({
3353
+ rule: "YOU MUST validate SQL syntax",
3354
+ before: "executing ANY query against the database",
3355
+ reason: "NEVER execute unvalidated queries"
3356
+ }),
3357
+ policy({
3358
+ rule: "YOU MUST complete ALL reasoning steps",
3359
+ before: "taking ANY tool call or response action",
3360
+ reason: "Once an action is taken, it CANNOT be undone. NO EXCEPTIONS."
3361
+ })
2784
3362
  ),
2785
- hint(
2786
- "Use window functions when the question requires ranking, running totals, or comparisons across rows."
3363
+ // Few-shot: Applying reasoning principles
3364
+ fragment(
3365
+ "Reasoning examples",
3366
+ example({
3367
+ question: "Show me sales last month",
3368
+ answer: `Applying Principle 1 (Logical dependencies):
3369
+ - Need: schema to know which table has sales data
3370
+ - Need: clarify "last month" = calendar month or rolling 30 days?
3371
+
3372
+ Applying Principle 5 (Information availability):
3373
+ - Schema shows: orders table with created_at, total columns
3374
+ - Missing: user's definition of "last month"
3375
+
3376
+ Action: Ask user for date range clarification BEFORE generating SQL.`
3377
+ }),
3378
+ example({
3379
+ question: "Why did my query return no results?",
3380
+ answer: `Applying Principle 3 (Abductive reasoning):
3381
+ - Hypothesis 1 (most likely): Filter too restrictive
3382
+ - Hypothesis 2: Data doesn't exist for that period
3383
+ - Hypothesis 3: JOIN eliminated matching rows
3384
+
3385
+ Testing hypotheses:
3386
+ 1. Remove filters one by one to isolate the issue
3387
+ 2. Check date range actually has data
3388
+ 3. Run subqueries separately to verify each table
3389
+
3390
+ Action: Start with most likely hypothesis, test incrementally. NEVER guess.`
3391
+ }),
3392
+ example({
3393
+ question: "Get me the top customers",
3394
+ answer: `Applying Principle 1 (Logical dependencies):
3395
+ - "Top" is ambiguous\u2014by revenue? by order count? by recency?
3396
+
3397
+ Applying Principle 9 (Inhibition):
3398
+ - MUST NOT generate SQL until "top" is defined
3399
+
3400
+ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or most recent activity?"`
3401
+ })
2787
3402
  ),
2788
- // Query semantics
2789
- hint(
2790
- 'Words like "reach", "reached", "hit" with a value (e.g., "temperature reach 80") mean >= (greater than or equal), not = (exact match).'
3403
+ // Schema adherence - consolidated into clear rules
3404
+ fragment(
3405
+ "Schema adherence",
3406
+ hint(
3407
+ "Use only tables and columns from the schema. For unspecified columns, use SELECT *. When showing related items, include IDs and requested details."
3408
+ ),
3409
+ hint(
3410
+ '"Show" means list items; "count" or "total" means aggregate. Use canonical values verbatim for filtering.'
3411
+ )
2791
3412
  ),
3413
+ // Joins - use relationship metadata
2792
3414
  hint(
2793
- 'For "shared by" two groups or mutually exclusive conditions (e.g., population > 1500 AND < 500), use INTERSECT between separate queries. A single WHERE with contradictory AND returns nothing.'
3415
+ "Use JOINs based on schema relationships. Favor PK/indexed columns; follow relationship metadata for direction and cardinality."
2794
3416
  ),
2795
- hint(
2796
- 'When filtering by a specific value from a joined table (e.g., "students who registered course statistics"), always include that WHERE condition. Do not omit mentioned filters.'
3417
+ // Aggregations - explain the concepts
3418
+ fragment(
3419
+ "Aggregations",
3420
+ hint(
3421
+ "Apply COUNT, SUM, AVG when the question implies summarization. Use window functions for ranking, running totals, or row comparisons."
3422
+ ),
3423
+ explain({
3424
+ concept: "counting variety vs counting rows",
3425
+ explanation: '"How many types/categories/statuses exist?" asks about variety (unique values), not total row count',
3426
+ therefore: "Use COUNT(DISTINCT column) for variety questions"
3427
+ })
2797
3428
  ),
2798
- hint(
2799
- "Handle NULL values appropriately using IS NULL, IS NOT NULL, or COALESCE."
3429
+ // Query semantics - explain concepts and document quirks
3430
+ fragment(
3431
+ "Query interpretation",
3432
+ explain({
3433
+ concept: "threshold language",
3434
+ explanation: 'Words like "reach", "hit", "exceed" with a value imply a threshold being met or passed',
3435
+ therefore: "Translate to >= (greater than or equal), not = (exact match)"
3436
+ }),
3437
+ quirk({
3438
+ issue: "Contradictory WHERE conditions (e.g., value > 100 AND value < 50) return empty results",
3439
+ workaround: 'Use INTERSECT between separate queries when finding items "shared by" groups with mutually exclusive conditions'
3440
+ }),
3441
+ quirk({
3442
+ issue: "NULL values behave unexpectedly in comparisons and aggregations",
3443
+ workaround: "Use IS NULL, IS NOT NULL, or COALESCE() to handle NULLs explicitly"
3444
+ }),
3445
+ hint(
3446
+ "Always include mentioned filters from joined tables in WHERE conditions."
3447
+ )
2800
3448
  ),
2801
- // Style and readability
3449
+ // Style preferences
2802
3450
  styleGuide({
2803
- prefer: 'For table aliases, use the full table name (e.g., "FROM users AS users", "JOIN order_items AS order_items"). For column aliases, use descriptive names that reflect the data (e.g., "COUNT(*) AS total_orders").',
2804
- never: "Use abbreviated table aliases (u, oi, ca) or generic positional aliases (t1, t2, a, b)."
3451
+ prefer: "Full table names as aliases (users AS users). Descriptive column aliases (COUNT(*) AS total_count).",
3452
+ never: "Abbreviated aliases (u, oi) or positional aliases (t1, t2, a, b)."
2805
3453
  }),
2806
3454
  styleGuide({
2807
- prefer: "Summaries should be concise, business-friendly, highlight key comparisons, and add a short helpful follow-up when useful."
3455
+ prefer: "Concise, business-friendly summaries with key comparisons and helpful follow-ups."
2808
3456
  }),
2809
- // Guardrails - Query safety
2810
- guardrail({
2811
- rule: "Generate ONLY valid, executable SQL.",
2812
- reason: "Invalid SQL wastes resources and confuses users.",
2813
- action: "Validate syntax and schema references before returning."
2814
- }),
2815
- guardrail({
2816
- rule: "Only generate SELECT/WITH statements (read-only queries).",
2817
- reason: "Prevents accidental data modification.",
2818
- action: "Never generate INSERT, UPDATE, DELETE, DROP, or other DDL/DML statements."
2819
- }),
2820
- guardrail({
2821
- rule: "Avoid unbounded scans on large tables.",
2822
- reason: "Protects performance and prevents runaway queries.",
2823
- action: "Ensure filters are applied on indexed columns before querying broad fact tables."
2824
- }),
2825
- guardrail({
2826
- rule: "Do not add LIMIT unless explicitly requested.",
2827
- action: 'Only add LIMIT when user explicitly asks for "top N", "first N", or similar. Do NOT add LIMIT for "list all", "show all", or simple "list" queries.',
2828
- reason: "Adding arbitrary limits changes query semantics."
2829
- }),
2830
- guardrail({
2831
- rule: "Add ORDER BY where appropriate for deterministic results.",
2832
- reason: "Ensures consistent query output.",
2833
- action: "Include ORDER BY when results have a natural ordering or when combined with LIMIT."
2834
- }),
2835
- guardrail({
2836
- rule: "Prevent cartesian or guesswork joins.",
2837
- reason: "Protect correctness and performance.",
2838
- action: "If join keys are missing or unclear, inspect relationships and ask for the intended join path before executing."
3457
+ // Safety guardrails - consolidated
3458
+ fragment(
3459
+ "Query safety",
3460
+ guardrail({
3461
+ rule: "Generate only valid, executable SELECT/WITH statements.",
3462
+ reason: "Read-only access prevents data modification.",
3463
+ action: "Never generate INSERT, UPDATE, DELETE, DROP, or DDL statements."
3464
+ }),
3465
+ guardrail({
3466
+ rule: "Avoid unbounded scans and cartesian joins.",
3467
+ reason: "Protects performance and correctness.",
3468
+ action: "Apply filters on indexed columns. If join keys are unclear, ask for clarification."
3469
+ }),
3470
+ guardrail({
3471
+ rule: "Preserve query semantics.",
3472
+ reason: "Arbitrary modifications change results.",
3473
+ action: 'Only add LIMIT for explicit "top N" requests. Add ORDER BY for deterministic results.'
3474
+ }),
3475
+ guardrail({
3476
+ rule: "Seek clarification for genuine ambiguity.",
3477
+ reason: "Prevents incorrect assumptions.",
3478
+ action: "Ask a focused question before guessing."
3479
+ })
3480
+ ),
3481
+ clarification({
3482
+ when: "Ambiguous ranking language (top, best, active) without a metric.",
3483
+ ask: "Clarify the ranking metric or definition.",
3484
+ reason: "Ensures correct aggregation and ordering."
2839
3485
  }),
2840
- guardrail({
2841
- rule: "Ensure the query is optimized for the schema.",
2842
- reason: "Better performance and resource usage.",
2843
- action: "Use indexed columns for filtering, avoid SELECT * on large joins, prefer specific column selection when appropriate."
3486
+ hint(
3487
+ 'Use sample cell values from schema hints to match exact casing and format in WHERE conditions (e.g., "Male" vs "male" vs "M").'
3488
+ ),
3489
+ workflow({
3490
+ task: "SQL generation",
3491
+ steps: [
3492
+ "Schema linking: identify which tables and columns are mentioned or implied by the question.",
3493
+ "Check if column names match question terms (e.g., Total_X). Select directly if match found.",
3494
+ "Identify SQL patterns needed: aggregation, segmentation, time range, ranking.",
3495
+ "Select tables and relationships. Note lookup tables and filter values from schema.",
3496
+ "Plan join/filter/aggregation order based on table sizes and indexes.",
3497
+ "Generate SQL that answers the question.",
3498
+ "Verify: mentally translate SQL back to natural language. Does it match the original question?"
3499
+ ]
2844
3500
  }),
2845
- guardrail({
2846
- rule: "When facing genuine ambiguity with multiple valid interpretations, seek clarification.",
2847
- reason: "Prevents incorrect assumptions in edge cases.",
2848
- action: "Ask a focused clarifying question before proceeding with a guess."
3501
+ workflow({
3502
+ task: "Error recovery",
3503
+ triggers: ["SQL error", "query failed", "execution error"],
3504
+ steps: [
3505
+ "Classify the error type: syntax error, missing join, wrong aggregation, invalid column, type mismatch.",
3506
+ "For syntax errors: check SQL keywords, quotes, parentheses balance.",
3507
+ "For missing join: identify unlinked tables and add appropriate JOIN clause.",
3508
+ "For wrong aggregation: verify GROUP BY includes all non-aggregated SELECT columns.",
3509
+ "For invalid column: re-check schema for correct column name and table.",
3510
+ "Apply targeted fix based on error classification. Avoid blind regeneration."
3511
+ ],
3512
+ notes: "Maximum 3 retry attempts. If still failing, explain the issue to the user."
2849
3513
  }),
2850
- // Clarifications
2851
- clarification({
2852
- when: 'The request uses ambiguous scoring or ranking language (e.g., "top", "best", "active") without a metric.',
2853
- ask: "Clarify the ranking metric or definition before writing the query.",
2854
- reason: "Ensures the correct aggregation/ordering is used."
3514
+ workflow({
3515
+ task: "Complex query decomposition",
3516
+ triggers: [
3517
+ "multiple conditions",
3518
+ "nested requirements",
3519
+ "compare across",
3520
+ "for each"
3521
+ ],
3522
+ steps: [
3523
+ "Identify if question has multiple independent parts or nested dependencies.",
3524
+ "For independent parts: break into sub-questions, solve each, then combine with UNION or JOIN.",
3525
+ "For nested dependencies: solve inner requirement first, use result in outer query (subquery or CTE).",
3526
+ "For comparisons across groups: use window functions or self-joins.",
3527
+ "Combine sub-results into final answer. Verify completeness."
3528
+ ],
3529
+ notes: "Complex questions often need CTEs (WITH clauses) for clarity and reusability."
2855
3530
  }),
2856
- // Workflow
2857
3531
  workflow({
2858
- task: "SQL generation plan",
3532
+ task: "Multi-turn context",
3533
+ triggers: ["follow-up", "and also", "what about", "same but", "instead"],
2859
3534
  steps: [
2860
- 'Scan column names for terms matching the question. If a phrase like "total X" or "number of Y" matches a column name (e.g., Total_X, Num_Y), select that column directly instead of aggregating.',
2861
- "Translate the question into SQL patterns (aggregation, segmentation, time range, ranking) only if no column name match.",
2862
- "Choose tables/relations that satisfy those patterns; note lookup tables and filter values implied by schema hints.",
2863
- "Sketch join/filter/aggregation order considering table sizes, indexes, and stats.",
2864
- "Generate precise, validated SQL that answers the question."
2865
- ]
3535
+ 'Identify references to previous context: "it", "that", "those", "the same".',
3536
+ "Resolve references using conversation history: which tables, filters, or results were mentioned.",
3537
+ 'For refinements ("but only X"): add filter to previous query.',
3538
+ 'For extensions ("and also Y"): expand SELECT or add JOIN.',
3539
+ 'For pivots ("what about Z instead"): replace the changed element, keep unchanged parts.',
3540
+ "Maintain consistency with previous query structure when possible."
3541
+ ],
3542
+ notes: "If reference is ambiguous, ask which previous result or entity the user means."
2866
3543
  })
2867
3544
  ];
2868
3545
  if (date === "strict") {
@@ -2884,6 +3561,14 @@ function guidelines(options = {}) {
2884
3561
  }
2885
3562
 
2886
3563
  // packages/text2sql/src/lib/sql.ts
3564
+ import {
3565
+ APICallError as APICallError2,
3566
+ InvalidToolInputError,
3567
+ NoSuchToolError as NoSuchToolError2,
3568
+ ToolCallRepairError,
3569
+ generateId as generateId3
3570
+ } from "ai";
3571
+ import "@deepagents/agent";
2887
3572
  var Text2Sql = class {
2888
3573
  #config;
2889
3574
  constructor(config) {
@@ -2971,21 +3656,52 @@ var Text2Sql = class {
2971
3656
  const schemaFragments = await this.index();
2972
3657
  const context = new ContextEngine({
2973
3658
  store: this.#config.store,
2974
- chatId: params.chatId
3659
+ chatId: params.chatId,
3660
+ userId: params.userId
2975
3661
  }).set(
2976
- ...this.#config.instructions,
3662
+ ...schemaFragments,
2977
3663
  ...this.#buildRenderingInstructions(),
2978
- ...schemaFragments
3664
+ fragment(
3665
+ "Bash tool usage",
3666
+ workflow({
3667
+ task: "Query execution",
3668
+ steps: [
3669
+ 'Execute SQL through bash tool: sql run "SELECT ..."',
3670
+ "Read the output: file path, column names, and row count.",
3671
+ "Use column names to construct jq filters: cat <path> | jq '.[] | {col1, col2}'",
3672
+ "For large results, slice first: cat <path> | jq '.[:10]'"
3673
+ ]
3674
+ }),
3675
+ hint(
3676
+ "The sql command outputs: file path, column names (comma-separated), and row count. Use column names to construct precise jq queries."
3677
+ ),
3678
+ hint(
3679
+ "If a query fails, the sql command returns an error message in stderr."
3680
+ )
3681
+ ),
3682
+ ...this.#config.instructions
2979
3683
  );
2980
3684
  const userMsg = messages.at(-1);
2981
3685
  if (userMsg) {
2982
3686
  context.set(user(userMsg));
2983
3687
  await context.save();
2984
3688
  }
2985
- const chatAgent = t_a_g.clone({
3689
+ const messageId = userMsg?.id ?? generateId3();
3690
+ const { bash } = await createResultTools({
3691
+ adapter: this.#config.adapter,
3692
+ chatId: params.chatId,
3693
+ messageId
3694
+ });
3695
+ const chatAgent = agent({
3696
+ name: "text2sql",
2986
3697
  model: this.#config.model,
2987
3698
  context,
2988
- tools: { ...t_a_g.tools, ...this.#config.tools }
3699
+ tools: {
3700
+ bash,
3701
+ ...this.#config.tools
3702
+ },
3703
+ guardrails: [errorRecoveryGuardrail],
3704
+ maxGuardrailRetries: 3
2989
3705
  });
2990
3706
  const result = await chatAgent.stream({});
2991
3707
  return result.toUIMessageStream({
@@ -3009,7 +3725,8 @@ var Text2Sql = class {
3009
3725
  const schemaFragments = await this.index();
3010
3726
  const context = new ContextEngine({
3011
3727
  store: this.#config.store,
3012
- chatId: params.chatId
3728
+ chatId: params.chatId,
3729
+ userId: params.userId
3013
3730
  }).set(
3014
3731
  ...developer_agent_default.fragments,
3015
3732
  ...this.#config.instructions,
@@ -3043,7 +3760,7 @@ var Text2Sql = class {
3043
3760
  });
3044
3761
  }
3045
3762
  #formatError(error) {
3046
- if (NoSuchToolError.isInstance(error)) {
3763
+ if (NoSuchToolError2.isInstance(error)) {
3047
3764
  return "The model tried to call an unknown tool.";
3048
3765
  } else if (InvalidToolInputError.isInstance(error)) {
3049
3766
  return "The model called a tool with invalid arguments.";
@@ -3062,10 +3779,13 @@ export {
3062
3779
  FileCache,
3063
3780
  JsonCache,
3064
3781
  Point,
3782
+ SQLValidationError,
3065
3783
  Text2Sql,
3784
+ UnanswerableSQLError,
3066
3785
  applyTablesFilter,
3067
3786
  column,
3068
3787
  constraint,
3788
+ createResultTools,
3069
3789
  dialectInfo,
3070
3790
  filterRelationshipsByTables,
3071
3791
  filterTablesByName,
@@ -3076,8 +3796,8 @@ export {
3076
3796
  matchesFilter,
3077
3797
  relationship,
3078
3798
  suggestionsAgent,
3079
- t_a_g,
3080
3799
  table,
3800
+ toSql,
3081
3801
  view
3082
3802
  };
3083
3803
  //# sourceMappingURL=index.js.map