@duckcodeailabs/dql-agent 1.6.21 → 1.6.22

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 (160) hide show
  1. package/dist/agent-run-engine.d.ts +287 -0
  2. package/dist/agent-run-engine.d.ts.map +1 -0
  3. package/dist/agent-run-engine.js +916 -0
  4. package/dist/agent-run-engine.js.map +1 -0
  5. package/dist/agent-run-gates.d.ts +26 -0
  6. package/dist/agent-run-gates.d.ts.map +1 -0
  7. package/dist/agent-run-gates.js +170 -0
  8. package/dist/agent-run-gates.js.map +1 -0
  9. package/dist/agent-run-planner.d.ts +32 -0
  10. package/dist/agent-run-planner.d.ts.map +1 -0
  11. package/dist/agent-run-planner.js +280 -0
  12. package/dist/agent-run-planner.js.map +1 -0
  13. package/dist/answer-loop.d.ts +49 -0
  14. package/dist/answer-loop.d.ts.map +1 -1
  15. package/dist/answer-loop.js +292 -17
  16. package/dist/answer-loop.js.map +1 -1
  17. package/dist/app-builder.d.ts +6 -0
  18. package/dist/app-builder.d.ts.map +1 -1
  19. package/dist/app-builder.js +95 -2
  20. package/dist/app-builder.js.map +1 -1
  21. package/dist/app-planner.d.ts +78 -0
  22. package/dist/app-planner.d.ts.map +1 -0
  23. package/dist/app-planner.js +145 -0
  24. package/dist/app-planner.js.map +1 -0
  25. package/dist/embeddings/provider.d.ts +66 -0
  26. package/dist/embeddings/provider.d.ts.map +1 -0
  27. package/dist/embeddings/provider.js +105 -0
  28. package/dist/embeddings/provider.js.map +1 -0
  29. package/dist/hints/git-store.d.ts +69 -0
  30. package/dist/hints/git-store.d.ts.map +1 -0
  31. package/dist/hints/git-store.js +244 -0
  32. package/dist/hints/git-store.js.map +1 -0
  33. package/dist/hints/retrieval.d.ts +49 -0
  34. package/dist/hints/retrieval.d.ts.map +1 -0
  35. package/dist/hints/retrieval.js +60 -0
  36. package/dist/hints/retrieval.js.map +1 -0
  37. package/dist/hints/store.d.ts +39 -0
  38. package/dist/hints/store.d.ts.map +1 -0
  39. package/dist/hints/store.js +279 -0
  40. package/dist/hints/store.js.map +1 -0
  41. package/dist/hints/types.d.ts +147 -0
  42. package/dist/hints/types.d.ts.map +1 -0
  43. package/dist/hints/types.js +92 -0
  44. package/dist/hints/types.js.map +1 -0
  45. package/dist/index.d.ts +43 -6
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +35 -3
  48. package/dist/index.js.map +1 -1
  49. package/dist/intent-controller.d.ts +63 -0
  50. package/dist/intent-controller.d.ts.map +1 -0
  51. package/dist/intent-controller.js +150 -0
  52. package/dist/intent-controller.js.map +1 -0
  53. package/dist/kg/build.d.ts.map +1 -1
  54. package/dist/kg/build.js +2 -0
  55. package/dist/kg/build.js.map +1 -1
  56. package/dist/kg/sqlite-fts.js +4 -0
  57. package/dist/kg/sqlite-fts.js.map +1 -1
  58. package/dist/kg/types.d.ts +10 -0
  59. package/dist/kg/types.d.ts.map +1 -1
  60. package/dist/metadata/catalog.d.ts +81 -4
  61. package/dist/metadata/catalog.d.ts.map +1 -1
  62. package/dist/metadata/catalog.js +208 -1
  63. package/dist/metadata/catalog.js.map +1 -1
  64. package/dist/metadata/drafts.d.ts +22 -0
  65. package/dist/metadata/drafts.d.ts.map +1 -1
  66. package/dist/metadata/drafts.js +126 -2
  67. package/dist/metadata/drafts.js.map +1 -1
  68. package/dist/metadata/grain-gate.d.ts +61 -0
  69. package/dist/metadata/grain-gate.d.ts.map +1 -0
  70. package/dist/metadata/grain-gate.js +265 -0
  71. package/dist/metadata/grain-gate.js.map +1 -0
  72. package/dist/metadata/identity.d.ts +47 -0
  73. package/dist/metadata/identity.d.ts.map +1 -0
  74. package/dist/metadata/identity.js +147 -0
  75. package/dist/metadata/identity.js.map +1 -0
  76. package/dist/metadata/metric-match.d.ts +93 -0
  77. package/dist/metadata/metric-match.d.ts.map +1 -0
  78. package/dist/metadata/metric-match.js +233 -0
  79. package/dist/metadata/metric-match.js.map +1 -0
  80. package/dist/metadata/sql-grounding.d.ts +149 -0
  81. package/dist/metadata/sql-grounding.d.ts.map +1 -0
  82. package/dist/metadata/sql-grounding.js +397 -0
  83. package/dist/metadata/sql-grounding.js.map +1 -0
  84. package/dist/metadata/sql-retrieval.d.ts +29 -0
  85. package/dist/metadata/sql-retrieval.d.ts.map +1 -0
  86. package/dist/metadata/sql-retrieval.js +79 -0
  87. package/dist/metadata/sql-retrieval.js.map +1 -0
  88. package/dist/narrate.d.ts +52 -0
  89. package/dist/narrate.d.ts.map +1 -0
  90. package/dist/narrate.js +263 -0
  91. package/dist/narrate.js.map +1 -0
  92. package/dist/propose/build-from-prompt.d.ts +160 -0
  93. package/dist/propose/build-from-prompt.d.ts.map +1 -0
  94. package/dist/propose/build-from-prompt.js +815 -0
  95. package/dist/propose/build-from-prompt.js.map +1 -0
  96. package/dist/propose/classify.d.ts +55 -0
  97. package/dist/propose/classify.d.ts.map +1 -0
  98. package/dist/propose/classify.js +167 -0
  99. package/dist/propose/classify.js.map +1 -0
  100. package/dist/propose/config.d.ts +39 -0
  101. package/dist/propose/config.d.ts.map +1 -0
  102. package/dist/propose/config.js +52 -0
  103. package/dist/propose/config.js.map +1 -0
  104. package/dist/propose/dbt-artifacts.d.ts +134 -0
  105. package/dist/propose/dbt-artifacts.d.ts.map +1 -0
  106. package/dist/propose/dbt-artifacts.js +320 -0
  107. package/dist/propose/dbt-artifacts.js.map +1 -0
  108. package/dist/propose/edit-block.d.ts +74 -0
  109. package/dist/propose/edit-block.d.ts.map +1 -0
  110. package/dist/propose/edit-block.js +133 -0
  111. package/dist/propose/edit-block.js.map +1 -0
  112. package/dist/propose/enrich.d.ts +48 -0
  113. package/dist/propose/enrich.d.ts.map +1 -0
  114. package/dist/propose/enrich.js +125 -0
  115. package/dist/propose/enrich.js.map +1 -0
  116. package/dist/propose/generate-sql.d.ts +61 -0
  117. package/dist/propose/generate-sql.d.ts.map +1 -0
  118. package/dist/propose/generate-sql.js +180 -0
  119. package/dist/propose/generate-sql.js.map +1 -0
  120. package/dist/propose/index.d.ts +24 -0
  121. package/dist/propose/index.d.ts.map +1 -0
  122. package/dist/propose/index.js +15 -0
  123. package/dist/propose/index.js.map +1 -0
  124. package/dist/propose/propose.d.ts +226 -0
  125. package/dist/propose/propose.d.ts.map +1 -0
  126. package/dist/propose/propose.js +668 -0
  127. package/dist/propose/propose.js.map +1 -0
  128. package/dist/propose/reflect-block.d.ts +99 -0
  129. package/dist/propose/reflect-block.d.ts.map +1 -0
  130. package/dist/propose/reflect-block.js +220 -0
  131. package/dist/propose/reflect-block.js.map +1 -0
  132. package/dist/propose/write-draft.d.ts +90 -0
  133. package/dist/propose/write-draft.d.ts.map +1 -0
  134. package/dist/propose/write-draft.js +185 -0
  135. package/dist/propose/write-draft.js.map +1 -0
  136. package/dist/providers/claude.d.ts +13 -1
  137. package/dist/providers/claude.d.ts.map +1 -1
  138. package/dist/providers/claude.js +20 -2
  139. package/dist/providers/claude.js.map +1 -1
  140. package/dist/providers/gemini.d.ts +13 -1
  141. package/dist/providers/gemini.d.ts.map +1 -1
  142. package/dist/providers/gemini.js +24 -3
  143. package/dist/providers/gemini.js.map +1 -1
  144. package/dist/providers/index.d.ts +3 -3
  145. package/dist/providers/index.d.ts.map +1 -1
  146. package/dist/providers/index.js +3 -3
  147. package/dist/providers/index.js.map +1 -1
  148. package/dist/research-loop.d.ts +62 -0
  149. package/dist/research-loop.d.ts.map +1 -0
  150. package/dist/research-loop.js +210 -0
  151. package/dist/research-loop.js.map +1 -0
  152. package/dist/skills/defaults.d.ts +37 -0
  153. package/dist/skills/defaults.d.ts.map +1 -0
  154. package/dist/skills/defaults.js +152 -0
  155. package/dist/skills/defaults.js.map +1 -0
  156. package/dist/skills/loader.d.ts +60 -0
  157. package/dist/skills/loader.d.ts.map +1 -1
  158. package/dist/skills/loader.js +164 -2
  159. package/dist/skills/loader.js.map +1 -1
  160. package/package.json +7 -5
