/** * Renders the 9-section POC markdown report. * * Input is the templatized pattern set + per-SIEM context; output is a * single markdown document. Every number here must come from pulled * events. No fabrication — confidence grades mark any estimate. */ import type { ExtractedPattern, ExtractedPatterns } from './pattern-extraction.js'; import type { SiemId } from './siem/pricing.js'; import { type Action as CostAction, type DollarSource } from './cost.js'; import { type PocEnrichment } from './poc-enrichers.js'; import type { IncidentCluster } from './detectors/incident-cluster.js'; import type { RedundancyPair } from './poc-enrichers.js'; export interface RenderInput { siem: SiemId; window: string; scope?: string; query?: string; extraction: ExtractedPatterns; /** Target event count for the pull (not necessarily reached). */ targetEventCount: number; /** Wall time spent inside SIEM pull. */ pullWallTimeMs: number; /** Wall time spent in the templater. */ templateWallTimeMs: number; /** * Raw bytes the customer's SIEM actually ingested across the * sampled events — i.e., the size of `events.jsonl` on disk, the * outer CW envelope and all. Cost projections use this when * present so the dollar figure matches what the vendor bills on, * NOT the smaller templater-input size after coerceToLine strips * the envelope. If absent, the envelope falls back to * extraction.totalBytes (templater input) and notes the gap. */ rawIngestBytes?: number; /** Reason the pull ended. */ reasonStopped: 'target_reached' | 'time_exhausted' | 'source_exhausted' | 'error'; /** Raw SIEM query string used. */ queryUsed: string; /** Windows in the 'window' string, parsed to hours, used to project $/wk. */ windowHours: number; /** Analyzer cost per GB for the detected SIEM. */ analyzerCostPerGb: number; /** * Origin of `analyzerCostPerGb`. Threaded so every dollar emission can * route through `fmtDisclosedDollar` with the right disclosure tail. * - 'list_price' — pulled from vendors.json (needs caveat). * - 'customer_supplied' — caller passed an override rate (no caveat). * - 'unset' — no rate available (dollar lines drop). * * TODO(Phase 1.4 upstream): wire from `cost.ts` rate_source upstream of * `poc-envelope-v2.ts`; today most callers default to 'list_price' via * the vendors.json lookup. */ rateSource?: DollarSource; /** * Vendor display name for the disclosure tail (e.g. "Splunk", "Datadog"). * When null, the disclosure renders "at SIEM list price …". Resolved * upstream from `SIEM_DISPLAY_NAMES[siem]` when not provided. */ siemLabel?: string | null; snapshotId: string; startedAt: string; finishedAt: string; mcpVersion: string; /** When a note needs to surface in the banner (e.g., dropped events). */ banners?: string[]; /** Pull notes from the connector (retry info, error detail, etc.). */ pullNotes?: string[]; /** * The customer's total daily log volume (GB/day). Provided by user * arg OR auto-detected from the SIEM. When set (and positive), per- * pattern costs are scaled from the sample to the full daily volume. */ totalDailyGb?: number; /** * Where the totalDailyGb came from: 'user_arg' | 'auto_detected' | * 'none'. Drives the banner text in the executive summary. */ volumeSource?: 'user_arg' | 'auto_detected' | 'none'; /** Human-readable label for the detection source (e.g., "Datadog Usage API, 7d avg"). */ volumeDetectSource?: string; /** Error note when auto-detect was attempted but failed. Surfaced under the banner. */ volumeDetectErrorNote?: string; /** * Cost-figure uncertainty bracket attached by the volume-detection * connector. Set when the detected `totalDailyGb` came from a fallback * estimator (Datadog `logs_by_index` × 500 B/event, CloudWatch * NEVER_EXPIRE retention) rather than a byte-precise source. When * present, every projected cost figure is rendered as a range * (`$3.8K - $15.2K/yr`) instead of a single misleading number. * Multipliers apply to the central estimate. */ volumeRangeMultiplier?: { low: number; high: number; }; /** * Optional: AI-generated display name per pattern identity. When set, * the identity is rendered as ` ()` in every * table instead of just the identity. Missing entries fall back to * raw identity — fail-soft. */ aiPrettyNames?: Record; /** Error note from the AI prettify call, if any. Surfaced in the appendix. */ aiPrettifyErrorNote?: string; /** * Per-pattern dependency-check counts, pre-warmed by the POC submit * pipeline. When present, the action column refines `mute` → * `blocked` for any identity with refs in monitors/dashboards/saved * searches. Absence ≠ "no deps"; the renderer marks the cell * `(not checked)` when the identity is missing from the map. */ dependencyByIdentity?: Map; /** * Per-pattern first-seen age in seconds, from engine history. Only * resolvable when the POC submit pipeline also has an engine env * configured AND the pattern has a `tenxHash` known to the engine. * Otherwise the column reads `(unknown)` — a degraded, honest cell. */ firstSeenByIdentity?: Map; /** * Epoch-ms window bounds of the SIEM pull. When set, the renderer * passes them to the enricher so per-pattern emergence (new / growing * / stable / recent_burst) is computed from per-event timestamps * inside the window rather than relying on engine history. */ windowStartMs?: number; windowEndMs?: number; } export interface RenderResult { markdown: string; summary: { eventsAnalyzed: number; patternsFound: number; totalCostAnalyzed: number; projectedSavings: number; top3Actions: string[]; }; } type Confidence = 'high' | 'medium' | 'low'; interface EnrichedPattern extends ExtractedPattern { costPerWindow: number; pctOfTotal: number; costPerWeek: number; /** * The lossless cost-cutting lever picked for this pattern. We NEVER * auto-recommend `mute`/`drop`/`sample` (that contradicts the "save * money WITHOUT losing data" pitch). Every reducible pattern gets a * lossless lever; everything else is kept verbatim. * - `compact` — re-encode in place, stays searchable in the SIEM. * - `offload` — route to the customer's own S3, recoverable any time. * - `tier_down` — move to the SIEM's cheaper in-platform tier, retained. * - `keep` — errors/warnings + low-volume patterns pass through. */ recommendedAction: 'compact' | 'offload' | 'tier_down' | 'keep'; /** * The MEASURED (compact) or modeled (offload/tier_down) fraction of * this pattern's bytes the lever removes from the SIEM bill. Drives * projectedSavings. `keep` → 0. */ leverFraction: number; /** * Retained for type-compat with the envelope/enricher param shapes. * Always 1 now (we do not sample). */ sampleRate: number; projectedSavings: number; reasoning: string; confidence: Confidence; /** Snake-case identity, for ready-to-paste receiver configs. */ identity: string; /** POC enrichment fields (incident cluster id, top slot, redundancy, dep-check, first-seen). */ poc: PocEnrichment; /** * Longest verbatim literal run from the template body, used as a * phrase-match anchor in exclusion configs. Indexed phrase queries * are 1–2 orders of magnitude cheaper at SIEM ingest scale than * regex with `.*` interleaving, and don't false-positive on token * reorderings. */ literalPhrase: string; /** * True when `literalPhrase` sits at the start of the template (so * a phrase-prefix anchor is exact). False when the template begins * with a variable slot and the phrase is the longest internal run. * Exclusion configs prepend an approximation footnote in that case. */ literalLeading: boolean; /** * The destination's preferred level-1 action (`tier_down`, `offload`, * `compact`, ...) per `DEFAULT_ACTION_BY_DESTINATION`. Informs which * lossless lever the decision falls to when compact is unavailable * (Datadog: tier_down, Splunk: offload, ClickHouse: offload, ...). */ destinationLevel1Action: CostAction; } /** * ────────── View-specific renderers ────────── * * The MCP tool returns one of six shapes depending on the caller's * `view` arg. The idea is progressive disclosure: the default * `summary` is scannable in a CLI (~30 lines); callers can re-invoke * status with a more verbose view when they need specific artifacts. * * All views share the same `enrichPatterns()` + `displayName()` * helpers so a given RenderInput always produces consistent output * across views — only the level of detail changes. */ /** * Short-form view — the default. Annual savings banner, top-5 table, * risk flags, available views CTA. Intended to be scannable in one * terminal screen. */ export declare function renderPocSummary(input: RenderInput, topN?: number): string; /** * YAML view — receiver mute-file entries for the top N patterns. * Paste-ready for a GitOps ConfigMap commit. Includes the display * name as a YAML comment so reviewers can scan without decoding the * identity strings. */ export declare function renderPocYaml(input: RenderInput, topN?: number): string; /** * Native SIEM exclusion-config view — the "I don't want the log10x * receiver, just give me the raw SIEM config" path. */ export declare function renderPocConfigs(input: RenderInput, topN?: number): string; /** * Top-N drivers view — the summary's table, but larger and without * the surrounding banner/CTA. For "show me the top 20." */ export declare function renderPocTop(input: RenderInput, topN?: number): string; /** * Pattern-detail view — one pattern, fully expanded. Sample event, * slot variables, recommended action, receiver YAML, risk context. */ export declare function renderPocPattern(input: RenderInput, identity: string): string; /** * Full view — the original 9-section report. Unchanged; the summary * / yaml / configs / top / pattern views are slices of the same data. */ export declare function renderPocReport(input: RenderInput): RenderResult; /** * Public surface for the v2 envelope builder. Same shape as the * internal `enrichPatternsWithSections`. Underscore prefix signals * "internal but cross-module — may change." */ export declare function _enrichForEnvelope(input: RenderInput): { patterns: EnrichedPattern[]; clusters: IncidentCluster[]; redundancyPairs: RedundancyPair[]; }; /** * Pull the strongest literal anchor from a templated pattern. * * A template body looks like `$(ts) ERROR payment_gateway_timeout for tenant=$ ms=$`, * with `$(...)` typed slots and bare `$` value slots. Splitting on either * variant gives the runs of literal text the template guarantees to emit * verbatim. The longest such run is the cheapest, most-discriminating * anchor for an exclusion query. * * Returns: * - `phrase`: the longest run with at least 3 alphanumeric chars. * - `leading`: true if `phrase` is the first run (no variable before it). * * Fallback: when no run clears the alphanumeric threshold, return the * spaced identity so the renderer still has *something* paste-worthy. * The caller surfaces an "approximation" footnote in that case via * `leading=false`. */ declare function extractLiteralPhrase(template: string, identity: string): { phrase: string; leading: boolean; }; /** * Test-only surface: phrase extractor + per-vendor exclusion renderer. * Not part of the public MCP API; the leading underscore signals * "internal, may change without notice." */ export declare const _internals: { extractLiteralPhrase: typeof extractLiteralPhrase; renderNativeExclusion: (siem: SiemId, drops: EnrichedPattern[]) => string; renderFluentBit: (drops: EnrichedPattern[]) => string; }; export type _EnrichedPattern = EnrichedPattern; export {};