@deepagents/text2sql 0.13.1 → 0.14.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 (32) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1060 -218
  4. package/dist/index.js.map +4 -4
  5. package/dist/lib/adapters/groundings/index.js +166 -154
  6. package/dist/lib/adapters/groundings/index.js.map +4 -4
  7. package/dist/lib/adapters/mysql/index.js +166 -154
  8. package/dist/lib/adapters/mysql/index.js.map +4 -4
  9. package/dist/lib/adapters/postgres/index.js +168 -155
  10. package/dist/lib/adapters/postgres/index.js.map +4 -4
  11. package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts.map +1 -1
  12. package/dist/lib/adapters/spreadsheet/index.js +22 -2
  13. package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
  14. package/dist/lib/adapters/sqlite/index.js +166 -154
  15. package/dist/lib/adapters/sqlite/index.js.map +4 -4
  16. package/dist/lib/adapters/sqlserver/index.js +166 -154
  17. package/dist/lib/adapters/sqlserver/index.js.map +4 -4
  18. package/dist/lib/agents/result-tools.d.ts +20 -23
  19. package/dist/lib/agents/result-tools.d.ts.map +1 -1
  20. package/dist/lib/fs/index.d.ts +4 -0
  21. package/dist/lib/fs/index.d.ts.map +1 -0
  22. package/dist/lib/fs/scoped-fs.d.ts +53 -0
  23. package/dist/lib/fs/scoped-fs.d.ts.map +1 -0
  24. package/dist/lib/fs/sqlite-fs.d.ts +66 -0
  25. package/dist/lib/fs/sqlite-fs.d.ts.map +1 -0
  26. package/dist/lib/fs/tracked-fs.d.ts +40 -0
  27. package/dist/lib/fs/tracked-fs.d.ts.map +1 -0
  28. package/dist/lib/sql.d.ts +3 -4
  29. package/dist/lib/sql.d.ts.map +1 -1
  30. package/dist/lib/synthesis/index.js +181 -181
  31. package/dist/lib/synthesis/index.js.map +4 -4
  32. package/package.json +5 -5
@@ -171,7 +171,7 @@ import {
171
171
  isTextUIPart,
172
172
  isToolOrDynamicToolUIPart
173
173
  } from "ai";
174
- import dedent from "dedent";
174
+ import dedent2 from "dedent";
175
175
  import z from "zod";
176
176
 
177
177
  // packages/context/dist/index.js
@@ -188,6 +188,7 @@ import spawn2 from "nano-spawn";
188
188
  import {
189
189
  createBashTool
190
190
  } from "bash-tool";
191
+ import dedent from "dedent";
191
192
  import YAML from "yaml";
192
193
  import { DatabaseSync } from "node:sqlite";
193
194
  import { groq } from "@ai-sdk/groq";
@@ -409,7 +410,7 @@ function assistantText(content, options) {
409
410
  parts: [{ type: "text", text: content }]
410
411
  });
411
412
  }
412
- var LAZY_ID = Symbol("lazy-id");
413
+ var LAZY_ID = Symbol.for("@deepagents/context:lazy-id");
413
414
  function isLazyFragment(fragment2) {
414
415
  return LAZY_ID in fragment2;
415
416
  }
@@ -784,7 +785,7 @@ var ContextEngine = class {
784
785
  async #createBranchFrom(messageId, switchTo) {
785
786
  const branches = await this.#store.listBranches(this.#chatId);
786
787
  const samePrefix = branches.filter(
787
- (b) => b.name === this.#branchName || b.name.startsWith(`${this.#branchName}-v`)
788
+ (it) => it.name === this.#branchName || it.name.startsWith(`${this.#branchName}-v`)
788
789
  );
789
790
  const newBranchName = `${this.#branchName}-v${samePrefix.length + 1}`;
790
791
  const newBranch = {
@@ -812,6 +813,15 @@ var ContextEngine = class {
812
813
  createdAt: newBranch.createdAt
813
814
  };
814
815
  }
816
+ /**
817
+ * Rewind to a message without clearing pending messages.
818
+ * Used internally when saving an update to an existing message.
819
+ */
820
+ async #rewindForUpdate(messageId) {
821
+ const pendingBackup = [...this.#pendingMessages];
822
+ await this.rewind(messageId);
823
+ this.#pendingMessages = pendingBackup;
824
+ }
815
825
  /**
816
826
  * Get the current chat ID.
817
827
  */
@@ -896,7 +906,18 @@ var ContextEngine = class {
896
906
  messages.push(message(msg.data).codec?.decode());
897
907
  }
898
908
  }
909
+ for (let i = 0; i < this.#pendingMessages.length; i++) {
910
+ const fragment2 = this.#pendingMessages[i];
911
+ if (isLazyFragment(fragment2)) {
912
+ this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
913
+ }
914
+ }
899
915
  for (const fragment2 of this.#pendingMessages) {
916
+ if (!fragment2.codec) {
917
+ throw new Error(
918
+ `Fragment "${fragment2.name}" is missing codec. Lazy fragments must be resolved before decode.`
919
+ );
920
+ }
900
921
  const decoded = fragment2.codec.decode();
901
922
  messages.push(decoded);
902
923
  }
