@duckcodeailabs/dql-cli 1.12.2 → 1.12.3

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 (70) hide show
  1. package/dist/assets/dql-notebook/assets/AgentAnswerCard-BEIwKGm4.js +5 -0
  2. package/dist/assets/dql-notebook/assets/AgentLogPage-CsSC2tOR.js +1 -0
  3. package/dist/assets/dql-notebook/assets/AiBuildDialog-CjJ-9bQW.js +1 -0
  4. package/dist/assets/dql-notebook/assets/AiBuildResult-BvTcW9oE.js +33 -0
  5. package/dist/assets/dql-notebook/assets/AiSidePanel-CxFrOfCE.js +6 -0
  6. package/dist/assets/dql-notebook/assets/AnalyticsHome-BtXEdJNR.js +6 -0
  7. package/dist/assets/dql-notebook/assets/AppsView-HjQkbbE6.js +4471 -0
  8. package/dist/assets/dql-notebook/assets/BlockStudio-oQHTJsXC.js +60 -0
  9. package/dist/assets/dql-notebook/assets/BusinessArtifactView-Dpsftjs7.js +1 -0
  10. package/dist/assets/dql-notebook/assets/DbtFirstModelingPage-c6ZteaI6.js +22 -0
  11. package/dist/assets/dql-notebook/assets/GitPage-Cj6mc-Yv.js +7 -0
  12. package/dist/assets/dql-notebook/assets/GlobalAiRail-8G_rEyhK.js +1 -0
  13. package/dist/assets/dql-notebook/assets/GovernedContextPage-BTtzt0Za.js +7 -0
  14. package/dist/assets/dql-notebook/assets/HelpDocsPage-e2CesE73.js +22 -0
  15. package/dist/assets/dql-notebook/assets/HomePage-U3ryMLEF.js +117 -0
  16. package/dist/assets/dql-notebook/assets/LineageDAG-CEmgJzm3.js +1 -0
  17. package/dist/assets/dql-notebook/assets/LineageDetailView-CeV5QMXS.js +1 -0
  18. package/dist/assets/dql-notebook/assets/LineageDrawer-B1oAn-N_.js +1 -0
  19. package/dist/assets/dql-notebook/assets/LineagePathBreadcrumb-fP6RTXut.js +14 -0
  20. package/dist/assets/dql-notebook/assets/MiniLineageGraph-CBYSTmRC.js +1 -0
  21. package/dist/assets/dql-notebook/assets/NewBlockModal-ksEwimjl.js +1 -0
  22. package/dist/assets/dql-notebook/assets/NewNotebookModal-CUx0WmXb.js +46 -0
  23. package/dist/assets/dql-notebook/assets/NotebookEditor-BdxJL3LX.js +202 -0
  24. package/dist/assets/dql-notebook/assets/ReadinessPage-DZ9wDW76.js +1 -0
  25. package/dist/assets/dql-notebook/assets/SetupOnboarding-Bt0AAe0h.js +6 -0
  26. package/dist/assets/dql-notebook/assets/TrustBadge-DNVJK_mP.js +1 -0
  27. package/dist/assets/dql-notebook/assets/UnifiedAgentRunPanel-BNqE169i.js +83 -0
  28. package/dist/assets/dql-notebook/assets/ai-build-bus-1BR770pY.js +1 -0
  29. package/dist/assets/dql-notebook/assets/answer-to-notebook-B_gE6rsY.js +2 -0
  30. package/dist/assets/dql-notebook/assets/arrow-left-DnKGhzOR.js +6 -0
  31. package/dist/assets/dql-notebook/assets/arrow-right-CQ73DG7_.js +6 -0
  32. package/dist/assets/dql-notebook/assets/book-open-text-CyBV5Nhw.js +6 -0
  33. package/dist/assets/dql-notebook/assets/bot-UOPtBzCj.js +6 -0
  34. package/dist/assets/dql-notebook/assets/chevron-up-CDHDU3N3.js +6 -0
  35. package/dist/assets/dql-notebook/assets/circle-x-DC5nkL_u.js +6 -0
  36. package/dist/assets/dql-notebook/assets/{codemirror-Co0dCEDr.js → codemirror-1_I9gm6Z.js} +1 -1
  37. package/dist/assets/dql-notebook/assets/dagre-BZV40eAE.css +1 -0
  38. package/dist/assets/dql-notebook/assets/dagre.esm-C7ujXipB.js +23 -0
  39. package/dist/assets/dql-notebook/assets/external-link-BiJ_dtTM.js +6 -0
  40. package/dist/assets/dql-notebook/assets/grip-vertical-eXvkUerq.js +6 -0
  41. package/dist/assets/dql-notebook/assets/index-BMVfPaXr.js +644 -0
  42. package/dist/assets/dql-notebook/assets/{index-DvLT6Kls.css → index-DKu4CZt3.css} +1 -1
  43. package/dist/assets/dql-notebook/assets/link-2-DkHVJTuH.js +6 -0
  44. package/dist/assets/dql-notebook/assets/list-tree-8lYW2Itb.js +6 -0
  45. package/dist/assets/dql-notebook/assets/maximize-2-DjSovOdP.js +6 -0
  46. package/dist/assets/dql-notebook/assets/minimize-2-DGlmQLES.js +6 -0
  47. package/dist/assets/dql-notebook/assets/panel-right-open-DPc4gShM.js +6 -0
  48. package/dist/assets/dql-notebook/assets/pencil-nwaGtOdL.js +6 -0
  49. package/dist/assets/dql-notebook/assets/{react-BRY8hXab.js → react-CRB3T2We.js} +2 -2
  50. package/dist/assets/dql-notebook/assets/rotate-ccw-ByjLhAu7.js +6 -0
  51. package/dist/assets/dql-notebook/assets/semantic-fields-eEhwnQhG.js +3 -0
  52. package/dist/assets/dql-notebook/assets/triangle-alert-zws0cKhp.js +6 -0
  53. package/dist/assets/dql-notebook/assets/usePersistedAgentThreadId-hxi1RZDm.js +18 -0
  54. package/dist/assets/dql-notebook/assets/user-round-CM8xNakq.js +11 -0
  55. package/dist/assets/dql-notebook/assets/wand-sparkles-CIKc8r-4.js +6 -0
  56. package/dist/assets/dql-notebook/assets/workflow-6C0ZLbTi.js +6 -0
  57. package/dist/assets/dql-notebook/assets/wrench-CeU7hF12.js +6 -0
  58. package/dist/assets/dql-notebook/assets/x-DLWEzvri.js +6 -0
  59. package/dist/assets/dql-notebook/index.html +3 -4
  60. package/dist/local-runtime.d.ts +10 -2
  61. package/dist/local-runtime.d.ts.map +1 -1
  62. package/dist/local-runtime.js +130 -39
  63. package/dist/local-runtime.js.map +1 -1
  64. package/dist/package.json +10 -10
  65. package/dist/warehouse-metadata.d.ts +1 -1
  66. package/dist/warehouse-metadata.d.ts.map +1 -1
  67. package/dist/warehouse-metadata.js +2 -2
  68. package/dist/warehouse-metadata.js.map +1 -1
  69. package/package.json +10 -10
  70. package/dist/assets/dql-notebook/assets/index-BRCtmVPa.js +0 -5952
