/** * HTTP handlers for `/api/apps`, `/api/apps/:id`, `/api/apps/:id/dashboards/:did`, * `/api/persona`. Designed to be invoked from `local-runtime.ts`'s request * dispatcher — returns `true` if the request was handled, `false` otherwise. */ import type { IncomingMessage, ServerResponse } from 'node:http'; import type { AgentAnswer, AppBuilderPlannerCompletion, NarrateInput, NarrateResult, ResearchPlan as AppResearchPlan } from '@duckcodeailabs/dql-agent'; import { type AppDocument, type DashboardDisplayMetadata, type DashboardDocument, type DashboardGridItem, type AppBuildDraft, type AppBuildDraftOperation } from '@duckcodeailabs/dql-core'; import { LocalAppStorage, type LocalAppInvestigation, type LocalAppInvestigationIntent, type LocalAppInvestigationReportSection } from '@duckcodeailabs/dql-project'; import type { LocalOperation, LocalOperationProgress } from './local-operation-coordinator.js'; interface Ctx { req: IncomingMessage; res: ServerResponse; url: URL; path: string; projectRoot: string; executeSql?: (sql: string) => Promise; generateInvestigationSql?: (input: AppInvestigationGenerationRequest) => Promise; runNotebook?: (appId: string, notebookPath: string) => Promise; /** Grounded ReAct research planner (P4) — supplied by the runtime, which holds the * metric/block catalog. Lets the App ask lane decide + offer real follow-ups. */ planResearch?: (input: { question: string; isFollowUp?: boolean; }) => Promise; /** Governed answer generator — supplied by the runtime. Lets the two-phase app * build fill coverage gaps with bounded, review-required generated SQL. */ generateGovernedAnswer?: (question: string, context?: AppContextEnvelopeV1) => Promise; /** App-specific planning call. The runtime supplies the same configured * provider adapter as Ask, but App Builder owns the structured state machine. */ planAppBuild?: AppBuilderPlannerCompletion; /** Story narrator — supplied by the runtime (LLM-backed with a deterministic * fallback). Commit uses it to write the app's narrated story sections. */ narrate?: (input: NarrateInput) => Promise; verifyDashboardRun?: (input: { runId: string; appId: string; dashboardId: string; tileIds: string[]; }) => { ok: true; snapshotId: string; resultFingerprint: string; } | { ok: false; error: string; }; verifyAppBuildPreview?: (input: { runId: string; draftId: string; dashboardId: string; tileIds: string[]; }) => { ok: true; snapshotId: string; resultFingerprint: string; } | { ok: false; error: string; }; queueOperation?: (input: { type: string; scope: string; resourceRevision?: string; message?: string; cancellable?: boolean; }, task: (signal: AbortSignal, report: (progress: Partial) => void) => Promise) => LocalOperation; scheduleProjectRefresh?: (reason: string) => void; } /** Runtime hooks the two-phase app build can use during propose (all optional — * without them, gaps stay listed as uncovered research questions). */ export interface AppBuildHooks { generateGovernedAnswer?: (question: string, context?: AppContextEnvelopeV1) => Promise; planAppBuild?: AppBuilderPlannerCompletion; } /** * One bounded repair trace shared by App proposal and dashboard execution. * Repaired SQL never inherits semantic certification: the trace is evidence that * the result is useful for review, not permission to publish it as governed. */ export interface AppExecutionRepairTrace { version: 1; status: 'repaired' | 'failed'; source: 'query_generator' | 'semantic_query' | 'draft_analysis' | 'certified_block' | 'review_block'; mode?: 'deterministic' | 'ai'; attemptedAt: string; originalFailure: string; originalSqlFingerprint?: string; repairedSqlFingerprint?: string; approvalEligible: false; message: string; } export type AppBuildGeneratedAnswer = AgentAnswer & { appBuilderRepair?: AppExecutionRepairTrace; }; export interface AppContextEnvelopeV1 { version: 1; app: { id: string; name: string; domain: string; audience?: string; lifecycle: string; visibility: string; publicationIntent: 'personal' | 'shared_project'; businessOutcome?: string; caveats: string[]; }; dashboards: Array<{ id: string; title: string; filters: DashboardDocument['filters']; story?: DashboardDocument['story']; tiles: Array>; }>; /** * The app's own review-required draft analyses (`apps//drafts/*.dql`). * * These never enter `manifest.blocks` — the manifest's block scan set is * blocks/domains/dashboards/workbooks — so they have no KG node and no * catalog row, and retrieval could not see them at any tier. They are carried * on this envelope instead of being indexed globally on purpose: they are * AI-generated exploratory SQL scoped to one app, and indexing them * project-wide would leak one app's drafts into every other app's context and * into every Ask question. */ drafts: Array<{ name: string; path: string; status: string; description?: string; question?: string; sql?: string; }>; focus?: { dashboardId?: string; tileId?: string; blockId?: string; }; } /** Runtime hooks for the commit step (all optional — commit falls back to the * deterministic narrator so story sections exist even offline). */ export interface AppBuildCommitHooks { narrate?: (input: NarrateInput) => Promise; } export declare function handleAppsApi(ctx: Ctx): Promise; type AppListEntry = { id: string; name: string; fingerprint: string; filePath: string; domain: string; ownerDomain?: string; usesDomains: string[]; purpose?: string; requiredExports: string[]; classification?: string; subdomain?: string; groups: string[]; description?: string; audience?: string; lifecycle: NonNullable; certification: 'certified' | 'uncertified'; status?: 'ready' | 'empty' | 'review'; storage: 'shared' | 'mine' | 'template'; visibility: NonNullable; publicationIntent: NonNullable; owners: string[]; tags: string[]; members: number; roles: number; policies: number; schedules: number; dashboards: Array<{ id: string; title: string; }>; notebooks: Array<{ path: string; title?: string; role: 'source' | 'analysis' | 'supporting'; visibility: NonNullable; }>; drafts: Array<{ path: string; name: string; reviewStatus?: string; }>; aiPins: number; investigations: number; homepage?: AppDocument['homepage']; }; declare function collectAppsList(projectRoot: string): AppListEntry[]; interface AppRecommendationRequest { domain?: string; tags?: string[]; purpose?: string; audience?: string; certifiedOnly?: boolean; } interface VisualizationRecommendationRequest { blockRef?: string; resultSchema?: unknown; rowSample?: Array>; rows?: Array>; appAudience?: string; audience?: string; prompt?: string; filters?: unknown; allowedVisualizations?: string[]; component?: DashboardDisplayMetadata['component']; defaultVisualization?: string; } interface AppCreateRequest { name?: string; domain?: string; ownerDomain?: string; usesDomains?: string[]; requiredExports?: string[]; classification?: string; dashboardTitle?: string; subdomain?: string; groups?: string[]; purpose?: string; audience?: string; visibility?: 'shared' | 'private' | 'template'; lifecycle?: AppDocument['lifecycle']; tags?: string[]; owners?: string[]; selectedBlockIds?: string[]; } interface AppGenerateRequest { /** Client-generated durable identity used to resume a background proposal. */ sessionId?: string; prompt?: string; domain?: string; owner?: string; force?: boolean; selectedBlockIds?: string[]; plannerMode?: 'deterministic' | 'ai_assisted'; audience?: string; notebookPath?: string; existingAppId?: string; mode?: 'personal' | 'stakeholder'; /** Explicit opt-in to analyze uncovered questions as app-scoped review drafts. */ exploreGaps?: boolean; /** Gap-fill budget: how many uncovered questions may get generated SQL (default 3, hard cap 5). */ maxGeneratedTiles?: number; } interface AppAiBuildSession { id: string; /** 'proposed' = plan + proposal saved, NO app files written yet (awaiting confirm). */ status: 'building' | 'proposed' | 'ready' | 'error'; createdAt: string; updatedAt: string; prompt: string; appId?: string; dashboardId?: string | null; generatedPaths: string[]; plan?: unknown; validation?: unknown; /** The reviewable content list shown to the user before commit. */ proposal?: AppBuildProposal; snapshotId?: string; /** Existing-App builds also bind to the exact App package reviewed at propose time. */ existingAppFingerprint?: string; proposalHash?: string; /** Tile ids the user confirmed at commit time (audit trail). */ committedTileIds?: string[]; committedBrief?: { appName?: string; pageTitle?: string; audience: string; filterIds: string[]; tileOverrides: Record; }; warnings: string[]; reviewTasks: string[]; inputs: { domain?: string; owner?: string; audience?: string; notebookPath?: string; existingAppId?: string; mode?: 'personal' | 'stakeholder'; exploreGaps?: boolean; selectedBlockIds: string[]; }; error?: string; } /** One confirmable entry in the pre-create proposal list. */ export interface AppBuildProposalTile { id: string; source: 'certified_block' | 'semantic_query' | 'ai_generated'; title: string; description?: string; /** Certified tiles reference an existing block. */ blockId?: string; /** Governed semantic candidates carry canonical semantic intent, never raw SQL. */ question?: string; semantic?: DashboardGridItem['semantic']; /** Legacy proposal fields remain readable; v2 planning never creates them. */ sql?: string; answer?: string; viz: string; allowedVisualizations?: string[]; certification: 'certified' | 'reviewed_semantic' | 'ai_generated'; sourceClass: 'certified_block' | 'governed_semantic' | 'exploratory_analysis'; reviewStatus: 'not_required' | 'required' | 'approved'; requirementIds?: string[]; sourceEvidence?: DashboardGridItem['sourceEvidence']; preflight: { status: 'passed' | 'review_required' | 'blocked'; snapshotId?: string; sourceFingerprint?: string; receiptId?: string; message: string; }; /** Bounded preview rows from executing the generated SQL (Phase 2). */ preview?: { columns: string[]; rows: Array>; rowCount?: number; }; /** Immutable evidence from the shared one-attempt Ask/Notebook repair boundary. */ repair?: AppExecutionRepairTrace; /** Generation failed — listed for transparency, not selectable. */ error?: string; selectedByDefault: boolean; followUps?: string[]; } /** Questions the plan could not cover; Phase 2 upgrades these into generated tiles. */ export interface AppBuildProposalGap { id: string; question: string; reason: string; } export interface AppBuildFrame { goal: string; audience: string; metrics: string[]; dimensions: string[]; grain?: string; timeRange?: string; comparison?: string; filters: string[]; desiredOutput: string; } export interface AppBuildClarification { id: string; question: string; choices: string[]; required: boolean; } export interface AppBuildProposal { intent: { target: 'personal' | 'shared_project'; initialVisibility: 'private'; }; buildFrame: AppBuildFrame; tiles: AppBuildProposalTile[]; gaps: AppBuildProposalGap[]; clarifications: AppBuildClarification[]; followUps: string[]; coverage: { certifiedTiles: number; semanticTiles: number; generatedTiles: number; gaps: number; }; } interface AppAskRequest { question?: string; dashboardId?: string; tileId?: string; blockId?: string; variables?: Record; context?: unknown; runInvestigation?: boolean; } interface AppAskDecision { mode: 'answer' | 'analysis' | 'app_change' | 'metadata'; reason: string; nextAction: string; requiresContext: boolean; usesCertifiedResult: boolean; confidence: number; } interface AppPromoteRequest { lifecycle?: AppDocument['lifecycle']; } interface DashboardTileRecommendationRequest extends VisualizationRecommendationRequest { tileId?: string; } interface AiPinCreateRequest { dashboardId?: string; tileId?: string; title?: string; answer?: string; question?: string; sql?: string; sourceTier?: string; certification?: 'certified' | 'ai_generated'; reviewStatus?: 'needs_review' | 'draft_created' | 'certified' | 'rejected'; refreshCadence?: 'none' | 'daily'; chartConfig?: Record; result?: unknown; citations?: unknown[]; analysisPlan?: unknown; evidence?: unknown; followUps?: string[]; } interface AppInvestigationCreateRequest { dashboardId?: string; sourceTileId?: string; sourceBlockId?: string; title?: string; question?: string; intent?: LocalAppInvestigationIntent; context?: unknown; generatedSql?: string; run?: boolean; } interface AppInvestigationRunRequest { question?: string; intent?: LocalAppInvestigationIntent; context?: unknown; generatedSql?: string; repairMode?: 'rebuild_from_certified'; } interface AppInvestigationGenerationRequest { appId: string; dashboardId?: string; sourceTileId?: string; sourceBlockId?: string; title?: string; question: string; intent: LocalAppInvestigationIntent; context?: unknown; mode?: 'sql_and_memo' | 'memo_only'; generatedSql?: string; metrics?: Record; drivers?: Array>; resultPreviews?: unknown[]; summaryHint?: string; recommendationHint?: string; sqlError?: string; sqlErrorKind?: SqlPreviewErrorKind; hasReportEvidence?: boolean; } interface AppInvestigationGenerationResult { sql?: string; answer?: string; result?: unknown; analysisPlan?: unknown; evidence?: unknown; citations?: unknown[]; suggestedViz?: string; executionError?: string; providerUsed?: string; } interface BlockCandidate { id: string; name: string; domain: string; status: string; owner: string | null; tags: string[]; path: string; fingerprint: string; lastModified: string; description: string; llmContext: string | null; chartType?: string; /** Stable App-filter ids declared by block filter/parameter bindings. */ filterIds: string[]; /** Declared governed result dimensions that may be linked as App filters. */ dimensionIds: string[]; score: number; reasons: string[]; } export declare function recommendBlocks(projectRoot: string, input: AppRecommendationRequest): BlockCandidate[]; export declare function recommendVisualization(projectRoot: string, input: VisualizationRecommendationRequest): { ok: true; display: DashboardDisplayMetadata; evidence: Array<{ source: string; reason: string; }>; warnings: string[]; } | { ok: false; error: string; }; export declare function generateAppPackage(projectRoot: string, input: AppGenerateRequest): Promise<{ ok: true; plan: unknown; validation: unknown; generated: { paths: string[]; }; app: ReturnType[number] | null; dashboardId: string | null; } | { ok: false; error: string; }>; export declare function createAppAiBuildSession(projectRoot: string, input: AppGenerateRequest): Promise; export declare function getAppAiBuildSession(projectRoot: string, id: string): AppAiBuildSession | null; /** * Phase 1 of the two-phase app build: plan + validate + build the confirmable * proposal, persist the session with status 'proposed'. Writes NO app files — * `commitAppAiBuild` does that after the user confirms the selection. */ export declare function proposeAppAiBuild(projectRoot: string, input: AppGenerateRequest, hooks?: AppBuildHooks): Promise; export interface AppBuildDraftAiProposal { id: string; draftId: string; baseRevision: number; baseProposalHash: string; operations: AppBuildDraftOperation[]; /** Governed sources start selected; generated review DQL is always opt-in. */ defaultSelectedSourceIds?: string[]; /** Complete bounded candidate set used by the App-specific planning call. */ candidateSourceIds?: string[]; warnings?: string[]; clarifications: Array<{ id: string; question: string; choices: Array<{ id: string; label: string; }>; required: boolean; }>; summary: { requirements: number; covered: number; gaps: number; certifiedSources: number; semanticSources: number; }; } export type AppBuildComposeRequest = { expectedRevision: number; expectedProposalHash: string; } & ({ mode: 'ai'; proposalId: string; selectedSourceIds: string[]; } | { mode: 'manual'; enableReviewRequired?: boolean; selections: Array<{ sourceId: string; pageId?: string; view: 'kpi' | 'chart' | 'table'; }>; }); /** * AI is a coauthor of the same local AppBuildDraft used by manual authoring. * This adapter reuses the governed planner, compiles its result outside the * project, and returns typed operations. It does not mutate the draft or write * any canonical App source. */ export declare function proposeAppBuildDraftOperations(projectRoot: string, draft: AppBuildDraft, input: { prompt: string; selectedBlockIds?: string[]; }, hooks?: AppBuildHooks): Promise; export interface CommitAppAiBuildInput { /** Proposal tile ids to include; omitted = everything selectable. */ selectedTileIds?: string[]; force?: boolean; expectedProposalHash?: string; /** User-approved Build Brief edits that do not alter governed source identity. */ appName?: string; /** Existing-App page builds may rename only the proposed page, never the App. */ pageTitle?: string; audience?: string; /** User-approved page filter ids; omitted keeps every planned filter. */ filterIds?: string[]; tileOverrides?: Record; } /** * Phase 2 of the two-phase app build: the user confirmed the proposal — filter the * plan to the selected tiles, re-validate (fail closed on drift), write the app * files, and mark the session ready. */ export declare function commitAppAiBuild(projectRoot: string, sessionId: string, input?: CommitAppAiBuildInput, hooks?: AppBuildCommitHooks): Promise<{ ok: true; session: AppAiBuildSession; app: ReturnType[number] | null; dashboardId: string | null; } | { ok: false; error: string; status?: number; }>; interface AppAskSuccess { ok: true; route: 'certified_answer' | 'generated_answer' | 'investigation' | 'app_change_proposal' | 'metadata_answer'; answer: string; trustState: 'certified' | 'review_required' | 'draft_ready'; reviewStatus: 'certified' | 'review_required' | 'draft_ready'; citations: Array<{ kind: string; name: string; path?: string; }>; followUps: string[]; decision: AppAskDecision; investigation?: LocalAppInvestigation; proposal?: unknown; /** Grounded ReAct research plan (P4): the decision, steps, and follow-up options. */ researchPlan?: AppResearchPlan; } type AppAskResult = AppAskSuccess | { ok: false; error: string; }; /** * Ask the App a question. Routes to the right lane (certified answer / investigation / * app change / metadata) and — when the runtime supplies a research planner — enriches * the result with a grounded ReAct plan + smart follow-up options (the P4 loop), so the * panel can DECIDE and offer real next steps instead of generic strings. */ declare function askAppQuestion(ctx: Ctx, appId: string, input: AppAskRequest): Promise; /** * The app's own draft that best covers a question the governed loop could not answer. * * Drafts are review-required exploratory analyses saved in the app. They are not * indexed into the manifest, so retrieval cannot find them — but when the loop * has just refused for want of a certified path, the app frequently already * contains a draft that does exactly the thing. Offering it is strictly better * than explaining the gap and stopping, and it never launders trust: the answer * stays review-required and says the source is an unreviewed draft. */ export declare function matchAppDraftForQuestion(drafts: AppContextEnvelopeV1['drafts'], question: string): AppContextEnvelopeV1['drafts'][number] | undefined; export declare function recommendDashboardTile(projectRoot: string, appId: string, dashboardId: string, input: DashboardTileRecommendationRequest): { ok: true; display: DashboardDisplayMetadata; filterBindings: DashboardGridItem['filterBindings']; parameterBindings: DashboardGridItem['parameterBindings']; sourceEvidence: DashboardGridItem['sourceEvidence']; trustState: NonNullable; reviewStatus: NonNullable; evidence: Array<{ source: string; reason: string; }>; warnings: string[]; } | { ok: false; error: string; }; /** * Rename an App, and optionally a page inside it. * * `dql.app.json` previously had exactly two writers — create and publish — so * an App could never be renamed after it was built. Only the display `name` * changes here: the App **id** is also its folder name and is referenced by * notebooks, product context, and deep links, so renaming must not move it. * * The write is staged and swapped like the publish path, and guarded by the * caller-supplied fingerprint so a concurrent edit cannot be clobbered. */ export declare function renameApp(projectRoot: string, appId: string, input: { name?: string; pageTitle?: string; dashboardId?: string; expectedFingerprint?: string; }): { ok: true; app: AppDocument; paths: string[]; } | { ok: false; error: string; code?: string; }; export declare function promoteAppForStakeholders(projectRoot: string, appId: string, input?: AppPromoteRequest): { ok: true; app: AppDocument; paths: string[]; removedLocalTiles: number; readiness: AppPublicationReadiness; } | { ok: false; error: string; readiness?: AppPublicationReadiness; }; export interface AppPublicationReadiness { ready: boolean; governedTiles: number; blockers: Array<{ dashboardId: string; tileId: string; code: 'exploratory_source' | 'semantic_review' | 'semantic_preflight' | 'block_not_certified' | 'block_source_unversioned' | 'block_source_changed' | 'filter_not_preflighted'; message: string; }>; } export declare function approveAppSemanticTiles(projectRoot: string, appId: string, dashboardId: string, input: { tileIds: string[]; runId: string; snapshotId: string; expectedDashboardFingerprint: string; reviewer: string; }): { ok: true; dashboard: DashboardDocument; path: string; } | { ok: false; error: string; }; export declare function createStoredAppBuildDraft(projectRoot: string, input: { appId?: string; baseAppId?: string; name?: string; goal?: string; audience?: string; domain?: string; authoringMode?: 'ai' | 'manual'; sourcePolicy?: 'governed_only' | 'include_review_required'; template?: 'executive_brief' | 'operational_dashboard' | 'investigation' | 'blank'; entrypoint?: 'studio' | 'ask'; }): AppBuildDraft; export interface AppBuildAskResultInput { expectedRevision: number; expectedProposalHash: string; pageId?: string; title?: string; question?: string; answer?: string; certifiedBlockId?: string; dqlSource?: string; sql?: string; visualization?: string; } /** * Adds an Ask result to the same local AppBuildDraft used by manual and AI * authoring. Certified answers retain their block identity. Exploratory DQL is * materialized as an app-scoped local artifact and remains review-required; * SQL is never embedded in the dashboard JSON or written to Project source. */ export declare function addAskResultToAppBuildDraft(projectRoot: string, draftId: string, input: AppBuildAskResultInput): { ok: true; draft: AppBuildDraft; pageId: string; tileId: string; deduped: boolean; }; export declare function writeStoredAppBuildDraft(projectRoot: string, draft: AppBuildDraft, input?: { expectedRevision?: number; operations?: AppBuildDraftOperation[]; }): void; export declare function deleteStoredAppBuildDraft(projectRoot: string, draft: AppBuildDraft): { ok: true; id: string; recoveryId: string; recoverableUntilRestart: false; }; declare function restoreStoredAppBuildDraft(projectRoot: string, recoveryId: string): { ok: true; id: string; restoredPath: string; draft: AppBuildDraft; } | { ok: false; status: 400 | 404 | 409; error: string; }; export declare function preflightStoredAppBuildDraft(projectRoot: string, draft: AppBuildDraft): string[]; export declare function markStoredAppBuildDraftPreflighted(projectRoot: string, draft: AppBuildDraft): AppBuildDraft; export declare function publishStoredAppBuildDraft(projectRoot: string, draft: AppBuildDraft): { ok: true; app: ReturnType[number]; draft: AppBuildDraft; paths: string[]; }; /** @deprecated Compatibility adapter for callers that still use the old name. */ export declare function commitStoredAppBuildDraft(projectRoot: string, draft: AppBuildDraft): ReturnType; declare function renderSqlTemplateParams(sql: string, values: Record): { sql: string; unresolved: string[]; }; declare function unresolvedSqlTemplateParams(sql: string): string[]; export declare function deleteAppPackage(projectRoot: string, id: string, input?: { expectedFingerprint?: string; }): { ok: true; id: string; deletedPath: string; trashPath: string; recoveryId: string; localRowsArchived: number; sourceControlImpact: 'delete'; } | { ok: false; status: 400 | 404 | 409; error: string; }; export declare function restoreAppPackage(projectRoot: string, recoveryId: string): { ok: true; id: string; restoredPath: string; } | { ok: false; status: 400 | 404 | 409; error: string; }; export declare function restoreAppRecovery(projectRoot: string, recoveryId: string): ReturnType | ReturnType; export declare function createAppPackage(projectRoot: string, input: AppCreateRequest): { ok: true; app: ReturnType[number]; paths: string[]; dashboardId: string; } | { ok: false; error: string; }; declare function runAppInvestigation(ctx: Ctx, storage: LocalAppStorage, investigation: LocalAppInvestigation, input?: AppInvestigationRunRequest | AppInvestigationCreateRequest): Promise; declare function selectedBlockContext(context: unknown): Record | null; declare function buildMetricSnapshot(selected: Record | null): Record; declare function buildPreviewMetricSnapshot(preview: Record, fallbackTitle?: string): Record; declare function buildPreviewDriverCards(preview: Record, intent: LocalAppInvestigationIntent): Array>; declare function buildDeterministicInvestigationSql(projectRoot: string, input: { question: string; intent: LocalAppInvestigationIntent; selected: Record | null; sourceBlockId?: string; context?: unknown; }): { sql: string; sourceBlockPath: string; sourceBlockName: string; } | undefined; declare function runGeneratedSqlPreview(ctx: Ctx, generatedSql?: string): Promise<{ preview?: Record; error?: string; fatal?: boolean; }>; type SqlPreviewErrorKind = 'runtime_unavailable' | 'ai_generation_timeout' | 'timeout' | 'sql_repair' | 'safety' | 'unknown'; declare function classifySqlPreviewError(error?: string): SqlPreviewErrorKind | undefined; declare function buildInvestigationSummary(intent: LocalAppInvestigationIntent, question: string, selected: Record | null, metrics: Record, drivers: Array>): string; declare function buildInvestigationReportSections(input: { intent: LocalAppInvestigationIntent; question: string; context: unknown; selected: Record | null; metrics: Record; drivers: Array>; summary: string; recommendation: string; agentAnswer?: string; hasReportEvidence?: boolean; sqlError?: string; sqlErrorKind?: SqlPreviewErrorKind; baselineGap?: boolean; }): LocalAppInvestigationReportSection[]; declare function investigationNarrativeAnswer(investigation: LocalAppInvestigation): string; export declare function createDashboardForApp(projectRoot: string, appId: string, input: { id?: string; title?: string; description?: string; }): { ok: true; dashboard: DashboardDocument; path: string; } | { ok: false; error: string; }; declare function createAiPinTile(projectRoot: string, appId: string, input: AiPinCreateRequest): { ok: true; pin: unknown; dashboard?: DashboardDocument; tile?: DashboardGridItem; deduped?: boolean; } | { ok: false; error: string; }; declare function loadAppById(projectRoot: string, id: string): { app: AppDocument; appDir: string; appPath: string; dashboards: Array<{ id: string; title: string; description?: string; itemCount: number; }>; notebooks: AppListEntry['notebooks']; drafts: AppListEntry['drafts']; aiPins: unknown[]; investigations: unknown[]; } | null; export declare function listNotebookCandidates(projectRoot: string, app: AppDocument, appDir: string): Array<{ path: string; title: string; attached: boolean; role?: 'source' | 'analysis' | 'supporting'; visibility?: NonNullable; lastModified?: string; }>; export declare function createNotebookForApp(projectRoot: string, appId: string, input: { name?: string; title?: string; role?: string; visibility?: string; template?: string; }): { ok: true; path: string; app: ReturnType; preview: unknown; } | { ok: false; error: string; }; export declare function previewNotebookForApp(projectRoot: string, appId: string, notebookPath: string): { ok: true; preview: unknown; } | { ok: false; status: number; error: string; }; export { writeDashboard as writeDashboardForTest }; declare function writeDashboard(projectRoot: string, appId: string, dashboardId: string, payload: unknown): { ok: true; path: string; } | { ok: false; error: string; }; export declare const __test__: { buildMetricSnapshot: typeof buildMetricSnapshot; buildPreviewDriverCards: typeof buildPreviewDriverCards; buildPreviewMetricSnapshot: typeof buildPreviewMetricSnapshot; buildDeterministicInvestigationSql: typeof buildDeterministicInvestigationSql; buildInvestigationSummary: typeof buildInvestigationSummary; buildInvestigationReportSections: typeof buildInvestigationReportSections; classifySqlPreviewError: typeof classifySqlPreviewError; investigationNarrativeAnswer: typeof investigationNarrativeAnswer; createAiPinTile: typeof createAiPinTile; runGeneratedSqlPreview: typeof runGeneratedSqlPreview; renderSqlTemplateParams: typeof renderSqlTemplateParams; unresolvedSqlTemplateParams: typeof unresolvedSqlTemplateParams; selectedBlockContext: typeof selectedBlockContext; runAppInvestigation: typeof runAppInvestigation; askAppQuestion: typeof askAppQuestion; collectAppsList: typeof collectAppsList; loadAppById: typeof loadAppById; }; //# sourceMappingURL=apps-api.d.ts.map