@@ -927,9 +948,24 @@ var ContextEngine = class {
927
948
  this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
928
949
  }
929
950
  }
951
+ for (const fragment2 of this.#pendingMessages) {
952
+ if (fragment2.id) {
953
+ const existing = await this.#store.getMessage(fragment2.id);
954
+ if (existing && existing.parentId) {
955
+ await this.#rewindForUpdate(existing.parentId);
956
+ fragment2.id = crypto.randomUUID();
957
+ break;
958
+ }
959
+ }
960
+ }
930
961
  let parentId = this.#branch.headMessageId;
931
962
  const now = Date.now();
932
963
  for (const fragment2 of this.#pendingMessages) {
964
+ if (!fragment2.codec) {
965
+ throw new Error(
966
+ `Fragment "${fragment2.name}" is missing codec. Lazy fragments must be resolved before encode.`
967
+ );
968
+ }
933
969
  const messageData = {
934
970
  id: fragment2.id ?? crypto.randomUUID(),
935
971
  chatId: this.#chatId,
@@ -1289,33 +1325,28 @@ var ContextEngine = class {
1289
1325
  return void 0;
1290
1326
  }
1291
1327
  /**
1292
- * Extract skill path mappings from available_skills fragments.
1293
- * Returns array of { host, sandbox } for mounting in sandbox filesystem.
1294
- *
1295
- * Reads the original `paths` configuration stored in fragment metadata
1296
- * by the skills() fragment helper.
1328
+ * Extract skill mounts from available_skills fragments.
1329
+ * Returns unified mount array where entries with `name` are individual skills.
1297
1330
  *
1298
1331
  * @example
1299
1332
  * ```ts
1300
1333
  * const context = new ContextEngine({ store, chatId, userId })
1301
1334
  * .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));
1302
1335
  *
1303
- * const mounts = context.getSkillMounts();
1304
- * // [{ host: './skills', sandbox: '/skills' }]
1336
+ * const { mounts } = context.getSkillMounts();
1337
+ * // mounts: [{ name: 'bi-dashboards', host: './skills/bi-dashboards/SKILL.md', sandbox: '/skills/bi-dashboards/SKILL.md' }]
1338
+ *
1339
+ * // Extract skills only (entries with name)
1340
+ * const skills = mounts.filter(m => m.name);
1305
1341
  * ```
1306
1342
  */
1307
1343
  getSkillMounts() {
1308
- const mounts = [];
1309
1344
  for (const fragment2 of this.#fragments) {
1310
- if (fragment2.name === "available_skills" && fragment2.metadata && Array.isArray(fragment2.metadata.paths)) {
1311
- for (const mapping of fragment2.metadata.paths) {
1312
- if (typeof mapping === "object" && mapping !== null && typeof mapping.host === "string" && typeof mapping.sandbox === "string") {
1313
- mounts.push({ host: mapping.host, sandbox: mapping.sandbox });
1314
- }
1315
- }
1345
+ if (fragment2.name === "available_skills" && fragment2.metadata?.mounts) {
1346
+ return { mounts: fragment2.metadata.mounts };
1316
1347
  }
1317
1348
  }
1318
- return mounts;
1349
+ return { mounts: [] };
1319
1350
  }
1320
1351
  /**
1321
1352
  * Inspect the full context state for debugging.
@@ -1471,84 +1502,46 @@ function persona(input) {
1471
1502
  }
1472
1503
  };
1473
1504
  }
1474
- var STORE_DDL = `
1475
- -- Chats table
1476
- -- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
1477
- CREATE TABLE IF NOT EXISTS chats (
1478
- id TEXT PRIMARY KEY,
1479
- userId TEXT NOT NULL,
1480
- title TEXT,
1481
- metadata TEXT,
1482
- createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
1483
- updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
1484
- );
1485
-
1486
- CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
1487
- CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
1505
+ var SKILLS_INSTRUCTIONS = dedent`A skill is a set of local instructions to follow that is stored in a \`SKILL.md\` file. Below is the list of skills that can be used. Each entry includes a name, description, and file path so you can open the source for full instructions when using a specific skill.
1488
1506
 
1489
- -- Messages table (nodes in the DAG)
1490
- CREATE TABLE IF NOT EXISTS messages (
1491
- id TEXT PRIMARY KEY,
1492
- chatId TEXT NOT NULL,
1493
- parentId TEXT,
1494
- name TEXT NOT NULL,
1495
- type TEXT,
1496
- data TEXT NOT NULL,
1497
- createdAt INTEGER NOT NULL,
1498
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1499
- FOREIGN KEY (parentId) REFERENCES messages(id)
1500
- );
1501
-
1502
- CREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);
1503
- CREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);
1504
-
1505
- -- Branches table (pointers to head messages)
1506
- CREATE TABLE IF NOT EXISTS branches (
1507
- id TEXT PRIMARY KEY,
1508
- chatId TEXT NOT NULL,
1509
- name TEXT NOT NULL,
1510
- headMessageId TEXT,
1511
- isActive INTEGER NOT NULL DEFAULT 0,
1512
- createdAt INTEGER NOT NULL,
1513
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1514
- FOREIGN KEY (headMessageId) REFERENCES messages(id),
1515
- UNIQUE(chatId, name)
1516
- );
1517
-
1518
- CREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);
1519
-
1520
- -- Checkpoints table (pointers to message nodes)
1521
- CREATE TABLE IF NOT EXISTS checkpoints (
1522
- id TEXT PRIMARY KEY,
1523
- chatId TEXT NOT NULL,
1524
- name TEXT NOT NULL,
1525
- messageId TEXT NOT NULL,
1526
- createdAt INTEGER NOT NULL,
1527
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1528
- FOREIGN KEY (messageId) REFERENCES messages(id),
1529
- UNIQUE(chatId, name)
1530
- );
1531
-
1532
- CREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);
1533
-
1534
- -- FTS5 virtual table for full-text search
1535
- -- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)
1536
- -- Only 'content' is indexed for full-text search
1537
- CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
1538
- messageId UNINDEXED,
1539
- chatId UNINDEXED,
1540
- name UNINDEXED,
1541
- content,
1542
- tokenize='porter unicode61'
1543
- );
1544
- `;
1507
+ ### How to use skills
1508
+ - Discovery: The list below shows the skills available in this session (name + description + file path). Skill bodies live on disk at the listed paths.
1509
+ - Trigger rules: If the user names a skill (with \`$SkillName\` or plain text) OR the task clearly matches a skill's description shown below, you must use that skill for that turn before doing anything else. Multiple mentions mean use them all. Do not carry skills across turns unless re-mentioned.
1510
+ - Missing/blocked: If a named skill isn't in the list or the path can't be read, say so briefly and continue with the best fallback.
1511
+ - How to use a skill (progressive disclosure):
1512
+ 1) After deciding to use a skill, open its \`SKILL.md\`. Read only enough to follow the workflow.
1513
+ 2) If \`SKILL.md\` points to extra folders such as \`references/\`, load only the specific files needed for the request; don't bulk-load everything.
1514
+ 3) If \`scripts/\` exist, prefer running or patching them instead of retyping large code blocks.
1515
+ 4) If \`assets/\` or templates exist, reuse them instead of recreating from scratch.
1516
+ - Coordination and sequencing:
1517
+ - If multiple skills apply, choose the minimal set that covers the request and state the order you'll use them.
1518
+ - Announce which skill(s) you're using and why (one short line). If you skip an obvious skill, say why.
1519
+ - Context hygiene:
1520
+ - Keep context small: summarize long sections instead of pasting them; only load extra files when needed.
1521
+ - Avoid deep reference-chasing: prefer opening only files directly linked from \`SKILL.md\` unless you're blocked.
1522
+ - When variants exist (frameworks, providers, domains), pick only the relevant reference file(s) and note that choice.
1523
+ - Safety and fallback: If a skill can't be applied cleanly (missing files, unclear instructions), state the issue, pick the next-best approach, and continue.`;
1524
+ var ddl_sqlite_default = "-- Context Store DDL for SQLite\n-- This schema implements a DAG-based message history with branching and checkpoints.\n\n-- Performance PRAGMAs (session-level, run on each connection)\nPRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA cache_size = -64000;\nPRAGMA temp_store = MEMORY;\nPRAGMA mmap_size = 268435456;\n\n-- Integrity\nPRAGMA foreign_keys = ON;\n\n-- Chats table\n-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates\nCREATE TABLE IF NOT EXISTS chats (\n id TEXT PRIMARY KEY,\n userId TEXT NOT NULL,\n title TEXT,\n metadata TEXT,\n createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),\n updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)\n);\n\nCREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);\nCREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);\n-- Composite index for listChats(): WHERE userId = ? ORDER BY updatedAt DESC\nCREATE INDEX IF NOT EXISTS idx_chats_userId_updatedAt ON chats(userId, updatedAt DESC);\n\n-- Messages table (nodes in the DAG)\nCREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n parentId TEXT,\n name TEXT NOT NULL,\n type TEXT,\n data TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (parentId) REFERENCES messages(id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);\nCREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);\n-- Composite index for recursive CTE parent traversal in getMessageChain()\nCREATE INDEX IF NOT EXISTS idx_messages_chatId_parentId ON messages(chatId, parentId);\n\n-- Branches table (pointers to head messages)\nCREATE TABLE IF NOT EXISTS branches (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n headMessageId TEXT,\n isActive INTEGER NOT NULL DEFAULT 0,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (headMessageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);\n-- Composite index for getActiveBranch(): WHERE chatId = ? AND isActive = 1\nCREATE INDEX IF NOT EXISTS idx_branches_chatId_isActive ON branches(chatId, isActive);\n\n-- Checkpoints table (pointers to message nodes)\nCREATE TABLE IF NOT EXISTS checkpoints (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n messageId TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (messageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);\n\n-- FTS5 virtual table for full-text search\n-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)\n-- Only 'content' is indexed for full-text search\nCREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(\n messageId UNINDEXED,\n chatId UNINDEXED,\n name UNINDEXED,\n content,\n tokenize='porter unicode61'\n);\n";
1545
1525
  var SqliteContextStore = class extends ContextStore {
1546
1526
  #db;
1527
+ #statements = /* @__PURE__ */ new Map();
1528
+ /**
1529
+ * Get or create a prepared statement.
1530
+ * Statements are cached for the lifetime of the store to avoid
1531
+ * repeated SQL parsing and compilation overhead.
1532
+ */
1533
+ #stmt(sql) {
1534
+ let stmt = this.#statements.get(sql);
1535
+ if (!stmt) {
1536
+ stmt = this.#db.prepare(sql);
1537
+ this.#statements.set(sql, stmt);
1538
+ }
1539
+ return stmt;
1540
+ }
1547
1541
  constructor(path3) {
1548
1542
  super();
1549
1543
  this.#db = new DatabaseSync(path3);
1550
- this.#db.exec("PRAGMA foreign_keys = ON");
1551
- this.#db.exec(STORE_DDL);
1544
+ this.#db.exec(ddl_sqlite_default);
1552
1545
  }
1553
1546
  /**
1554
1547
  * Execute a function within a transaction.
@@ -1569,11 +1562,12 @@ var SqliteContextStore = class extends ContextStore {
1569
1562
  // Chat Operations
1570
1563
  // ==========================================================================
1571
1564
  async createChat(chat) {
1572
- this.#useTransaction(() => {
1573
- this.#db.prepare(
1565
+ return this.#useTransaction(() => {
1566
+ const row = this.#db.prepare(
1574
1567
  `INSERT INTO chats (id, userId, title, metadata)
1575
- VALUES (?, ?, ?, ?)`
1576
- ).run(
1568
+ VALUES (?, ?, ?, ?)
1569
+ RETURNING *`
1570
+ ).get(
1577
1571
  chat.id,
1578
1572
  chat.userId,
1579
1573
  chat.title ?? null,
@@ -1583,6 +1577,14 @@ var SqliteContextStore = class extends ContextStore {
1583
1577
  `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
1584
1578
  VALUES (?, ?, 'main', NULL, 1, ?)`
1585
1579
  ).run(crypto.randomUUID(), chat.id, Date.now());
1580
+ return {
1581
+ id: row.id,
1582
+ userId: row.userId,
1583
+ title: row.title ?? void 0,
1584
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1585
+ createdAt: row.createdAt,
1586
+ updatedAt: row.updatedAt
1587
+ };
1586
1588
  });
1587
1589
  }
1588
1590
  async upsertChat(chat) {
@@ -1725,21 +1727,16 @@ var SqliteContextStore = class extends ContextStore {
1725
1727
  // Message Operations (Graph Nodes)
1726
1728
  // ==========================================================================
1727
1729
  async addMessage(message2) {
1728
- this.#db.prepare(
1730
+ if (message2.parentId === message2.id) {
1731
+ throw new Error(`Message ${message2.id} cannot be its own parent`);
1732
+ }
1733
+ this.#stmt(
1729
1734
  `INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
1730
- VALUES (
1731
- ?1,
1732
- ?2,
1733
- CASE WHEN ?3 = ?1 THEN (SELECT parentId FROM messages WHERE id = ?1) ELSE ?3 END,
1734
- ?4,
1735
- ?5,
1736
- ?6,
1737
- ?7
1738
- )
1739
- ON CONFLICT(id) DO UPDATE SET
1740
- name = excluded.name,
1741
- type = excluded.type,
1742
- data = excluded.data`
1735
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1736
+ ON CONFLICT(id) DO UPDATE SET
1737
+ name = excluded.name,
1738
+ type = excluded.type,
1739
+ data = excluded.data`
1743
1740
  ).run(
1744
1741
  message2.id,
1745
1742
  message2.chatId,
@@ -1750,14 +1747,16 @@ var SqliteContextStore = class extends ContextStore {
1750
1747
  message2.createdAt
1751
1748
  );
1752
1749
  const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
1753
- this.#db.prepare(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
1754
- this.#db.prepare(
1750
+ this.#stmt(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
1751
+ this.#stmt(
1755
1752
  `INSERT INTO messages_fts(messageId, chatId, name, content)
1756
- VALUES (?, ?, ?, ?)`
1753
+ VALUES (?, ?, ?, ?)`
1757
1754
  ).run(message2.id, message2.chatId, message2.name, content);
1758
1755
  }
1759
1756
  async getMessage(messageId) {
1760
- const row = this.#db.prepare("SELECT * FROM messages WHERE id = ?").get(messageId);
1757
+ const row = this.#stmt("SELECT * FROM messages WHERE id = ?").get(
1758
+ messageId
1759
+ );
1761
1760
  if (!row) {
1762
1761
  return void 0;
1763
1762
  }
@@ -1772,15 +1771,16 @@ var SqliteContextStore = class extends ContextStore {
1772
1771
  };
1773
1772
  }
1774
1773
  async getMessageChain(headId) {
1775
- const rows = this.#db.prepare(
1774
+ const rows = this.#stmt(
1776
1775
  `WITH RECURSIVE chain AS (
1777
- SELECT *, 0 as depth FROM messages WHERE id = ?
1778
- UNION ALL
1779
- SELECT m.*, c.depth + 1 FROM messages m
1780
- INNER JOIN chain c ON m.id = c.parentId
1781
- )
1782
- SELECT * FROM chain
1783
- ORDER BY depth DESC`
1776
+ SELECT *, 0 as depth FROM messages WHERE id = ?
1777
+ UNION ALL
1778
+ SELECT m.*, c.depth + 1 FROM messages m
1779
+ INNER JOIN chain c ON m.id = c.parentId
1780
+ WHERE c.depth < 100000
1781
+ )
1782
+ SELECT * FROM chain
1783
+ ORDER BY depth DESC`
1784
1784
  ).all(headId);
1785
1785
  return rows.map((row) => ({
1786
1786
  id: row.id,
@@ -1793,7 +1793,7 @@ var SqliteContextStore = class extends ContextStore {
1793
1793
  }));
1794
1794
  }
1795
1795
  async hasChildren(messageId) {
1796
- const row = this.#db.prepare(
1796
+ const row = this.#stmt(
1797
1797
  "SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ?) as hasChildren"
1798
1798
  ).get(messageId);
1799
1799
  return row.hasChildren === 1;
@@ -1840,7 +1840,9 @@ var SqliteContextStore = class extends ContextStore {
1840
1840
  };
1841
1841
  }
1842
1842
  async getActiveBranch(chatId) {
1843
- const row = this.#db.prepare("SELECT * FROM branches WHERE chatId = ? AND isActive = 1").get(chatId);
1843
+ const row = this.#stmt(
1844
+ "SELECT * FROM branches WHERE chatId = ? AND isActive = 1"
1845
+ ).get(chatId);
1844
1846
  if (!row) {
1845
1847
  return void 0;
1846
1848
  }
@@ -1858,45 +1860,43 @@ var SqliteContextStore = class extends ContextStore {
1858
1860
  this.#db.prepare("UPDATE branches SET isActive = 1 WHERE id = ?").run(branchId);
1859
1861
  }
1860
1862
  async updateBranchHead(branchId, messageId) {
1861
- this.#db.prepare("UPDATE branches SET headMessageId = ? WHERE id = ?").run(messageId, branchId);
1863
+ this.#stmt("UPDATE branches SET headMessageId = ? WHERE id = ?").run(
1864
+ messageId,
1865
+ branchId
1866
+ );
1862
1867
  }
1863
1868
  async listBranches(chatId) {
1864
- const branches = this.#db.prepare(
1869
+ const rows = this.#db.prepare(
1865
1870
  `SELECT
1866
1871
  b.id,
1867
1872
  b.name,
1868
1873
  b.headMessageId,
1869
1874
  b.isActive,
1870
- b.createdAt
1875
+ b.createdAt,
1876
+ COALESCE(
1877
+ (
1878
+ WITH RECURSIVE chain AS (
1879
+ SELECT id, parentId FROM messages WHERE id = b.headMessageId
1880
+ UNION ALL
1881
+ SELECT m.id, m.parentId FROM messages m
1882
+ INNER JOIN chain c ON m.id = c.parentId
1883
+ )
1884
+ SELECT COUNT(*) FROM chain
1885
+ ),
1886
+ 0
1887
+ ) as messageCount
1871
1888
  FROM branches b
1872
1889
  WHERE b.chatId = ?
1873
1890
  ORDER BY b.createdAt ASC`
1874
1891
  ).all(chatId);
1875
- const result = [];
1876
- for (const branch of branches) {
1877
- let messageCount = 0;
1878
- if (branch.headMessageId) {
1879
- const countRow = this.#db.prepare(
1880
- `WITH RECURSIVE chain AS (
1881
- SELECT id, parentId FROM messages WHERE id = ?
1882
- UNION ALL
1883
- SELECT m.id, m.parentId FROM messages m
1884
- INNER JOIN chain c ON m.id = c.parentId
1885
- )
1886
- SELECT COUNT(*) as count FROM chain`
1887
- ).get(branch.headMessageId);
1888
- messageCount = countRow.count;
1889
- }
1890
- result.push({
1891
- id: branch.id,
1892
- name: branch.name,
1893
- headMessageId: branch.headMessageId,
1894
- isActive: branch.isActive === 1,
1895
- messageCount,
1896
- createdAt: branch.createdAt
1897
- });
1898
- }
1899
- return result;
1892
+ return rows.map((row) => ({
1893
+ id: row.id,
1894
+ name: row.name,
1895
+ headMessageId: row.headMessageId,
1896
+ isActive: row.isActive === 1,
1897
+ messageCount: row.messageCount,
1898
+ createdAt: row.createdAt
1899
+ }));
1900
1900
  }
1901
1901
  // ==========================================================================
1902
1902
  // Checkpoint Operations
@@ -2149,7 +2149,7 @@ async function resolveContext(params) {
2149
2149
  fragment("sql", params.sql),
2150
2150
  fragment(
2151
2151
  "task",
2152
- dedent`
2152
+ dedent2`
2153
2153
  Given the conversation above and the SQL query that was executed,
2154
2154
  generate a single, standalone natural language question that:
2155
2155
  1. Fully captures the user's intent without needing prior context
@@ -2160,7 +2160,7 @@ async function resolveContext(params) {
2160
2160
  ),
2161
2161
  fragment(
2162
2162
  "examples",
2163
- dedent`
2163
+ dedent2`
2164
2164
  Conversation: "Show me customers" → "Filter to NY" → "Sort by revenue"
2165
2165
  SQL: SELECT * FROM customers WHERE region = 'NY' ORDER BY revenue DESC
2166
2166
  Question: "Show me customers in the NY region sorted by revenue"
@@ -2354,7 +2354,7 @@ var MessageExtractor = class extends PairProducer {
2354
2354
 
2355
2355
  // packages/text2sql/src/lib/synthesis/extractors/sql-extractor.ts
2356
2356
  import { groq as groq3 } from "@ai-sdk/groq";
2357
- import dedent2 from "dedent";
2357
+ import dedent3 from "dedent";
2358
2358
  import z2 from "zod";
2359
2359
  var outputSchema = z2.object({
2360
2360
  question: z2.string().describe("A natural language question that the SQL query answers")
@@ -2405,7 +2405,7 @@ var SqlExtractor = class extends PairProducer {
2405
2405
  fragment("sql", sql),
2406
2406
  fragment(
2407
2407
  "task",
2408
- dedent2`
2408
+ dedent3`
2409
2409
  Given the database schema and the SQL query above, generate a single
2410
2410
  natural language question that:
2411
2411
  1. Accurately describes what information the query retrieves
@@ -2416,7 +2416,7 @@ var SqlExtractor = class extends PairProducer {
2416
2416
  ),
2417
2417
  fragment(
2418
2418
  "examples",
2419
- dedent2`
2419
+ dedent3`
2420
2420
  SQL: SELECT COUNT(*) FROM customers WHERE region = 'NY'
2421
2421
  Question: "How many customers do we have in New York?"
2422
2422
 
@@ -2491,7 +2491,7 @@ var WindowedContextExtractor = class extends BaseContextualExtractor {
2491
2491
 
2492
2492
  // packages/text2sql/src/lib/synthesis/extractors/segmented-context-extractor.ts
2493
2493
  import { groq as groq4 } from "@ai-sdk/groq";
2494
- import dedent3 from "dedent";
2494
+ import dedent4 from "dedent";
2495
2495
  import z3 from "zod";
2496
2496
  var topicChangeSchema = z3.object({
2497
2497
  isTopicChange: z3.boolean().describe("Whether the new message represents a topic change"),
@@ -2513,7 +2513,7 @@ async function detectTopicChange(params) {
2513
2513
  fragment("new_message", params.newMessage),
2514
2514
  fragment(
2515
2515
  "task",
2516
- dedent3`
2516
+ dedent4`
2517
2517
  Determine if the new message represents a significant topic change from the
2518
2518
  prior conversation context. A topic change occurs when:
2519
2519
  1. The user asks about a completely different entity/table/domain
@@ -2528,7 +2528,7 @@ async function detectTopicChange(params) {
2528
2528
  ),
2529
2529
  fragment(
2530
2530
  "examples",
2531
- dedent3`
2531
+ dedent4`
2532
2532
  Context: "Show me customers in NY" → "Sort by revenue"
2533
2533
  New: "Filter to those with orders over $1000"
2534
2534
  Decision: NOT a topic change (still refining customer query)
@@ -2646,11 +2646,11 @@ import pLimit from "p-limit";
2646
2646
 
2647
2647
  // packages/text2sql/src/lib/agents/question.agent.ts
2648
2648
  import { groq as groq5 } from "@ai-sdk/groq";
2649
- import dedent4 from "dedent";
2649
+ import dedent5 from "dedent";
2650
2650
  import z4 from "zod";
2651
2651
  import "@deepagents/agent";
2652
2652
  var complexityInstructions = {
2653
- simple: dedent4`
2653
+ simple: dedent5`
2654
2654
  Generate simple questions that require:
2655
2655
  - Basic SELECT with single table
2656
2656
  - Simple WHERE clauses with one condition
@@ -2658,7 +2658,7 @@ var complexityInstructions = {
2658
2658
  - No joins required
2659
2659
  Examples: "How many customers do we have?", "List all products", "What is the total revenue?"
2660
2660
  `,
2661
- moderate: dedent4`
2661
+ moderate: dedent5`
2662
2662
  Generate moderate questions that require:
2663
2663
  - JOINs between 2-3 tables
2664
2664
  - Multiple WHERE conditions (AND/OR)
@@ -2667,7 +2667,7 @@ var complexityInstructions = {
2667
2667
  - Basic subqueries
2668
2668
  Examples: "What are the top 5 customers by total orders?", "Which products have never been ordered?"
2669
2669
  `,
2670
- complex: dedent4`
2670
+ complex: dedent5`
2671
2671
  Generate complex questions that require:
2672
2672
  - Multiple JOINs (3+ tables)
2673
2673
  - Nested subqueries or CTEs
@@ -2676,7 +2676,7 @@ var complexityInstructions = {
2676
2676
  - Date/time calculations
2677
2677
  Examples: "What is the month-over-month growth rate?", "Which customers have increased spending compared to last year?"
2678
2678
  `,
2679
- "high complex": dedent4`
2679
+ "high complex": dedent5`
2680
2680
  Generate highly complex questions that require advanced SQL features:
2681
2681
  - Window functions (ROW_NUMBER, RANK, DENSE_RANK)
2682
2682
  - LAG, LEAD for comparisons
@@ -2711,7 +2711,7 @@ async function generateQuestions(params) {
2711
2711
  ),
2712
2712
  fragment(
2713
2713
  "task",
2714
- dedent4`
2714
+ dedent5`
2715
2715
  Generate exactly ${count} natural language questions at the "${complexity}" complexity level.
2716
2716
  The questions should:
2717
2717
  1. Match the complexity requirements above
@@ -3014,7 +3014,7 @@ Generate ${this.options.count} questions at ${complexity} complexity.` : void 0;
3014
3014
 
3015
3015
  // packages/text2sql/src/lib/synthesis/synthesizers/breadth-evolver.ts
3016
3016
  import { groq as groq7 } from "@ai-sdk/groq";
3017
- import dedent5 from "dedent";
3017
+ import dedent6 from "dedent";
3018
3018
  import pLimit2 from "p-limit";
3019
3019
  import z6 from "zod";
3020
3020
  import "@deepagents/agent";
@@ -3064,7 +3064,7 @@ async function paraphraseQuestion(params) {
3064
3064
  chatId: `paraphraser-${crypto.randomUUID()}`,
3065
3065
  userId: "system"
3066
3066
  });
3067
- const personaInstruction = params.persona ? dedent5`
3067
+ const personaInstruction = params.persona ? dedent6`
3068
3068
  <persona role="${params.persona.role}">
3069
3069
  ${params.persona.perspective}
3070
3070
 
@@ -3072,7 +3072,7 @@ async function paraphraseQuestion(params) {
3072
3072
  Use their vocabulary, priorities, and framing style.
3073
3073
  </persona>
3074
3074
  ` : "";
3075
- const styleInstruction = params.persona?.styles && params.persona.styles.length > 0 ? dedent5`
3075
+ const styleInstruction = params.persona?.styles && params.persona.styles.length > 0 ? dedent6`
3076
3076
  <communication_styles>
3077
3077
  Generate paraphrases using these communication styles: ${params.persona.styles.join(", ")}
3078
3078
 
@@ -3098,7 +3098,7 @@ async function paraphraseQuestion(params) {
3098
3098
  ...styleInstruction ? [fragment("communication_styles", styleInstruction)] : [],
3099
3099
  fragment(
3100
3100
  "task",
3101
- dedent5`
3101
+ dedent6`
3102
3102
  Generate exactly ${params.count} paraphrased versions of the original question.
3103
3103
 
3104
3104
  Requirements:
@@ -3175,13 +3175,13 @@ var BreadthEvolver = class extends PairProducer {
3175
3175
  // packages/text2sql/src/lib/synthesis/synthesizers/depth-evolver.ts
3176
3176
  import { groq as groq8 } from "@ai-sdk/groq";
3177
3177
  import { NoObjectGeneratedError as NoObjectGeneratedError2, NoOutputGeneratedError as NoOutputGeneratedError2 } from "ai";
3178
- import dedent6 from "dedent";
3178
+ import dedent7 from "dedent";
3179
3179
  import pLimit3 from "p-limit";
3180
3180
  import pRetry2 from "p-retry";
3181
3181
  import z7 from "zod";
3182
3182
  import "@deepagents/agent";
3183
3183
  var techniqueInstructions = {
3184
- "add-aggregation": dedent6`
3184
+ "add-aggregation": dedent7`
3185
3185
  Add aggregation requirements to the question.
3186
3186
  Transform it to require GROUP BY, COUNT, SUM, AVG, MIN, MAX, or similar operations.
3187
3187
  Examples:
@@ -3189,7 +3189,7 @@ var techniqueInstructions = {
3189
3189
  - "List products" → "What is the average price per category?"
3190
3190
  - "Get employees" → "How many employees are in each department?"
3191
3191
  `,
3192
- "add-filter": dedent6`
3192
+ "add-filter": dedent7`
3193
3193
  Add filtering conditions to the question.
3194
3194
  Transform it to require WHERE clauses with specific conditions.
3195
3195
  Examples:
@@ -3197,7 +3197,7 @@ var techniqueInstructions = {
3197
3197
  - "List customers" → "List customers who have made more than 5 purchases"
3198
3198
  - "Get products" → "Get products with price above $100"
3199
3199
  `,
3200
- "add-join": dedent6`
3200
+ "add-join": dedent7`
3201
3201
  Add requirements that need data from related tables.
3202
3202
  Transform it to require JOIN operations between multiple tables.
3203
3203
  Examples:
@@ -3205,7 +3205,7 @@ var techniqueInstructions = {
3205
3205
  - "List products" → "List products with their supplier information"
3206
3206
  - "Get employees" → "Get employees with their department and manager names"
3207
3207
  `,
3208
- "add-reasoning": dedent6`
3208
+ "add-reasoning": dedent7`
3209
3209
  Add multi-step reasoning requirements.
3210
3210
  Transform it to require logical deduction, comparisons, or derived calculations.
3211
3211
  Examples:
@@ -3213,7 +3213,7 @@ var techniqueInstructions = {
3213
3213
  - "List products" → "Which products are underperforming compared to their category average?"
3214
3214
  - "Get revenue" → "Which month had the highest growth compared to the previous month?"
3215
3215
  `,
3216
- hypothetical: dedent6`
3216
+ hypothetical: dedent7`
3217
3217
  Add a hypothetical or speculative scenario.
3218
3218
  Transform it to require applying calculations or projections.
3219
3219
  Examples:
@@ -3251,7 +3251,7 @@ async function evolveQuestion(params) {
3251
3251
  ),
3252
3252
  fragment(
3253
3253
  "task",
3254
- dedent6`
3254
+ dedent7`
3255
3255
  Evolve the original question using the "${params.technique}" technique.
3256
3256
 
3257
3257
  Requirements:
@@ -3396,7 +3396,7 @@ async function withRetry2(computation) {
3396
3396
 
3397
3397
  // packages/text2sql/src/lib/synthesis/synthesizers/persona-generator.ts
3398
3398
  import { groq as groq9 } from "@ai-sdk/groq";
3399
- import dedent7 from "dedent";
3399
+ import dedent8 from "dedent";
3400
3400
  import z8 from "zod";
3401
3401
  import "@deepagents/agent";
3402
3402
  var outputSchema3 = z8.object({
@@ -3429,7 +3429,7 @@ async function generatePersonas(schemaFragments, options) {
3429
3429
  fragment("database_schema", schema),
3430
3430
  fragment(
3431
3431
  "task",
3432
- dedent7`
3432
+ dedent8`
3433
3433
  Analyze the database schema and generate realistic personas representing
3434
3434
  the different types of users who would query this database.
3435
3435
 
@@ -3462,7 +3462,7 @@ async function generatePersonas(schemaFragments, options) {
3462
3462
  ),
3463
3463
  fragment(
3464
3464
  "example",
3465
- dedent7`
3465
+ dedent8`
3466
3466
  For an e-commerce schema with orders, customers, products tables:
3467
3467
 
3468
3468
  {
@@ -3502,7 +3502,7 @@ async function generatePersonas(schemaFragments, options) {
3502
3502
 
3503
3503
  // packages/text2sql/src/lib/agents/teachables.agent.ts
3504
3504
  import { groq as groq10 } from "@ai-sdk/groq";
3505
- import dedent8 from "dedent";
3505
+ import dedent9 from "dedent";
3506
3506
  import z9 from "zod";
3507
3507
  import "@deepagents/agent";
3508
3508
  var outputSchema4 = z9.object({
@@ -3572,7 +3572,7 @@ async function toTeachings(input, options) {
3572
3572
  ...input.context ? [fragment("additional_context", input.context)] : [],
3573
3573
  fragment(
3574
3574
  "output_structure",
3575
- dedent8`
3575
+ dedent9`
3576
3576
  Output a JSON object with these optional arrays (include only relevant ones):
3577
3577
  - terms: [{ name: string, definition: string }] - Domain terminology
3578
3578
  - hints: [{ text: string }] - Helpful SQL generation hints
@@ -3588,7 +3588,7 @@ async function toTeachings(input, options) {
3588
3588
  ),
3589
3589
  fragment(
3590
3590
  "task",
3591
- dedent8`
3591
+ dedent9`
3592
3592
  1. Analyze the schema to infer domain, relationships, and sensitive columns.
3593
3593
  2. Generate 3-10 fragments total across all categories, prioritizing:
3594
3594
  - guardrails for PII columns (email, ssn, phone, etc)