@@ -968,7 +968,10 @@ export async function startLocalServer(opts) {
968
968
  return result;
969
969
  };
970
970
  await reloadSemanticLayer();
971
- await refreshLocalMetadataCatalog(projectRoot);
971
+ // Normal startup reuses the fingerprinted local catalog when its inputs are
972
+ // unchanged. Explicit refresh, save, and certification paths still request
973
+ // the default forced rebuild below.
974
+ await refreshLocalMetadataCatalog(projectRoot, undefined, undefined, false);
972
975
  const startupDbtManifestPath = resolveDbtManifestPath(projectRoot, projectConfig);
973
976
  if (startupDbtManifestPath && !isAgentProjectIndexReady(projectRoot)) {
974
977
  try {
@@ -1338,8 +1341,14 @@ export async function startLocalServer(opts) {
1338
1341
  throw error;
1339
1342
  }
1340
1343
  const semanticDriver = semanticConnection?.driver;
1344
+ const defaultExecutionTarget = executionTargetDescriptor({});
1345
+ const semanticConnectionName = request.executionTarget?.target === 'connection'
1346
+ ? request.executionTarget.connectionName
1347
+ : defaultExecutionTarget.target === 'connection'
1348
+ ? defaultExecutionTarget.connectionName
1349
+ : undefined;
1341
1350
  const semanticTableMapping = semanticLayer && semanticConnection
1342
- ? await resolveSemanticTableMapping(executor, semanticConnection, semanticLayer, projectRoot)
1351
+ ? await resolveSemanticTableMapping(executor, semanticConnection, semanticLayer, projectRoot, semanticConnectionName)
1343
1352
  : undefined;
1344
1353
  const requestedDomain = normalizeAgentRunDomain(agentRunWorkspaceValue(request, 'domain'));
1345
1354
  const requestedPurpose = agentRunWorkspaceValue(request, 'purpose');
@@ -1527,9 +1536,9 @@ export async function startLocalServer(opts) {
1527
1536
  ? { resolvedAnalyticalPlan: routeDecision.resolvedAnalyticalPlan }
1528
1537
  : {}),
1529
1538
  analyticalReferenceInstant: new Date().toISOString(),
1530
- executeCertifiedBlock: (node, invocation) => executeCertifiedBlockForAgent(node, invocation, semanticConnection),
1531
- executeGeneratedSql: (sql, artifact) => executeGeneratedArtifactForAgent(request.question, sql, artifact, semanticConnection),
1532
- executeDqlArtifact: (artifact) => executeArtifactReferenceForAgent(artifact, request.question, semanticConnection),
1539
+ executeCertifiedBlock: (node, invocation) => executeCertifiedBlockForAgent(node, invocation, semanticConnection, semanticConnectionName),
1540
+ executeGeneratedSql: (sql, artifact) => executeGeneratedArtifactForAgent(request.question, sql, artifact, semanticConnection, semanticConnectionName),
1541
+ executeDqlArtifact: (artifact) => executeArtifactReferenceForAgent(artifact, request.question, semanticConnection, semanticConnectionName),
1533
1542
  getSchemaContext: (question, preparedContextPack) => getSchemaContextForAgent(question, preparedContextPack, semanticConnection, request.executionTarget?.target === 'connection'
1534
1543
  ? request.executionTarget.connectionName
1535
1544
  : undefined),
@@ -2713,6 +2722,18 @@ export async function startLocalServer(opts) {
2713
2722
  const preparedMetadataFingerprint = isAgentProjectIndexReady(projectRoot)
2714
2723
  ? currentMetadataFingerprint(projectRoot)
2715
2724
  : undefined;
2725
+ const targetDescriptor = executionTargetDescriptor(request);
2726
+ const targetConnectionName = targetDescriptor.target === 'connection'
2727
+ ? targetDescriptor.connectionName
2728
+ : undefined;
2729
+ let runtimeSchemaSnapshot;
2730
+ if (targetConnectionName) {
2731
+ const targetConnection = resolveNamedConnection(targetConnectionName);
2732
+ const activeScope = targetConnection
2733
+ ? optionalActiveConnectionMetadataScope(projectRoot, projectConfig, targetConnection, targetConnectionName)
2734
+ : null;
2735
+ runtimeSchemaSnapshot = runtimeSchemaSnapshotForAgentConnection(projectRoot, targetConnectionName, activeScope?.scopeFingerprint);
2736
+ }
2716
2737
  const task = buildLocalContextPack(projectRoot, {
2717
2738
  question: request.question,
2718
2739
  focusObjectKey: request.selectedEvidenceId,
@@ -2724,7 +2745,11 @@ export async function startLocalServer(opts) {
2724
2745
  || topicRelation === 'shift'
2725
2746
  ? topicRelation
2726
2747
  : undefined,
2748
+ // Reuse remains available for fast follow-ups; the context-pack freshness
2749
+ // contract now binds it to this runtime snapshot's connection/generation.
2750
+ reusePolicy: 'seed',
2727
2751
  preparedMetadataFingerprint,
2752
+ ...(runtimeSchemaSnapshot ? { runtimeSchemaSnapshot } : {}),
2728
2753
  surface: 'notebook',
2729
2754
  selectedContext: {
2730
2755
  selectedObject: request.selectedObject,
@@ -2995,7 +3020,7 @@ export async function startLocalServer(opts) {
2995
3020
  cells: snapshotCells,
2996
3021
  });
2997
3022
  };
2998
- const executeDqlArtifactSourceForAgent = async (source, metadata = {}, invocationInput, executionConnection) => {
3023
+ const executeDqlArtifactSourceForAgent = async (source, metadata = {}, invocationInput, executionConnection, executionConnectionName) => {
2999
3024
  const invocation = prepareBlockInvocation({
3000
3025
  source,
3001
3026
  parameters: invocationInput?.parameters,
@@ -3011,7 +3036,7 @@ export async function startLocalServer(opts) {
3011
3036
  const activeConnection = requireActiveConnection(executionConnection);
3012
3037
  const precompiledSemanticQuery = Boolean(extractBlockStudioSql(source));
3013
3038
  const tableMapping = !precompiledSemanticQuery
3014
- ? await resolveSemanticTableMapping(executor, activeConnection, semanticLayer, projectRoot)
3039
+ ? await resolveSemanticTableMapping(executor, activeConnection, semanticLayer, projectRoot, executionConnectionName)
3015
3040
  : undefined;
3016
3041
  const semanticCompose = semanticLayer
3017
3042
  ? await composeSemanticBlockSqlForRuntime(source, semanticLayer, {
@@ -3080,7 +3105,7 @@ export async function startLocalServer(opts) {
3080
3105
  } : {}),
3081
3106
  };
3082
3107
  };
3083
- const executeCertifiedBlockByNameForAgent = async (blockName, invocationInput, requireCertified = false, executionConnection) => {
3108
+ const executeCertifiedBlockByNameForAgent = async (blockName, invocationInput, requireCertified = false, executionConnection, executionConnectionName) => {
3084
3109
  const manifest = buildManifest({ projectRoot });
3085
3110
  const block = manifest.blocks[blockName];
3086
3111
  if (!block) {
@@ -3095,7 +3120,7 @@ export async function startLocalServer(opts) {
3095
3120
  path: block.filePath,
3096
3121
  domain: block.domain,
3097
3122
  chartType: block.chartType,
3098
- }, invocationInput, executionConnection);
3123
+ }, invocationInput, executionConnection, executionConnectionName);
3099
3124
  return {
3100
3125
  ...result,
3101
3126
  dqlArtifact: {
@@ -3114,17 +3139,17 @@ export async function startLocalServer(opts) {
3114
3139
  },
3115
3140
  };
3116
3141
  };
3117
- const executeCertifiedBlockForAgent = async (node, invocationInput, executionConnection) => {
3142
+ const executeCertifiedBlockForAgent = async (node, invocationInput, executionConnection, executionConnectionName) => {
3118
3143
  if (node.kind !== 'block') {
3119
3144
  throw new Error(`Certified ${node.kind} "${node.name}" is a navigation artifact and cannot be executed as a block.`);
3120
3145
  }
3121
- return executeCertifiedBlockByNameForAgent(node.name || node.nodeId.replace(/^block:/, ''), invocationInput, false, executionConnection);
3146
+ return executeCertifiedBlockByNameForAgent(node.name || node.nodeId.replace(/^block:/, ''), invocationInput, false, executionConnection, executionConnectionName);
3122
3147
  };
3123
3148
  // Secondary agent surfaces use this compatibility callback. Delegate to the
3124
3149
  // same artifact-first path as Ask so research/app/notebook execution cannot
3125
3150
  // silently reintroduce a raw-SQL result followed by a different DQL artifact.
3126
3151
  const executeGeneratedSqlForAgent = async (sql, artifact) => executeGeneratedArtifactForAgent(artifact?.name ?? 'AI-generated query', sql, artifact);
3127
- const executeArtifactReferenceForAgent = async (artifact, question, executionConnection) => {
3152
+ const executeArtifactReferenceForAgent = async (artifact, question, executionConnection, executionConnectionName) => {
3128
3153
  const invocation = prepareBlockInvocation({
3129
3154
  source: artifact.source,
3130
3155
  parameters: artifact.parameterValues,
@@ -3139,7 +3164,7 @@ export async function startLocalServer(opts) {
3139
3164
  question,
3140
3165
  parameters: invocation.values,
3141
3166
  rowLimit: artifact.limit ?? 200,
3142
- }, executionConnection);
3167
+ }, executionConnection, executionConnectionName);
3143
3168
  return {
3144
3169
  ...result,
3145
3170
  dqlArtifact: {
@@ -3151,9 +3176,9 @@ export async function startLocalServer(opts) {
3151
3176
  },
3152
3177
  };
3153
3178
  };
3154
- const executeGeneratedArtifactForAgent = async (question, sql, seed, executionConnection) => {
3179
+ const executeGeneratedArtifactForAgent = async (question, sql, seed, executionConnection, executionConnectionName) => {
3155
3180
  const artifact = generatedSqlArtifactContract(question, sql, seed);
3156
- return executeArtifactReferenceForAgent({ ...artifact, limit: seed?.limit ?? artifact.limit ?? 200 }, question, executionConnection);
3181
+ return executeArtifactReferenceForAgent({ ...artifact, limit: seed?.limit ?? artifact.limit ?? 200 }, question, executionConnection, executionConnectionName);
3157
3182
  };
3158
3183
  /**
3159
3184
  * EXP-001: execution host for the deliberately narrow non-governed lane.
@@ -3277,7 +3302,7 @@ export async function startLocalServer(opts) {
3277
3302
  const activeScope = schemaConnection
3278
3303
  ? optionalActiveConnectionMetadataScope(projectRoot, projectConfig, schemaConnection, executionConnectionName ?? projectConfig.defaultConnectionName ?? 'default')
3279
3304
  : null;
3280
- const activeRuntimeSnapshot = latestRuntimeSchemaSnapshotForProject(projectRoot);
3305
+ const activeRuntimeSnapshot = latestRuntimeSchemaSnapshotForProject(projectRoot, executionConnectionName);
3281
3306
  const hasActivatedMetadataGeneration = Boolean(activeScope
3282
3307
  && activeRuntimeSnapshot?.scopeFingerprint === activeScope.scopeFingerprint
3283
3308
  && activeRuntimeSnapshot.status !== 'stale');
@@ -3289,9 +3314,6 @@ export async function startLocalServer(opts) {
3289
3314
  // row value must not turn a warm Ask into an information_schema scan over
3290
3315
  // thousands of enterprise tables; only field-scoped value probes are live.
3291
3316
  const enriched = await enrichAgentSchemaContextWithValueMatches(question, catalogContext, executor, schemaConnection, valueGrounding.searchSafeColumns);
3292
- if (!hasActivatedMetadataGeneration) {
3293
- recordAgentRuntimeSchemaSnapshot(projectRoot, catalogContext, 'catalog runtime schema');
3294
- }
3295
3317
  return enriched;
3296
3318
  }
3297
3319
  // An activated, scope-qualified warehouse generation is the complete warm
@@ -3307,7 +3329,7 @@ export async function startLocalServer(opts) {
3307
3329
  const enriched = valueGrounding.mode === 'safe_automatic'
3308
3330
  ? await enrichAgentSchemaContextWithValueMatches(question, schemaContext.ranked, executor, schemaConnection, valueGrounding.searchSafeColumns)
3309
3331
  : schemaContext.ranked;
3310
- recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'question-scoped information_schema search');
3332
+ recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'question-scoped information_schema search', executionConnectionName);
3311
3333
  return enriched;
3312
3334
  }
3313
3335
  catch {
@@ -6519,6 +6541,12 @@ export async function startLocalServer(opts) {
6519
6541
  res.end(serializeJSON({ ok: true }));
6520
6542
  return;
6521
6543
  }
6544
+ if (req.method === 'DELETE' && !action) {
6545
+ store.deleteThread(threadId);
6546
+ res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
6547
+ res.end(serializeJSON({ ok: true }));
6548
+ return;
6549
+ }
6522
6550
  // Explicit promotion — the ONLY path from conversation into durable
6523
6551
  // memory ("remember this"). Deliberate user act, tagged for audit.
6524
6552
  if (req.method === 'POST' && action === 'promote') {
@@ -9884,7 +9912,7 @@ export async function startLocalServer(opts) {
9884
9912
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
9885
9913
  res.end(serializeJSON({
9886
9914
  scope,
9887
- status: warehouseMetadataStatus(projectRoot, scope.scopeFingerprint),
9915
+ status: warehouseMetadataStatus(projectRoot, scope.scopeFingerprint, connectionId),
9888
9916
  }));
9889
9917
  }
9890
9918
  catch (error) {
@@ -9904,7 +9932,7 @@ export async function startLocalServer(opts) {
9904
9932
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
9905
9933
  res.end(serializeJSON({
9906
9934
  scope,
9907
- status: warehouseMetadataStatus(projectRoot, scope.scopeFingerprint),
9935
+ status: warehouseMetadataStatus(projectRoot, scope.scopeFingerprint, connectionId),
9908
9936
  }));
9909
9937
  }
9910
9938
  catch (error) {
@@ -9933,7 +9961,7 @@ export async function startLocalServer(opts) {
9933
9961
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
9934
9962
  res.end(serializeJSON({
9935
9963
  scope: synced.scope,
9936
- status: warehouseMetadataStatus(projectRoot, synced.scope.scopeFingerprint),
9964
+ status: warehouseMetadataStatus(projectRoot, synced.scope.scopeFingerprint, connectionId),
9937
9965
  }));
9938
9966
  }
9939
9967
  catch (error) {
@@ -9959,7 +9987,7 @@ export async function startLocalServer(opts) {
9959
9987
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
9960
9988
  res.end(serializeJSON({
9961
9989
  scope: synced.scope,
9962
- status: warehouseMetadataStatus(projectRoot, synced.scope.scopeFingerprint),
9990
+ status: warehouseMetadataStatus(projectRoot, synced.scope.scopeFingerprint, connectionId),
9963
9991
  }));
9964
9992
  }
9965
9993
  catch (error) {
@@ -9996,7 +10024,7 @@ export async function startLocalServer(opts) {
9996
10024
  connectorStatus,
9997
10025
  metadataScope,
9998
10026
  metadataStatus: metadataScope
9999
- ? warehouseMetadataStatus(projectRoot, metadataScope.scopeFingerprint)
10027
+ ? warehouseMetadataStatus(projectRoot, metadataScope.scopeFingerprint, defaultKey)
10000
10028
  : warehouseMetadataStatus(projectRoot),
10001
10029
  }));
10002
10030
  return;
@@ -12524,7 +12552,11 @@ table: ${table}${tagList}
12524
12552
  return;
12525
12553
  }
12526
12554
  const content = readFileSync(filePath);
12527
- res.writeHead(200, { 'Content-Type': contentTypeFor(filePath) });
12555
+ res.writeHead(200, {
12556
+ 'Content-Type': contentTypeFor(filePath),
12557
+ 'Cache-Control': staticResponseCacheControl(filePath),
12558
+ 'Content-Length': content.byteLength,
12559
+ });
12528
12560
  res.end(content);
12529
12561
  })().catch((error) => {
12530
12562
  // API-007/E2E-014: no rejected request promise may terminate the local
@@ -13480,10 +13512,10 @@ export function compileBlockStudioManifest(projectRoot, projectConfig = loadProj
13480
13512
  _lineageCache.delete(projectRoot);
13481
13513
  return manifest;
13482
13514
  }
13483
- async function refreshLocalMetadataCatalog(projectRoot, manifest, semanticLayer) {
13515
+ async function refreshLocalMetadataCatalog(projectRoot, manifest, semanticLayer, force = true) {
13484
13516
  try {
13485
13517
  await ensureMetadataCatalogFresh(projectRoot, {
13486
- force: true,
13518
+ force,
13487
13519
  ...(manifest ? { manifest } : {}),
13488
13520
  ...(semanticLayer ? { semanticLayer } : {}),
13489
13521
  });
@@ -15691,6 +15723,16 @@ function contentTypeFor(filePath) {
15691
15723
  return 'text/plain; charset=utf-8';
15692
15724
  }
15693
15725
  }
15726
+ export function staticResponseCacheControl(filePath) {
15727
+ // Vite fingerprints every generated asset filename, so a new DQL build gets
15728
+ // a new URL and the previous file is safe to retain indefinitely. Keep the
15729
+ // HTML shell revalidated so it can point at the current asset hashes.
15730
+ if (extname(filePath).toLowerCase() === '.html')
15731
+ return 'no-cache';
15732
+ return filePath.split(sep).includes('assets')
15733
+ ? 'public, max-age=31536000, immutable'
15734
+ : 'no-cache';
15735
+ }
15694
15736
  function listProjectFiles(projectRoot) {
15695
15737
  const allowed = new Set(['.dql', '.sql', '.md', '.json', '.csv', '.yaml', '.yml', '.dqlnb']);
15696
15738
  const files = [];
@@ -16132,11 +16174,11 @@ function parseSemanticBlockConfig(source) {
16132
16174
  limit: limitMatch ? Number.parseInt(limitMatch[1], 10) : undefined,
16133
16175
  };
16134
16176
  }
16135
- export async function resolveSemanticTableMapping(executor, connection, semanticLayer, projectRoot) {
16177
+ export async function resolveSemanticTableMapping(executor, connection, semanticLayer, projectRoot, connectionId) {
16136
16178
  if (!semanticLayer)
16137
16179
  return undefined;
16138
16180
  if (projectRoot) {
16139
- const snapshot = latestRuntimeSchemaSnapshotForProject(projectRoot);
16181
+ const snapshot = latestRuntimeSchemaSnapshotForProject(projectRoot, connectionId);
16140
16182
  if (snapshot?.scopeFingerprint && snapshot.tables.length) {
16141
16183
  return buildSemanticTableMapping(semanticLayer, snapshot.tables.map((table) => ({
16142
16184
  table_catalog: table.catalogOrDatabase,
@@ -16158,16 +16200,30 @@ export async function resolveSemanticTableMapping(executor, connection, semantic
16158
16200
  }
16159
16201
  }
16160
16202
  export function buildSemanticTableMapping(semanticLayer, rows) {
16161
- const dbTableNames = new Set();
16162
- const schemaQualified = new Map();
16203
+ const normalizeIdentifierPart = (value) => value
16204
+ .trim()
16205
+ .replace(/^["'`\[]|["'`\]]$/g, '')
16206
+ .toLowerCase();
16207
+ const normalizeRelation = (value) => value
16208
+ .split('.')
16209
+ .map(normalizeIdentifierPart)
16210
+ .filter(Boolean)
16211
+ .join('.');
16212
+ const candidates = [];
16163
16213
  for (const row of rows) {
16214
+ const catalog = String(row['table_catalog'] ?? '');
16164
16215
  const schema = String(row['table_schema'] ?? '');
16165
16216
  const name = String(row['table_name'] ?? '');
16166
- const relation = typeof row['table_relation'] === 'string' ? row['table_relation'] : undefined;
16217
+ const relation = typeof row['table_relation'] === 'string' && row['table_relation'].trim()
16218
+ ? row['table_relation']
16219
+ : [catalog, schema, name].filter(Boolean).join('.');
16167
16220
  if (!name)
16168
16221
  continue;
16169
- dbTableNames.add(name);
16170
- schemaQualified.set(name, relation ?? (schema ? `${schema}.${name}` : name));
16222
+ candidates.push({
16223
+ name: normalizeIdentifierPart(name),
16224
+ relation,
16225
+ normalizedRelation: normalizeRelation(relation),
16226
+ });
16171
16227
  }
16172
16228
  const tableMapping = {};
16173
16229
  const allSemanticTables = new Set();
@@ -16182,9 +16238,19 @@ export function buildSemanticTableMapping(semanticLayer, rows) {
16182
16238
  for (const semTable of allSemanticTables) {
16183
16239
  if (!semTable)
16184
16240
  continue;
16185
- if (dbTableNames.has(semTable) && schemaQualified.has(semTable)) {
16186
- tableMapping[semTable] = schemaQualified.get(semTable);
16187
- }
16241
+ const normalizedSemantic = normalizeRelation(semTable);
16242
+ const semanticLeaf = normalizedSemantic.split('.').at(-1);
16243
+ if (!semanticLeaf)
16244
+ continue;
16245
+ const qualifiedMatches = candidates.filter((candidate) => candidate.normalizedRelation === normalizedSemantic
16246
+ || candidate.normalizedRelation.endsWith(`.${normalizedSemantic}`));
16247
+ const leafMatches = candidates.filter((candidate) => candidate.name === semanticLeaf);
16248
+ const matches = qualifiedMatches.length > 0 ? qualifiedMatches : leafMatches;
16249
+ // Never guess when the same semantic leaf exists in several schemas. A
16250
+ // qualified semantic relation can disambiguate; otherwise the semantic
16251
+ // runtime must surface the modeling gap instead of executing the wrong one.
16252
+ if (matches.length === 1)
16253
+ tableMapping[semTable] = matches[0].relation;
16188
16254
  }
16189
16255
  return Object.keys(tableMapping).length > 0 ? tableMapping : undefined;
16190
16256
  }
@@ -21433,11 +21499,36 @@ export function runtimeSnapshotStale(projectRoot, maxAgeMs = RUNTIME_SNAPSHOT_MA
21433
21499
  return false;
21434
21500
  }
21435
21501
  }
21436
- function recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext, source) {
21502
+ /**
21503
+ * Resolve only metadata captured for the connection executing this Ask run.
21504
+ * Returning an explicit empty snapshot is a correctness boundary: callers pass
21505
+ * it into the context builder so it cannot silently fall back to another
21506
+ * connection's project-global runtime FTS rows.
21507
+ */
21508
+ export function runtimeSchemaSnapshotForAgentConnection(projectRoot, connectionId, expectedScopeFingerprint) {
21509
+ const snapshot = latestRuntimeSchemaSnapshotForProject(projectRoot, connectionId);
21510
+ const current = Boolean(snapshot
21511
+ && snapshot.status !== 'stale'
21512
+ && (!expectedScopeFingerprint
21513
+ || !snapshot.scopeFingerprint
21514
+ || snapshot.scopeFingerprint === expectedScopeFingerprint));
21515
+ return current
21516
+ ? snapshot
21517
+ : {
21518
+ version: 1,
21519
+ connectionId,
21520
+ status: 'partial',
21521
+ source: 'selected connection has no current runtime metadata',
21522
+ tables: [],
21523
+ };
21524
+ }
21525
+ function recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext, source, connectionId) {
21437
21526
  if (schemaContext.length === 0)
21438
21527
  return;
21439
21528
  try {
21440
21529
  recordRuntimeSchemaSnapshot(projectRoot, {
21530
+ ...(connectionId ? { connectionId } : {}),
21531
+ status: 'partial',
21441
21532
  source,
21442
21533
  tables: schemaContext.map((table) => ({
21443
21534
  relation: table.relation,