@duckcodeailabs/dql-agent 1.6.5 → 1.6.6

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.
@@ -19,11 +19,12 @@ import type { KGNode, KGSearchHit } from './kg/types.js';
19
19
  import type { AgentProvider } from './providers/types.js';
20
20
  import type { Skill } from './skills/loader.js';
21
21
  import type { AgentMemory } from './memory/sqlite-memory.js';
22
+ import type { LocalContextPack, MetadataAgentIntent } from './metadata/catalog.js';
22
23
  export type AnswerKind = 'certified' | 'uncertified' | 'no_answer';
23
24
  export type AnswerSourceTier = 'certified_artifact' | 'business_context' | 'semantic_layer' | 'dbt_manifest' | 'no_answer';
24
25
  export type AnswerCertification = 'certified' | 'ai_generated' | 'analyst_review_required';
25
26
  export type AnswerReviewStatus = 'none' | 'draft_ready' | 'analyst_review_required' | 'certified';
26
- export type AgentIntent = 'exact_certified_lookup' | 'ad_hoc_analysis' | 'drillthrough' | 'clarify';
27
+ export type AgentIntent = MetadataAgentIntent | 'ad_hoc_analysis' | 'drillthrough';
27
28
  export interface AgentCitation {
28
29
  nodeId: string;
29
30
  kind: KGNode['kind'] | 'memory' | 'runtime_schema';
@@ -170,6 +171,8 @@ export interface AgentAnswer {
170
171
  analysisPlan?: AgentAnalysisPlan;
171
172
  /** Provider name used (for telemetry / UI badge). */
172
173
  providerUsed?: string;
174
+ /** Local SQLite metadata context pack used to ground retrieval, when supplied by the host. */
175
+ contextPack?: LocalContextPack;
173
176
  /** Top KG hits the loop considered, useful for the UI's "we considered" panel. */
174
177
  considered: KGSearchHit[];
175
178
  }
@@ -228,6 +231,8 @@ export interface AnswerLoopInput {
228
231
  executeGeneratedSql?: (sql: string) => Promise<AgentResultPayload>;
229
232
  /** Runtime schema/column context supplied by the host for generated analysis. */
230
233
  schemaContext?: AgentSchemaTable[];
234
+ /** Shared local metadata context pack from `.dql/cache/metadata.sqlite`. */
235
+ contextPack?: LocalContextPack;
231
236
  }
232
237
  export declare function answer(input: AnswerLoopInput): Promise<AgentAnswer>;
233
238
  interface ParsedProposal {
@@ -1 +1 @@
1
- {"version":3,"file":"answer-loop.d.ts","sourceRoot":"","sources":["../src/answer-loop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"answer-loop.d.ts","sourceRoot":"","sources":["../src/answer-loop.ts"],"names":[],"mappings":"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"}
@@ -40,7 +40,8 @@ export async function answer(input) {
40
40
  const semanticHits = kg.search({ query: question, domain, kinds: SEMANTIC_KINDS, limit: 12 });
41
41
  const manifestHits = kg.search({ query: question, domain, kinds: MANIFEST_KINDS, limit: 12 });
42
42
  const considered = mergeHits(artifactHits, semanticHits, manifestHits, kg.search({ query: question, domain, limit: 10 })).slice(0, 30);
43
- const intent = classifyAgentIntent({
43
+ const catalogRoute = input.contextPack?.routeDecision;
44
+ const fallbackIntent = classifyAgentIntent({
44
45
  question,
45
46
  followUp: input.followUp,
46
47
  artifactHits,
@@ -48,18 +49,20 @@ export async function answer(input) {
48
49
  manifestHits,
49
50
  schemaContext: input.schemaContext ?? [],
50
51
  });
52
+ const intent = catalogRoute ? agentIntentFromCatalogRoute(catalogRoute) : fallbackIntent;
51
53
  // Stage 1: certified artifact match. Blocks can be executed; dashboards,
52
54
  // Apps, and notebooks are returned as governed citations/navigation targets.
53
- const artifactHit = intent === 'exact_certified_lookup'
54
- ? pickCertifiedArtifact({
55
- artifactHits,
56
- executableArtifactHits,
57
- businessHits,
58
- question,
59
- blockHints: input.followUp?.kind === 'drilldown' ? [] : effectiveBlockHints,
60
- excludedArtifactIds,
61
- kg,
62
- })
55
+ const artifactHit = shouldUseCertifiedRoute(catalogRoute, intent)
56
+ ? certifiedHitFromContextPack(input.contextPack, kg)
57
+ ?? pickCertifiedArtifact({
58
+ artifactHits,
59
+ executableArtifactHits,
60
+ businessHits,
61
+ question,
62
+ blockHints: input.followUp?.kind === 'drilldown' ? [] : effectiveBlockHints,
63
+ excludedArtifactIds,
64
+ kg,
65
+ })
63
66
  : null;
64
67
  if (artifactHit) {
65
68
  let result;
@@ -89,7 +92,7 @@ export async function answer(input) {
89
92
  const analysisPlan = buildAnalysisPlan({
90
93
  question,
91
94
  intent,
92
- routeReason: 'The question matched a certified DQL artifact closely enough to answer without generating new SQL.',
95
+ routeReason: catalogRoute?.reason ?? 'The question matched a certified DQL artifact closely enough to answer without generating new SQL.',
93
96
  selectedNodes: [artifactHit.node],
94
97
  schemaContext: input.schemaContext ?? [],
95
98
  sql: result?.sql,
@@ -124,19 +127,22 @@ export async function answer(input) {
124
127
  memoryContext: input.memoryContext ?? [],
125
128
  analysisPlan,
126
129
  }),
130
+ contextPack: input.contextPack,
127
131
  considered,
128
132
  providerUsed: provider.name,
129
133
  };
130
134
  }
131
- if (intent === 'clarify') {
132
- const text = composeClarificationText(question, considered, input.schemaContext ?? []);
135
+ if (intent === 'clarify' || catalogRoute?.route === 'clarify') {
136
+ const text = composeCatalogClarificationText(question, catalogRoute) ?? composeClarificationText(question, considered, input.schemaContext ?? []);
133
137
  const analysisPlan = buildAnalysisPlan({
134
138
  question,
135
139
  intent,
136
- routeReason: 'No certified artifact, semantic object, dbt/source table, or runtime schema match was strong enough to safely generate SQL.',
140
+ routeReason: catalogRoute?.reason ?? 'No certified artifact, semantic object, dbt/source table, or runtime schema match was strong enough to safely generate SQL.',
137
141
  selectedNodes: considered.slice(0, 4).map((hit) => hit.node),
138
142
  schemaContext: input.schemaContext ?? [],
139
- assumptions: ['Need a clearer business object, measure, or grain before querying.'],
143
+ assumptions: catalogRoute?.missingContext.length
144
+ ? catalogRoute.missingContext.map((item) => item.message)
145
+ : ['Need a clearer business object, measure, or grain before querying.'],
140
146
  });
141
147
  return {
142
148
  kind: 'no_answer',
@@ -160,17 +166,18 @@ export async function answer(input) {
160
166
  memoryContext: input.memoryContext ?? [],
161
167
  analysisPlan,
162
168
  }),
169
+ contextPack: input.contextPack,
163
170
  considered,
164
171
  providerUsed: provider.name,
165
172
  };
166
173
  }
167
174
  // Stage 2/3: generate only after certified artifacts miss. Semantic context
168
175
  // wins over raw dbt manifest context; memory is appended last as advisory.
169
- const activeTier = semanticHits.length > 0
176
+ const activeTier = sourceTierFromContextPack(input.contextPack) ?? (semanticHits.length > 0
170
177
  ? 'semantic_layer'
171
178
  : manifestHits.length > 0
172
179
  ? 'dbt_manifest'
173
- : 'dbt_manifest';
180
+ : 'dbt_manifest');
174
181
  const reviewRequiredArtifactHits = artifactHits
175
182
  .filter((hit) => hit.score >= CERTIFIED_HIT_THRESHOLD && !isCertifiedHit(hit, kg))
176
183
  .slice(0, 4);
@@ -192,13 +199,18 @@ export async function answer(input) {
192
199
  messages.push({ role: 'system', content: skillsPrompt });
193
200
  messages.push({
194
201
  role: 'system',
195
- content: renderContextPrompt(contextBlocks, contextBusiness, contextOther, activeTier, input.memoryContext ?? [], input.extraContext, input.followUp, input.schemaContext ?? [], intent),
202
+ content: renderContextPrompt(contextBlocks, contextBusiness, contextOther, activeTier, input.memoryContext ?? [], input.extraContext, input.followUp, input.schemaContext ?? [], intent, input.contextPack),
196
203
  });
197
204
  messages.push({ role: 'user', content: question });
198
205
  const localProposal = buildSchemaAwareProposal({
199
206
  question,
200
207
  intent,
201
208
  schemaContext: input.schemaContext ?? [],
209
+ }) ?? buildContextPackAwareProposal({
210
+ question,
211
+ intent,
212
+ contextPack: input.contextPack,
213
+ followUp: input.followUp,
202
214
  });
203
215
  let proposed = '';
204
216
  let parsed;
@@ -231,6 +243,7 @@ export async function answer(input) {
231
243
  considered,
232
244
  memoryContext: input.memoryContext ?? [],
233
245
  }),
246
+ contextPack: input.contextPack,
234
247
  considered,
235
248
  providerUsed: provider.name,
236
249
  };
@@ -259,11 +272,13 @@ export async function answer(input) {
259
272
  considered,
260
273
  memoryContext: input.memoryContext ?? [],
261
274
  }),
275
+ contextPack: input.contextPack,
262
276
  considered,
263
277
  providerUsed: provider.name,
264
278
  };
265
279
  }
266
280
  const generatedCitations = [
281
+ ...contextPackCitations(input.contextPack, 4),
267
282
  ...contextNodes.slice(0, 4).map((n) => ({
268
283
  nodeId: n.nodeId,
269
284
  kind: n.kind,
@@ -303,7 +318,7 @@ export async function answer(input) {
303
318
  executionError = retryErr instanceof Error ? retryErr.message : String(retryErr);
304
319
  }
305
320
  }
306
- if (executionError) {
321
+ if (executionError && hasUsableRepairSchema(input.schemaContext ?? [])) {
307
322
  const repairedRaw = await requestSqlRepair({
308
323
  provider,
309
324
  baseMessages: messages,
@@ -316,7 +331,6 @@ export async function answer(input) {
316
331
  const repaired = parseProposal(repairedRaw);
317
332
  if (repaired.sql) {
318
333
  repairAttempts += 1;
319
- parsed.text = repaired.text || parsed.text;
320
334
  parsed.sql = repaired.sql;
321
335
  parsed.viz = repaired.viz ?? parsed.viz;
322
336
  try {
@@ -331,19 +345,22 @@ export async function answer(input) {
331
345
  }
332
346
  }
333
347
  }
348
+ if (executionError) {
349
+ parsed.text = composeGeneratedExecutionFailureText(question, executionError);
350
+ }
334
351
  const analysisPlan = buildAnalysisPlan({
335
352
  question,
336
353
  intent,
337
- routeReason: intent === 'drillthrough'
354
+ routeReason: catalogRoute?.reason ?? (intent === 'drillthrough'
338
355
  ? 'The user asked for a drill-through or follow-up, so DQL generated review-required SQL from the prior context and current metadata.'
339
- : 'The question asks for a custom analysis, ranking, breakdown, comparison, or grain that should not be answered by a loose certified block match.',
356
+ : 'The question asks for a custom analysis, ranking, breakdown, comparison, or grain that should not be answered by a loose certified block match.'),
340
357
  selectedNodes: contextNodes,
341
358
  schemaContext: input.schemaContext ?? [],
342
359
  sql: parsed.sql,
343
360
  suggestedViz: parsed.viz ?? 'table',
344
361
  assumptions: [
345
362
  'Generated SQL is an uncertified preview until an analyst reviews and promotes it.',
346
- ...(localProposal ? ['A schema-aware local planner selected the customer ranking grain before provider generation.'] : []),
363
+ ...(localProposal ? ['A local metadata planner selected a review-required SQL grain before provider generation.'] : []),
347
364
  ...(executionError ? ['The preview execution error must be reviewed before reuse.'] : []),
348
365
  ],
349
366
  repairAttempts,
@@ -382,6 +399,7 @@ export async function answer(input) {
382
399
  executorWasAvailable: Boolean(input.executeGeneratedSql),
383
400
  analysisPlan,
384
401
  }),
402
+ contextPack: input.contextPack,
385
403
  considered,
386
404
  providerUsed: localProposal ? 'schema_planner' : provider.name,
387
405
  };
@@ -410,7 +428,7 @@ Rules:
410
428
  PRAGMA, SET, or multiple statements.
411
429
  8. If the schema is insufficient to answer, say so explicitly and ask a
412
430
  clarifying question instead of guessing.`;
413
- function renderContextPrompt(blocks, businessContext, others, activeTier, memoryContext, extraContext, followUp, schemaContext = [], intent = 'ad_hoc_analysis') {
431
+ function renderContextPrompt(blocks, businessContext, others, activeTier, memoryContext, extraContext, followUp, schemaContext = [], intent = 'ad_hoc_analysis', contextPack) {
414
432
  const intentSection = `## Routing intent\n\nintent: ${intent}\n${intent === 'exact_certified_lookup'
415
433
  ? 'Use a certified artifact only if it exactly answers the question.'
416
434
  : 'Generate review-required SQL for this question. Certified blocks are trusted context, not a reason to answer the wrong grain.'}`;
@@ -458,7 +476,154 @@ function renderContextPrompt(blocks, businessContext, others, activeTier, memory
458
476
  const followUpSection = followUp
459
477
  ? `\n\n## Follow-up routing context\n\n${renderFollowUpContext(followUp)}`
460
478
  : '';
461
- return `${intentSection}\n\n${blockSection}${businessSection}${otherSection}${schemaSection}${memorySection}${extraSection}${followUpSection}`;
479
+ const contextPackSection = contextPack
480
+ ? `\n\n## Local metadata context pack\n\n${renderContextPackForPrompt(contextPack)}`
481
+ : '';
482
+ return `${intentSection}\n\n${blockSection}${businessSection}${otherSection}${schemaSection}${contextPackSection}${memorySection}${extraSection}${followUpSection}`;
483
+ }
484
+ function renderContextPackForPrompt(contextPack) {
485
+ const warnings = contextPack.warnings.length
486
+ ? `Warnings:\n${contextPack.warnings.slice(0, 8).map((warning) => `- ${warning}`).join('\n')}\n`
487
+ : '';
488
+ const objects = contextPack.objects.slice(0, 18).map((object) => {
489
+ const detail = [
490
+ object.objectType,
491
+ object.domain ? `domain: ${object.domain}` : '',
492
+ object.status ? `status: ${object.status}` : '',
493
+ object.description ? `description: ${object.description}` : '',
494
+ ].filter(Boolean).join('; ');
495
+ return `- ${object.objectKey} (${detail})`;
496
+ }).join('\n');
497
+ const conflicts = contextPack.retrievalDiagnostics.candidateConflicts.length
498
+ ? `\nCandidate conflicts:\n${contextPack.retrievalDiagnostics.candidateConflicts.slice(0, 4).map((conflict) => `- ${conflict.reason} ${conflict.prompt}`).join('\n')}`
499
+ : '';
500
+ const route = contextPack.routeDecision
501
+ ? `\nRoute decision: ${contextPack.routeDecision.route} / ${contextPack.routeDecision.intent}\nReason: ${contextPack.routeDecision.reason}\nMissing context: ${contextPack.routeDecision.missingContext.map((item) => item.message).join(' ') || 'none'}`
502
+ : '';
503
+ const allowed = contextPack.allowedSqlContext?.relations.length
504
+ ? `\nAllowed SQL relations:\n${contextPack.allowedSqlContext.relations.slice(0, 12).map((relation) => `- ${relation.relation}: ${relation.columns.slice(0, 24).map((column) => column.name).join(', ') || '(columns unavailable)'}`).join('\n')}`
505
+ : '';
506
+ const sourceSql = contextPack.allowedSqlContext?.sourceBlockSql.length
507
+ ? `\nSource block SQL for review-required drafts:\n${contextPack.allowedSqlContext.sourceBlockSql.slice(0, 4).map((source) => `- ${source.name}${source.status ? ` (${source.status})` : ''}:\n${indentSqlForPrompt(source.sql)}`).join('\n')}`
508
+ : '';
509
+ return [
510
+ `context_pack_id: ${contextPack.id}`,
511
+ `trust_label: ${contextPack.trustLabel}`,
512
+ route.trim(),
513
+ warnings.trim(),
514
+ `Selected evidence:\n${objects || '- none'}`,
515
+ allowed.trim(),
516
+ sourceSql.trim(),
517
+ conflicts.trim(),
518
+ ].filter(Boolean).join('\n');
519
+ }
520
+ function indentSqlForPrompt(sql) {
521
+ const normalized = sql.trim().slice(0, 1600);
522
+ return normalized.split(/\r?\n/).map((line) => ` ${line}`).join('\n');
523
+ }
524
+ function contextPackCitations(contextPack, limit) {
525
+ if (!contextPack)
526
+ return [];
527
+ return contextPack.objects.slice(0, limit).map((object) => ({
528
+ nodeId: object.objectKey,
529
+ kind: metadataObjectKindForCitation(object.objectType),
530
+ name: object.name,
531
+ sourceTier: metadataObjectSourceTier(object.objectType),
532
+ provenance: object.sourceSystem,
533
+ }));
534
+ }
535
+ function agentIntentFromCatalogRoute(route) {
536
+ if (route.route === 'clarify')
537
+ return 'clarify';
538
+ if (route.route === 'certified')
539
+ return route.intent === 'definition_lookup' ? 'definition_lookup' : 'exact_certified_lookup';
540
+ return route.intent;
541
+ }
542
+ function shouldUseCertifiedRoute(route, intent) {
543
+ if (route)
544
+ return route.route === 'certified';
545
+ return intent === 'exact_certified_lookup' || intent === 'definition_lookup';
546
+ }
547
+ function certifiedHitFromContextPack(contextPack, kg) {
548
+ const key = contextPack?.routeDecision.exactObjectKey;
549
+ if (!key)
550
+ return null;
551
+ const object = contextPack.objects.find((item) => item.objectKey === key);
552
+ if (!object)
553
+ return null;
554
+ const nodeId = object.objectType === 'dql_block'
555
+ ? `block:${object.name}`
556
+ : object.objectType === 'dql_term'
557
+ ? `term:${object.name}`
558
+ : object.objectType === 'business_view'
559
+ ? `business_view:${object.name}`
560
+ : undefined;
561
+ const node = nodeId ? kg.getNode(nodeId) : null;
562
+ return node ? { node, score: 1, snippet: object.snippet } : null;
563
+ }
564
+ function composeCatalogClarificationText(question, route) {
565
+ if (!route?.missingContext.length)
566
+ return undefined;
567
+ const missing = route.missingContext.map((item) => item.message).join(' ');
568
+ const followUp = route.followUps[0] ? ` ${route.followUps[0]}?` : '';
569
+ return `I need one more detail before querying "${question}". ${missing}${followUp}`;
570
+ }
571
+ function sourceTierFromContextPack(contextPack) {
572
+ if (!contextPack)
573
+ return undefined;
574
+ if (contextPack.objects.some((object) => object.objectType === 'semantic_metric'))
575
+ return 'semantic_layer';
576
+ if (contextPack.objects.some((object) => object.objectType.startsWith('dbt_') || object.objectType === 'warehouse_table' || object.objectType === 'runtime_table'))
577
+ return 'dbt_manifest';
578
+ if (contextPack.objects.some((object) => object.objectType === 'dql_term' || object.objectType === 'business_view'))
579
+ return 'business_context';
580
+ if (contextPack.objects.some((object) => object.objectType === 'dql_block'))
581
+ return 'certified_artifact';
582
+ return undefined;
583
+ }
584
+ function isGeneratedAgentIntent(intent) {
585
+ return intent === 'ad_hoc_analysis'
586
+ || intent === 'drillthrough'
587
+ || intent === 'ad_hoc_ranking'
588
+ || intent === 'driver_breakdown'
589
+ || intent === 'diagnose_change'
590
+ || intent === 'segment_compare'
591
+ || intent === 'entity_drilldown'
592
+ || intent === 'anomaly_investigation';
593
+ }
594
+ function metadataObjectKindForCitation(objectType) {
595
+ if (objectType === 'dql_block')
596
+ return 'block';
597
+ if (objectType === 'dql_term')
598
+ return 'term';
599
+ if (objectType === 'business_view')
600
+ return 'business_view';
601
+ if (objectType === 'semantic_metric')
602
+ return 'metric';
603
+ if (objectType === 'semantic_dimension')
604
+ return 'dimension';
605
+ if (objectType === 'dbt_model')
606
+ return 'dbt_model';
607
+ if (objectType === 'dbt_source' || objectType === 'warehouse_table')
608
+ return 'dbt_source';
609
+ if (objectType === 'notebook')
610
+ return 'notebook';
611
+ if (objectType === 'dashboard')
612
+ return 'dashboard';
613
+ if (objectType === 'app')
614
+ return 'app';
615
+ return 'runtime_schema';
616
+ }
617
+ function metadataObjectSourceTier(objectType) {
618
+ if (objectType === 'dql_block')
619
+ return 'certified_artifact';
620
+ if (objectType === 'dql_term' || objectType === 'business_view')
621
+ return 'business_context';
622
+ if (objectType.startsWith('semantic_'))
623
+ return 'semantic_layer';
624
+ if (objectType.startsWith('dbt_') || objectType === 'warehouse_table')
625
+ return 'dbt_manifest';
626
+ return 'business_context';
462
627
  }
463
628
  function renderFollowUpContext(followUp) {
464
629
  const parts = [
@@ -491,7 +656,7 @@ export function parseProposal(raw) {
491
656
  return { text, sql, viz };
492
657
  }
493
658
  function buildSchemaAwareProposal(input) {
494
- if (input.intent !== 'ad_hoc_analysis' && input.intent !== 'drillthrough')
659
+ if (!isGeneratedAgentIntent(input.intent))
495
660
  return undefined;
496
661
  if (isFilteredEntityQuestion(input.question))
497
662
  return undefined;
@@ -548,6 +713,115 @@ function buildSchemaAwareProposal(input) {
548
713
  viz: 'table',
549
714
  };
550
715
  }
716
+ function buildContextPackAwareProposal(input) {
717
+ if (!isGeneratedAgentIntent(input.intent))
718
+ return undefined;
719
+ if (!input.contextPack)
720
+ return undefined;
721
+ const contextPack = input.contextPack;
722
+ const lower = input.question.toLowerCase();
723
+ const filteredSourceProposal = buildSourceBlockFilterProposal({
724
+ question: input.question,
725
+ contextPack,
726
+ followUp: input.followUp,
727
+ });
728
+ if (filteredSourceProposal)
729
+ return filteredSourceProposal;
730
+ if (!/\b(least|lowest|fewest|bottom|min(?:imum)?)\b/.test(lower))
731
+ return undefined;
732
+ for (const object of input.contextPack.objects) {
733
+ if (object.objectType !== 'dql_block' || object.status !== 'certified')
734
+ continue;
735
+ const sql = typeof object.payload?.sql === 'string' ? object.payload.sql.trim() : '';
736
+ if (!sql || !/\border\s+by\b/i.test(sql) || !/\bdesc\b/i.test(sql))
737
+ continue;
738
+ const inverted = invertRankingSql(sql);
739
+ if (!inverted || inverted === sql)
740
+ continue;
741
+ return {
742
+ text: `Generated a review-required least-ranking query by using certified block "${object.name}" as context and reversing its ranking direction. This result is uncertified until reviewed and promoted.`,
743
+ sql: ensurePreviewLimit(inverted, 10),
744
+ viz: 'table',
745
+ };
746
+ }
747
+ return undefined;
748
+ }
749
+ function buildSourceBlockFilterProposal(input) {
750
+ const year = extractYearFilterValue(input.question, input.followUp);
751
+ if (!year)
752
+ return undefined;
753
+ const source = preferredSourceBlockSql(input.contextPack, input.followUp?.sourceBlockName);
754
+ if (!source?.sql.trim())
755
+ return undefined;
756
+ const sql = source.sql.trim();
757
+ const filterColumn = pickTimeFilterColumn(sql);
758
+ if (!filterColumn)
759
+ return undefined;
760
+ return {
761
+ text: `Generated a review-required ${year} view from certified block "${source.name}" as context. This filters the selected block grain instead of reusing the certified answer directly, so it remains uncertified until reviewed.`,
762
+ sql: [
763
+ 'SELECT *',
764
+ 'FROM (',
765
+ indentSubquerySql(stripFinalLimit(sql)),
766
+ ') AS dql_source',
767
+ `WHERE ${sqlIdentifier(filterColumn)} = ${year}`,
768
+ 'LIMIT 200',
769
+ ].join('\n'),
770
+ viz: 'table',
771
+ };
772
+ }
773
+ function preferredSourceBlockSql(contextPack, sourceBlockName) {
774
+ const sources = contextPack.allowedSqlContext?.sourceBlockSql ?? [];
775
+ const normalizedSource = sourceBlockName ? normalizeSourceName(sourceBlockName) : undefined;
776
+ if (normalizedSource) {
777
+ const exact = sources.find((source) => normalizeSourceName(source.name) === normalizedSource);
778
+ if (exact)
779
+ return exact;
780
+ }
781
+ return sources.find((source) => source.status === 'certified') ?? sources[0];
782
+ }
783
+ function extractYearFilterValue(question, followUp) {
784
+ const direct = question.match(/\b(19|20)\d{2}\b/)?.[0];
785
+ if (direct)
786
+ return direct;
787
+ const fromFollowUp = followUp?.filters
788
+ ?.map((filter) => filter.match(/\b(19|20)\d{2}\b/)?.[0])
789
+ .find(Boolean);
790
+ return fromFollowUp;
791
+ }
792
+ function pickTimeFilterColumn(sql) {
793
+ if (/\bseason\b/i.test(sql))
794
+ return 'season';
795
+ if (/\byear\b/i.test(sql))
796
+ return 'year';
797
+ return undefined;
798
+ }
799
+ function stripFinalLimit(sql) {
800
+ return sql.replace(/;\s*$/, '').replace(/\s+limit\s+\d+\s*$/i, '').trim();
801
+ }
802
+ function indentSubquerySql(sql) {
803
+ return sql.split(/\r?\n/).map((line) => ` ${line}`).join('\n');
804
+ }
805
+ function normalizeSourceName(value) {
806
+ return value.replace(/^block:/i, '').trim().toLowerCase();
807
+ }
808
+ function invertRankingSql(sql) {
809
+ const withoutTrailingSemicolon = sql.replace(/;\s*$/, '').trim();
810
+ const inverted = withoutTrailingSemicolon.replace(/\border\s+by\s+([\s\S]*?)(\blimit\b|$)/i, (match, orderExpr, limitKeyword) => {
811
+ if (!/\bdesc\b/i.test(orderExpr))
812
+ return match;
813
+ const nextExpr = orderExpr
814
+ .replace(/\bDESC\b/gi, 'ASC')
815
+ .replace(/\bNULLS\s+FIRST\b/gi, 'NULLS LAST');
816
+ return `ORDER BY ${nextExpr}${limitKeyword}`;
817
+ });
818
+ return inverted !== withoutTrailingSemicolon ? inverted : undefined;
819
+ }
820
+ function ensurePreviewLimit(sql, limit) {
821
+ if (/\blimit\s+\d+\b/i.test(sql))
822
+ return sql;
823
+ return `${sql.replace(/;\s*$/, '').trim()}\nLIMIT ${limit}`;
824
+ }
551
825
  function findSchemaTable(schemaContext, names) {
552
826
  return schemaContext.find((table) => {
553
827
  const tableNames = new Set([table.name, table.relation.split('.').at(-1) ?? table.relation].map((name) => name.toLowerCase()));
@@ -588,7 +862,7 @@ function pickCertifiedArtifact(input) {
588
862
  // user at a specific block. We still validate it's certified.
589
863
  for (const hint of input.blockHints) {
590
864
  const node = input.kg.getNode(`block:${hint}`);
591
- if (node && node.status === 'certified') {
865
+ if (node && node.status === 'certified' && hasCompatibleCertifiedBlockMatch(input.question, node)) {
592
866
  return { node, score: 1, snippet: undefined };
593
867
  }
594
868
  }
@@ -628,7 +902,7 @@ function pickFirstCertifiedHit(hits, kg, excludedNodeIds, question) {
628
902
  continue;
629
903
  if (!isCertifiedHit(hit, kg))
630
904
  continue;
631
- if (question && hit.node.kind === 'block' && !hasMeaningfulCertifiedBlockSignal(question, hit.node))
905
+ if (question && hit.node.kind === 'block' && !hasCompatibleCertifiedBlockMatch(question, hit.node))
632
906
  continue;
633
907
  return hit;
634
908
  }
@@ -711,6 +985,44 @@ function hasMeaningfulCertifiedBlockSignal(question, node) {
711
985
  }
712
986
  return false;
713
987
  }
988
+ function hasCompatibleCertifiedBlockMatch(question, node) {
989
+ return hasMeaningfulCertifiedBlockSignal(question, node)
990
+ && hasCompatibleRankingDirection(question, node);
991
+ }
992
+ function hasCompatibleRankingDirection(question, node) {
993
+ const questionDirection = rankingDirectionFromText(question);
994
+ if (!questionDirection)
995
+ return true;
996
+ const blockDirection = rankingDirectionFromText(certifiedBlockSignalText(node));
997
+ if (!blockDirection)
998
+ return true;
999
+ return questionDirection === blockDirection;
1000
+ }
1001
+ function rankingDirectionFromText(text) {
1002
+ const lower = text.toLowerCase();
1003
+ const hasBottomSignal = /\b(bottom|least|fewest|lowest|minimum|min|smallest|worst|underperform(?:ing|ed|er|ers)?)\b/.test(lower);
1004
+ const hasTopSignal = /\b(top|most|highest|maximum|max|greatest|best|leader|leaders|leading)\b/.test(lower);
1005
+ if (hasBottomSignal && !hasTopSignal)
1006
+ return 'bottom';
1007
+ if (hasTopSignal && !hasBottomSignal)
1008
+ return 'top';
1009
+ return undefined;
1010
+ }
1011
+ function certifiedBlockSignalText(node) {
1012
+ const examples = (node.examples ?? [])
1013
+ .flatMap((example) => [example.question, example.sql ?? '']);
1014
+ return [
1015
+ node.name,
1016
+ node.domain ?? '',
1017
+ node.description ?? '',
1018
+ node.llmContext ?? '',
1019
+ node.provenance ?? '',
1020
+ ...(node.tags ?? []),
1021
+ ...(node.businessRules ?? []),
1022
+ ...(node.caveats ?? []),
1023
+ ...examples,
1024
+ ].join(' ');
1025
+ }
714
1026
  function hasExactExecutableArtifactSignal(question, node) {
715
1027
  if (!EXECUTABLE_ARTIFACT_KINDS.includes(node.kind))
716
1028
  return false;
@@ -876,7 +1188,7 @@ function isAdHocAnalysisQuestion(question) {
876
1188
  const lower = question.toLowerCase();
877
1189
  if (isBusinessDefinitionQuestion(question))
878
1190
  return false;
879
- return /\b(break\s*down|breakdown|drill\s*(?:down|into)|slice|segment|split|compare|versus|vs\.?|trend|over time|top|bottom|best|worst|highest|lowest|rank|ranking|performed better|better performing|why|what drove|driver|drivers|top movers?|changed?|change|dropped?|drop|decreased?|decrease|declined?|decline|increased?|increase|anomal(?:y|ies)|exceptions?|root cause|contribut(?:e|ed|ion)|variance|delta|by\s+[a-z][\w\s-]{1,40})\b/i.test(lower)
1191
+ return /\b(break\s*down|breakdown|drill\s*(?:down|into)|slice|segment|split|compare|versus|vs\.?|trend|over time|top|bottom|best|worst|highest|lowest|least|fewest|minimum|min|smallest|rank|ranking|performed better|better performing|why|what drove|driver|drivers|top movers?|changed?|change|dropped?|drop|decreased?|decrease|declined?|decline|increased?|increase|anomal(?:y|ies)|exceptions?|root cause|contribut(?:e|ed|ion)|variance|delta|by\s+[a-z][\w\s-]{1,40})\b/i.test(lower)
880
1192
  || /\b(show|list|find|give)\b.+\b(account|accounts|customer|customers|product|products|order|orders|region|location|month|week|day|user|users)\b/i.test(lower);
881
1193
  }
882
1194
  function isFilteredEntityQuestion(question) {
@@ -1047,12 +1359,26 @@ async function requestSqlRepair(input) {
1047
1359
  'The generated SQL failed during bounded preview execution.',
1048
1360
  `Question: ${input.question}`,
1049
1361
  `Execution error: ${input.executionError}`,
1050
- 'Return one corrected read-only SQL query using only the runtime schema below.',
1362
+ 'Return only one corrected read-only SQL query in a fenced sql block using only the runtime schema below.',
1363
+ 'If the runtime schema is not enough to repair the SQL, return "NEEDS_CONTEXT:" followed by one short missing-context question. Do not propose proxy tables.',
1051
1364
  schema,
1052
1365
  ].join('\n\n'),
1053
1366
  },
1054
1367
  ], { signal: input.signal });
1055
1368
  }
1369
+ function hasUsableRepairSchema(schemaContext) {
1370
+ return schemaContext.some((table) => table.columns.length > 0);
1371
+ }
1372
+ function composeGeneratedExecutionFailureText(question, executionError) {
1373
+ const compactError = executionError.replace(/\s+/g, ' ').trim();
1374
+ const shownError = compactError.length > 220 ? `${compactError.slice(0, 217)}...` : compactError;
1375
+ return [
1376
+ `I generated a review-required SQL draft for "${question}", but the bounded preview did not run successfully.`,
1377
+ 'I did not switch to a proxy table because that could answer a different question.',
1378
+ `Execution issue: ${shownError}`,
1379
+ 'Refresh the runtime schema or fix the source relation/columns, then rerun the draft.',
1380
+ ].join(' ');
1381
+ }
1056
1382
  function isRetryableGeneratedSqlError(error) {
1057
1383
  return !/\b(read-only|readonly|select or with|unsafe|delete|insert|update|drop|alter|create|attach|copy|pragma)\b/i.test(error);
1058
1384
  }