@duckcodeailabs/dql-cli 1.12.1 → 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.
- package/dist/assets/dql-notebook/assets/AgentAnswerCard-BEIwKGm4.js +5 -0
- package/dist/assets/dql-notebook/assets/AgentLogPage-CsSC2tOR.js +1 -0
- package/dist/assets/dql-notebook/assets/AiBuildDialog-CjJ-9bQW.js +1 -0
- package/dist/assets/dql-notebook/assets/AiBuildResult-BvTcW9oE.js +33 -0
- package/dist/assets/dql-notebook/assets/AiSidePanel-CxFrOfCE.js +6 -0
- package/dist/assets/dql-notebook/assets/AnalyticsHome-BtXEdJNR.js +6 -0
- package/dist/assets/dql-notebook/assets/AppsView-HjQkbbE6.js +4471 -0
- package/dist/assets/dql-notebook/assets/BlockStudio-oQHTJsXC.js +60 -0
- package/dist/assets/dql-notebook/assets/BusinessArtifactView-Dpsftjs7.js +1 -0
- package/dist/assets/dql-notebook/assets/DbtFirstModelingPage-c6ZteaI6.js +22 -0
- package/dist/assets/dql-notebook/assets/GitPage-Cj6mc-Yv.js +7 -0
- package/dist/assets/dql-notebook/assets/GlobalAiRail-8G_rEyhK.js +1 -0
- package/dist/assets/dql-notebook/assets/GovernedContextPage-BTtzt0Za.js +7 -0
- package/dist/assets/dql-notebook/assets/HelpDocsPage-e2CesE73.js +22 -0
- package/dist/assets/dql-notebook/assets/HomePage-U3ryMLEF.js +117 -0
- package/dist/assets/dql-notebook/assets/LineageDAG-CEmgJzm3.js +1 -0
- package/dist/assets/dql-notebook/assets/LineageDetailView-CeV5QMXS.js +1 -0
- package/dist/assets/dql-notebook/assets/LineageDrawer-B1oAn-N_.js +1 -0
- package/dist/assets/dql-notebook/assets/LineagePathBreadcrumb-fP6RTXut.js +14 -0
- package/dist/assets/dql-notebook/assets/MiniLineageGraph-CBYSTmRC.js +1 -0
- package/dist/assets/dql-notebook/assets/NewBlockModal-ksEwimjl.js +1 -0
- package/dist/assets/dql-notebook/assets/NewNotebookModal-CUx0WmXb.js +46 -0
- package/dist/assets/dql-notebook/assets/NotebookEditor-BdxJL3LX.js +202 -0
- package/dist/assets/dql-notebook/assets/ReadinessPage-DZ9wDW76.js +1 -0
- package/dist/assets/dql-notebook/assets/SetupOnboarding-Bt0AAe0h.js +6 -0
- package/dist/assets/dql-notebook/assets/TrustBadge-DNVJK_mP.js +1 -0
- package/dist/assets/dql-notebook/assets/UnifiedAgentRunPanel-BNqE169i.js +83 -0
- package/dist/assets/dql-notebook/assets/ai-build-bus-1BR770pY.js +1 -0
- package/dist/assets/dql-notebook/assets/answer-to-notebook-B_gE6rsY.js +2 -0
- package/dist/assets/dql-notebook/assets/arrow-left-DnKGhzOR.js +6 -0
- package/dist/assets/dql-notebook/assets/arrow-right-CQ73DG7_.js +6 -0
- package/dist/assets/dql-notebook/assets/book-open-text-CyBV5Nhw.js +6 -0
- package/dist/assets/dql-notebook/assets/bot-UOPtBzCj.js +6 -0
- package/dist/assets/dql-notebook/assets/chevron-up-CDHDU3N3.js +6 -0
- package/dist/assets/dql-notebook/assets/circle-x-DC5nkL_u.js +6 -0
- package/dist/assets/dql-notebook/assets/{codemirror-Co0dCEDr.js → codemirror-1_I9gm6Z.js} +1 -1
- package/dist/assets/dql-notebook/assets/dagre-BZV40eAE.css +1 -0
- package/dist/assets/dql-notebook/assets/dagre.esm-C7ujXipB.js +23 -0
- package/dist/assets/dql-notebook/assets/external-link-BiJ_dtTM.js +6 -0
- package/dist/assets/dql-notebook/assets/grip-vertical-eXvkUerq.js +6 -0
- package/dist/assets/dql-notebook/assets/index-BMVfPaXr.js +644 -0
- package/dist/assets/dql-notebook/assets/{index-DvLT6Kls.css → index-DKu4CZt3.css} +1 -1
- package/dist/assets/dql-notebook/assets/link-2-DkHVJTuH.js +6 -0
- package/dist/assets/dql-notebook/assets/list-tree-8lYW2Itb.js +6 -0
- package/dist/assets/dql-notebook/assets/maximize-2-DjSovOdP.js +6 -0
- package/dist/assets/dql-notebook/assets/minimize-2-DGlmQLES.js +6 -0
- package/dist/assets/dql-notebook/assets/panel-right-open-DPc4gShM.js +6 -0
- package/dist/assets/dql-notebook/assets/pencil-nwaGtOdL.js +6 -0
- package/dist/assets/dql-notebook/assets/{react-BRY8hXab.js → react-CRB3T2We.js} +2 -2
- package/dist/assets/dql-notebook/assets/rotate-ccw-ByjLhAu7.js +6 -0
- package/dist/assets/dql-notebook/assets/semantic-fields-eEhwnQhG.js +3 -0
- package/dist/assets/dql-notebook/assets/triangle-alert-zws0cKhp.js +6 -0
- package/dist/assets/dql-notebook/assets/usePersistedAgentThreadId-hxi1RZDm.js +18 -0
- package/dist/assets/dql-notebook/assets/user-round-CM8xNakq.js +11 -0
- package/dist/assets/dql-notebook/assets/wand-sparkles-CIKc8r-4.js +6 -0
- package/dist/assets/dql-notebook/assets/workflow-6C0ZLbTi.js +6 -0
- package/dist/assets/dql-notebook/assets/wrench-CeU7hF12.js +6 -0
- package/dist/assets/dql-notebook/assets/x-DLWEzvri.js +6 -0
- package/dist/assets/dql-notebook/index.html +3 -4
- package/dist/llm/providers/dql-agent-provider.d.ts.map +1 -1
- package/dist/llm/providers/dql-agent-provider.js +3 -1
- package/dist/llm/providers/dql-agent-provider.js.map +1 -1
- package/dist/local-runtime.d.ts +10 -2
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +215 -60
- package/dist/local-runtime.js.map +1 -1
- package/dist/package.json +10 -10
- package/dist/warehouse-metadata.d.ts +34 -1
- package/dist/warehouse-metadata.d.ts.map +1 -1
- package/dist/warehouse-metadata.js +136 -2
- package/dist/warehouse-metadata.js.map +1 -1
- package/package.json +10 -10
- package/dist/assets/dql-notebook/assets/index-CNQTSBBg.js +0 -5943
package/dist/local-runtime.js
CHANGED
|
@@ -40,7 +40,7 @@ import { observeWarehouseTargetIdentity } from './semantic-execution/connection-
|
|
|
40
40
|
import { configuredWarehouseTargetIdentity } from './semantic-execution/connection-identity.js';
|
|
41
41
|
import { executeTargetBoundSemanticQuery, semanticExecutionFailureCode, semanticExecutionFailureDetails, } from './semantic-execution/execution-gateway.js';
|
|
42
42
|
import { buildSemanticTargetBinding } from './semantic-execution/target-binding.js';
|
|
43
|
-
import { normalizeConnectionMetadataScope, syncWarehouseMetadata, warehouseMetadataStatus, } from './warehouse-metadata.js';
|
|
43
|
+
import { discoverWarehouseMetadataScopes, normalizeConnectionMetadataScope, syncWarehouseMetadata, warehouseMetadataStatus, } from './warehouse-metadata.js';
|
|
44
44
|
import { NotebookDatasetWorkspace, } from "./notebook-datasets.js";
|
|
45
45
|
import { prepareBlockInvocation } from './block-invocation.js';
|
|
46
46
|
const NOTEBOOK_EXECUTE_PREVIEW_ROW_LIMIT = 500;
|
|
@@ -204,6 +204,18 @@ export function parseAgentRunRequestBody(body) {
|
|
|
204
204
|
if (!question)
|
|
205
205
|
return { error: 'question is required.' };
|
|
206
206
|
const selectedObject = parseAgentRunSelectedObject(record.selectedObject);
|
|
207
|
+
const rawExecutionTarget = agentRunRecord(record.executionTarget)
|
|
208
|
+
?? agentRunRecord(agentRunRecord(record.workspaceContext)?.executionTarget);
|
|
209
|
+
const executionTarget = rawExecutionTarget?.target === 'local'
|
|
210
|
+
? { target: 'local' }
|
|
211
|
+
: rawExecutionTarget?.target === 'connection'
|
|
212
|
+
? {
|
|
213
|
+
target: 'connection',
|
|
214
|
+
...(agentRunString(rawExecutionTarget.connectionName)
|
|
215
|
+
? { connectionName: agentRunString(rawExecutionTarget.connectionName) }
|
|
216
|
+
: {}),
|
|
217
|
+
}
|
|
218
|
+
: undefined;
|
|
207
219
|
const workspaceContext = agentRunRecord(record.workspaceContext) ?? agentRunRecord(record.context);
|
|
208
220
|
const signals = agentRunRecord(record.signals);
|
|
209
221
|
const requestedMode = parseAgentRunRequestedMode(record.requestedMode) ?? parseAgentRunRequestedMode(record.mode);
|
|
@@ -219,6 +231,7 @@ export function parseAgentRunRequestBody(body) {
|
|
|
219
231
|
intent: agentRunString(record.intent),
|
|
220
232
|
signals: signals,
|
|
221
233
|
selectedObject,
|
|
234
|
+
executionTarget,
|
|
222
235
|
workspaceContext,
|
|
223
236
|
conversationContext: agentRunRecord(record.conversationContext),
|
|
224
237
|
history: parseAgentRunHistory(record.history),
|
|
@@ -887,6 +900,7 @@ export async function startLocalServer(opts) {
|
|
|
887
900
|
}
|
|
888
901
|
return requireActiveConnection(isConnectionConfig(body.connection) ? body.connection : connection);
|
|
889
902
|
};
|
|
903
|
+
const resolveAgentRunExecutionConnection = (request) => resolveExecutionConnection(request.executionTarget ? { executionTarget: request.executionTarget } : {});
|
|
890
904
|
const executionTargetDescriptor = (body) => {
|
|
891
905
|
const target = body.executionTarget && typeof body.executionTarget === 'object'
|
|
892
906
|
? body.executionTarget
|
|
@@ -954,7 +968,10 @@ export async function startLocalServer(opts) {
|
|
|
954
968
|
return result;
|
|
955
969
|
};
|
|
956
970
|
await reloadSemanticLayer();
|
|
957
|
-
|
|
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);
|
|
958
975
|
const startupDbtManifestPath = resolveDbtManifestPath(projectRoot, projectConfig);
|
|
959
976
|
if (startupDbtManifestPath && !isAgentProjectIndexReady(projectRoot)) {
|
|
960
977
|
try {
|
|
@@ -1312,17 +1329,26 @@ export async function startLocalServer(opts) {
|
|
|
1312
1329
|
// Best-effort active warehouse dialect so Lane-2 semantic compiles emit
|
|
1313
1330
|
// dialect-correct SQL (e.g. DATE_TRUNC / identifier quoting). Absent when no
|
|
1314
1331
|
// connection is configured — the compiler then uses its default dialect.
|
|
1315
|
-
let semanticDriver;
|
|
1316
1332
|
let semanticConnection;
|
|
1317
1333
|
try {
|
|
1318
|
-
semanticConnection =
|
|
1319
|
-
semanticDriver = semanticConnection.driver;
|
|
1334
|
+
semanticConnection = await resolveAgentRunExecutionConnection(request);
|
|
1320
1335
|
}
|
|
1321
|
-
catch {
|
|
1322
|
-
|
|
1336
|
+
catch (error) {
|
|
1337
|
+
// An explicit target is a contract and must fail closed. Without one,
|
|
1338
|
+
// conversational/non-data runs retain the existing no-connection path;
|
|
1339
|
+
// any later analytical execution will surface the normal setup action.
|
|
1340
|
+
if (request.executionTarget)
|
|
1341
|
+
throw error;
|
|
1323
1342
|
}
|
|
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;
|
|
1324
1350
|
const semanticTableMapping = semanticLayer && semanticConnection
|
|
1325
|
-
? await resolveSemanticTableMapping(executor, semanticConnection, semanticLayer, projectRoot)
|
|
1351
|
+
? await resolveSemanticTableMapping(executor, semanticConnection, semanticLayer, projectRoot, semanticConnectionName)
|
|
1326
1352
|
: undefined;
|
|
1327
1353
|
const requestedDomain = normalizeAgentRunDomain(agentRunWorkspaceValue(request, 'domain'));
|
|
1328
1354
|
const requestedPurpose = agentRunWorkspaceValue(request, 'purpose');
|
|
@@ -1510,10 +1536,12 @@ export async function startLocalServer(opts) {
|
|
|
1510
1536
|
? { resolvedAnalyticalPlan: routeDecision.resolvedAnalyticalPlan }
|
|
1511
1537
|
: {}),
|
|
1512
1538
|
analyticalReferenceInstant: new Date().toISOString(),
|
|
1513
|
-
executeCertifiedBlock: executeCertifiedBlockForAgent,
|
|
1514
|
-
executeGeneratedSql: (sql, artifact) => executeGeneratedArtifactForAgent(request.question, sql, artifact),
|
|
1515
|
-
executeDqlArtifact: (artifact) => executeArtifactReferenceForAgent(artifact, request.question),
|
|
1516
|
-
getSchemaContext: getSchemaContextForAgent,
|
|
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),
|
|
1542
|
+
getSchemaContext: (question, preparedContextPack) => getSchemaContextForAgent(question, preparedContextPack, semanticConnection, request.executionTarget?.target === 'connection'
|
|
1543
|
+
? request.executionTarget.connectionName
|
|
1544
|
+
: undefined),
|
|
1517
1545
|
}, (turn) => {
|
|
1518
1546
|
if (turn.kind === 'thinking')
|
|
1519
1547
|
onProgress?.(turn.text);
|
|
@@ -1762,7 +1790,7 @@ export async function startLocalServer(opts) {
|
|
|
1762
1790
|
}));
|
|
1763
1791
|
const exploration = await executeExploratoryCandidate(governedAnswer.exploratoryCandidate, explorationSchema, request.question, governedAnswer.contextPack?.questionPlan.requestedShape.topN?.scope === 'overall'
|
|
1764
1792
|
? governedAnswer.contextPack.questionPlan.requestedShape.topN.n
|
|
1765
|
-
: undefined);
|
|
1793
|
+
: undefined, await resolveAgentRunExecutionConnection(request));
|
|
1766
1794
|
const probeSummary = exploration.proofs.length > 0
|
|
1767
1795
|
? `${exploration.proofs.length} bounded join probe${exploration.proofs.length === 1 ? '' : 's'} completed.`
|
|
1768
1796
|
: 'No join probe was required for this query.';
|
|
@@ -2694,6 +2722,18 @@ export async function startLocalServer(opts) {
|
|
|
2694
2722
|
const preparedMetadataFingerprint = isAgentProjectIndexReady(projectRoot)
|
|
2695
2723
|
? currentMetadataFingerprint(projectRoot)
|
|
2696
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
|
+
}
|
|
2697
2737
|
const task = buildLocalContextPack(projectRoot, {
|
|
2698
2738
|
question: request.question,
|
|
2699
2739
|
focusObjectKey: request.selectedEvidenceId,
|
|
@@ -2705,7 +2745,11 @@ export async function startLocalServer(opts) {
|
|
|
2705
2745
|
|| topicRelation === 'shift'
|
|
2706
2746
|
? topicRelation
|
|
2707
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',
|
|
2708
2751
|
preparedMetadataFingerprint,
|
|
2752
|
+
...(runtimeSchemaSnapshot ? { runtimeSchemaSnapshot } : {}),
|
|
2709
2753
|
surface: 'notebook',
|
|
2710
2754
|
selectedContext: {
|
|
2711
2755
|
selectedObject: request.selectedObject,
|
|
@@ -2976,7 +3020,7 @@ export async function startLocalServer(opts) {
|
|
|
2976
3020
|
cells: snapshotCells,
|
|
2977
3021
|
});
|
|
2978
3022
|
};
|
|
2979
|
-
const executeDqlArtifactSourceForAgent = async (source, metadata = {}, invocationInput) => {
|
|
3023
|
+
const executeDqlArtifactSourceForAgent = async (source, metadata = {}, invocationInput, executionConnection, executionConnectionName) => {
|
|
2980
3024
|
const invocation = prepareBlockInvocation({
|
|
2981
3025
|
source,
|
|
2982
3026
|
parameters: invocationInput?.parameters,
|
|
@@ -2989,10 +3033,10 @@ export async function startLocalServer(opts) {
|
|
|
2989
3033
|
if (invocation.unresolvedParameters.length > 0) {
|
|
2990
3034
|
throw new Error(`I need values for: ${invocation.unresolvedParameters.join(', ')}.`);
|
|
2991
3035
|
}
|
|
2992
|
-
const activeConnection = requireActiveConnection();
|
|
3036
|
+
const activeConnection = requireActiveConnection(executionConnection);
|
|
2993
3037
|
const precompiledSemanticQuery = Boolean(extractBlockStudioSql(source));
|
|
2994
3038
|
const tableMapping = !precompiledSemanticQuery
|
|
2995
|
-
? await resolveSemanticTableMapping(executor, activeConnection, semanticLayer, projectRoot)
|
|
3039
|
+
? await resolveSemanticTableMapping(executor, activeConnection, semanticLayer, projectRoot, executionConnectionName)
|
|
2996
3040
|
: undefined;
|
|
2997
3041
|
const semanticCompose = semanticLayer
|
|
2998
3042
|
? await composeSemanticBlockSqlForRuntime(source, semanticLayer, {
|
|
@@ -3061,7 +3105,7 @@ export async function startLocalServer(opts) {
|
|
|
3061
3105
|
} : {}),
|
|
3062
3106
|
};
|
|
3063
3107
|
};
|
|
3064
|
-
const executeCertifiedBlockByNameForAgent = async (blockName, invocationInput, requireCertified = false) => {
|
|
3108
|
+
const executeCertifiedBlockByNameForAgent = async (blockName, invocationInput, requireCertified = false, executionConnection, executionConnectionName) => {
|
|
3065
3109
|
const manifest = buildManifest({ projectRoot });
|
|
3066
3110
|
const block = manifest.blocks[blockName];
|
|
3067
3111
|
if (!block) {
|
|
@@ -3076,7 +3120,7 @@ export async function startLocalServer(opts) {
|
|
|
3076
3120
|
path: block.filePath,
|
|
3077
3121
|
domain: block.domain,
|
|
3078
3122
|
chartType: block.chartType,
|
|
3079
|
-
}, invocationInput);
|
|
3123
|
+
}, invocationInput, executionConnection, executionConnectionName);
|
|
3080
3124
|
return {
|
|
3081
3125
|
...result,
|
|
3082
3126
|
dqlArtifact: {
|
|
@@ -3095,17 +3139,17 @@ export async function startLocalServer(opts) {
|
|
|
3095
3139
|
},
|
|
3096
3140
|
};
|
|
3097
3141
|
};
|
|
3098
|
-
const executeCertifiedBlockForAgent = async (node, invocationInput) => {
|
|
3142
|
+
const executeCertifiedBlockForAgent = async (node, invocationInput, executionConnection, executionConnectionName) => {
|
|
3099
3143
|
if (node.kind !== 'block') {
|
|
3100
3144
|
throw new Error(`Certified ${node.kind} "${node.name}" is a navigation artifact and cannot be executed as a block.`);
|
|
3101
3145
|
}
|
|
3102
|
-
return executeCertifiedBlockByNameForAgent(node.name || node.nodeId.replace(/^block:/, ''), invocationInput);
|
|
3146
|
+
return executeCertifiedBlockByNameForAgent(node.name || node.nodeId.replace(/^block:/, ''), invocationInput, false, executionConnection, executionConnectionName);
|
|
3103
3147
|
};
|
|
3104
3148
|
// Secondary agent surfaces use this compatibility callback. Delegate to the
|
|
3105
3149
|
// same artifact-first path as Ask so research/app/notebook execution cannot
|
|
3106
3150
|
// silently reintroduce a raw-SQL result followed by a different DQL artifact.
|
|
3107
3151
|
const executeGeneratedSqlForAgent = async (sql, artifact) => executeGeneratedArtifactForAgent(artifact?.name ?? 'AI-generated query', sql, artifact);
|
|
3108
|
-
const executeArtifactReferenceForAgent = async (artifact, question) => {
|
|
3152
|
+
const executeArtifactReferenceForAgent = async (artifact, question, executionConnection, executionConnectionName) => {
|
|
3109
3153
|
const invocation = prepareBlockInvocation({
|
|
3110
3154
|
source: artifact.source,
|
|
3111
3155
|
parameters: artifact.parameterValues,
|
|
@@ -3120,7 +3164,7 @@ export async function startLocalServer(opts) {
|
|
|
3120
3164
|
question,
|
|
3121
3165
|
parameters: invocation.values,
|
|
3122
3166
|
rowLimit: artifact.limit ?? 200,
|
|
3123
|
-
});
|
|
3167
|
+
}, executionConnection, executionConnectionName);
|
|
3124
3168
|
return {
|
|
3125
3169
|
...result,
|
|
3126
3170
|
dqlArtifact: {
|
|
@@ -3132,9 +3176,9 @@ export async function startLocalServer(opts) {
|
|
|
3132
3176
|
},
|
|
3133
3177
|
};
|
|
3134
3178
|
};
|
|
3135
|
-
const executeGeneratedArtifactForAgent = async (question, sql, seed) => {
|
|
3179
|
+
const executeGeneratedArtifactForAgent = async (question, sql, seed, executionConnection, executionConnectionName) => {
|
|
3136
3180
|
const artifact = generatedSqlArtifactContract(question, sql, seed);
|
|
3137
|
-
return executeArtifactReferenceForAgent({ ...artifact, limit: seed?.limit ?? artifact.limit ?? 200 }, question);
|
|
3181
|
+
return executeArtifactReferenceForAgent({ ...artifact, limit: seed?.limit ?? artifact.limit ?? 200 }, question, executionConnection, executionConnectionName);
|
|
3138
3182
|
};
|
|
3139
3183
|
/**
|
|
3140
3184
|
* EXP-001: execution host for the deliberately narrow non-governed lane.
|
|
@@ -3144,8 +3188,8 @@ export async function startLocalServer(opts) {
|
|
|
3144
3188
|
* existing read-only 200-row preview. Any malformed, broad, or unsupported
|
|
3145
3189
|
* candidate remains review-required and is not executed automatically.
|
|
3146
3190
|
*/
|
|
3147
|
-
const executeExploratoryCandidate = async (candidate, schemaContext = [], question = '', requestedTopN) => {
|
|
3148
|
-
const activeConnection = requireActiveConnection();
|
|
3191
|
+
const executeExploratoryCandidate = async (candidate, schemaContext = [], question = '', requestedTopN, executionConnection) => {
|
|
3192
|
+
const activeConnection = requireActiveConnection(executionConnection);
|
|
3149
3193
|
const preflight = repairExploratorySqlBeforeExecution(candidate.sql, schemaContext, question, activeConnection.driver);
|
|
3150
3194
|
const boundedSql = applyRequestedTopNToExploratorySql(preflight.sql, requestedTopN);
|
|
3151
3195
|
const repairs = boundedSql === preflight.sql
|
|
@@ -3236,42 +3280,40 @@ export async function startLocalServer(opts) {
|
|
|
3236
3280
|
return { proofs, sql: boundedSql, repairs, error: contradiction };
|
|
3237
3281
|
}
|
|
3238
3282
|
}
|
|
3239
|
-
const result = await executeGeneratedArtifactForAgent(question, boundedSql);
|
|
3283
|
+
const result = await executeGeneratedArtifactForAgent(question, boundedSql, undefined, activeConnection);
|
|
3240
3284
|
return { result, proofs, sql: boundedSql, repairs };
|
|
3241
3285
|
}
|
|
3242
3286
|
catch (error) {
|
|
3243
3287
|
return { proofs, sql: boundedSql, repairs, error: error instanceof Error ? error.message : String(error) };
|
|
3244
3288
|
}
|
|
3245
3289
|
};
|
|
3246
|
-
const getSchemaContextForAgent = async (question, preparedContextPack) => {
|
|
3290
|
+
const getSchemaContextForAgent = async (question, preparedContextPack, executionConnection, executionConnectionName) => {
|
|
3291
|
+
const schemaConnection = executionConnection ?? connection;
|
|
3247
3292
|
const scanRuntimeSchema = async () => {
|
|
3248
|
-
if (!
|
|
3293
|
+
if (!schemaConnection)
|
|
3249
3294
|
return { ranked: [], snapshot: [] };
|
|
3250
|
-
const result = await executor.executePositional(buildRuntimeSchemaSearchSql(question), [],
|
|
3295
|
+
const result = await executor.executePositional(buildRuntimeSchemaSearchSql(question), [], schemaConnection, { maxRows: 600, maxBytes: 2 * 1024 * 1024, batchSize: 200, deadlineMs: 30_000 });
|
|
3251
3296
|
return {
|
|
3252
3297
|
ranked: buildAgentSchemaContext(question, result.rows),
|
|
3253
3298
|
snapshot: buildAgentSchemaContext(question, result.rows, { includeUnscored: true, limit: 100 }),
|
|
3254
3299
|
};
|
|
3255
3300
|
};
|
|
3256
3301
|
const catalogContext = await buildAgentSchemaContextFromCatalog(projectRoot, question, preparedContextPack).catch(() => []);
|
|
3257
|
-
const activeScope =
|
|
3258
|
-
? optionalActiveConnectionMetadataScope(projectRoot, projectConfig,
|
|
3302
|
+
const activeScope = schemaConnection
|
|
3303
|
+
? optionalActiveConnectionMetadataScope(projectRoot, projectConfig, schemaConnection, executionConnectionName ?? projectConfig.defaultConnectionName ?? 'default')
|
|
3259
3304
|
: null;
|
|
3260
|
-
const activeRuntimeSnapshot = latestRuntimeSchemaSnapshotForProject(projectRoot);
|
|
3305
|
+
const activeRuntimeSnapshot = latestRuntimeSchemaSnapshotForProject(projectRoot, executionConnectionName);
|
|
3261
3306
|
const hasActivatedMetadataGeneration = Boolean(activeScope
|
|
3262
3307
|
&& activeRuntimeSnapshot?.scopeFingerprint === activeScope.scopeFingerprint
|
|
3263
3308
|
&& activeRuntimeSnapshot.status !== 'stale');
|
|
3264
3309
|
const valueGrounding = resolveAgentRuntimeValueGrounding(projectConfig);
|
|
3265
3310
|
if (catalogContext.length > 0) {
|
|
3266
|
-
if (!
|
|
3311
|
+
if (!schemaConnection || valueGrounding.mode !== 'safe_automatic')
|
|
3267
3312
|
return catalogContext;
|
|
3268
3313
|
// The immutable dbt/DQL snapshot already owns schema discovery. A named
|
|
3269
3314
|
// row value must not turn a warm Ask into an information_schema scan over
|
|
3270
3315
|
// thousands of enterprise tables; only field-scoped value probes are live.
|
|
3271
|
-
const enriched = await enrichAgentSchemaContextWithValueMatches(question, catalogContext, executor,
|
|
3272
|
-
if (!hasActivatedMetadataGeneration) {
|
|
3273
|
-
recordAgentRuntimeSchemaSnapshot(projectRoot, catalogContext, 'catalog runtime schema');
|
|
3274
|
-
}
|
|
3316
|
+
const enriched = await enrichAgentSchemaContextWithValueMatches(question, catalogContext, executor, schemaConnection, valueGrounding.searchSafeColumns);
|
|
3275
3317
|
return enriched;
|
|
3276
3318
|
}
|
|
3277
3319
|
// An activated, scope-qualified warehouse generation is the complete warm
|
|
@@ -3280,14 +3322,14 @@ export async function startLocalServer(opts) {
|
|
|
3280
3322
|
// user may explicitly refresh the selected scope.
|
|
3281
3323
|
if (hasActivatedMetadataGeneration)
|
|
3282
3324
|
return [];
|
|
3283
|
-
if (!
|
|
3325
|
+
if (!schemaConnection)
|
|
3284
3326
|
return [];
|
|
3285
3327
|
try {
|
|
3286
3328
|
const schemaContext = await scanRuntimeSchema();
|
|
3287
3329
|
const enriched = valueGrounding.mode === 'safe_automatic'
|
|
3288
|
-
? await enrichAgentSchemaContextWithValueMatches(question, schemaContext.ranked, executor,
|
|
3330
|
+
? await enrichAgentSchemaContextWithValueMatches(question, schemaContext.ranked, executor, schemaConnection, valueGrounding.searchSafeColumns)
|
|
3289
3331
|
: schemaContext.ranked;
|
|
3290
|
-
recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'question-scoped information_schema search');
|
|
3332
|
+
recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'question-scoped information_schema search', executionConnectionName);
|
|
3291
3333
|
return enriched;
|
|
3292
3334
|
}
|
|
3293
3335
|
catch {
|
|
@@ -6499,6 +6541,12 @@ export async function startLocalServer(opts) {
|
|
|
6499
6541
|
res.end(serializeJSON({ ok: true }));
|
|
6500
6542
|
return;
|
|
6501
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
|
+
}
|
|
6502
6550
|
// Explicit promotion — the ONLY path from conversation into durable
|
|
6503
6551
|
// memory ("remember this"). Deliberate user act, tagged for audit.
|
|
6504
6552
|
if (req.method === 'POST' && action === 'promote') {
|
|
@@ -6732,6 +6780,7 @@ export async function startLocalServer(opts) {
|
|
|
6732
6780
|
hintId,
|
|
6733
6781
|
title: typeof body.title === 'string' ? body.title : undefined,
|
|
6734
6782
|
guidance: typeof body.guidance === 'string' ? body.guidance : undefined,
|
|
6783
|
+
lesson: parseHintLessonInput(body.lesson),
|
|
6735
6784
|
correctedSql: typeof body.correctedSql === 'string' ? body.correctedSql : undefined,
|
|
6736
6785
|
scope: rawScope ? {
|
|
6737
6786
|
metric: scopeValue('metric'),
|
|
@@ -6911,6 +6960,7 @@ export async function startLocalServer(opts) {
|
|
|
6911
6960
|
requiredEvaluation: correctionEvaluation,
|
|
6912
6961
|
hintTitle: typeof body.title === 'string' && body.title.trim() ? body.title.trim() : undefined,
|
|
6913
6962
|
hintGuidance: typeof body.guidance === 'string' && body.guidance.trim() ? body.guidance.trim() : undefined,
|
|
6963
|
+
lesson: parseHintLessonInput(body.lesson),
|
|
6914
6964
|
tags: Array.isArray(body.tags) ? body.tags.map(String) : undefined,
|
|
6915
6965
|
});
|
|
6916
6966
|
// `approve` is intentionally ignored: capture never promotes a hint.
|
|
@@ -9824,8 +9874,32 @@ export async function startLocalServer(opts) {
|
|
|
9824
9874
|
return;
|
|
9825
9875
|
}
|
|
9826
9876
|
const metadataScopePreviewMatch = path.match(/^\/api\/connections\/([^/]+)\/metadata-scope\/preview$/);
|
|
9877
|
+
const metadataDiscoveryMatch = path.match(/^\/api\/connections\/([^/]+)\/metadata-scope\/discovery$/);
|
|
9827
9878
|
const metadataScopeMatch = path.match(/^\/api\/connections\/([^/]+)\/metadata-scope$/);
|
|
9828
9879
|
const metadataSyncMatch = path.match(/^\/api\/connections\/([^/]+)\/metadata-sync$/);
|
|
9880
|
+
if (metadataDiscoveryMatch && req.method === 'POST') {
|
|
9881
|
+
try {
|
|
9882
|
+
const connectionId = decodeURIComponent(metadataDiscoveryMatch[1]);
|
|
9883
|
+
const cfg = loadProjectConfig(projectRoot);
|
|
9884
|
+
const selectedConnection = projectConnectionById(projectRoot, cfg, connectionId);
|
|
9885
|
+
if (!selectedConnection)
|
|
9886
|
+
throw new Error(`Unknown connection "${connectionId}".`);
|
|
9887
|
+
const defaults = metadataScopeDbtDefaults(projectRoot, cfg);
|
|
9888
|
+
const discovery = await discoverWarehouseMetadataScopes({
|
|
9889
|
+
connectionId,
|
|
9890
|
+
executor,
|
|
9891
|
+
connection: selectedConnection,
|
|
9892
|
+
dbtRelations: defaults.relations,
|
|
9893
|
+
});
|
|
9894
|
+
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9895
|
+
res.end(serializeJSON(discovery));
|
|
9896
|
+
}
|
|
9897
|
+
catch (error) {
|
|
9898
|
+
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9899
|
+
res.end(serializeJSON({ error: error instanceof Error ? error.message : String(error) }));
|
|
9900
|
+
}
|
|
9901
|
+
return;
|
|
9902
|
+
}
|
|
9829
9903
|
if (metadataScopePreviewMatch && req.method === 'POST') {
|
|
9830
9904
|
try {
|
|
9831
9905
|
const connectionId = decodeURIComponent(metadataScopePreviewMatch[1]);
|
|
@@ -9838,7 +9912,7 @@ export async function startLocalServer(opts) {
|
|
|
9838
9912
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9839
9913
|
res.end(serializeJSON({
|
|
9840
9914
|
scope,
|
|
9841
|
-
status: warehouseMetadataStatus(projectRoot, scope.scopeFingerprint),
|
|
9915
|
+
status: warehouseMetadataStatus(projectRoot, scope.scopeFingerprint, connectionId),
|
|
9842
9916
|
}));
|
|
9843
9917
|
}
|
|
9844
9918
|
catch (error) {
|
|
@@ -9858,7 +9932,7 @@ export async function startLocalServer(opts) {
|
|
|
9858
9932
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9859
9933
|
res.end(serializeJSON({
|
|
9860
9934
|
scope,
|
|
9861
|
-
status: warehouseMetadataStatus(projectRoot, scope.scopeFingerprint),
|
|
9935
|
+
status: warehouseMetadataStatus(projectRoot, scope.scopeFingerprint, connectionId),
|
|
9862
9936
|
}));
|
|
9863
9937
|
}
|
|
9864
9938
|
catch (error) {
|
|
@@ -9887,7 +9961,7 @@ export async function startLocalServer(opts) {
|
|
|
9887
9961
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9888
9962
|
res.end(serializeJSON({
|
|
9889
9963
|
scope: synced.scope,
|
|
9890
|
-
status: warehouseMetadataStatus(projectRoot, synced.scope.scopeFingerprint),
|
|
9964
|
+
status: warehouseMetadataStatus(projectRoot, synced.scope.scopeFingerprint, connectionId),
|
|
9891
9965
|
}));
|
|
9892
9966
|
}
|
|
9893
9967
|
catch (error) {
|
|
@@ -9913,7 +9987,7 @@ export async function startLocalServer(opts) {
|
|
|
9913
9987
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9914
9988
|
res.end(serializeJSON({
|
|
9915
9989
|
scope: synced.scope,
|
|
9916
|
-
status: warehouseMetadataStatus(projectRoot, synced.scope.scopeFingerprint),
|
|
9990
|
+
status: warehouseMetadataStatus(projectRoot, synced.scope.scopeFingerprint, connectionId),
|
|
9917
9991
|
}));
|
|
9918
9992
|
}
|
|
9919
9993
|
catch (error) {
|
|
@@ -9950,7 +10024,7 @@ export async function startLocalServer(opts) {
|
|
|
9950
10024
|
connectorStatus,
|
|
9951
10025
|
metadataScope,
|
|
9952
10026
|
metadataStatus: metadataScope
|
|
9953
|
-
? warehouseMetadataStatus(projectRoot, metadataScope.scopeFingerprint)
|
|
10027
|
+
? warehouseMetadataStatus(projectRoot, metadataScope.scopeFingerprint, defaultKey)
|
|
9954
10028
|
: warehouseMetadataStatus(projectRoot),
|
|
9955
10029
|
}));
|
|
9956
10030
|
return;
|
|
@@ -12478,7 +12552,11 @@ table: ${table}${tagList}
|
|
|
12478
12552
|
return;
|
|
12479
12553
|
}
|
|
12480
12554
|
const content = readFileSync(filePath);
|
|
12481
|
-
res.writeHead(200, {
|
|
12555
|
+
res.writeHead(200, {
|
|
12556
|
+
'Content-Type': contentTypeFor(filePath),
|
|
12557
|
+
'Cache-Control': staticResponseCacheControl(filePath),
|
|
12558
|
+
'Content-Length': content.byteLength,
|
|
12559
|
+
});
|
|
12482
12560
|
res.end(content);
|
|
12483
12561
|
})().catch((error) => {
|
|
12484
12562
|
// API-007/E2E-014: no rejected request promise may terminate the local
|
|
@@ -13434,10 +13512,10 @@ export function compileBlockStudioManifest(projectRoot, projectConfig = loadProj
|
|
|
13434
13512
|
_lineageCache.delete(projectRoot);
|
|
13435
13513
|
return manifest;
|
|
13436
13514
|
}
|
|
13437
|
-
async function refreshLocalMetadataCatalog(projectRoot, manifest, semanticLayer) {
|
|
13515
|
+
async function refreshLocalMetadataCatalog(projectRoot, manifest, semanticLayer, force = true) {
|
|
13438
13516
|
try {
|
|
13439
13517
|
await ensureMetadataCatalogFresh(projectRoot, {
|
|
13440
|
-
force
|
|
13518
|
+
force,
|
|
13441
13519
|
...(manifest ? { manifest } : {}),
|
|
13442
13520
|
...(semanticLayer ? { semanticLayer } : {}),
|
|
13443
13521
|
});
|
|
@@ -15438,6 +15516,24 @@ function internalDqlRelationIdValidationError(sql, subject) {
|
|
|
15438
15516
|
return null;
|
|
15439
15517
|
return `${subject} contains internal DQL graph relation identifier${internalIds.length === 1 ? '' : 's'} ${internalIds.join(', ')}. Use the physical database.schema.table relation instead, or choose Ask AI to fix from the notebook error.`;
|
|
15440
15518
|
}
|
|
15519
|
+
function parseHintLessonInput(value) {
|
|
15520
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
15521
|
+
return undefined;
|
|
15522
|
+
const raw = value;
|
|
15523
|
+
const stringList = (field) => Array.isArray(raw[field])
|
|
15524
|
+
? raw[field].filter((item) => typeof item === 'string')
|
|
15525
|
+
: undefined;
|
|
15526
|
+
return {
|
|
15527
|
+
version: 1,
|
|
15528
|
+
category: typeof raw.category === 'string'
|
|
15529
|
+
? raw.category
|
|
15530
|
+
: undefined,
|
|
15531
|
+
rule: typeof raw.rule === 'string' ? raw.rule : undefined,
|
|
15532
|
+
intentExamples: stringList('intentExamples'),
|
|
15533
|
+
avoid: stringList('avoid'),
|
|
15534
|
+
expectedOutcome: typeof raw.expectedOutcome === 'string' ? raw.expectedOutcome : undefined,
|
|
15535
|
+
};
|
|
15536
|
+
}
|
|
15441
15537
|
class DqlInternalRelationIdError extends Error {
|
|
15442
15538
|
name = 'DqlInternalRelationIdError';
|
|
15443
15539
|
}
|
|
@@ -15627,6 +15723,16 @@ function contentTypeFor(filePath) {
|
|
|
15627
15723
|
return 'text/plain; charset=utf-8';
|
|
15628
15724
|
}
|
|
15629
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
|
+
}
|
|
15630
15736
|
function listProjectFiles(projectRoot) {
|
|
15631
15737
|
const allowed = new Set(['.dql', '.sql', '.md', '.json', '.csv', '.yaml', '.yml', '.dqlnb']);
|
|
15632
15738
|
const files = [];
|
|
@@ -16068,11 +16174,11 @@ function parseSemanticBlockConfig(source) {
|
|
|
16068
16174
|
limit: limitMatch ? Number.parseInt(limitMatch[1], 10) : undefined,
|
|
16069
16175
|
};
|
|
16070
16176
|
}
|
|
16071
|
-
export async function resolveSemanticTableMapping(executor, connection, semanticLayer, projectRoot) {
|
|
16177
|
+
export async function resolveSemanticTableMapping(executor, connection, semanticLayer, projectRoot, connectionId) {
|
|
16072
16178
|
if (!semanticLayer)
|
|
16073
16179
|
return undefined;
|
|
16074
16180
|
if (projectRoot) {
|
|
16075
|
-
const snapshot = latestRuntimeSchemaSnapshotForProject(projectRoot);
|
|
16181
|
+
const snapshot = latestRuntimeSchemaSnapshotForProject(projectRoot, connectionId);
|
|
16076
16182
|
if (snapshot?.scopeFingerprint && snapshot.tables.length) {
|
|
16077
16183
|
return buildSemanticTableMapping(semanticLayer, snapshot.tables.map((table) => ({
|
|
16078
16184
|
table_catalog: table.catalogOrDatabase,
|
|
@@ -16094,16 +16200,30 @@ export async function resolveSemanticTableMapping(executor, connection, semantic
|
|
|
16094
16200
|
}
|
|
16095
16201
|
}
|
|
16096
16202
|
export function buildSemanticTableMapping(semanticLayer, rows) {
|
|
16097
|
-
const
|
|
16098
|
-
|
|
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 = [];
|
|
16099
16213
|
for (const row of rows) {
|
|
16214
|
+
const catalog = String(row['table_catalog'] ?? '');
|
|
16100
16215
|
const schema = String(row['table_schema'] ?? '');
|
|
16101
16216
|
const name = String(row['table_name'] ?? '');
|
|
16102
|
-
const relation = typeof row['table_relation'] === 'string'
|
|
16217
|
+
const relation = typeof row['table_relation'] === 'string' && row['table_relation'].trim()
|
|
16218
|
+
? row['table_relation']
|
|
16219
|
+
: [catalog, schema, name].filter(Boolean).join('.');
|
|
16103
16220
|
if (!name)
|
|
16104
16221
|
continue;
|
|
16105
|
-
|
|
16106
|
-
|
|
16222
|
+
candidates.push({
|
|
16223
|
+
name: normalizeIdentifierPart(name),
|
|
16224
|
+
relation,
|
|
16225
|
+
normalizedRelation: normalizeRelation(relation),
|
|
16226
|
+
});
|
|
16107
16227
|
}
|
|
16108
16228
|
const tableMapping = {};
|
|
16109
16229
|
const allSemanticTables = new Set();
|
|
@@ -16118,9 +16238,19 @@ export function buildSemanticTableMapping(semanticLayer, rows) {
|
|
|
16118
16238
|
for (const semTable of allSemanticTables) {
|
|
16119
16239
|
if (!semTable)
|
|
16120
16240
|
continue;
|
|
16121
|
-
|
|
16122
|
-
|
|
16123
|
-
|
|
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;
|
|
16124
16254
|
}
|
|
16125
16255
|
return Object.keys(tableMapping).length > 0 ? tableMapping : undefined;
|
|
16126
16256
|
}
|
|
@@ -21369,11 +21499,36 @@ export function runtimeSnapshotStale(projectRoot, maxAgeMs = RUNTIME_SNAPSHOT_MA
|
|
|
21369
21499
|
return false;
|
|
21370
21500
|
}
|
|
21371
21501
|
}
|
|
21372
|
-
|
|
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) {
|
|
21373
21526
|
if (schemaContext.length === 0)
|
|
21374
21527
|
return;
|
|
21375
21528
|
try {
|
|
21376
21529
|
recordRuntimeSchemaSnapshot(projectRoot, {
|
|
21530
|
+
...(connectionId ? { connectionId } : {}),
|
|
21531
|
+
status: 'partial',
|
|
21377
21532
|
source,
|
|
21378
21533
|
tables: schemaContext.map((table) => ({
|
|
21379
21534
|
relation: table.relation,
|