@deepagents/text2sql 0.12.0 → 0.13.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 (29) hide show
  1. package/dist/index.js +263 -81
  2. package/dist/index.js.map +4 -4
  3. package/dist/lib/adapters/groundings/index.js +167 -11
  4. package/dist/lib/adapters/groundings/index.js.map +4 -4
  5. package/dist/lib/adapters/mysql/index.js +167 -11
  6. package/dist/lib/adapters/mysql/index.js.map +4 -4
  7. package/dist/lib/adapters/postgres/index.js +167 -11
  8. package/dist/lib/adapters/postgres/index.js.map +4 -4
  9. package/dist/lib/adapters/spreadsheet/index.js +2 -0
  10. package/dist/lib/adapters/spreadsheet/index.js.map +3 -3
  11. package/dist/lib/adapters/sqlite/index.js +167 -11
  12. package/dist/lib/adapters/sqlite/index.js.map +4 -4
  13. package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts.map +1 -1
  14. package/dist/lib/adapters/sqlserver/index.js +168 -14
  15. package/dist/lib/adapters/sqlserver/index.js.map +4 -4
  16. package/dist/lib/agents/result-tools.d.ts +12 -3
  17. package/dist/lib/agents/result-tools.d.ts.map +1 -1
  18. package/dist/lib/agents/sql.agent.d.ts +1 -1
  19. package/dist/lib/agents/sql.agent.d.ts.map +1 -1
  20. package/dist/lib/instructions.d.ts.map +1 -1
  21. package/dist/lib/sql.d.ts +4 -15
  22. package/dist/lib/sql.d.ts.map +1 -1
  23. package/dist/lib/synthesis/index.js +176 -10
  24. package/dist/lib/synthesis/index.js.map +4 -4
  25. package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts +3 -3
  26. package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts.map +1 -1
  27. package/dist/lib/synthesis/synthesizers/schema-synthesizer.d.ts +1 -1
  28. package/dist/lib/synthesis/synthesizers/schema-synthesizer.d.ts.map +1 -1
  29. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -392,6 +392,7 @@ import z2 from "zod";
392
392
  import { toState } from "@deepagents/agent";
393
393
 
394
394
  // packages/context/dist/index.js
395
+ import { mergeWith } from "lodash-es";
395
396
  import { encode } from "gpt-tokenizer";
396
397
  import { generateId } from "ai";
397
398
  import pluralize from "pluralize";
@@ -603,7 +604,7 @@ function message(content) {
603
604
  } : content;
604
605
  return {
605
606
  id: message2.id,
606
- name: "message",
607
+ name: message2.role,
607
608
  data: "content",
608
609
  type: "message",
609
610
  persist: true,
@@ -625,6 +626,22 @@ function assistantText(content, options) {
625
626
  parts: [{ type: "text", text: content }]
626
627
  });
627
628
  }
