/** * `dql agent` — block-first answer loop on the command line. * * dql agent ask "what was revenue last week?" * [--provider claude|openai|gemini|ollama] * [--user alice@acme.com] (filters Skills + records feedback as this user) * [--domain growth] [--purpose growth_attribution] * [--format json] (emits structured JSON instead of prose) * [--thread ] (continue a persisted conversation thread: the * question runs through the runtime's agent-run * engine, which injects prior turns and records * this one server-side) * * dql agent threads * Lists persisted conversation threads (id, updated, title) from the runtime. * * dql agent reindex [path] * Rebuilds .dql/cache/agent-kg.sqlite and metadata.sqlite from the * project's manifest + Skills folder. Equivalent to `dql app reindex`. * * dql agent feedback --block --question "..." * Records feedback into the KG. Used by clients without MCP access. */ import { type RuntimeDrivenRun } from './agent-eval-runtime.js'; import { answer, type AgentAnswer, type AgentFollowUpContext, type AnalysisDepth, type NarrationIntegrityReceiptV1, type ReasoningEffort } from '@duckcodeailabs/dql-agent'; import type { CLIFlags } from '../args.js'; declare function cliReasoningEffort(flags: CLIFlags): ReasoningEffort | undefined; declare function cliAnalysisDepth(flags: CLIFlags): AnalysisDepth | undefined; export declare function runAgent(sub: string | null, rest: string[], flags: CLIFlags): Promise; interface AgentEvalCase { name?: string; question: string; domain?: string; followUp?: AgentFollowUpContext; selectedContext?: unknown; expected?: { sourceTier?: 'certified_artifact' | 'business_context' | 'semantic_layer' | 'dbt_manifest' | 'no_answer'; certification?: 'certified' | 'ai_generated' | 'analyst_review_required'; kind?: 'certified' | 'uncertified' | 'no_answer'; sqlContains?: string | string[]; sqlNotContains?: string | string[]; citationKind?: string; noHallucinatedColumns?: string[]; route?: 'certified' | 'generated_sql' | 'research' | 'clarify' | 'blocked'; intent?: string; reviewStatus?: 'none' | 'draft_ready' | 'analyst_review_required' | 'certified'; missingContextKind?: string; /** Persisted router terminal outcome required for runtime-driven gap cases. */ terminalOutcomeKind?: 'modeling_gap' | 'policy_blocked'; allowedRelationsOnly?: boolean; allowedColumnsOnly?: boolean; draftSaved?: boolean; minToolCalls?: number; rows?: unknown[]; /** * Is this question answerable at all? * * When true, ANY refusal (`no_answer` / `clarify`) is a FALSE REFUSAL — the * single number that makes "Ask AI refuses too much" measurable instead of * anecdotal. When false, the case belongs to the genuine-refusal class and a * refusal is the correct outcome; answering it would be a hallucination. * * Omitted, it is inferred from the other expectations, so existing case files * contribute to the metric without being rewritten. */ answerable?: boolean; }; } interface AgentEvalResult { name: string; passed: boolean; failures: string[]; durationMs: number; /** * True when verified narration failed its fact check and the deterministic * record was shown instead. A silent rise here is exactly the truncation * defect that shipped unnoticed, so it is measured. */ narrationFallback?: boolean; /** * Was verified-fact narration ATTEMPTED at all? Undefined on cases that never * reach the narrator (refusals, conversational replies). Without this the * grounded-narration denominator counted every case, diluting real failures * with runs that were never at risk — a metric that masks the defect it was * added to catch. */ narrationAttempted?: boolean; executionMs?: number; executionMatched?: boolean; kind: AgentAnswer['kind']; route?: string; intent?: string; reviewStatus?: string; /** Undefined when the runtime did not persist a retrieval count. */ contextObjects?: number; followUp: boolean; draftSaved: boolean; expected?: AgentEvalCase['expected']; validationCode?: string; trace: AgentEvalTraceStage[]; toolCalls: number; judgeScore?: number; judgePass?: boolean; /** * Selectable options offered with a clarification. A clarification that offers * real choices is answerable in one more turn; one that offers none is the * dead end this suite exists to catch. */ clarificationOptionCount?: number; /** True when the run replied conversationally instead of asserting data. */ conversational?: boolean; /** True when that conversational reply actually carried content. */ conversationalAnswer?: boolean; /** True when the meaning resolver ran (a provider was reachable). */ meaningResolved?: boolean; } type AgentEvalTraceStageName = 'context' | 'rewrite' | 'lane' | 'tools' | 'answer' | 'validation' | 'execution' | 'draft' | 'scoring'; interface AgentEvalTraceStage { stage: AgentEvalTraceStageName; status: 'passed' | 'failed' | 'not_run' | 'info'; message: string; payload?: unknown; } declare function evaluateCase(testCase: AgentEvalCase, result: Awaited>, runtime?: RuntimeDrivenRun): { failures: string[]; validationCode?: string; executionMatched?: boolean; }; /** * Is the case answerable? Explicit `expected.answerable` wins; otherwise infer * from the expectations already present, so the metric covers legacy case files. * A case with no expectations at all is excluded — it asserts nothing, so it can * neither prove nor disprove a false refusal. */ export declare function evalCaseIsAnswerable(expected: AgentEvalCase['expected']): boolean | undefined; /** * Did the run leave the user with NO way forward? * * Deliberately narrower than "did not answer". A clarification that offers * selectable options is answerable on the next turn — worth minimising, tracked * separately as `clarification_rate`, but not the defect. A clarification with * ZERO options is a true dead end: the reported production loop was exactly * this, and a free-text reply to it reproduced the same question forever. */ export declare function evalResultRefused(result: Pick): boolean; /** Did the run ask an answerable clarification rather than answering outright? */ export declare function evalResultClarified(result: Pick): boolean; /** * Translate only the durable narration receipt into evaluation fields. * * Reader prose, row count, and result shape are deliberately absent: a skipped * narration can have rows, and a deterministic fallback can use any wording. */ export declare function narrationOutcomeForEval(receipt: NarrationIntegrityReceiptV1 | undefined): Pick; declare function computeEvalMetrics(results: AgentEvalResult[]): { certified_hit_rate: number | null; judge_mean_score: number | null; judge_pass_rate: number | null; generated_followup_pass_rate: number | null; safe_refusal_rate: number | null; execution_match_rate: number | null; tool_requirement_pass_rate: number | null; wrong_certified_count: number; outside_context_rejection_count: number; /** * THE headline number: how often an answerable question was refused. * Bounds every other quality metric — a run that refuses cannot be wrong, * so a falling false-refusal rate must be read together with * `execution_match_rate` to be sure refusals were replaced by CORRECT answers. */ false_refusal_rate: number | null; false_refusal_count: number; answerable_case_count: number; /** * Answerable cases that asked an option-bearing clarification instead of * answering. Not a defect, but a direct cost in turns — read it next to * false_refusal_rate so a fall in refusals is not just a rise in questions. */ clarification_rate: number | null; /** * Cases where semantic judgment ran. Without a provider `mayAssumeInterpretation` * is false (AGT-017), so every ambiguous question clarifies by design and * `clarification_rate` says nothing about product quality. */ meaning_resolved_rate: number | null; /** * Latency, which the acceptance matrix asked for and nothing measured. A * quality gain paid for entirely in wall clock is not a gain: the plan's * two-tier target is certified/semantic under 5s while research takes * minutes, and only a per-class p95 can tell those apart from a regression. */ latency_p50_ms: number | null; latency_p95_ms: number | null; latency_p95_answerable_ms: number | null; /** * How often verified narration survived. When the drafted narration fails * its fact check the reader gets the deterministic record under a * disclaimer — correct, but visibly worse. A silent fall here is exactly * the truncation defect that shipped unnoticed, so it is measured. */ grounded_narration_rate: number | null; grounded_narration_attempted: number; /** * The guard on the above: cases that must NOT produce a data answer. * Scored on "did not answer" rather than "dead-ended", because declining via * a clarification is still declining — what would be wrong is asserting * something about data the project does not have. */ refusal_recall: number | null; refusal_required_case_count: number; draft_saved_count: number; tool_observed_case_count: number; avg_tool_calls: number; avg_context_objects: number; avg_execution_ms: number | null; }; declare function agentEvalThresholdsPass(metrics: ReturnType, thresholds: { minToolRequirement: number | null; minExecutionMatch?: number | null; minJudgePass?: number | null; maxWrongCertified?: number | null; maxFalseRefusal?: number | null; minRefusalRecall?: number | null; minGroundedNarration?: number | null; }): boolean; declare function buildEvalTrace(input: { testCase: AgentEvalCase; result: Awaited>; evaluation: ReturnType; durationMs: number; draftSaved: boolean; /** Persisted runtime evidence; never projected into a synthetic context pack. */ runtime?: RuntimeDrivenRun; }): AgentEvalTraceStage[]; export declare const __test__: { agentEvalThresholdsPass: typeof agentEvalThresholdsPass; buildEvalTrace: typeof buildEvalTrace; cliAnalysisDepth: typeof cliAnalysisDepth; cliReasoningEffort: typeof cliReasoningEffort; computeEvalMetrics: typeof computeEvalMetrics; narrationOutcomeForEval: typeof narrationOutcomeForEval; evaluateCase: typeof evaluateCase; }; export {}; //# sourceMappingURL=agent.d.ts.map