@@ -14,6 +14,7 @@
14
14
  * step. Tests can mock the provider with a canned response and exercise the
15
15
  * full pipeline.
16
16
  */
17
+ import { type ResolvedTrustLabel } from '@duckcodeailabs/dql-core';
17
18
  import type { KGStore } from './kg/sqlite-fts.js';
18
19
  import type { KGNode, KGSearchHit } from './kg/types.js';
19
20
  import type { AgentProvider } from './providers/types.js';
@@ -21,8 +22,22 @@ import type { Skill } from './skills/loader.js';
21
22
  import type { AgentMemory } from './memory/sqlite-memory.js';
22
23
  import type { LocalContextPack, MetadataAgentIntent } from './metadata/catalog.js';
23
24
  import type { GeneratedDraftBlock } from './metadata/drafts.js';
25
+ import { type MetricMatch } from './metadata/metric-match.js';
26
+ import { type IntentDecision } from './intent-controller.js';
24
27
  export type AnswerKind = 'certified' | 'uncertified' | 'no_answer';
25
28
  export type AnswerSourceTier = 'certified_artifact' | 'business_context' | 'semantic_layer' | 'dbt_manifest' | 'no_answer';
29
+ /**
30
+ * The chosen route, surfaced on EVERY AI result (spec 17, part C) so the UI can
31
+ * show "where the answer came from". `tier` is the coarse route bucket; `ref`
32
+ * names the governed artifact/metric used (e.g. `cumulative_revenue`); `label`
33
+ * is a ready-to-render sentence.
34
+ */
35
+ export type AiRouteTier = 'certified_block' | 'semantic_metric' | 'generated_sql' | 'business_context' | 'no_answer';
36
+ export interface AiRoute {
37
+ tier: AiRouteTier;
38
+ label: string;
39
+ ref?: string;
40
+ }
26
41
  export type AnswerCertification = 'certified' | 'ai_generated' | 'analyst_review_required';
27
42
  export type AnswerReviewStatus = 'none' | 'draft_ready' | 'analyst_review_required' | 'certified';
28
43
  export type AgentIntent = MetadataAgentIntent | 'ad_hoc_analysis' | 'drillthrough';
@@ -157,6 +172,12 @@ export interface AgentAnswer {
157
172
  certification?: AnswerCertification;
158
173
  reviewStatus?: AnswerReviewStatus;
159
174
  confidence?: number;
175
+ /**
176
+ * P0 intent controller — the high-level action the agent decided this turn
177
+ * deserves (answer / clarify / investigate / compose_app) with a rationale.
178
+ * Advisory: callers route on it (compose_app → app build, investigate → research).
179
+ */
180
+ intentDecision?: IntentDecision;
160
181
  /** Final answer text (NL summary). */
161
182
  text: string;
162
183
  /** Alias for UI envelopes. */
@@ -177,7 +198,15 @@ export interface AgentAnswer {
177
198
  draftBlockId?: string;
178
199
  draftBlock?: GeneratedDraftBlock;
179
200
  promoteCommand?: string;
201
+ /** Legacy free-form trust label string, retained for backward compatibility. */
180
202
  trustLabel?: string;
203
+ /**
204
+ * Canonical trust label (base + optional qualifier) drawn from the one shared
205
+ * vocabulary in dql-core, derived from this answer's source tier and review
206
+ * state. Lets every surface render the same label set as the MCP answer
207
+ * contract and the UI badge.
208
+ */
209
+ trustLabelInfo?: ResolvedTrustLabel;
181
210
  sourceCertifiedBlock?: string;
182
211
  contextPackId?: string;
183
212
  validationWarnings?: string[];
@@ -185,6 +214,8 @@ export interface AgentAnswer {
185
214
  citations: AgentCitation[];
186
215
  /** Relevant local memory supplied as advisory context. */
187
216
  memoryContext?: AgentMemory[];
217
+ /** Approved Hint-Graph corrections that were applied to this answer (for transparency). */
218
+ appliedHints?: LocalContextPack['appliedHints'];
188
219
  /** Evidence path connecting the question to metadata, SQL/block execution, and review state. */
189
220
  evidence?: AgentEvidence;
190
221
  /** Business-facing plan the agent used to answer the question. */
@@ -195,6 +226,24 @@ export interface AgentAnswer {
195
226
  contextPack?: LocalContextPack;
196
227
  /** Top KG hits the loop considered, useful for the UI's "we considered" panel. */
197
228
  considered: KGSearchHit[];
229
+ /** The Skills that shaped this answer (selected, not all), for transparency. */
230
+ appliedSkills?: Array<{
231
+ id: string;
232
+ description?: string;
233
+ }>;
234
+ /**
235
+ * The chosen route (spec 17, part C). Surfaced on every result so the UI can
236
+ * show which tier answered (certified block, governed semantic metric,
237
+ * generated SQL, business context, or an honest refusal). Computed once at the
238
+ * single exit point in `answer()`.
239
+ */
240
+ route?: AiRoute;
241
+ /**
242
+ * Internal: the governed metric the semantic tier matched (spec 17, part C).
243
+ * Used only to build `route` at the `answer()` exit point; not part of the
244
+ * stable public payload.
245
+ */
246
+ _semanticMetricMatch?: MetricMatch;
198
247
  }
199
248
  export interface AgentResultPayload {
200
249
  columns: unknown[];
@@ -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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"}
@@ -14,8 +14,12 @@
14
14
  * step. Tests can mock the provider with a canned response and exercise the
15
15
  * full pipeline.
16
16
  */
17
- import { buildSkillBlockHints, buildSkillsPrompt } from './skills/loader.js';
17
+ import { composeEffectiveTrust, } from '@duckcodeailabs/dql-core';
18
+ import { buildSkillBlockHints, buildSkillsPrompt, selectRelevantSkills } from './skills/loader.js';
19
+ import { matchSemanticMetric, resolveGovernedMetricSql } from './metadata/metric-match.js';
20
+ import { decideAgentAction } from './intent-controller.js';
18
21
  import { validateSqlAgainstLocalContext } from './metadata/sql-context-validation.js';
22
+ import { buildGroundingFromRuntimeRelations, resolveRelationsInSql, validateSqlAgainstGrounding } from './metadata/sql-grounding.js';
19
23
  import { compactSqlSnippet, extractSimpleSelectShape, selectExpressionOutputName, } from './metadata/sql-shape.js';
20
24
  const CERTIFIED_HIT_THRESHOLD = 0.18;
21
25
  const HARD_NEGATIVE_RATIO = 0.5;
@@ -24,8 +28,112 @@ const BUSINESS_CONTEXT_KINDS = ['term', 'business_view'];
24
28
  const ARTIFACT_KINDS = [...EXECUTABLE_ARTIFACT_KINDS, ...BUSINESS_CONTEXT_KINDS];
25
29
  const SEMANTIC_KINDS = ['metric', 'dimension', 'measure', 'entity', 'semantic_model', 'saved_query'];
26
30
  const MANIFEST_KINDS = ['dbt_model', 'dbt_source'];
31
+ /**
32
+ * Map an answer-loop result's source tier + certification + review state to a
33
+ * canonical trust-label id from the one shared vocabulary in dql-core.
34
+ * Additive and lenient — keeps the legacy `trustLabel` string untouched.
35
+ */
36
+ function canonicalTrustLabelId(result) {
37
+ if (result.kind === 'no_answer')
38
+ return 'insufficient_context';
39
+ if (result.certification === 'certified' || result.kind === 'certified')
40
+ return 'certified';
41
+ if (result.sourceTier === 'business_context' && result.reviewStatus === 'certified')
42
+ return 'reviewed';
43
+ if (result.certification === 'ai_generated' ||
44
+ result.certification === 'analyst_review_required' ||
45
+ result.reviewStatus === 'analyst_review_required' ||
46
+ result.reviewStatus === 'draft_ready') {
47
+ return 'ai_generated';
48
+ }
49
+ return 'insufficient_context';
50
+ }
27
51
  export async function answer(input) {
52
+ const result = await runAnswerLoop(input);
53
+ // Attach the canonical trust label once, at the single exit point, so every
54
+ // return site inside runAnswerLoop stays untouched and backward compatible.
55
+ // Freshness-aware trust: for a certified answer, fold the source block's data
56
+ // health (stale/failed upstream) into the label so it reads "Certified ·
57
+ // stale data" / "Certified · upstream failed". Non-certified or fresh answers
58
+ // are unaffected.
59
+ const id = canonicalTrustLabelId(result);
60
+ const dataState = id === 'certified'
61
+ ? (result.block?.dataState)
62
+ : undefined;
63
+ const { _semanticMetricMatch, ...publicResult } = result;
64
+ const chosenRoute = result.route ?? deriveAiRoute(result, _semanticMetricMatch);
65
+ // P0 — record the high-level action this turn warranted, so callers can route
66
+ // (compose_app → app build, investigate → research) and the UI can show the
67
+ // agent's reasoning. Computed once at the single exit from the finished answer.
68
+ const tier = chosenRoute?.tier;
69
+ const intentDecision = decideAgentAction({
70
+ question: input.question,
71
+ intent: tier === 'no_answer' ? 'clarify' : 'ad_hoc_ranking',
72
+ signals: {
73
+ certifiedScore: tier === 'certified_block' ? 0.9 : 0,
74
+ metricScore: tier === 'semantic_metric' ? 0.9 : 0,
75
+ hasRetrieval: (result.considered?.length ?? 0) > 0,
76
+ missingContext: tier === 'no_answer' ? ['Need a clearer business object, measure, or grain before answering.'] : [],
77
+ },
78
+ isFollowUp: Boolean(input.followUp),
79
+ });
80
+ return {
81
+ ...publicResult,
82
+ intentDecision,
83
+ trustLabelInfo: composeEffectiveTrust({ id, dataState }),
84
+ // Stamp the SELECTED skills that shaped the answer (transparency). Computed
85
+ // here so every return site inside runAnswerLoop stays untouched.
86
+ appliedSkills: result.appliedSkills ??
87
+ selectRelevantSkills(input.skills ?? [], input.question, { userId: input.userId ?? null }).map((s) => ({
88
+ id: s.id,
89
+ description: s.description,
90
+ })),
91
+ // Stamp the chosen route once, at the single exit point, so every return
92
+ // site inside runAnswerLoop stays untouched (spec 17, part C).
93
+ route: chosenRoute,
94
+ };
95
+ }
96
+ /**
97
+ * Derive the UI-facing route from a finished answer (spec 17, part C). The
98
+ * semantic-metric tier is named explicitly when the loop matched a governed
99
+ * metric; otherwise the route is mapped from the answer's source tier / kind.
100
+ */
101
+ function deriveAiRoute(result, metricMatch) {
102
+ if (result.kind === 'no_answer') {
103
+ return { tier: 'no_answer', label: 'No governed answer — needs more context or review.' };
104
+ }
105
+ if (result.kind === 'certified') {
106
+ if (result.sourceTier === 'business_context') {
107
+ const ref = result.citations[0]?.name;
108
+ return {
109
+ tier: 'business_context',
110
+ label: ref ? `Answered from certified business context ${ref}` : 'Answered from certified business context',
111
+ ref,
112
+ };
113
+ }
114
+ const ref = result.sourceCertifiedBlock ?? result.block?.name ?? result.citations[0]?.name;
115
+ return {
116
+ tier: 'certified_block',
117
+ label: ref ? `Answered from certified block ${ref}` : 'Answered from a certified block',
118
+ ref,
119
+ };
120
+ }
121
+ // Uncertified: a governed metric matched → semantic_metric; else generated SQL.
122
+ if (result.sourceTier === 'semantic_layer' && metricMatch) {
123
+ return {
124
+ tier: 'semantic_metric',
125
+ label: `Answered from metric ${metricMatch.metric.name}`,
126
+ ref: metricMatch.metric.name,
127
+ };
128
+ }
129
+ return { tier: 'generated_sql', label: 'Answered with generated SQL (review required).' };
130
+ }
131
+ async function runAnswerLoop(input) {
28
132
  const { question, userId, domain, provider, kg, skills = [], blockHints = [] } = input;
133
+ // Select the RELEVANT skills (not all) for this question; keep pinned project
134
+ // skills (SQL conventions). Block hints still come from the full set so a
135
+ // preferred-block mapping is never lost.
136
+ const selectedSkills = selectRelevantSkills(skills, question, { userId: userId ?? null });
29
137
  const effectiveBlockHints = Array.from(new Set([
30
138
  ...blockHints,
31
139
  ...buildSkillBlockHints(skills, userId ?? null),
@@ -148,7 +256,16 @@ export async function answer(input) {
148
256
  providerUsed: provider.name,
149
257
  };
150
258
  }
151
- if (intent === 'clarify' || catalogRoute?.route === 'clarify') {
259
+ // Spec 17, part C SEMANTIC-METRIC MATCHING. Must run BEFORE the clarify
260
+ // short-circuit: FTS alone misses clear metric questions ("total revenue" never
261
+ // literally names a metric), so a clarify route would otherwise refuse a question
262
+ // a governed metric can answer. Match by name + synonyms + measure family + hybrid
263
+ // rank over the FTS semantic hits, then ALL metric KG nodes (revenue ⇄
264
+ // cumulative_revenue). Certified-first is still preserved (checked above).
265
+ const semanticMetricNodes = collectMetricCandidates(semanticHits, considered, kg);
266
+ let semanticMetricMatch = await matchSemanticMetric(question, semanticMetricNodes).catch(() => null);
267
+ // Clarify only when there is ALSO no confident governed-metric match.
268
+ if ((intent === 'clarify' || catalogRoute?.route === 'clarify') && !semanticMetricMatch) {
152
269
  const text = composeCatalogClarificationText(question, catalogRoute) ?? composeClarificationText(question, considered, schemaContext);
153
270
  const analysisPlan = buildAnalysisPlan({
154
271
  question,
@@ -189,19 +306,27 @@ export async function answer(input) {
189
306
  }
190
307
  // Stage 2/3: generate only after certified artifacts miss. Semantic context
191
308
  // wins over raw dbt manifest context; memory is appended last as advisory.
192
- const activeTier = sourceTierFromContextPack(input.contextPack) ?? (semanticHits.length > 0
193
- ? 'semantic_layer'
194
- : manifestHits.length > 0
195
- ? 'dbt_manifest'
196
- : 'dbt_manifest');
309
+ // A confident metric match forces the semantic tier even when FTS returned no
310
+ // semantic hits, so the governed metric (not refusal) answers the question.
311
+ const activeTier = sourceTierFromContextPack(input.contextPack)
312
+ ?? (semanticHits.length > 0 || semanticMetricMatch
313
+ ? 'semantic_layer'
314
+ : manifestHits.length > 0
315
+ ? 'dbt_manifest'
316
+ : 'dbt_manifest');
197
317
  const reviewRequiredArtifactHits = artifactHits
198
318
  .filter((hit) => hit.score >= CERTIFIED_HIT_THRESHOLD && !isCertifiedHit(hit, kg))
199
319
  .slice(0, 4);
200
320
  const trustedArtifactContext = rankGeneratedContextHits(executableArtifactHits.filter((hit) => !excludedArtifactIds?.has(hit.node.nodeId)), schemaContext, question)
201
321
  .filter((hit) => !excludedArtifactIds?.has(hit.node.nodeId))
202
322
  .slice(0, 5);
323
+ // When a governed metric matched (spec 17, part C), pin it at the front of the
324
+ // semantic context so the generated SQL is grounded on the metric definition.
325
+ const matchedMetricHit = semanticMetricMatch
326
+ ? [{ node: semanticMetricMatch.metric, score: Math.max(semanticMetricMatch.score, CERTIFIED_HIT_THRESHOLD) }]
327
+ : [];
203
328
  const contextHits = activeTier === 'semantic_layer'
204
- ? [...trustedArtifactContext, ...reviewRequiredArtifactHits, ...businessHits.slice(0, 4), ...semanticHits, ...manifestHits].slice(0, 14)
329
+ ? [...matchedMetricHit, ...trustedArtifactContext, ...reviewRequiredArtifactHits, ...businessHits.slice(0, 4), ...semanticHits, ...manifestHits].slice(0, 14)
205
330
  : [...trustedArtifactContext, ...reviewRequiredArtifactHits, ...businessHits.slice(0, 4), ...manifestHits].slice(0, 14);
206
331
  const contextNodes = mergeNodes(followUpSourceBlock && input.followUp?.kind === 'drilldown' ? [followUpSourceBlock] : [], (contextHits.length > 0 ? contextHits : considered.slice(0, 6)).map((h) => h.node));
207
332
  const contextBlocks = contextNodes.filter((n) => n.kind === 'block');
@@ -210,7 +335,7 @@ export async function answer(input) {
210
335
  const messages = [
211
336
  { role: 'system', content: SYSTEM_PROMPT },
212
337
  ];
213
- const skillsPrompt = buildSkillsPrompt(skills, userId ?? null);
338
+ const skillsPrompt = buildSkillsPrompt(selectedSkills, userId ?? null);
214
339
  if (skillsPrompt)
215
340
  messages.push({ role: 'system', content: skillsPrompt });
216
341
  messages.push({
@@ -267,6 +392,35 @@ export async function answer(input) {
267
392
  }
268
393
  parsed = parseProposal(proposed);
269
394
  }
395
+ // True when `parsed.sql` was synthesized deterministically from a governed
396
+ // semantic-layer metric (not the LLM). Such SQL is trusted and grounded against
397
+ // the runtime schema, so it skips the hallucination-guard context validation
398
+ // that exists to catch model-invented relations/columns.
399
+ let governedMetricAnswer = false;
400
+ if (!parsed.sql) {
401
+ // Spec 17, part C — if a governed metric matched confidently but the model
402
+ // declined SQL, answer from the metric definition (deterministic, offline)
403
+ // rather than refusing. The semantic tier is the governed answer here. A
404
+ // derived MetricFlow metric (e.g. `revenue`) often carries no executable
405
+ // definition itself — its `table:`/`sql:` live on the backing measure node
406
+ // (`order_item.revenue`). resolveGovernedMetricSql resolves a thin metric to
407
+ // that synthesizable sibling so the route lands on a real number, not refusal.
408
+ const resolved = activeTier === 'semantic_layer' && semanticMetricMatch
409
+ ? resolveGovernedMetricSql(semanticMetricMatch.metric, semanticMetricNodes)
410
+ : undefined;
411
+ if (resolved) {
412
+ // Point the match at the metric we actually answered from so the route
413
+ // badge, citation, and carrier all reflect the executable definition used.
414
+ semanticMetricMatch = { ...semanticMetricMatch, metric: resolved.metric };
415
+ governedMetricAnswer = true;
416
+ parsed = {
417
+ sql: resolved.sql,
418
+ text: parsed.text ||
419
+ `Answered from the governed metric ${resolved.metric.name}. This result is uncertified until reviewed and promoted.`,
420
+ viz: parsed.viz ?? 'single_value',
421
+ };
422
+ }
423
+ }
270
424
  if (!parsed.sql) {
271
425
  const text = parsed.text || 'No answer (the model declined to propose SQL).';
272
426
  return {
@@ -294,11 +448,69 @@ export async function answer(input) {
294
448
  providerUsed: provider.name,
295
449
  };
296
450
  }
297
- const contextValidation = validateSqlAgainstLocalContext(parsed.sql, input.contextPack, {
298
- question,
299
- intent,
300
- filterValues: input.followUp?.filters,
301
- });
451
+ // Shared grounding (spec 15): deterministically qualify any bare relation the
452
+ // model emitted to its real warehouse relation from the runtime schema BEFORE
453
+ // governance validation. Same resolver the build path uses — one grounding,
454
+ // no weak path. `allowedSqlContext` relations are already qualified.
455
+ let grounding;
456
+ if (parsed.sql && schemaContext.length > 0) {
457
+ grounding = buildGroundingFromRuntimeRelations(schemaContext.map((table) => ({
458
+ relation: table.relation,
459
+ name: table.name,
460
+ columns: table.columns.map((column) => ({ name: column.name, type: column.type, description: column.description })),
461
+ })));
462
+ parsed.sql = resolveRelationsInSql(parsed.sql, grounding, { prefer: 'qualified' }).sql;
463
+ }
464
+ // Validation gate. Governed metric SQL synthesized from the semantic layer is
465
+ // already trusted (deterministic + grounded); model SQL is validated against the
466
+ // inspected context to catch hallucinated relations/columns.
467
+ const semanticMetricRoute = activeTier === 'semantic_layer' && Boolean(semanticMetricMatch);
468
+ let contextValidation;
469
+ if (governedMetricAnswer) {
470
+ contextValidation = { ok: true, warnings: [] };
471
+ }
472
+ else {
473
+ const c = validateSqlAgainstLocalContext(parsed.sql, input.contextPack, {
474
+ question,
475
+ intent,
476
+ filterValues: input.followUp?.filters,
477
+ });
478
+ contextValidation = c.ok
479
+ ? { ok: true, warnings: c.warnings }
480
+ : { ok: false, error: c.error, warnings: c.warnings };
481
+ }
482
+ // Spec 17, part C — semantic-metric route recovery. A metric question is often
483
+ // catalog-routed to clarify, leaving a thin contextPack that rejects otherwise-valid
484
+ // SQL. Rather than refuse, recover in two steps:
485
+ // 1) Re-judge the model's own SQL against the RUNTIME grounding. If it references
486
+ // real relations/columns it is valid (the contextPack rejection was a false
487
+ // negative) — keep the model SQL so a precise answer like `count(*) FROM orders`
488
+ // stands instead of guessing a measure.
489
+ // 2) Otherwise fall back to a CLEAN governed-metric definition (direct or exact
490
+ // leaf-measure; no fuzzy family guess that could answer the wrong measure).
491
+ if (!contextValidation.ok && semanticMetricRoute && semanticMetricMatch) {
492
+ const grounded = grounding ? validateSqlAgainstGrounding(parsed.sql, grounding) : undefined;
493
+ if (grounded?.ok) {
494
+ contextValidation = { ok: true, warnings: grounded.warnings };
495
+ }
496
+ else {
497
+ const recovered = resolveGovernedMetricSql(semanticMetricMatch.metric, semanticMetricNodes);
498
+ if (recovered) {
499
+ semanticMetricMatch = { ...semanticMetricMatch, metric: recovered.metric };
500
+ parsed.sql = grounding
501
+ ? resolveRelationsInSql(recovered.sql, grounding, { prefer: 'qualified' }).sql
502
+ : recovered.sql;
503
+ parsed.viz = parsed.viz ?? 'single_value';
504
+ parsed.text = parsed.text
505
+ || `Answered from the governed metric ${recovered.metric.name}. This result is uncertified until reviewed and promoted.`;
506
+ governedMetricAnswer = true;
507
+ contextValidation = {
508
+ ok: true,
509
+ warnings: ['Generated SQL failed context validation; answered from the governed metric definition instead.'],
510
+ };
511
+ }
512
+ }
513
+ }
302
514
  if (!contextValidation.ok) {
303
515
  const text = `I could not safely prepare this generated SQL from the inspected context. ${contextValidation.error}`;
304
516
  const analysisPlan = buildAnalysisPlan({
@@ -514,6 +726,9 @@ export async function answer(input) {
514
726
  contextPack: input.contextPack,
515
727
  considered,
516
728
  providerUsed: localProposal ? 'schema_planner' : provider.name,
729
+ // Carry the governed metric match so the exit point can name a
730
+ // `semantic_metric` route (spec 17, part C).
731
+ _semanticMetricMatch: activeTier === 'semantic_layer' ? semanticMetricMatch ?? undefined : undefined,
517
732
  };
518
733
  }
519
734
  const SYSTEM_PROMPT = `You are the DQL Analytics Agent.
@@ -590,6 +805,17 @@ function renderContextPrompt(blocks, businessContext, others, activeTier, memory
590
805
  .map((m) => `- ${m.scope}${m.scopeId ? `:${m.scopeId}` : ''} \`${m.title}\` (${m.source}, confidence ${m.confidence}): ${m.content}`)
591
806
  .join('\n')}`
592
807
  : '';
808
+ // Approved, scope-matched correction hints from the Hint Graph. These are
809
+ // human-reviewed lessons (often from a prior wrong→right SQL correction), so
810
+ // they carry more weight than advisory memory when generating SQL — but they
811
+ // still must NOT override a certified artifact that already answers exactly.
812
+ const appliedHints = contextPack?.appliedHints ?? [];
813
+ const hintsSection = appliedHints.length > 0
814
+ ? `\n\n## Applied governed hints (human-approved corrections)\n\nReviewed, scope-matched corrections from your team. Apply them when generating SQL to avoid known mistakes; they refine generated SQL but MUST NOT override a certified artifact that already answers the question.\n${appliedHints
815
+ .slice(0, 6)
816
+ .map((h) => `- \`${h.title}\`: ${h.guidance}${h.correctedSql ? `\n corrected SQL pattern: ${h.correctedSql.replace(/\s+/g, ' ').trim().slice(0, 240)}` : ''}`)
817
+ .join('\n')}`
818
+ : '';
593
819
  const extraSection = extraContext?.trim()
594
820
  ? `\n\n## Current notebook/app context\n\nThis context may help interpret the user's request, but it MUST NOT override certified artifacts, semantic metrics, dbt metadata, or generated SQL validation.\n\n${extraContext.trim()}`
595
821
  : '';
@@ -599,7 +825,7 @@ function renderContextPrompt(blocks, businessContext, others, activeTier, memory
599
825
  const contextPackSection = contextPack
600
826
  ? `\n\n## Local metadata context pack\n\n${renderContextPackForPrompt(contextPack)}`
601
827
  : '';
602
- return `${intentSection}\n\n${blockSection}${businessSection}${otherSection}${schemaSection}${contextPackSection}${memorySection}${extraSection}${followUpSection}`;
828
+ return `${intentSection}\n\n${blockSection}${businessSection}${otherSection}${schemaSection}${contextPackSection}${memorySection}${hintsSection}${extraSection}${followUpSection}`;
603
829
  }
604
830
  function renderContextPackForPrompt(contextPack) {
605
831
  const questionPlan = contextPack.questionPlan
@@ -718,14 +944,19 @@ function renderSourceBlockSqlContext(contextPack) {
718
944
  .join(', ')
719
945
  : '';
720
946
  const snippet = compactSqlSnippet(source.sql, 280);
947
+ // Pair each certified block's SQL with the natural-language question it
948
+ // answers so it reads as a question→SQL few-shot exemplar (DAIL-SQL).
949
+ const anchorRaw = source.exampleQuestion ?? source.description;
950
+ const anchor = anchorRaw ? anchorRaw.replace(/\s+/g, ' ').trim().slice(0, 160) : '';
721
951
  return [
722
- `- ${source.name}${source.status ? ` (${source.status})` : ''}`,
952
+ `- ${source.name}${source.status ? ` (${source.status})` : ''}${source.grain ? ` — grain: ${source.grain}` : ''}`,
953
+ anchor ? ` answers: ${anchor}` : '',
723
954
  shape?.relation ? ` relation: ${shape.relation}` : '',
724
955
  projectedColumns ? ` projected columns: ${projectedColumns}` : '',
725
956
  snippet ? ` sql: ${snippet}` : '',
726
957
  ].filter(Boolean).join('\n');
727
958
  });
728
- return `Certified source SQL shape context:\n${lines.join('\n')}`;
959
+ return `## Worked examples from certified blocks (few-shot patterns)\n\nThese certified blocks already answer similar questions. Learn their join paths, grain, and filters and ADAPT them to the question — do not copy blindly, and do not relabel generated SQL as certified.\n${lines.join('\n')}`;
729
960
  }
730
961
  function contextPackCitations(contextPack, limit) {
731
962
  if (!contextPack)
@@ -2789,9 +3020,30 @@ function composeCertifiedAnswer(artifact, question, result, executionError) {
2789
3020
  : artifact.kind === 'block'
2790
3021
  ? 'Governed execution was not requested by this host.'
2791
3022
  : `Matched certified ${artifact.kind.replace('_', ' ')} context.`;
3023
+ // Freshness-aware trust: the block's logic is certified, but its upstream
3024
+ // data may be stale or its last dbt run may have failed. Caveat the answer so
3025
+ // a consumer can weigh it — "certified" is not the same as "fresh".
3026
+ const freshnessCaveat = certifiedFreshnessCaveat(artifact);
2792
3027
  return `Outcome: Reuse certified block\n\nAnswered by certified ${artifact.kind.replace('_', ' ')} **${artifact.name}**${tag}.\n\n${desc ? `${desc}\n\n${resultText}` : resultText}`
3028
+ + (freshnessCaveat ? `\n\n${freshnessCaveat}` : '')
2793
3029
  + `\n\n_Question:_ ${question}`;
2794
3030
  }
3031
+ /**
3032
+ * Build a one-line data-freshness caveat for a certified answer, or `undefined`
3033
+ * when the upstream data is fresh / un-instrumented. Stale and failed upstreams
3034
+ * are surfaced so a certified-but-stale answer is never presented as if its data
3035
+ * were current.
3036
+ */
3037
+ function certifiedFreshnessCaveat(artifact) {
3038
+ switch (artifact.dataState) {
3039
+ case 'failed':
3040
+ return `⚠️ Data caveat: an upstream dbt model's last run failed, so this certified result may be missing or out of date.${artifact.dataStateDetail ? ` ${artifact.dataStateDetail}` : ''}`;
3041
+ case 'stale':
3042
+ return `⚠️ Data caveat: upstream data is past its freshness window, so this certified result may be stale.${artifact.dataStateDetail ? ` ${artifact.dataStateDetail}` : ''}`;
3043
+ default:
3044
+ return undefined;
3045
+ }
3046
+ }
2795
3047
  function mergeHits(...groups) {
2796
3048
  const byId = new Map();
2797
3049
  for (const group of groups) {
@@ -2813,6 +3065,29 @@ function mergeNodes(...groups) {
2813
3065
  }
2814
3066
  return Array.from(byId.values());
2815
3067
  }
3068
+ /**
3069
+ * Candidate metric KG nodes for semantic-metric matching (spec 17, part C).
3070
+ * Starts with the FTS semantic + considered hits, then folds in EVERY metric
3071
+ * node from the KG so a confident measure-family match is found even when FTS
3072
+ * surfaced no metric at all (the "total revenue" miss). Bounded for safety.
3073
+ */
3074
+ function collectMetricCandidates(semanticHits, considered, kg) {
3075
+ const byId = new Map();
3076
+ for (const hit of [...semanticHits, ...considered]) {
3077
+ if (hit.node.kind === 'metric')
3078
+ byId.set(hit.node.nodeId, hit.node);
3079
+ }
3080
+ try {
3081
+ for (const node of kg.getNodesByKind('metric', 200)) {
3082
+ if (!byId.has(node.nodeId))
3083
+ byId.set(node.nodeId, node);
3084
+ }
3085
+ }
3086
+ catch {
3087
+ // Best-effort: a KG without a getNodesByKind still matches the FTS hits.
3088
+ }
3089
+ return Array.from(byId.values());
3090
+ }
2816
3091
  function buildCertifiedEvidence(input) {
2817
3092
  const businessContextAssets = uniqueAssets(input.businessHits
2818
3093
  .map((hit) => hit.node)