@deepagents/text2sql 4.3.0 → 6.0.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.
- package/CLICKHOUSE-DEPLOYMENT.md +181 -0
- package/README.md +33 -7
- package/dist/bin/sql.js +33 -51
- package/dist/index.js +68 -299
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts +8 -1
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/bigquery.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/bigquery.sql-policy.d.ts +9 -0
- package/dist/lib/adapters/bigquery/bigquery.sql-policy.d.ts.map +1 -0
- package/dist/lib/adapters/bigquery/index.d.ts +1 -0
- package/dist/lib/adapters/bigquery/index.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +303 -259
- package/dist/lib/adapters/bigquery/index.js.map +4 -4
- package/dist/lib/adapters/clickhouse/clickhouse.d.ts +22 -0
- package/dist/lib/adapters/clickhouse/clickhouse.d.ts.map +1 -0
- package/dist/lib/adapters/clickhouse/clickhouse.sql-policy.d.ts +8 -0
- package/dist/lib/adapters/clickhouse/clickhouse.sql-policy.d.ts.map +1 -0
- package/dist/lib/adapters/clickhouse/constraint.clickhouse.grounding.d.ts +8 -0
- package/dist/lib/adapters/clickhouse/constraint.clickhouse.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/clickhouse/index.d.ts +33 -0
- package/dist/lib/adapters/clickhouse/index.d.ts.map +1 -0
- package/dist/lib/adapters/clickhouse/index.js +1544 -0
- package/dist/lib/adapters/clickhouse/index.js.map +7 -0
- package/dist/lib/adapters/clickhouse/indexes.clickhouse.grounding.d.ts +8 -0
- package/dist/lib/adapters/clickhouse/indexes.clickhouse.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/clickhouse/info.clickhouse.grounding.d.ts +8 -0
- package/dist/lib/adapters/clickhouse/info.clickhouse.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/clickhouse/row-count.clickhouse.grounding.d.ts +8 -0
- package/dist/lib/adapters/clickhouse/row-count.clickhouse.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/clickhouse/table.clickhouse.grounding.d.ts +11 -0
- package/dist/lib/adapters/clickhouse/table.clickhouse.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/clickhouse/view.clickhouse.grounding.d.ts +9 -0
- package/dist/lib/adapters/clickhouse/view.clickhouse.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/abstract.grounding.d.ts +11 -16
- package/dist/lib/adapters/groundings/abstract.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/index.js +16 -0
- package/dist/lib/adapters/groundings/index.js.map +2 -2
- package/dist/lib/adapters/groundings/table.grounding.d.ts +1 -0
- package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/view.grounding.d.ts +1 -0
- package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/mysql/index.d.ts +1 -0
- package/dist/lib/adapters/mysql/index.d.ts.map +1 -1
- package/dist/lib/adapters/mysql/index.js +317 -259
- package/dist/lib/adapters/mysql/index.js.map +4 -4
- package/dist/lib/adapters/mysql/mysql.d.ts.map +1 -1
- package/dist/lib/adapters/mysql/mysql.sql-policy.d.ts +11 -0
- package/dist/lib/adapters/mysql/mysql.sql-policy.d.ts.map +1 -0
- package/dist/lib/adapters/parser-sql-policy.d.ts +23 -0
- package/dist/lib/adapters/parser-sql-policy.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/index.d.ts +1 -0
- package/dist/lib/adapters/postgres/index.d.ts.map +1 -1
- package/dist/lib/adapters/postgres/index.js +323 -259
- package/dist/lib/adapters/postgres/index.js.map +4 -4
- package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -1
- package/dist/lib/adapters/postgres/postgres.sql-policy.d.ts +10 -0
- package/dist/lib/adapters/postgres/postgres.sql-policy.d.ts.map +1 -0
- package/dist/lib/adapters/spreadsheet/index.js +301 -259
- package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
- package/dist/lib/adapters/spreadsheet/parser.d.ts.map +1 -1
- package/dist/lib/adapters/sql-policy.d.ts +19 -0
- package/dist/lib/adapters/sql-policy.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/index.d.ts +1 -0
- package/dist/lib/adapters/sqlite/index.d.ts.map +1 -1
- package/dist/lib/adapters/sqlite/index.js +303 -259
- package/dist/lib/adapters/sqlite/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -1
- package/dist/lib/adapters/sqlite/sqlite.sql-policy.d.ts +8 -0
- package/dist/lib/adapters/sqlite/sqlite.sql-policy.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/index.d.ts +1 -0
- package/dist/lib/adapters/sqlserver/index.d.ts.map +1 -1
- package/dist/lib/adapters/sqlserver/index.js +310 -259
- package/dist/lib/adapters/sqlserver/index.js.map +4 -4
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -1
- package/dist/lib/adapters/sqlserver/sqlserver.sql-policy.d.ts +11 -0
- package/dist/lib/adapters/sqlserver/sqlserver.sql-policy.d.ts.map +1 -0
- package/dist/lib/agents/exceptions.d.ts +1 -1
- package/dist/lib/agents/sql-transform-plugins.d.ts +1 -2
- package/dist/lib/agents/sql-transform-plugins.d.ts.map +1 -1
- package/dist/lib/agents/sql.agent.d.ts.map +1 -1
- package/dist/lib/sql-command.d.ts.map +1 -1
- package/dist/lib/sql-hooks.d.ts +5 -1
- package/dist/lib/sql-hooks.d.ts.map +1 -1
- package/dist/lib/sql-meta-hook.d.ts +6 -2
- package/dist/lib/sql-meta-hook.d.ts.map +1 -1
- package/dist/lib/sql-scope-error.d.ts +3 -0
- package/dist/lib/sql-scope-error.d.ts.map +1 -0
- package/dist/lib/sql-validate-reminder.d.ts +3 -3
- package/dist/lib/sql-validate-reminder.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +32 -38
- package/dist/lib/synthesis/index.js.map +4 -4
- package/package.json +30 -24
- package/dist/lib/adapters/runtime-scope.d.ts +0 -16
- package/dist/lib/adapters/runtime-scope.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -22,10 +22,10 @@ function validateAdapterNames(names) {
|
|
|
22
22
|
import { format as formatSql } from "sql-formatter";
|
|
23
23
|
|
|
24
24
|
// packages/text2sql/src/lib/agents/exceptions.ts
|
|
25
|
-
var sqlValidationMarker = Symbol("SQLValidationError");
|
|
26
|
-
var unanswerableSqlMarker = Symbol("UnanswerableSQLError");
|
|
27
|
-
var sqlScopeMarker = Symbol("SQLScopeError");
|
|
28
|
-
var sqlReadOnlyMarker = Symbol("SQLReadOnlyError");
|
|
25
|
+
var sqlValidationMarker = /* @__PURE__ */ Symbol("SQLValidationError");
|
|
26
|
+
var unanswerableSqlMarker = /* @__PURE__ */ Symbol("UnanswerableSQLError");
|
|
27
|
+
var sqlScopeMarker = /* @__PURE__ */ Symbol("SQLScopeError");
|
|
28
|
+
var sqlReadOnlyMarker = /* @__PURE__ */ Symbol("SQLReadOnlyError");
|
|
29
29
|
var SQLValidationError = class _SQLValidationError extends Error {
|
|
30
30
|
[sqlValidationMarker];
|
|
31
31
|
constructor(message) {
|
|
@@ -190,159 +190,15 @@ function createGroundingContext(options = {}) {
|
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
// packages/text2sql/src/lib/adapters/runtime-scope.ts
|
|
194
|
-
import nodeSqlParser from "node-sql-parser";
|
|
195
|
-
var { Parser } = nodeSqlParser;
|
|
196
|
-
var parser = new Parser();
|
|
197
|
-
function parseStatementTypes(sql, dialect) {
|
|
198
|
-
const ast = parser.astify(sql, { database: dialect });
|
|
199
|
-
const statements = Array.isArray(ast) ? ast : [ast];
|
|
200
|
-
return statements.flatMap(
|
|
201
|
-
(statement) => isAstLike(statement) && typeof statement.type === "string" ? [statement.type] : []
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
function extractBaseEntityReferences(sql, dialect) {
|
|
205
|
-
const ast = parser.astify(sql, { database: dialect });
|
|
206
|
-
const state = {
|
|
207
|
-
cteNames: /* @__PURE__ */ new Set(),
|
|
208
|
-
references: /* @__PURE__ */ new Map()
|
|
209
|
-
};
|
|
210
|
-
visitNode(ast, state);
|
|
211
|
-
return Array.from(state.references.values());
|
|
212
|
-
}
|
|
213
|
-
function buildOutOfScopePayload(sql, referencedEntities, allowedEntities) {
|
|
214
|
-
return {
|
|
215
|
-
error: `Query references entities outside grounded scope: ${referencedEntities.join(", ")}`,
|
|
216
|
-
error_type: "OUT_OF_SCOPE",
|
|
217
|
-
suggestion: "Restrict the query to grounded tables/views or expand grounding to include the referenced entities.",
|
|
218
|
-
sql_attempted: sql,
|
|
219
|
-
referenced_entities: referencedEntities,
|
|
220
|
-
allowed_entities: allowedEntities
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
function buildScopeParseErrorPayload(sql, dialect, error) {
|
|
224
|
-
const parserError = error instanceof Error ? error.message : String(error ?? "Unknown error");
|
|
225
|
-
return {
|
|
226
|
-
error: `SQL scope analysis failed before validation/execution: ${parserError}`,
|
|
227
|
-
error_type: "SQL_SCOPE_PARSE_ERROR",
|
|
228
|
-
suggestion: "Rewrite the query into simpler SQL that can be analyzed safely, or extend parser coverage for this dialect feature.",
|
|
229
|
-
sql_attempted: sql,
|
|
230
|
-
parser_dialect: dialect,
|
|
231
|
-
parser_error: parserError
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
function visitNode(node, state) {
|
|
235
|
-
if (Array.isArray(node)) {
|
|
236
|
-
for (const item of node) {
|
|
237
|
-
visitNode(item, state);
|
|
238
|
-
}
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
if (!isAstLike(node)) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
if (isStatementNode(node)) {
|
|
245
|
-
visitStatement(node, state);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
if (isTableReferenceNode(node)) {
|
|
249
|
-
addReference(node, state);
|
|
250
|
-
}
|
|
251
|
-
for (const value of Object.values(node)) {
|
|
252
|
-
visitNode(value, state);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function visitStatement(node, parentState) {
|
|
256
|
-
const localState = {
|
|
257
|
-
cteNames: new Set(parentState.cteNames),
|
|
258
|
-
references: parentState.references
|
|
259
|
-
};
|
|
260
|
-
const withItems = Array.isArray(node.with) ? node.with : [];
|
|
261
|
-
for (const item of withItems) {
|
|
262
|
-
if (!isAstLike(item)) {
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
const cteName = readCteName(item);
|
|
266
|
-
if (cteName) {
|
|
267
|
-
localState.cteNames.add(caseFold(cteName));
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
for (const item of withItems) {
|
|
271
|
-
if (!isAstLike(item)) {
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
visitNode(item.stmt, localState);
|
|
275
|
-
}
|
|
276
|
-
for (const [key, value] of Object.entries(node)) {
|
|
277
|
-
if (key === "with") {
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
visitNode(value, localState);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
function addReference(node, state) {
|
|
284
|
-
const table2 = typeof node.table === "string" ? node.table : null;
|
|
285
|
-
if (!table2) {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
if (state.cteNames.has(caseFold(table2))) {
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
const db = typeof node.db === "string" ? node.db : null;
|
|
292
|
-
const key = db ? `${db}.${table2}` : table2;
|
|
293
|
-
if (!state.references.has(key)) {
|
|
294
|
-
state.references.set(key, { db, table: table2 });
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
function readCteName(node) {
|
|
298
|
-
const name = node.name;
|
|
299
|
-
if (typeof name === "string") {
|
|
300
|
-
return name;
|
|
301
|
-
}
|
|
302
|
-
if (!isAstLike(name)) {
|
|
303
|
-
return void 0;
|
|
304
|
-
}
|
|
305
|
-
const value = name.value;
|
|
306
|
-
return typeof value === "string" ? value : void 0;
|
|
307
|
-
}
|
|
308
|
-
function isStatementNode(node) {
|
|
309
|
-
const type = node.type;
|
|
310
|
-
return typeof type === "string" && ["delete", "insert", "replace", "select", "update"].includes(type);
|
|
311
|
-
}
|
|
312
|
-
function isTableReferenceNode(node) {
|
|
313
|
-
if (node.type === "column_ref") {
|
|
314
|
-
return false;
|
|
315
|
-
}
|
|
316
|
-
if (typeof node.table !== "string") {
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
return "addition" in node || "as" in node || "db" in node || "join" in node || "operator" in node || "surround" in node || "table_hint" in node || "temporal_table" in node;
|
|
320
|
-
}
|
|
321
|
-
function isAstLike(value) {
|
|
322
|
-
return typeof value === "object" && value !== null;
|
|
323
|
-
}
|
|
324
|
-
function caseFold(value) {
|
|
325
|
-
return value.toLowerCase();
|
|
326
|
-
}
|
|
327
|
-
|
|
328
193
|
// packages/text2sql/src/lib/adapters/adapter.ts
|
|
329
|
-
var PARSER_DIALECTS_BY_FORMATTER_LANGUAGE = {
|
|
330
|
-
sqlite: ["sqlite", "mysql"],
|
|
331
|
-
postgresql: ["postgresql"],
|
|
332
|
-
bigquery: ["bigquery"],
|
|
333
|
-
transactsql: ["transactsql"],
|
|
334
|
-
mysql: ["mysql"]
|
|
335
|
-
};
|
|
336
|
-
function parserDialectsFor(formatterLanguage) {
|
|
337
|
-
const candidates = PARSER_DIALECTS_BY_FORMATTER_LANGUAGE[formatterLanguage];
|
|
338
|
-
if (!candidates) {
|
|
339
|
-
throw new TypeError(
|
|
340
|
-
`No scope dialect mapping for formatter language "${formatterLanguage}". Add it to PARSER_DIALECTS_BY_FORMATTER_LANGUAGE in adapter.ts.`
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
return candidates;
|
|
344
|
-
}
|
|
345
194
|
var Adapter = class {
|
|
195
|
+
#policyAnalyzer;
|
|
196
|
+
/**
|
|
197
|
+
* Injects the strategy that guards validate() and execute().
|
|
198
|
+
*/
|
|
199
|
+
constructor(policyAnalyzer) {
|
|
200
|
+
this.#policyAnalyzer = policyAnalyzer;
|
|
201
|
+
}
|
|
346
202
|
/**
|
|
347
203
|
* Introspect the database schema and return context fragments.
|
|
348
204
|
*
|
|
@@ -378,14 +234,14 @@ var Adapter = class {
|
|
|
378
234
|
}
|
|
379
235
|
/**
|
|
380
236
|
* Resolve the allowed entity names (tables + views) from grounding config.
|
|
381
|
-
*
|
|
237
|
+
* Asks each grounding to contribute its entities; annotators contribute
|
|
238
|
+
* nothing, so their metadata scans are skipped.
|
|
382
239
|
* Results are NOT cached — call once and store the result.
|
|
383
240
|
*/
|
|
384
241
|
async resolveAllowedEntities() {
|
|
385
242
|
const ctx = createGroundingContext();
|
|
386
243
|
for (const fn of this.grounding) {
|
|
387
|
-
|
|
388
|
-
await grounding.execute(ctx);
|
|
244
|
+
await fn(this).contributeEntities(ctx);
|
|
389
245
|
}
|
|
390
246
|
return [...ctx.tables.map((t) => t.name), ...ctx.views.map((v) => v.name)];
|
|
391
247
|
}
|
|
@@ -562,120 +418,33 @@ var Adapter = class {
|
|
|
562
418
|
#decodeShellEscapes(sql) {
|
|
563
419
|
return sql.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\([().*])/g, "$1").replace(/\\(?=$)/gm, "");
|
|
564
420
|
}
|
|
565
|
-
#checkReadOnly(sql) {
|
|
566
|
-
const statementTypes = this.#readParsedStatementTypes(sql);
|
|
567
|
-
if (statementTypes) {
|
|
568
|
-
if (statementTypes.length === 1 && statementTypes[0] === "select") {
|
|
569
|
-
return null;
|
|
570
|
-
}
|
|
571
|
-
return "only SELECT or WITH queries allowed";
|
|
572
|
-
}
|
|
573
|
-
const keyword = this.#firstStatementKeyword(sql);
|
|
574
|
-
if (keyword === "SELECT" || keyword === "WITH") return null;
|
|
575
|
-
return "only SELECT or WITH queries allowed";
|
|
576
|
-
}
|
|
577
|
-
#readParsedStatementTypes(sql) {
|
|
578
|
-
for (const dialect of parserDialectsFor(this.formatterLanguage)) {
|
|
579
|
-
try {
|
|
580
|
-
return parseStatementTypes(sql, dialect);
|
|
581
|
-
} catch {
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
#firstStatementKeyword(sql) {
|
|
587
|
-
let offset = 0;
|
|
588
|
-
while (offset < sql.length) {
|
|
589
|
-
if (/\s/.test(sql[offset])) {
|
|
590
|
-
offset++;
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
if (sql.startsWith("--", offset)) {
|
|
594
|
-
offset += 2;
|
|
595
|
-
while (offset < sql.length && sql[offset] !== "\n" && sql[offset] !== "\r") {
|
|
596
|
-
offset++;
|
|
597
|
-
}
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
if (sql.startsWith("/*", offset)) {
|
|
601
|
-
const commentEnd = sql.indexOf("*/", offset + 2);
|
|
602
|
-
if (commentEnd === -1) return null;
|
|
603
|
-
offset = commentEnd + 2;
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
606
|
-
const keyword = /^[A-Za-z]+/.exec(sql.slice(offset));
|
|
607
|
-
return keyword ? keyword[0].toUpperCase() : null;
|
|
608
|
-
}
|
|
609
|
-
return null;
|
|
610
|
-
}
|
|
611
421
|
#cachedAllowedEntities = null;
|
|
612
422
|
async #resolveScope() {
|
|
613
423
|
if (this.#cachedAllowedEntities) return this.#cachedAllowedEntities;
|
|
614
424
|
this.#cachedAllowedEntities = await this.resolveAllowedEntities();
|
|
615
425
|
return this.#cachedAllowedEntities;
|
|
616
426
|
}
|
|
617
|
-
async #
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
let lastError;
|
|
621
|
-
let lastDialect = candidates[0];
|
|
622
|
-
for (const candidate of candidates) {
|
|
623
|
-
try {
|
|
624
|
-
references = extractBaseEntityReferences(sql, candidate);
|
|
625
|
-
break;
|
|
626
|
-
} catch (error) {
|
|
627
|
-
lastDialect = candidate;
|
|
628
|
-
lastError = error;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
if (references === null) {
|
|
632
|
-
return buildScopeParseErrorPayload(sql, lastDialect, lastError);
|
|
633
|
-
}
|
|
634
|
-
if (references.length === 0) return null;
|
|
635
|
-
const allowedQualified = new Set(
|
|
636
|
-
allowedEntities.map((e) => e.toLowerCase())
|
|
637
|
-
);
|
|
638
|
-
const allowedUnqualified = /* @__PURE__ */ new Set();
|
|
639
|
-
for (const entity of allowedEntities) {
|
|
640
|
-
const dot = entity.lastIndexOf(".");
|
|
641
|
-
if (dot !== -1) {
|
|
642
|
-
allowedUnqualified.add(entity.slice(dot + 1).toLowerCase());
|
|
643
|
-
} else {
|
|
644
|
-
allowedUnqualified.add(entity.toLowerCase());
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
const outOfScope = references.map((ref) => ref.db ? `${ref.db}.${ref.table}` : ref.table).filter((name) => {
|
|
648
|
-
const lower = name.toLowerCase();
|
|
649
|
-
if (name.includes(".")) {
|
|
650
|
-
if (allowedQualified.has(lower)) return false;
|
|
651
|
-
const parts = lower.split(".");
|
|
652
|
-
if (parts.length >= 3) {
|
|
653
|
-
const datasetTable = parts.slice(-2).join(".");
|
|
654
|
-
if (allowedQualified.has(datasetTable)) return false;
|
|
655
|
-
}
|
|
656
|
-
return true;
|
|
657
|
-
}
|
|
658
|
-
return !allowedQualified.has(lower) && !allowedUnqualified.has(lower);
|
|
427
|
+
async #analyzePolicy(sql) {
|
|
428
|
+
return this.#policyAnalyzer.analyze(sql, {
|
|
429
|
+
resolveAllowedEntities: () => this.#resolveScope()
|
|
659
430
|
});
|
|
660
|
-
if (outOfScope.length === 0) return null;
|
|
661
|
-
return buildOutOfScopePayload(sql, outOfScope, allowedEntities);
|
|
662
431
|
}
|
|
663
432
|
async validate(sql) {
|
|
664
433
|
const decoded = this.#decodeShellEscapes(sql);
|
|
665
|
-
const
|
|
666
|
-
if (
|
|
667
|
-
|
|
668
|
-
const scopeError = await this.#checkScope(decoded, allowed);
|
|
669
|
-
if (scopeError) return JSON.stringify(scopeError);
|
|
434
|
+
const violation = await this.#analyzePolicy(decoded);
|
|
435
|
+
if (violation?.kind === "read-only") return violation.message;
|
|
436
|
+
if (violation?.kind === "scope") return JSON.stringify(violation.payload);
|
|
670
437
|
return this.validateImpl(decoded);
|
|
671
438
|
}
|
|
672
439
|
async execute(sql) {
|
|
673
440
|
const decoded = this.#decodeShellEscapes(sql);
|
|
674
|
-
const
|
|
675
|
-
if (
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
if (
|
|
441
|
+
const violation = await this.#analyzePolicy(decoded);
|
|
442
|
+
if (violation?.kind === "read-only") {
|
|
443
|
+
throw new SQLReadOnlyError(violation.message);
|
|
444
|
+
}
|
|
445
|
+
if (violation?.kind === "scope") {
|
|
446
|
+
throw new SQLScopeError(violation.payload);
|
|
447
|
+
}
|
|
679
448
|
return this.executeImpl(decoded);
|
|
680
449
|
}
|
|
681
450
|
/**
|
|
@@ -827,16 +596,24 @@ import {
|
|
|
827
596
|
user,
|
|
828
597
|
workflow
|
|
829
598
|
} from "@deepagents/context";
|
|
599
|
+
|
|
600
|
+
// packages/text2sql/src/lib/sql-scope-error.ts
|
|
601
|
+
function buildScopeParseErrorPayload(sql, parserDialect, error) {
|
|
602
|
+
const parserError = error instanceof Error ? error.message : String(error ?? "Unknown error");
|
|
603
|
+
return {
|
|
604
|
+
error: `SQL scope analysis failed before validation/execution: ${parserError}`,
|
|
605
|
+
error_type: "SQL_SCOPE_PARSE_ERROR",
|
|
606
|
+
suggestion: "Rewrite the query into simpler SQL that can be analyzed safely, or extend parser coverage for this dialect feature.",
|
|
607
|
+
sql_attempted: sql,
|
|
608
|
+
parser_dialect: parserDialect,
|
|
609
|
+
parser_error: parserError
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// packages/text2sql/src/lib/agents/sql.agent.ts
|
|
830
614
|
var RETRY_TEMPERATURES = [0, 0.2, 0.3];
|
|
831
615
|
var SQL_AGENT_ROLE = "Expert SQL query generator.";
|
|
832
616
|
var SQL_AGENT_OBJECTIVE = "Generate precise SQL grounded in provided schema.";
|
|
833
|
-
var FORMATTER_SCOPE_DIALECTS = /* @__PURE__ */ new Map([
|
|
834
|
-
["sqlite", "sqlite"],
|
|
835
|
-
["postgresql", "postgresql"],
|
|
836
|
-
["bigquery", "bigquery"],
|
|
837
|
-
["transactsql", "transactsql"],
|
|
838
|
-
["mysql", "mysql"]
|
|
839
|
-
]);
|
|
840
617
|
var SQL_AGENT_POLICIES = [
|
|
841
618
|
fragment(
|
|
842
619
|
"schema_mapping",
|
|
@@ -1042,26 +819,6 @@ function extractSql(output) {
|
|
|
1042
819
|
const match = output.match(/```sql\n?([\s\S]*?)```/);
|
|
1043
820
|
return match ? match[1].trim() : output.trim();
|
|
1044
821
|
}
|
|
1045
|
-
function scopeDialectFor(adapter) {
|
|
1046
|
-
const dialect = FORMATTER_SCOPE_DIALECTS.get(adapter.formatterLanguage);
|
|
1047
|
-
if (!dialect) {
|
|
1048
|
-
throw new TypeError(
|
|
1049
|
-
`No scope dialect mapping for formatter language "${adapter.formatterLanguage}".`
|
|
1050
|
-
);
|
|
1051
|
-
}
|
|
1052
|
-
return dialect;
|
|
1053
|
-
}
|
|
1054
|
-
function fencedMarkdownParseError(sql, adapter) {
|
|
1055
|
-
return new SQLValidationError(
|
|
1056
|
-
JSON.stringify(
|
|
1057
|
-
buildScopeParseErrorPayload(
|
|
1058
|
-
sql,
|
|
1059
|
-
scopeDialectFor(adapter),
|
|
1060
|
-
new Error("SQL response was fenced markdown, not executable SQL.")
|
|
1061
|
-
)
|
|
1062
|
-
)
|
|
1063
|
-
);
|
|
1064
|
-
}
|
|
1065
822
|
async function toSql(options) {
|
|
1066
823
|
const { maxRetries = 3 } = options;
|
|
1067
824
|
return withRetry(
|
|
@@ -1143,7 +900,17 @@ Question: ${options.input}
|
|
|
1143
900
|
}
|
|
1144
901
|
const sql = options.adapter.format(extractedSql);
|
|
1145
902
|
if (rawSql.trimStart().startsWith("```") && extractedSql === rawSql.trim()) {
|
|
1146
|
-
throw
|
|
903
|
+
throw new SQLValidationError(
|
|
904
|
+
JSON.stringify(
|
|
905
|
+
buildScopeParseErrorPayload(
|
|
906
|
+
sql,
|
|
907
|
+
options.adapter.formatterLanguage,
|
|
908
|
+
new Error(
|
|
909
|
+
"SQL response was fenced markdown, not executable SQL."
|
|
910
|
+
)
|
|
911
|
+
)
|
|
912
|
+
)
|
|
913
|
+
);
|
|
1147
914
|
}
|
|
1148
915
|
const validationError = await options.adapter.validate(sql);
|
|
1149
916
|
if (validationError) {
|
|
@@ -4683,9 +4450,6 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4683
4450
|
return baseTeachings;
|
|
4684
4451
|
}
|
|
4685
4452
|
|
|
4686
|
-
// packages/text2sql/src/lib/sql-meta-hook.ts
|
|
4687
|
-
import { useBashMeta } from "@deepagents/context";
|
|
4688
|
-
|
|
4689
4453
|
// packages/text2sql/src/lib/sql-invocation.ts
|
|
4690
4454
|
import { parse } from "just-bash";
|
|
4691
4455
|
import { asStaticWordText } from "@deepagents/context";
|
|
@@ -4758,12 +4522,12 @@ function createSqlMetaHook({ adapters }) {
|
|
|
4758
4522
|
const adapter = adapters[invocation.dbName];
|
|
4759
4523
|
if (!adapter) return void 0;
|
|
4760
4524
|
try {
|
|
4761
|
-
|
|
4762
|
-
formattedSql: adapter.format(invocation.sql)
|
|
4763
|
-
}
|
|
4525
|
+
return {
|
|
4526
|
+
meta: { formattedSql: adapter.format(invocation.sql) }
|
|
4527
|
+
};
|
|
4764
4528
|
} catch {
|
|
4529
|
+
return void 0;
|
|
4765
4530
|
}
|
|
4766
|
-
return void 0;
|
|
4767
4531
|
}
|
|
4768
4532
|
};
|
|
4769
4533
|
}
|
|
@@ -4810,7 +4574,10 @@ import { BashTransformPipeline } from "just-bash";
|
|
|
4810
4574
|
import { parse as parse2, serialize } from "just-bash";
|
|
4811
4575
|
import { createHash as createHash4 } from "node:crypto";
|
|
4812
4576
|
import * as path6 from "node:path";
|
|
4813
|
-
import {
|
|
4577
|
+
import {
|
|
4578
|
+
BashException,
|
|
4579
|
+
asStaticWordText as asStaticWordText2
|
|
4580
|
+
} from "@deepagents/context";
|
|
4814
4581
|
function isScriptNode(value) {
|
|
4815
4582
|
if (typeof value !== "object" || value === null) {
|
|
4816
4583
|
return false;
|
|
@@ -5623,8 +5390,8 @@ import {
|
|
|
5623
5390
|
|
|
5624
5391
|
// packages/text2sql/src/lib/sql.ts
|
|
5625
5392
|
import "@deepagents/agent";
|
|
5626
|
-
var text2SqlValidationMarker = Symbol("Text2SqlValidationError");
|
|
5627
|
-
var text2SqlUnknownAdapterMarker = Symbol("Text2SqlUnknownAdapterError");
|
|
5393
|
+
var text2SqlValidationMarker = /* @__PURE__ */ Symbol("Text2SqlValidationError");
|
|
5394
|
+
var text2SqlUnknownAdapterMarker = /* @__PURE__ */ Symbol("Text2SqlUnknownAdapterError");
|
|
5628
5395
|
var Text2SqlValidationError = class _Text2SqlValidationError extends Error {
|
|
5629
5396
|
[text2SqlValidationMarker];
|
|
5630
5397
|
constructor(message) {
|
|
@@ -5652,7 +5419,7 @@ var Text2SqlUnknownAdapterError = class _Text2SqlUnknownAdapterError extends Err
|
|
|
5652
5419
|
return error instanceof _Text2SqlUnknownAdapterError && error[text2SqlUnknownAdapterMarker] === true;
|
|
5653
5420
|
}
|
|
5654
5421
|
};
|
|
5655
|
-
var text2SqlUnknownDatabaseMarker = Symbol("Text2SqlUnknownDatabaseError");
|
|
5422
|
+
var text2SqlUnknownDatabaseMarker = /* @__PURE__ */ Symbol("Text2SqlUnknownDatabaseError");
|
|
5656
5423
|
var Text2SqlUnknownDatabaseError = class _Text2SqlUnknownDatabaseError extends Error {
|
|
5657
5424
|
[text2SqlUnknownDatabaseMarker];
|
|
5658
5425
|
requested;
|
|
@@ -5925,8 +5692,10 @@ function alreadyValidated(messages, run) {
|
|
|
5925
5692
|
}
|
|
5926
5693
|
function sqlRunMissingValidate() {
|
|
5927
5694
|
return (ctx) => {
|
|
5928
|
-
const call = ctx.
|
|
5929
|
-
if (!call || call.name !== "bash")
|
|
5695
|
+
const call = ctx.toolOutcome;
|
|
5696
|
+
if (!call || call.state !== "output-available" || call.name !== "bash") {
|
|
5697
|
+
return false;
|
|
5698
|
+
}
|
|
5930
5699
|
const run = extractSqlInvocation(inputCommand(call.input));
|
|
5931
5700
|
if (run?.subcommand !== "run") return false;
|
|
5932
5701
|
return !alreadyValidated(ctx.lastAssistantMessages, run);
|