@duckcodeailabs/dql-cli 1.13.5 → 1.14.0
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/args.d.ts +2 -0
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +5 -0
- package/dist/args.js.map +1 -1
- package/dist/assets/dql-notebook/assets/{AgentLogPage-BzQOKjyV.js → AgentLogPage-Ch7VK20X.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AiBuildDialog-CTxha499.js → AiBuildDialog-DBr5TmyM.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AiBuildResult--MuD_I6g.js → AiBuildResult-jLPzQO7O.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AiSidePanel-DcI4PMJ1.js → AiSidePanel-CdlGsiVC.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AnalyticsHome-Dq54vjNz.js → AnalyticsHome-C0DXbOwY.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AppsView-0SlwWYex.js → AppsView-CcpwjApv.js} +4 -4
- package/dist/assets/dql-notebook/assets/{BlockStudio-CxxXZD3M.js → BlockStudio-CFYxafw-.js} +1 -1
- package/dist/assets/dql-notebook/assets/{BusinessArtifactView-DxdAmeUN.js → BusinessArtifactView-C0kLYg2p.js} +1 -1
- package/dist/assets/dql-notebook/assets/{DbtFirstModelingPage-y81gFg_b.js → DbtFirstModelingPage-CMwElXD_.js} +1 -1
- package/dist/assets/dql-notebook/assets/{GitPage-GQtcwncb.js → GitPage-JjhRDeWY.js} +1 -1
- package/dist/assets/dql-notebook/assets/{GlobalAiRail-DM4wkxR_.js → GlobalAiRail-DakE4NdR.js} +1 -1
- package/dist/assets/dql-notebook/assets/{GovernedContextPage-B8Ft6JES.js → GovernedContextPage-BokDqG6a.js} +1 -1
- package/dist/assets/dql-notebook/assets/{HelpDocsPage-DnD4nMuF.js → HelpDocsPage-CjOv6_gz.js} +1 -1
- package/dist/assets/dql-notebook/assets/{HomePage-Ehb0ITj-.js → HomePage-nGaNcwdw.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDAG-Lvjc2AQX.js → LineageDAG-CO6CFJRg.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDetailView-DX32pfbp.js → LineageDetailView-BnGb7OF7.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDrawer-CFq3jPfk.js → LineageDrawer-C5Y0Ht0b.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineagePathBreadcrumb-CVHXuHls.js → LineagePathBreadcrumb-Cgh3GR3F.js} +1 -1
- package/dist/assets/dql-notebook/assets/{MiniLineageGraph-BKaeT5Kt.js → MiniLineageGraph-Kla9PYuj.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NewBlockModal-DI-JDzFH.js → NewBlockModal-BR2SnmPT.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NewNotebookModal-Csw6eF74.js → NewNotebookModal-BaCHMYeB.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NotebookEditor-FEqs3789.js → NotebookEditor-CBLqY8cE.js} +1 -1
- package/dist/assets/dql-notebook/assets/{ReadinessPage-Bk0S_MEA.js → ReadinessPage-CiN0IWSS.js} +1 -1
- package/dist/assets/dql-notebook/assets/{SetupOnboarding-DyLaPFGn.js → SetupOnboarding-BhiCsYF-.js} +1 -1
- package/dist/assets/dql-notebook/assets/{SkillsPage-DLHyvhci.js → SkillsPage-CCSf8VMm.js} +1 -1
- package/dist/assets/dql-notebook/assets/{TrustBadge-CIDLj2A6.js → TrustBadge-BgQmFe_x.js} +1 -1
- package/dist/assets/dql-notebook/assets/UnifiedAgentRunPanel-Bw5AXNDB.js +88 -0
- package/dist/assets/dql-notebook/assets/{answer-to-notebook-CFEJHLvs.js → answer-to-notebook-AeDYUDla.js} +1 -1
- package/dist/assets/dql-notebook/assets/{arrow-left-B-Zdcyvm.js → arrow-left-C55x2hq_.js} +1 -1
- package/dist/assets/dql-notebook/assets/{arrow-right-Cn8TM7cp.js → arrow-right-C1cJrhOm.js} +1 -1
- package/dist/assets/dql-notebook/assets/{book-open-text-Bko2NNUs.js → book-open-text-CQf_sdv2.js} +1 -1
- package/dist/assets/dql-notebook/assets/{circle-x-AWJAmUBB.js → circle-x-X8-Z2yLY.js} +1 -1
- package/dist/assets/dql-notebook/assets/{dagre.esm-Cl_ucrRh.js → dagre.esm-BjjNYKyY.js} +1 -1
- package/dist/assets/dql-notebook/assets/{external-link-DN57tb5f.js → external-link-C2zrz5DH.js} +1 -1
- package/dist/assets/dql-notebook/assets/{grip-vertical-BOWwFFva.js → grip-vertical-qGV_PYGU.js} +1 -1
- package/dist/assets/dql-notebook/assets/{index-Ck-wqvV2.js → index-ByTDPDaH.js} +4 -4
- package/dist/assets/dql-notebook/assets/{link-2-xUhbfBs8.js → link-2-VpyOxQXG.js} +1 -1
- package/dist/assets/dql-notebook/assets/{list-tree-BDWcBr67.js → list-tree-CH2Jhwms.js} +1 -1
- package/dist/assets/dql-notebook/assets/{minimize-2-CEeNoMTS.js → minimize-2-B2TZJ8BT.js} +1 -1
- package/dist/assets/dql-notebook/assets/{panel-right-open-DnSeUvOY.js → panel-right-open-BunF88lt.js} +1 -1
- package/dist/assets/dql-notebook/assets/{play-EoDj8-fa.js → play-DAFVF4_G.js} +1 -1
- package/dist/assets/dql-notebook/assets/{rotate-ccw-BAMxWT9A.js → rotate-ccw-DGCrrqtY.js} +1 -1
- package/dist/assets/dql-notebook/assets/{semantic-fields-BwLOs1kl.js → semantic-fields-Ci-9QL9F.js} +1 -1
- package/dist/assets/dql-notebook/assets/{sliders-horizontal-BYkm8SWW.js → sliders-horizontal-BOAlXXbn.js} +1 -1
- package/dist/assets/dql-notebook/assets/{star-sVexqNCs.js → star-CkksZXHt.js} +1 -1
- package/dist/assets/dql-notebook/assets/{triangle-alert-D2njv4o4.js → triangle-alert-D3mjyJZE.js} +1 -1
- package/dist/assets/dql-notebook/assets/{upload-CXTMxIA8.js → upload-CTNOAVEO.js} +1 -1
- package/dist/assets/dql-notebook/assets/{usePersistedAgentThreadId-CPDeaAgL.js → usePersistedAgentThreadId-DiQjc7x-.js} +4 -4
- package/dist/assets/dql-notebook/assets/{user-round-zVsI_uud.js → user-round-BWd5tQRg.js} +1 -1
- package/dist/assets/dql-notebook/assets/{wand-sparkles-BMIwIzXT.js → wand-sparkles-CqsAv8P-.js} +1 -1
- package/dist/assets/dql-notebook/assets/{workflow-Dj9y1sNk.js → workflow-CSqsj-sC.js} +1 -1
- package/dist/assets/dql-notebook/assets/{wrench-DPQi6zrs.js → wrench-DWqzqlX8.js} +1 -1
- package/dist/assets/dql-notebook/assets/{x-XhhbtinL.js → x-65M5rLCE.js} +1 -1
- package/dist/assets/dql-notebook/index.html +1 -1
- package/dist/commands/eval.d.ts +15 -1
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +32 -3
- package/dist/commands/eval.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/llm/providers/dql-agent-provider.d.ts.map +1 -1
- package/dist/llm/providers/dql-agent-provider.js +37 -3
- package/dist/llm/providers/dql-agent-provider.js.map +1 -1
- package/dist/local-runtime.d.ts +25 -2
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +508 -88
- package/dist/local-runtime.js.map +1 -1
- package/dist/package.json +10 -10
- package/package.json +10 -10
- package/dist/assets/dql-notebook/assets/UnifiedAgentRunPanel-C0oKTU6G.js +0 -88
package/dist/local-runtime.js
CHANGED
|
@@ -25,7 +25,7 @@ import { fetchLatestPublishedDqlVersion, resolveDqlRuntimeVersionStatus } from '
|
|
|
25
25
|
import { resolveRetrievalHealthStatus } from './retrieval-health.js';
|
|
26
26
|
import { createDqlAgentProviderRunner, resolveAgentFollowUpContext } from './llm/providers/dql-agent-provider.js';
|
|
27
27
|
import { listRemoteMcpSettings, saveRemoteMcpSettings } from './llm/mcp-config.js';
|
|
28
|
-
import { ClaudeProvider, ConversationStore, advanceThreadState, buildConversationSnapshot, conversationHistoryFromContext, recallRelevantTurns, renderConversationEnvelopeForPrompt, GeminiProvider, MemoryStore, OllamaProvider, OpenAIProvider, buildBlockBusinessFingerprint, buildBlockSqlFingerprints, buildAnalysisQuestionPlan, composeSemanticQueryForQuestion, aggregationIntegrityIssuesForSql, buildAggregationSafetyProof, buildLocalContextPack, applyContextPackCompatibility, toAgentRetrievalEvidence, prepareConversationPath, defaultMemoryPath, ensureDefaultMemoryFiles, ensureAgentProjectReady, isAgentProjectIndexReady, currentMetadataFingerprint, ensureMetadataCatalogFresh, readIndexedDomainKnowledge, readIndexedKnowledge360, compactSemanticRuntimeFailure, classifyAnalyticalFailure, normalizeWarehouseSqlFailure, parseProposal, propose, proposePlan, recordGovernedCorrection, HintStore, defaultHintIndexPath, ensureHintIndexFresh, listHintsFromGit, getHintEvaluationFromGit, getCorrectionTraceFromGit, inspectGovernedHint, editGovernedHintCandidate, reopenGovernedHint, retireHint, supersedeHint, hintsConflict, mineJoinPatterns, reviewGovernedHint, AgentRunEngine, SqliteAgentRunStore, defaultAgentRunGates, createLlmAgentRunPlanner, createHybridRouter, computeResultStats, buildDeterministicDashboardStory, synthesizeAnswer, streamOrGenerate, narrateResult, buildProposePreview, buildFromPrompt, internalRelationIdsInSql, defaultAgentRunStorePath, defaultAgentRunSqlitePath, resolveLocalOwner, resolveProposeConfig, recordQueryRun, recordRuntimeSchemaSnapshot, latestRuntimeSchemaSnapshotForProject, loadSkills, migrateLegacySkills, configuredSkillsPath, skillsDir, draftDomainSkillBootstrap, buildDomainSkillBootstrapPrompt, mergeDomainSkillBootstrapEnrichment, writeSkill, previewSkillChange, buildContextAuthoringProposal, contextAuthoringDependencyClosure, FileContextAuthoringProposalStore, deleteSkill, deriveGeneratedDraftSlug, deriveAnalyticalRepair, reindexProject, invalidateAgentProjectState, recordAgentRuntimeVersion, resolveDomainContextEnvelope, projectEmbeddingProvider, isHashedEmbeddingProvider, clearProjectEmbeddingCache, upgradeVectorIndexForProject, openMetadataCatalog, defaultKgPath, planAppFromPrompt, KGStore, planResearch, loadSemanticMetrics, cascadeTraceToEvidenceRouteSteps, createCascadeAnswerResult, createCascadeTrace, routeReasoningEffort, createAgentRunBudget, routeForCascadeAnswerTier, clampReasoningEffort, bumpReasoningEffort, resolveThinkingMode, coerceThinkingMode, upsertGeneratedDqlArtifactDraft, loadAgentSemanticLayer, isTrustedConversationTurn, resolveInternalRelationIds, analyticalError, tagAnalyticalError, withAnalyticalErrorOrigin, withAnalyticalErrorOriginSync, assertProviderPayloadAllowed, createProviderDispatchEgressReceipt, prepareProviderWireEnvelopeForDispatch, markProviderMetadataArray, createProviderEgressReceipt, redactProviderResultRows, composeVerifiedAnalyticalNarrative, DEFAULT_ASK_ROW_EGRESS_POLICY, ZERO_ROW_EGRESS_POLICY, resolveProviderResultRowEgressPolicy, } from '@duckcodeailabs/dql-agent';
|
|
28
|
+
import { ClaudeProvider, ConversationStore, advanceThreadState, buildConversationSnapshot, conversationHistoryFromContext, recallRelevantTurns, renderConversationEnvelopeForPrompt, GeminiProvider, MemoryStore, OllamaProvider, OpenAIProvider, buildBlockBusinessFingerprint, buildBlockSqlFingerprints, buildAnalysisQuestionPlan, composeSemanticQueryForQuestion, aggregationIntegrityIssuesForSql, buildAggregationSafetyProof, buildLocalContextPack, applyContextPackCompatibility, toAgentRetrievalEvidence, prepareConversationPath, defaultMemoryPath, ensureDefaultMemoryFiles, ensureAgentProjectReady, isAgentProjectIndexReady, currentMetadataFingerprint, ensureMetadataCatalogFresh, readIndexedDomainKnowledge, readIndexedKnowledge360, compactSemanticRuntimeFailure, classifyAnalyticalFailure, normalizeWarehouseSqlFailure, parseProposal, propose, proposePlan, recordGovernedCorrection, HintStore, defaultHintIndexPath, ensureHintIndexFresh, listHintsFromGit, getHintEvaluationFromGit, getCorrectionTraceFromGit, inspectGovernedHint, editGovernedHintCandidate, reopenGovernedHint, retireHint, supersedeHint, hintsConflict, mineJoinPatterns, reviewGovernedHint, AgentRunEngine, SqliteAgentRunStore, defaultAgentRunGates, createLlmAgentRunPlanner, createHybridRouter, computeResultStats, buildDeterministicDashboardStory, synthesizeAnswer, streamOrGenerate, narrateResult, buildProposePreview, buildFromPrompt, internalRelationIdsInSql, defaultAgentRunStorePath, defaultAgentRunSqlitePath, resolveLocalOwner, resolveProposeConfig, recordQueryRun, recordRuntimeSchemaSnapshot, latestRuntimeSchemaSnapshotForProject, loadSkills, migrateLegacySkills, configuredSkillsPath, skillsDir, draftDomainSkillBootstrap, buildDomainSkillBootstrapPrompt, mergeDomainSkillBootstrapEnrichment, writeSkill, previewSkillChange, buildContextAuthoringProposal, contextAuthoringDependencyClosure, FileContextAuthoringProposalStore, deleteSkill, deriveGeneratedDraftSlug, deriveAnalyticalRepair, reindexProject, invalidateAgentProjectState, recordAgentRuntimeVersion, resolveDomainContextEnvelope, projectEmbeddingProvider, isHashedEmbeddingProvider, clearProjectEmbeddingCache, upgradeVectorIndexForProject, openMetadataCatalog, defaultKgPath, planAppFromPrompt, KGStore, planResearch, loadSemanticMetrics, cascadeTraceToEvidenceRouteSteps, createCascadeAnswerResult, createCascadeTrace, routeReasoningEffort, createAgentRunBudget, routeForCascadeAnswerTier, clampReasoningEffort, bumpReasoningEffort, resolveThinkingMode, coerceThinkingMode, upsertGeneratedDqlArtifactDraft, loadAgentSemanticLayer, isTrustedConversationTurn, resolveInternalRelationIds, analyticalError, tagAnalyticalError, withAnalyticalErrorOrigin, withAnalyticalErrorOriginSync, assertProviderPayloadAllowed, createProviderDispatchEgressReceipt, prepareProviderWireEnvelopeForDispatch, markProviderMetadataArray, createProviderEgressReceipt, redactProviderResultRows, composeVerifiedAnalyticalNarrative, buildCoverageGap, capResearchBranches, buildResearchEvidenceLedger, buildAnalyticalTurnPlan, DEFAULT_ASK_ROW_EGRESS_POLICY, ZERO_ROW_EGRESS_POLICY, resolveProviderResultRowEgressPolicy, normalizeCanonicalQueryResult, normalizeAnalyticalExecutionFingerprint, normalizeAnalyticalExecutionReceipt, createAgentRunCancellationError, } from '@duckcodeailabs/dql-agent';
|
|
29
29
|
import { addSqlResultFilter, dashboardFilterableResultColumns, filterableResultColumns, replaceBlockStudioSql } from './sql-result-filter.js';
|
|
30
30
|
import { gatherProposeEnrichment } from './propose-enrich.js';
|
|
31
31
|
import { handleAppsApi, proposeAppAiBuild, recommendVisualization, } from './apps-api.js';
|
|
@@ -564,7 +564,7 @@ export function validAskRepairDqlWrapper(source) {
|
|
|
564
564
|
}
|
|
565
565
|
/** Build retained automatic-repair authority from analytical failure state only. */
|
|
566
566
|
export function analyticalRepairCapabilityForAgentRun(run, resolvedTargetFingerprint) {
|
|
567
|
-
if (run.status !== 'blocked')
|
|
567
|
+
if (run.status !== 'blocked' || run.stopReason !== 'blocked')
|
|
568
568
|
return undefined;
|
|
569
569
|
const failedRun = analyticalFailedRunFromAgentRun(run);
|
|
570
570
|
const failure = failedRun?.failure;
|
|
@@ -963,6 +963,33 @@ export function slimAgentRunForTransport(run) {
|
|
|
963
963
|
: {}),
|
|
964
964
|
};
|
|
965
965
|
}
|
|
966
|
+
/**
|
|
967
|
+
* INDEX projection for `GET /api/agent-runs` — strictly lighter than the
|
|
968
|
+
* presentation projection above.
|
|
969
|
+
*
|
|
970
|
+
* A run history list renders one ROW per run: question, route, status, trust,
|
|
971
|
+
* timing, summary. It never renders an answer body, an event stream, or a step
|
|
972
|
+
* trace. Shipping those anyway dominated the response: on 300 real stored runs
|
|
973
|
+
* a 20-run page was 47.61 MB whole and still 6.80 MB under the presentation
|
|
974
|
+
* projection, of which 6.35 MB was `artifacts[].payload` alone.
|
|
975
|
+
*
|
|
976
|
+
* Artifact identity (`id`/`kind`/`title`/`trustState`) is kept so a row can say
|
|
977
|
+
* what it produced; only the payload body is dropped. The complete immutable
|
|
978
|
+
* record stays available from `GET /api/agent-runs/:id`.
|
|
979
|
+
*
|
|
980
|
+
* Acceptance: PERF-003, E2E-022.
|
|
981
|
+
*/
|
|
982
|
+
export function agentRunListEntryForTransport(run) {
|
|
983
|
+
const slim = slimAgentRunForTransport(run);
|
|
984
|
+
const artifacts = (slim.artifacts ?? []).map((artifact) => {
|
|
985
|
+
const record = agentRunRecord(artifact);
|
|
986
|
+
if (!record)
|
|
987
|
+
return artifact;
|
|
988
|
+
const { payload: _payload, ...rest } = record;
|
|
989
|
+
return rest;
|
|
990
|
+
});
|
|
991
|
+
return { ...slim, artifacts, steps: [], events: [] };
|
|
992
|
+
}
|
|
966
993
|
export function conversationTurnInputFromRun(run) {
|
|
967
994
|
const artifact = run.artifacts.find((candidate) => candidate.kind === 'answer')
|
|
968
995
|
?? run.artifacts.find((candidate) => candidate.kind === 'research_run')
|
|
@@ -970,7 +997,9 @@ export function conversationTurnInputFromRun(run) {
|
|
|
970
997
|
const payload = agentRunRecord(artifact?.payload);
|
|
971
998
|
// A blocked run may retain diagnostic artifacts, but their result-shaped
|
|
972
999
|
// payload is never accepted conversation evidence or prose authority.
|
|
973
|
-
const result = run.status === 'blocked'
|
|
1000
|
+
const result = run.status === 'blocked' || run.status === 'cancelled'
|
|
1001
|
+
? undefined
|
|
1002
|
+
: agentRunRecord(payload?.result);
|
|
974
1003
|
const columns = conversationResultColumns(result?.columns);
|
|
975
1004
|
// The visual preview stays tiny, but member resolution needs a wider bounded
|
|
976
1005
|
// value window. Deriving dimensions from only the eight preview rows caused a
|
|
@@ -2598,9 +2627,143 @@ export async function startLocalServer(opts) {
|
|
|
2598
2627
|
}
|
|
2599
2628
|
const answerRunExecutor = async ({ request, route, routeDecision, attempt, repairHint, emit }) => {
|
|
2600
2629
|
const runStartedAtMs = Date.now();
|
|
2630
|
+
const turnPlan = buildAnalyticalTurnPlan({
|
|
2631
|
+
question: request.question,
|
|
2632
|
+
turnId: request.runId,
|
|
2633
|
+
candidateIds: routeDecision?.retrievalEvidence?.candidateIds ?? [],
|
|
2634
|
+
frozen: routeDecision?.resolvedAnalyticalPlan?.mode === 'authoritative',
|
|
2635
|
+
snapshotId: routeDecision?.resolvedAnalyticalPlan?.snapshotId ?? routeDecision?.retrievalEvidence?.snapshotId,
|
|
2636
|
+
sourceFingerprint: routeDecision?.resolvedAnalyticalPlan?.sourceFingerprint ?? routeDecision?.retrievalEvidence?.sourceFingerprint,
|
|
2637
|
+
});
|
|
2638
|
+
const childTurn = Boolean(request.workspaceContext && typeof request.workspaceContext === 'object'
|
|
2639
|
+
&& request.workspaceContext.analyticalTaskChild === true);
|
|
2640
|
+
// Compound questions are a bounded task graph, not one query whose answer
|
|
2641
|
+
// is copied into several labels. Independent children share the parent's
|
|
2642
|
+
// signal/deadline and return truthful partial success.
|
|
2643
|
+
if (turnPlan.tasks.length > 1 && !childTurn && (attempt ?? 0) === 0) {
|
|
2644
|
+
const childResults = await Promise.all(turnPlan.tasks.slice(0, 6).map(async (task) => {
|
|
2645
|
+
if (request.signal?.aborted)
|
|
2646
|
+
rethrowIfCancelled(request.signal.reason, request.signal);
|
|
2647
|
+
try {
|
|
2648
|
+
const childRequest = {
|
|
2649
|
+
...request,
|
|
2650
|
+
question: task.question,
|
|
2651
|
+
workspaceContext: {
|
|
2652
|
+
...(request.workspaceContext && typeof request.workspaceContext === 'object' ? request.workspaceContext : {}),
|
|
2653
|
+
analyticalTaskChild: true,
|
|
2654
|
+
analyticalParentRunId: request.runId,
|
|
2655
|
+
analyticalTaskId: task.id,
|
|
2656
|
+
},
|
|
2657
|
+
};
|
|
2658
|
+
const answer = await runGovernedAgentAnswerForRun(childRequest, { attempt: 0, repairHint }, route, (message) => emit({ type: 'executor.started', message: `Task ${task.id}: ${message}`, route }), undefined);
|
|
2659
|
+
if (answer.result) {
|
|
2660
|
+
const canonical = normalizeCanonicalQueryResult({
|
|
2661
|
+
...answer.result,
|
|
2662
|
+
resultFingerprint: answer.result.resultFingerprint ?? answer.result.executionReceipt?.resultFingerprint,
|
|
2663
|
+
executionReceipt: answer.result.executionReceipt,
|
|
2664
|
+
answerTier: answer.route?.tier ?? answer.sourceTier,
|
|
2665
|
+
});
|
|
2666
|
+
answer.result = {
|
|
2667
|
+
...answer.result,
|
|
2668
|
+
columns: canonical.columns,
|
|
2669
|
+
rows: canonical.rows,
|
|
2670
|
+
rowCount: canonical.rowCount,
|
|
2671
|
+
resultFingerprint: canonical.resultFingerprint,
|
|
2672
|
+
...(canonical.executionReceipt ? { executionReceipt: canonical.executionReceipt } : {}),
|
|
2673
|
+
...(canonical.answerTier ? { answerTier: canonical.answerTier } : {}),
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
return { task, answer };
|
|
2677
|
+
}
|
|
2678
|
+
catch (error) {
|
|
2679
|
+
rethrowIfCancelled(error, request.signal);
|
|
2680
|
+
return { task, error: error instanceof Error ? error.message : String(error) };
|
|
2681
|
+
}
|
|
2682
|
+
}));
|
|
2683
|
+
const outcomes = childResults.map(({ task, answer, error }) => ({
|
|
2684
|
+
version: 1,
|
|
2685
|
+
taskId: task.id,
|
|
2686
|
+
status: error || answer?.kind === 'no_answer' ? 'gap' : 'completed',
|
|
2687
|
+
...(answer?.answer || answer?.text ? { summary: answer.answer ?? answer.text } : {}),
|
|
2688
|
+
...(answer?.result?.resultFingerprint ? { resultFingerprint: answer.result.resultFingerprint } : {}),
|
|
2689
|
+
...(error || answer?.kind === 'no_answer' ? {
|
|
2690
|
+
gap: buildCoverageGap({
|
|
2691
|
+
code: answer?.refusalCode === 'ambiguous' ? 'AMBIGUOUS_MEANING' : 'EXECUTION_FAILED',
|
|
2692
|
+
phase: answer?.executionError ? 'execution' : 'meaning',
|
|
2693
|
+
message: error ?? answer?.answer ?? answer?.text ?? 'The task did not produce an accepted analytical result.',
|
|
2694
|
+
searchedSources: routeDecision?.retrievalEvidence?.candidateIds ?? [],
|
|
2695
|
+
attemptedRoutes: ['certified', 'semantic', 'governed_relational', 'generated'],
|
|
2696
|
+
missing: [],
|
|
2697
|
+
recoverable: false,
|
|
2698
|
+
planFrozen: turnPlan.frozen,
|
|
2699
|
+
nextActions: ['Review the task context and retry the clause.'],
|
|
2700
|
+
}),
|
|
2701
|
+
} : {}),
|
|
2702
|
+
}));
|
|
2703
|
+
const completedCount = outcomes.filter((outcome) => outcome.status === 'completed').length;
|
|
2704
|
+
const tasks = turnPlan.tasks.map((task) => ({
|
|
2705
|
+
...task,
|
|
2706
|
+
status: outcomes.find((outcome) => outcome.taskId === task.id)?.status === 'completed' ? 'completed' : 'gap',
|
|
2707
|
+
}));
|
|
2708
|
+
const answerText = childResults.map(({ task, answer, error }) => `${task.question}: ${error ?? answer?.answer ?? answer?.text ?? 'No accepted result was produced.'}`).join('\n\n');
|
|
2709
|
+
return {
|
|
2710
|
+
summary: completedCount === outcomes.length
|
|
2711
|
+
? `Answered ${completedCount} independent analytical clauses.`
|
|
2712
|
+
: `Answered ${completedCount} of ${outcomes.length} analytical clauses; the remaining clauses need review.`,
|
|
2713
|
+
answer: answerText,
|
|
2714
|
+
status: completedCount === outcomes.length ? 'completed' : completedCount > 0 ? 'needs_review' : 'needs_clarification',
|
|
2715
|
+
trustState: completedCount === outcomes.length ? 'governed' : 'review_required',
|
|
2716
|
+
stopReason: completedCount === outcomes.length ? 'governed_semantic_answer' : 'human_review_required',
|
|
2717
|
+
artifacts: childResults.map(({ task, answer, error }) => agentRunArtifact('answer', `Task: ${task.question}`, {
|
|
2718
|
+
taskId: task.id,
|
|
2719
|
+
question: task.question,
|
|
2720
|
+
answer: answer?.answer ?? answer?.text,
|
|
2721
|
+
resultFingerprint: answer?.result?.resultFingerprint,
|
|
2722
|
+
error,
|
|
2723
|
+
})),
|
|
2724
|
+
evaluations: outcomes.map((outcome) => agentRunEvaluation(`analytical-task-${outcome.taskId}`, `Analytical task ${outcome.taskId}`, outcome.status === 'completed', outcome.status === 'completed' ? 'info' : 'warning', outcome.summary ?? outcome.gap?.message ?? 'Task outcome recorded.', outcome)),
|
|
2725
|
+
analyticalTurnPlan: { ...turnPlan, tasks, frozen: true },
|
|
2726
|
+
analyticalTaskOutcomes: outcomes,
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2601
2729
|
let governedAnswer;
|
|
2602
2730
|
try {
|
|
2603
2731
|
governedAnswer = await runGovernedAgentAnswerForRun(request, { attempt, repairHint }, route, (message) => emit({ type: 'executor.started', message, route }), routeDecision);
|
|
2732
|
+
// Keep the canonical result contract on the answer itself, not only on
|
|
2733
|
+
// the narration preview. Conversation persistence, follow-up member
|
|
2734
|
+
// resolution, Apply, and the notebook table all read this payload; if a
|
|
2735
|
+
// connector returned positional rows, dropping normalization here makes
|
|
2736
|
+
// the next question lose its customer/product/region binding (AGT-032).
|
|
2737
|
+
if (governedAnswer.result) {
|
|
2738
|
+
const canonical = normalizeCanonicalQueryResult({
|
|
2739
|
+
...governedAnswer.result,
|
|
2740
|
+
resultFingerprint: governedAnswer.result.resultFingerprint
|
|
2741
|
+
?? governedAnswer.result.executionReceipt?.resultFingerprint,
|
|
2742
|
+
executionReceipt: governedAnswer.result.executionReceipt,
|
|
2743
|
+
answerTier: governedAnswer.route?.tier ?? governedAnswer.sourceTier,
|
|
2744
|
+
trustState: governedAnswer.certification === 'certified'
|
|
2745
|
+
? 'certified'
|
|
2746
|
+
: governedAnswer.kind === 'no_answer'
|
|
2747
|
+
? 'blocked'
|
|
2748
|
+
: governedAnswer.reviewStatus === 'analyst_review_required'
|
|
2749
|
+
? 'review_required'
|
|
2750
|
+
: 'governed',
|
|
2751
|
+
});
|
|
2752
|
+
governedAnswer.result = {
|
|
2753
|
+
...governedAnswer.result,
|
|
2754
|
+
columns: canonical.columns,
|
|
2755
|
+
rows: canonical.rows,
|
|
2756
|
+
rowCount: canonical.rowCount,
|
|
2757
|
+
// Preserve the execution-service/receipt fingerprint. A local UI
|
|
2758
|
+
// digest is only a legacy fallback in normalizeCanonicalQueryResult;
|
|
2759
|
+
// it must never replace the cryptographic identity of this run.
|
|
2760
|
+
resultFingerprint: canonical.resultFingerprint,
|
|
2761
|
+
...(canonical.executionTime !== undefined ? { executionTime: canonical.executionTime } : {}),
|
|
2762
|
+
...(canonical.truncated ? { truncated: true } : {}),
|
|
2763
|
+
...(canonical.executionReceipt ? { executionReceipt: canonical.executionReceipt } : {}),
|
|
2764
|
+
...(canonical.answerTier ? { answerTier: canonical.answerTier } : {}),
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2604
2767
|
// Surface the approved Hint-Graph corrections that shaped this answer so the
|
|
2605
2768
|
// UI can show an "applied learnings" chip (memoryContext is already on the answer).
|
|
2606
2769
|
if (!governedAnswer.appliedHints) {
|
|
@@ -2818,6 +2981,38 @@ export async function startLocalServer(opts) {
|
|
|
2818
2981
|
// has already spent its one evidence-aware repair. Keep this terminal and
|
|
2819
2982
|
// inspectable; an ordinary Ask must never silently become a second Research run.
|
|
2820
2983
|
const isModelDeclined = governedAnswer.kind === 'no_answer' && governedAnswer.refusalCode === 'model_declined';
|
|
2984
|
+
// Before an analytical plan is frozen, a generated Ask gap is a recoverable
|
|
2985
|
+
// coverage result rather than a terminal governed error. Let the run engine
|
|
2986
|
+
// consume the typed evaluation and make one bounded cascade decision. Frozen
|
|
2987
|
+
// certified/semantic plans and provider/policy/execution failures remain
|
|
2988
|
+
// fail-closed.
|
|
2989
|
+
const canRecoverPreFreezeGap = (isGroundingGap || isModelDeclined)
|
|
2990
|
+
&& route === 'generated_answer'
|
|
2991
|
+
&& (request.requestedMode === undefined || request.requestedMode === 'auto' || request.requestedMode === 'ask')
|
|
2992
|
+
&& routeDecision?.resolvedAnalyticalPlan?.mode !== 'authoritative';
|
|
2993
|
+
const typedCoverageGap = (isGroundingGap || isModelDeclined)
|
|
2994
|
+
? buildCoverageGap({
|
|
2995
|
+
code: governedAnswer.refusalCode === 'modeling_gap' ? 'MISSING_RELATIONSHIP' : 'MISSING_RUNTIME_CAPABILITY',
|
|
2996
|
+
phase: 'planning',
|
|
2997
|
+
message: governedAnswer.refusalDetails?.message
|
|
2998
|
+
?? (isModelDeclined
|
|
2999
|
+
? 'The available context did not produce a safe analytical tuple.'
|
|
3000
|
+
: 'The retrieved context did not prove the metadata required for this analytical question.'),
|
|
3001
|
+
searchedSources: ['certified_blocks', 'semantic_metrics', 'dbt_manifest', 'relationship_graph', 'warehouse_metadata'],
|
|
3002
|
+
attemptedRoutes: ['certified', 'semantic', 'governed_relational', 'generated'],
|
|
3003
|
+
missing: governedAnswer.refusalDetails?.offending
|
|
3004
|
+
? [
|
|
3005
|
+
governedAnswer.refusalDetails.offending.relation,
|
|
3006
|
+
governedAnswer.refusalDetails.offending.column,
|
|
3007
|
+
].filter((value) => Boolean(value))
|
|
3008
|
+
: ['an executable metric/dimension/relationship tuple'],
|
|
3009
|
+
recoverable: canRecoverPreFreezeGap,
|
|
3010
|
+
planFrozen: routeDecision?.resolvedAnalyticalPlan?.mode === 'authoritative',
|
|
3011
|
+
nextActions: canRecoverPreFreezeGap
|
|
3012
|
+
? ['continue through DBT-grounded relational context', 'run review-required generated SQL', 'start bounded Research if coverage remains incomplete']
|
|
3013
|
+
: ['review the retained metadata gap', 'select a governed metric or dimension', 'repair modeling if the relationship is missing'],
|
|
3014
|
+
})
|
|
3015
|
+
: undefined;
|
|
2821
3016
|
// Only a genuinely AMBIGUOUS question is surfaced as "needs clarification".
|
|
2822
3017
|
// Grounding/compiler gaps are terminal review states with their evidence trace;
|
|
2823
3018
|
// provider outages are blocked so the UI can offer an explicit retry.
|
|
@@ -2971,7 +3166,7 @@ export async function startLocalServer(opts) {
|
|
|
2971
3166
|
? 'blocked'
|
|
2972
3167
|
: needsClarification
|
|
2973
3168
|
? 'needs_clarification'
|
|
2974
|
-
: isGroundingGap || isModelDeclined
|
|
3169
|
+
: (isGroundingGap || isModelDeclined) && !canRecoverPreFreezeGap
|
|
2975
3170
|
? 'blocked'
|
|
2976
3171
|
: isCertified || isSemantic
|
|
2977
3172
|
? 'completed'
|
|
@@ -2980,7 +3175,7 @@ export async function startLocalServer(opts) {
|
|
|
2980
3175
|
? 'blocked'
|
|
2981
3176
|
: needsClarification
|
|
2982
3177
|
? 'not_applicable'
|
|
2983
|
-
: isGroundingGap || isModelDeclined
|
|
3178
|
+
: (isGroundingGap || isModelDeclined) && !canRecoverPreFreezeGap
|
|
2984
3179
|
? 'blocked'
|
|
2985
3180
|
: isCertified
|
|
2986
3181
|
? 'certified'
|
|
@@ -2994,7 +3189,7 @@ export async function startLocalServer(opts) {
|
|
|
2994
3189
|
// A gap is terminal, but it is NOT a provider outage: it still owes the
|
|
2995
3190
|
// user its evidence trace, and its next-actions below stay the
|
|
2996
3191
|
// grounding-gap set rather than "retry the provider".
|
|
2997
|
-
: isGroundingGap || isModelDeclined
|
|
3192
|
+
: (isGroundingGap || isModelDeclined) && !canRecoverPreFreezeGap
|
|
2998
3193
|
? 'human_review_required'
|
|
2999
3194
|
: isCertified
|
|
3000
3195
|
? 'certified_answer_found'
|
|
@@ -3083,7 +3278,7 @@ export async function startLocalServer(opts) {
|
|
|
3083
3278
|
? (governedAnswer.semanticExecutionTrace
|
|
3084
3279
|
? [agentRunArtifact('answer', governedAnswer.refusalCode === 'ambiguous'
|
|
3085
3280
|
? 'Semantic path selection required'
|
|
3086
|
-
: 'Semantic compilation details', governedAnswer, undefined, needsClarification ? 'not_applicable' : 'review_required')]
|
|
3281
|
+
: 'Semantic compilation details', typedCoverageGap ? { ...governedAnswer, coverageGap: typedCoverageGap } : governedAnswer, undefined, needsClarification ? 'not_applicable' : 'review_required')]
|
|
3087
3282
|
: governedAnswer.dqlArtifact && !isProviderError && !isGroundingGap && !isModelDeclined && !isPolicyBlocked
|
|
3088
3283
|
? [agentRunArtifact('dql_block_draft', 'DQL draft (review required)', governedAnswer.dqlArtifact, undefined, 'review_required')]
|
|
3089
3284
|
// Every refusal still owes the user an account of itself. Emitting no
|
|
@@ -3092,7 +3287,7 @@ export async function startLocalServer(opts) {
|
|
|
3092
3287
|
// answered", the DQL draft and the compiled SQL all become
|
|
3093
3288
|
// unreachable exactly when the user most needs to see why it stopped
|
|
3094
3289
|
// and carry the query into a notebook.
|
|
3095
|
-
: [agentRunArtifact('answer', 'No answer was accepted', governedAnswer, undefined, needsClarification ? 'not_applicable' : 'blocked')])
|
|
3290
|
+
: [agentRunArtifact('answer', 'No answer was accepted', typedCoverageGap ? { ...governedAnswer, coverageGap: typedCoverageGap } : governedAnswer, undefined, needsClarification ? 'not_applicable' : 'blocked')])
|
|
3096
3291
|
: [agentRunArtifact('answer', isCertified ? 'Certified answer' : isSemantic ? 'Governed semantic answer' : isExploratory ? 'Exploratory DBT-grounded answer' : 'Review-required answer', governedAnswer, governedAnswer.sourceCertifiedBlock ?? governedAnswer.block?.name, isCertified ? 'certified' : isSemantic ? 'governed' : 'review_required')],
|
|
3097
3292
|
evaluations: [
|
|
3098
3293
|
agentRunEvaluation('route-decision', 'Route decision', true, 'info', routeDecision?.reason ?? 'Routed request to governed answer.', {
|
|
@@ -3127,22 +3322,34 @@ export async function startLocalServer(opts) {
|
|
|
3127
3322
|
...(isExecutionFailure ? [agentRunEvaluation('query-execution', 'Query execution', false, 'blocking', `The governed query failed before it produced a result: ${governedAnswer.executionError}`)] : []),
|
|
3128
3323
|
...(isGroundingGap ? [
|
|
3129
3324
|
{
|
|
3130
|
-
...agentRunEvaluation('grounding-gap', 'Metadata grounding', false, 'warning',
|
|
3325
|
+
...agentRunEvaluation('grounding-gap', 'Metadata grounding', false, 'warning', canRecoverPreFreezeGap
|
|
3326
|
+
? 'The first governed lookup did not prove the required metadata grounding. Continue through the bounded relational/generated cascade before asking the user to repair modeling.'
|
|
3327
|
+
: 'The bounded lookup could not prove the required metadata grounding. This plan is already frozen, so no automatic Research escalation was started.', {
|
|
3131
3328
|
refusalCode: governedAnswer.refusalCode,
|
|
3132
3329
|
refusalDetails: governedAnswer.refusalDetails,
|
|
3133
3330
|
validationWarnings: governedAnswer.validationWarnings,
|
|
3134
3331
|
route: governedAnswer.route,
|
|
3332
|
+
coverageGap: typedCoverageGap,
|
|
3135
3333
|
}),
|
|
3334
|
+
...(canRecoverPreFreezeGap ? {
|
|
3335
|
+
suggestedRepair: 'Continue the unfrozen Ask cascade through DBT-grounded relational context and review-required generated SQL.',
|
|
3336
|
+
} : {}),
|
|
3136
3337
|
},
|
|
3137
3338
|
] : []),
|
|
3138
3339
|
...(isModelDeclined ? [
|
|
3139
3340
|
{
|
|
3140
|
-
...agentRunEvaluation('declined-despite-context', 'Answer grounding', false, 'blocking',
|
|
3341
|
+
...agentRunEvaluation('declined-despite-context', 'Answer grounding', false, 'blocking', canRecoverPreFreezeGap
|
|
3342
|
+
? 'The governed lookup could not compose a query after its bounded in-lane repair. Continue with the Research context ledger before returning a typed gap.'
|
|
3343
|
+
: 'The bounded lookup could not compose a governed query after its in-lane repair. Start Research explicitly to investigate beyond this lookup budget.', {
|
|
3141
3344
|
refusalCode: governedAnswer.refusalCode,
|
|
3142
3345
|
refusalDetails: governedAnswer.refusalDetails,
|
|
3143
3346
|
validationWarnings: governedAnswer.validationWarnings,
|
|
3144
3347
|
route: governedAnswer.route,
|
|
3348
|
+
coverageGap: typedCoverageGap,
|
|
3145
3349
|
}),
|
|
3350
|
+
...(canRecoverPreFreezeGap ? {
|
|
3351
|
+
suggestedRepair: 'Search the surrounding metadata and relationship context before giving up on the analytical turn.',
|
|
3352
|
+
} : {}),
|
|
3146
3353
|
},
|
|
3147
3354
|
] : []),
|
|
3148
3355
|
...(isPolicyBlocked ? [
|
|
@@ -3160,6 +3367,14 @@ export async function startLocalServer(opts) {
|
|
|
3160
3367
|
...(governedAnswer.executionError ? [
|
|
3161
3368
|
agentRunEvaluation('execution-error', 'Execution error', false, 'warning', governedAnswer.executionError),
|
|
3162
3369
|
] : []),
|
|
3370
|
+
// A rejected narration must say WHY it was rejected. The failures were
|
|
3371
|
+
// computed and then dropped, so a reader saw "Verified narration was
|
|
3372
|
+
// unavailable" with no way to tell whether the model invented a number,
|
|
3373
|
+
// cited a fact id that does not exist, or the provider simply failed.
|
|
3374
|
+
// The fallback itself stays: this only makes its reason inspectable.
|
|
3375
|
+
...(narrationSource === 'deterministic' && narrationValidationFailures.length > 0 ? [
|
|
3376
|
+
agentRunEvaluation('narration-verification', 'Narration verification', false, 'warning', `The drafted narration was rejected against the result fact set, so the deterministic record was shown instead: ${narrationValidationFailures.join('; ')}`, { narrationSource, validationFailures: narrationValidationFailures }),
|
|
3377
|
+
] : []),
|
|
3163
3378
|
],
|
|
3164
3379
|
nextActions,
|
|
3165
3380
|
providerEgressReceipts: finalProviderEgressReceipts,
|
|
@@ -3687,6 +3902,7 @@ export async function startLocalServer(opts) {
|
|
|
3687
3902
|
plan,
|
|
3688
3903
|
};
|
|
3689
3904
|
let researchRun;
|
|
3905
|
+
const researchRuns = [];
|
|
3690
3906
|
let researchWorkspaceError;
|
|
3691
3907
|
if (!needsClarification) {
|
|
3692
3908
|
try {
|
|
@@ -3711,26 +3927,106 @@ export async function startLocalServer(opts) {
|
|
|
3711
3927
|
});
|
|
3712
3928
|
emit({
|
|
3713
3929
|
type: 'artifact.created',
|
|
3714
|
-
message: 'Saved
|
|
3930
|
+
message: 'Saved the immutable root research plan; executing bounded child branches.',
|
|
3715
3931
|
route: 'research',
|
|
3716
3932
|
trustState: 'review_required',
|
|
3717
|
-
payload: { researchRunId: created.id, notebookPath },
|
|
3933
|
+
payload: { researchRunId: created.id, notebookPath, branchCap: 6 },
|
|
3718
3934
|
});
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3935
|
+
// The root record is a plan/dossier parent. Only child runs are
|
|
3936
|
+
// observed research executions. This prevents one root result from
|
|
3937
|
+
// being copied into six fabricated ledger entries (AGT-016/033).
|
|
3938
|
+
// An explicit Research request still gets one real child when the
|
|
3939
|
+
// catalog planner has no grounded step (for example, an empty
|
|
3940
|
+
// starter project). The child is an observed metadata/baseline
|
|
3941
|
+
// attempt, not a fabricated successful finding; its durable status
|
|
3942
|
+
// and receipt determine the ledger entry.
|
|
3943
|
+
const fallbackBranch = {
|
|
3944
|
+
thought: 'Inspect the requested analytical question against the frozen root context.',
|
|
3945
|
+
action: {
|
|
3946
|
+
kind: 'lookup_metric',
|
|
3947
|
+
target: routeDecision?.resolvedAnalyticalPlan?.executionId ?? request.question,
|
|
3948
|
+
},
|
|
3949
|
+
expectation: 'Whether the frozen context contains enough evidence for a bounded answer.',
|
|
3950
|
+
};
|
|
3951
|
+
const branches = capResearchBranches(plan.steps.length > 0 ? plan.steps : [fallbackBranch], 6);
|
|
3952
|
+
for (let index = 0; index < branches.length; index += 1) {
|
|
3953
|
+
const step = branches[index];
|
|
3954
|
+
if (request.signal?.aborted)
|
|
3955
|
+
rethrowIfCancelled(request.signal.reason, request.signal);
|
|
3956
|
+
const branchId = `${step.action.kind}:${step.action.target}`;
|
|
3957
|
+
const branchQuestion = `${request.question}\nResearch branch ${index + 1} (${branchId}): ${step.expectation}`;
|
|
3958
|
+
const childId = `${created.id}:research:${index + 1}`;
|
|
3959
|
+
const child = storage.createRun({
|
|
3960
|
+
id: childId,
|
|
3961
|
+
notebookPath,
|
|
3962
|
+
title: `${agentRunTitle(request.question, 'Agent research')} · branch ${index + 1}`,
|
|
3963
|
+
question: branchQuestion,
|
|
3964
|
+
sourceCell,
|
|
3965
|
+
sourceCellId,
|
|
3966
|
+
sourceCellName,
|
|
3967
|
+
sourceCellFingerprint,
|
|
3968
|
+
intent: researchIntent,
|
|
3969
|
+
domain: agentRunWorkspaceValue(request, 'domain'),
|
|
3970
|
+
owner: agentRunWorkspaceValue(request, 'owner'),
|
|
3971
|
+
context: {
|
|
3972
|
+
...researchContextEnvelope,
|
|
3973
|
+
rootRunId: created.id,
|
|
3974
|
+
rootPlanId: plan.rootPlanId,
|
|
3975
|
+
branch: {
|
|
3976
|
+
id: branchId,
|
|
3977
|
+
index: index + 1,
|
|
3978
|
+
expectation: step.expectation,
|
|
3979
|
+
action: step.action,
|
|
3980
|
+
},
|
|
3981
|
+
},
|
|
3982
|
+
});
|
|
3983
|
+
emit({
|
|
3984
|
+
type: 'artifact.created',
|
|
3985
|
+
message: `Started research branch ${index + 1} of ${branches.length}.`,
|
|
3986
|
+
route: 'research',
|
|
3987
|
+
trustState: 'review_required',
|
|
3988
|
+
payload: { researchRunId: child.id, parentResearchRunId: created.id, branchId },
|
|
3989
|
+
});
|
|
3990
|
+
try {
|
|
3991
|
+
const executed = await runNotebookResearch(storage, child, {
|
|
3992
|
+
domain: agentRunWorkspaceValue(request, 'domain'),
|
|
3993
|
+
owner: agentRunWorkspaceValue(request, 'owner'),
|
|
3994
|
+
sourceCellFingerprint,
|
|
3995
|
+
question: branchQuestion,
|
|
3996
|
+
intent: researchIntent,
|
|
3997
|
+
context: {
|
|
3998
|
+
...researchContextEnvelope,
|
|
3999
|
+
rootRunId: created.id,
|
|
4000
|
+
rootPlanId: plan.rootPlanId,
|
|
4001
|
+
branch: { id: branchId, index: index + 1, expectation: step.expectation, action: step.action },
|
|
4002
|
+
},
|
|
4003
|
+
executionConnection: researchExecutionConnection,
|
|
4004
|
+
executionConnectionName: researchExecutionConnectionName,
|
|
4005
|
+
signal: request.signal,
|
|
4006
|
+
baselineSql: agentRunString(researchSource?.sql),
|
|
4007
|
+
baselineDqlArtifact: researchSource?.dqlArtifact,
|
|
4008
|
+
baselineRunId: agentRunString(researchSource?.runId),
|
|
4009
|
+
});
|
|
4010
|
+
researchRuns.push(withNotebookResearchChecklist(executed));
|
|
4011
|
+
}
|
|
4012
|
+
catch (error) {
|
|
4013
|
+
// A child is a real durable run even when cancellation stops the
|
|
4014
|
+
// shared branch budget. Persist the truthful stop before
|
|
4015
|
+
// propagating cancellation to the parent run.
|
|
4016
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4017
|
+
storage.updateRun(child.id, {
|
|
4018
|
+
status: 'error',
|
|
4019
|
+
error: message,
|
|
4020
|
+
summary: 'Research branch stopped before producing a result.',
|
|
4021
|
+
reviewStatus: 'needs_review',
|
|
4022
|
+
});
|
|
4023
|
+
const stopped = storage.getRun(child.id);
|
|
4024
|
+
if (stopped)
|
|
4025
|
+
researchRuns.push(withNotebookResearchChecklist(stopped));
|
|
4026
|
+
rethrowIfCancelled(error, request.signal);
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
researchRun = researchRuns[0];
|
|
3734
4030
|
}
|
|
3735
4031
|
finally {
|
|
3736
4032
|
storage.close();
|
|
@@ -3741,15 +4037,81 @@ export async function startLocalServer(opts) {
|
|
|
3741
4037
|
researchWorkspaceError = formatNotebookResearchStorageError(error);
|
|
3742
4038
|
}
|
|
3743
4039
|
}
|
|
3744
|
-
const researchResultPreview =
|
|
4040
|
+
const researchResultPreview = researchRuns
|
|
4041
|
+
.map((run) => run.resultPreview)
|
|
4042
|
+
// Keep zero-row executions in the proof path. `coerceNarrateResultData`
|
|
4043
|
+
// intentionally omits empty row sets for prose, but an empty result is
|
|
4044
|
+
// still an executed result when it carries its execution identity.
|
|
4045
|
+
.find((preview) => {
|
|
4046
|
+
const record = agentRunRecord(preview);
|
|
4047
|
+
return Boolean(record && Array.isArray(record.rows));
|
|
4048
|
+
})
|
|
4049
|
+
?? researchRun?.resultPreview;
|
|
3745
4050
|
const researchResultData = coerceNarrateResultData(researchResultPreview);
|
|
3746
4051
|
const researchResultRecord = agentRunRecord(researchResultPreview);
|
|
4052
|
+
// Keep each bounded research branch inspectable as an evidence ledger.
|
|
4053
|
+
// The notebook workspace remains the durable execution record; this
|
|
4054
|
+
// additive projection gives Ask/Research narration a stable list of
|
|
4055
|
+
// observations, failures, facts, and receipts without exposing provider
|
|
4056
|
+
// chain-of-thought. Six is the hard branch budget for one root question.
|
|
4057
|
+
const researchLedger = buildResearchEvidenceLedger({
|
|
4058
|
+
rootQuestion: request.question,
|
|
4059
|
+
planId: plan.rootPlanId,
|
|
4060
|
+
snapshotId: routeDecision?.resolvedAnalyticalPlan?.snapshotId,
|
|
4061
|
+
entries: researchRuns.slice(0, 6).map((branch, index) => {
|
|
4062
|
+
const branchContext = agentRunRecord(branch.context)?.branch;
|
|
4063
|
+
const branchPreviewRecord = agentRunRecord(branch.resultPreview);
|
|
4064
|
+
const branchPreview = coerceNarrateResultData(branchPreviewRecord);
|
|
4065
|
+
const previewRecord = branchPreviewRecord;
|
|
4066
|
+
const executionReceipt = normalizeAnalyticalExecutionReceipt(previewRecord?.executionReceipt);
|
|
4067
|
+
const resultFingerprint = normalizeAnalyticalExecutionFingerprint(previewRecord?.resultFingerprint)
|
|
4068
|
+
?? executionReceipt?.resultFingerprint;
|
|
4069
|
+
// A child run ID or context-pack ID proves that planning happened,
|
|
4070
|
+
// not that a query executed. Only the canonical result fingerprint
|
|
4071
|
+
// (on the result or its execution receipt) can make a branch
|
|
4072
|
+
// observed (AGT-016/033).
|
|
4073
|
+
const executionProof = resultFingerprint;
|
|
4074
|
+
const observed = branch.status === 'ready' && Boolean(executionProof);
|
|
4075
|
+
return {
|
|
4076
|
+
id: branch.id,
|
|
4077
|
+
branchId: agentRunString(branchContext?.id) ?? `branch:${index + 1}`,
|
|
4078
|
+
question: branch.question,
|
|
4079
|
+
status: observed ? 'observed' : branch.status === 'error' ? 'failed' : 'skipped',
|
|
4080
|
+
...(branchPreviewRecord && Array.isArray(branchPreviewRecord.rows)
|
|
4081
|
+
? { rowCount: branchPreviewRecord.rows.length }
|
|
4082
|
+
: {}),
|
|
4083
|
+
...(resultFingerprint ? { resultFingerprint } : {}),
|
|
4084
|
+
...(executionReceipt ? { executionReceipt } : {}),
|
|
4085
|
+
facts: [agentRunString(branchContext?.expectation) ?? branch.question, ...(branch.evidence?.citations ?? []).flatMap((item) => typeof item === 'string' ? [item] : [])].slice(0, 8),
|
|
4086
|
+
// A context-pack ID or child run ID is not execution evidence. Keep
|
|
4087
|
+
// the receipt list empty until the execution service supplies a
|
|
4088
|
+
// receipt/fingerprint (AGT-016/033).
|
|
4089
|
+
receipts: executionProof ? [executionProof] : [],
|
|
4090
|
+
...(!observed ? {
|
|
4091
|
+
error: branch.error
|
|
4092
|
+
?? (branch.status === 'error'
|
|
4093
|
+
? branch.summary
|
|
4094
|
+
: 'Research branch did not produce an execution receipt or result fingerprint.'),
|
|
4095
|
+
} : {}),
|
|
4096
|
+
};
|
|
4097
|
+
}),
|
|
4098
|
+
stoppingReason: needsClarification
|
|
4099
|
+
? 'not_started'
|
|
4100
|
+
: researchRuns.some((run) => run.status === 'error')
|
|
4101
|
+
? 'insufficient_evidence'
|
|
4102
|
+
: plan.steps.length > 6
|
|
4103
|
+
? 'budget'
|
|
4104
|
+
: 'completed',
|
|
4105
|
+
});
|
|
3747
4106
|
// A query that ran and matched 0 rows STILL executed — treat it as a clean,
|
|
3748
4107
|
// grounded execution (not "no result"), so an empty answer is surfaced as
|
|
3749
4108
|
// "0 rows matched" rather than silently downgraded to review-required.
|
|
3750
4109
|
const researchDidExecute = Boolean(researchResultData) || Boolean(researchResultRecord && Array.isArray(researchResultRecord.rows));
|
|
3751
4110
|
const researchZeroRows = !researchResultData && researchDidExecute;
|
|
3752
|
-
const researchExecutedCleanly = researchDidExecute
|
|
4111
|
+
const researchExecutedCleanly = researchDidExecute
|
|
4112
|
+
&& !researchWorkspaceError
|
|
4113
|
+
&& researchRuns.length > 0
|
|
4114
|
+
&& researchRuns.every((run) => run.status === 'ready');
|
|
3753
4115
|
const narration = !needsClarification && researchResultData
|
|
3754
4116
|
? await narrateForAgentRun({
|
|
3755
4117
|
question: request.question,
|
|
@@ -3785,8 +4147,11 @@ export async function startLocalServer(opts) {
|
|
|
3785
4147
|
? []
|
|
3786
4148
|
: [agentRunArtifact('research_run', 'Research plan', {
|
|
3787
4149
|
plan,
|
|
4150
|
+
researchLedger,
|
|
3788
4151
|
researchRun,
|
|
4152
|
+
researchRuns,
|
|
3789
4153
|
researchRunId: researchRun?.id,
|
|
4154
|
+
researchRunIds: researchRuns.map((run) => run.id),
|
|
3790
4155
|
notebookPath,
|
|
3791
4156
|
workspaceError: researchWorkspaceError,
|
|
3792
4157
|
routeDecision,
|
|
@@ -3817,7 +4182,7 @@ export async function startLocalServer(opts) {
|
|
|
3817
4182
|
: [
|
|
3818
4183
|
...(researchRun?.id ? [{ id: 'open-research', label: 'Open research dossier', artifactKind: 'research_run' }] : []),
|
|
3819
4184
|
{ id: 'create-block', label: 'Review DQL draft', route: 'dql_block_draft', artifactKind: 'dql_block_draft' },
|
|
3820
|
-
...(
|
|
4185
|
+
...(researchRuns.some((run) => run.generatedSql || run.reviewedSql) ? [{ id: 'insert-sql', label: 'Insert SQL preview', route: 'sql_cell', artifactKind: 'sql_cell' }] : []),
|
|
3821
4186
|
],
|
|
3822
4187
|
};
|
|
3823
4188
|
},
|
|
@@ -4288,11 +4653,12 @@ export async function startLocalServer(opts) {
|
|
|
4288
4653
|
},
|
|
4289
4654
|
getCatalogContext: buildRankedAgentRunCatalogContext,
|
|
4290
4655
|
});
|
|
4291
|
-
//
|
|
4292
|
-
//
|
|
4293
|
-
//
|
|
4294
|
-
//
|
|
4656
|
+
// Explicit selections and conversation-only turns remain deterministic;
|
|
4657
|
+
// each fresh natural-language analytical turn gets one bounded candidate-ID
|
|
4658
|
+
// interpretation call by default. The rollback is host-owned and cannot be
|
|
4659
|
+
// supplied by an HTTP/MCP client.
|
|
4295
4660
|
const agentRunRouter = createHybridRouter({
|
|
4661
|
+
requireMeaningCallForNaturalLanguage: opts.requireMeaningCallForNaturalLanguage ?? true,
|
|
4296
4662
|
complete: async ({ system, user, signal }) => {
|
|
4297
4663
|
const provider = await createBlockStudioAssistProvider(projectRoot);
|
|
4298
4664
|
if (!provider)
|
|
@@ -4332,6 +4698,25 @@ export async function startLocalServer(opts) {
|
|
|
4332
4698
|
// A run may outlive its streaming browser connection, so cancellation is
|
|
4333
4699
|
// server-owned and keyed by run id rather than relying on fetch abort alone.
|
|
4334
4700
|
const activeAgentRunControllers = new Map();
|
|
4701
|
+
const cancelActiveAgentRun = (id) => {
|
|
4702
|
+
const controller = activeAgentRunControllers.get(id);
|
|
4703
|
+
if (!controller)
|
|
4704
|
+
return false;
|
|
4705
|
+
const progress = agentRunStore.getProgress(id);
|
|
4706
|
+
if (progress) {
|
|
4707
|
+
agentRunStore.saveProgress({
|
|
4708
|
+
...progress,
|
|
4709
|
+
lifecycle: {
|
|
4710
|
+
...progress.lifecycle,
|
|
4711
|
+
state: 'cancelling',
|
|
4712
|
+
revision: progress.lifecycle.revision + 1,
|
|
4713
|
+
updatedAt: new Date().toISOString(),
|
|
4714
|
+
},
|
|
4715
|
+
});
|
|
4716
|
+
}
|
|
4717
|
+
controller.abort(createAgentRunCancellationError());
|
|
4718
|
+
return true;
|
|
4719
|
+
};
|
|
4335
4720
|
const activeAnalyticalRepairReservations = new Set();
|
|
4336
4721
|
const consumedAnalyticalRepairCapabilities = new Set();
|
|
4337
4722
|
// Server-side conversation threads: persisted multi-turn state (survives refresh).
|
|
@@ -6113,6 +6498,19 @@ export async function startLocalServer(opts) {
|
|
|
6113
6498
|
: notebookResearchString(governedAnswer?.answer)
|
|
6114
6499
|
?? notebookResearchString(governedAnswer?.text)
|
|
6115
6500
|
?? notebookResearchSummary(question, resultPreview, previewError);
|
|
6501
|
+
const previewRecord = agentRunRecord(resultPreview);
|
|
6502
|
+
const executionReceipt = normalizeAnalyticalExecutionReceipt(previewRecord?.executionReceipt);
|
|
6503
|
+
// Do not treat a child run ID as execution evidence. The canonical
|
|
6504
|
+
// fingerprint is the only proof that this Research branch produced a
|
|
6505
|
+
// result; planning, SQL text, and a durable run record remain review
|
|
6506
|
+
// required until that proof exists (AGT-016/033).
|
|
6507
|
+
const executionProof = normalizeAnalyticalExecutionFingerprint(previewRecord?.resultFingerprint)
|
|
6508
|
+
?? executionReceipt?.resultFingerprint;
|
|
6509
|
+
const executionUnavailable = !previewError && !executionProof;
|
|
6510
|
+
const terminalError = previewError
|
|
6511
|
+
?? (executionUnavailable
|
|
6512
|
+
? 'Research did not produce an executed result or execution receipt; the branch remains review-required.'
|
|
6513
|
+
: undefined);
|
|
6116
6514
|
const recommendation = previewError
|
|
6117
6515
|
? 'Review the SQL, selected metadata, and connection context before rerunning.'
|
|
6118
6516
|
: dqlArtifact && !reviewedSql
|
|
@@ -6137,8 +6535,14 @@ export async function startLocalServer(opts) {
|
|
|
6137
6535
|
question,
|
|
6138
6536
|
intent,
|
|
6139
6537
|
context,
|
|
6140
|
-
|
|
6141
|
-
|
|
6538
|
+
// A plan, generated SQL, or DQL artifact is not an observed execution.
|
|
6539
|
+
// Only a result carrying an execution fingerprint/receipt may become
|
|
6540
|
+
// `ready`; otherwise persist a failed review state with no fabricated
|
|
6541
|
+
// evidence (AGT-016/033).
|
|
6542
|
+
status: terminalError ? 'error' : 'ready',
|
|
6543
|
+
summary: terminalError && executionUnavailable
|
|
6544
|
+
? 'Research branch stopped without an executed result; no observation was recorded.'
|
|
6545
|
+
: summary,
|
|
6142
6546
|
recommendation,
|
|
6143
6547
|
resultPreview,
|
|
6144
6548
|
evidence,
|
|
@@ -6158,7 +6562,7 @@ export async function startLocalServer(opts) {
|
|
|
6158
6562
|
...(display && display.ok ? display.warnings : []),
|
|
6159
6563
|
],
|
|
6160
6564
|
reviewStatus: 'needs_review',
|
|
6161
|
-
error:
|
|
6565
|
+
error: terminalError,
|
|
6162
6566
|
lastRunAt: startedAt,
|
|
6163
6567
|
}) ?? run;
|
|
6164
6568
|
}
|
|
@@ -6985,7 +7389,12 @@ export async function startLocalServer(opts) {
|
|
|
6985
7389
|
return;
|
|
6986
7390
|
}
|
|
6987
7391
|
if (operationMatch && req.method === 'DELETE') {
|
|
6988
|
-
const
|
|
7392
|
+
const operationId = decodeURIComponent(operationMatch[1]);
|
|
7393
|
+
const existingOperation = operationCoordinator.get(operationId);
|
|
7394
|
+
if (existingOperation?.type === 'agent_run' && existingOperation.scope.startsWith('agent-run:')) {
|
|
7395
|
+
cancelActiveAgentRun(existingOperation.scope.slice('agent-run:'.length));
|
|
7396
|
+
}
|
|
7397
|
+
const operation = operationCoordinator.cancel(operationId);
|
|
6989
7398
|
res.writeHead(operation ? 200 : 404, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
6990
7399
|
res.end(serializeJSON(operation ?? { error: 'Operation not found.' }));
|
|
6991
7400
|
return;
|
|
@@ -8098,12 +8507,17 @@ export async function startLocalServer(opts) {
|
|
|
8098
8507
|
const limit = Number.isFinite(rawLimit) && rawLimit > 0
|
|
8099
8508
|
? Math.min(200, Math.floor(rawLimit))
|
|
8100
8509
|
: 50;
|
|
8101
|
-
const
|
|
8102
|
-
|
|
8510
|
+
const stored = agentRunStore.list();
|
|
8511
|
+
// This is an INDEX payload: one row per run, no answer bodies. Shipping
|
|
8512
|
+
// stored runs whole meant a measured 47.61 MB for 20 real runs.
|
|
8513
|
+
// `GET /api/agent-runs/:id` still serves the complete immutable record.
|
|
8514
|
+
const runs = stored
|
|
8515
|
+
.slice()
|
|
8103
8516
|
.sort((a, b) => b.startedAt.localeCompare(a.startedAt))
|
|
8104
|
-
.slice(0, limit)
|
|
8517
|
+
.slice(0, limit)
|
|
8518
|
+
.map(agentRunListEntryForTransport);
|
|
8105
8519
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
8106
|
-
res.end(serializeJSON({ runs, total:
|
|
8520
|
+
res.end(serializeJSON({ runs, total: stored.length, limit }));
|
|
8107
8521
|
return;
|
|
8108
8522
|
}
|
|
8109
8523
|
/**
|
|
@@ -8641,6 +9055,14 @@ export async function startLocalServer(opts) {
|
|
|
8641
9055
|
res.end(serializeJSON({ error: 'Agent run not found.' }));
|
|
8642
9056
|
return;
|
|
8643
9057
|
}
|
|
9058
|
+
if (run.status !== 'blocked' || run.stopReason !== 'blocked') {
|
|
9059
|
+
res.writeHead(409, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9060
|
+
res.end(serializeJSON({
|
|
9061
|
+
code: 'REPAIR_CAPABILITY_REQUIRED',
|
|
9062
|
+
error: 'Only a terminal blocked run with a blocked stop reason may derive an analytical repair.',
|
|
9063
|
+
}));
|
|
9064
|
+
return;
|
|
9065
|
+
}
|
|
8644
9066
|
const source = analyticalFailedRunFromAgentRun(run);
|
|
8645
9067
|
if (!source) {
|
|
8646
9068
|
res.writeHead(409, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
@@ -8679,25 +9101,11 @@ export async function startLocalServer(opts) {
|
|
|
8679
9101
|
if (req.method === 'POST' && /^\/api\/agent-runs\/[^/]+\/cancel$/.test(path)) {
|
|
8680
9102
|
const match = path.match(/^\/api\/agent-runs\/([^/]+)\/cancel$/);
|
|
8681
9103
|
const id = decodeURIComponent(match?.[1] ?? '');
|
|
8682
|
-
|
|
8683
|
-
if (!controller) {
|
|
9104
|
+
if (!cancelActiveAgentRun(id)) {
|
|
8684
9105
|
res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
8685
9106
|
res.end(serializeJSON({ ok: false, error: 'This run is no longer active.' }));
|
|
8686
9107
|
return;
|
|
8687
9108
|
}
|
|
8688
|
-
const progress = agentRunStore.getProgress(id);
|
|
8689
|
-
if (progress) {
|
|
8690
|
-
agentRunStore.saveProgress({
|
|
8691
|
-
...progress,
|
|
8692
|
-
lifecycle: {
|
|
8693
|
-
...progress.lifecycle,
|
|
8694
|
-
state: 'cancelling',
|
|
8695
|
-
revision: progress.lifecycle.revision + 1,
|
|
8696
|
-
updatedAt: new Date().toISOString(),
|
|
8697
|
-
},
|
|
8698
|
-
});
|
|
8699
|
-
}
|
|
8700
|
-
controller.abort(new Error('Stopped by user.'));
|
|
8701
9109
|
res.writeHead(202, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
8702
9110
|
res.end(serializeJSON({ ok: true, id }));
|
|
8703
9111
|
return;
|
|
@@ -8842,7 +9250,10 @@ export async function startLocalServer(opts) {
|
|
|
8842
9250
|
return;
|
|
8843
9251
|
}
|
|
8844
9252
|
res.writeHead(201, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
8845
|
-
|
|
9253
|
+
// Same run, same reader as the stream above, so it ships the same
|
|
9254
|
+
// projection. Sending the stored record here instead made an ordinary
|
|
9255
|
+
// completed Ask a 4.66 MB reply.
|
|
9256
|
+
res.end(serializeJSON({ run: slimAgentRunForTransport(completedRun) }));
|
|
8846
9257
|
}
|
|
8847
9258
|
finally {
|
|
8848
9259
|
if (runId)
|
|
@@ -17527,21 +17938,31 @@ function connectionDriverLabel(connection) {
|
|
|
17527
17938
|
* The notebook SPA expects columns as string[] (just names).
|
|
17528
17939
|
*/
|
|
17529
17940
|
function normalizeQueryResult(result, semanticRefs) {
|
|
17530
|
-
const
|
|
17531
|
-
|
|
17941
|
+
const canonical = normalizeCanonicalQueryResult({
|
|
17942
|
+
columns: result?.columns,
|
|
17943
|
+
rows: result?.rows,
|
|
17944
|
+
rowCount: result?.rowCount,
|
|
17945
|
+
executionTime: result?.executionTime,
|
|
17946
|
+
executionTimeMs: result?.executionTimeMs,
|
|
17947
|
+
truncated: result?.truncated,
|
|
17948
|
+
resultFingerprint: result?.resultFingerprint,
|
|
17949
|
+
executionReceipt: result?.executionReceipt,
|
|
17950
|
+
trustState: result?.trustState,
|
|
17951
|
+
answerTier: result?.answerTier,
|
|
17952
|
+
});
|
|
17532
17953
|
const rawRows = Array.isArray(result?.rows) ? result.rows : [];
|
|
17533
|
-
const rows =
|
|
17954
|
+
const rows = canonical.rows.slice(0, NOTEBOOK_EXECUTE_PREVIEW_ROW_LIMIT);
|
|
17534
17955
|
const hasRefs = semanticRefs && (semanticRefs.metrics.length > 0 || semanticRefs.dimensions.length > 0);
|
|
17535
17956
|
return {
|
|
17536
|
-
columns,
|
|
17957
|
+
columns: canonical.columns,
|
|
17537
17958
|
rows,
|
|
17538
|
-
rowCount:
|
|
17539
|
-
|
|
17540
|
-
|
|
17541
|
-
|
|
17542
|
-
|
|
17543
|
-
|
|
17544
|
-
...(
|
|
17959
|
+
rowCount: canonical.rowCount,
|
|
17960
|
+
resultFingerprint: canonical.resultFingerprint,
|
|
17961
|
+
executionTime: canonical.executionTime ?? 0,
|
|
17962
|
+
...(rawRows.length > rows.length || canonical.truncated ? { truncated: true } : {}),
|
|
17963
|
+
...(canonical.executionReceipt ? { executionReceipt: canonical.executionReceipt } : {}),
|
|
17964
|
+
...(canonical.trustState ? { trustState: canonical.trustState } : {}),
|
|
17965
|
+
...(canonical.answerTier ? { answerTier: canonical.answerTier } : {}),
|
|
17545
17966
|
...(hasRefs ? { semanticRefs } : {}),
|
|
17546
17967
|
};
|
|
17547
17968
|
}
|
|
@@ -28888,26 +29309,25 @@ function notebookResearchContextPreview(contextPack) {
|
|
|
28888
29309
|
};
|
|
28889
29310
|
}
|
|
28890
29311
|
function normalizeNotebookAgentResult(result) {
|
|
28891
|
-
const
|
|
28892
|
-
|
|
28893
|
-
|
|
28894
|
-
|
|
28895
|
-
|
|
28896
|
-
|
|
28897
|
-
|
|
28898
|
-
|
|
28899
|
-
|
|
28900
|
-
|
|
28901
|
-
const rows = Array.isArray(result.rows)
|
|
28902
|
-
? result.rows
|
|
28903
|
-
.filter((row) => Boolean(row && typeof row === 'object' && !Array.isArray(row)))
|
|
28904
|
-
.map((row) => row)
|
|
28905
|
-
: [];
|
|
29312
|
+
const canonical = normalizeCanonicalQueryResult({
|
|
29313
|
+
columns: result.columns,
|
|
29314
|
+
rows: result.rows,
|
|
29315
|
+
rowCount: result.rowCount,
|
|
29316
|
+
executionTime: result.executionTime,
|
|
29317
|
+
resultFingerprint: result.resultFingerprint,
|
|
29318
|
+
executionReceipt: result.executionReceipt,
|
|
29319
|
+
trustState: result.executableArtifact?.trustState,
|
|
29320
|
+
answerTier: result.answerTier,
|
|
29321
|
+
});
|
|
28906
29322
|
return {
|
|
28907
|
-
columns,
|
|
28908
|
-
rows,
|
|
28909
|
-
rowCount:
|
|
28910
|
-
|
|
29323
|
+
columns: canonical.columns,
|
|
29324
|
+
rows: canonical.rows,
|
|
29325
|
+
rowCount: canonical.rowCount,
|
|
29326
|
+
resultFingerprint: canonical.resultFingerprint,
|
|
29327
|
+
executionTime: canonical.executionTime ?? 0,
|
|
29328
|
+
...(canonical.truncated ? { truncated: true } : {}),
|
|
29329
|
+
...(canonical.executionReceipt ? { executionReceipt: canonical.executionReceipt } : {}),
|
|
29330
|
+
...(canonical.answerTier ? { answerTier: canonical.answerTier } : {}),
|
|
28911
29331
|
};
|
|
28912
29332
|
}
|
|
28913
29333
|
function notebookResearchSummary(question, result, error) {
|