@duckcodeailabs/dql-cli 1.8.13 → 1.9.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.
@@ -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, 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, 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';
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';
@@ -33,8 +33,8 @@ import { buildSemanticObjectDetail, buildSemanticTree, computeSyncDiff, loadSema
33
33
  import { clearBlockStudioImportSessions, candidateToDqlSource, createBlockStudioImportSession, deleteBlockStudioImportSession, listBlockStudioImportSessions, loadBlockStudioImportSession, readBlockStudioImportCandidate, parameterizeSqlForDqlImport, updateBlockStudioImportCandidate, writeBlockStudioImportSession, writeBlockStudioImportCandidate, } from './block-studio-import.js';
34
34
  import { MetricFlowUnavailableError, hasDbtSemanticManifest, } from "./metricflow.js";
35
35
  import { ManagedMetricFlowInstaller, isMetricFlowWarehouseAdapter, metricFlowAdapterForDriver, } from './metricflow-installer.js';
36
- import { compileSemanticRuntimeQuery, getSemanticRuntimeStatus, isSemanticRuntimeError, listRuntimeCompatibleDimensions, semanticMetricExecutionCapability as runtimeMetricExecutionCapability, SemanticRuntimeRequiredError, testSemanticRuntimeDraft, } from './semantic-runtime.js';
37
- import { getSemanticRuntimeSettings, saveTestedSemanticRuntimeSettings, } from './semantic-runtime-settings.js';
36
+ import { compileSemanticRuntimeQuery, describeRuntimeCompatibility, getSemanticRuntimeStatus, isSemanticRuntimeError, semanticMetricExecutionCapability as runtimeMetricExecutionCapability, SemanticRuntimeRequiredError, testSemanticRuntimeDraft, } from './semantic-runtime.js';
37
+ import { getSemanticRuntimeSettings, saveTestedSemanticRuntimeSettings, saveSemanticRuntimePreference, } from './semantic-runtime-settings.js';
38
38
  import { NotebookDatasetWorkspace, } from "./notebook-datasets.js";
39
39
  import { prepareBlockInvocation } from './block-invocation.js';
40
40
  const NOTEBOOK_EXECUTE_PREVIEW_ROW_LIMIT = 500;
@@ -204,15 +204,54 @@ function agentColumnDisplayFormats(projectRoot, columns) {
204
204
  * no env override. The defaults must fit the transport's physics. */
205
205
  const AGENT_CLI_LOOKUP_DEADLINE_MS = 150_000;
206
206
  const AGENT_CLI_RESEARCH_DEADLINE_MS = 300_000;
207
+ /** High reasoning effort makes every LLM call materially slower, so the same
208
+ * multi-step build that fits a budget at medium effort overruns it at high. The
209
+ * budget must scale with effort or "High · thorough" reliably times out. */
210
+ function reasoningEffortBudgetMultiplier(effort) {
211
+ return effort === 'high' ? 1.8 : effort === 'low' ? 0.9 : 1;
212
+ }
213
+ /**
214
+ * A ranking/breakdown/filtered question needs LLM SQL generation (the grain
215
+ * dimension usually lives on a joined model the semantic layer can't compose),
216
+ * and that multi-call build does NOT fit the bare lookup budget on a slow or
217
+ * high-effort provider — it dead-ends at the deadline. It is not "research", but
218
+ * it needs research-sized time to finish. Detected from the requested SHAPE (a
219
+ * top-N, a member filter, a breakdown dimension, or a time window) rather than a
220
+ * recognized metric word, since office questions like "top bcm customers from
221
+ * the south region" name no metric the planner knows yet. A bare scalar
222
+ * ("total revenue") has none of these and stays on the short budget.
223
+ */
224
+ function isAnalyticalBuildQuestion(plan) {
225
+ if (!plan.needsGeneratedSql)
226
+ return false;
227
+ return plan.requestedShape.dimensions.length > 0
228
+ || plan.dimensionTerms.length > 0
229
+ || plan.requestedShape.filters.length > 0
230
+ || plan.timeTerms.length > 0
231
+ || Boolean(plan.requestedShape.topN);
232
+ }
207
233
  export function agentRunDeadlineMs(request, env = process.env, activeProviderId) {
208
234
  const cliProvider = activeProviderId === 'claude-code' || activeProviderId === 'codex';
209
235
  const lookupDeadline = resolveAgentDeadlineMs('DQL_AGENT_LOOKUP_DEADLINE_MS', cliProvider ? AGENT_CLI_LOOKUP_DEADLINE_MS : AGENT_LOOKUP_DEADLINE_MS, env);
210
236
  const researchDeadline = resolveAgentDeadlineMs('DQL_AGENT_RESEARCH_DEADLINE_MS', cliProvider ? AGENT_CLI_RESEARCH_DEADLINE_MS : AGENT_RESEARCH_DEADLINE_MS, env);
211
- if (request.requestedMode === 'research' || request.analysisDepth === 'deep') {
212
- return researchDeadline;
237
+ // The "Thinking" control sends a thinkingMode; explicit reasoningEffort/
238
+ // analysisDepth (CLI flags) take precedence but the mode is the common case.
239
+ // The deadline MUST see the resolved values — "High" both slows every call
240
+ // AND means deep — or picking High reliably times out at the lookup budget.
241
+ const resolvedMode = request.thinkingMode ? resolveThinkingMode(request.thinkingMode) : {};
242
+ const effectiveEffort = request.reasoningEffort ?? resolvedMode.reasoningEffort;
243
+ const effectiveDepth = request.analysisDepth ?? resolvedMode.analysisDepth;
244
+ // The budget is a CEILING, not a target — a fast answer returns immediately;
245
+ // the larger tiers only matter when the build genuinely needs the time.
246
+ const scale = (ms) => Math.min(600_000, Math.round(ms * reasoningEffortBudgetMultiplier(effectiveEffort)));
247
+ if (request.requestedMode === 'research' || effectiveDepth === 'deep') {
248
+ return scale(researchDeadline);
213
249
  }
214
250
  const plan = buildAnalysisQuestionPlan(request.question);
215
- return plan.needsResearchWorkspace ? researchDeadline : lookupDeadline;
251
+ if (plan.needsResearchWorkspace || isAnalyticalBuildQuestion(plan)) {
252
+ return scale(researchDeadline);
253
+ }
254
+ return scale(lookupDeadline);
216
255
  }
217
256
  export function shouldSynthesizeAgentRunAnswer(governedAnswer) {
218
257
  if (governedAnswer.kind === 'no_answer')
@@ -344,9 +383,12 @@ export function conversationTurnInputFromRun(run) {
344
383
  // value window. Deriving dimensions from only the eight preview rows caused a
345
384
  // valid row 9/10 member to disappear before the next turn (AGT-012/E2E-010).
346
385
  const memberRows = Array.isArray(result?.rows)
347
- ? result.rows.filter((row) => Boolean(row && typeof row === 'object' && !Array.isArray(row))).slice(0, 24)
386
+ ? result.rows.filter((row) => Boolean(row && typeof row === 'object' && !Array.isArray(row))).slice(0, 50)
348
387
  : [];
349
- const rows = memberRows.slice(0, 8);
388
+ // The full retained window feeds both member resolution and cross-result
389
+ // follow-up compute ("of the results above, the average"). The store clamps
390
+ // it (MAX_SAMPLE_ROWS); rows are never dumped into the prompt.
391
+ const rows = memberRows;
350
392
  const rowsSample = rows.map((row) => columns.map((column) => row[column]));
351
393
  const contextPack = agentRunRecord(payload?.contextPack);
352
394
  const questionPlan = agentRunRecord(contextPack?.questionPlan);
@@ -8549,8 +8591,11 @@ export async function startLocalServer(opts) {
8549
8591
  res.end(serializeJSON(preview));
8550
8592
  }
8551
8593
  catch (error) {
8594
+ const raw = error instanceof Error ? error.message : String(error);
8552
8595
  res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
8553
- res.end(serializeJSON({ error: error instanceof Error ? error.message : String(error) }));
8596
+ // `error` stays the full compiler output (the UI shows it in a
8597
+ // collapsible); `friendlyMessage` is the one actionable sentence.
8598
+ res.end(serializeJSON({ error: raw, friendlyMessage: compactSemanticRuntimeFailure(raw) }));
8554
8599
  }
8555
8600
  return;
8556
8601
  }
@@ -9095,6 +9140,26 @@ export async function startLocalServer(opts) {
9095
9140
  }
9096
9141
  return;
9097
9142
  }
9143
+ // Set the runtime preference alone (no dbt Cloud test required), so a user
9144
+ // can prefer Native / Local MetricFlow / dbt Cloud explicitly.
9145
+ if (req.method === 'POST' && path === '/api/semantic-runtime/preference') {
9146
+ try {
9147
+ const body = await readJSON(req);
9148
+ const preference = body.preference;
9149
+ if (preference !== 'auto' && preference !== 'native' && preference !== 'metricflow-cli' && preference !== 'dbt-cloud') {
9150
+ throw new Error('preference must be one of: auto, native, metricflow-cli, dbt-cloud');
9151
+ }
9152
+ const settings = saveSemanticRuntimePreference(projectRoot, preference);
9153
+ const runtime = await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true });
9154
+ res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
9155
+ res.end(serializeJSON({ ok: true, ...settings, runtime }));
9156
+ }
9157
+ catch (error) {
9158
+ res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
9159
+ res.end(serializeJSON({ ok: false, error: error instanceof Error ? error.message : String(error) }));
9160
+ }
9161
+ return;
9162
+ }
9098
9163
  // ── Semantic layer discovery API ─────────────────────────────────────────
9099
9164
  if (req.method === 'GET' && path === '/api/semantic-layer') {
9100
9165
  const userPrefs = readUserPrefs(userPrefsPath);
@@ -9588,7 +9653,8 @@ export async function startLocalServer(opts) {
9588
9653
  .split(',')
9589
9654
  .map((value) => value.trim())
9590
9655
  .filter(Boolean);
9591
- const dimensions = (await listRuntimeCompatibleDimensions(projectRoot, semanticLayer, metrics)).map((d) => ({
9656
+ const compat = await describeRuntimeCompatibility(projectRoot, semanticLayer, metrics, projectConfig);
9657
+ const dimensions = compat.dimensions.map((d) => ({
9592
9658
  name: d.name,
9593
9659
  label: d.label,
9594
9660
  description: d.description,
@@ -9598,9 +9664,19 @@ export async function startLocalServer(opts) {
9598
9664
  table: d.table,
9599
9665
  tags: d.tags ?? [],
9600
9666
  owner: d.owner ?? null,
9667
+ // Additive: qualified name, real grains, and time flag (existing fields
9668
+ // above are byte-identical for compatibility).
9669
+ qualifiedName: d.qualifiedName ?? null,
9670
+ granularities: d.granularities ?? d.granularities ?? null,
9671
+ isTimeDimension: Boolean(d.isTimeDimension),
9601
9672
  }));
9602
9673
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
9603
- res.end(serializeJSON({ dimensions }));
9674
+ res.end(serializeJSON({
9675
+ dimensions,
9676
+ engine: compat.engine,
9677
+ incompatible: compat.incompatible,
9678
+ degraded: compat.degraded ?? null,
9679
+ }));
9604
9680
  return;
9605
9681
  }
9606
9682
  if (req.method === 'GET' && path === '/api/user-prefs/favorites') {