629
+ var LAZY_ID = Symbol("lazy-id");
630
+ function isLazyFragment(fragment2) {
631
+ return LAZY_ID in fragment2;
632
+ }
633
+ function lastAssistantMessage(content) {
634
+ return {
635
+ name: "assistant",
636
+ type: "message",
637
+ persist: true,
638
+ data: "content",
639
+ [LAZY_ID]: {
640
+ type: "last-assistant",
641
+ content
642
+ }
643
+ };
644
+ }
628
645
  var ContextRenderer = class {
629
646
  options;
630
647
  constructor(options = {}) {
@@ -951,6 +968,8 @@ var ContextEngine = class {
951
968
  #branch = null;
952
969
  #chatData = null;
953
970
  #initialized = false;
971
+ /** Initial metadata to merge on first initialization */
972
+ #initialMetadata;
954
973
  constructor(options) {
955
974
  if (!options.chatId) {
956
975
  throw new Error("chatId is required");
@@ -962,6 +981,7 @@ var ContextEngine = class {
962
981
  this.#chatId = options.chatId;
963
982
  this.#userId = options.userId;
964
983
  this.#branchName = "main";
984
+ this.#initialMetadata = options.metadata;
965
985
  }
966
986
  /**
967
987
  * Initialize the chat and branch if they don't exist.
@@ -974,6 +994,15 @@ var ContextEngine = class {
974
994
  id: this.#chatId,
975
995
  userId: this.#userId
976
996
  });
997
+ if (this.#initialMetadata) {
998
+ this.#chatData = await this.#store.updateChat(this.#chatId, {
999
+ metadata: {
1000
+ ...this.#chatData.metadata,
1001
+ ...this.#initialMetadata
1002
+ }
1003
+ });
1004
+ this.#initialMetadata = void 0;
1005
+ }
977
1006
  this.#branch = await this.#store.getActiveBranch(this.#chatId);
978
1007
  this.#initialized = true;
979
1008
  }
@@ -1121,6 +1150,12 @@ var ContextEngine = class {
1121
1150
  if (this.#pendingMessages.length === 0) {
1122
1151
  return;
1123
1152
  }
1153
+ for (let i = 0; i < this.#pendingMessages.length; i++) {
1154
+ const fragment2 = this.#pendingMessages[i];
1155
+ if (isLazyFragment(fragment2)) {
1156
+ this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
1157
+ }
1158
+ }
1124
1159
  let parentId = this.#branch.headMessageId;
1125
1160
  const now = Date.now();
1126
1161
  for (const fragment2 of this.#pendingMessages) {
@@ -1140,6 +1175,39 @@ var ContextEngine = class {
1140
1175
  this.#branch.headMessageId = parentId;
1141
1176
  this.#pendingMessages = [];
1142
1177
  }
1178
+ /**
1179
+ * Resolve a lazy fragment by finding the appropriate ID.
1180
+ */
1181
+ async #resolveLazyFragment(fragment2) {
1182
+ const lazy = fragment2[LAZY_ID];
1183
+ if (lazy.type === "last-assistant") {
1184
+ const lastId = await this.#getLastAssistantId();
1185
+ return assistantText(lazy.content, { id: lastId ?? crypto.randomUUID() });
1186
+ }
1187
+ throw new Error(`Unknown lazy fragment type: ${lazy.type}`);
1188
+ }
1189
+ /**
1190
+ * Find the most recent assistant message ID (pending or persisted).
1191
+ */
1192
+ async #getLastAssistantId() {
1193
+ for (let i = this.#pendingMessages.length - 1; i >= 0; i--) {
1194
+ const msg = this.#pendingMessages[i];
1195
+ if (msg.name === "assistant" && !isLazyFragment(msg)) {
1196
+ return msg.id;
1197
+ }
1198
+ }
1199
+ if (this.#branch?.headMessageId) {
1200
+ const chain = await this.#store.getMessageChain(
1201
+ this.#branch.headMessageId
1202
+ );
1203
+ for (let i = chain.length - 1; i >= 0; i--) {
1204
+ if (chain[i].name === "assistant") {
1205
+ return chain[i].id;
1206
+ }
1207
+ }
1208
+ }
1209
+ return void 0;
1210
+ }
1143
1211
  /**
1144
1212
  * Estimate token count and cost for the full context.
1145
1213
  *
@@ -1408,6 +1476,36 @@ var ContextEngine = class {
1408
1476
  }
1409
1477
  this.#chatData = await this.#store.updateChat(this.#chatId, storeUpdates);
1410
1478
  }
1479
+ /**
1480
+ * Track token usage for the current chat.
1481
+ * Accumulates usage metrics in chat.metadata.usage.
1482
+ *
1483
+ * @param usage - Token usage from AI SDK (LanguageModelUsage)
1484
+ *
1485
+ * @example
1486
+ * ```ts
1487
+ * // In onFinish callback
1488
+ * const usage = await result.totalUsage;
1489
+ * await context.trackUsage(usage);
1490
+ * ```
1491
+ */
1492
+ async trackUsage(usage) {
1493
+ await this.#ensureInitialized();
1494
+ const freshChatData = await this.#store.getChat(this.#chatId);
1495
+ const currentUsage = freshChatData?.metadata?.usage ?? {};
1496
+ const updatedUsage = mergeWith(
1497
+ {},
1498
+ currentUsage,
1499
+ usage,
1500
+ (a, b) => typeof a === "number" || typeof b === "number" ? (a ?? 0) + (b ?? 0) : void 0
1501
+ );
1502
+ this.#chatData = await this.#store.updateChat(this.#chatId, {
1503
+ metadata: {
1504
+ ...freshChatData?.metadata,
1505
+ usage: updatedUsage
1506
+ }
1507
+ });
1508
+ }
1411
1509
  /**
1412
1510
  * Consolidate context fragments (no-op for now).
1413
1511
  *
@@ -1419,6 +1517,35 @@ var ContextEngine = class {
1419
1517
  consolidate() {
1420
1518
  return void 0;
1421
1519
  }
1520
+ /**
1521
+ * Extract skill path mappings from available_skills fragments.
1522
+ * Returns array of { host, sandbox } for mounting in sandbox filesystem.
1523
+ *
1524
+ * Reads the original `paths` configuration stored in fragment metadata
1525
+ * by the skills() fragment helper.
1526
+ *
1527
+ * @example
1528
+ * ```ts
1529
+ * const context = new ContextEngine({ store, chatId, userId })
1530
+ * .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));
1531
+ *
1532
+ * const mounts = context.getSkillMounts();
1533
+ * // [{ host: './skills', sandbox: '/skills' }]
1534
+ * ```
1535
+ */
1536
+ getSkillMounts() {
1537
+ const mounts = [];
1538
+ for (const fragment2 of this.#fragments) {
1539
+ if (fragment2.name === "available_skills" && fragment2.metadata && Array.isArray(fragment2.metadata.paths)) {
1540
+ for (const mapping of fragment2.metadata.paths) {
1541
+ if (typeof mapping === "object" && mapping !== null && typeof mapping.host === "string" && typeof mapping.sandbox === "string") {
1542
+ mounts.push({ host: mapping.host, sandbox: mapping.sandbox });
1543
+ }
1544
+ }
1545
+ }
1546
+ }
1547
+ return mounts;
1548
+ }
1422
1549
  /**
1423
1550
  * Inspect the full context state for debugging.
1424
1551
  * Returns a JSON-serializable object with context information.
@@ -1658,9 +1785,10 @@ var errorRecoveryGuardrail = {
1658
1785
  "My response format was invalid. Let me try again with a properly formatted response."
1659
1786
  );
1660
1787
  }
1788
+ console.dir({ part }, { depth: null });
1661
1789
  return logAndFail(
1662
1790
  "Unknown error",
1663
- `An error occurred: ${errorText.slice(0, 100)}. Let me try a different approach.`
1791
+ `An error occurred: ${errorText}. Let me try a different approach.`
1664
1792
  );
1665
1793
  }
1666
1794
  };
@@ -1845,12 +1973,20 @@ var SqliteContextStore = class extends ContextStore {
1845
1973
  }
1846
1974
  async listChats(options) {
1847
1975
  const params = [];
1848
- let whereClause = "";
1976
+ const whereClauses = [];
1849
1977
  let limitClause = "";
1850
1978
  if (options?.userId) {
1851
- whereClause = "WHERE c.userId = ?";
1979
+ whereClauses.push("c.userId = ?");
1852
1980
  params.push(options.userId);
1853
1981
  }
1982
+ if (options?.metadata) {
1983
+ whereClauses.push(`json_extract(c.metadata, '$.' || ?) = ?`);
1984
+ params.push(options.metadata.key);
1985
+ params.push(
1986
+ typeof options.metadata.value === "boolean" ? options.metadata.value ? 1 : 0 : options.metadata.value
1987
+ );
1988
+ }
1989
+ const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
1854
1990
  if (options?.limit !== void 0) {
1855
1991
  limitClause = " LIMIT ?";
1856
1992
  params.push(options.limit);
@@ -1864,6 +2000,7 @@ var SqliteContextStore = class extends ContextStore {
1864
2000
  c.id,
1865
2001
  c.userId,
1866
2002
  c.title,
2003
+ c.metadata,
1867
2004
  c.createdAt,
1868
2005
  c.updatedAt,
1869
2006
  COUNT(DISTINCT m.id) as messageCount,
@@ -1879,6 +2016,7 @@ var SqliteContextStore = class extends ContextStore {
1879
2016
  id: row.id,
1880
2017
  userId: row.userId,
1881
2018
  title: row.title ?? void 0,
2019
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1882
2020
  messageCount: row.messageCount,
1883
2021
  branchCount: row.branchCount,
1884
2022
  createdAt: row.createdAt,
@@ -1910,9 +2048,16 @@ var SqliteContextStore = class extends ContextStore {
1910
2048
  async addMessage(message2) {
1911
2049
  this.#db.prepare(
1912
2050
  `INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
1913
- VALUES (?, ?, ?, ?, ?, ?, ?)
2051
+ VALUES (
2052
+ ?1,
2053
+ ?2,
2054
+ CASE WHEN ?3 = ?1 THEN (SELECT parentId FROM messages WHERE id = ?1) ELSE ?3 END,
2055
+ ?4,
2056
+ ?5,
2057
+ ?6,
2058
+ ?7
2059
+ )
1914
2060
  ON CONFLICT(id) DO UPDATE SET
1915
- parentId = excluded.parentId,
1916
2061
  name = excluded.name,
1917
2062
  type = excluded.type,
1918
2063
  data = excluded.data`
@@ -2387,13 +2532,9 @@ var Agent = class _Agent {
2387
2532
  writer.write({ type: "finish" });
2388
2533
  return;
2389
2534
  }
2390
- writer.write({
2391
- type: "text-delta",
2392
- id: generateId2(),
2393
- delta: ` ${failureFeedback}`
2394
- });
2535
+ writeText(writer, failureFeedback);
2395
2536
  const selfCorrectionText = accumulatedText + " " + failureFeedback;
2396
- context.set(assistantText(selfCorrectionText));
2537
+ context.set(lastAssistantMessage(selfCorrectionText));
2397
2538
  await context.save();
2398
2539
  currentResult = await this.#createRawStream(
2399
2540
  contextVariables,
@@ -2484,10 +2625,10 @@ var repairToolCall = async ({
2484
2625
  if (NoSuchToolError.isInstance(error)) {
2485
2626
  return null;
2486
2627
  }
2487
- const tool3 = tools3[toolCall.toolName];
2628
+ const tool4 = tools3[toolCall.toolName];
2488
2629
  const { output } = await generateText({
2489
2630
  model: groq("openai/gpt-oss-20b"),
2490
- output: Output.object({ schema: tool3.inputSchema }),
2631
+ output: Output.object({ schema: tool4.inputSchema }),
2491
2632
  prompt: [
2492
2633
  `The model tried to call the tool "${toolCall.toolName}" with the following inputs:`,
2493
2634
  JSON.stringify(toolCall.input),
@@ -2498,6 +2639,22 @@ var repairToolCall = async ({
2498
2639
  });
2499
2640
  return { ...toolCall, input: JSON.stringify(output) };
2500
2641
  };
2642
+ function writeText(writer, text) {
2643
+ const feedbackPartId = generateId2();
2644
+ writer.write({
2645
+ id: feedbackPartId,
2646
+ type: "text-start"
2647
+ });
2648
+ writer.write({
2649
+ id: feedbackPartId,
2650
+ type: "text-delta",
2651
+ delta: ` ${text}`
2652
+ });
2653
+ writer.write({
2654
+ id: feedbackPartId,
2655
+ type: "text-end"
2656
+ });
2657
+ }
2501
2658
 
2502
2659
  // packages/text2sql/src/lib/agents/explainer.agent.ts
2503
2660
  import { groq as groq2 } from "@ai-sdk/groq";
@@ -2602,10 +2759,12 @@ var fragments = [
2602
2759
  var developer_agent_default = { tools, fragments };
2603
2760
 
2604
2761
  // packages/text2sql/src/lib/agents/result-tools.ts
2762
+ import "ai";
2605
2763
  import { createBashTool as createBashTool2 } from "bash-tool";
2606
2764
  import chalk3 from "chalk";
2607
2765
  import {
2608
2766
  Bash,
2767
+ InMemoryFs,
2609
2768
  MountableFs,
2610
2769
  OverlayFs,
2611
2770
  ReadWriteFs,
@@ -2613,6 +2772,7 @@ import {
2613
2772
  } from "just-bash";
2614
2773
  import * as fs from "node:fs/promises";
2615
2774
  import * as path from "node:path";
2775
+ import { v7 } from "uuid";
2616
2776
  function createCommand(name, subcommands) {
2617
2777
  const usageLines = Object.entries(subcommands).map(([, def]) => ` ${name} ${def.usage.padEnd(30)} ${def.description}`).join("\n");
2618
2778
  return defineCommand2(name, async (args, ctx) => {
@@ -2663,12 +2823,20 @@ function createSqlCommand(adapter) {
2663
2823
  try {
2664
2824
  const rows = await adapter.execute(query);
2665
2825
  const rowsArray = Array.isArray(rows) ? rows : [];
2666
- const filePath = `/results/${crypto.randomUUID()}.json`;
2667
- await ctx.fs.writeFile(filePath, JSON.stringify(rowsArray, null, 2));
2826
+ const content = JSON.stringify(rowsArray, null, 2);
2827
+ const filename = `${v7()}.json`;
2828
+ const isolatedPath = `/results/${filename}`;
2829
+ const sharedPath = `/artifacts/${filename}`;
2830
+ await Promise.all([
2831
+ ctx.fs.writeFile(isolatedPath, content),
2832
+ // Current turn's isolated copy
2833
+ ctx.fs.writeFile(sharedPath, content)
2834
+ // Shared copy for cross-turn access
2835
+ ]);
2668
2836
  const columns = rowsArray.length > 0 ? Object.keys(rowsArray[0]) : [];
2669
2837
  return {
2670
2838
  stdout: [
2671
- filePath,
2839
+ `results stored in ${sharedPath}`,
2672
2840
  `columns: ${columns.join(", ") || "(none)"}`,
2673
2841
  `rows: ${rowsArray.length}`
2674
2842
  ].join("\n") + "\n",
@@ -2714,14 +2882,24 @@ function createSqlCommand(adapter) {
2714
2882
  });
2715
2883
  }
2716
2884
  async function createResultTools(options) {
2717
- const { adapter, chatId, messageId } = options;
2885
+ const { adapter, chatId, messageId, skillMounts = [] } = options;
2718
2886
  const sqlCommand = createSqlCommand(adapter);
2719
- const chatDir = path.join(process.cwd(), "artifacts", chatId);
2887
+ const root = process.env.TEXT2SQL_FS_ROOT || process.cwd();
2888
+ const chatDir = path.join(root, "artifacts", chatId);
2720
2889
  const resultsDir = path.join(chatDir, messageId, "results");
2721
2890
  await fs.mkdir(resultsDir, { recursive: true });
2891
+ const fsMounts = skillMounts.map(({ host, sandbox: sandbox2 }) => ({
2892
+ mountPoint: sandbox2,
2893
+ filesystem: new OverlayFs({
2894
+ root: host,
2895
+ mountPoint: "/",
2896
+ readOnly: true
2897
+ })
2898
+ }));
2722
2899
  const filesystem = new MountableFs({
2723
- base: new OverlayFs({ root: process.cwd() }),
2900
+ base: new InMemoryFs(),
2724
2901
  mounts: [
2902
+ ...fsMounts,
2725
2903
  {
2726
2904
  mountPoint: "/results",
2727
2905
  filesystem: new ReadWriteFs({ root: resultsDir })
@@ -2739,10 +2917,6 @@ async function createResultTools(options) {
2739
2917
  const { bash, sandbox } = await createBashTool2({
2740
2918
  sandbox: bashInstance,
2741
2919
  destination: "/",
2742
- uploadDirectory: {
2743
- source: process.cwd(),
2744
- include: "packages/text2sql/src/skills/**/*.md"
2745
- },
2746
2920
  onBeforeBashCall: ({ command }) => {
2747
2921
  console.log(chalk3.cyan(`[onBeforeBashCall]: ${command}`));
2748
2922
  return { command };
@@ -2765,7 +2939,9 @@ import {
2765
2939
  NoContentGeneratedError,
2766
2940
  NoObjectGeneratedError,
2767
2941
  NoOutputGeneratedError,
2768
- TypeValidationError
2942
+ TypeValidationError,
2943
+ defaultSettingsMiddleware,
2944
+ wrapLanguageModel
2769
2945
  } from "ai";
2770
2946
  import { Console } from "node:console";
2771
2947
  import { createWriteStream } from "node:fs";
@@ -2777,6 +2953,7 @@ var logger = new Console({
2777
2953
  stderr: createWriteStream("./sql-agent-error.log", { flags: "a" }),
2778
2954
  inspectOptions: { depth: null }
2779
2955
  });
2956
+ var RETRY_TEMPERATURES = [0, 0.2, 0.3];
2780
2957
  function extractSql(output) {
2781
2958
  const match = output.match(/```sql\n?([\s\S]*?)```/);
2782
2959
  return match ? match[1].trim() : output.trim();
@@ -2830,8 +3007,14 @@ async function toSql(options) {
2830
3007
  } else {
2831
3008
  context.set(user(options.input));
2832
3009
  }
3010
+ const temperature = RETRY_TEMPERATURES[attemptNumber - 1] ?? RETRY_TEMPERATURES[RETRY_TEMPERATURES.length - 1];
3011
+ const baseModel = options.model ?? groq3("openai/gpt-oss-20b");
3012
+ const model = wrapLanguageModel({
3013
+ model: baseModel,
3014
+ middleware: defaultSettingsMiddleware({ settings: { temperature } })
3015
+ });
2833
3016
  const sqlOutput = structuredOutput({
2834
- model: options.model ?? groq3("openai/gpt-oss-20b"),
3017
+ model,
2835
3018
  context,
2836
3019
  schema: z3.union([
2837
3020
  z3.object({
@@ -2971,12 +3154,12 @@ var suggestionsAgent = agent2({
2971
3154
  });
2972
3155
 
2973
3156
  // packages/text2sql/src/lib/agents/text2sql.agent.ts
2974
- import { tool as tool2 } from "ai";
3157
+ import { tool as tool3 } from "ai";
2975
3158
  import z5 from "zod";
2976
3159
  import { toState as toState2 } from "@deepagents/agent";
2977
3160
  import { scratchpad_tool } from "@deepagents/toolbox";
2978
3161
  var tools2 = {
2979
- validate_query: tool2({
3162
+ validate_query: tool3({
2980
3163
  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
3164
  inputSchema: z5.object({
2982
3165
  sql: z5.string().describe("The SQL query to validate.")
@@ -2990,7 +3173,7 @@ var tools2 = {
2990
3173
  return "Query is valid.";
2991
3174
  }
2992
3175
  }),
2993
- db_query: tool2({
3176
+ db_query: tool3({
2994
3177
  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
3178
  inputSchema: z5.object({
2996
3179
  reasoning: z5.string().describe(
@@ -3327,6 +3510,10 @@ function reasoningFramework() {
3327
3510
  principle({
3328
3511
  title: "Inhibit your response",
3329
3512
  description: "Only take an action after all the above reasoning is completed. Once you've taken an action, you cannot take it back."
3513
+ }),
3514
+ principle({
3515
+ title: "Continuous self-monitoring",
3516
+ description: "Constantly evaluate your own reasoning process for any gaps, biases, or errors. Apply the above principles iteratively as needed."
3330
3517
  })
3331
3518
  )
3332
3519
  ];
@@ -3540,7 +3727,31 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
3540
3727
  "Maintain consistency with previous query structure when possible."
3541
3728
  ],
3542
3729
  notes: "If reference is ambiguous, ask which previous result or entity the user means."
3543
- })
3730
+ }),
3731
+ fragment(
3732
+ "Bash tool usage",
3733
+ workflow({
3734
+ task: "Query execution",
3735
+ steps: [
3736
+ 'Execute SQL through bash tool: sql run "SELECT ..."',
3737
+ "Read the output: file path, column names, and row count.",
3738
+ "Use column names to construct jq filters: cat <path> | jq '.[] | {col1, col2}'",
3739
+ "For large results, slice first: cat <path> | jq '.[:10]'"
3740
+ ]
3741
+ }),
3742
+ hint(
3743
+ `You cannot access sql through a tool, it'll fail so the proper way to access it is through the bash tool using "sql run" and "sql validate" commands.`
3744
+ ),
3745
+ hint(
3746
+ "The sql command outputs: file path, column names (comma-separated), and row count. Use column names to construct precise jq queries."
3747
+ ),
3748
+ hint(
3749
+ 'This is virtual bash environment and "sql" commands proxy to the database hence you cannot access sql files directly.'
3750
+ ),
3751
+ hint(
3752
+ "If a query fails, the sql command returns an error message in stderr."
3753
+ )
3754
+ )
3544
3755
  ];
3545
3756
  if (date === "strict") {
3546
3757
  baseTeachings.push(
@@ -3573,12 +3784,9 @@ var Text2Sql = class {
3573
3784
  #config;
3574
3785
  constructor(config) {
3575
3786
  this.#config = {
3787
+ teachingsOptions: config.teachingsOptions,
3576
3788
  adapter: config.adapter,
3577
- store: config.store,
3578
- instructions: [
3579
- ...guidelines(config.teachingsOptions),
3580
- ...config.instructions ?? []
3581
- ],
3789
+ context: config.context,
3582
3790
  tools: config.tools ?? {},
3583
3791
  model: config.model,
3584
3792
  introspection: new JsonCache(
@@ -3592,7 +3800,7 @@ var Text2Sql = class {
3592
3800
  input,
3593
3801
  adapter: this.#config.adapter,
3594
3802
  schemaFragments,
3595
- instructions: this.#config.instructions,
3803
+ instructions: [],
3596
3804
  model: this.#config.model
3597
3805
  });
3598
3806
  return result.sql;
@@ -3652,45 +3860,24 @@ var Text2Sql = class {
3652
3860
  })
3653
3861
  ];
3654
3862
  }
3655
- async chat(messages, params) {
3656
- const schemaFragments = await this.index();
3657
- const context = new ContextEngine({
3658
- store: this.#config.store,
3659
- chatId: params.chatId,
3660
- userId: params.userId
3661
- }).set(
3662
- ...schemaFragments,
3663
- ...this.#buildRenderingInstructions(),
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
3863
+ async chat(messages) {
3864
+ const context = this.#config.context(
3865
+ ...guidelines(this.#config.teachingsOptions),
3866
+ ...await this.index(),
3867
+ ...this.#buildRenderingInstructions()
3683
3868
  );
3684
3869
  const userMsg = messages.at(-1);
3685
3870
  if (userMsg) {
3686
- context.set(user(userMsg));
3871
+ context.set(message(userMsg));
3687
3872
  await context.save();
3688
3873
  }
3689
3874
  const messageId = userMsg?.id ?? generateId3();
3875
+ const skillMounts = context.getSkillMounts();
3690
3876
  const { bash } = await createResultTools({
3691
3877
  adapter: this.#config.adapter,
3692
- chatId: params.chatId,
3693
- messageId
3878
+ chatId: context.chatId,
3879
+ messageId,
3880
+ skillMounts
3694
3881
  });
3695
3882
  const chatAgent = agent({
3696
3883
  name: "text2sql",
@@ -3710,31 +3897,25 @@ var Text2Sql = class {
3710
3897
  sendFinish: true,
3711
3898
  sendReasoning: true,
3712
3899
  sendSources: true,
3900
+ originalMessages: messages,
3713
3901
  generateMessageId: generateId3,
3714
3902
  onFinish: async ({ responseMessage }) => {
3715
3903
  context.set(assistant(responseMessage));
3716
3904
  await context.save();
3905
+ await context.trackUsage(await result.totalUsage);
3717
3906
  }
3718
3907
  });
3719
3908
  }
3720
- /**
3721
- * Developer chat interface - power-user mode for SQL generation.
3722
- * Uses db_query tool for direct SQL execution (LLM writes SQL).
3723
- */
3724
- async developer(messages, params) {
3725
- const schemaFragments = await this.index();
3726
- const context = new ContextEngine({
3727
- store: this.#config.store,
3728
- chatId: params.chatId,
3729
- userId: params.userId
3730
- }).set(
3909
+ async developer(messages) {
3910
+ const context = this.#config.context(
3911
+ ...guidelines(this.#config.teachingsOptions),
3731
3912
  ...developer_agent_default.fragments,
3732
- ...this.#config.instructions,
3733
- ...schemaFragments
3913
+ ...await this.index(),
3914
+ ...this.#buildRenderingInstructions()
3734
3915
  );
3735
3916
  const userMsg = messages.at(-1);
3736
3917
  if (userMsg) {
3737
- context.set(user(userMsg));
3918
+ context.set(message(userMsg));
3738
3919
  await context.save();
3739
3920
  }
3740
3921
  const developerAgent = agent({
@@ -3756,6 +3937,7 @@ var Text2Sql = class {
3756
3937
  onFinish: async ({ responseMessage }) => {
3757
3938
  context.set(assistant(responseMessage));
3758
3939
  await context.save();
3940
+ await context.trackUsage(await result.totalUsage);
3759
3941
  }
3760
3942
  });
3761
3943
  }