@duckcodeailabs/dql-cli 1.10.2 → 1.10.4

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 (32) hide show
  1. package/dist/assets/dql-notebook/assets/{index-DVimJaeG.js → index-m7ziVzoC.js} +213 -210
  2. package/dist/assets/dql-notebook/index.html +1 -1
  3. package/dist/commands/doctor.d.ts +1 -0
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +15 -0
  6. package/dist/commands/doctor.js.map +1 -1
  7. package/dist/local-runtime.d.ts +8 -0
  8. package/dist/local-runtime.d.ts.map +1 -1
  9. package/dist/local-runtime.js +333 -140
  10. package/dist/local-runtime.js.map +1 -1
  11. package/dist/package.json +10 -10
  12. package/dist/semantic-execution/connection-identity.d.ts +11 -0
  13. package/dist/semantic-execution/connection-identity.d.ts.map +1 -0
  14. package/dist/semantic-execution/connection-identity.js +81 -0
  15. package/dist/semantic-execution/connection-identity.js.map +1 -0
  16. package/dist/semantic-execution/execution-gateway.d.ts +42 -0
  17. package/dist/semantic-execution/execution-gateway.d.ts.map +1 -0
  18. package/dist/semantic-execution/execution-gateway.js +203 -0
  19. package/dist/semantic-execution/execution-gateway.js.map +1 -0
  20. package/dist/semantic-execution/target-binding.d.ts +44 -0
  21. package/dist/semantic-execution/target-binding.d.ts.map +1 -0
  22. package/dist/semantic-execution/target-binding.js +185 -0
  23. package/dist/semantic-execution/target-binding.js.map +1 -0
  24. package/dist/semantic-runtime-settings.d.ts +7 -1
  25. package/dist/semantic-runtime-settings.d.ts.map +1 -1
  26. package/dist/semantic-runtime-settings.js +8 -1
  27. package/dist/semantic-runtime-settings.js.map +1 -1
  28. package/dist/semantic-runtime.d.ts +93 -2
  29. package/dist/semantic-runtime.d.ts.map +1 -1
  30. package/dist/semantic-runtime.js +335 -30
  31. package/dist/semantic-runtime.js.map +1 -1
  32. package/package.json +10 -10
