@deepagents/text2sql 0.13.0 → 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 +1065 -219
  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 +5 -5
  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 +10 -7
@@ -221,7 +221,7 @@ var InfoGrounding = class extends AbstractGrounding {
221
221
  // packages/text2sql/src/lib/adapters/groundings/report.grounding.ts
222
222
  import { groq as groq2 } from "@ai-sdk/groq";
223
223
  import { tool } from "ai";
224
- import dedent from "dedent";
224
+ import dedent2 from "dedent";
225
225
  import z from "zod";
226
226
  import "@deepagents/agent";
227
227
 
@@ -239,6 +239,7 @@ import spawn2 from "nano-spawn";
239
239
  import {
240
240
  createBashTool
241
241
  } from "bash-tool";
242
+ import dedent from "dedent";
242
243
  import YAML from "yaml";
243
244
  import { DatabaseSync } from "node:sqlite";
244
245
  import { groq } from "@ai-sdk/groq";
@@ -460,7 +461,7 @@ function assistantText(content, options) {
460
461
  parts: [{ type: "text", text: content }]
461
462
  });
462
463
  }
463
- var LAZY_ID = Symbol("lazy-id");
464
+ var LAZY_ID = Symbol.for("@deepagents/context:lazy-id");
464
465
  function isLazyFragment(fragment2) {
465
466
  return LAZY_ID in fragment2;
466
467
  }
@@ -847,7 +848,7 @@ var ContextEngine = class {
847
848
  async #createBranchFrom(messageId, switchTo) {
848
849
  const branches = await this.#store.listBranches(this.#chatId);
849
850
  const samePrefix = branches.filter(
850
- (b) => b.name === this.#branchName || b.name.startsWith(`${this.#branchName}-v`)
851
+ (it) => it.name === this.#branchName || it.name.startsWith(`${this.#branchName}-v`)
851
852
  );
852
853
  const newBranchName = `${this.#branchName}-v${samePrefix.length + 1}`;
853
854
  const newBranch = {
@@ -875,6 +876,15 @@ var ContextEngine = class {
875
876
  createdAt: newBranch.createdAt
876
877
  };
877
878
  }
879
+ /**
880
+ * Rewind to a message without clearing pending messages.
881
+ * Used internally when saving an update to an existing message.
882
+ */
883
+ async #rewindForUpdate(messageId) {
884
+ const pendingBackup = [...this.#pendingMessages];
885
+ await this.rewind(messageId);
886
+ this.#pendingMessages = pendingBackup;
887
+ }
878
888
  /**
879
889
  * Get the current chat ID.
880
890
  */
@@ -959,7 +969,18 @@ var ContextEngine = class {
959
969
  messages.push(message(msg.data).codec?.decode());
960
970
  }
961
971
  }
972
+ for (let i = 0; i < this.#pendingMessages.length; i++) {
973
+ const fragment2 = this.#pendingMessages[i];
974
+ if (isLazyFragment(fragment2)) {
975
+ this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
976
+ }
977
+ }
962
978
  for (const fragment2 of this.#pendingMessages) {
979
+ if (!fragment2.codec) {
980
+ throw new Error(
981
+ `Fragment "${fragment2.name}" is missing codec. Lazy fragments must be resolved before decode.`
982
+ );
983
+ }
963
984
  const decoded = fragment2.codec.decode();
964
985
  messages.push(decoded);
965
986
  }
@@ -990,9 +1011,24 @@ var ContextEngine = class {
990
1011
  this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
991
1012
  }
992
1013
  }
1014
+ for (const fragment2 of this.#pendingMessages) {
1015
+ if (fragment2.id) {
1016
+ const existing = await this.#store.getMessage(fragment2.id);
1017
+ if (existing && existing.parentId) {
1018
+ await this.#rewindForUpdate(existing.parentId);
1019
+ fragment2.id = crypto.randomUUID();
1020
+ break;
1021
+ }
1022
+ }
1023
+ }
993
1024
  let parentId = this.#branch.headMessageId;
994
1025
  const now = Date.now();
