@duckcodeailabs/dql-cli 1.9.5 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/dql-notebook/assets/{index-CY3qwACP.js → index-pl1On-IO.js} +359 -358
- package/dist/assets/dql-notebook/index.html +1 -1
- package/dist/llm/providers/dql-agent-provider.d.ts.map +1 -1
- package/dist/llm/providers/dql-agent-provider.js +9 -0
- package/dist/llm/providers/dql-agent-provider.js.map +1 -1
- package/dist/llm/types.d.ts +7 -1
- package/dist/llm/types.d.ts.map +1 -1
- package/dist/local-runtime.d.ts +11 -2
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +273 -55
- package/dist/local-runtime.js.map +1 -1
- package/dist/package.json +10 -10
- package/package.json +10 -10
package/dist/local-runtime.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Readable } from "node:stream";
|
|
|
10
10
|
import { buildMixedSourceWarehouseFallbackSql, findMentionedNotebookDataset, planMixedSourceNotebookSql, planMixedSourceSql, } from "./mixed-source-sql.js";
|
|
11
11
|
import { resolveNpmInvocation } from './npm-runtime.js';
|
|
12
12
|
import { buildExecutionPlan, createWelcomeNotebook, deserializeNotebook, getConnectorFormSchemas, hasSemanticRefs, hasStandaloneSemanticRef, resolveSemanticRefs, } from '@duckcodeailabs/dql-notebook';
|
|
13
|
-
import { loadSemanticLayerFromDir, normalizeDqlArtifactReference, serializeMetricDefinitionToYaml, resolveSemanticLayerAsync, resolveRepoSource, getDialect, Parser, NodeKind, blockParameterDefinitions, buildLineageGraph, buildManifest, collectInputFiles, findAppDocuments, findDashboardsForApp, isBlockIdRef, loadAppDocument, loadDashboardDocument, analyzeImpact, buildTrustChain, detectDomainFlows, getDomainTrustOverview, queryLineage, queryBusiness360, queryCompleteLineagePaths, LineageGraph, canonicalize, canonicalizeNotebook, diffDQL, diffNotebook, domainFolderSlug, previewModelingChange, applyModelingChange, previewDbtSourcePatch, applyDbtSourcePatch, loadDomainPackageRegistry, loadDbtNodeAuthoringDetail, relationshipValidationProofFingerprint, discoverDbtDomains, renderDomainDeclaration, ProjectSnapshotService, analyzeSqlReferences, } from '@duckcodeailabs/dql-core';
|
|
13
|
+
import { loadSemanticLayerFromDir, normalizeDqlArtifactReference, serializeMetricDefinitionToYaml, resolveSemanticLayerAsync, resolveRepoSource, getDialect, Parser, NodeKind, blockParameterDefinitions, buildLineageGraph, buildManifest, collectInputFiles, findAppDocuments, findDashboardsForApp, isBlockIdRef, loadAppDocument, loadDashboardDocument, analyzeImpact, buildTrustChain, detectDomainFlows, getDomainTrustOverview, queryLineage, queryBusiness360, queryCompleteLineagePaths, LineageGraph, canonicalize, canonicalizeNotebook, diffDQL, diffNotebook, domainFolderSlug, previewModelingChange, applyModelingChange, previewDbtSourcePatch, applyDbtSourcePatch, loadDomainPackageRegistry, loadDbtNodeAuthoringDetail, normalizeAnalyticalFailureV1, relationshipValidationProofFingerprint, discoverDbtDomains, renderDomainDeclaration, ProjectSnapshotService, analyzeSqlReferences, } from '@duckcodeailabs/dql-core';
|
|
14
14
|
import { load as loadYaml } from 'js-yaml';
|
|
15
15
|
import { listBlockTemplates } from './block-templates.js';
|
|
16
16
|
import { getRunner as getLLMRunner } from './llm/index.js';
|
|
@@ -19,7 +19,7 @@ import { fetchLatestPublishedDqlVersion, resolveDqlRuntimeVersionStatus } from '
|
|
|
19
19
|
import { resolveRetrievalHealthStatus } from './retrieval-health.js';
|
|
20
20
|
import { createDqlAgentProviderRunner, resolveAgentFollowUpContext } from './llm/providers/dql-agent-provider.js';
|
|
21
21
|
import { listRemoteMcpSettings, saveRemoteMcpSettings } from './llm/mcp-config.js';
|
|
22
|
-
import { ClaudeProvider, ConversationStore, advanceThreadState, buildConversationSnapshot, recallRelevantTurns, GeminiProvider, MemoryStore, OllamaProvider, OpenAIProvider, buildBlockBusinessFingerprint, buildBlockSqlFingerprints, buildAnalysisQuestionPlan, aggregationIntegrityIssuesForSql, buildLocalContextPack, toAgentRetrievalEvidence, defaultConversationPath, defaultMemoryPath, ensureDefaultMemoryFiles, ensureAgentProjectReady, isAgentProjectIndexReady, currentMetadataFingerprint, ensureMetadataCatalogFresh, readIndexedDomainKnowledge, readIndexedKnowledge360, compactSemanticRuntimeFailure, propose, proposePlan, recordCorrectionTrace, emitCorrectionEvalCase, mineJoinPatterns, reviewHint, AgentRunEngine, SqliteAgentRunStore, defaultAgentRunGates, createLlmAgentRunPlanner, createHybridRouter, computeResultStats, buildDeterministicDashboardStory, synthesizeAnswer, streamOrGenerate, narrateResult, buildProposePreview, buildFromPrompt, defaultAgentRunStorePath, defaultAgentRunSqlitePath, resolveLocalOwner, resolveProposeConfig, recordQueryRun, recordRuntimeSchemaSnapshot, latestRuntimeSchemaSnapshotForProject, loadSkills, migrateLegacySkills, configuredSkillsPath, skillsDir, draftDomainSkillBootstrap, buildDomainSkillBootstrapPrompt, mergeDomainSkillBootstrapEnrichment, writeSkill, deleteSkill, deriveGeneratedDraftSlug, reindexProject, invalidateAgentProjectState, recordAgentRuntimeVersion, resolveDomainContextEnvelope, defaultKgPath, planAppFromPrompt, KGStore, planResearch, loadSemanticMetrics, cascadeTraceToEvidenceRouteSteps, createCascadeAnswerResult, createCascadeTrace, routeReasoningEffort, routeForCascadeAnswerTier, clampReasoningEffort, bumpReasoningEffort, resolveThinkingMode, coerceThinkingMode, upsertGeneratedDqlArtifactDraft, loadAgentSemanticLayer, } from '@duckcodeailabs/dql-agent';
|
|
22
|
+
import { ClaudeProvider, ConversationStore, advanceThreadState, buildConversationSnapshot, recallRelevantTurns, GeminiProvider, MemoryStore, OllamaProvider, OpenAIProvider, buildBlockBusinessFingerprint, buildBlockSqlFingerprints, buildAnalysisQuestionPlan, aggregationIntegrityIssuesForSql, buildLocalContextPack, applyContextPackCompatibility, toAgentRetrievalEvidence, defaultConversationPath, defaultMemoryPath, ensureDefaultMemoryFiles, ensureAgentProjectReady, isAgentProjectIndexReady, currentMetadataFingerprint, ensureMetadataCatalogFresh, readIndexedDomainKnowledge, readIndexedKnowledge360, compactSemanticRuntimeFailure, propose, proposePlan, recordCorrectionTrace, emitCorrectionEvalCase, mineJoinPatterns, reviewHint, AgentRunEngine, SqliteAgentRunStore, defaultAgentRunGates, createLlmAgentRunPlanner, createHybridRouter, computeResultStats, buildDeterministicDashboardStory, synthesizeAnswer, streamOrGenerate, narrateResult, buildProposePreview, buildFromPrompt, defaultAgentRunStorePath, defaultAgentRunSqlitePath, resolveLocalOwner, resolveProposeConfig, recordQueryRun, recordRuntimeSchemaSnapshot, latestRuntimeSchemaSnapshotForProject, loadSkills, migrateLegacySkills, configuredSkillsPath, skillsDir, draftDomainSkillBootstrap, buildDomainSkillBootstrapPrompt, mergeDomainSkillBootstrapEnrichment, writeSkill, deleteSkill, deriveGeneratedDraftSlug, deriveAnalyticalRepair, reindexProject, invalidateAgentProjectState, recordAgentRuntimeVersion, resolveDomainContextEnvelope, defaultKgPath, planAppFromPrompt, KGStore, planResearch, loadSemanticMetrics, cascadeTraceToEvidenceRouteSteps, createCascadeAnswerResult, createCascadeTrace, routeReasoningEffort, routeForCascadeAnswerTier, clampReasoningEffort, bumpReasoningEffort, resolveThinkingMode, coerceThinkingMode, upsertGeneratedDqlArtifactDraft, loadAgentSemanticLayer, } from '@duckcodeailabs/dql-agent';
|
|
23
23
|
import { gatherProposeEnrichment } from './propose-enrich.js';
|
|
24
24
|
import { handleAppsApi, proposeAppAiBuild, recommendVisualization } from './apps-api.js';
|
|
25
25
|
import { getActiveProvider, getEffectiveProviderConfig, isProviderSettingsId, listProviderSettings, saveProviderSettings, } from './settings/provider-settings.js';
|
|
@@ -279,6 +279,53 @@ export function agentAnswerHasExecutionFailure(governedAnswer) {
|
|
|
279
279
|
return typeof governedAnswer.executionError === 'string'
|
|
280
280
|
&& governedAnswer.executionError.trim().length > 0;
|
|
281
281
|
}
|
|
282
|
+
/** Rebuild the immutable failed-run input from the artifact retained by API-007. */
|
|
283
|
+
export function analyticalFailedRunFromAgentRun(run) {
|
|
284
|
+
for (const artifact of run.artifacts) {
|
|
285
|
+
const payload = agentRunRecord(artifact.payload);
|
|
286
|
+
if (!payload)
|
|
287
|
+
continue;
|
|
288
|
+
const failure = normalizeAnalyticalFailureV1(payload.analyticalFailure);
|
|
289
|
+
const plan = agentRunRecord(payload.resolvedAnalyticalPlan);
|
|
290
|
+
const planFingerprint = agentRunString(plan?.fingerprint) ?? failure?.planFingerprint;
|
|
291
|
+
if (!failure || !planFingerprint)
|
|
292
|
+
continue;
|
|
293
|
+
const dqlArtifact = normalizeDqlArtifactReference(payload.dqlArtifact);
|
|
294
|
+
const dqlSource = dqlArtifact?.source;
|
|
295
|
+
const compiledSql = agentRunString(payload.sql)
|
|
296
|
+
?? agentRunString(payload.proposedSql)
|
|
297
|
+
?? dqlArtifact?.compiledSql;
|
|
298
|
+
const graph = agentRunRecord(payload.analyticalExecutionGraph);
|
|
299
|
+
const rawRoute = agentRunString(graph?.route) ?? agentRunString(plan?.recommendedRoute);
|
|
300
|
+
const route = rawRoute === 'certified'
|
|
301
|
+
? 'certified'
|
|
302
|
+
: rawRoute === 'semantic'
|
|
303
|
+
? 'semantic'
|
|
304
|
+
: rawRoute === 'governed_sql'
|
|
305
|
+
? 'governed_sql'
|
|
306
|
+
: 'generated_sql';
|
|
307
|
+
const rawTrust = dqlArtifact?.trustState ?? agentRunString(payload.reviewStatus) ?? artifact.trustState;
|
|
308
|
+
const trustState = rawTrust === 'certified'
|
|
309
|
+
? 'certified'
|
|
310
|
+
: rawTrust === 'governed'
|
|
311
|
+
? 'governed'
|
|
312
|
+
: 'review_required';
|
|
313
|
+
return {
|
|
314
|
+
version: 1,
|
|
315
|
+
runId: failure.runId,
|
|
316
|
+
snapshotId: failure.snapshotId,
|
|
317
|
+
route,
|
|
318
|
+
trustState,
|
|
319
|
+
planFingerprint,
|
|
320
|
+
...(dqlSource ? { dqlSource } : {}),
|
|
321
|
+
...(failure.dqlFingerprint ? { dqlFingerprint: failure.dqlFingerprint } : {}),
|
|
322
|
+
...(compiledSql ? { compiledSql } : {}),
|
|
323
|
+
...(failure.sqlFingerprint ? { sqlFingerprint: failure.sqlFingerprint } : {}),
|
|
324
|
+
failure,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
282
329
|
function businessNarrativeGaps(warnings) {
|
|
283
330
|
const businessRelevant = (warnings ?? []).filter((warning) => !(/missing requested output column/i.test(warning)
|
|
284
331
|
|| /\bquery plan\b/i.test(warning)
|
|
@@ -1220,6 +1267,57 @@ export async function startLocalServer(opts) {
|
|
|
1220
1267
|
},
|
|
1221
1268
|
...(semanticDriver ? { semanticDriver } : {}),
|
|
1222
1269
|
...(semanticTableMapping ? { semanticTableMapping } : {}),
|
|
1270
|
+
...(semanticLayer && semanticConnection ? {
|
|
1271
|
+
resolveAnalyticalFreshness: async (freshnessRequest) => {
|
|
1272
|
+
const binding = freshnessRequest.authorizedAdapterRequest;
|
|
1273
|
+
if (!binding || binding.route !== 'semantic') {
|
|
1274
|
+
throw Object.assign(new Error('The analytical freshness request is not bound to an authorized semantic route.'), {
|
|
1275
|
+
code: 'FRESHNESS_BINDING_REQUIRED',
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
if (freshnessRequest.snapshotId !== routeDecision?.resolvedAnalyticalPlan?.snapshotId) {
|
|
1279
|
+
throw Object.assign(new Error('The analytical freshness request does not match the routed metadata snapshot.'), {
|
|
1280
|
+
code: 'SNAPSHOT_DRIFT',
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
projectSnapshot();
|
|
1284
|
+
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
|
+
}, {
|
|
1295
|
+
projectRoot,
|
|
1296
|
+
projectConfig,
|
|
1297
|
+
detectedProvider: semanticDetectedProvider,
|
|
1298
|
+
semanticLayer: semanticLayer,
|
|
1299
|
+
driver: semanticDriver,
|
|
1300
|
+
tableMapping: semanticTableMapping,
|
|
1301
|
+
});
|
|
1302
|
+
if (!compiled) {
|
|
1303
|
+
throw Object.assign(new Error('The authorized semantic freshness query could not be compiled.'), {
|
|
1304
|
+
code: 'COMPILATION_FAILED',
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
const prepared = prepareLocalExecution(compiled.sql, semanticConnection, projectRoot, projectConfig);
|
|
1308
|
+
const result = await executor.executeQuery(prepared.sql, [], runtimeVariables({}), prepared.connection);
|
|
1309
|
+
projectSnapshot();
|
|
1310
|
+
projectSnapshots.assertCurrent(runProjectSnapshot.snapshotId);
|
|
1311
|
+
return {
|
|
1312
|
+
version: 1,
|
|
1313
|
+
snapshotId: freshnessRequest.snapshotId,
|
|
1314
|
+
metricId: freshnessRequest.metricId,
|
|
1315
|
+
timeDimensionId: freshnessRequest.timeDimensionId,
|
|
1316
|
+
observedThrough: analyticalFreshnessObservedThrough(result, freshnessRequest),
|
|
1317
|
+
observedAt: new Date().toISOString(),
|
|
1318
|
+
};
|
|
1319
|
+
},
|
|
1320
|
+
} : {}),
|
|
1223
1321
|
...(semanticLayer ? {
|
|
1224
1322
|
semanticQueryCompiler: async (selection) => {
|
|
1225
1323
|
const compiled = await compileSemanticRuntimeQuery({
|
|
@@ -1259,6 +1357,10 @@ export async function startLocalServer(opts) {
|
|
|
1259
1357
|
...(routeDecision?.meaningResolution?.recommendedExecutionId
|
|
1260
1358
|
? { preferredExecutionId: routeDecision.meaningResolution.recommendedExecutionId }
|
|
1261
1359
|
: {}),
|
|
1360
|
+
...(routeDecision?.resolvedAnalyticalPlan
|
|
1361
|
+
? { resolvedAnalyticalPlan: routeDecision.resolvedAnalyticalPlan }
|
|
1362
|
+
: {}),
|
|
1363
|
+
analyticalReferenceInstant: new Date().toISOString(),
|
|
1262
1364
|
executeCertifiedBlock: executeCertifiedBlockForAgent,
|
|
1263
1365
|
executeGeneratedSql: (sql, artifact) => executeGeneratedArtifactForAgent(request.question, sql, artifact),
|
|
1264
1366
|
executeDqlArtifact: (artifact) => executeArtifactReferenceForAgent(artifact, request.question),
|
|
@@ -1619,6 +1721,8 @@ export async function startLocalServer(opts) {
|
|
|
1619
1721
|
&& (governedAnswer.refusalCode === 'grounding_gap' || governedAnswer.refusalCode === 'modeling_gap')
|
|
1620
1722
|
&& !isExploratory;
|
|
1621
1723
|
const isProviderError = governedAnswer.kind === 'no_answer' && governedAnswer.refusalCode === 'provider_error';
|
|
1724
|
+
const isAnalyticalFailure = Boolean(governedAnswer.analyticalFailure);
|
|
1725
|
+
const isTerminalFailure = isProviderError || isExecutionFailure || isAnalyticalFailure;
|
|
1622
1726
|
// AGT-004: a rejected attribution/export/proof policy is a deliberate
|
|
1623
1727
|
// governance boundary, not an ambiguous user question and not a repairable
|
|
1624
1728
|
// retrieval miss. Keep the precise policy detail visible, but never burn two
|
|
@@ -1678,14 +1782,14 @@ export async function startLocalServer(opts) {
|
|
|
1678
1782
|
synthesizedAnswer = undefined;
|
|
1679
1783
|
}
|
|
1680
1784
|
}
|
|
1681
|
-
const status =
|
|
1785
|
+
const status = isTerminalFailure
|
|
1682
1786
|
? 'blocked'
|
|
1683
1787
|
: needsClarification
|
|
1684
1788
|
? 'needs_clarification'
|
|
1685
1789
|
: isCertified || isSemantic
|
|
1686
1790
|
? 'completed'
|
|
1687
1791
|
: 'needs_review';
|
|
1688
|
-
const trustState =
|
|
1792
|
+
const trustState = isTerminalFailure
|
|
1689
1793
|
? 'blocked'
|
|
1690
1794
|
: needsClarification
|
|
1691
1795
|
? 'not_applicable'
|
|
@@ -1694,7 +1798,7 @@ export async function startLocalServer(opts) {
|
|
|
1694
1798
|
: isSemantic
|
|
1695
1799
|
? 'governed'
|
|
1696
1800
|
: 'review_required';
|
|
1697
|
-
const stopReason =
|
|
1801
|
+
const stopReason = isTerminalFailure
|
|
1698
1802
|
? 'blocked'
|
|
1699
1803
|
: needsClarification
|
|
1700
1804
|
? 'needs_clarification'
|
|
@@ -1703,10 +1807,23 @@ export async function startLocalServer(opts) {
|
|
|
1703
1807
|
: isSemantic
|
|
1704
1808
|
? 'governed_semantic_answer'
|
|
1705
1809
|
: 'human_review_required';
|
|
1810
|
+
const terminalFailureActions = governedAnswer.analyticalFailure
|
|
1811
|
+
? governedAnswer.analyticalFailure.code === 'PERMISSION_DENIED'
|
|
1812
|
+
? [{ id: 'repair-access', label: 'Change connection or request access', route: 'blocked' }]
|
|
1813
|
+
: governedAnswer.analyticalFailure.code === 'TIMEOUT'
|
|
1814
|
+
? [{ id: 'retry-same-plan', label: 'Retry the same bounded plan', route: 'generated_answer' }]
|
|
1815
|
+
: governedAnswer.analyticalFailure.code === 'SNAPSHOT_DRIFT'
|
|
1816
|
+
|| governedAnswer.analyticalFailure.code === 'COLUMN_NOT_FOUND'
|
|
1817
|
+
|| governedAnswer.analyticalFailure.code === 'RELATION_NOT_FOUND'
|
|
1818
|
+
? [{ id: 'refresh-snapshot', label: 'Refresh the governed snapshot before retrying', route: 'blocked' }]
|
|
1819
|
+
: [{ id: 'review-analytical-failure', label: 'Review the plan, DQL, SQL, and safe repair actions', route: 'blocked' }]
|
|
1820
|
+
: isProviderError
|
|
1821
|
+
? [{ id: 'retry-after-provider', label: 'Retry after fixing the AI provider', route: 'generated_answer' }]
|
|
1822
|
+
: [{ id: 'retry-after-connection', label: 'Retry after fixing the database connection', route: 'generated_answer' }];
|
|
1706
1823
|
const nextActions = needsClarification
|
|
1707
1824
|
? [{ id: 'clarify', label: 'Clarify question', route: 'generated_answer' }]
|
|
1708
|
-
:
|
|
1709
|
-
?
|
|
1825
|
+
: isTerminalFailure
|
|
1826
|
+
? terminalFailureActions
|
|
1710
1827
|
: isGroundingGap
|
|
1711
1828
|
? [{ id: 'research-gap', label: 'Research missing metadata coverage', route: 'research', artifactKind: 'research_run' }]
|
|
1712
1829
|
: [
|
|
@@ -1723,15 +1840,15 @@ export async function startLocalServer(opts) {
|
|
|
1723
1840
|
resolvedRoute,
|
|
1724
1841
|
answerRefusalCode: governedAnswer.kind === 'no_answer' ? governedAnswer.refusalCode : undefined,
|
|
1725
1842
|
answerTier: governedAnswer.route?.tier,
|
|
1726
|
-
summary:
|
|
1843
|
+
summary: isTerminalFailure
|
|
1727
1844
|
? 'The governed query could not be executed.'
|
|
1728
1845
|
: governedAnswer.route?.label ?? (isCertified ? 'Answered from certified DQL context.' : isExploratory ? 'Exploratory DBT-grounded analysis requires review.' : 'Answered with review-required generated analysis.'),
|
|
1729
1846
|
answer: synthesizedAnswer ?? governedAnswer.answer ?? governedAnswer.text,
|
|
1730
1847
|
status,
|
|
1731
1848
|
trustState,
|
|
1732
1849
|
stopReason,
|
|
1733
|
-
artifacts:
|
|
1734
|
-
? []
|
|
1850
|
+
artifacts: isTerminalFailure
|
|
1851
|
+
? [agentRunArtifact('answer', 'Failed governed analytical run', governedAnswer, governedAnswer.sourceCertifiedBlock ?? governedAnswer.block?.name, 'blocked')]
|
|
1735
1852
|
: governedAnswer.kind === 'no_answer'
|
|
1736
1853
|
// A refusal still keeps the DQL draft the answer loop produced (when any),
|
|
1737
1854
|
// so the "Review DQL draft" next-action isn't a dead link and the user can
|
|
@@ -1880,6 +1997,7 @@ export async function startLocalServer(opts) {
|
|
|
1880
1997
|
history: request.history,
|
|
1881
1998
|
// When the user explicitly picked research, investigate — don't collapse to one step.
|
|
1882
1999
|
forceInvestigate: request.requestedMode === 'research',
|
|
2000
|
+
rootPlan: routeDecision?.resolvedAnalyticalPlan,
|
|
1883
2001
|
});
|
|
1884
2002
|
// Direct governed answer ("the metric answers this directly"): don't wrap it in
|
|
1885
2003
|
// a review-required research dossier — run the query through the answer loop and
|
|
@@ -2400,54 +2518,23 @@ export async function startLocalServer(opts) {
|
|
|
2400
2518
|
const meaningEvidence = pack.retrievalDiagnostics.meaningEvidence;
|
|
2401
2519
|
if (!meaningEvidence) {
|
|
2402
2520
|
return {
|
|
2403
|
-
snapshotId: pack.
|
|
2521
|
+
snapshotId: pack.knowledgeLens.snapshotId,
|
|
2404
2522
|
sourceFingerprint: pack.freshness.fingerprint ?? undefined,
|
|
2523
|
+
knowledgeLens: pack.knowledgeLens,
|
|
2405
2524
|
candidates: [],
|
|
2406
2525
|
diagnostics: { durationMs: Date.now() - startedAt },
|
|
2407
2526
|
};
|
|
2408
2527
|
}
|
|
2409
2528
|
const evidence = toAgentRetrievalEvidence(meaningEvidence, pack.questionPlan, {
|
|
2410
|
-
snapshotId: pack.
|
|
2529
|
+
snapshotId: pack.knowledgeLens.snapshotId,
|
|
2411
2530
|
sourceFingerprint: pack.freshness.fingerprint ?? undefined,
|
|
2531
|
+
knowledgeLens: pack.knowledgeLens,
|
|
2532
|
+
analyticalPolicies: pack.skills.flatMap((skill) => skill.analyticalPolicy ? [skill.analyticalPolicy] : []),
|
|
2533
|
+
contextObjects: pack.objects,
|
|
2412
2534
|
durationMs: Date.now() - startedAt,
|
|
2413
2535
|
truncated: pack.retrievalDiagnostics.topRejected.length > 0,
|
|
2414
2536
|
});
|
|
2415
|
-
|
|
2416
|
-
const semanticEvidence = new Set(pack.routeDecision.selectedEvidence
|
|
2417
|
-
.filter((item) => item.role === 'semantic_metric')
|
|
2418
|
-
.map((item) => item.objectKey));
|
|
2419
|
-
return {
|
|
2420
|
-
...evidence,
|
|
2421
|
-
candidates: evidence.candidates.map((candidate) => {
|
|
2422
|
-
if (candidate.kind === 'certified_block') {
|
|
2423
|
-
const fit = certifiedFits.get(candidate.id);
|
|
2424
|
-
return {
|
|
2425
|
-
...candidate,
|
|
2426
|
-
compatibility: fit?.action === 'certified_answer'
|
|
2427
|
-
? 'compatible'
|
|
2428
|
-
: fit?.action === 'rejected_for_fit'
|
|
2429
|
-
? 'incompatible'
|
|
2430
|
-
: 'partial',
|
|
2431
|
-
};
|
|
2432
|
-
}
|
|
2433
|
-
if ((candidate.kind === 'semantic_metric' || candidate.kind === 'semantic_member')
|
|
2434
|
-
&& (semanticEvidence.has(candidate.id) || request.selectedEvidenceId === candidate.id)
|
|
2435
|
-
&& (request.selectedEvidenceId === candidate.id
|
|
2436
|
-
|| (pack.routeDecision.route !== 'clarify' && pack.routeDecision.route !== 'conflict'))) {
|
|
2437
|
-
const requestedDimensions = pack.questionPlan.requestedShape.dimensions.map((dimension) => dimension.toLowerCase());
|
|
2438
|
-
const availableDimensions = (candidate.dimensions ?? []).map((dimension) => dimension.toLowerCase());
|
|
2439
|
-
const dimensionsFit = requestedDimensions.length === 0 || requestedDimensions.every((requested) => availableDimensions.some((available) => available === requested || available.endsWith(`.${requested}`)));
|
|
2440
|
-
const requestedTimeGrain = pack.questionPlan.timeTerms[0]?.toLowerCase();
|
|
2441
|
-
const availableTimeGrains = (candidate.timeGrains ?? []).map((grain) => grain.toLowerCase());
|
|
2442
|
-
const timeGrainFits = !requestedTimeGrain
|
|
2443
|
-
|| availableTimeGrains.includes(requestedTimeGrain);
|
|
2444
|
-
return { ...candidate, compatibility: dimensionsFit && timeGrainFits ? 'compatible' : 'partial' };
|
|
2445
|
-
}
|
|
2446
|
-
if (candidate.trustTier === 'governed_sql')
|
|
2447
|
-
return { ...candidate, compatibility: 'partial' };
|
|
2448
|
-
return candidate;
|
|
2449
|
-
}),
|
|
2450
|
-
};
|
|
2537
|
+
return applyContextPackCompatibility(evidence, pack, request.selectedEvidenceId);
|
|
2451
2538
|
};
|
|
2452
2539
|
const buildRankedAgentRunCatalogContext = async (request) => {
|
|
2453
2540
|
const evidence = await buildAgentRunEvidence(request);
|
|
@@ -4025,13 +4112,25 @@ export async function startLocalServer(opts) {
|
|
|
4025
4112
|
}
|
|
4026
4113
|
if (req.method === 'GET' && path === '/api/onboarding/launch') {
|
|
4027
4114
|
const requestId = apiRequestId('onboarding-launch');
|
|
4028
|
-
const
|
|
4115
|
+
const prefs = readUserPrefs(userPrefsPath);
|
|
4116
|
+
const acknowledgedVersion = prefs.setup?.acknowledgedVersion ?? null;
|
|
4117
|
+
const dbtAppliedVersion = prefs.setup?.dbtAppliedVersion ?? null;
|
|
4029
4118
|
const shouldOpen = acknowledgedVersion !== runtimeVersion;
|
|
4119
|
+
const configuredDbtSource = projectConfig.dbt?.repoUrl ?? projectConfig.dbt?.projectDir;
|
|
4120
|
+
const configuredDbtRoot = findDbtProjectPath(projectRoot, projectConfig);
|
|
4121
|
+
const dbtConfigured = existsSync(join(configuredDbtRoot, 'dbt_project.yml'))
|
|
4122
|
+
|| Boolean(configuredDbtSource && !/\{\{[^}]+\}\}/.test(configuredDbtSource));
|
|
4123
|
+
const requiresDbtReapply = Boolean(shouldOpen
|
|
4124
|
+
&& acknowledgedVersion
|
|
4125
|
+
&& dbtConfigured
|
|
4126
|
+
&& dbtAppliedVersion !== runtimeVersion);
|
|
4030
4127
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
4031
4128
|
res.end(serializeJSON({
|
|
4032
4129
|
requestId,
|
|
4033
4130
|
version: runtimeVersion,
|
|
4034
4131
|
acknowledgedVersion,
|
|
4132
|
+
dbtAppliedVersion,
|
|
4133
|
+
requiresDbtReapply,
|
|
4035
4134
|
shouldOpen,
|
|
4036
4135
|
reason: shouldOpen ? (acknowledgedVersion ? 'version_upgrade' : 'first_install') : null,
|
|
4037
4136
|
}));
|
|
@@ -4040,7 +4139,24 @@ export async function startLocalServer(opts) {
|
|
|
4040
4139
|
if (req.method === 'POST' && path === '/api/onboarding/acknowledge') {
|
|
4041
4140
|
const requestId = apiRequestId('onboarding-acknowledge');
|
|
4042
4141
|
const prefs = readUserPrefs(userPrefsPath);
|
|
4142
|
+
const configuredDbtSource = projectConfig.dbt?.repoUrl ?? projectConfig.dbt?.projectDir;
|
|
4143
|
+
const configuredDbtRoot = findDbtProjectPath(projectRoot, projectConfig);
|
|
4144
|
+
const dbtConfigured = existsSync(join(configuredDbtRoot, 'dbt_project.yml'))
|
|
4145
|
+
|| Boolean(configuredDbtSource && !/\{\{[^}]+\}\}/.test(configuredDbtSource));
|
|
4146
|
+
const isVersionUpgrade = Boolean(prefs.setup?.acknowledgedVersion
|
|
4147
|
+
&& prefs.setup.acknowledgedVersion !== runtimeVersion);
|
|
4148
|
+
if (isVersionUpgrade && dbtConfigured && prefs.setup?.dbtAppliedVersion !== runtimeVersion) {
|
|
4149
|
+
res.writeHead(409, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
4150
|
+
res.end(serializeJSON(apiErrorEnvelope({
|
|
4151
|
+
requestId,
|
|
4152
|
+
code: 'DBT_REAPPLY_REQUIRED',
|
|
4153
|
+
message: `Reapply the saved dbt project with DQL ${runtimeVersion} before completing the update review.`,
|
|
4154
|
+
nextActions: ['Open Project & dbt, preview the prefilled project, then choose Reapply project.'],
|
|
4155
|
+
})));
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
4043
4158
|
prefs.setup = {
|
|
4159
|
+
...prefs.setup,
|
|
4044
4160
|
acknowledgedVersion: runtimeVersion,
|
|
4045
4161
|
acknowledgedAt: new Date().toISOString(),
|
|
4046
4162
|
};
|
|
@@ -4227,6 +4343,16 @@ export async function startLocalServer(opts) {
|
|
|
4227
4343
|
await reloadSemanticLayer();
|
|
4228
4344
|
throw Object.assign(new Error(`dbt-first configuration did not compile and was rolled back: ${error instanceof Error ? error.message : String(error)}`), { code: 'SNAPSHOT_BUILD_FAILED' });
|
|
4229
4345
|
}
|
|
4346
|
+
// An OSS version upgrade is acknowledged only after the user explicitly
|
|
4347
|
+
// previews and reapplies the saved dbt project. Record that successful
|
|
4348
|
+
// user-controlled compile without replacing their other preferences.
|
|
4349
|
+
const prefs = readUserPrefs(userPrefsPath);
|
|
4350
|
+
prefs.setup = {
|
|
4351
|
+
...prefs.setup,
|
|
4352
|
+
dbtAppliedVersion: runtimeVersion,
|
|
4353
|
+
dbtAppliedAt: new Date().toISOString(),
|
|
4354
|
+
};
|
|
4355
|
+
writeUserPrefs(userPrefsPath, prefs);
|
|
4230
4356
|
const preparation = startDbtPreparationJob({
|
|
4231
4357
|
kind: 'dbt_prepare',
|
|
4232
4358
|
snapshotId,
|
|
@@ -4798,6 +4924,50 @@ export async function startLocalServer(opts) {
|
|
|
4798
4924
|
res.end(serializeJSON({ runs, total: agentRunStore.list().length, limit }));
|
|
4799
4925
|
return;
|
|
4800
4926
|
}
|
|
4927
|
+
if (req.method === 'POST' && /^\/api\/agent-runs\/[^/]+\/analytical-repair$/.test(path)) {
|
|
4928
|
+
const match = path.match(/^\/api\/agent-runs\/([^/]+)\/analytical-repair$/);
|
|
4929
|
+
const id = decodeURIComponent(match?.[1] ?? '');
|
|
4930
|
+
const run = await agentRunStore.get(id);
|
|
4931
|
+
if (!run) {
|
|
4932
|
+
res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
4933
|
+
res.end(serializeJSON({ error: 'Agent run not found.' }));
|
|
4934
|
+
return;
|
|
4935
|
+
}
|
|
4936
|
+
const source = analyticalFailedRunFromAgentRun(run);
|
|
4937
|
+
if (!source) {
|
|
4938
|
+
res.writeHead(409, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
4939
|
+
res.end(serializeJSON({ error: 'This run has no retained analytical failure artifact.' }));
|
|
4940
|
+
return;
|
|
4941
|
+
}
|
|
4942
|
+
const body = await readJSON(req).catch(() => null);
|
|
4943
|
+
const record = agentRunRecord(body);
|
|
4944
|
+
const repair = (agentRunRecord(record?.repair) ?? record);
|
|
4945
|
+
if (!repair) {
|
|
4946
|
+
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
4947
|
+
res.end(serializeJSON({ error: 'A v1 analytical repair request is required.' }));
|
|
4948
|
+
return;
|
|
4949
|
+
}
|
|
4950
|
+
// API-007: snapshot refresh is resolved by the runtime, not trusted from
|
|
4951
|
+
// browser input. projectSnapshot() recompiles when governed sources have
|
|
4952
|
+
// changed and gives the immutable identity used by the derivation.
|
|
4953
|
+
const repairWithRuntimeContext = repair.action === 'refresh_snapshot'
|
|
4954
|
+
? { ...repair, refreshedSnapshotId: projectSnapshot().snapshotId }
|
|
4955
|
+
: repair;
|
|
4956
|
+
const result = deriveAnalyticalRepair(source, repairWithRuntimeContext);
|
|
4957
|
+
if (result.status === 'blocked') {
|
|
4958
|
+
const status = result.code === 'SOURCE_FINGERPRINT_MISMATCH'
|
|
4959
|
+
? 409
|
|
4960
|
+
: result.code === 'PERMISSION_FAILURE_TERMINAL' || result.code === 'ACTION_NOT_ALLOWED'
|
|
4961
|
+
? 403
|
|
4962
|
+
: 400;
|
|
4963
|
+
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
4964
|
+
res.end(serializeJSON(result));
|
|
4965
|
+
return;
|
|
4966
|
+
}
|
|
4967
|
+
res.writeHead(201, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
4968
|
+
res.end(serializeJSON(result));
|
|
4969
|
+
return;
|
|
4970
|
+
}
|
|
4801
4971
|
if (req.method === 'POST' && /^\/api\/agent-runs\/[^/]+\/cancel$/.test(path)) {
|
|
4802
4972
|
const match = path.match(/^\/api\/agent-runs\/([^/]+)\/cancel$/);
|
|
4803
4973
|
const id = decodeURIComponent(match?.[1] ?? '');
|
|
@@ -11452,6 +11622,48 @@ export function serializeJSON(value) {
|
|
|
11452
11622
|
return current;
|
|
11453
11623
|
});
|
|
11454
11624
|
}
|
|
11625
|
+
/**
|
|
11626
|
+
* Extract the single time value returned by an authorized semantic freshness
|
|
11627
|
+
* lookup. Adapter aliases may differ by warehouse, so typed date metadata is
|
|
11628
|
+
* the only fallback; arbitrary string columns are never treated as freshness.
|
|
11629
|
+
* Acceptance: AGT-019, SEC-004.
|
|
11630
|
+
*/
|
|
11631
|
+
export function analyticalFreshnessObservedThrough(result, request) {
|
|
11632
|
+
const binding = request.authorizedAdapterRequest;
|
|
11633
|
+
if (!binding || binding.route !== 'semantic') {
|
|
11634
|
+
throw Object.assign(new Error('The freshness lookup has no authorized semantic adapter binding.'), {
|
|
11635
|
+
code: 'FRESHNESS_BINDING_REQUIRED',
|
|
11636
|
+
});
|
|
11637
|
+
}
|
|
11638
|
+
const row = result.rows[0];
|
|
11639
|
+
if (!row) {
|
|
11640
|
+
throw Object.assign(new Error('The authorized freshness lookup returned no rows.'), {
|
|
11641
|
+
code: 'FRESHNESS_OBSERVATION_MISSING',
|
|
11642
|
+
});
|
|
11643
|
+
}
|
|
11644
|
+
const normalize = (value) => value.replace(/[^a-zA-Z0-9]+/g, '_').toLowerCase();
|
|
11645
|
+
const target = normalize(binding.outputField);
|
|
11646
|
+
const exact = result.columns.find((column) => normalize(column.name) === target);
|
|
11647
|
+
const typedDates = result.columns.filter((column) => column.type === 'date' || column.type === 'datetime');
|
|
11648
|
+
const column = exact ?? (typedDates.length === 1 ? typedDates[0] : undefined);
|
|
11649
|
+
if (!column) {
|
|
11650
|
+
throw Object.assign(new Error('The authorized freshness lookup did not return one identifiable time field.'), {
|
|
11651
|
+
code: 'FRESHNESS_OBSERVATION_INVALID',
|
|
11652
|
+
});
|
|
11653
|
+
}
|
|
11654
|
+
const value = row[column.name];
|
|
11655
|
+
const instant = value instanceof Date
|
|
11656
|
+
? value
|
|
11657
|
+
: typeof value === 'string' && value.trim()
|
|
11658
|
+
? new Date(value)
|
|
11659
|
+
: undefined;
|
|
11660
|
+
if (!instant || Number.isNaN(instant.getTime())) {
|
|
11661
|
+
throw Object.assign(new Error('The authorized freshness value is not a valid instant.'), {
|
|
11662
|
+
code: 'FRESHNESS_OBSERVATION_INVALID',
|
|
11663
|
+
});
|
|
11664
|
+
}
|
|
11665
|
+
return instant.toISOString();
|
|
11666
|
+
}
|
|
11455
11667
|
/** Serialize a Skill to the shared API contract shape (spec 16). */
|
|
11456
11668
|
function serializeSkill(skill) {
|
|
11457
11669
|
return {
|
|
@@ -14123,15 +14335,21 @@ function readUserPrefs(userPrefsPath) {
|
|
|
14123
14335
|
const acknowledgedVersion = typeof raw.setup?.acknowledgedVersion === 'string'
|
|
14124
14336
|
? raw.setup.acknowledgedVersion.trim()
|
|
14125
14337
|
: '';
|
|
14338
|
+
const dbtAppliedVersion = typeof raw.setup?.dbtAppliedVersion === 'string'
|
|
14339
|
+
? raw.setup.dbtAppliedVersion.trim()
|
|
14340
|
+
: '';
|
|
14341
|
+
const setup = acknowledgedVersion || dbtAppliedVersion
|
|
14342
|
+
? {
|
|
14343
|
+
...(acknowledgedVersion ? { acknowledgedVersion } : {}),
|
|
14344
|
+
...(typeof raw.setup?.acknowledgedAt === 'string' ? { acknowledgedAt: raw.setup.acknowledgedAt } : {}),
|
|
14345
|
+
...(dbtAppliedVersion ? { dbtAppliedVersion } : {}),
|
|
14346
|
+
...(typeof raw.setup?.dbtAppliedAt === 'string' ? { dbtAppliedAt: raw.setup.dbtAppliedAt } : {}),
|
|
14347
|
+
}
|
|
14348
|
+
: undefined;
|
|
14126
14349
|
return {
|
|
14127
14350
|
favorites: Array.isArray(raw.favorites) ? raw.favorites.map(String) : [],
|
|
14128
14351
|
recentlyUsed: Array.isArray(raw.recentlyUsed) ? raw.recentlyUsed.map(String) : [],
|
|
14129
|
-
...(
|
|
14130
|
-
setup: {
|
|
14131
|
-
acknowledgedVersion,
|
|
14132
|
-
...(typeof raw.setup?.acknowledgedAt === 'string' ? { acknowledgedAt: raw.setup.acknowledgedAt } : {}),
|
|
14133
|
-
},
|
|
14134
|
-
} : {}),
|
|
14352
|
+
...(setup ? { setup } : {}),
|
|
14135
14353
|
};
|
|
14136
14354
|
}
|
|
14137
14355
|
catch {
|