@duckcodeailabs/dql-agent 1.11.9 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-run-engine.d.ts.map +1 -1
- package/dist/agent-run-engine.js +7 -3
- package/dist/agent-run-engine.js.map +1 -1
- package/dist/answer-loop.d.ts +25 -1
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +139 -16
- package/dist/answer-loop.js.map +1 -1
- package/dist/conversation/rolling-summary.d.ts +23 -0
- package/dist/conversation/rolling-summary.d.ts.map +1 -1
- package/dist/conversation/rolling-summary.js +64 -0
- package/dist/conversation/rolling-summary.js.map +1 -1
- package/dist/conversation/session-store.d.ts +18 -2
- package/dist/conversation/session-store.d.ts.map +1 -1
- package/dist/conversation/session-store.js +65 -8
- package/dist/conversation/session-store.js.map +1 -1
- package/dist/conversation/snapshot.d.ts +37 -4
- package/dist/conversation/snapshot.d.ts.map +1 -1
- package/dist/conversation/snapshot.js +115 -6
- package/dist/conversation/snapshot.js.map +1 -1
- package/dist/hints/correction-eval.d.ts.map +1 -1
- package/dist/hints/correction-eval.js +4 -6
- package/dist/hints/correction-eval.js.map +1 -1
- package/dist/hints/dependencies.d.ts +63 -0
- package/dist/hints/dependencies.d.ts.map +1 -0
- package/dist/hints/dependencies.js +196 -0
- package/dist/hints/dependencies.js.map +1 -0
- package/dist/hints/git-store.d.ts +55 -2
- package/dist/hints/git-store.d.ts.map +1 -1
- package/dist/hints/git-store.js +246 -11
- package/dist/hints/git-store.js.map +1 -1
- package/dist/hints/graph.d.ts +17 -0
- package/dist/hints/graph.d.ts.map +1 -0
- package/dist/hints/graph.js +222 -0
- package/dist/hints/graph.js.map +1 -0
- package/dist/hints/lifecycle.d.ts +101 -0
- package/dist/hints/lifecycle.d.ts.map +1 -0
- package/dist/hints/lifecycle.js +387 -0
- package/dist/hints/lifecycle.js.map +1 -0
- package/dist/hints/retrieval.d.ts +13 -1
- package/dist/hints/retrieval.d.ts.map +1 -1
- package/dist/hints/retrieval.js +74 -11
- package/dist/hints/retrieval.js.map +1 -1
- package/dist/hints/store.d.ts +9 -2
- package/dist/hints/store.d.ts.map +1 -1
- package/dist/hints/store.js +155 -15
- package/dist/hints/store.js.map +1 -1
- package/dist/hints/types.d.ts +63 -3
- package/dist/hints/types.d.ts.map +1 -1
- package/dist/hints/types.js +1 -1
- package/dist/hints/types.js.map +1 -1
- package/dist/index.d.ts +16 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/intent-controller.d.ts.map +1 -1
- package/dist/intent-controller.js +24 -3
- package/dist/intent-controller.js.map +1 -1
- package/dist/metadata/catalog.d.ts +32 -0
- package/dist/metadata/catalog.d.ts.map +1 -1
- package/dist/metadata/catalog.js +86 -4
- package/dist/metadata/catalog.js.map +1 -1
- package/dist/metadata/metric-match.d.ts.map +1 -1
- package/dist/metadata/metric-match.js +28 -2
- package/dist/metadata/metric-match.js.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +32 -9
- package/dist/router.js.map +1 -1
- package/dist/skills/loader.js +1 -1
- package/dist/skills/loader.js.map +1 -1
- package/dist/tools/registry.js +5 -5
- package/dist/tools/registry.js.map +1 -1
- package/package.json +4 -4
package/dist/answer-loop.js
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
import { createHash } from 'node:crypto';
|
|
18
18
|
import { describeDialectForPrompt, normalizeMetricCapabilityContract, } from '@duckcodeailabs/dql-core';
|
|
19
19
|
import { buildSkillBlockHints, buildSkillMetricHints, buildSkillsPrompt, expandQuestionWithSkillVocabulary, selectRelevantSkills } from './skills/loader.js';
|
|
20
|
+
import { conversationTurnContextState, } from './conversation/snapshot.js';
|
|
21
|
+
import { renderStructuredConversationSummary } from './conversation/rolling-summary.js';
|
|
20
22
|
import { detectResultSetOperation, computeResultSetOperation } from './conversation/result-ops.js';
|
|
21
23
|
import { classifyGovernedQueryShape } from './semantic-bridge/query-shape.js';
|
|
22
24
|
import { domainContextSearchDomains } from './domain-context.js';
|
|
@@ -2900,6 +2902,7 @@ async function runAnswerLoop(input) {
|
|
|
2900
2902
|
];
|
|
2901
2903
|
let result;
|
|
2902
2904
|
let executionError;
|
|
2905
|
+
let warehouseFailure;
|
|
2903
2906
|
let repairAttempts = 0;
|
|
2904
2907
|
// The repair turn returns error-recovery prose ("the column X was not recognized…"),
|
|
2905
2908
|
// NOT a user-facing answer. Keep it for the trace only — never as the answer text.
|
|
@@ -2909,6 +2912,7 @@ async function runAnswerLoop(input) {
|
|
|
2909
2912
|
}
|
|
2910
2913
|
if (deepCandidateExecutionError) {
|
|
2911
2914
|
executionError = deepCandidateExecutionError;
|
|
2915
|
+
warehouseFailure = normalizeWarehouseSqlFailure(deepCandidateExecutionError, input.semanticDriver);
|
|
2912
2916
|
}
|
|
2913
2917
|
const executeCurrentSql = () => {
|
|
2914
2918
|
const requestedLimit = questionPlan.requestedShape.topN?.scope === 'per_group'
|
|
@@ -2961,10 +2965,12 @@ async function runAnswerLoop(input) {
|
|
|
2961
2965
|
result = await executeCurrentSql();
|
|
2962
2966
|
}
|
|
2963
2967
|
catch (err) {
|
|
2964
|
-
|
|
2968
|
+
warehouseFailure = normalizeWarehouseSqlFailure(err, input.semanticDriver);
|
|
2969
|
+
executionError = warehouseFailure.redactedMessage;
|
|
2965
2970
|
}
|
|
2966
2971
|
if (executionError && !fanoutContradiction && !authoritativeSemanticBinding) {
|
|
2967
|
-
|
|
2972
|
+
warehouseFailure ??= normalizeWarehouseSqlFailure(executionError, input.semanticDriver);
|
|
2973
|
+
if (isRetryableGeneratedSqlError(warehouseFailure)) {
|
|
2968
2974
|
const localRepairSql = repairGeneratedSqlLocally(parsed.sql, executionError, schemaContext);
|
|
2969
2975
|
if (localRepairSql && canUseLaneRepair(repairBudgetState, 'execution')) {
|
|
2970
2976
|
recordLaneRepair(repairBudgetState, 'execution');
|
|
@@ -2989,23 +2995,28 @@ async function runAnswerLoop(input) {
|
|
|
2989
2995
|
try {
|
|
2990
2996
|
result = await executeCurrentSql();
|
|
2991
2997
|
executionError = undefined;
|
|
2998
|
+
warehouseFailure = undefined;
|
|
2992
2999
|
}
|
|
2993
3000
|
catch (retryErr) {
|
|
2994
|
-
|
|
3001
|
+
warehouseFailure = normalizeWarehouseSqlFailure(retryErr, input.semanticDriver);
|
|
3002
|
+
executionError = warehouseFailure.redactedMessage;
|
|
2995
3003
|
}
|
|
2996
3004
|
}
|
|
2997
3005
|
else {
|
|
2998
3006
|
executionError = localRepairValidation.error;
|
|
3007
|
+
warehouseFailure = normalizeWarehouseSqlFailure(localRepairValidation.error, input.semanticDriver);
|
|
2999
3008
|
}
|
|
3000
3009
|
}
|
|
3001
|
-
if (executionError
|
|
3010
|
+
if (executionError
|
|
3011
|
+
&& warehouseFailure?.retryDisposition === 'model_repair'
|
|
3012
|
+
&& canUseLaneRepair(repairBudgetState, 'execution')) {
|
|
3002
3013
|
recordLaneRepair(repairBudgetState, 'execution');
|
|
3003
3014
|
const repairedRaw = await requestSqlRepair({
|
|
3004
3015
|
provider,
|
|
3005
3016
|
baseMessages: messages,
|
|
3006
3017
|
question,
|
|
3007
3018
|
parsed,
|
|
3008
|
-
|
|
3019
|
+
failure: warehouseFailure,
|
|
3009
3020
|
schemaContext,
|
|
3010
3021
|
signal: input.signal,
|
|
3011
3022
|
reasoningEffort: input.reasoningEffort,
|
|
@@ -3037,13 +3048,16 @@ async function runAnswerLoop(input) {
|
|
|
3037
3048
|
try {
|
|
3038
3049
|
result = await executeCurrentSql();
|
|
3039
3050
|
executionError = undefined;
|
|
3051
|
+
warehouseFailure = undefined;
|
|
3040
3052
|
}
|
|
3041
3053
|
catch (retryErr) {
|
|
3042
|
-
|
|
3054
|
+
warehouseFailure = normalizeWarehouseSqlFailure(retryErr, input.semanticDriver);
|
|
3055
|
+
executionError = warehouseFailure.redactedMessage;
|
|
3043
3056
|
}
|
|
3044
3057
|
}
|
|
3045
3058
|
else {
|
|
3046
3059
|
executionError = repairedValidation.error;
|
|
3060
|
+
warehouseFailure = normalizeWarehouseSqlFailure(repairedValidation.error, input.semanticDriver);
|
|
3047
3061
|
}
|
|
3048
3062
|
}
|
|
3049
3063
|
}
|
|
@@ -3204,6 +3218,7 @@ async function runAnswerLoop(input) {
|
|
|
3204
3218
|
sql: parsed.sql,
|
|
3205
3219
|
result,
|
|
3206
3220
|
executionError,
|
|
3221
|
+
...(warehouseFailure ? { warehouseFailure } : {}),
|
|
3207
3222
|
...(finalSemanticExecutionTrace ? { semanticExecutionTrace: finalSemanticExecutionTrace } : {}),
|
|
3208
3223
|
...(governedMetricExecutionFailure ? { refusalCode: 'grounding_gap' } : {}),
|
|
3209
3224
|
suggestedViz: parsed.viz ?? 'table',
|
|
@@ -4271,7 +4286,7 @@ function renderContextPrompt(blocks, businessContext, others, activeTier, memory
|
|
|
4271
4286
|
const hintsSection = appliedHints.length > 0
|
|
4272
4287
|
? `\n\n## Applied governed hints (human-approved corrections)\n\nReviewed, scope-matched corrections from your team. Apply them when preparing a SQL preview to avoid known mistakes; they refine the DQL artifact's SQL preview but MUST NOT override a certified artifact that already answers the question.\n${appliedHints
|
|
4273
4288
|
.slice(0, 6)
|
|
4274
|
-
.map((h) => `- \`${h.title}\`: ${h.guidance}${h.correctedSql ? `\n corrected SQL pattern: ${h.correctedSql.replace(/\s+/g, ' ').trim().slice(0, 240)}` : ''}`)
|
|
4289
|
+
.map((h) => `- \`${h.title}\`: ${h.guidance}${h.graphReason ? `\n matched graph context: ${h.graphReason}` : ''}${h.correctedSql ? `\n corrected SQL pattern: ${h.correctedSql.replace(/\s+/g, ' ').trim().slice(0, 240)}` : ''}`)
|
|
4275
4290
|
.join('\n')}`
|
|
4276
4291
|
: '';
|
|
4277
4292
|
const extraSection = extraContext?.trim()
|
|
@@ -4347,8 +4362,12 @@ function renderConversationSnapshot(snapshot) {
|
|
|
4347
4362
|
else if (snapshot.topicRelation) {
|
|
4348
4363
|
parts.push(`Working state: topic=${snapshot.topicRelation}`);
|
|
4349
4364
|
}
|
|
4350
|
-
|
|
4351
|
-
|
|
4365
|
+
const structuredSummary = renderStructuredConversationSummary(snapshot.structuredSummary);
|
|
4366
|
+
if (structuredSummary) {
|
|
4367
|
+
parts.push(`Earlier in this conversation (source-attributed):\n${structuredSummary}`);
|
|
4368
|
+
}
|
|
4369
|
+
else if (snapshot.rollingSummary) {
|
|
4370
|
+
parts.push(`Earlier in this conversation (legacy compacted view):\n${snapshot.rollingSummary.slice(0, 600)}`);
|
|
4352
4371
|
}
|
|
4353
4372
|
if (snapshot.recentTurns.length > 0) {
|
|
4354
4373
|
const turns = snapshot.recentTurns.slice(-4).map((turn, index) => {
|
|
@@ -4356,16 +4375,20 @@ function renderConversationSnapshot(snapshot) {
|
|
|
4356
4375
|
});
|
|
4357
4376
|
parts.push(`Recent turns:\n${turns.join('\n')}`);
|
|
4358
4377
|
}
|
|
4359
|
-
|
|
4360
|
-
|
|
4378
|
+
const recalledTurns = snapshot.recalledTurns
|
|
4379
|
+
?.filter((turn) => conversationTurnContextState(turn) !== 'blocked')
|
|
4380
|
+
.slice(0, 3);
|
|
4381
|
+
if (recalledTurns?.length) {
|
|
4382
|
+
parts.push(`Recalled earlier turns (semantic match):\n${recalledTurns
|
|
4361
4383
|
.map((turn) => `- ${renderConversationSnapshotTurnLine(turn, 520)}`)
|
|
4362
4384
|
.join('\n')}`);
|
|
4363
4385
|
}
|
|
4364
|
-
parts.push('Use this only where the question refers to it. On a new topic, answer fresh.');
|
|
4386
|
+
parts.push('Use this only where the question refers to it. Provisional, unresolved, and blocked turns are not verified facts. On a new topic, answer fresh.');
|
|
4365
4387
|
return parts.join('\n\n');
|
|
4366
4388
|
}
|
|
4367
4389
|
function renderConversationSnapshotTurnLine(turn, max) {
|
|
4368
4390
|
const line = [
|
|
4391
|
+
`state: ${conversationTurnContextState(turn)}`,
|
|
4369
4392
|
`Q: ${turn.question}`,
|
|
4370
4393
|
turn.answerSummary ? `A: ${turn.answerSummary}` : '',
|
|
4371
4394
|
turn.resultColumns?.length ? `cols: ${turn.resultColumns.slice(0, 6).join(', ')}` : '',
|
|
@@ -6221,12 +6244,18 @@ async function requestSqlRepair(input) {
|
|
|
6221
6244
|
content: [
|
|
6222
6245
|
'The SQL preview for the review-required DQL artifact failed during bounded preview execution.',
|
|
6223
6246
|
`Question: ${input.question}`,
|
|
6224
|
-
`
|
|
6247
|
+
`Warehouse dialect: ${input.failure.driver ?? 'unknown'}`,
|
|
6248
|
+
`Failure category: ${input.failure.category}`,
|
|
6249
|
+
`Execution error: ${input.failure.redactedMessage}`,
|
|
6250
|
+
input.failure.vendorCode ? `Vendor code: ${input.failure.vendorCode}` : '',
|
|
6251
|
+
input.failure.sqlState ? `SQL state: ${input.failure.sqlState}` : '',
|
|
6252
|
+
input.failure.line !== undefined ? `Failure line: ${input.failure.line}` : '',
|
|
6253
|
+
input.failure.position !== undefined ? `Failure position: ${input.failure.position}` : '',
|
|
6225
6254
|
'Qualify every source column independently inside each SELECT/CTE scope. Do not repair an ambiguous column by choosing the first FROM relation; use the runtime schema to identify its unique owner, or leave the query unresolved when no unique owner is proven.',
|
|
6226
6255
|
'Preserve every requested output measure and dimension while repairing the failing reference.',
|
|
6227
6256
|
'Return one corrected read-only SQL query using only the runtime schema below, as a single ```json fenced object with summary, sql, viz, outputs, and optional dql metadata fields.',
|
|
6228
6257
|
schema,
|
|
6229
|
-
].join('\n\n'),
|
|
6258
|
+
].filter(Boolean).join('\n\n'),
|
|
6230
6259
|
},
|
|
6231
6260
|
], {
|
|
6232
6261
|
signal: input.signal,
|
|
@@ -6499,8 +6528,102 @@ function summarizeEvidencePayload(value, maxLength = 700) {
|
|
|
6499
6528
|
return undefined;
|
|
6500
6529
|
return compact.length <= maxLength ? compact : `${compact.slice(0, maxLength - 3)}...`;
|
|
6501
6530
|
}
|
|
6502
|
-
function
|
|
6503
|
-
|
|
6531
|
+
export function normalizeWarehouseSqlFailure(error, fallbackDriver) {
|
|
6532
|
+
const record = error && typeof error === 'object'
|
|
6533
|
+
? error
|
|
6534
|
+
: undefined;
|
|
6535
|
+
const rawMessage = error instanceof Error
|
|
6536
|
+
? error.message
|
|
6537
|
+
: typeof error === 'string'
|
|
6538
|
+
? error
|
|
6539
|
+
: typeof record?.message === 'string'
|
|
6540
|
+
? record.message
|
|
6541
|
+
: String(error);
|
|
6542
|
+
const message = redactWarehouseSqlFailureMessage(rawMessage);
|
|
6543
|
+
const category = classifyWarehouseSqlFailure(message);
|
|
6544
|
+
return {
|
|
6545
|
+
version: 1,
|
|
6546
|
+
category,
|
|
6547
|
+
retryDisposition: warehouseSqlRetryDisposition(category),
|
|
6548
|
+
redactedMessage: message,
|
|
6549
|
+
...(stringField(record, 'driver') ?? fallbackDriver
|
|
6550
|
+
? { driver: stringField(record, 'driver') ?? fallbackDriver }
|
|
6551
|
+
: {}),
|
|
6552
|
+
...(stringField(record, 'vendorCode') ? { vendorCode: stringField(record, 'vendorCode') } : {}),
|
|
6553
|
+
...(stringField(record, 'sqlState') ? { sqlState: stringField(record, 'sqlState') } : {}),
|
|
6554
|
+
...(stringField(record, 'queryId') ? { queryId: stringField(record, 'queryId') } : {}),
|
|
6555
|
+
...(numberField(record, 'line') !== undefined ? { line: numberField(record, 'line') } : {}),
|
|
6556
|
+
...(numberField(record, 'position') !== undefined ? { position: numberField(record, 'position') } : {}),
|
|
6557
|
+
...(typeof record?.retryable === 'boolean' ? { retryable: record.retryable } : {}),
|
|
6558
|
+
};
|
|
6559
|
+
}
|
|
6560
|
+
function redactWarehouseSqlFailureMessage(message) {
|
|
6561
|
+
const compact = message.replace(/\s+/g, ' ').trim()
|
|
6562
|
+
.replace(/\b(password|token|secret|private[_ -]?key)\s*[=:]\s*([^\s,;]+)/gi, '$1=[redacted]')
|
|
6563
|
+
.replace(/\b(?:https?|snowflake|postgres(?:ql)?):\/\/[^\s]+/gi, '[redacted connection]');
|
|
6564
|
+
return (compact || 'Warehouse query failed.').slice(0, 700);
|
|
6565
|
+
}
|
|
6566
|
+
function classifyWarehouseSqlFailure(message) {
|
|
6567
|
+
if (/\b(read-only|readonly|select or with|unsafe statement|not permitted sql|delete|insert|update|drop|alter|create|attach|copy|pragma)\b/i.test(message)) {
|
|
6568
|
+
return 'unsafe';
|
|
6569
|
+
}
|
|
6570
|
+
if (/\b(authentication|authenticate|invalid credentials?|incorrect username|login failed|oauth|expired token)\b/i.test(message)) {
|
|
6571
|
+
return 'authentication';
|
|
6572
|
+
}
|
|
6573
|
+
if (/\b(permission denied|insufficient privileges?|not authorized|access denied|authorization failed|does not have privilege)\b/i.test(message)) {
|
|
6574
|
+
return 'permission';
|
|
6575
|
+
}
|
|
6576
|
+
if (/\b(cancelled|canceled|aborted by user)\b/i.test(message))
|
|
6577
|
+
return 'cancelled';
|
|
6578
|
+
if (/\b(timeout|timed out|deadline exceeded|statement timeout)\b/i.test(message))
|
|
6579
|
+
return 'timeout';
|
|
6580
|
+
if (/\bambiguous\s+(?:reference|column(?:\s+name)?)\b/i.test(message))
|
|
6581
|
+
return 'ambiguous_column';
|
|
6582
|
+
if (/\b(column|identifier|field)\b.*\b(not found|does not exist|not recognized|invalid identifier|unknown)\b/i.test(message)
|
|
6583
|
+
|| /\bdoes not have a column named\b/i.test(message)
|
|
6584
|
+
|| /\binvalid identifier\b/i.test(message)) {
|
|
6585
|
+
return 'unknown_column';
|
|
6586
|
+
}
|
|
6587
|
+
if (/\b(table|relation|view|object)\b.*\b(not found|does not exist|unknown|not exist|not authorized)\b/i.test(message)) {
|
|
6588
|
+
return 'unknown_relation';
|
|
6589
|
+
}
|
|
6590
|
+
if (/\b(function|routine)\b.*\b(not found|does not exist|unknown|unsupported|no matching signature)\b/i.test(message)) {
|
|
6591
|
+
return 'unsupported_function';
|
|
6592
|
+
}
|
|
6593
|
+
if (/\b(type mismatch|cannot cast|conversion error|invalid argument types?|operator does not exist)\b/i.test(message)) {
|
|
6594
|
+
return 'type_mismatch';
|
|
6595
|
+
}
|
|
6596
|
+
if (/\b(parser error|parse error|syntax error|sql compilation error|unexpected token|unexpected keyword)\b/i.test(message)) {
|
|
6597
|
+
return 'syntax';
|
|
6598
|
+
}
|
|
6599
|
+
return 'unknown';
|
|
6600
|
+
}
|
|
6601
|
+
function warehouseSqlRetryDisposition(category) {
|
|
6602
|
+
if (category === 'syntax'
|
|
6603
|
+
|| category === 'unknown_column'
|
|
6604
|
+
|| category === 'ambiguous_column'
|
|
6605
|
+
|| category === 'unsupported_function'
|
|
6606
|
+
|| category === 'type_mismatch') {
|
|
6607
|
+
return 'model_repair';
|
|
6608
|
+
}
|
|
6609
|
+
if (category === 'unknown_relation')
|
|
6610
|
+
return 'refresh_metadata';
|
|
6611
|
+
if (category === 'timeout')
|
|
6612
|
+
return 'explicit_retry';
|
|
6613
|
+
if (category === 'permission' || category === 'authentication')
|
|
6614
|
+
return 'change_authorized_access';
|
|
6615
|
+
return 'terminal';
|
|
6616
|
+
}
|
|
6617
|
+
function stringField(record, key) {
|
|
6618
|
+
const value = record?.[key];
|
|
6619
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
6620
|
+
}
|
|
6621
|
+
function numberField(record, key) {
|
|
6622
|
+
const value = record?.[key];
|
|
6623
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
6624
|
+
}
|
|
6625
|
+
function isRetryableGeneratedSqlError(failure) {
|
|
6626
|
+
return failure.retryDisposition === 'model_repair';
|
|
6504
6627
|
}
|
|
6505
6628
|
/**
|
|
6506
6629
|
* Only SQL-shape/binding failures may leave the certified lane. Connectivity,
|