995
1026
  for (const fragment2 of this.#pendingMessages) {
1027
+ if (!fragment2.codec) {
1028
+ throw new Error(
1029
+ `Fragment "${fragment2.name}" is missing codec. Lazy fragments must be resolved before encode.`
1030
+ );
1031
+ }
996
1032
  const messageData = {
997
1033
  id: fragment2.id ?? crypto.randomUUID(),
998
1034
  chatId: this.#chatId,
@@ -1352,33 +1388,28 @@ var ContextEngine = class {
1352
1388
  return void 0;
1353
1389
  }
1354
1390
  /**
1355
- * Extract skill path mappings from available_skills fragments.
1356
- * Returns array of { host, sandbox } for mounting in sandbox filesystem.
1357
- *
1358
- * Reads the original `paths` configuration stored in fragment metadata
1359
- * by the skills() fragment helper.
1391
+ * Extract skill mounts from available_skills fragments.
1392
+ * Returns unified mount array where entries with `name` are individual skills.
1360
1393
  *
1361
1394
  * @example
1362
1395
  * ```ts
1363
1396
  * const context = new ContextEngine({ store, chatId, userId })
1364
1397
  * .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));
1365
1398
  *
1366
- * const mounts = context.getSkillMounts();
1367
- * // [{ host: './skills', sandbox: '/skills' }]
1399
+ * const { mounts } = context.getSkillMounts();
1400
+ * // mounts: [{ name: 'bi-dashboards', host: './skills/bi-dashboards/SKILL.md', sandbox: '/skills/bi-dashboards/SKILL.md' }]
1401
+ *
1402
+ * // Extract skills only (entries with name)
1403
+ * const skills = mounts.filter(m => m.name);
1368
1404
  * ```
1369
1405
  */
1370
1406
  getSkillMounts() {
1371
- const mounts = [];
1372
1407
  for (const fragment2 of this.#fragments) {
1373
- if (fragment2.name === "available_skills" && fragment2.metadata && Array.isArray(fragment2.metadata.paths)) {
1374
- for (const mapping of fragment2.metadata.paths) {
1375
- if (typeof mapping === "object" && mapping !== null && typeof mapping.host === "string" && typeof mapping.sandbox === "string") {
1376
- mounts.push({ host: mapping.host, sandbox: mapping.sandbox });
1377
- }
1378
- }
1408
+ if (fragment2.name === "available_skills" && fragment2.metadata?.mounts) {
1409
+ return { mounts: fragment2.metadata.mounts };
1379
1410
  }
1380
1411
  }
1381
- return mounts;
1412
+ return { mounts: [] };
1382
1413
  }
1383
1414
  /**
1384
1415
  * Inspect the full context state for debugging.
@@ -1447,91 +1478,53 @@ function runGuardrailChain(part, guardrails, context) {
1447
1478
  let currentPart = part;
1448
1479
  for (const guardrail2 of guardrails) {
1449
1480
  const result = guardrail2.handle(currentPart, context);
1450
- if (result.type === "fail") {
1481
+ if (result.type === "fail" || result.type === "stop") {
1451
1482
  return result;
1452
1483
  }
1453
1484
  currentPart = result.part;
1454
1485
  }
1455
1486
  return pass(currentPart);
1456
1487
  }
1457
- var STORE_DDL = `
1458
- -- Chats table
1459
- -- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
1460
- CREATE TABLE IF NOT EXISTS chats (
1461
- id TEXT PRIMARY KEY,
1462
- userId TEXT NOT NULL,
1463
- title TEXT,
1464
- metadata TEXT,
1465
- createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
1466
- updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
1467
- );
1468
-
1469
- CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
1470
- CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
1471
-
1472
- -- Messages table (nodes in the DAG)
1473
- CREATE TABLE IF NOT EXISTS messages (
1474
- id TEXT PRIMARY KEY,
1475
- chatId TEXT NOT NULL,
1476
- parentId TEXT,
1477
- name TEXT NOT NULL,
1478
- type TEXT,
1479
- data TEXT NOT NULL,
1480
- createdAt INTEGER NOT NULL,
1481
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1482
- FOREIGN KEY (parentId) REFERENCES messages(id)
1483
- );
1484
-
1485
- CREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);
1486
- CREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);
1487
-
1488
- -- Branches table (pointers to head messages)
1489
- CREATE TABLE IF NOT EXISTS branches (
1490
- id TEXT PRIMARY KEY,
1491
- chatId TEXT NOT NULL,
1492
- name TEXT NOT NULL,
1493
- headMessageId TEXT,
1494
- isActive INTEGER NOT NULL DEFAULT 0,
1495
- createdAt INTEGER NOT NULL,
1496
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1497
- FOREIGN KEY (headMessageId) REFERENCES messages(id),
1498
- UNIQUE(chatId, name)
1499
- );
1500
-
1501
- CREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);
1488
+ 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.
1502
1489
 
1503
- -- Checkpoints table (pointers to message nodes)
1504
- CREATE TABLE IF NOT EXISTS checkpoints (
1505
- id TEXT PRIMARY KEY,
1506
- chatId TEXT NOT NULL,
1507
- name TEXT NOT NULL,
1508
- messageId TEXT NOT NULL,
1509
- createdAt INTEGER NOT NULL,
1510
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1511
- FOREIGN KEY (messageId) REFERENCES messages(id),
1512
- UNIQUE(chatId, name)
1513
- );
1514
-
1515
- CREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);
1516
-
1517
- -- FTS5 virtual table for full-text search
1518
- -- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)
1519
- -- Only 'content' is indexed for full-text search
1520
- CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
1521
- messageId UNINDEXED,
1522
- chatId UNINDEXED,
1523
- name UNINDEXED,
1524
- content,
1525
- tokenize='porter unicode61'
1526
- );
1527
- `;
1490
+ ### How to use skills
1491
+ - Discovery: The list below shows the skills available in this session (name + description + file path). Skill bodies live on disk at the listed paths.
1492
+ - 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.
1493
+ - 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.
1494
+ - How to use a skill (progressive disclosure):
1495
+ 1) After deciding to use a skill, open its \`SKILL.md\`. Read only enough to follow the workflow.
1496
+ 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.
1497
+ 3) If \`scripts/\` exist, prefer running or patching them instead of retyping large code blocks.
1498
+ 4) If \`assets/\` or templates exist, reuse them instead of recreating from scratch.
1499
+ - Coordination and sequencing:
1500
+ - If multiple skills apply, choose the minimal set that covers the request and state the order you'll use them.
1501
+ - Announce which skill(s) you're using and why (one short line). If you skip an obvious skill, say why.
1502
+ - Context hygiene:
1503
+ - Keep context small: summarize long sections instead of pasting them; only load extra files when needed.
1504
+ - Avoid deep reference-chasing: prefer opening only files directly linked from \`SKILL.md\` unless you're blocked.
1505
+ - When variants exist (frameworks, providers, domains), pick only the relevant reference file(s) and note that choice.
1506
+ - 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.`;
1507
+ 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";
1528
1508
  var SqliteContextStore = class extends ContextStore {
1529
1509
  #db;
1510
+ #statements = /* @__PURE__ */ new Map();
1511
+ /**
1512
+ * Get or create a prepared statement.
1513
+ * Statements are cached for the lifetime of the store to avoid
1514
+ * repeated SQL parsing and compilation overhead.
1515
+ */
1516
+ #stmt(sql) {
1517
+ let stmt = this.#statements.get(sql);
1518
+ if (!stmt) {
1519
+ stmt = this.#db.prepare(sql);
1520
+ this.#statements.set(sql, stmt);
1521
+ }
1522
+ return stmt;
1523
+ }
1530
1524
  constructor(path3) {
1531
1525
  super();
1532
1526
  this.#db = new DatabaseSync(path3);
1533
- this.#db.exec("PRAGMA foreign_keys = ON");
1534
- this.#db.exec(STORE_DDL);
1527
+ this.#db.exec(ddl_sqlite_default);
1535
1528
  }
1536
1529
  /**
1537
1530
  * Execute a function within a transaction.
@@ -1552,11 +1545,12 @@ var SqliteContextStore = class extends ContextStore {
1552
1545
  // Chat Operations
1553
1546
  // ==========================================================================
1554
1547
  async createChat(chat) {
1555
- this.#useTransaction(() => {
1556
- this.#db.prepare(
1548
+ return this.#useTransaction(() => {
1549
+ const row = this.#db.prepare(
1557
1550
  `INSERT INTO chats (id, userId, title, metadata)
1558
- VALUES (?, ?, ?, ?)`
1559
- ).run(
1551
+ VALUES (?, ?, ?, ?)
1552
+ RETURNING *`
1553
+ ).get(
1560
1554
  chat.id,
1561
1555
  chat.userId,
1562
1556
  chat.title ?? null,
@@ -1566,6 +1560,14 @@ var SqliteContextStore = class extends ContextStore {
1566
1560
  `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
1567
1561
  VALUES (?, ?, 'main', NULL, 1, ?)`
1568
1562
  ).run(crypto.randomUUID(), chat.id, Date.now());
1563
+ return {
1564
+ id: row.id,
1565
+ userId: row.userId,
1566
+ title: row.title ?? void 0,
1567
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1568
+ createdAt: row.createdAt,
1569
+ updatedAt: row.updatedAt
1570
+ };
1569
1571
  });
1570
1572
  }
1571
1573
  async upsertChat(chat) {
@@ -1708,21 +1710,16 @@ var SqliteContextStore = class extends ContextStore {
1708
1710
  // Message Operations (Graph Nodes)
1709
1711
  // ==========================================================================
1710
1712
  async addMessage(message2) {
1711
- this.#db.prepare(
1713
+ if (message2.parentId === message2.id) {
1714
+ throw new Error(`Message ${message2.id} cannot be its own parent`);
1715
+ }
1716
+ this.#stmt(
1712
1717
  `INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
1713
- VALUES (
1714
- ?1,
1715
- ?2,
1716
- CASE WHEN ?3 = ?1 THEN (SELECT parentId FROM messages WHERE id = ?1) ELSE ?3 END,
1717
- ?4,
1718
- ?5,
1719
- ?6,
1720
- ?7
1721
- )
1722
- ON CONFLICT(id) DO UPDATE SET
1723
- name = excluded.name,
1724
- type = excluded.type,
1725
- data = excluded.data`
1718
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1719
+ ON CONFLICT(id) DO UPDATE SET
1720
+ name = excluded.name,
1721
+ type = excluded.type,
1722
+ data = excluded.data`
1726
1723
  ).run(
1727
1724
  message2.id,
1728
1725
  message2.chatId,
@@ -1733,14 +1730,16 @@ var SqliteContextStore = class extends ContextStore {
1733
1730
  message2.createdAt
1734
1731
  );
1735
1732
  const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
1736
- this.#db.prepare(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
1737
- this.#db.prepare(
1733
+ this.#stmt(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
1734
+ this.#stmt(
1738
1735
  `INSERT INTO messages_fts(messageId, chatId, name, content)
1739
- VALUES (?, ?, ?, ?)`
1736
+ VALUES (?, ?, ?, ?)`
1740
1737
  ).run(message2.id, message2.chatId, message2.name, content);
1741
1738
  }
1742
1739
  async getMessage(messageId) {
1743
- const row = this.#db.prepare("SELECT * FROM messages WHERE id = ?").get(messageId);
1740
+ const row = this.#stmt("SELECT * FROM messages WHERE id = ?").get(
1741
+ messageId
1742
+ );
1744
1743
  if (!row) {
1745
1744
  return void 0;
1746
1745
  }
@@ -1755,15 +1754,16 @@ var SqliteContextStore = class extends ContextStore {
1755
1754
  };
1756
1755
  }
1757
1756
  async getMessageChain(headId) {
1758
- const rows = this.#db.prepare(
1757
+ const rows = this.#stmt(
1759
1758
  `WITH RECURSIVE chain AS (
1760
- SELECT *, 0 as depth FROM messages WHERE id = ?
1761
- UNION ALL
1762
- SELECT m.*, c.depth + 1 FROM messages m
1763
- INNER JOIN chain c ON m.id = c.parentId
1764
- )
1765
- SELECT * FROM chain
1766
- ORDER BY depth DESC`
1759
+ SELECT *, 0 as depth FROM messages WHERE id = ?
1760
+ UNION ALL
1761
+ SELECT m.*, c.depth + 1 FROM messages m
1762
+ INNER JOIN chain c ON m.id = c.parentId
1763
+ WHERE c.depth < 100000
1764
+ )
1765
+ SELECT * FROM chain
1766
+ ORDER BY depth DESC`
1767
1767
  ).all(headId);
1768
1768
  return rows.map((row) => ({
1769
1769
  id: row.id,
@@ -1776,7 +1776,7 @@ var SqliteContextStore = class extends ContextStore {
1776
1776
  }));
1777
1777
  }
1778
1778
  async hasChildren(messageId) {
1779
- const row = this.#db.prepare(
1779
+ const row = this.#stmt(
1780
1780
  "SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ?) as hasChildren"
1781
1781
  ).get(messageId);
1782
1782
  return row.hasChildren === 1;
@@ -1823,7 +1823,9 @@ var SqliteContextStore = class extends ContextStore {
1823
1823
  };
1824
1824
  }
1825
1825
  async getActiveBranch(chatId) {
1826
- const row = this.#db.prepare("SELECT * FROM branches WHERE chatId = ? AND isActive = 1").get(chatId);
1826
+ const row = this.#stmt(
1827
+ "SELECT * FROM branches WHERE chatId = ? AND isActive = 1"
1828
+ ).get(chatId);
1827
1829
  if (!row) {
1828
1830
  return void 0;
1829
1831
  }
@@ -1841,45 +1843,43 @@ var SqliteContextStore = class extends ContextStore {
1841
1843
  this.#db.prepare("UPDATE branches SET isActive = 1 WHERE id = ?").run(branchId);
1842
1844
  }
1843
1845
  async updateBranchHead(branchId, messageId) {
1844
- this.#db.prepare("UPDATE branches SET headMessageId = ? WHERE id = ?").run(messageId, branchId);
1846
+ this.#stmt("UPDATE branches SET headMessageId = ? WHERE id = ?").run(
1847
+ messageId,
1848
+ branchId
1849
+ );
1845
1850
  }
1846
1851
  async listBranches(chatId) {
1847
- const branches = this.#db.prepare(
1852
+ const rows = this.#db.prepare(
1848
1853
  `SELECT
1849
1854
  b.id,
1850
1855
  b.name,
1851
1856
  b.headMessageId,
1852
1857
  b.isActive,
1853
- b.createdAt
1858
+ b.createdAt,
1859
+ COALESCE(
1860
+ (
1861
+ WITH RECURSIVE chain AS (
1862
+ SELECT id, parentId FROM messages WHERE id = b.headMessageId
1863
+ UNION ALL
1864
+ SELECT m.id, m.parentId FROM messages m
1865
+ INNER JOIN chain c ON m.id = c.parentId
1866
+ )
1867
+ SELECT COUNT(*) FROM chain
1868
+ ),
1869
+ 0
1870
+ ) as messageCount
1854
1871
  FROM branches b
1855
1872
  WHERE b.chatId = ?
1856
1873
  ORDER BY b.createdAt ASC`
1857
1874
  ).all(chatId);
1858
- const result = [];
1859
- for (const branch of branches) {
1860
- let messageCount = 0;
1861
- if (branch.headMessageId) {
1862
- const countRow = this.#db.prepare(
1863
- `WITH RECURSIVE chain AS (
1864
- SELECT id, parentId FROM messages WHERE id = ?
1865
- UNION ALL
1866
- SELECT m.id, m.parentId FROM messages m
1867
- INNER JOIN chain c ON m.id = c.parentId
1868
- )
1869
- SELECT COUNT(*) as count FROM chain`
1870
- ).get(branch.headMessageId);
1871
- messageCount = countRow.count;
1872
- }
1873
- result.push({
1874
- id: branch.id,
1875
- name: branch.name,
1876
- headMessageId: branch.headMessageId,
1877
- isActive: branch.isActive === 1,
1878
- messageCount,
1879
- createdAt: branch.createdAt
1880
- });
1881
- }
1882
- return result;
1875
+ return rows.map((row) => ({
1876
+ id: row.id,
1877
+ name: row.name,
1878
+ headMessageId: row.headMessageId,
1879
+ isActive: row.isActive === 1,
1880
+ messageCount: row.messageCount,
1881
+ createdAt: row.createdAt
1882
+ }));
1883
1883
  }
1884
1884
  // ==========================================================================
1885
1885
  // Checkpoint Operations
@@ -2147,8 +2147,10 @@ var Agent = class _Agent {
2147
2147
  execute: async ({ writer }) => {
2148
2148
  let currentResult = result;
2149
2149
  let attempt = 0;
2150
+ const { mounts } = context.getSkillMounts();
2150
2151
  const guardrailContext = {
2151
- availableTools: Object.keys(this.tools)
2152
+ availableTools: Object.keys(this.tools),
2153
+ availableSkills: mounts
2152
2154
  };
2153
2155
  while (attempt < maxRetries) {
2154
2156
  if (config?.abortSignal?.aborted) {
@@ -2176,10 +2178,20 @@ var Agent = class _Agent {
2176
2178
  );
2177
2179
  break;
2178
2180
  }
2181
+ if (checkResult.type === "stop") {
2182
+ console.log(
2183
+ chalk2.red(
2184
+ `[${this.#options.name}] Guardrail stopped - unrecoverable error, no retry`
2185
+ )
2186
+ );
2187
+ writer.write(part);
2188
+ writer.write({ type: "finish" });
2189
+ return;
2190
+ }
2179
2191
  if (checkResult.part.type === "text-delta") {
2180
2192
  accumulatedText += checkResult.part.delta;
2181
2193
  }
2182
- writer.write(checkResult.part);
2194
+ writer.write(part);
2183
2195
  }
2184
2196
  if (!guardrailFailed) {
2185
2197
  writer.write({ type: "finish" });
@@ -2307,7 +2319,7 @@ var ReportGrounding = class extends AbstractGrounding {
2307
2319
  }),
2308
2320
  fragment(
2309
2321
  "instructions",
2310
- dedent`
2322
+ dedent2`
2311
2323
  Write a business context that helps another agent answer questions accurately.
2312
2324
 
2313
2325
  For EACH table, do queries ONE AT A TIME: