@duckcodeailabs/dql-agent 1.13.5 → 1.14.1

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 (158) hide show
  1. package/dist/agent-run-engine.d.ts +79 -4
  2. package/dist/agent-run-engine.d.ts.map +1 -1
  3. package/dist/agent-run-engine.js +457 -39
  4. package/dist/agent-run-engine.js.map +1 -1
  5. package/dist/agent-run-gates.d.ts.map +1 -1
  6. package/dist/agent-run-gates.js +13 -0
  7. package/dist/agent-run-gates.js.map +1 -1
  8. package/dist/agent-run-store.d.ts.map +1 -1
  9. package/dist/agent-run-store.js +42 -25
  10. package/dist/agent-run-store.js.map +1 -1
  11. package/dist/agentic/analyst-loop.d.ts +91 -0
  12. package/dist/agentic/analyst-loop.d.ts.map +1 -0
  13. package/dist/agentic/analyst-loop.js +311 -0
  14. package/dist/agentic/analyst-loop.js.map +1 -0
  15. package/dist/agentic/assumptions.d.ts +62 -0
  16. package/dist/agentic/assumptions.d.ts.map +1 -0
  17. package/dist/agentic/assumptions.js +73 -0
  18. package/dist/agentic/assumptions.js.map +1 -0
  19. package/dist/agentic/business-explanation.d.ts +56 -0
  20. package/dist/agentic/business-explanation.d.ts.map +1 -0
  21. package/dist/agentic/business-explanation.js +130 -0
  22. package/dist/agentic/business-explanation.js.map +1 -0
  23. package/dist/agentic/identifier-ledger.d.ts +74 -0
  24. package/dist/agentic/identifier-ledger.d.ts.map +1 -0
  25. package/dist/agentic/identifier-ledger.js +151 -0
  26. package/dist/agentic/identifier-ledger.js.map +1 -0
  27. package/dist/agentic/ledger-tools.d.ts +68 -0
  28. package/dist/agentic/ledger-tools.d.ts.map +1 -0
  29. package/dist/agentic/ledger-tools.js +198 -0
  30. package/dist/agentic/ledger-tools.js.map +1 -0
  31. package/dist/agentic/orchestrator-policy.d.ts +60 -0
  32. package/dist/agentic/orchestrator-policy.d.ts.map +1 -0
  33. package/dist/agentic/orchestrator-policy.js +73 -0
  34. package/dist/agentic/orchestrator-policy.js.map +1 -0
  35. package/dist/agentic/orchestrator.d.ts +54 -0
  36. package/dist/agentic/orchestrator.d.ts.map +1 -0
  37. package/dist/agentic/orchestrator.js +67 -0
  38. package/dist/agentic/orchestrator.js.map +1 -0
  39. package/dist/agentic/preview-tool.d.ts +36 -0
  40. package/dist/agentic/preview-tool.d.ts.map +1 -0
  41. package/dist/agentic/preview-tool.js +71 -0
  42. package/dist/agentic/preview-tool.js.map +1 -0
  43. package/dist/agentic/rerank.d.ts +50 -0
  44. package/dist/agentic/rerank.d.ts.map +1 -0
  45. package/dist/agentic/rerank.js +140 -0
  46. package/dist/agentic/rerank.js.map +1 -0
  47. package/dist/agentic/research/hypothesis.d.ts +110 -0
  48. package/dist/agentic/research/hypothesis.d.ts.map +1 -0
  49. package/dist/agentic/research/hypothesis.js +125 -0
  50. package/dist/agentic/research/hypothesis.js.map +1 -0
  51. package/dist/agentic/research/synthesis.d.ts +32 -0
  52. package/dist/agentic/research/synthesis.d.ts.map +1 -0
  53. package/dist/agentic/research/synthesis.js +94 -0
  54. package/dist/agentic/research/synthesis.js.map +1 -0
  55. package/dist/agentic/research-agent.d.ts +54 -0
  56. package/dist/agentic/research-agent.d.ts.map +1 -0
  57. package/dist/agentic/research-agent.js +176 -0
  58. package/dist/agentic/research-agent.js.map +1 -0
  59. package/dist/agentic/sql-authorization.d.ts +131 -0
  60. package/dist/agentic/sql-authorization.d.ts.map +1 -0
  61. package/dist/agentic/sql-authorization.js +232 -0
  62. package/dist/agentic/sql-authorization.js.map +1 -0
  63. package/dist/agentic/tool-loop.d.ts +29 -0
  64. package/dist/agentic/tool-loop.d.ts.map +1 -1
  65. package/dist/agentic/tool-loop.js +129 -52
  66. package/dist/agentic/tool-loop.js.map +1 -1
  67. package/dist/agentic/toolset.d.ts.map +1 -1
  68. package/dist/agentic/toolset.js +155 -0
  69. package/dist/agentic/toolset.js.map +1 -1
  70. package/dist/agentic/turn-plan.d.ts +35 -0
  71. package/dist/agentic/turn-plan.d.ts.map +1 -0
  72. package/dist/agentic/turn-plan.js +108 -0
  73. package/dist/agentic/turn-plan.js.map +1 -0
  74. package/dist/agentic/value-probe.d.ts +89 -0
  75. package/dist/agentic/value-probe.d.ts.map +1 -0
  76. package/dist/agentic/value-probe.js +304 -0
  77. package/dist/agentic/value-probe.js.map +1 -0
  78. package/dist/analytical-orchestration.d.ts +336 -0
  79. package/dist/analytical-orchestration.d.ts.map +1 -0
  80. package/dist/analytical-orchestration.js +631 -0
  81. package/dist/analytical-orchestration.js.map +1 -0
  82. package/dist/analytical-request-policy.d.ts +26 -0
  83. package/dist/analytical-request-policy.d.ts.map +1 -0
  84. package/dist/analytical-request-policy.js +82 -0
  85. package/dist/analytical-request-policy.js.map +1 -0
  86. package/dist/analytical-result-facts.d.ts +14 -1
  87. package/dist/analytical-result-facts.d.ts.map +1 -1
  88. package/dist/analytical-result-facts.js +76 -1
  89. package/dist/analytical-result-facts.js.map +1 -1
  90. package/dist/answer-loop.d.ts +61 -16
  91. package/dist/answer-loop.d.ts.map +1 -1
  92. package/dist/answer-loop.js +256 -36
  93. package/dist/answer-loop.js.map +1 -1
  94. package/dist/cascade/budgets.d.ts.map +1 -1
  95. package/dist/cascade/budgets.js +8 -1
  96. package/dist/cascade/budgets.js.map +1 -1
  97. package/dist/cascade/packer.d.ts +88 -0
  98. package/dist/cascade/packer.d.ts.map +1 -0
  99. package/dist/cascade/packer.js +106 -0
  100. package/dist/cascade/packer.js.map +1 -0
  101. package/dist/conversation/result-ops.d.ts +18 -3
  102. package/dist/conversation/result-ops.d.ts.map +1 -1
  103. package/dist/conversation/result-ops.js +77 -5
  104. package/dist/conversation/result-ops.js.map +1 -1
  105. package/dist/conversation/rolling-summary.d.ts.map +1 -1
  106. package/dist/conversation/rolling-summary.js +7 -2
  107. package/dist/conversation/rolling-summary.js.map +1 -1
  108. package/dist/conversation/session-store.d.ts +3 -0
  109. package/dist/conversation/session-store.d.ts.map +1 -1
  110. package/dist/conversation/session-store.js +8 -4
  111. package/dist/conversation/session-store.js.map +1 -1
  112. package/dist/conversation/snapshot.d.ts.map +1 -1
  113. package/dist/conversation/snapshot.js +13 -1
  114. package/dist/conversation/snapshot.js.map +1 -1
  115. package/dist/conversation/turn-trust.d.ts.map +1 -1
  116. package/dist/conversation/turn-trust.js +6 -0
  117. package/dist/conversation/turn-trust.js.map +1 -1
  118. package/dist/hints/store.d.ts.map +1 -1
  119. package/dist/hints/store.js +15 -5
  120. package/dist/hints/store.js.map +1 -1
  121. package/dist/index.d.ts +27 -5
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +28 -4
  124. package/dist/index.js.map +1 -1
  125. package/dist/intent-controller.d.ts +8 -0
  126. package/dist/intent-controller.d.ts.map +1 -1
  127. package/dist/intent-controller.js +65 -0
  128. package/dist/intent-controller.js.map +1 -1
  129. package/dist/meaning-resolution.d.ts +7 -0
  130. package/dist/meaning-resolution.d.ts.map +1 -1
  131. package/dist/meaning-resolution.js.map +1 -1
  132. package/dist/metadata/block-fit.d.ts +18 -0
  133. package/dist/metadata/block-fit.d.ts.map +1 -1
  134. package/dist/metadata/block-fit.js +41 -1
  135. package/dist/metadata/block-fit.js.map +1 -1
  136. package/dist/metadata/catalog.d.ts +44 -23
  137. package/dist/metadata/catalog.d.ts.map +1 -1
  138. package/dist/metadata/catalog.js +363 -53
  139. package/dist/metadata/catalog.js.map +1 -1
  140. package/dist/metadata/meaning-evidence.d.ts.map +1 -1
  141. package/dist/metadata/meaning-evidence.js +101 -8
  142. package/dist/metadata/meaning-evidence.js.map +1 -1
  143. package/dist/metadata/metric-match.d.ts +32 -0
  144. package/dist/metadata/metric-match.d.ts.map +1 -1
  145. package/dist/metadata/metric-match.js +120 -9
  146. package/dist/metadata/metric-match.js.map +1 -1
  147. package/dist/research-loop.d.ts +33 -2
  148. package/dist/research-loop.d.ts.map +1 -1
  149. package/dist/research-loop.js +44 -4
  150. package/dist/research-loop.js.map +1 -1
  151. package/dist/resolved-analytical-plan.d.ts.map +1 -1
  152. package/dist/resolved-analytical-plan.js +92 -5
  153. package/dist/resolved-analytical-plan.js.map +1 -1
  154. package/dist/router.d.ts +36 -16
  155. package/dist/router.d.ts.map +1 -1
  156. package/dist/router.js +604 -25
  157. package/dist/router.js.map +1 -1
  158. package/package.json +4 -4
@@ -0,0 +1,91 @@
1
+ import type { AdmittedIdentifier } from './identifier-ledger.js';
2
+ /**
3
+ * The analyst loop — explore, then compose, with provenance enforced.
4
+ *
5
+ * This is the handler the strangler seam has been holding a slot for. It runs
6
+ * the governed tools, harvests every identifier a tool actually returned, and
7
+ * refuses to execute SQL naming anything it did not observe. A violation comes
8
+ * back as a CORRECTION with the nearest observed name and one bounded retry —
9
+ * not a refusal, which is what the legacy pipeline turned it into.
10
+ *
11
+ * It is deliberately small. The legacy loop earned its 10k lines by owning
12
+ * retrieval, certified matching, semantic compilation, narration, and trust
13
+ * labelling; re-implementing any of that here would fork behaviour that already
14
+ * works. This owns exactly one thing the old path cannot do: notice that a
15
+ * proposed identifier was never observed, and say which real one was meant.
16
+ */
17
+ import type { AgentAnswer, AnswerLoopInput } from '../answer-loop.js';
18
+ import type { AgentToolDefinition } from '../providers/types.js';
19
+ import type { AnalystTurnPlan } from './turn-plan.js';
20
+ /** One observable step, so the wait is legible instead of a spinner. */
21
+ export interface AnalystStep {
22
+ kind: 'plan' | 'tool' | 'observe' | 'verify' | 'answer';
23
+ label: string;
24
+ detail?: string;
25
+ }
26
+ export interface AnalystLoopDeps {
27
+ /** Extract relations/columns from candidate SQL. Injected so the loop does not fork a parser. */
28
+ extractReferences: (sql: string) => {
29
+ relations: string[];
30
+ columns: string[];
31
+ };
32
+ /** Turn raw model output into SQL. Injected for the same reason. */
33
+ parseSql: (raw: string) => string | undefined;
34
+ /** Tools the model may call. Already surface-filtered by the host. */
35
+ tools: AgentToolDefinition[];
36
+ /**
37
+ * Run the deterministic safety verifiers over candidate SQL and return a
38
+ * correction, or `undefined` when it passes.
39
+ *
40
+ * The verifiers keep their logic — aggregation safety, grain, relationship
41
+ * proof — but a failure becomes an OBSERVATION the loop can act on instead of
42
+ * a terminal refusal. That inversion is the point: the legacy path turned
43
+ * "this would double-count" into "nothing was executed", which is true and
44
+ * useless. Telling the model that joining those tables fans out is something
45
+ * it can fix.
46
+ */
47
+ verifySql?: (sql: string) => string | undefined;
48
+ maxIterations: number;
49
+ /** Physical provider sends available to this text/native tool loop. */
50
+ maxProviderDispatches?: number;
51
+ /**
52
+ * Optional structured planning call. Absent, the loop runs exactly as before
53
+ * and the trace falls back to a fixed label.
54
+ */
55
+ planTurn?: (question: string, toolNames: string[]) => Promise<AnalystTurnPlan | undefined>;
56
+ onStep?: (step: AnalystStep) => void;
57
+ }
58
+ export interface AnalystOutcome {
59
+ sql?: string;
60
+ /** Identifiers the run proved, for the audit trail. */
61
+ admitted: string[];
62
+ /** The same admissions with their evidence, for minting an authorization. */
63
+ admittedEntries: AdmittedIdentifier[];
64
+ /** Corrections fed back to the model, in order. */
65
+ corrections: string[];
66
+ /** Why the loop stopped. */
67
+ stop: 'composed' | 'no_sql' | 'unverified' | 'budget_exhausted';
68
+ /** Typed terminal detail: this never grants an execution fallback. */
69
+ terminal?: 'no_final_sql' | 'tool_budget_exhausted' | 'provider_dispatch_budget_exhausted' | 'unverified_identifiers' | 'missing_execution_binding' | 'tool_loop_error';
70
+ }
71
+ /**
72
+ * Run explore-then-compose and return verified SQL.
73
+ *
74
+ * Returns `stop: 'unverified'` rather than throwing when the repair budget is
75
+ * spent: the caller decides whether an unverified draft is still worth showing
76
+ * as review-required, and that is a governance choice, not this loop's.
77
+ */
78
+ export declare function runAnalystLoop(input: AnswerLoopInput, deps: AnalystLoopDeps): Promise<AnalystOutcome>;
79
+ /**
80
+ * Wrap the loop as a lane handler, falling back to the legacy answer loop.
81
+ *
82
+ * The legacy loop still produces the ANSWER — trust labels, citations,
83
+ * narration, execution. This only decides whether the SQL that reaches it was
84
+ * built from observed identifiers. Handing verified SQL to a path that already
85
+ * knows how to govern it is a much smaller change than replacing that path.
86
+ */
87
+ export declare function createAnalystLaneHandler(deps: {
88
+ legacy: (input: AnswerLoopInput) => Promise<AgentAnswer>;
89
+ buildDeps: (input: AnswerLoopInput) => AnalystLoopDeps | undefined;
90
+ }): (input: AnswerLoopInput) => Promise<AgentAnswer>;
91
+ //# sourceMappingURL=analyst-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyst-loop.d.ts","sourceRoot":"","sources":["../../src/agentic/analyst-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKtD,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,iGAAiG;IACjG,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC/E,oEAAoE;IACpE,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC9C,sEAAsE;IACtE,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAC3F,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,6EAA6E;IAC7E,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,mDAAmD;IACnD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,4BAA4B;IAC5B,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,GAAG,kBAAkB,CAAC;IAChE,sEAAsE;IACtE,QAAQ,CAAC,EAAE,cAAc,GACrB,uBAAuB,GACvB,oCAAoC,GACpC,wBAAwB,GACxB,2BAA2B,GAC3B,iBAAiB,CAAC;CACvB;AAWD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,cAAc,CAAC,CAmIzB;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACzD,SAAS,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,eAAe,GAAG,SAAS,CAAC;CACpE,IACe,OAAO,eAAe,KAAG,OAAO,CAAC,WAAW,CAAC,CAoG5D"}
@@ -0,0 +1,311 @@
1
+ import { createAgenticSqlExecutionCapability } from './sql-authorization.js';
2
+ import { runAgenticToolLoopDetailed } from './tool-loop.js';
3
+ import { IdentifierLedger } from './identifier-ledger.js';
4
+ import { ANALYST_TOOL_POLICY, adjudicateProposedSql, withLedgerHarvest } from './ledger-tools.js';
5
+ function budgetTerminalForToolLoop(stop) {
6
+ return stop === 'tool_budget_exhausted' || stop === 'provider_dispatch_budget_exhausted'
7
+ ? stop
8
+ : undefined;
9
+ }
10
+ /**
11
+ * Run explore-then-compose and return verified SQL.
12
+ *
13
+ * Returns `stop: 'unverified'` rather than throwing when the repair budget is
14
+ * spent: the caller decides whether an unverified draft is still worth showing
15
+ * as review-required, and that is a governance choice, not this loop's.
16
+ */
17
+ export async function runAnalystLoop(input, deps) {
18
+ const ledger = new IdentifierLedger();
19
+ const corrections = [];
20
+ // Seed from the context pack. These are retrieved, not observed, so they enter
21
+ // at the weakest tier — enough to let correct SQL through without pretending a
22
+ // catalog row proves a column exists in the warehouse.
23
+ for (const object of input.contextPack?.allowedSqlContext?.relations ?? []) {
24
+ ledger.admit('catalog', [object.relation, object.name], 'context_pack');
25
+ ledger.admit('catalog', (object.columns ?? []).map((column) => column.name), 'context_pack');
26
+ }
27
+ const tools = withLedgerHarvest(deps.tools, ledger, (event) => {
28
+ deps.onStep?.({
29
+ kind: 'observe',
30
+ label: `${event.tool} returned ${event.admitted} identifier${event.admitted === 1 ? '' : 's'}`,
31
+ });
32
+ });
33
+ const messages = [
34
+ { role: 'system', content: ANALYST_TOOL_POLICY },
35
+ { role: 'user', content: input.question },
36
+ ];
37
+ // One structured planning call before acting. It is optional by design:
38
+ // `planAnalystTurn` swallows its own failures, so a slow or unparseable plan
39
+ // costs a bounded wait and nothing else. What it buys is a trace that names
40
+ // what the agent is about to verify instead of a fixed string.
41
+ const turnPlan = deps.planTurn
42
+ ? await deps.planTurn(input.question, tools.map((tool) => tool.name))
43
+ : undefined;
44
+ if (turnPlan) {
45
+ deps.onStep?.({ kind: 'plan', label: turnPlan.restatement });
46
+ for (const item of turnPlan.mustEstablish) {
47
+ deps.onStep?.({ kind: 'plan', label: `Must establish: ${item}` });
48
+ }
49
+ // Carry the plan into the loop so it steers the run rather than only
50
+ // describing it — otherwise the trace would promise work the model never
51
+ // agreed to do.
52
+ messages.push({
53
+ role: 'assistant',
54
+ content: `Before answering I must establish, using tools:\n${turnPlan.mustEstablish.map((item) => `- ${item}`).join('\n')}${turnPlan.openingTool ? `\nStarting with ${turnPlan.openingTool}.` : ''}`,
55
+ });
56
+ }
57
+ else {
58
+ deps.onStep?.({ kind: 'plan', label: 'Establishing what exists before writing SQL' });
59
+ }
60
+ const initial = await runAgenticToolLoopDetailed(input.provider, messages, tools, {
61
+ ...(input.signal ? { signal: input.signal } : {}),
62
+ maxToolCalls: deps.maxIterations,
63
+ ...(deps.maxProviderDispatches !== undefined ? { maxProviderDispatches: deps.maxProviderDispatches } : {}),
64
+ });
65
+ let raw = initial.text;
66
+ const initialBudgetTerminal = budgetTerminalForToolLoop(initial.stop);
67
+ if (initialBudgetTerminal) {
68
+ return {
69
+ admitted: ledger.entries().map((e) => e.identifier),
70
+ admittedEntries: ledger.entries(),
71
+ corrections,
72
+ stop: 'budget_exhausted',
73
+ terminal: initialBudgetTerminal,
74
+ };
75
+ }
76
+ let sql = deps.parseSql(raw);
77
+ if (!sql) {
78
+ return {
79
+ admitted: ledger.entries().map((e) => e.identifier),
80
+ admittedEntries: ledger.entries(),
81
+ corrections,
82
+ stop: 'no_sql',
83
+ terminal: 'no_final_sql',
84
+ };
85
+ }
86
+ // One bounded repair. A second would mostly re-spend the budget: if the first
87
+ // correction — which names the exact identifier that was observed — does not
88
+ // land, the problem is not a typo.
89
+ for (let attempt = 0; attempt < 2; attempt += 1) {
90
+ deps.onStep?.({ kind: 'verify', label: 'Checking every identifier was actually observed' });
91
+ const verdict = adjudicateProposedSql(ledger, deps.extractReferences(sql));
92
+ // Identifiers first: a safety verdict over SQL naming a column that does not
93
+ // exist is noise, and would send the model chasing the wrong correction.
94
+ const safety = verdict.ok ? deps.verifySql?.(sql) : undefined;
95
+ if (verdict.ok && !safety) {
96
+ deps.onStep?.({ kind: 'answer', label: 'Verified — every name came from a tool result' });
97
+ return { sql, admitted: ledger.entries().map((e) => e.identifier), admittedEntries: ledger.entries(), corrections, stop: 'composed' };
98
+ }
99
+ const correction = verdict.ok ? safety : verdict.correction;
100
+ corrections.push(correction);
101
+ if (attempt === 1)
102
+ break;
103
+ deps.onStep?.({
104
+ kind: 'tool',
105
+ label: verdict.ok ? 'Correcting an unsafe aggregation' : 'Correcting an unobserved identifier',
106
+ detail: verdict.ok ? safety : verdict.unadmitted.join(', '),
107
+ });
108
+ const repair = await runAgenticToolLoopDetailed(input.provider, [...messages, { role: 'assistant', content: raw }, { role: 'user', content: correction }], tools, {
109
+ ...(input.signal ? { signal: input.signal } : {}),
110
+ maxToolCalls: deps.maxIterations,
111
+ ...(deps.maxProviderDispatches !== undefined ? { maxProviderDispatches: deps.maxProviderDispatches } : {}),
112
+ });
113
+ raw = repair.text;
114
+ const repairBudgetTerminal = budgetTerminalForToolLoop(repair.stop);
115
+ if (repairBudgetTerminal) {
116
+ return {
117
+ admitted: ledger.entries().map((e) => e.identifier),
118
+ admittedEntries: ledger.entries(),
119
+ corrections,
120
+ stop: 'budget_exhausted',
121
+ terminal: repairBudgetTerminal,
122
+ };
123
+ }
124
+ const repaired = deps.parseSql(raw);
125
+ if (!repaired)
126
+ break;
127
+ sql = repaired;
128
+ }
129
+ return {
130
+ sql,
131
+ admitted: ledger.entries().map((e) => e.identifier),
132
+ admittedEntries: ledger.entries(),
133
+ corrections,
134
+ stop: 'unverified',
135
+ terminal: 'unverified_identifiers',
136
+ };
137
+ }
138
+ /**
139
+ * Wrap the loop as a lane handler, falling back to the legacy answer loop.
140
+ *
141
+ * The legacy loop still produces the ANSWER — trust labels, citations,
142
+ * narration, execution. This only decides whether the SQL that reaches it was
143
+ * built from observed identifiers. Handing verified SQL to a path that already
144
+ * knows how to govern it is a much smaller change than replacing that path.
145
+ */
146
+ export function createAnalystLaneHandler(deps) {
147
+ return async (input) => {
148
+ const noGeneratedExecution = {
149
+ ...input,
150
+ forcedGeneratedProposal: undefined,
151
+ agenticSqlExecutionCapability: undefined,
152
+ executeGeneratedSql: undefined,
153
+ executeAgenticGeneratedSql: undefined,
154
+ };
155
+ const safeLegacy = async (outcome) => {
156
+ try {
157
+ return await deps.legacy(noGeneratedExecution);
158
+ }
159
+ catch {
160
+ // Never let `answerAgentic` turn a failure in the guarded path into an
161
+ // ambient legacy retry. This explicit no-answer retains the truthful
162
+ // non-executing terminal rather than emitting a second, unproved SQL.
163
+ return analystNonExecutingAnswer(input, outcome);
164
+ }
165
+ };
166
+ const loopDeps = deps.buildDeps(input);
167
+ // A generated agentic route may never fall through to an ambient generated
168
+ // executor when its proof tools are unavailable. Certified/semantic routes
169
+ // remain host-owned, but this lane has no generated SQL authority.
170
+ if (!loopDeps || loopDeps.tools.length === 0) {
171
+ return safeLegacy({ admitted: [], admittedEntries: [], corrections: [], stop: 'no_sql', terminal: 'no_final_sql' });
172
+ }
173
+ let outcome;
174
+ try {
175
+ outcome = await runAnalystLoop(input, loopDeps);
176
+ }
177
+ catch {
178
+ outcome = {
179
+ admitted: [],
180
+ admittedEntries: [],
181
+ corrections: [],
182
+ stop: 'no_sql',
183
+ terminal: 'tool_loop_error',
184
+ };
185
+ }
186
+ if (process.env.DQL_ORCHESTRATOR_TRACE) {
187
+ console.warn(`[dql] analyst loop outcome: stop=${outcome.stop} sql=${outcome.sql ? 'yes' : 'no'} admitted=${outcome.admitted.length} corrections=${outcome.corrections.length}`);
188
+ }
189
+ const scope = input.agenticExecutionScope;
190
+ const composedSql = outcome.stop === 'composed' ? outcome.sql : undefined;
191
+ const capability = composedSql
192
+ ? createAgenticSqlExecutionCapability({
193
+ sql: composedSql,
194
+ // Retrieval catalog rows cannot mint execution authority. Preserve
195
+ // them in the audit ledger, but pass only observed evidence to the
196
+ // server-only capability.
197
+ proven: outcome.admittedEntries
198
+ .filter((entry) => entry.source !== 'catalog')
199
+ .map((entry) => ({ identifier: entry.identifier, evidence: entry.source })),
200
+ runId: scope?.runId,
201
+ executionId: scope?.executionId,
202
+ snapshotId: scope?.snapshotId ?? input.resolvedAnalyticalPlan?.snapshotId,
203
+ planId: scope?.planId ?? input.resolvedAnalyticalPlan?.planId,
204
+ targetFingerprint: scope?.targetFingerprint ?? input.generatedProposalTargetFingerprint,
205
+ bindings: scope?.bindings ?? {},
206
+ })
207
+ : undefined;
208
+ if (outcome.stop === 'composed' && composedSql && !capability) {
209
+ outcome = { ...outcome, stop: 'unverified', terminal: 'missing_execution_binding' };
210
+ }
211
+ const answer = outcome.stop === 'budget_exhausted'
212
+ // A terminal budget is not a recoverable model answer. Do not ask the
213
+ // legacy path to take another generation turn: it would conceal the
214
+ // terminal reason and could turn an explicit bounded stop into an
215
+ // unrelated answer. This remains deliberately non-executing.
216
+ ? analystNonExecutingAnswer(input, outcome)
217
+ : outcome.stop === 'composed' && composedSql && capability
218
+ ? await (async () => {
219
+ try {
220
+ return await deps.legacy({
221
+ ...input,
222
+ // This is a hard handoff, not prompt context. The legacy answer loop
223
+ // keeps its validation/narration/artifact behavior but cannot ask a
224
+ // second model to replace the analyst SQL.
225
+ forcedGeneratedProposal: {
226
+ sql: composedSql,
227
+ summary: 'Prepared from identifiers observed during this bounded analyst run. Review-required until an analyst promotes it.',
228
+ },
229
+ agenticSqlExecutionCapability: capability,
230
+ });
231
+ }
232
+ catch {
233
+ // Do not rethrow into `answerAgentic`: its compatibility fallback
234
+ // executes the original legacy input, which lacks this capability.
235
+ return analystNonExecutingAnswer(input, {
236
+ ...outcome,
237
+ stop: 'unverified',
238
+ terminal: 'tool_loop_error',
239
+ });
240
+ }
241
+ })()
242
+ : await safeLegacy(outcome);
243
+ // ALWAYS record the verdict, not only when a correction was needed. A
244
+ // verification that passed is precisely what an audit wants to see, and a
245
+ // loop that is invisible when it works cannot be told apart from one that
246
+ // never ran — the same silent-degradation trap the fallback marker avoids.
247
+ return withAnalystEvidence(answer, outcome);
248
+ };
249
+ }
250
+ function analystNonExecutingAnswer(input, outcome) {
251
+ const reason = outcome.terminal === 'missing_execution_binding'
252
+ ? 'DQL established the query identifiers but could not bind this run to its frozen plan and execution target, so it did not run generated SQL.'
253
+ : outcome.terminal === 'tool_loop_error'
254
+ ? 'DQL could not complete the bounded evidence check, so it did not run generated SQL.'
255
+ : outcome.terminal === 'tool_budget_exhausted'
256
+ ? 'DQL reached its bounded tool budget before it received final SQL, so no generated warehouse query was run.'
257
+ : outcome.terminal === 'provider_dispatch_budget_exhausted'
258
+ ? 'DQL reached its bounded AI dispatch budget before it received final SQL, so no generated warehouse query was run.'
259
+ : outcome.terminal === 'no_final_sql'
260
+ ? 'DQL did not receive final SQL from the bounded analyst loop, so no generated warehouse query was run.'
261
+ : 'DQL could not verify every generated identifier against a tool observation, so no generated warehouse query was run.';
262
+ return {
263
+ kind: 'no_answer',
264
+ certification: 'analyst_review_required',
265
+ reviewStatus: 'analyst_review_required',
266
+ refusalCode: 'grounding_gap',
267
+ text: reason,
268
+ answer: reason,
269
+ citations: [],
270
+ considered: [],
271
+ ...(input.contextPack ? { contextPack: input.contextPack } : {}),
272
+ };
273
+ }
274
+ function withAnalystEvidence(answer, outcome) {
275
+ // The host initialises `evidence` AFTER the answer call, so at this point it is
276
+ // usually undefined. Bailing out then silently dropped the verdict on every
277
+ // real run — the loop worked and left no trace, which is indistinguishable
278
+ // from it never having run. Create the envelope instead of skipping.
279
+ const evidence = answer.evidence ?? {
280
+ route: [], lineage: [], businessContext: [], selectedAssets: [],
281
+ sourceTables: [], semanticObjects: [], citations: answer.citations ?? [],
282
+ };
283
+ return {
284
+ ...answer,
285
+ evidence: {
286
+ ...evidence,
287
+ route: [
288
+ ...(evidence.route ?? []),
289
+ {
290
+ tool: 'identifier_ledger',
291
+ // 'checked' not 'ok': the ledger VERIFIED the identifiers, it did not
292
+ // select the route or execute anything.
293
+ status: outcome.stop === 'composed' ? 'checked' : 'failed',
294
+ label: outcome.stop === 'composed'
295
+ ? `Verified ${outcome.admitted.length} identifier(s) against tool observations`
296
+ : outcome.terminal === 'no_final_sql'
297
+ ? 'The analyst loop did not produce final SQL; no generated warehouse query was run'
298
+ : outcome.terminal === 'tool_budget_exhausted'
299
+ ? 'The analyst loop reached its bounded tool budget before final SQL; no generated warehouse query was run'
300
+ : outcome.terminal === 'provider_dispatch_budget_exhausted'
301
+ ? 'The analyst loop reached its bounded provider-dispatch budget before final SQL; no generated warehouse query was run'
302
+ : outcome.terminal === 'missing_execution_binding'
303
+ ? 'The analyst proposal had no complete run/plan/target execution binding; no generated warehouse query was run'
304
+ : 'Could not verify every identifier against a tool observation',
305
+ ...(outcome.corrections.length > 0 ? { detail: outcome.corrections.join(' ') } : {}),
306
+ },
307
+ ],
308
+ },
309
+ };
310
+ }
311
+ //# sourceMappingURL=analyst-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyst-loop.js","sourceRoot":"","sources":["../../src/agentic/analyst-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Assumptions — the mechanism that lets a turn ANSWER where it would otherwise
3
+ * ask a question with only one sensible reply.
4
+ *
5
+ * The measured baseline on the jaffle fixture was: false-refusal 0%, but
6
+ * clarification 100%. Nothing dead-ended, yet every answerable question came
7
+ * back as a question. That is a dead end with extra steps whenever the user's
8
+ * only real move is to accept the obvious candidate.
9
+ *
10
+ * The rule is a MARGIN, not a count. One candidate is not the same as no
11
+ * ambiguity: a lone `top_beverage_customers` block is a poor answer to "who are
12
+ * the top customers", and binding it silently would narrow the question without
13
+ * saying so. What makes an assumption safe is that the leading candidate is
14
+ * clearly better than the alternatives — and that we say which one we took and
15
+ * offer the others.
16
+ */
17
+ /** What part of the question was assumed rather than proven. */
18
+ export type AssumptionAbout = 'metric' | 'grain' | 'timeframe' | 'filter' | 'entity' | 'join';
19
+ export interface AnswerAssumption {
20
+ about: AssumptionAbout;
21
+ /** The governed identifier actually used. */
22
+ chose: string;
23
+ /** Human label for `chose`, when it differs from the id. */
24
+ choseLabel?: string;
25
+ /** One line the user can check at a glance. */
26
+ because: string;
27
+ /** Other candidates, offered as one-click corrections. */
28
+ alternatives: Array<{
29
+ id: string;
30
+ label: string;
31
+ }>;
32
+ }
33
+ /**
34
+ * How far ahead the leader must be before binding it without asking.
35
+ *
36
+ * Set from the shape of the failure it prevents rather than tuned for a score:
37
+ * at 1.25 a near-tie between two plausible measures still asks, which is the
38
+ * case where guessing wrong produces a confidently wrong number.
39
+ */
40
+ export declare const ASSUMPTION_DOMINANCE_RATIO = 1.25;
41
+ export interface AssumptionCandidate {
42
+ id: string;
43
+ label: string;
44
+ score: number;
45
+ }
46
+ /**
47
+ * Decide whether the leading candidate is safe to assume.
48
+ *
49
+ * Returns `undefined` when the field is genuinely ambiguous, so the caller
50
+ * clarifies instead — that path stays intact on purpose. "Never dead-end" must
51
+ * not become "always guess": a wrong silent binding is worse than a good
52
+ * question, because the user cannot see that it happened.
53
+ */
54
+ export declare function assumeDominantCandidate(input: {
55
+ about: AssumptionAbout;
56
+ candidates: AssumptionCandidate[];
57
+ because: (chosen: AssumptionCandidate) => string;
58
+ dominanceRatio?: number;
59
+ }): AnswerAssumption | undefined;
60
+ /** One line the user reads above the answer. */
61
+ export declare function renderAssumptionPreamble(assumptions: AnswerAssumption[]): string | undefined;
62
+ //# sourceMappingURL=assumptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assumptions.d.ts","sourceRoot":"","sources":["../../src/agentic/assumptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,gEAAgE;AAChE,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,OAAO,GACP,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,MAAM,CAAC;AAEX,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,eAAe,CAAC;IACvB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,YAAY,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,KAAK,EAAE,eAAe,CAAC;IACvB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,OAAO,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,MAAM,CAAC;IACjD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,gBAAgB,GAAG,SAAS,CAwB/B;AAED,gDAAgD;AAChD,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,MAAM,GAAG,SAAS,CAU5F"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Assumptions — the mechanism that lets a turn ANSWER where it would otherwise
3
+ * ask a question with only one sensible reply.
4
+ *
5
+ * The measured baseline on the jaffle fixture was: false-refusal 0%, but
6
+ * clarification 100%. Nothing dead-ended, yet every answerable question came
7
+ * back as a question. That is a dead end with extra steps whenever the user's
8
+ * only real move is to accept the obvious candidate.
9
+ *
10
+ * The rule is a MARGIN, not a count. One candidate is not the same as no
11
+ * ambiguity: a lone `top_beverage_customers` block is a poor answer to "who are
12
+ * the top customers", and binding it silently would narrow the question without
13
+ * saying so. What makes an assumption safe is that the leading candidate is
14
+ * clearly better than the alternatives — and that we say which one we took and
15
+ * offer the others.
16
+ */
17
+ /**
18
+ * How far ahead the leader must be before binding it without asking.
19
+ *
20
+ * Set from the shape of the failure it prevents rather than tuned for a score:
21
+ * at 1.25 a near-tie between two plausible measures still asks, which is the
22
+ * case where guessing wrong produces a confidently wrong number.
23
+ */
24
+ export const ASSUMPTION_DOMINANCE_RATIO = 1.25;
25
+ /**
26
+ * Decide whether the leading candidate is safe to assume.
27
+ *
28
+ * Returns `undefined` when the field is genuinely ambiguous, so the caller
29
+ * clarifies instead — that path stays intact on purpose. "Never dead-end" must
30
+ * not become "always guess": a wrong silent binding is worse than a good
31
+ * question, because the user cannot see that it happened.
32
+ */
33
+ export function assumeDominantCandidate(input) {
34
+ const ranked = [...input.candidates]
35
+ .filter((candidate) => Number.isFinite(candidate.score))
36
+ .sort((left, right) => right.score - left.score || left.id.localeCompare(right.id));
37
+ const leader = ranked[0];
38
+ if (!leader)
39
+ return undefined;
40
+ const runnerUp = ranked[1];
41
+ const ratio = input.dominanceRatio ?? ASSUMPTION_DOMINANCE_RATIO;
42
+ // A single candidate is dominant by definition — there is nothing to confuse
43
+ // it with. Whether it FITS the question is a separate check the caller owns.
44
+ if (runnerUp) {
45
+ if (runnerUp.score <= 0) {
46
+ // Leader is the only scoring candidate; the rest carry no signal.
47
+ }
48
+ else if (leader.score < runnerUp.score * ratio) {
49
+ return undefined;
50
+ }
51
+ }
52
+ return {
53
+ about: input.about,
54
+ chose: leader.id,
55
+ ...(leader.label && leader.label !== leader.id ? { choseLabel: leader.label } : {}),
56
+ because: input.because(leader),
57
+ alternatives: ranked.slice(1, 4).map((candidate) => ({ id: candidate.id, label: candidate.label })),
58
+ };
59
+ }
60
+ /** One line the user reads above the answer. */
61
+ export function renderAssumptionPreamble(assumptions) {
62
+ if (assumptions.length === 0)
63
+ return undefined;
64
+ const lines = assumptions.map((assumption) => {
65
+ const chosen = assumption.choseLabel ?? assumption.chose;
66
+ const alternatives = assumption.alternatives.length > 0
67
+ ? ` Other options: ${assumption.alternatives.map((alternative) => alternative.label).join(', ')}.`
68
+ : '';
69
+ return `Assumed ${assumption.about}: **${chosen}** — ${assumption.because}${alternatives}`;
70
+ });
71
+ return lines.join('\n');
72
+ }
73
+ //# sourceMappingURL=assumptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assumptions.js","sourceRoot":"","sources":["../../src/agentic/assumptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAuBH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAQ/C;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAKvC;IACC,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;SACjC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;SACvD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,cAAc,IAAI,0BAA0B,CAAC;IACjE,6EAA6E;IAC7E,6EAA6E;IAC7E,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;YACxB,kEAAkE;QACpE,CAAC;aAAM,IAAI,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC;YACjD,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,KAAK,EAAE,MAAM,CAAC,EAAE;QAChB,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAC9B,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;KACpG,CAAC;AACJ,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,wBAAwB,CAAC,WAA+B;IACtE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;QAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,IAAI,UAAU,CAAC,KAAK,CAAC;QACzD,MAAM,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;YACrD,CAAC,CAAC,mBAAmB,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAClG,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,WAAW,UAAU,CAAC,KAAK,OAAO,MAAM,QAAQ,UAAU,CAAC,OAAO,GAAG,YAAY,EAAE,CAAC;IAC7F,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Answering "what does this mean?" from governed metadata, without SQL.
3
+ *
4
+ * A definitional question — "what does the top_customers block measure?", "how
5
+ * is revenue defined here?" — is routed to `converse` today, which replies
6
+ * conversationally with NO governed evidence attached. So the product knows the
7
+ * answer (it has the description, the owner, the grain, the outputs, the
8
+ * certification status) and does not use it, which is the shape of the
9
+ * complaint that Ask AI cannot explain business context.
10
+ *
11
+ * This composes the explanation deterministically from what the catalog already
12
+ * holds. No provider, no warehouse: the facts are all in the object, and a model
13
+ * paraphrasing them can only add drift.
14
+ */
15
+ /** The subset of a catalog object this needs. Structural, so any source can supply it. */
16
+ export interface ExplainableObject {
17
+ objectKey: string;
18
+ objectType: string;
19
+ name: string;
20
+ fullName?: string;
21
+ description?: string;
22
+ domain?: string;
23
+ owner?: string;
24
+ status?: string;
25
+ payload?: Record<string, unknown>;
26
+ }
27
+ export interface BusinessExplanation {
28
+ text: string;
29
+ /** Object keys the explanation drew on, for the provenance footer. */
30
+ citations: string[];
31
+ /**
32
+ * Whether every claim came from a governed artifact. False when the object is
33
+ * a raw dbt/warehouse node rather than something a person certified — the
34
+ * answer is still useful, it is just not an authored definition.
35
+ */
36
+ governed: boolean;
37
+ }
38
+ /**
39
+ * Compose an explanation of one governed object.
40
+ *
41
+ * Returns `null` when there is nothing beyond a bare name to say. A confident
42
+ * paragraph assembled from an empty description is worse than falling through
43
+ * to the normal cascade: it reads like an answer and carries no information.
44
+ */
45
+ export declare function explainObject(object: ExplainableObject): BusinessExplanation | null;
46
+ /**
47
+ * Pick the object a definitional question is about, then explain it.
48
+ *
49
+ * Matching is by NAME MENTION, not relevance rank: "what does top_customers
50
+ * measure?" is asking about a specific artifact, and answering about a merely
51
+ * similar one would be a different question confidently answered. When nothing
52
+ * is named, return null and let the cascade run — guessing the subject of a
53
+ * definition is exactly the failure this is meant to avoid.
54
+ */
55
+ export declare function composeBusinessExplanation(question: string, objects: readonly ExplainableObject[]): BusinessExplanation | null;
56
+ //# sourceMappingURL=business-explanation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"business-explanation.d.ts","sourceRoot":"","sources":["../../src/agentic/business-explanation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,0FAA0F;AAC1F,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAuCD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,IAAI,CAgCnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,iBAAiB,EAAE,GACpC,mBAAmB,GAAG,IAAI,CA2B5B"}