import type { ProviderDispatchPhaseV1, ProviderEgressPurpose } from '@duckcodeailabs/dql-core'; import { QueryExecutor, type ConnectionConfig, type DatabaseConnector, type QueryResult, type SQLParamSpec } from "@duckcodeailabs/dql-connectors"; import { type NotebookCell, type SemanticRefResolutionOptions } from '@duckcodeailabs/dql-notebook'; import { type SemanticLayer, type MetricDefinition, type SemanticLayerProviderConfig, type DashboardDocument, type DashboardGridItem, type DQLManifest, type DqlArtifactReference, type ManifestBlock, type DomainInput, type AnalyticalRepairCapabilityV1 } from '@duckcodeailabs/dql-core'; import { type AgenticSqlExecutionCapabilityV1, type SqlAuthorizationCheck } from '@duckcodeailabs/dql-agent'; import type { AgentRunner as LLMAgentRunner, ProviderDispatchTerminalEvidence, ProviderDispatchEvidenceSink, ProviderId } from './llm/types.js'; import { type JoinPatternCandidate, type AnalyticalFreshnessRequestV1, type AnalyticalFailedRunV1, type AgentAnswer, type WarehouseSqlFailureV1, type ConversationTurnInput, type AgentResultPayload, type AgentSchemaTable, type RuntimeSchemaSnapshot, type ProposalResult, type ProposePlan, type ProposePlanCandidate, type ProposeConfigInput, type DomainContextEnvelope, type ResolveDomainContextInput, type AgentRun, type AgentRunExecutors, type AgentRunRequest, type AgentRunBudget, type AgentRunRequestedMode, type AgentRunRoute, type AgentRunStopReason, type AgentRunTrustState, type IntentDecision, type ProviderDispatchEvent, type ProviderResultRowEgressPolicy, type AnalyticalTaskV1, type AnalyticalTaskDependencyBindingV1, type AnalyticalTaskDependencyResolution } from '@duckcodeailabs/dql-agent'; import type { NotebookResearchDqlArtifact } from '@duckcodeailabs/dql-project'; import { type InvariantResult } from '@duckcodeailabs/dql-governance'; import { type BlockStudioImportInputMode, type BlockStudioImportSource, type BlockStudioImportSourceKind, type DqlGenerationSession } from './block-studio-import.js'; import { type ConnectionMetadataScopeInput } from './warehouse-metadata.js'; import { prepareBlockInvocation } from './block-invocation.js'; export declare const APP_SOURCE_REUSABLE_TAG = "app-source"; export interface ProjectConfig { project?: string; manifestVersion?: 1 | 2 | 3; modeling?: { mode?: 'dbt-first'; }; layout?: { version?: number; mode?: string; skillsPath?: string; }; defaultConnection?: ConnectionConfig; defaultConnectionName?: string; connections?: Record; dataDir?: string; semanticLayer?: SemanticLayerProviderConfig; dbt?: { projectDir?: string; manifestPath?: string; profilesDir?: string; repoUrl?: string; branch?: string; subPath?: string; }; preview?: { port?: number; theme?: string; open?: boolean; }; notebook?: { staging?: { maxRows?: number; maxBytes?: number; timeoutSeconds?: number; expiryDays?: number; }; }; agent?: { runtimeValueGrounding?: { /** Runtime value lookup is opt-in because query literals may be sensitive. */ mode?: 'disabled' | 'safe_automatic'; /** Fully-qualified `schema.table.column` names approved by a project admin. */ searchSafeColumns?: string[]; }; /** * How many executed rows may reach the AI provider when it writes the answer. * Defaults to a bounded, redacted sample: a model that cannot see the values * cannot describe them, and the fallback for that is a `column: value` dump. * Set `mode: 'disabled'` to keep every cell value on the host — narration * still runs, grounded in column names and computed statistics only. */ providerResultRowEgress?: { mode?: 'bounded_sample' | 'disabled'; /** Clamped to 0..20. Zero is treated as the kill-switch. */ maxNarrationRows?: number; }; /** * Which orchestrator answers a turn, migrated one lane at a time. * * `legacy` (the default) is the 10k-line answer loop. `agentic` routes the * listed lanes through the new loop with the old one as fallback. `shadow` * runs the new path for comparison only and never serves its answer. * * Defaults to legacy with no lanes on purpose: a flag that defaults on is * not a migration control, it is a release. */ orchestrator?: { mode?: 'legacy' | 'shadow' | 'agentic'; lanes?: string[]; maxIterations?: number; fallbackOnError?: boolean; }; }; metadataScopes?: Record; /** Optional `dql propose` conventions (classifier + bounded selection). */ propose?: ProposeConfigInput; } export declare function resolveProjectSemanticConfig(projectConfig: ProjectConfig, projectRoot: string): SemanticLayerProviderConfig | undefined; export interface DbtProfileConnectionCandidate { id: string; profileName: string; targetName: string; adapter: string; path: string; connection: ConnectionConfig; missingFields: string[]; warnings: string[]; } export interface ConnectorInstallStatus { driver: 'duckdb' | 'snowflake' | 'databricks'; label: string; packageName?: string; packageSpec?: string; installed: boolean; builtIn: boolean; installPath: string; installCommand?: string; } export interface LocalServerOptions { rootDir: string; projectRoot?: string; executor: QueryExecutor; connection?: ConnectionConfig | null; preferredPort: number; /** * Host the HTTP server binds to. Defaults to `127.0.0.1` (loopback only) * for security. Set to `0.0.0.0` when running inside a container so the * port is reachable from the host. Honours `DQL_HOST` env var when unset. */ host?: string; /** Required for non-loopback bindings. Defaults to `DQL_SERVER_TOKEN`. */ authToken?: string; /** Exact browser origins allowed for non-loopback API access. */ allowedOrigins?: string[]; /** * Receives the underlying HTTP server once created, so short-lived callers * (e.g. `dql agent ask` starting an ephemeral runtime) can `close()` it and * let the process exit instead of hanging on an open listener. */ captureServer?: (server: import('node:http').Server) => void; /** * Test/embedding seam for deterministic agent-run execution without a live LLM. * Production callers leave this unset and use the default governed executors. */ agentRunExecutors?: AgentRunExecutors; /** Host-owned rollback seam; never read from client request payloads. */ requireMeaningCallForNaturalLanguage?: boolean; } export interface AppCopilotResearchAgentInput { appId: string; dashboardId?: string; sourceTileId?: string; sourceBlockId?: string; title?: string; question: string; intent: string; context?: unknown; mode?: 'sql_and_memo' | 'memo_only'; generatedSql?: string; metrics?: Record; drivers?: Array>; resultPreviews?: unknown[]; summaryHint?: string; recommendationHint?: string; sqlError?: string; sqlErrorKind?: string; hasReportEvidence?: boolean; } /** AGT-007 / AGT-022: App-specific context adapter into the uniform AgentRun. */ export declare function buildAppCopilotResearchAgentRequest(input: AppCopilotResearchAgentInput): AgentRunRequest; /** UI catalog fallback labels are not declared project domains. */ /** * Resolve a UI-pinned domain scope, tolerating one that no longer exists. * * The Ask scope persists across reloads, so a domain that was renamed, deleted, * or simply mistyped would otherwise throw `Unknown domain: x` and BLOCK every * question with "no answer produced" until the user found the chip and cleared * it. A scope that cannot be resolved should widen the search, never wedge the * surface. Anything other than an unresolvable scope still throws, because that * is a real contract violation worth reporting. */ export declare function resolveUiDomainContext(input: ResolveDomainContextInput): DomainContextEnvelope | undefined; /** * Embedding models we offer by name so a user never has to know one. * * Retrieval quality is bounded by the embedder, and picking one is the single * step between lexical-only matching and real meaning-based recall — so the * product should name good defaults rather than expect a model string. * `dimensions` is informational: the index records whatever the provider * actually returns. */ export declare const EMBEDDING_MODEL_CATALOG: { readonly ollama: readonly [{ readonly model: "nomic-embed-text"; readonly label: "Nomic Embed Text"; readonly dimensions: 768; readonly size: "274 MB"; readonly recommended: true; readonly description: "Best general default. Strong paraphrase matching for metric and dimension descriptions."; }, { readonly model: "mxbai-embed-large"; readonly label: "MxBai Embed Large"; readonly dimensions: 1024; readonly size: "670 MB"; readonly recommended: false; readonly description: "Higher quality, slower and larger. Worth it for very large catalogs."; }, { readonly model: "all-minilm"; readonly label: "All-MiniLM"; readonly dimensions: 384; readonly size: "46 MB"; readonly recommended: false; readonly description: "Smallest and fastest. Use on constrained machines."; }]; readonly openai: readonly [{ readonly model: "text-embedding-3-small"; readonly label: "text-embedding-3-small"; readonly dimensions: 1536; readonly size: "hosted"; readonly recommended: true; readonly description: "Cheapest hosted option and strong for catalog text."; }, { readonly model: "text-embedding-3-large"; readonly label: "text-embedding-3-large"; readonly dimensions: 3072; readonly size: "hosted"; readonly recommended: false; readonly description: "Highest quality hosted embedding; higher cost per index."; }]; }; export declare function normalizeAgentRunDomain(value: unknown): string | undefined; export declare function parseAgentRunRequestBody(body: unknown): { request?: AgentRunRequest; error?: string; }; export declare function agentRunDeadlineMs(request: Pick, env?: NodeJS.ProcessEnv, activeProviderId?: string | null): number; /** Result shape retained by the bounded compound scheduler. */ type CompoundDependencyFailure = Extract; export interface ScheduledCompoundAnalyticalTask { task: AnalyticalTaskV1; value?: T; error?: string; dependencyError?: CompoundDependencyFailure; } /** * Run ready independent compound clauses concurrently, but wait for a typed * parent result before executing a declared dependent clause. The scheduler * itself has no authority to query or filter; callers supply both execution and * a dependency resolver so immutable-plan and SQL guards remain unchanged. */ export declare function scheduleCompoundAnalyticalTasks(input: { tasks: AnalyticalTaskV1[]; runTask: (task: AnalyticalTaskV1, binding?: AnalyticalTaskDependencyBindingV1) => Promise>; resolveDependency: (task: AnalyticalTaskV1, parent?: ScheduledCompoundAnalyticalTask) => AnalyticalTaskDependencyResolution; }): Promise>>; /** What kind of narration a settled run has earned, and how many rows may ground it. */ export type AgentNarrationPlan = { mode: 'skip'; reason: 'no_answer' | 'no_provider' | 'nothing_to_narrate'; } /** Claim-verified narration over the immutable fact set (analytical graph lanes). */ | { mode: 'verified_facts'; maxRows: number; } /** Preview-grounded narration for lanes that executed rows without a fact set. */ | { mode: 'preview_grounded'; maxRows: number; }; export type AgentNarrationAnswer = Pick & Partial>; /** * Decide how a settled answer gets its business-facing prose. * * This deliberately does NOT read `requestedMode`. Gating narration on * `requestedMode === 'research'` meant every ordinary Ask — which the UI sends * as `'auto'` — skipped synthesis entirely and shipped the answer loop's * deterministic fact-join as the primary answer: the reported `column: value` * dump. Narration is owed to any run that actually produced values. * * Certification, a DQL artifact, and the exploratory candidate are no longer * vetoes. EXP-001's grain concern is real, but the answer to "the model might * relabel an entity-level measure" is to VERIFY the claims against the fact set * (`verified_facts`) and pass the grain statement in as a caveat — not to refuse * to write a sentence. */ export declare function planAgentRunNarration(governedAnswer: AgentNarrationAnswer, context: { requestedMode?: AgentRunRequestedMode; providerAvailable: boolean; rowEgress: ProviderResultRowEgressPolicy; }): AgentNarrationPlan; /** * Boolean view of {@link planAgentRunNarration}, kept for callers and tests that * only need "does this run narrate at all". */ export declare function shouldSynthesizeAgentRunAnswer(governedAnswer: AgentNarrationAnswer, requestedMode?: AgentRunRequestedMode | undefined): boolean; /** * AGT-010 — the semantic route label is descriptive, while the exact * route-specific aggregation proof is authoritative for governed trust. * Missing proof remains blocked for legacy or malformed results. */ /** * Trust for ONE answer, by the same rule the single-answer path uses: a route * label is not authority, and a semantic route earns `governed` only when its * aggregation proof actually passed. */ export declare function trustStateForAgentAnswer(answer: Pick): AgentRunTrustState; /** * A compound answer is exactly as trustworthy as its WEAKEST successful child. * * The previous rule was `every child completed ? 'governed' : 'review_required'`, * which stamped `governed` on a parent whose children were review-required * generated SQL — completion is not proof. That is a governance violation and * the worst possible failure for this product: the reader is told a number * carries governed authority when nothing proved it. * * `certified` is deliberately NOT reachable here. Certified trust is granted * only by executing the exact certified artifact; a parent that merely * assembled certified children did not execute one, so it caps at `governed`. */ export declare function compoundTrustState(childTrust: readonly AgentRunTrustState[]): AgentRunTrustState; /** Neutral parent outcome: governed only when every child completed governed. */ export declare function compoundStopReason(completedCount: number, childCount: number, trustState: AgentRunTrustState): AgentRunStopReason; export declare function semanticAnswerHasPassedAggregationProof(governedAnswer: Pick): boolean; export declare function agentAnswerHasExecutionFailure(governedAnswer: Pick): boolean; /** * Return only a router/producer-issued analytical gap witness. * * `terminalOutcome.kind === 'modeling_gap'` is deliberately insufficient to * claim a relationship problem. Older receipts and generic tuple failures * return `undefined`; callers render generic coverage guidance for those. */ export declare function persistedAnalyticalGapWitness(routeDecision: IntentDecision | undefined): NonNullable['gap']> | undefined; /** Rebuild the immutable failed-run input from the artifact retained by API-007. */ export declare function analyticalFailedRunFromAgentRun(run: AgentRun): AnalyticalFailedRunV1 | undefined; /** Exact failed statement retained on an immutable run, if one exists. */ export declare function repairableSqlFromAgentRun(run: AgentRun): string | undefined; /** Editable DQL retained on an immutable failed run, including its bound values. */ export declare function repairableDqlArtifactFromAgentRun(run: AgentRun): DqlArtifactReference | undefined; /** * Keep the parameter surface immutable while repairing malformed DQL. A parser * cannot reliably read a broken wrapper, so collect both declared parameter * names and SQL interpolation names with a deliberately small lexical pass. */ export declare function dqlRepairParameterContract(source: string): string[]; /** Structured producer failure retained on the source run. */ export declare function warehouseFailureFromAgentRun(run: AgentRun): WarehouseSqlFailureV1 | undefined; /** Validate the immutable custom wrapper before any automatic repair is offered. */ export declare function validAskRepairDqlWrapper(source: string): boolean; /** Build retained automatic-repair authority from analytical failure state only. */ export declare function analyticalRepairCapabilityForAgentRun(run: AgentRun, resolvedTargetFingerprint?: string): AnalyticalRepairCapabilityV1 | undefined; export declare function targetGenerationFingerprint(connection: ConnectionConfig, connectionName?: string): string; /** * Retain planning and retrieval evidence for a successful derived repair while * deliberately dropping the source failure and any result-bound facts. This * is the presentation contract used by "How it was answered" and by grounded * follow-ups after repair. */ export declare function repairPresentationContextFromAgentRun(run: AgentRun): Record; /** Permission, authentication, policy and unsafe-query failures are never AI-repaired. */ export declare function analyticalFailureAllowsDeterministicRetry(failure?: WarehouseSqlFailureV1): boolean; /** Structured policy/access refusals stay terminal even when retained SQL exists. */ export declare function analyticalFailureAllowsAppRepair(failure?: AgentAnswer['analyticalFailure']): boolean; /** * Presentation projection of a run, for anything that SHIPS it — thread history * and the live SSE stream alike. * * A stored run is dominated by DUPLICATED diagnostics rather than content. On a * real 2.4 MB run: `diagnosticReceipt` is held twice — once top-level and once * inside `artifacts[0].payload`, byte-identical — and each copy embeds its own * full `steps` and `artifacts`, which embed the payload again. The part the UI * actually renders (`payload.result`) was 3 KB. * * Everything the thread view reads is preserved, including * `diagnosticReceipt.failure.message`; only the self-referential nesting and the * unread `considered` block are dropped. The complete record stays available * from the existing `GET /api/agent-runs/:id` run-state route. */ export declare function slimAgentRunForTransport(run: AgentRun): AgentRun; /** * INDEX projection for `GET /api/agent-runs` — strictly lighter than the * presentation projection above. * * A run history list renders one ROW per run: question, route, status, trust, * timing, summary. It never renders an answer body, an event stream, or a step * trace. Shipping those anyway dominated the response: on 300 real stored runs * a 20-run page was 47.61 MB whole and still 6.80 MB under the presentation * projection, of which 6.35 MB was `artifacts[].payload` alone. * * Artifact identity (`id`/`kind`/`title`/`trustState`) is kept so a row can say * what it produced; only the payload body is dropped. The complete immutable * record stays available from `GET /api/agent-runs/:id`. * * Acceptance: PERF-003, E2E-022. */ export declare function agentRunListEntryForTransport(run: AgentRun): AgentRun; export declare function conversationTurnInputFromRun(run: AgentRun): ConversationTurnInput; /** UI-007 / E2E-005: report the installed CLI version that owns this runtime. */ export declare function readDqlRuntimeVersion(runtimeUrl?: string): string; export declare class RunScopedProviderDispatchEvidence implements ProviderDispatchEvidenceSink { private readonly policy; private readonly runBudget?; private readonly rowEgress; private readonly receipts; private readonly phaseCounts; private currentRoute; /** Wall-clock start of the previous dispatch, used to learn this provider's cost. */ private lastDispatchStartedAtMs?; private readonly observedDispatchDurations; /** * How long the NEXT dispatch is likely to take, learned from this run. * * Counting dispatches is not a time budget. A provider costing ~13s a call * will burn a 45s deadline in three calls and get killed mid-flight, so the * run ends with nothing — strictly worse than stopping early and answering * from what it already has. */ private expectedDispatchMs; /** True when the remaining wall clock cannot fit another provider call. */ private cannotFitAnotherDispatch; /** * The run budget's own clock when there is one, so dispatch cost is measured * against the same timeline the deadline is enforced on (and stays injectable * for deterministic tests). */ private nowMs; private recordDispatchStart; constructor(policy: { total: number; meaningResolution: number; generationGroup: number; /** * Narration has its own bucket. It used to share `generationGroup`, so a * run that spent its generation attempts had nothing left to write the * answer with and threw `PROVIDER_DISPATCH_BUDGET_EXHAUSTED` — which is how * an ordinary Ask ended up shipping its deterministic draft as the answer. */ narration: number; repair: number; }, runBudget?: AgentRunBudget | undefined, rowEgress?: ProviderResultRowEgressPolicy); setRoute(route: AgentRunRoute | undefined): void; mayStartToolCall(): boolean; observe(event: ProviderDispatchEvent, context: { purpose: ProviderEgressPurpose; dispatchPhase: ProviderDispatchPhaseV1; optIn: boolean; serializedResultShape?: { resultRowCount: number; columnCount: number; }; cumulativeResultRowCount?: number; }): Record; snapshot(fallbackReason?: string): ProviderDispatchTerminalEvidence; } /** * Final physical generated-SQL boundary. * * This receives the exact prepared statement immediately before the connector * callback. It intentionally validates before invoking `execute`: a bad * capability or unproven prepared reference must result in zero warehouse * calls, not a post-execution warning. It is module-exported only for the * local-runtime boundary harness; it is never an HTTP API or durable artifact. * * @internal */ export declare function executePreparedAgenticSqlBoundary(input: { capability?: AgenticSqlExecutionCapabilityV1; preparedSql: string; bindings: unknown; scope?: SqlAuthorizationCheck; execute: () => Promise; }): Promise; export declare function startLocalServer(opts: LocalServerOptions): Promise; export declare function assertLocalQueryRuntimeReady(executor: QueryExecutor, connection: ConnectionConfig): Promise; export declare function formatLocalQueryRuntimeError(connection: ConnectionConfig, error: unknown): string; export interface ConnectionTestResult { ok: boolean; message: string; details?: Record; } export declare function validateConnectionForTest(connector: DatabaseConnector, connection: ConnectionConfig): Promise; /** * Normalize connector QueryResult → SPA-friendly shape. * Connector returns columns as ColumnMeta[] ({name,type,driverType}). * The notebook SPA expects columns as string[] (just names). */ declare function normalizeQueryResult(result: any, semanticRefs?: { metrics: string[]; dimensions: string[]; }): { columns: string[]; rows: Record[]; rowCount: number; resultFingerprint: string; executionTime: number; truncated?: boolean; executionReceipt?: AgentResultPayload['executionReceipt']; trustState?: string; answerTier?: string; semanticRefs?: { metrics: string[]; dimensions: string[]; }; }; export declare function resolveDefaultLLMProvider(projectRoot: string): ProviderId | null; /** * Resolve the runner that produces a GOVERNED answer (answer-loop with a completion * provider). Subscription CLI providers (Claude Code / Codex) are forced through the * answer-loop runner — never the MCP `claudeCodeRunner`, which doesn't emit a governed * answer envelope. Everything else uses the Settings-resolved default runner. */ export declare function resolveGovernedAnswerRunner(projectRoot: string): { provider: ProviderId; runner: LLMAgentRunner; } | null; export declare function serializeJSON(value: unknown): string; /** * Extract the single time value returned by an authorized semantic freshness * lookup. Adapter aliases may differ by warehouse, so typed date metadata is * the only fallback; arbitrary string columns are never treated as freshness. * Acceptance: AGT-019, SEC-004. */ export declare function analyticalFreshnessObservedThrough(result: QueryResult, request: AnalyticalFreshnessRequestV1): string; /** The Domain shape the frontend codes to (spec 17 shared contract). */ interface DomainDto { id: string; name: string; parent?: string; owner?: string; businessOwner?: string; boundedContext?: string; sourceSystems?: string[]; description?: string; primaryTerms?: string[]; tags?: string[]; businessOutcome?: string; reviewCadence?: string; inScope?: string[]; outOfScope?: string[]; dbtGroups?: string[]; dbtPaths?: string[]; dbtTags?: string[]; semanticDomains?: string[]; semanticTags?: string[]; sourcePath?: string; blockCount?: number; skillCount?: number; termCount?: number; } /** List authored domains with per-domain block/skill/term counts. */ export declare function listDomains(projectRoot: string): DomainDto[]; /** Validate + normalize an inbound `{ domain }` body into a DomainInput. */ export declare function parseDomainInput(raw: unknown, fallbackId?: string): DomainInput | null; type AiModelingCandidate = { kind: 'upsert_entity'; id?: string; dbtModel?: string; businessName?: string; businessContext?: string; grain?: string; /** Analytical key column names asserted on the bound dbt model. */ keys: string[]; analyticalRole?: unknown; } | { kind: 'upsert_relationship'; id?: string; from?: string; to?: string; keys: Array<{ from: string; to: string; }>; verb?: string; description?: string; rationale?: string; cardinality?: unknown; fanout?: unknown; }; /** Tolerant JSON extraction; a malformed provider reply yields no operations. */ export declare function parseAiModelingOperations(raw: string): AiModelingCandidate[]; /** * API-004 / E2E-006: save-time compile for Block Studio. The manifest is * replaced atomically so lineage readers and agent retrieval never observe a * half-written snapshot, and an existing manifest survives compilation errors. */ export declare function compileBlockStudioManifest(projectRoot: string, projectConfig?: ProjectConfig): DQLManifest; export declare function findProjectRoot(startDir: string): string; export declare function loadProjectConfig(projectRoot: string): ProjectConfig; export interface SkillPathSettings { path: string; resolvedPath: string; exists: boolean; skillCount: number; errorCount: number; } /** Current Git-backed Skills folder and what the loader can read from it. */ export declare function buildSkillPathSettings(projectRoot: string): SkillPathSettings; /** * Shape returned by `/api/propose`. Drives the notebook Readiness surface: * a readiness summary plus a ranked queue of DRAFT proposals, each carrying its * stored Certifier verdict ("what's missing to certify"). The endpoint NEVER * certifies — proposals always render as AI-Generated drafts and route into the * existing human review/certify flow. */ export interface ProposeReadinessResult { /** True when a dbt manifest was found and the engine ran. */ ready: boolean; /** * Why the engine could not run (no dbt manifest). Present only when * `ready === false`; the UI shows this as a "what to do next" hint. */ reason?: string; summary: { projectName?: string; /** dbt models the engine scanned (whole manifest). */ modelsScanned: number; /** Models classified `business` by the cascade. */ businessModels: number; /** Models classified `plumbing` and excluded from generation. */ plumbingExcluded: number; /** Semantic metrics discovered in the manifest. */ metricsFound: number; /** Selected (bounded, business-only) proposals the engine ranked. */ proposalsRanked: number; /** Drafts already written to the project (skipped on re-run). */ draftsExisting: number; /** Proposals with zero blocking certifier errors (closest to certifiable). */ readyForReview: number; /** Total blocking certifier errors across the queue. */ blockingTotal: number; /** Total certifier warnings across the queue. */ warningTotal: number; /** Review telemetry for the ranked queue. */ reviewTelemetry?: { existingDrafts: number; medianReviewAgeHours: number | null; readyForReviewRate: number | null; estimatedReviewMinutes: number; }; }; /** * Deterministic PLAN of the bounded, business-only seed (writes nothing). * Drives the plan/approve gate in the Get Started flow. */ plan: ProposePlan; /** Ranked DRAFT proposals for the selected scope (engine order preserved). */ proposals: ReviewableProposalResult[]; } export interface ReviewableProposalResult extends ProposalResult { /** One-screen review handoff for the certification flywheel. */ review: { queueRank: number; status: 'new' | 'draft_exists' | 'ready_for_review'; priorityScore: number; blockingCount: number; warningCount: number; estimatedReviewMinutes: number; draftPath: string; draftExists: boolean; firstSeenAt?: string; lastUpdatedAt?: string; reviewAgeHours?: number; certifyCommand: string; payload: { question: string; model: string; domain: string; sqlPreview?: string; outputs: string[]; grain?: string; pattern?: string; evidence: string[]; resultSample: { status: 'not_run'; rows: []; }; nearestCertifiedBlock?: { name: string; path?: string; domain?: string; overlapScore: number; sharedOutputs: string[]; }; }; }; } /** * Core of the `/api/propose` endpoint, factored out as a pure function so it can * be unit-tested without standing up an HTTP server. * * It reuses the existing `propose` engine from `@duckcodeailabs/dql-agent` * verbatim (no inference/ranking logic is duplicated here) in `dryRun` mode so a * readiness preview never mutates the project. Every returned proposal is a * `status: draft` block with the engine's stored Certifier verdict attached. */ export declare function buildProposeReadiness(projectRoot: string, projectConfig?: ProjectConfig, options?: { owner?: string; limit?: number; }): ProposeReadinessResult; export interface SemanticCompostingChangesetResult { ready: boolean; reason?: string; summary: { certifiedBlocksScanned: number; eligibleMetricClusters: number; candidatesRanked: number; existingDrafts: number; donorBlocksUsed: number; minSupport: number; }; candidates: SemanticCompostingMetricCandidate[]; /** Recurring join shapes across certified blocks (composting v2, W4.4). */ joinCandidates: JoinPatternCandidate[]; prBody: string; } export interface SemanticCompostingMetricCandidate { id: string; kind: 'metric'; name: string; label: string; description: string; domain: string; table: string; type: MetricDefinition['type']; sql: string; filter?: string; status: 'draft'; support: number; donorBlocks: Array<{ name: string; path: string; domain?: string; outputs: string[]; filters: string[]; }>; draftPath: string; draftExists: boolean; yaml: string; provenance: { normalizedExpression: string; normalizedFilter?: string; recurringFilters: string[]; }; review: { priorityScore: number; rationale: string; }; } export interface GenerateSemanticCompostingDraftsResult { ready: boolean; reason?: string; draftsWritten: number; draftsSkipped: number; candidates: SemanticCompostingMetricCandidate[]; paths: string[]; prBody: string; prBodyPath?: string; } /** * Mine certified block clusters for recurring metric definitions and render a * reviewable semantic-layer changeset. This is read-only: callers must confirm * candidate ids through generateSemanticCompostingDrafts before files are written. */ export declare function buildSemanticCompostingChangeset(projectRoot: string, options?: { minSupport?: number; limit?: number; owner?: string; }): SemanticCompostingChangesetResult; export declare function generateSemanticCompostingDrafts(projectRoot: string, candidateIds?: string[], options?: { minSupport?: number; limit?: number; owner?: string; }): GenerateSemanticCompostingDraftsResult; /** * Materialize drafts for an APPROVED scope (selected slugs / domains). Reuses * the propose engine's `onlySlugs` path + the draft writer. Plumbing is never * generated even if an approved slug names a plumbing model. Returns the written * summary so the caller can route into the per-block review flow. */ export interface ProposeGenerateResult { ready: boolean; reason?: string; draftsWritten: number; draftsSkipped: number; proposals: ProposalResult[]; } export declare function generateProposeDrafts(projectRoot: string, slugs: string[], projectConfig?: ProjectConfig, options?: { owner?: string; }): Promise; /** * Build the FILLED transparent preview for ONE proposed candidate slug (spec 14, * part A). Reuses the deterministic `buildProposePreview` engine (real SQL + * Certifier verdict) and best-effort AI enrichment (description/llmContext/ * examples) when a provider is available. Writes NOTHING. Returns `undefined` * when the slug is not part of the bounded, business-only selection. */ export declare function buildProposeCandidatePreview(projectRoot: string, slug: string, owner?: string, projectConfig?: ProjectConfig): Promise; export declare function getConnectorInstallStatuses(projectRoot: string): ConnectorInstallStatus[]; /** * Ensure the active connection's driver package is present at startup. * * The connection CONFIG persists in dql.config.json, but the driver package * (duckdb, snowflake-sdk, …) lives in the gitignored, per-project .dql/connectors * — so a fresh clone, a `npm i -g` upgrade of the CLI, or even a Node version bump * (native bindings are Node-version-specific) leaves a configured connection with * no loadable driver, and the user has to reinstall it by hand from the Connections * page. This installs it once, at boot, so connections just work after any * install/upgrade. Best-effort and non-fatal: an offline machine keeps the manual * "Install" button and a clear message rather than a failed startup. */ export declare function ensureConnectorInstalledForStartup(projectRoot: string, driver: string | undefined): void; export declare function assertConnectionNodeCompatibility(connection: Pick, version?: string): void; export declare function prepareLocalExecution(sql: string, connection: ConnectionConfig, projectRoot: string, projectConfig: ProjectConfig): { sql: string; connection: ConnectionConfig; }; export interface AnalyticalExecutionPreparation { /** SQL supplied by the caller, before host-owned normalization. */ sourceSql: string; /** SQL after internal graph identities have been decoded. */ decodedSql: string; /** SQL after dbt macros and project-relative paths have been resolved. */ preparedSql: string; /** Exact statement handed to the connector (may include a safe row bound). */ executedSql: string; connection: ConnectionConfig; rewrites: Array<{ from: string; to: string; }>; rowBound?: AnalyticalRowBoundResult; } export interface ExecutionServiceInput { sql: string; subject: string; connection: ConnectionConfig; enforceReadOnly?: boolean; rowLimit?: number; sqlParams?: SQLParamSpec[]; variables?: Record; semanticRefs?: { metrics: string[]; dimensions: string[]; }; executePrepared?: (preparation: AnalyticalExecutionPreparation) => Promise; } export interface ExecutionServiceResult { preparation: AnalyticalExecutionPreparation; result: ReturnType; compiledSqlFingerprint: string; resultFingerprint: string; } /** * The single connector boundary for Ask, Notebook, and artifact execution. * Callers compile their route-specific source first; this service owns target * preparation, parameters, execution, and result normalization after that. */ export declare class ExecutionService { private readonly host; constructor(host: { executor: QueryExecutor; projectRoot: string; projectConfig: () => ProjectConfig; }); execute(input: ExecutionServiceInput): Promise; } /** * One host-owned SQL preparation boundary shared by Ask and Notebook. * * Surface-specific compilation happens before this function and surface policy * (for example exploratory join proof) may run around it, but no analytical * entrypoint may decode internal IDs, resolve dbt paths, enforce read-only * execution, or append a preview bound differently anymore. * * Acceptance: API-003, API-006, API-007, EXP-001, E2E-014. */ export declare function prepareAnalyticalExecutionSql(input: { sql: string; subject: string; executor: QueryExecutor; connection: ConnectionConfig; projectRoot: string; projectConfig: ProjectConfig; enforceReadOnly?: boolean; rowLimit?: number; }): Promise; export interface DashboardFilterApplicationResult { sql: string; sqlParams: SQLParamSpec[]; variables: Record; appliedFilters: Array<{ filter: string; binding?: string; mode: 'parameter' | 'predicate'; paramNames: string[]; }>; skippedFilters: Array<{ filter: string; reason: string; }>; } export declare function dashboardRuntimeVariables(dashboard: Pick, overrides?: Record): Record; export interface DashboardFilterOptionSet { filterId: string; values: string[]; truncated: boolean; sourceTileIds: string[]; /** * Run-scoped availability metadata for date controls. The bounds are * derived from safe result columns and are never persisted to the App. */ valueCount?: number; dateRange?: { min: string; max: string; }; } /** Resolve semantic filters only through the component's explicit mapping. */ export declare function dashboardSemanticFiltersForTile(dashboard: Pick, item: Pick, dashboardValues: Record): Array<{ dimension: string; operator: string; values: string[]; }>; type DashboardFilterOptionTile = { tileId: string; status?: string; result?: unknown; filterableColumns?: Array<{ column: string; predicateTarget: string; }>; }; /** * Return bounded, run-scoped filter choices only from columns the server has * already proven safe for predicate filtering. These sampled labels are sent * to the browser for a searchable control and are never written to `.dqld`. */ export declare function collectDashboardFilterOptions(dashboard: Pick, tiles: DashboardFilterOptionTile[], defaultLimit?: number): DashboardFilterOptionSet[]; /** * Resolve only declared tile parameter bindings. Dashboard filters remain * values; they never become SQL identifiers or an implicit name-based binding. */ export declare function dashboardTileParameterValues(input: { item: Pick; dashboardValues: Record; requestValues?: Record; }): Record; export declare function applyDashboardFiltersToBlockExecution(input: { sql: string; sqlParams: SQLParamSpec[]; variables: Record; block: Pick; dashboard: Pick; tileId?: string; tileFilterBindings?: DashboardGridItem['filterBindings']; }): DashboardFilterApplicationResult; export declare function resolveDbtMacrosForExecution(sql: string, projectRoot: string, projectConfig?: ProjectConfig): string; export declare function clampAnalyticalRowBound(rowLimit: number): number; export type AnalyticalRowBoundOutcome = 'appended' | 'existing' | 'skipped'; export interface AnalyticalRowBoundResult { sql: string; outcome: AnalyticalRowBoundOutcome; reason?: string; } /** * Bound a result set WITHOUT rewriting the statement's shape. * * The previous approach wrapped every Ask query as * `SELECT * FROM () AS dql_agent_preview LIMIT n`. That is not a no-op: * - `SELECT * FROM (WITH x AS (…) SELECT …) AS t` is a syntax error on * MSSQL/Fabric, and the model emits CTEs constantly; * - duplicate output column names are legal at top level but not as a * derived table on several engines; * - the inner `ORDER BY` stops being guaranteed, silently breaking top-N. * All three produced "fails in Ask, runs in the notebook" for identical SQL. * * Appending is skipped whenever it cannot be proven safe. Callers must still * truncate rows host-side — that, not this, is what actually enforces the bound. */ export declare function buildRowBoundedSql(sql: string, rowLimit: number | undefined, dialect?: string): AnalyticalRowBoundResult; /** * @deprecated Kept for one release so any out-of-tree caller keeps working. * Use {@link executeAnalyticalSql}, which bounds rows without wrapping. */ export declare function buildAgentPreviewSql(sql: string, rowLimit?: number): string; export interface ExploratorySqlPreflightResult { sql: string; repairs: string[]; blockedReason?: string; } /** * EXP-001 preflight for model-authored exploratory SQL. * * This is deliberately narrow and deterministic: it repairs a singular/plural * qualifier typo only when it resolves to exactly one relation already present * in the query, and it changes SUM to MAX for a non-additive parent attribute * only when the owning entity is retained in GROUP BY. It never invents a * relation, join key, or allocation rule. */ export declare function repairExploratorySqlBeforeExecution(sql: string, schemaContext: AgentSchemaTable[], question?: string, dialect?: string): ExploratorySqlPreflightResult; /** * EXP-003 — make the typed overall ranking contract executable before the host * runs a review-required exploratory candidate. The answer-shape gate remains a * backstop, but a provider's generic LIMIT 100 must not trigger a second model * call when the planner already established top 10. */ export declare function applyRequestedTopNToExploratorySql(sql: string, requestedTopN?: number): string; /** * Bind an already-authored unqualified relation to a unique inspected physical * relation. The caller owns whether that binding is permitted (exploratory * preflight or a frozen certified artifact); this helper itself never adds a * relation, key, join, predicate, or column. */ export declare function qualifyUnambiguousSqlRelationsFromSchema(sql: string, schemaContext: AgentSchemaTable[], repairs: string[], dialect?: string): string; /** One bounded, sampled relationship observation per inferred equality join. */ export declare function buildExploratoryJoinProbeSql(input: { leftRelation: string; leftColumn: string; rightRelation: string; rightColumn: string; }): string; /** * CTE names and nested SELECT aliases are query-internal derived relations, not * warehouse tables. A join whose endpoint is `joy_items` or generated * `subq_2` must be excluded from declared-path enforcement and join probes — * probing it as `FROM "subq_2"` asks Snowflake for an object that only exists * inside the statement. Physical joins inside the derived relation remain in * the parsed join list and are validated/probed on their own. */ export declare function probeableExploratoryJoins(joins: T[], ctes: string[], derivedRelations?: string[]): T[]; /** * Gate a join probe against the declared relationship. Returns an error string * when the UNFILTERED key samples structurally contradict the declaration: * - zero key overlap while both sides have rows (wrong/mistyped key), or * - duplicate keys on the declared "one" side of a *_to_one / one_to_* edge. * Sampling caveat: gates only fire on definitive contradictions, never on low * match rates, so a sparse but real relationship still executes. */ export declare function exploratoryProbeContradiction(rows: unknown[], join: { leftRelation?: string; leftColumn: string; rightRelation?: string; rightColumn: string; }, edge?: { relationshipId: string; fromRelation?: string; toRelation?: string; cardinality: string; }): string | undefined; export interface PreparedSemanticSql { sql: string; semanticRefs: { metrics: string[]; dimensions: string[]; }; unresolvedRefs: string[]; } /** * Shared resolver for `@metric(name)` / `@dim(name)` refs in raw SQL. * Used by notebook SQL execution and Block Studio validation so both paths * behave identically. If the SQL has no refs, returns it unchanged. */ export declare function prepareSemanticSql(sql: string, semanticLayer: SemanticLayer | undefined, options?: SemanticRefResolutionOptions): PreparedSemanticSql; export declare function normalizeProjectConnection(connection: ConnectionConfig, projectRoot: string): ConnectionConfig; export declare function resolveProjectRelativeSqlPaths(sql: string, projectRoot: string, dataDir?: string): string; export declare function staticResponseCacheControl(filePath: string): string; export declare function notebookCellRepairSql(cell: NotebookCell): string | undefined; export declare function notebookDqlSourceAllowsBackgroundRepair(source: string): boolean; export declare function replaceNotebookDqlQueryForRepair(source: string, sql: string): string | undefined; /** * Notebook background repair is deliberately narrower than conversational * repair. Access, policy, target, parameter, and dependency failures require a * user decision; only query-shape failures may enter the one-attempt loop. * Acceptance: API-006, API-007, UI-012, UI-013, SEC-004, E2E-014. */ export declare function notebookFailureAllowsBackgroundRepair(input: { code?: string; message?: string; }): boolean; export declare function applyNotebookRepairRewrites(sql: string, rewrites: Array<{ from: string; to: string; }>): string; export declare function restoreNotebookDqlParameterInterpolations(sql: string, parameters: SQLParamSpec[]): string; /** Normalize DESCRIBE output across Snowflake, DuckDB, and PostgreSQL shapes. */ export declare function schemaColumnsFromDescribeRows(rows: Array>): Array<{ name: string; type: string; }>; /** Parse dbt relation strings without letting relation text become SQL syntax. */ export declare function splitQualifiedRelationIdentifier(value: string): string[] | null; export declare function buildDbtDatabaseSchemaTree(manifest: DQLManifest, limit?: number, physicalIds?: ReadonlySet): Array<{ id: string; label: string; kind: 'schema' | 'table' | 'column'; path?: string; type?: string; children?: Array<{ id: string; label: string; kind: 'schema' | 'table' | 'column'; path?: string; type?: string; children?: unknown[]; }>; }>; export declare function openBlockStudioDocument(projectRoot: string, relativePath: string, semanticLayer?: SemanticLayer): { path: string; source: string; metadata: { name: string; path: string | null; domain: string; folderPath?: string; description: string; owner: string; tags: string[]; reviewStatus?: string; sourceFingerprint: string; }; companionPath: string | null; validation: ReturnType; lastRun?: BlockStudioRunSummary; }; interface BlockStudioRunSummary { rowCount: number; executionTime?: number; columns: string[]; ranAt: string; } type BlockStudioDiagnostic = { severity: 'error' | 'warning' | 'info'; message: string; code?: string; title?: string; field?: string; references?: string[]; correction?: string; technicalDetails?: string; action?: 'edit_source' | 'review_metrics' | 'configure_runtime' | 'edit_parameters' | 'run_again'; location?: { line?: number; column?: number; }; }; interface BlockStudioValidationResult { valid: boolean; saveable: boolean; diagnostics: BlockStudioDiagnostic[]; semanticRefs: { metrics: string[]; dimensions: string[]; segments: string[]; }; chartConfig?: { chart?: string; x?: string; y?: string; color?: string; title?: string; }; executableSql?: string | null; parameters?: ReturnType['parameters']; } export declare function compactBlockStudioRuntimeFailure(failure: string): string; /** * Certification and preview must use the same compiler verdict. A successful * dbt/MetricFlow compile supersedes the native compiler's capability warning; * syntax, unsafe SQL, unknown references, and parameter errors remain intact. */ export declare function reconcileBlockStudioRuntimeValidation(validation: BlockStudioValidationResult, runtime: { sql: string | null; diagnostics: BlockStudioDiagnostic[]; }): BlockStudioValidationResult; /** * Draft persistence is stricter than loose text storage but does not require a * configured execution runtime. Syntax, unsafe SQL, invalid semantic identity, * and parameter-contract errors must be corrected before a block file is * written; runtime availability remains a Run/Certify concern. */ export declare function blockStudioValidationAllowsDraftSave(validation: { diagnostics: BlockStudioDiagnostic[]; }): boolean; /** * Replace the CONTENTS of every string literal with same-length filler, keeping * the quotes and every offset intact. * * Field lookups below scan the whole block source for `key = ...`. A block's * `description` is free text — on an AI-generated block it is literally the * user's question — so a description mentioning `granularity = "day"` or * `dimensions = [...]` was matched as if it were the field itself and silently * replaced the real one. Masking first means a match can only ever land on a * real assignment; reading the value back from the ORIGINAL at the same offsets * keeps it exact. */ export declare function maskDqlStringContents(source: string): string; export declare function parseBlockStudioArrayField(source: string, key: string): string[]; export declare function parseBlockStudioStringField(source: string, key: string): string | undefined; export declare function resolveSemanticTableMapping(executor: QueryExecutor, connection: ConnectionConfig, semanticLayer?: SemanticLayer, projectRoot?: string, connectionId?: string): Promise | undefined>; export declare function buildSemanticTableMapping(semanticLayer: SemanticLayer, rows: Array>): Record | undefined; export declare function validateBlockStudioSource(source: string, semanticLayer?: SemanticLayer): BlockStudioValidationResult; export interface CreateDqlGenerationSessionForProjectOptions { inputPath?: string; inputMode?: BlockStudioImportInputMode; sources?: BlockStudioImportSource[]; sourceKind?: BlockStudioImportSourceKind | 'raw-sql'; domain?: string; owner?: string; tags?: string[]; provider?: string; async?: boolean; persistence?: 'session-only' | 'draft-files'; } export interface CreateDqlArtifactGenerationSessionForProjectOptions { question: string; dqlArtifact: NotebookResearchDqlArtifact; inputPath?: string; inputMode?: BlockStudioImportInputMode; domain?: string; owner?: string; tags?: string[]; generatedSql?: string; contextPackId?: string; routeIntent?: string; sourceBlock?: string; } export declare function createDqlArtifactGenerationSessionForProject(projectRoot: string, options: CreateDqlArtifactGenerationSessionForProjectOptions, semanticLayer?: SemanticLayer): Promise; export declare function createDqlGenerationSessionForProject(projectRoot: string, options: CreateDqlGenerationSessionForProjectOptions, semanticLayer?: SemanticLayer): Promise; /** AI may propose block logic, but ownership is assigned by a human at promotion. */ export declare function sanitizeAgentBlockDraftSource(source: string): string; /** * UI-016 / CONTRACT-002 / AGT-021 — derive the transient Block AI artifact * from the exact governed answer artifact. Semantic drafts are re-rendered * through the one canonical metrics/dimensions writer; custom SQL drafts retain * their query body. Both become ownerless, review-required drafts and never * inherit a saved/certified identity from their answer source. */ export declare function ownerlessReviewDqlArtifactFromAnswer(answer: Pick, question: string): DqlArtifactReference | undefined; export declare function saveBlockStudioArtifacts(projectRoot: string, options: { currentPath?: string; source: string; name: string; domain?: string; folderPath?: string; description?: string; owner?: string; tags?: string[]; lineage?: string[]; importMeta?: { importId?: string; candidateId?: string; sourceKind?: string; sourcePath?: string; }; }): string; /** * Delete one exact Block Studio artifact and its generated semantic companion. * Path validation is intentionally strict so the UI cannot turn this endpoint * into a broad project-file deletion primitive. */ export declare function deleteBlockStudioArtifacts(projectRoot: string, relativePath: string): { path: string; companionPath: string | null; }; export declare function saveBlockStudioDraftArtifacts(projectRoot: string, options: { currentPath?: string; source: string; name: string; domain?: string; description?: string; owner?: string; tags?: string[]; lineage?: string[]; importMeta?: { importId?: string; candidateId?: string; sourceKind?: string; sourcePath?: string; }; stableSuffix?: string; }): string; /** * Extract the declared `invariants` from a block's DQL source using the core * parser (the same path that populates the manifest). Returns an empty array * when the source has no invariants or cannot be parsed — invariant evaluation * is best-effort and must never break a run. */ export declare function extractBlockInvariants(source: string): string[]; /** * Evaluate a block's declared invariants against a normalized query result. * Returns `null` when the block declares no invariants so callers can omit the * field entirely (blocks without invariants behave exactly as before). */ export declare function evaluateBlockInvariants(source: string, result: { columns: string[]; rows: Array>; }): { invariantResults: InvariantResult[]; invariantViolation: boolean; } | null; export declare function parseBlockSourceMetadata(source: string): { name: string; domain: string; description: string; owner: string; tags: string[]; status: string; blockType: string; llmContext: string; pattern: string; grain: string; entities: string[]; outputs: string[]; dimensions: string[]; allowedFilters: string[]; parameterPolicy: Array<{ name: string; policy: string; }>; filterBindings: Array<{ filter: string; binding: string; }>; sourceSystems: string[]; replacementFor: string[]; reviewCadence: string; metricRef: string; metricsRef: string[]; }; /** * Persist the explicit App-source reuse decision in canonical DQL. Generated * execution stays transient; only Block Studio save/promotion routes call this * helper. The marker is a normal parser-supported tag, so refresh/reindex can * make the participation decision without relying on filenames or prose. */ export declare function markBlockStudioSourceReusable(source: string): string; export declare function setBlockStudioStatus(projectRoot: string, blockPath: string, newStatus: string): void; export declare function buildConversationContextRecap(context: Record | undefined): string | undefined; /** * Explain the latest successful answer from its persisted artifact contract. * This is intentionally deterministic and never executes SQL: missing evidence * is reported as missing instead of being guessed or converted into a new metric. */ export declare function buildPriorAnswerExplanation(question: string, context: Record | undefined): string | undefined; export declare function extractBlockStudioSql(source: string): string | null; export interface BlockGitMetadata { commitSha: string; repo: string | null; branch: string | null; } export declare function readGitMetadata(projectRoot: string): BlockGitMetadata | null; export declare function createBlockArtifacts(projectRoot: string, options: { name: string; domain?: string; owner?: string; content?: string; description?: string; tags?: string[]; folderPath?: string; metricRefs?: string[]; template?: string; blockType?: 'custom' | 'semantic'; llmContext?: string; examples?: Array<{ question: string; sql?: string; }>; invariants?: string[]; gitMetadata?: BlockGitMetadata | null; }): { path: string; content: string; companionPath: string; }; export declare function createSemanticBuilderBlock(projectRoot: string, options: { name: string; domain?: string; description?: string; owner?: string; tags?: string[]; metrics: string[]; dimensions: string[]; timeDimension?: { name: string; granularity: string; }; chart?: string; blockType: 'semantic' | 'custom'; sql: string; tables: string[]; provider: string; }): { path: string; content: string; companionPath: string; }; export declare function discoverDbtProfileConnections(projectRoot: string, projectConfig: ProjectConfig, explicitPath?: string): DbtProfileConnectionCandidate[]; export declare function buildDbtParseArgs(dbtProjectDir: string, profilesDir?: string): string[]; /** CFG-003: use a complete default dbt target when no saved DQL connection exists. */ export declare function resolveDbtProfileRuntimeConnection(projectRoot: string, projectConfig: ProjectConfig): ConnectionConfig | null; export declare function buildDbtStatus(projectRoot: string, projectConfig: ProjectConfig, lastSyncTime: string | null): { configured: boolean; provider: "dql" | "snowflake" | "dbt" | "cubejs" | "lookml" | null; projectPath: string; projectName: any; artifacts: { manifest: { path: string; exists: boolean; count: number | undefined; generatedAt: string | null; }; catalog: { path: string; exists: boolean; count: number | undefined; generatedAt: string | null; }; semanticManifest: { path: string; exists: boolean; count: number | undefined; generatedAt: string | null; }; runResults: { path: string; exists: boolean; count: number | undefined; generatedAt: string | null; }; }; counts: { models: number; sources: number; metrics: number; semanticModels: number; savedQueries: number; }; lastSyncTime: string | null; setupHint: string; }; export type SemanticLayerDiagnosticsIssueSeverity = 'info' | 'warning' | 'error'; export interface SemanticLayerDiagnosticsIssue { severity: SemanticLayerDiagnosticsIssueSeverity; code: string; message: string; action?: string; path?: string; } export declare function buildSemanticLayerDiagnostics(projectRoot: string, projectConfig: ProjectConfig, options: { semanticLayer?: SemanticLayer; semanticErrors?: string[]; semanticConfig?: SemanticLayerProviderConfig; detectedProvider?: string; lastSyncTime: string | null; }): { available: boolean; provider: string | null; sourceOfTruth: string; errors: string[]; lastSyncTime: string | null; counts: { domains: number; metrics: number; measures: number; dimensions: number; timeDimensions: number; entities: number; hierarchies: number; semanticModels: number; savedQueries: number; }; dbt: { configured: boolean; provider: "dql" | "snowflake" | "dbt" | "cubejs" | "lookml" | null; projectPath: string; projectName: any; artifacts: { manifest: { path: string; exists: boolean; count: number | undefined; generatedAt: string | null; }; catalog: { path: string; exists: boolean; count: number | undefined; generatedAt: string | null; }; semanticManifest: { path: string; exists: boolean; count: number | undefined; generatedAt: string | null; }; runResults: { path: string; exists: boolean; count: number | undefined; generatedAt: string | null; }; }; counts: { models: number; sources: number; metrics: number; semanticModels: number; savedQueries: number; }; lastSyncTime: string | null; setupHint: string; }; issues: SemanticLayerDiagnosticsIssue[]; warnings: string[]; }; export interface GitStatusResult { inRepo: boolean; branch: string | null; ahead: number; behind: number; changes: Array<{ path: string; status: string; }>; error?: string; } export interface GitCommit { hash: string; author: string; date: string; subject: string; } export declare function ensureLocalRuntimeGitignore(projectRoot: string): void; /** * Ceiling on the one bounded meaning-resolution call. * * 10s assumes a hosted model. A local Ollama model needs ~7s for a ONE-WORD * reply, so a 600-token resolution over a dozen candidates never lands: it * aborts, the router falls back to its evidence-only decision, and * `mayAssumeInterpretation` goes false — which sends every ambiguous question to * the clarification gate (AGT-017). The effect is that a local model cannot * answer anything ambiguous, in a product whose whole positioning is local-first. * * Scaled by the same `DQL_AGENT_DEADLINE_SCALE` as the run budget, so one * setting moves the provider's whole time envelope together rather than leaving * an inner bound to silently cap an outer one. */ /** * Predict how long the next provider call will take, for admission control. * * With fewer than three samples the MAX is the only honest predictor: there is * no distribution yet, and admitting a call the deadline then kills wastes the * whole remaining budget. * * With a real sample, p75 rather than the max. One slow response — a cold model * load, a retried connection — otherwise poisons admission control for the rest * of the run: every later call is refused against a worst case that already * passed. A recorded run tripped RUN_DEADLINE_INSUFFICIENT 6.4s into a 45s * budget for exactly that reason. p75 still errs slow, so a genuinely slow * provider is still respected. */ export declare function predictDispatchMs(observed: readonly number[], assumedMs?: number): number; export declare function boundedAgentMeaningSignal(signal?: AbortSignal, timeoutMs?: number): AbortSignal; /** * Whether the project's stored live-schema snapshot is missing or older than the * freshness window (P6). Used to force a fresh information_schema scan even when the * question-shape heuristic wouldn't otherwise trigger one — so a warehouse schema * change between sessions isn't silently reasoned over. Best-effort: a catalog error * returns false so it never causes a rescan storm. */ export declare function runtimeSnapshotStale(projectRoot: string, maxAgeMs?: number): boolean; /** * Resolve only metadata captured for the connection executing this Ask run. * Returning an explicit empty snapshot is a correctness boundary: callers pass * it into the context builder so it cannot silently fall back to another * connection's project-global runtime FTS rows. */ export declare function runtimeSchemaSnapshotForAgentConnection(projectRoot: string, connectionId: string, expectedScopeFingerprint?: string): RuntimeSchemaSnapshot; export declare function buildAgentSchemaContext(question: string, rows: unknown[], options?: { includeUnscored?: boolean; limit?: number; }): AgentSchemaTable[]; /** * Physical catalog evidence fallback. This is deliberately question-scoped and * result-bounded: dbt/DQL metadata remains the planning authority, while * information_schema confirms discoverable relations/columns in the selected * execution target. Tokens are reduced to safe identifier characters before * interpolation, so user text cannot become SQL syntax. */ /** * Point-lookup of NAMED relations in `information_schema.columns`. * * This is deliberately not `buildRuntimeSchemaSearchSql`. That one matches * `LOWER(table_name) LIKE '%term%'` across every visible schema, which is the * unbounded rescan the "a lexical miss is not permission to rescan" policy * exists to prevent. This one asks about specific `(schema, table)` pairs the * model actually referenced, with equality predicates — a metadata point * lookup, not a scan. * * Returns null when nothing safe to ask about survives normalization. */ export declare function buildNamedRelationProbeSql(relations: string[], maxRelations?: number): string | null; /** * Conservative fallback for deciding whether an unparsed statement still * needs DQL's structural join validation. Join-free SELECT syntax may be * newer than the local parser and can safely continue to the read-only * warehouse boundary; an unparsed join must never bypass relationship proof. */ export declare function sqlMayContainJoin(sql: string): boolean; /** * Resolve a BARE internal graph identity (`source::orders` — a name with no * database or schema) against the warehouse. * * A qualified identity decodes mechanically, but a bare one carries no * location, so the old behaviour was to reject the cell and hand the user an * "Ask AI to fix" button. That was an LLM round trip for a lookup: the name is * unambiguous whenever exactly one relation in the connection answers to it. * Probe for it, and only refuse when the answer is genuinely zero or many — * at which point the refusal can say which it was. * * Runs ONLY after a decode has already failed, so the happy path pays nothing. */ export declare function resolveBareInternalRelationIds(sql: string, executor: QueryExecutor, connection: ConnectionConfig): Promise<{ sql: string; resolved: Array<{ from: string; to: string; }>; ambiguous: string[]; }>; export declare function buildRuntimeSchemaSearchSql(question: string): string; /** * Keep dbt descriptions and semantic meaning, but make a complete live point * lookup authoritative for executable column names on the selected connection. * No fuzzy aliases are invented: a stale `customer_name` declaration disappears * when the live relation exposes only `name`. */ export declare function reconcileAgentSchemaContextWithLive(catalog: AgentSchemaTable[], live: AgentSchemaTable[]): AgentSchemaTable[]; /** * Decide whether to run the (advisory, best-effort) runtime value-scan + schema * enrichment for a question. Generic multi-entity / detail intent — no hard-coded * project vocabulary — so it fires for ANY repo's join-shaped questions, not just * jaffle. Over-triggering only costs a few extra bounded probes (all in try/catch). */ export declare function shouldAugmentAgentRuntimeSchema(question: string, questionPlan?: { entities?: Array; metricTerms?: string[]; dimensionTerms?: string[]; }): boolean; export interface AgentRuntimeValueGroundingPolicy { mode: 'disabled' | 'safe_automatic'; searchSafeColumns: ReadonlySet; } /** * Resolve the project-admin boundary for live value lookup. An absent/malformed * policy is deliberately disabled; a broad table or wildcard cannot make an * unknown column search-safe. */ export declare function resolveAgentRuntimeValueGrounding(config: ProjectConfig): AgentRuntimeValueGroundingPolicy; export declare function isAgentValueProbeColumn(column: AgentSchemaTable['columns'][number]): boolean; export declare function buildAgentValueProbeSql(table: AgentSchemaTable, column: string, searchTerms: string[], connection: ConnectionConfig): string; export declare function extractAgentValueSearchTerms(question: string): string[]; export {}; //# sourceMappingURL=local-runtime.d.ts.map