@duckcodeailabs/dql-cli 1.13.3 → 1.13.5
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/apps-api.d.ts +1 -0
- package/dist/apps-api.d.ts.map +1 -1
- package/dist/apps-api.js +31 -23
- package/dist/apps-api.js.map +1 -1
- package/dist/assets/dql-notebook/assets/{AgentLogPage-B3ugEb3H.js → AgentLogPage-BzQOKjyV.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AiBuildDialog-BK_TDNcM.js → AiBuildDialog-CTxha499.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AiBuildResult-Cob_Lj05.js → AiBuildResult--MuD_I6g.js} +3 -3
- package/dist/assets/dql-notebook/assets/{AiSidePanel-CzIgxmP9.js → AiSidePanel-DcI4PMJ1.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AnalyticsHome-C2vJ2fxY.js → AnalyticsHome-Dq54vjNz.js} +1 -1
- package/dist/assets/dql-notebook/assets/AppsView-0SlwWYex.js +5595 -0
- package/dist/assets/dql-notebook/assets/{BlockStudio-CaDNTKMM.js → BlockStudio-CxxXZD3M.js} +12 -12
- package/dist/assets/dql-notebook/assets/{BusinessArtifactView-zwUOtddi.js → BusinessArtifactView-DxdAmeUN.js} +1 -1
- package/dist/assets/dql-notebook/assets/{DbtFirstModelingPage-BlnhTMu3.js → DbtFirstModelingPage-y81gFg_b.js} +4 -4
- package/dist/assets/dql-notebook/assets/{GitPage-Coevr9NJ.js → GitPage-GQtcwncb.js} +3 -3
- package/dist/assets/dql-notebook/assets/{GlobalAiRail-BTZS11x5.js → GlobalAiRail-DM4wkxR_.js} +1 -1
- package/dist/assets/dql-notebook/assets/{GovernedContextPage-CjyyM_ne.js → GovernedContextPage-B8Ft6JES.js} +3 -3
- package/dist/assets/dql-notebook/assets/{HelpDocsPage-C1UeP85v.js → HelpDocsPage-DnD4nMuF.js} +1 -1
- package/dist/assets/dql-notebook/assets/{HomePage-zGPFIvOY.js → HomePage-Ehb0ITj-.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDAG-DbXnQWu3.js → LineageDAG-Lvjc2AQX.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDetailView-Ch1Db01I.js → LineageDetailView-DX32pfbp.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDrawer-DLxqJO6D.js → LineageDrawer-CFq3jPfk.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineagePathBreadcrumb-B2tNdkr_.js → LineagePathBreadcrumb-CVHXuHls.js} +1 -1
- package/dist/assets/dql-notebook/assets/{MiniLineageGraph-0gY-ZwCy.js → MiniLineageGraph-BKaeT5Kt.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NewBlockModal-DKVkXD20.js → NewBlockModal-DI-JDzFH.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NewNotebookModal-BUIByvYT.js → NewNotebookModal-Csw6eF74.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NotebookEditor-D2fIUxjE.js → NotebookEditor-FEqs3789.js} +1 -1
- package/dist/assets/dql-notebook/assets/{ReadinessPage-Ci4LyNiQ.js → ReadinessPage-Bk0S_MEA.js} +1 -1
- package/dist/assets/dql-notebook/assets/{SetupOnboarding-BF7ai_0t.js → SetupOnboarding-DyLaPFGn.js} +1 -1
- package/dist/assets/dql-notebook/assets/{SkillsPage-BgYrgirJ.js → SkillsPage-DLHyvhci.js} +1 -1
- package/dist/assets/dql-notebook/assets/{TrustBadge-Bob1Laiq.js → TrustBadge-CIDLj2A6.js} +1 -1
- package/dist/assets/dql-notebook/assets/UnifiedAgentRunPanel-C0oKTU6G.js +88 -0
- package/dist/assets/dql-notebook/assets/{answer-to-notebook-Dk42N0pU.js → answer-to-notebook-CFEJHLvs.js} +1 -1
- package/dist/assets/dql-notebook/assets/{arrow-left-DKuuTw9l.js → arrow-left-B-Zdcyvm.js} +1 -1
- package/dist/assets/dql-notebook/assets/{arrow-right-TH8_ufKG.js → arrow-right-Cn8TM7cp.js} +1 -1
- package/dist/assets/dql-notebook/assets/{book-open-text-B-dcHHn0.js → book-open-text-Bko2NNUs.js} +1 -1
- package/dist/assets/dql-notebook/assets/{circle-x-BOc2KCZQ.js → circle-x-AWJAmUBB.js} +1 -1
- package/dist/assets/dql-notebook/assets/{dagre.esm-CPT_vn5p.js → dagre.esm-Cl_ucrRh.js} +1 -1
- package/dist/assets/dql-notebook/assets/{external-link-DtOfpYc6.js → external-link-DN57tb5f.js} +1 -1
- package/dist/assets/dql-notebook/assets/{grip-vertical-B8joDDH9.js → grip-vertical-BOWwFFva.js} +1 -1
- package/dist/assets/dql-notebook/assets/{index-DJRMz64S.js → index-Ck-wqvV2.js} +153 -153
- package/dist/assets/dql-notebook/assets/{link-2-DMZPHawg.js → link-2-xUhbfBs8.js} +1 -1
- package/dist/assets/dql-notebook/assets/{list-tree-Dw0qlxPK.js → list-tree-BDWcBr67.js} +1 -1
- package/dist/assets/dql-notebook/assets/{minimize-2-DUiSrPS3.js → minimize-2-CEeNoMTS.js} +1 -1
- package/dist/assets/dql-notebook/assets/{panel-right-open-BrrFrRZQ.js → panel-right-open-DnSeUvOY.js} +1 -1
- package/dist/assets/dql-notebook/assets/{play-Ced0HT9g.js → play-EoDj8-fa.js} +1 -1
- package/dist/assets/dql-notebook/assets/{rotate-ccw-D_9U9o7X.js → rotate-ccw-BAMxWT9A.js} +1 -1
- package/dist/assets/dql-notebook/assets/{semantic-fields-BlDOaonV.js → semantic-fields-BwLOs1kl.js} +1 -1
- package/dist/assets/dql-notebook/assets/{sliders-horizontal-BDt6KRGR.js → sliders-horizontal-BYkm8SWW.js} +1 -1
- package/dist/assets/dql-notebook/assets/{star-CxUbF_BW.js → star-sVexqNCs.js} +1 -1
- package/dist/assets/dql-notebook/assets/{triangle-alert-COWnsSdh.js → triangle-alert-D2njv4o4.js} +1 -1
- package/dist/assets/dql-notebook/assets/{upload-DSFC2kJX.js → upload-CXTMxIA8.js} +1 -1
- package/dist/assets/dql-notebook/assets/{usePersistedAgentThreadId-DYdx896E.js → usePersistedAgentThreadId-CPDeaAgL.js} +1 -1
- package/dist/assets/dql-notebook/assets/{user-round-Bfu7fosn.js → user-round-zVsI_uud.js} +1 -1
- package/dist/assets/dql-notebook/assets/{wand-sparkles-DSpG0c0j.js → wand-sparkles-BMIwIzXT.js} +1 -1
- package/dist/assets/dql-notebook/assets/{workflow-B1_pj9gq.js → workflow-Dj9y1sNk.js} +1 -1
- package/dist/assets/dql-notebook/assets/{wrench-CvfM03KZ.js → wrench-DPQi6zrs.js} +1 -1
- package/dist/assets/dql-notebook/assets/{x-B4coLiOl.js → x-XhhbtinL.js} +1 -1
- package/dist/assets/dql-notebook/index.html +1 -1
- package/dist/llm/answer-loop-tools.d.ts +3 -1
- package/dist/llm/answer-loop-tools.d.ts.map +1 -1
- package/dist/llm/answer-loop-tools.js +31 -23
- package/dist/llm/answer-loop-tools.js.map +1 -1
- package/dist/llm/providers/dql-agent-provider.d.ts +3 -1
- package/dist/llm/providers/dql-agent-provider.d.ts.map +1 -1
- package/dist/llm/providers/dql-agent-provider.js +278 -19
- package/dist/llm/providers/dql-agent-provider.js.map +1 -1
- package/dist/llm/types.d.ts +29 -2
- package/dist/llm/types.d.ts.map +1 -1
- package/dist/local-operation-coordinator.d.ts.map +1 -1
- package/dist/local-operation-coordinator.js +16 -2
- package/dist/local-operation-coordinator.js.map +1 -1
- package/dist/local-runtime.d.ts +184 -4
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +1858 -387
- package/dist/local-runtime.js.map +1 -1
- package/dist/package.json +10 -10
- package/dist/providers/oauth/claude-oauth.d.ts +1 -1
- package/dist/providers/oauth/claude-oauth.d.ts.map +1 -1
- package/dist/providers/oauth/claude-oauth.js +18 -11
- package/dist/providers/oauth/claude-oauth.js.map +1 -1
- package/dist/providers/oauth/codex-oauth.d.ts +1 -1
- package/dist/providers/oauth/codex-oauth.d.ts.map +1 -1
- package/dist/providers/oauth/codex-oauth.js +9 -1
- package/dist/providers/oauth/codex-oauth.js.map +1 -1
- package/dist/providers/subscription-cli.d.ts +1 -1
- package/dist/providers/subscription-cli.d.ts.map +1 -1
- package/dist/providers/subscription-cli.js +33 -4
- package/dist/providers/subscription-cli.js.map +1 -1
- package/package.json +10 -10
- package/dist/assets/dql-notebook/assets/AppsView-DEWQ0sHT.js +0 -5588
- package/dist/assets/dql-notebook/assets/UnifiedAgentRunPanel-IxDUKSG8.js +0 -88
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { ClaudeProvider, KGStore, MemoryStore, defaultKgPath, defaultMemoryPath, GeminiProvider, loadAgentSemanticLayer, OllamaProvider, OpenAIProvider, answer, buildAnalysisQuestionPlan, buildLocalContextPack, contextRetrievalBudgetForQuestion, ensureAgentProjectReady, isLikelyClarificationReply, isTrustedConversationTurn, } from '@duckcodeailabs/dql-agent';
|
|
1
|
+
import { ClaudeProvider, KGStore, MemoryStore, defaultKgPath, defaultMemoryPath, GeminiProvider, loadAgentSemanticLayer, OllamaProvider, OpenAIProvider, answer, buildAnalysisQuestionPlan, buildLocalContextPack, contextRetrievalBudgetForQuestion, ensureAgentProjectReady, isLikelyClarificationReply, isTrustedConversationTurn, createProviderDispatchEgressReceipt, createProviderEgressReceipt, assertProviderPayloadAllowed, prepareProviderContextForDispatch, prepareProviderWireEnvelopeForDispatch, prepareServerOwnedProviderSchemaContext, } from '@duckcodeailabs/dql-agent';
|
|
2
2
|
import { buildManifest, normalizeDqlArtifactReference, resolveDbtManifestPath } from '@duckcodeailabs/dql-core';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { buildAnswerLoopTools, createGroundingContextExpander } from '../answer-loop-tools.js';
|
|
5
|
-
import { rethrowIfCancelled } from '../cancellation.js';
|
|
6
5
|
import { getSemanticRuntimeStatus } from '../../semantic-runtime.js';
|
|
7
6
|
import { blockProposalDqlMetadata } from '../proposal-metadata.js';
|
|
8
7
|
import { getEffectiveProviderConfig } from '../../settings/provider-settings.js';
|
|
@@ -258,11 +257,126 @@ function emitProposalFromText(text, emit) {
|
|
|
258
257
|
// Ignore malformed proposal text. The visible assistant response still streams as text.
|
|
259
258
|
}
|
|
260
259
|
}
|
|
261
|
-
export function createDqlAgentProviderRunner(id) {
|
|
260
|
+
export function createDqlAgentProviderRunner(id, providerOverride) {
|
|
262
261
|
return {
|
|
263
262
|
async run(req, emit, signal) {
|
|
264
263
|
const spec = SPECS[id];
|
|
265
|
-
const
|
|
264
|
+
const rawProvider = providerOverride ?? spec.create(req.projectRoot);
|
|
265
|
+
const isResearch = req.analysisDepth === 'deep';
|
|
266
|
+
const researchRowsOptIn = isResearch && req.researchResultRowsOptIn === true;
|
|
267
|
+
const sharedDispatchEvidence = req.providerDispatchEvidenceSink;
|
|
268
|
+
let providerRoundTrips = 0;
|
|
269
|
+
let sqlExecutions = 0;
|
|
270
|
+
let pendingResultRowCount = 0;
|
|
271
|
+
let pendingColumnCount = 0;
|
|
272
|
+
let pendingCumulativeResultRowCount = 0;
|
|
273
|
+
let pendingResearchPurpose = 'research_narration';
|
|
274
|
+
const providerEgressReceipts = [];
|
|
275
|
+
const dispatchEvidence = (fallbackReason) => {
|
|
276
|
+
const shared = sharedDispatchEvidence?.snapshot(fallbackReason);
|
|
277
|
+
return shared
|
|
278
|
+
? { ...shared, sqlExecutions, fallbackReason }
|
|
279
|
+
: {
|
|
280
|
+
providerEgressReceipts: [...providerEgressReceipts],
|
|
281
|
+
providerRoundTrips,
|
|
282
|
+
toolCalls: 0,
|
|
283
|
+
sqlExecutions,
|
|
284
|
+
repairs: 0,
|
|
285
|
+
fallbackReason,
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
signal.addEventListener('abort', () => {
|
|
289
|
+
const reason = signal.reason;
|
|
290
|
+
if (reason && typeof reason === 'object') {
|
|
291
|
+
Object.assign(reason, { providerDispatchEvidence: dispatchEvidence('cancelled') });
|
|
292
|
+
}
|
|
293
|
+
}, { once: true });
|
|
294
|
+
const withPhysicalDispatchObserver = (options) => ({
|
|
295
|
+
...(options ?? {}),
|
|
296
|
+
// A tool-calling loop capped at two physical sends can make exactly one
|
|
297
|
+
// tool call before it must answer, which is not enough to look something
|
|
298
|
+
// up and then use it. The run-scoped ledger is the real guardrail.
|
|
299
|
+
maxProviderDispatches: isResearch ? 8 : 4,
|
|
300
|
+
...(sharedDispatchEvidence?.mayStartToolCall
|
|
301
|
+
? { mayStartToolCall: () => sharedDispatchEvidence.mayStartToolCall() }
|
|
302
|
+
: {}),
|
|
303
|
+
onProviderDispatch: (event) => {
|
|
304
|
+
const purpose = isResearch ? pendingResearchPurpose : 'answer_generation';
|
|
305
|
+
if (sharedDispatchEvidence) {
|
|
306
|
+
const envelope = sharedDispatchEvidence.observe(event, {
|
|
307
|
+
purpose,
|
|
308
|
+
dispatchPhase: 'generation',
|
|
309
|
+
optIn: pendingResultRowCount > 0 && researchRowsOptIn,
|
|
310
|
+
serializedResultShape: {
|
|
311
|
+
resultRowCount: pendingResultRowCount,
|
|
312
|
+
columnCount: pendingColumnCount,
|
|
313
|
+
},
|
|
314
|
+
...(pendingCumulativeResultRowCount > 0
|
|
315
|
+
? { cumulativeResultRowCount: pendingCumulativeResultRowCount }
|
|
316
|
+
: {}),
|
|
317
|
+
});
|
|
318
|
+
pendingResultRowCount = 0;
|
|
319
|
+
pendingColumnCount = 0;
|
|
320
|
+
pendingCumulativeResultRowCount = 0;
|
|
321
|
+
pendingResearchPurpose = 'research_narration';
|
|
322
|
+
return envelope;
|
|
323
|
+
}
|
|
324
|
+
// Fallback path only (CLI/MCP-direct runs carry no shared ledger).
|
|
325
|
+
// Kept in step with `maxProviderDispatches` above so the same question
|
|
326
|
+
// does not get a different budget depending on which surface asked it.
|
|
327
|
+
if (!isResearch && providerRoundTrips >= 4) {
|
|
328
|
+
throw Object.assign(new Error('Provider dispatch budget exhausted after four ordinary generation attempts.'), {
|
|
329
|
+
code: 'PROVIDER_DISPATCH_BUDGET_EXHAUSTED',
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
const envelope = prepareProviderWireEnvelopeForDispatch(rawProvider.name, event.envelope);
|
|
333
|
+
assertProviderPayloadAllowed(envelope, {
|
|
334
|
+
allowResultRows: false,
|
|
335
|
+
maxResultRows: 0,
|
|
336
|
+
purpose,
|
|
337
|
+
});
|
|
338
|
+
providerRoundTrips += 1;
|
|
339
|
+
providerEgressReceipts.push(createProviderDispatchEgressReceipt({
|
|
340
|
+
purpose,
|
|
341
|
+
dispatchPhase: 'generation',
|
|
342
|
+
provider: rawProvider.name,
|
|
343
|
+
permittedCategories: pendingResultRowCount > 0
|
|
344
|
+
? ['instructions', 'question', 'schema_metadata', 'governed_context', 'result_rows']
|
|
345
|
+
: ['instructions', 'question', 'schema_metadata', 'governed_context'],
|
|
346
|
+
optIn: pendingResultRowCount > 0 && researchRowsOptIn,
|
|
347
|
+
envelope,
|
|
348
|
+
serializedResultShape: {
|
|
349
|
+
resultRowCount: pendingResultRowCount,
|
|
350
|
+
columnCount: pendingColumnCount,
|
|
351
|
+
},
|
|
352
|
+
...(pendingCumulativeResultRowCount > 0
|
|
353
|
+
? { cumulativeResultRowCount: pendingCumulativeResultRowCount }
|
|
354
|
+
: {}),
|
|
355
|
+
}));
|
|
356
|
+
pendingResultRowCount = 0;
|
|
357
|
+
pendingColumnCount = 0;
|
|
358
|
+
pendingCumulativeResultRowCount = 0;
|
|
359
|
+
pendingResearchPurpose = 'research_narration';
|
|
360
|
+
return envelope;
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
const provider = {
|
|
364
|
+
name: rawProvider.name,
|
|
365
|
+
available: () => rawProvider.available(),
|
|
366
|
+
generate: (...args) => {
|
|
367
|
+
return rawProvider.generate(args[0], withPhysicalDispatchObserver(args[1]));
|
|
368
|
+
},
|
|
369
|
+
...(rawProvider.generateWithTools ? {
|
|
370
|
+
generateWithTools: (...args) => {
|
|
371
|
+
return rawProvider.generateWithTools(args[0], args[1], withPhysicalDispatchObserver(args[2]));
|
|
372
|
+
},
|
|
373
|
+
} : {}),
|
|
374
|
+
...(rawProvider.generateStream ? {
|
|
375
|
+
generateStream: (...args) => {
|
|
376
|
+
return rawProvider.generateStream(args[0], withPhysicalDispatchObserver(args[1]), args[2]);
|
|
377
|
+
},
|
|
378
|
+
} : {}),
|
|
379
|
+
};
|
|
266
380
|
const available = await provider.available().catch(() => false);
|
|
267
381
|
if (!available) {
|
|
268
382
|
emit({ kind: 'error', message: `${spec.label} is not configured or reachable. ${spec.setup}` });
|
|
@@ -362,7 +476,9 @@ export function createDqlAgentProviderRunner(id) {
|
|
|
362
476
|
sourcePath: skill.sourcePath ?? '',
|
|
363
477
|
}));
|
|
364
478
|
const contextDurationMs = Date.now() - contextStartedAt;
|
|
365
|
-
const answerLoopTools = buildAnswerLoopTools(req.projectRoot
|
|
479
|
+
const answerLoopTools = buildAnswerLoopTools(req.projectRoot, {
|
|
480
|
+
researchResultRowsOptIn: researchRowsOptIn,
|
|
481
|
+
});
|
|
366
482
|
const sourceSearchTool = answerLoopTools.find((tool) => tool.name === 'search_project_files');
|
|
367
483
|
const sourceSearchStartedAt = Date.now();
|
|
368
484
|
const earlySourceSearch = sourceSearchTool && shouldSearchProjectFiles(contextPack)
|
|
@@ -404,6 +520,19 @@ export function createDqlAgentProviderRunner(id) {
|
|
|
404
520
|
if (req.projectSnapshot)
|
|
405
521
|
req.assertProjectSnapshot?.(req.projectSnapshot.snapshotId);
|
|
406
522
|
};
|
|
523
|
+
assertProviderPayloadAllowed(prepareProviderContextForDispatch({
|
|
524
|
+
question,
|
|
525
|
+
...(conversationSnapshot ? { conversationSnapshot } : {}),
|
|
526
|
+
...(memoryContext ? { memoryContext } : {}),
|
|
527
|
+
schemaContext: prepareServerOwnedProviderSchemaContext(schemaContext),
|
|
528
|
+
...(contextPack ? { contextPack } : {}),
|
|
529
|
+
skills,
|
|
530
|
+
...(followUp ? { followUp } : {}),
|
|
531
|
+
}), {
|
|
532
|
+
allowResultRows: false,
|
|
533
|
+
maxResultRows: 0,
|
|
534
|
+
purpose: 'answer_generation',
|
|
535
|
+
});
|
|
407
536
|
const result = await answer({
|
|
408
537
|
question,
|
|
409
538
|
...(req.resolvedAnalyticalPlan
|
|
@@ -442,22 +571,55 @@ export function createDqlAgentProviderRunner(id) {
|
|
|
442
571
|
signal,
|
|
443
572
|
reasoningEffort: req.reasoningEffort,
|
|
444
573
|
analysisDepth: contextBudget.analysisDepth,
|
|
574
|
+
allowProviderSemanticMemberSelection: req.allowProviderSemanticMemberSelection === true,
|
|
445
575
|
...(req.semanticDriver ? { semanticDriver: req.semanticDriver } : {}),
|
|
446
576
|
...(req.semanticTableMapping ? { semanticTableMapping: req.semanticTableMapping } : {}),
|
|
447
577
|
...(req.semanticQueryCompiler ? { semanticQueryCompiler: req.semanticQueryCompiler } : {}),
|
|
448
578
|
...(req.preferredEvidenceIds?.length ? { preferredEvidenceIds: req.preferredEvidenceIds } : {}),
|
|
449
579
|
...(req.preferredExecutionId ? { preferredExecutionId: req.preferredExecutionId } : {}),
|
|
450
580
|
executeCertifiedBlock: req.executeCertifiedBlock
|
|
451
|
-
? async (...args) => { guardSnapshot(); return req.executeCertifiedBlock(...args); }
|
|
581
|
+
? async (...args) => { guardSnapshot(); sqlExecutions += 1; return req.executeCertifiedBlock(...args); }
|
|
452
582
|
: undefined,
|
|
453
583
|
executeGeneratedSql: req.executeGeneratedSql
|
|
454
|
-
? async (...args) => { guardSnapshot(); return req.executeGeneratedSql(...args); }
|
|
584
|
+
? async (...args) => { guardSnapshot(); sqlExecutions += 1; return req.executeGeneratedSql(...args); }
|
|
455
585
|
: undefined,
|
|
456
586
|
executeDqlArtifact: req.executeDqlArtifact
|
|
457
|
-
? async (...args) => { guardSnapshot(); return req.executeDqlArtifact(...args); }
|
|
587
|
+
? async (...args) => { guardSnapshot(); sqlExecutions += 1; return req.executeDqlArtifact(...args); }
|
|
458
588
|
: undefined,
|
|
459
589
|
expandGroundingContext: createGroundingContextExpander(req.projectRoot, req.probeNamedRelations),
|
|
460
590
|
answerLoopTools,
|
|
591
|
+
providerPayloadGuard: {
|
|
592
|
+
purpose: 'research_tool',
|
|
593
|
+
allowedResultRowTools: researchRowsOptIn
|
|
594
|
+
? { sample_notebook_dataset: 20, execute_local_analysis: 200 }
|
|
595
|
+
: {},
|
|
596
|
+
resultRowBudgetGroupByTool: {
|
|
597
|
+
sample_notebook_dataset: 'research_sample',
|
|
598
|
+
execute_local_analysis: 'research_local_analysis',
|
|
599
|
+
},
|
|
600
|
+
cumulativeResultRowBudgets: {
|
|
601
|
+
research_sample: 20,
|
|
602
|
+
research_local_analysis: 200,
|
|
603
|
+
},
|
|
604
|
+
onPayload: ({ toolName, output, resultRowCount, columnCount, cumulativeResultRowCount }) => {
|
|
605
|
+
pendingResearchPurpose = researchDispatchPurposeForTool(toolName);
|
|
606
|
+
pendingResultRowCount += resultRowCount;
|
|
607
|
+
pendingColumnCount = Math.max(pendingColumnCount, columnCount);
|
|
608
|
+
pendingCumulativeResultRowCount = Math.max(pendingCumulativeResultRowCount, cumulativeResultRowCount);
|
|
609
|
+
if (resultRowCount === 0)
|
|
610
|
+
return;
|
|
611
|
+
providerEgressReceipts.push(createProviderEgressReceipt({
|
|
612
|
+
purpose: researchDispatchPurposeForTool(toolName),
|
|
613
|
+
provider: provider.name,
|
|
614
|
+
permittedCategories: ['question', 'schema_metadata', 'result_rows'],
|
|
615
|
+
optIn: researchRowsOptIn,
|
|
616
|
+
payload: { toolName, output },
|
|
617
|
+
resultRowCount,
|
|
618
|
+
columnCount,
|
|
619
|
+
cumulativeResultRowCount,
|
|
620
|
+
}));
|
|
621
|
+
},
|
|
622
|
+
},
|
|
461
623
|
// NOTE: no captureGeneratedDraft here — a plain answer/research question must NOT
|
|
462
624
|
// auto-write a draft into the blocks space. A draft is created only when the user
|
|
463
625
|
// explicitly acts (the "Create DQL draft" action → the dql_block_draft route).
|
|
@@ -483,6 +645,27 @@ export function createDqlAgentProviderRunner(id) {
|
|
|
483
645
|
{ phase: 'answer_resolution', durationMs: answerDurationMs, detail: result.route?.tier ?? result.kind },
|
|
484
646
|
{ phase: 'total', durationMs: Date.now() - requestStartedAt },
|
|
485
647
|
];
|
|
648
|
+
const terminalDispatchEvidence = dispatchEvidence('none');
|
|
649
|
+
result.evidence.runtimeCounters = {
|
|
650
|
+
providerRoundTrips: terminalDispatchEvidence.providerRoundTrips,
|
|
651
|
+
toolCalls: result.evidence.toolCalls?.length ?? 0,
|
|
652
|
+
sqlExecutions,
|
|
653
|
+
repairs: result.analysisPlan?.repairAttempts ?? 0,
|
|
654
|
+
};
|
|
655
|
+
if (req.analysisDepth === 'deep' && !providerEgressReceipts.some((receipt) => receipt.purpose === 'research_tool')) {
|
|
656
|
+
providerEgressReceipts.push(createProviderEgressReceipt({
|
|
657
|
+
purpose: 'research_tool',
|
|
658
|
+
provider: provider.name,
|
|
659
|
+
permittedCategories: ['instructions', 'question', 'schema_metadata', 'governed_context'],
|
|
660
|
+
optIn: researchRowsOptIn,
|
|
661
|
+
payload: { resultRows: 0, enabled: researchRowsOptIn },
|
|
662
|
+
resultRowCount: 0,
|
|
663
|
+
columnCount: 0,
|
|
664
|
+
}));
|
|
665
|
+
}
|
|
666
|
+
result.providerEgressReceipts = sharedDispatchEvidence
|
|
667
|
+
? terminalDispatchEvidence.providerEgressReceipts
|
|
668
|
+
: providerEgressReceipts;
|
|
486
669
|
emit({ kind: 'tool_result', id: 'governed_answer', output: result });
|
|
487
670
|
emit({ kind: 'text', text: formatAgentAnswer(result) });
|
|
488
671
|
if (result.proposedSql) {
|
|
@@ -507,10 +690,41 @@ export function createDqlAgentProviderRunner(id) {
|
|
|
507
690
|
// A deadline/cancellation is NOT a provider failure. Rethrow it intact so
|
|
508
691
|
// the engine renders its graceful bounded-deadline message instead of a
|
|
509
692
|
// raw "<provider> failed: The operation was aborted due to timeout".
|
|
510
|
-
|
|
693
|
+
if (signal.aborted) {
|
|
694
|
+
const reason = signal.reason ?? err;
|
|
695
|
+
if (reason && typeof reason === 'object') {
|
|
696
|
+
Object.assign(reason, { providerDispatchEvidence: dispatchEvidence('cancelled') });
|
|
697
|
+
}
|
|
698
|
+
throw reason;
|
|
699
|
+
}
|
|
511
700
|
const message = err instanceof Error ? err.message : String(err);
|
|
701
|
+
// Both codes are DQL's OWN budget refusing to start another dispatch.
|
|
702
|
+
// Only the count-based one was recognized, so a time-based refusal was
|
|
703
|
+
// reported as "<provider> failed: The 30-second soft target elapsed" —
|
|
704
|
+
// blaming the user's AI subscription for DQL's timer and sending them
|
|
705
|
+
// to debug the wrong system entirely.
|
|
706
|
+
const budgetCode = err && typeof err === 'object'
|
|
707
|
+
? String(err.code)
|
|
708
|
+
: '';
|
|
709
|
+
const orchestrationBudgetExhausted = budgetCode === 'PROVIDER_DISPATCH_BUDGET_EXHAUSTED'
|
|
710
|
+
|| budgetCode === 'RUN_SOFT_TARGET_EXCEEDED'
|
|
711
|
+
|| budgetCode === 'RUN_DEADLINE_INSUFFICIENT';
|
|
712
|
+
// The project catalog was rebuilt underneath a run in flight. That is
|
|
713
|
+
// DQL's own concurrency, and telling the user their AI subscription
|
|
714
|
+
// failed sends them to re-authenticate a provider that worked fine.
|
|
715
|
+
const snapshotDrift = budgetCode === 'PROJECT_SNAPSHOT_MISMATCH';
|
|
512
716
|
const setupHint = shouldShowProviderSetupHint(message) ? ` ${spec.setup}` : '';
|
|
513
|
-
emit({
|
|
717
|
+
emit({
|
|
718
|
+
kind: 'error',
|
|
719
|
+
message: orchestrationBudgetExhausted
|
|
720
|
+
? `Ask stopped at its own bounded orchestration budget before a final answer was available — the AI provider did not fail. (${message})`
|
|
721
|
+
: snapshotDrift
|
|
722
|
+
? 'The project snapshot was rebuilt while this run was in flight, so DQL discarded the in-progress plan rather than answer from a stale catalog. The AI provider did not fail — retry the question.'
|
|
723
|
+
: `${spec.label} failed: ${message}.${setupHint}`,
|
|
724
|
+
dispatchEvidence: dispatchEvidence(orchestrationBudgetExhausted ? 'orchestration_budget_exhausted'
|
|
725
|
+
: snapshotDrift ? 'project_snapshot_mismatch'
|
|
726
|
+
: 'provider_error'),
|
|
727
|
+
});
|
|
514
728
|
}
|
|
515
729
|
},
|
|
516
730
|
};
|
|
@@ -663,13 +877,20 @@ export function renderExtraContext(req, followUp) {
|
|
|
663
877
|
// questions. Render it as prose with its trust instruction, and strip it
|
|
664
878
|
// from the JSON below rather than shipping the same content twice.
|
|
665
879
|
try {
|
|
666
|
-
const
|
|
667
|
-
const appContext =
|
|
880
|
+
const rawEnvelope = JSON.parse(upstream);
|
|
881
|
+
const appContext = rawEnvelope.workspaceContext?.appContext;
|
|
668
882
|
const rendered = renderAppContextForPrompt(appContext);
|
|
669
883
|
if (rendered) {
|
|
670
884
|
parts.push(rendered);
|
|
671
|
-
const { appContext: _dropped, ...restWorkspace } =
|
|
672
|
-
|
|
885
|
+
const { appContext: _dropped, ...restWorkspace } = rawEnvelope.workspaceContext ?? {};
|
|
886
|
+
const envelope = prepareProviderContextForDispatch({
|
|
887
|
+
...rawEnvelope,
|
|
888
|
+
workspaceContext: restWorkspace,
|
|
889
|
+
});
|
|
890
|
+
upstream = JSON.stringify(envelope, null, 2);
|
|
891
|
+
}
|
|
892
|
+
else {
|
|
893
|
+
upstream = JSON.stringify(prepareProviderContextForDispatch(rawEnvelope), null, 2);
|
|
673
894
|
}
|
|
674
895
|
}
|
|
675
896
|
catch {
|
|
@@ -943,6 +1164,7 @@ function resolveAgentFollowUpContextRaw(rawContext, question) {
|
|
|
943
1164
|
})),
|
|
944
1165
|
resolvedReferences: resolvedReferences.labels,
|
|
945
1166
|
unresolvedReferences: resolvedReferences.unresolved,
|
|
1167
|
+
deicticChoices: resolvedReferences.deicticChoices,
|
|
946
1168
|
// The prior turn's actual rows, so a follow-up can compute across the shown
|
|
947
1169
|
// results ("of these, the average") without a fresh query. A relative
|
|
948
1170
|
// comparison deliberately drops the prior contract, so skip it there too.
|
|
@@ -1161,7 +1383,18 @@ function resolveConversationReferences(question, turns, activeValues) {
|
|
|
1161
1383
|
];
|
|
1162
1384
|
let filters = resolveDeicticFilters(question, activeValues) ?? [];
|
|
1163
1385
|
const labels = [];
|
|
1164
|
-
|
|
1386
|
+
// An ambiguous singular reference is the single most common follow-up shape
|
|
1387
|
+
// ("what region does this customer belong to?" after a ranked list). Keep the
|
|
1388
|
+
// candidates so the run can offer them instead of dead-ending.
|
|
1389
|
+
const ambiguousSingular = !namedValues
|
|
1390
|
+
? resolveSingularDeicticCandidates(question, activeValues)
|
|
1391
|
+
: undefined;
|
|
1392
|
+
const deicticChoices = ambiguousSingular && ambiguousSingular.values.length > 1
|
|
1393
|
+
? ambiguousSingular
|
|
1394
|
+
: undefined;
|
|
1395
|
+
// Do NOT backfill an ambiguous singular from history: filtering on all of the
|
|
1396
|
+
// candidates answers a different question than the one that was asked.
|
|
1397
|
+
if (!namedValues && !deicticChoices && dimensions.length > 0 && filters.length === 0) {
|
|
1165
1398
|
for (const turn of [...turns].reverse()) {
|
|
1166
1399
|
const values = cleanStringRecordArray(cleanRecord(turn.result)?.dimensionValues);
|
|
1167
1400
|
if (!values)
|
|
@@ -1200,7 +1433,9 @@ function resolveConversationReferences(question, turns, activeValues) {
|
|
|
1200
1433
|
}]
|
|
1201
1434
|
: [];
|
|
1202
1435
|
const unresolved = referencesNeedValues(question) && filters.length === 0
|
|
1203
|
-
? [
|
|
1436
|
+
? [deicticChoices
|
|
1437
|
+
? `"${deicticChoices.dimension}" was referenced in the singular, but the previous result had ${deicticChoices.values.length} of them.`
|
|
1438
|
+
: 'Could not resolve the referenced prior result values from conversation state.']
|
|
1204
1439
|
: undefined;
|
|
1205
1440
|
return {
|
|
1206
1441
|
filters: filters.length > 0 ? Array.from(new Set(filters)).slice(0, 24) : undefined,
|
|
@@ -1209,6 +1444,7 @@ function resolveConversationReferences(question, turns, activeValues) {
|
|
|
1209
1444
|
unresolved,
|
|
1210
1445
|
valuesByDimension: namedValues,
|
|
1211
1446
|
memberBindings: memberBindings.length > 0 ? memberBindings : undefined,
|
|
1447
|
+
deicticChoices,
|
|
1212
1448
|
};
|
|
1213
1449
|
}
|
|
1214
1450
|
/**
|
|
@@ -1353,15 +1589,34 @@ function cleanStringRecordArray(value) {
|
|
|
1353
1589
|
}
|
|
1354
1590
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
1355
1591
|
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Values a singular deictic reference ("this customer") could be pointing at.
|
|
1594
|
+
*
|
|
1595
|
+
* This used to `.slice(0, 1)` — silently picking the FIRST row of the previous
|
|
1596
|
+
* answer and binding it as a required filter. After a ten-row ranking, "this
|
|
1597
|
+
* customer" would quietly become "Melissa Lopez" because she happened to sort
|
|
1598
|
+
* first, and the user was never told. An ambiguous reference must stay
|
|
1599
|
+
* ambiguous so the caller can ask which one was meant.
|
|
1600
|
+
*/
|
|
1601
|
+
function resolveSingularDeicticCandidates(question, priorValues) {
|
|
1602
|
+
if (!priorValues)
|
|
1603
|
+
return undefined;
|
|
1604
|
+
const singular = resolveSingularDeicticDimension(question, priorValues);
|
|
1605
|
+
if (!singular)
|
|
1606
|
+
return undefined;
|
|
1607
|
+
const values = Array.from(new Set(valuesForPriorDimension(priorValues, singular))).slice(0, 24);
|
|
1608
|
+
return values.length > 0 ? { dimension: singular, values } : undefined;
|
|
1609
|
+
}
|
|
1356
1610
|
function resolveDeicticFilters(question, priorValues) {
|
|
1357
1611
|
if (!priorValues)
|
|
1358
1612
|
return undefined;
|
|
1359
1613
|
const hasExplicitPluralReference = /\b(?:these|those|same)\s+(?:customers?|products?|cat(?:egor|agor|ogor)(?:y|ies)|segments?|regions?)\b/i.test(question);
|
|
1360
1614
|
if (!hasExplicitPluralReference) {
|
|
1361
|
-
const singular =
|
|
1615
|
+
const singular = resolveSingularDeicticCandidates(question, priorValues);
|
|
1362
1616
|
if (singular) {
|
|
1363
|
-
|
|
1364
|
-
|
|
1617
|
+
// Exactly one candidate is unambiguous and can be bound as a filter.
|
|
1618
|
+
// Several candidates are a question for the user, not a coin flip.
|
|
1619
|
+
return singular.values.length === 1 ? singular.values : undefined;
|
|
1365
1620
|
}
|
|
1366
1621
|
}
|
|
1367
1622
|
const dims = resolveDeicticDimensions(question, priorValues) ?? [];
|
|
@@ -1499,7 +1754,11 @@ export const __test__ = {
|
|
|
1499
1754
|
shouldLoadSchemaContext,
|
|
1500
1755
|
shouldSearchProjectFiles,
|
|
1501
1756
|
renderProjectSourceSearch,
|
|
1757
|
+
researchDispatchPurposeForTool,
|
|
1502
1758
|
};
|
|
1759
|
+
function researchDispatchPurposeForTool(toolName) {
|
|
1760
|
+
return toolName === 'execute_local_analysis' ? 'research_tool' : 'research_narration';
|
|
1761
|
+
}
|
|
1503
1762
|
const GENERIC_FOLLOW_UP_WORDS = new Set([
|
|
1504
1763
|
'a', 'about', 'again', 'all', 'also', 'and', 'are', 'as', 'be', 'block', 'can', 'could', 'data', 'did', 'do', 'does',
|
|
1505
1764
|
'execute', 'for', 'from', 'get', 'give', 'import', 'it', 'its', 'let', 'lets', 'me', 'metrics',
|