@@ -33,8 +33,12 @@ import { buildSemanticObjectDetail, buildSemanticTree, computeSyncDiff, loadSema
33
33
  import { clearBlockStudioImportSessions, candidateToDqlSource, createBlockStudioImportSession, deleteBlockStudioImportSession, listBlockStudioImportSessions, loadBlockStudioImportSession, readBlockStudioImportCandidate, parameterizeSqlForDqlImport, updateBlockStudioImportCandidate, writeBlockStudioImportSession, writeBlockStudioImportCandidate, } from './block-studio-import.js';
34
34
  import { MetricFlowUnavailableError, hasDbtSemanticManifest, } from "./metricflow.js";
35
35
  import { ManagedMetricFlowInstaller, isMetricFlowWarehouseAdapter, metricFlowAdapterForDriver, } from './metricflow-installer.js';
36
- import { compileSemanticRuntimeQuery, describeRuntimeCompatibility, explainMissingSemanticRuntime, getSemanticRuntimeStatus, semanticRuntimeErrorCode, semanticMetricExecutionCapability as runtimeMetricExecutionCapability, SemanticRuntimeRequiredError, testSemanticRuntimeDraft, } from './semantic-runtime.js';
36
+ import { applySemanticPathSelection, compileSemanticRuntimeQuery, decodeSemanticPathEvidenceId, describeRuntimeCompatibility, explainMissingSemanticRuntime, getSemanticRuntimeStatus, semanticRuntimeErrorDetails, semanticRuntimeErrorCode, semanticMetricExecutionCapability as runtimeMetricExecutionCapability, SemanticRuntimeRequiredError, testSemanticRuntimeDraft, parseSemanticDimensionSelection, } from './semantic-runtime.js';
37
37
  import { getSemanticRuntimeSettings, saveTestedSemanticRuntimeSettings, saveSemanticRuntimePreference, } from './semantic-runtime-settings.js';
38
+ import { observeWarehouseTargetIdentity } from './semantic-execution/connection-identity.js';
39
+ import { configuredWarehouseTargetIdentity } from './semantic-execution/connection-identity.js';
40
+ import { executeTargetBoundSemanticQuery, semanticExecutionFailureCode, semanticExecutionFailureDetails, } from './semantic-execution/execution-gateway.js';
41
+ import { buildSemanticTargetBinding } from './semantic-execution/target-binding.js';
38
42
  import { NotebookDatasetWorkspace, } from "./notebook-datasets.js";
39
43
  import { prepareBlockInvocation } from './block-invocation.js';
40
44
  const NOTEBOOK_EXECUTE_PREVIEW_ROW_LIMIT = 500;
@@ -585,6 +589,10 @@ export async function startLocalServer(opts) {
585
589
  : null;
586
590
  let connection = configuredConnection ?? resolveDbtProfileRuntimeConnection(projectRoot, projectConfig);
587
591
  const projectSnapshots = new ProjectSnapshotService();
592
+ // Server-lifetime registry for exact compiler-owned SQL. The downstream DQL
593
+ // artifact executor uses this to preserve the selected adapter and target
594
+ // rather than treating compiled semantic SQL as unrelated generic SQL.
595
+ const compiledSemanticQueries = new Map();
588
596
  const dashboardRunEvidence = new Map();
589
597
  const dbtNodeDetailCache = new Map();
590
598
  const onboardingJobs = new Map();
@@ -1282,30 +1290,42 @@ export async function startLocalServer(opts) {
1282
1290
  }
1283
1291
  projectSnapshot();
1284
1292
  projectSnapshots.assertCurrent(runProjectSnapshot.snapshotId);
1285
- const compiled = await compileSemanticRuntimeQuery({
1286
- metrics: [binding.metric],
1287
- dimensions: [],
1288
- timeDimension: {
1289
- name: binding.timeDimension,
1290
- granularity: binding.granularity,
1291
- },
1292
- orderBy: [{ name: binding.outputField, direction: 'desc' }],
1293
- limit: 1,
1294
- }, {
1293
+ const plannedAdapter = await resolvePlannedSemanticAdapter(projectRoot, undefined);
1294
+ const execution = await executeTargetBoundSemanticQuery({
1295
+ executor,
1296
+ connection: semanticConnection,
1295
1297
  projectRoot,
1296
- projectConfig,
1297
- detectedProvider: semanticDetectedProvider,
1298
- semanticLayer: semanticLayer,
1299
- driver: semanticDriver,
1300
- tableMapping: semanticTableMapping,
1298
+ plannedAdapter,
1299
+ metricFlow: plannedAdapter === 'metricflow-cli'
1300
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
1301
+ : undefined,
1302
+ compile: () => compileSemanticRuntimeQuery({
1303
+ metrics: [binding.metric],
1304
+ dimensions: [],
1305
+ timeDimension: {
1306
+ name: binding.timeDimension,
1307
+ granularity: binding.granularity,
1308
+ },
1309
+ orderBy: [{ name: binding.outputField, direction: 'desc' }],
1310
+ limit: 1,
1311
+ engine: plannedAdapter,
1312
+ }, {
1313
+ projectRoot,
1314
+ projectConfig,
1315
+ detectedProvider: semanticDetectedProvider,
1316
+ semanticLayer: semanticLayer,
1317
+ driver: semanticDriver,
1318
+ tableMapping: semanticTableMapping,
1319
+ }),
1320
+ prepareSql: (sql) => prepareLocalExecution(sql, semanticConnection, projectRoot, projectConfig),
1321
+ rowBound: 1,
1301
1322
  });
1302
- if (!compiled) {
1323
+ if (!execution) {
1303
1324
  throw Object.assign(new Error('The authorized semantic freshness query could not be compiled.'), {
1304
1325
  code: 'COMPILATION_FAILED',
1305
1326
  });
1306
1327
  }
1307
- const prepared = prepareLocalExecution(compiled.sql, semanticConnection, projectRoot, projectConfig);
1308
- const result = await executor.executeQuery(prepared.sql, [], runtimeVariables({}), prepared.connection);
1328
+ const result = execution.result;
1309
1329
  projectSnapshot();
1310
1330
  projectSnapshots.assertCurrent(runProjectSnapshot.snapshotId);
1311
1331
  return {
@@ -1320,9 +1340,31 @@ export async function startLocalServer(opts) {
1320
1340
  } : {}),
1321
1341
  ...(semanticLayer ? {
1322
1342
  semanticQueryCompiler: async (selection) => {
1323
- const compiled = await compileSemanticRuntimeQuery({
1343
+ const requestedPath = decodeSemanticPathEvidenceId(request.selectedEvidenceId);
1344
+ const semanticRequest = {
1324
1345
  ...selection,
1325
1346
  dimensions: selection.dimensions ?? [],
1347
+ };
1348
+ const requestedSemanticQuery = requestedPath
1349
+ ? applySemanticPathSelection(semanticRequest, requestedPath.authoringReference, requestedPath.entityPath)
1350
+ : semanticRequest;
1351
+ const plannedAdapter = await resolvePlannedSemanticAdapter(projectRoot, requestedSemanticQuery.engine);
1352
+ const executionTarget = semanticConnection
1353
+ ? await observeWarehouseTargetIdentity(executor, semanticConnection)
1354
+ : undefined;
1355
+ const targetBinding = executionTarget
1356
+ ? buildSemanticTargetBinding({
1357
+ projectRoot,
1358
+ adapterId: plannedAdapter,
1359
+ executionTarget,
1360
+ metricFlow: plannedAdapter === 'metricflow-cli'
1361
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
1362
+ : undefined,
1363
+ })
1364
+ : undefined;
1365
+ const compiled = await compileSemanticRuntimeQuery({
1366
+ ...requestedSemanticQuery,
1367
+ engine: plannedAdapter,
1326
1368
  }, {
1327
1369
  projectRoot,
1328
1370
  projectConfig,
@@ -1337,6 +1379,7 @@ export async function startLocalServer(opts) {
1337
1379
  : projectConfig.dbt?.projectDir;
1338
1380
  throw new SemanticRuntimeRequiredError(await explainMissingSemanticRuntime(projectRoot, dbtProjectPath));
1339
1381
  }
1382
+ compiledSemanticQueries.set(executionFingerprint(compiled.sql), compiled);
1340
1383
  return {
1341
1384
  sql: compiled.sql,
1342
1385
  engine: compiled.engine,
@@ -1348,6 +1391,10 @@ export async function startLocalServer(opts) {
1348
1391
  ? { timeDimension: compiled.effectiveRequest.timeDimension }
1349
1392
  : {}),
1350
1393
  },
1394
+ trace: {
1395
+ ...compiled.semanticTrace,
1396
+ ...(targetBinding ? { targetBinding } : {}),
1397
+ },
1351
1398
  };
1352
1399
  },
1353
1400
  } : {}),
@@ -1844,6 +1891,9 @@ export async function startLocalServer(opts) {
1844
1891
  ? 'The governed query could not be executed.'
1845
1892
  : governedAnswer.route?.label ?? (isCertified ? 'Answered from certified DQL context.' : isExploratory ? 'Exploratory DBT-grounded analysis requires review.' : 'Answered with review-required generated analysis.'),
1846
1893
  answer: synthesizedAnswer ?? governedAnswer.answer ?? governedAnswer.text,
1894
+ ...(governedAnswer.clarificationOptions?.length
1895
+ ? { clarificationOptions: governedAnswer.clarificationOptions }
1896
+ : {}),
1847
1897
  status,
1848
1898
  trustState,
1849
1899
  stopReason,
@@ -1853,9 +1903,13 @@ export async function startLocalServer(opts) {
1853
1903
  // A refusal still keeps the DQL draft the answer loop produced (when any),
1854
1904
  // so the "Review DQL draft" next-action isn't a dead link and the user can
1855
1905
  // see the SQL that was about to run. Provider outages carry no draft.
1856
- ? (governedAnswer.dqlArtifact && !isProviderError && !isGroundingGap && !isModelDeclined && !isPolicyBlocked
1857
- ? [agentRunArtifact('dql_block_draft', 'DQL draft (review required)', governedAnswer.dqlArtifact, undefined, 'review_required')]
1858
- : [])
1906
+ ? (governedAnswer.semanticExecutionTrace
1907
+ ? [agentRunArtifact('answer', governedAnswer.refusalCode === 'ambiguous'
1908
+ ? 'Semantic path selection required'
1909
+ : 'Semantic compilation details', governedAnswer, undefined, needsClarification ? 'not_applicable' : 'review_required')]
1910
+ : governedAnswer.dqlArtifact && !isProviderError && !isGroundingGap && !isModelDeclined && !isPolicyBlocked
1911
+ ? [agentRunArtifact('dql_block_draft', 'DQL draft (review required)', governedAnswer.dqlArtifact, undefined, 'review_required')]
1912
+ : [])
1859
1913
  : [agentRunArtifact('answer', isCertified ? 'Certified answer' : isSemantic ? 'Governed semantic answer' : isExploratory ? 'Exploratory DBT-grounded answer' : 'Review-required answer', governedAnswer, governedAnswer.sourceCertifiedBlock ?? governedAnswer.block?.name, isCertified ? 'certified' : isSemantic ? 'governed' : 'review_required')],
1860
1914
  evaluations: [
1861
1915
  agentRunEvaluation('route-decision', 'Route decision', true, 'info', routeDecision?.reason ?? 'Routed request to governed answer.', {
@@ -2766,7 +2820,10 @@ export async function startLocalServer(opts) {
2766
2820
  throw new Error(`I need values for: ${invocation.unresolvedParameters.join(', ')}.`);
2767
2821
  }
2768
2822
  const activeConnection = requireActiveConnection();
2769
- const tableMapping = await resolveSemanticTableMapping(executor, activeConnection, semanticLayer);
2823
+ const precompiledSemanticQuery = Boolean(extractBlockStudioSql(source));
2824
+ const tableMapping = !precompiledSemanticQuery
2825
+ ? await resolveSemanticTableMapping(executor, activeConnection, semanticLayer)
2826
+ : undefined;
2770
2827
  const semanticCompose = semanticLayer
2771
2828
  ? await composeSemanticBlockSqlForRuntime(source, semanticLayer, {
2772
2829
  driver: activeConnection.driver,
@@ -2795,7 +2852,24 @@ export async function startLocalServer(opts) {
2795
2852
  const app = loadRuntimeApp(projectRoot, activePersonaAppId());
2796
2853
  const sourceDomain = metadata.domain ?? source.match(/\bdomain\s*=\s*"([^"]+)"/i)?.[1];
2797
2854
  assertAppAccess({ app, domain: sourceDomain ?? app?.domain, level: 'execute' });
2798
- const rawResult = await executor.executeQuery(executionSql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...invocation.values }), prepared.connection);
2855
+ const pinnedSemanticCompile = semanticCompose?.sql
2856
+ ? compiledSemanticQueries.get(executionFingerprint(semanticCompose.sql))
2857
+ : undefined;
2858
+ const semanticExecution = pinnedSemanticCompile
2859
+ ? await executeTargetBoundSemanticQuery({
2860
+ executor,
2861
+ connection: activeConnection,
2862
+ projectRoot,
2863
+ plannedAdapter: pinnedSemanticCompile.engine,
2864
+ metricFlow: pinnedSemanticCompile.engine === 'metricflow-cli'
2865
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
2866
+ : undefined,
2867
+ compile: async () => pinnedSemanticCompile,
2868
+ prepareSql: () => ({ sql: executionSql, connection: prepared.connection }),
2869
+ rowBound: invocationInput?.rowLimit ?? pinnedSemanticCompile.effectiveRequest.limit,
2870
+ })
2871
+ : null;
2872
+ const rawResult = semanticExecution?.result ?? await executor.executeQuery(executionSql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...invocation.values }), prepared.connection);
2799
2873
  const normalized = normalizeQueryResult(rawResult);
2800
2874
  const executionReceipt = createDqlArtifactExecutionReceipt(source, executionSql, invocation.values, normalized);
2801
2875
  return {
@@ -2810,6 +2884,11 @@ export async function startLocalServer(opts) {
2810
2884
  parameters: invocation.resolvedParameters,
2811
2885
  auditId: invocation.auditId,
2812
2886
  executionReceipt,
2887
+ ...(semanticExecution ? {
2888
+ semanticExecutionReceipt: semanticExecution.executionReceipt,
2889
+ semanticTargetBinding: semanticExecution.targetBinding,
2890
+ semanticTrace: semanticExecution.semanticTrace,
2891
+ } : {}),
2813
2892
  };
2814
2893
  };
2815
2894
  const executeCertifiedBlockByNameForAgent = async (blockName, invocationInput, requireCertified = false) => {
@@ -2998,14 +3077,10 @@ export async function startLocalServer(opts) {
2998
3077
  const scanRuntimeSchema = async () => {
2999
3078
  if (!connection)
3000
3079
  return { ranked: [], snapshot: [] };
3001
- const result = await executor.executeQuery(`SELECT table_schema, table_name, column_name, data_type
3002
- FROM information_schema.columns
3003
- WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
3004
- ORDER BY table_schema, table_name, ordinal_position
3005
- LIMIT 2000`, [], runtimeVariables({}), connection);
3080
+ const result = await executor.executePositional(buildRuntimeSchemaSearchSql(question), [], connection, { maxRows: 600, maxBytes: 2 * 1024 * 1024, batchSize: 200, deadlineMs: 30_000 });
3006
3081
  return {
3007
3082
  ranked: buildAgentSchemaContext(question, result.rows),
3008
- snapshot: buildAgentSchemaContext(question, result.rows, { includeUnscored: true, limit: 500 }),
3083
+ snapshot: buildAgentSchemaContext(question, result.rows, { includeUnscored: true, limit: 100 }),
3009
3084
  };
3010
3085
  };
3011
3086
  const catalogContext = await buildAgentSchemaContextFromCatalog(projectRoot, question, preparedContextPack).catch(() => []);
@@ -3027,7 +3102,7 @@ export async function startLocalServer(opts) {
3027
3102
  const enriched = valueGrounding.mode === 'safe_automatic'
3028
3103
  ? await enrichAgentSchemaContextWithValueMatches(question, schemaContext.ranked, executor, connection, valueGrounding.searchSafeColumns)
3029
3104
  : schemaContext.ranked;
3030
- recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'full information_schema runtime scan');
3105
+ recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'question-scoped information_schema search');
3031
3106
  return enriched;
3032
3107
  }
3033
3108
  catch {
@@ -7431,8 +7506,15 @@ export async function startLocalServer(opts) {
7431
7506
  refreshedAt: dataset.refreshedAt,
7432
7507
  trustState: dataset.trustState,
7433
7508
  }));
7509
+ const schemaLimit = Math.min(500, Math.max(1, Number(url.searchParams.get('limit')) || 200));
7510
+ const schemaOffset = Math.max(0, Number(url.searchParams.get('offset')) || 0);
7511
+ const schemaSearch = (url.searchParams.get('q') ?? '').trim();
7434
7512
  const { tables, columnsByPath } = connection
7435
- ? await introspectSchema(executor, connection)
7513
+ ? await introspectSchema(executor, connection, {
7514
+ limit: schemaLimit,
7515
+ offset: schemaOffset,
7516
+ search: schemaSearch,
7517
+ })
7436
7518
  : { tables: [], columnsByPath: new Map() };
7437
7519
  const dbTables = tables.map((t) => ({
7438
7520
  name: t.path,
@@ -9330,7 +9412,9 @@ export async function startLocalServer(opts) {
9330
9412
  try {
9331
9413
  const body = await readJSON(req);
9332
9414
  const result = await testSemanticRuntimeDraft(projectRoot, body);
9333
- const settings = saveTestedSemanticRuntimeSettings(projectRoot, body, result);
9415
+ const targetConnection = requireActiveConnection(connection);
9416
+ const executionTarget = await observeWarehouseTargetIdentity(executor, targetConnection);
9417
+ const settings = saveTestedSemanticRuntimeSettings(projectRoot, body, result, executionTarget);
9334
9418
  const runtime = await getSemanticRuntimeStatus(projectRoot);
9335
9419
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
9336
9420
  res.end(serializeJSON({ ok: true, ...settings, runtime }));
@@ -10399,25 +10483,41 @@ export async function startLocalServer(opts) {
10399
10483
  const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy, savedQuery, engine } = body;
10400
10484
  // Resolve which connection to use — request can override default
10401
10485
  const targetConnection = requireActiveConnection(isConnectionConfig(body.connection) ? body.connection : connection);
10402
- const driver = targetConnection.driver;
10403
- const tableMapping = await resolveSemanticTableMapping(executor, targetConnection, semanticLayer);
10404
- const composed = await composeRuntimeSemanticQuery({
10405
- metrics,
10406
- dimensions,
10407
- filters,
10408
- limit,
10409
- timeDimension,
10410
- orderBy,
10411
- savedQuery,
10412
- engine,
10413
- }, semanticLayer, {
10486
+ const plannedAdapter = await resolvePlannedSemanticAdapter(projectRoot, engine);
10487
+ const metricFlow = plannedAdapter === 'metricflow-cli'
10488
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
10489
+ : undefined;
10490
+ const execution = await executeTargetBoundSemanticQuery({
10491
+ executor,
10492
+ connection: targetConnection,
10414
10493
  projectRoot,
10415
- projectConfig,
10416
- detectedProvider: semanticDetectedProvider,
10417
- driver,
10418
- tableMapping,
10494
+ plannedAdapter,
10495
+ metricFlow,
10496
+ compile: async () => {
10497
+ const tableMapping = plannedAdapter === 'native'
10498
+ ? await resolveSemanticTableMapping(executor, targetConnection, semanticLayer)
10499
+ : undefined;
10500
+ return composeRuntimeSemanticQuery({
10501
+ metrics,
10502
+ dimensions,
10503
+ filters,
10504
+ limit,
10505
+ timeDimension,
10506
+ orderBy,
10507
+ savedQuery,
10508
+ engine: plannedAdapter,
10509
+ }, semanticLayer, {
10510
+ projectRoot,
10511
+ projectConfig,
10512
+ detectedProvider: semanticDetectedProvider,
10513
+ driver: targetConnection.driver,
10514
+ tableMapping,
10515
+ });
10516
+ },
10517
+ prepareSql: (sql) => prepareLocalExecution(sql, targetConnection, projectRoot, projectConfig),
10518
+ rowBound: limit,
10419
10519
  });
10420
- if (!composed) {
10520
+ if (!execution) {
10421
10521
  const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
10422
10522
  const semanticRuntime = await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true });
10423
10523
  const blocked = metrics.map((metricName) => ({
@@ -10434,17 +10534,20 @@ export async function startLocalServer(opts) {
10434
10534
  }));
10435
10535
  return;
10436
10536
  }
10437
- // Execute the composed SQL against the resolved connection
10438
- const prepared = prepareLocalExecution(composed.sql, targetConnection, projectRoot, projectConfig);
10439
- const result = await executor.executeQuery(prepared.sql, [], {}, prepared.connection);
10440
10537
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
10441
10538
  res.end(serializeJSON({
10442
- sql: composed.sql,
10443
- tables: composed.tables,
10444
- joins: composed.joins,
10445
- engine: composed.engine,
10446
- effectiveRequest: composed.effectiveRequest,
10447
- result: normalizeQueryResult(result),
10539
+ sql: execution.compiled.sql,
10540
+ executedSql: execution.preparedSql,
10541
+ tables: execution.compiled.tables,
10542
+ joins: execution.compiled.joins,
10543
+ engine: execution.compiled.engine,
10544
+ effectiveRequest: execution.compiled.effectiveRequest,
10545
+ runtimeRequest: execution.compiled.runtimeRequest,
10546
+ semanticTrace: execution.semanticTrace,
10547
+ warnings: execution.compiled.warnings,
10548
+ targetBinding: execution.targetBinding,
10549
+ executionReceipt: execution.executionReceipt,
10550
+ result: normalizeQueryResult(execution.result),
10448
10551
  }));
10449
10552
  }
10450
10553
  catch (error) {
@@ -10453,12 +10556,13 @@ export async function startLocalServer(opts) {
10453
10556
  res.end(serializeJSON({ error: error.message, code: 'unauthorized' }));
10454
10557
  return;
10455
10558
  }
10456
- const runtimeCode = semanticRuntimeErrorCode(error);
10457
- const status = runtimeCode ? 400 : 500;
10559
+ const runtimeCode = semanticExecutionFailureCode(error) ?? semanticRuntimeErrorCode(error);
10560
+ const status = runtimeCode === 'EXECUTION_TARGET_MISMATCH' ? 409 : runtimeCode ? 400 : 500;
10458
10561
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
10459
10562
  res.end(serializeJSON({
10460
10563
  error: error instanceof Error ? error.message : String(error),
10461
10564
  code: runtimeCode,
10565
+ details: semanticExecutionFailureDetails(error) ?? semanticRuntimeErrorDetails(error),
10462
10566
  hint: runtimeCode === 'SEMANTIC_RUNTIME_REQUIRED'
10463
10567
  ? 'Configure dbt Cloud Semantic Layer in Project & dbt settings, or install a compatible local MetricFlow runtime.'
10464
10568
  : undefined,
@@ -10476,25 +10580,41 @@ export async function startLocalServer(opts) {
10476
10580
  const body = await readJSON(req);
10477
10581
  const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy, savedQuery, engine } = body;
10478
10582
  const targetConnection = requireActiveConnection(isConnectionConfig(body.connection) ? body.connection : connection);
10479
- const driver = targetConnection.driver;
10480
- const tableMapping = await resolveSemanticTableMapping(executor, targetConnection, semanticLayer);
10481
- const composed = await composeRuntimeSemanticQuery({
10482
- metrics,
10483
- dimensions,
10484
- filters,
10485
- limit,
10486
- timeDimension,
10487
- orderBy,
10488
- savedQuery,
10489
- engine,
10490
- }, semanticLayer, {
10583
+ const plannedAdapter = await resolvePlannedSemanticAdapter(projectRoot, engine);
10584
+ const metricFlow = plannedAdapter === 'metricflow-cli'
10585
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
10586
+ : undefined;
10587
+ const execution = await executeTargetBoundSemanticQuery({
10588
+ executor,
10589
+ connection: targetConnection,
10491
10590
  projectRoot,
10492
- projectConfig,
10493
- detectedProvider: semanticDetectedProvider,
10494
- driver,
10495
- tableMapping,
10591
+ plannedAdapter,
10592
+ metricFlow,
10593
+ compile: async () => {
10594
+ const tableMapping = plannedAdapter === 'native'
10595
+ ? await resolveSemanticTableMapping(executor, targetConnection, semanticLayer)
10596
+ : undefined;
10597
+ return composeRuntimeSemanticQuery({
10598
+ metrics,
10599
+ dimensions,
10600
+ filters,
10601
+ limit,
10602
+ timeDimension,
10603
+ orderBy,
10604
+ savedQuery,
10605
+ engine: plannedAdapter,
10606
+ }, semanticLayer, {
10607
+ projectRoot,
10608
+ projectConfig,
10609
+ detectedProvider: semanticDetectedProvider,
10610
+ driver: targetConnection.driver,
10611
+ tableMapping,
10612
+ });
10613
+ },
10614
+ prepareSql: (sql) => prepareLocalExecution(sql, targetConnection, projectRoot, projectConfig),
10615
+ rowBound: limit,
10496
10616
  });
10497
- if (!composed) {
10617
+ if (!execution) {
10498
10618
  const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
10499
10619
  const semanticRuntime = await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true });
10500
10620
  const blocked = metrics.map((metricName) => ({
@@ -10511,25 +10631,30 @@ export async function startLocalServer(opts) {
10511
10631
  }));
10512
10632
  return;
10513
10633
  }
10514
- const prepared = prepareLocalExecution(composed.sql, targetConnection, projectRoot, projectConfig);
10515
- const result = await executor.executeQuery(prepared.sql, [], {}, prepared.connection);
10516
10634
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
10517
10635
  res.end(serializeJSON({
10518
- sql: composed.sql,
10519
- joins: composed.joins,
10520
- tables: composed.tables,
10521
- engine: composed.engine,
10522
- effectiveRequest: composed.effectiveRequest,
10523
- result: normalizeQueryResult(result),
10636
+ sql: execution.compiled.sql,
10637
+ executedSql: execution.preparedSql,
10638
+ joins: execution.compiled.joins,
10639
+ tables: execution.compiled.tables,
10640
+ engine: execution.compiled.engine,
10641
+ effectiveRequest: execution.compiled.effectiveRequest,
10642
+ runtimeRequest: execution.compiled.runtimeRequest,
10643
+ semanticTrace: execution.semanticTrace,
10644
+ warnings: execution.compiled.warnings,
10645
+ targetBinding: execution.targetBinding,
10646
+ executionReceipt: execution.executionReceipt,
10647
+ result: normalizeQueryResult(execution.result),
10524
10648
  }));
10525
10649
  }
10526
10650
  catch (error) {
10527
- const runtimeCode = semanticRuntimeErrorCode(error);
10528
- const status = runtimeCode ? 400 : 500;
10651
+ const runtimeCode = semanticExecutionFailureCode(error) ?? semanticRuntimeErrorCode(error);
10652
+ const status = runtimeCode === 'EXECUTION_TARGET_MISMATCH' ? 409 : runtimeCode ? 400 : 500;
10529
10653
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
10530
10654
  res.end(serializeJSON({
10531
10655
  error: error instanceof Error ? error.message : String(error),
10532
10656
  code: runtimeCode,
10657
+ details: semanticExecutionFailureDetails(error) ?? semanticRuntimeErrorDetails(error),
10533
10658
  hint: runtimeCode === 'SEMANTIC_RUNTIME_REQUIRED'
10534
10659
  ? 'Configure dbt Cloud Semantic Layer in Project & dbt settings, or install a compatible local MetricFlow runtime.'
10535
10660
  : undefined,
@@ -10605,6 +10730,7 @@ export async function startLocalServer(opts) {
10605
10730
  res.end(serializeJSON({
10606
10731
  error: error instanceof Error ? error.message : String(error),
10607
10732
  code: runtimeCode,
10733
+ details: semanticRuntimeErrorDetails(error),
10608
10734
  hint: runtimeCode === 'SEMANTIC_RUNTIME_REQUIRED'
10609
10735
  ? 'Configure dbt Cloud Semantic Layer in Project & dbt settings, or install a compatible local MetricFlow runtime.'
10610
10736
  : undefined,
@@ -11087,7 +11213,24 @@ export async function startLocalServer(opts) {
11087
11213
  const prepared = prepareLocalExecution(executableSql, cellConnection, projectRoot, projectConfig);
11088
11214
  const app = loadRuntimeApp(projectRoot, typeof body.appId === 'string' ? body.appId : activePersonaAppId());
11089
11215
  assertAppAccess({ app, domain: resolved.domain ?? app?.domain, level: 'execute' });
11090
- const rawResult = await executor.executeQuery(prepared.sql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...(invocation?.values ?? {}) }), prepared.connection);
11216
+ const semanticExecution = semanticCompose?.compiled
11217
+ ? await executeTargetBoundSemanticQuery({
11218
+ executor,
11219
+ connection: cellConnection,
11220
+ projectRoot,
11221
+ plannedAdapter: semanticCompose.compiled.engine,
11222
+ metricFlow: semanticCompose.compiled.engine === 'metricflow-cli'
11223
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
11224
+ : undefined,
11225
+ // Compilation already produced the exact pinned adapter result
11226
+ // used by the plan. The gateway validates target identity before
11227
+ // accepting that result for preflight and execution.
11228
+ compile: async () => semanticCompose.compiled,
11229
+ prepareSql: () => ({ sql: prepared.sql, connection: prepared.connection }),
11230
+ rowBound: semanticCompose.compiled.effectiveRequest.limit,
11231
+ })
11232
+ : null;
11233
+ const rawResult = semanticExecution?.result ?? await executor.executeQuery(prepared.sql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...(invocation?.values ?? {}) }), prepared.connection);
11091
11234
  const normalized = normalizeQueryResult(rawResult);
11092
11235
  // Enforce the block's declared invariants against the result set. This
11093
11236
  // is additive: blocks without invariants produce `null` and the
@@ -11125,6 +11268,12 @@ export async function startLocalServer(opts) {
11125
11268
  chartConfig: plan?.chartConfig,
11126
11269
  tests: plan?.tests,
11127
11270
  result: normalized,
11271
+ ...(semanticExecution ? {
11272
+ semanticTrace: semanticExecution.semanticTrace,
11273
+ targetBinding: semanticExecution.targetBinding,
11274
+ executionReceipt: semanticExecution.executionReceipt,
11275
+ executedSql: semanticExecution.preparedSql,
11276
+ } : {}),
11128
11277
  ...(invocation ? {
11129
11278
  invocation: {
11130
11279
  resolvedParameters: invocation.resolvedParameters,
@@ -11157,8 +11306,13 @@ export async function startLocalServer(opts) {
11157
11306
  error: error instanceof Error ? error.message : String(error),
11158
11307
  });
11159
11308
  }
11160
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
11161
- res.end(serializeJSON({ error: error instanceof Error ? error.message : String(error) }));
11309
+ const code = semanticExecutionFailureCode(error) ?? semanticRuntimeErrorCode(error);
11310
+ res.writeHead(code === 'EXECUTION_TARGET_MISMATCH' ? 409 : 500, { 'Content-Type': 'application/json; charset=utf-8' });
11311
+ res.end(serializeJSON({
11312
+ error: error instanceof Error ? error.message : String(error),
11313
+ code,
11314
+ details: semanticExecutionFailureDetails(error) ?? semanticRuntimeErrorDetails(error),
11315
+ }));
11162
11316
  }
11163
11317
  return;
11164
11318
  }
@@ -14410,15 +14564,25 @@ function readUserPrefs(userPrefsPath) {
14410
14564
  function writeUserPrefs(userPrefsPath, prefs) {
14411
14565
  writeFileSync(userPrefsPath, JSON.stringify(prefs, null, 2) + '\n', 'utf-8');
14412
14566
  }
14413
- async function introspectSchema(executor, connection) {
14567
+ async function introspectSchema(executor, connection, options = {}) {
14414
14568
  let tables = [];
14415
- let columnsByPath = new Map();
14416
- // Tier 1: information_schema (PG, MySQL, Snowflake, MSSQL, DuckDB, Redshift, Fabric, Databricks)
14569
+ const columnsByPath = new Map();
14570
+ const limit = Math.min(1_000, Math.max(1, Math.floor(options.limit ?? 200)));
14571
+ const offset = Math.max(0, Math.floor(options.offset ?? 0));
14572
+ const escapedSearch = options.search?.trim().replace(/'/g, "''");
14573
+ const searchPredicate = escapedSearch
14574
+ ? ` AND (LOWER(table_schema) LIKE LOWER('%${escapedSearch}%') OR LOWER(table_name) LIKE LOWER('%${escapedSearch}%'))`
14575
+ : '';
14576
+ // Runtime inventory is table-first and bounded. Columns are loaded only for
14577
+ // a selected table through /api/describe-table; constructing one account-wide
14578
+ // table+column tree is an enterprise-scale OOM risk (PERF-001 / CTX-005).
14417
14579
  try {
14418
14580
  const catalogRows = await executor.executeQuery(`SELECT table_schema, table_name, table_type
14419
14581
  FROM information_schema.tables
14420
14582
  WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
14421
- ORDER BY table_schema, table_name`, [], {}, connection);
14583
+ ${searchPredicate}
14584
+ ORDER BY table_schema, table_name
14585
+ LIMIT ${limit} OFFSET ${offset}`, [], {}, connection);
14422
14586
  tables = catalogRows.rows.map((row) => {
14423
14587
  const schema = String(row['table_schema'] ?? row['TABLE_SCHEMA'] ?? 'default');
14424
14588
  const name = String(row['table_name'] ?? row['TABLE_NAME'] ?? '');
@@ -14426,22 +14590,6 @@ async function introspectSchema(executor, connection) {
14426
14590
  const path = schema ? `${schema}.${name}` : name;
14427
14591
  return { schema, name, path, type };
14428
14592
  });
14429
- const columnRows = await executor.executeQuery(`SELECT table_schema, table_name, column_name, data_type
14430
- FROM information_schema.columns
14431
- WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
14432
- ORDER BY table_schema, table_name, ordinal_position`, [], {}, connection);
14433
- columnsByPath = columnRows.rows.reduce((map, row) => {
14434
- const schema = String(row['table_schema'] ?? row['TABLE_SCHEMA'] ?? 'default');
14435
- const tableName = String(row['table_name'] ?? row['TABLE_NAME'] ?? '');
14436
- const path = schema ? `${schema}.${tableName}` : tableName;
14437
- const next = map.get(path) ?? [];
14438
- next.push({
14439
- name: String(row['column_name'] ?? row['COLUMN_NAME'] ?? ''),
14440
- type: String(row['data_type'] ?? row['DATA_TYPE'] ?? ''),
14441
- });
14442
- map.set(path, next);
14443
- return map;
14444
- }, new Map());
14445
14593
  return { tables, columnsByPath };
14446
14594
  }
14447
14595
  catch {
@@ -14452,23 +14600,18 @@ async function introspectSchema(executor, connection) {
14452
14600
  const connector = await executor.getConnector(connection);
14453
14601
  if (typeof connector.listTables === 'function') {
14454
14602
  const rawTables = await connector.listTables();
14455
- tables = rawTables.map((t) => {
14603
+ const normalizedSearch = options.search?.trim().toLowerCase();
14604
+ tables = rawTables
14605
+ .filter((table) => !normalizedSearch
14606
+ || table.schema.toLowerCase().includes(normalizedSearch)
14607
+ || table.name.toLowerCase().includes(normalizedSearch))
14608
+ .slice(offset, offset + limit)
14609
+ .map((t) => {
14456
14610
  const schema = t.schema || 'default';
14457
14611
  const path = t.schema ? `${t.schema}.${t.name}` : t.name;
14458
14612
  return { schema, name: t.name, path, type: t.type };
14459
14613
  });
14460
14614
  }
14461
- if (typeof connector.listColumns === 'function') {
14462
- const rawColumns = await connector.listColumns();
14463
- columnsByPath = rawColumns.reduce((map, col) => {
14464
- const schema = col.schema || 'default';
14465
- const path = schema ? `${schema}.${col.table}` : col.table;
14466
- const next = map.get(path) ?? [];
14467
- next.push({ name: col.name, type: col.dataType });
14468
- map.set(path, next);
14469
- return map;
14470
- }, new Map());
14471
- }
14472
14615
  }
14473
14616
  catch {
14474
14617
  // Tier 3: tables only, no columns — already have what we have
@@ -14478,9 +14621,9 @@ async function introspectSchema(executor, connection) {
14478
14621
  function buildDatabaseSchemaTree(projectRoot, executor, connection) {
14479
14622
  return (async () => {
14480
14623
  const dataFiles = scanDataFiles(projectRoot);
14481
- const { tables: dbTables, columnsByPath: dbColumnsByPath } = connection
14482
- ? await introspectSchema(executor, connection)
14483
- : { tables: [], columnsByPath: new Map() };
14624
+ const dbTables = connection
14625
+ ? (await introspectSchema(executor, connection, { limit: 1_000 })).tables
14626
+ : [];
14484
14627
  const schemaMap = new Map();
14485
14628
  for (const table of dbTables) {
14486
14629
  const schemaName = table.schema || 'default';
@@ -14502,13 +14645,8 @@ function buildDatabaseSchemaTree(projectRoot, executor, connection) {
14502
14645
  kind: 'table',
14503
14646
  path: table.path,
14504
14647
  type: table.type,
14505
- children: (dbColumnsByPath.get(table.path) ?? []).map((column) => ({
14506
- id: `db-column:${table.path}:${column.name}`,
14507
- label: column.name,
14508
- kind: 'column',
14509
- path: table.path,
14510
- type: column.type,
14511
- })),
14648
+ // Warehouse columns are loaded lazily for the selected table.
14649
+ children: [],
14512
14650
  })),
14513
14651
  }));
14514
14652
  // Eagerly resolve file columns via DuckDB DESCRIBE
@@ -14677,6 +14815,28 @@ function isDbtSemanticRuntime(projectConfig, detectedProvider, semanticLayer) {
14677
14815
  return true;
14678
14816
  return Boolean(semanticLayer?.listMetrics().some((metric) => metric.source?.provider === 'dbt'));
14679
14817
  }
14818
+ async function resolvePlannedSemanticAdapter(projectRoot, requested) {
14819
+ if (requested === 'dbt-cloud')
14820
+ return 'dbt-cloud';
14821
+ if (requested === 'metricflow' || requested === 'metricflow-cli')
14822
+ return 'metricflow-cli';
14823
+ if (requested === 'native')
14824
+ return 'native';
14825
+ return (await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true })).active;
14826
+ }
14827
+ function resolveMetricFlowTargetMetadata(projectRoot, projectConfig) {
14828
+ const candidates = discoverDbtProfileConnections(projectRoot, projectConfig)
14829
+ .filter((candidate) => candidate.missingFields.length === 0);
14830
+ const candidate = candidates.find((item) => !item.warnings.some((warning) => warning.startsWith('Not the default dbt target'))) ?? (candidates.length === 1 ? candidates[0] : undefined);
14831
+ if (!candidate)
14832
+ return undefined;
14833
+ return {
14834
+ expectedTarget: configuredWarehouseTargetIdentity(candidate.connection),
14835
+ profileName: candidate.profileName,
14836
+ targetName: candidate.targetName,
14837
+ profilesPath: candidate.path,
14838
+ };
14839
+ }
14680
14840
  async function composeRuntimeSemanticQuery(request, semanticLayer, context) {
14681
14841
  return compileSemanticRuntimeQuery(request, {
14682
14842
  projectRoot: context.projectRoot,
@@ -14688,13 +14848,18 @@ async function composeRuntimeSemanticQuery(request, semanticLayer, context) {
14688
14848
  });
14689
14849
  }
14690
14850
  function composeRuntimeSemanticQueryNative(request, semanticLayer, context) {
14851
+ const stripPath = (value) => parseSemanticDimensionSelection(value).reference;
14691
14852
  const composed = semanticLayer.composeQuery({
14692
14853
  metrics: request.metrics,
14693
- dimensions: request.dimensions,
14694
- filters: request.filters,
14854
+ dimensions: request.dimensions.map(stripPath),
14855
+ filters: request.filters?.map((filter) => filter.dimension
14856
+ ? { ...filter, dimension: stripPath(filter.dimension) }
14857
+ : filter),
14695
14858
  limit: request.limit,
14696
- timeDimension: request.timeDimension,
14697
- orderBy: request.orderBy,
14859
+ timeDimension: request.timeDimension
14860
+ ? { ...request.timeDimension, name: stripPath(request.timeDimension.name) }
14861
+ : undefined,
14862
+ orderBy: request.orderBy?.map((order) => ({ ...order, name: stripPath(order.name) })),
14698
14863
  driver: context.driver,
14699
14864
  tableMapping: context.tableMapping,
14700
14865
  });
@@ -14737,8 +14902,8 @@ function composeSemanticBlockSql(source, semanticLayer, options) {
14737
14902
  }
14738
14903
  const refValidation = semanticLayer.validateReferences([
14739
14904
  ...metrics,
14740
- ...config.dimensions,
14741
- ...(config.timeDimension ? [config.timeDimension] : []),
14905
+ ...config.dimensions.map((dimension) => parseSemanticDimensionSelection(dimension).reference),
14906
+ ...(config.timeDimension ? [parseSemanticDimensionSelection(config.timeDimension).reference] : []),
14742
14907
  ]);
14743
14908
  for (const unknown of refValidation.unknown) {
14744
14909
  diagnostics.push({
@@ -14872,6 +15037,7 @@ async function composeSemanticBlockSqlForRuntime(source, semanticLayer, options)
14872
15037
  // no longer true for this run.
14873
15038
  diagnostics: native.diagnostics.filter((diagnostic) => diagnostic.code !== 'semantic_compose_failed'),
14874
15039
  semanticRefs: native.semanticRefs,
15040
+ compiled,
14875
15041
  };
14876
15042
  }
14877
15043
  catch (error) {
@@ -20547,6 +20713,33 @@ export function buildAgentSchemaContext(question, rows, options = {}) {
20547
20713
  .slice(0, limit)
20548
20714
  .map((entry) => entry.table);
20549
20715
  }
20716
+ const RUNTIME_SCHEMA_SEARCH_STOPWORDS = new Set([
20717
+ 'what', 'which', 'where', 'when', 'who', 'why', 'how', 'show', 'give', 'find',
20718
+ 'from', 'with', 'without', 'this', 'that', 'these', 'those', 'current', 'today',
20719
+ 'yesterday', 'last', 'year', 'month', 'week', 'day', 'top', 'bottom', 'their',
20720
+ 'have', 'does', 'into', 'than', 'then', 'also', 'all', 'the', 'and', 'for',
20721
+ ]);
20722
+ /**
20723
+ * Physical catalog evidence fallback. This is deliberately question-scoped and
20724
+ * result-bounded: dbt/DQL metadata remains the planning authority, while
20725
+ * information_schema confirms discoverable relations/columns in the selected
20726
+ * execution target. Tokens are reduced to safe identifier characters before
20727
+ * interpolation, so user text cannot become SQL syntax.
20728
+ */
20729
+ export function buildRuntimeSchemaSearchSql(question) {
20730
+ const terms = Array.from(agentSchemaTokens(question))
20731
+ .map((term) => term.toLowerCase().replace(/[^a-z0-9_]/g, ''))
20732
+ .filter((term) => term.length >= 3 && !RUNTIME_SCHEMA_SEARCH_STOPWORDS.has(term))
20733
+ .slice(0, 8);
20734
+ const predicate = terms.length > 0
20735
+ ? `\n AND (\n${terms.map((term) => ` LOWER(table_name) LIKE '%${term}%' OR LOWER(column_name) LIKE '%${term}%'`).join(' OR\n')}\n )`
20736
+ : '';
20737
+ return `SELECT table_schema, table_name, column_name, data_type
20738
+ FROM information_schema.columns
20739
+ WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')${predicate}
20740
+ ORDER BY table_schema, table_name, ordinal_position
20741
+ LIMIT 600`;
20742
+ }
20550
20743
  function mergeAgentSchemaContexts(...contexts) {
20551
20744
  const byRelation = new Map();
20552
20745
  for (const table of contexts.flat()) {