@jeffjassky/telemetry 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/types.ts","../src/server/rollups.ts","../src/server/funnel.ts","../src/server/query.ts","../src/server/views.ts","../src/server/mcp.ts"],"names":["Schema"],"mappings":"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","file":"mcp.js","sourcesContent":["import { uuidv7 } from 'uuidv7';\n\n/**\n * UUIDv7 — time-ordered, insertion-local. Node's crypto.randomUUID() is v4:\n * random, NOT sortable. Do not substitute it here; _ids double as insertion\n * order and traceIds are sampled on their low 32 bits (schema §2.6).\n */\nexport const newId = uuidv7;\n\nexport const TelemetryKind = {\n Event: 'event',\n Error: 'error',\n Span: 'span',\n State: 'state',\n Usage: 'usage',\n} as const;\nexport type TelemetryKind = (typeof TelemetryKind)[keyof typeof TelemetryKind];\n\nexport const TELEMETRY_KINDS: readonly TelemetryKind[] = Object.values(TelemetryKind);\n\nexport const LogLevel = {\n Debug: 'debug', Info: 'info', Warn: 'warn', Error: 'error', Fatal: 'fatal',\n} as const;\nexport type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];\n\nexport const Env = { Prod: 'prod', Staging: 'staging', Dev: 'dev' } as const;\nexport type Env = (typeof Env)[keyof typeof Env];\n\nexport const Origin = { Server: 'server', Client: 'client' } as const;\nexport type Origin = (typeof Origin)[keyof typeof Origin];\n\nexport type EntityRef = `${string}:${string}`;\n\nexport const UNKNOWN = 'unknown';\n\n/**\n * The platform read scope. A dashboard `Viewer` whose tenantId is `'*'` reads\n * ACROSS tenants — a support console, a platform-wide cost page. The package\n * never decides who is a platform admin; the host's `viewerAdapter` does. All\n * `'*'` buys is that the escape hatch is expressible and inside the boundary\n * instead of a host reaching around `scoped()`.\n *\n * Which is exactly why it is RESERVED on the write side. A tenant literally\n * named `'*'` would silently become a cross-tenant read — privilege escalation\n * via a string — so every path that can mint a tenantId from outside the\n * package refuses it: emit(), forget(), createKey() in fixed mode, and the\n * ingest handler once the tenant has resolved (key / session / claimed alike).\n */\nexport const PLATFORM_SCOPE = '*';\n\n/** true when a scope is the cross-tenant platform scope rather than a tenant */\nexport const isPlatformScope = (tenantId: unknown): boolean => tenantId === PLATFORM_SCOPE;\n\n/** one wording for the refusal, so the reservation is named wherever it bites */\nexport const RESERVED_TENANT_MESSAGE =\n `telemetry: tenantId \"${PLATFORM_SCOPE}\" is reserved for the dashboard's cross-tenant ` +\n 'platform scope and may never be written';\n\n/** null = never expires. Sized for small-SaaS volume; per-name override in EventSpec. */\nexport const RETENTION_DAYS: Record<TelemetryKind, number | null> = {\n // keep-all + 90d is cheap at this scale, and it makes p95-by-route a raw\n // query instead of a rollup design (schema §2.1)\n [TelemetryKind.Span]: 90,\n [TelemetryKind.Error]: 90,\n [TelemetryKind.Event]: 730,\n [TelemetryKind.State]: 730,\n [TelemetryKind.Usage]: null, // money is immortal\n};\n\n/**\n * Keep-all across the board — small-SaaS scale, exactness beats extrapolation.\n * The per-trace machinery stays, dormant: turn a kind down here (or one name\n * via EventSpec.sampleRate) when volume ever demands it. Rollups are exact\n * either way — they run before the verdict (schema §4.6).\n */\nexport const SAMPLE_RATE: Record<TelemetryKind, number> = {\n [TelemetryKind.Span]: 1,\n [TelemetryKind.Event]: 1,\n [TelemetryKind.Error]: 1, // raw storage burst-capped per fingerprint instead\n [TelemetryKind.State]: 1,\n [TelemetryKind.Usage]: 1, // NEVER sample. money.\n};\n\nexport const REJECT_TTL_DAYS = 30;\nexport const SCHEMA_VERSION = 2;\n\n/**\n * `body` is the only unbounded field on the envelope, and it feeds Mongo's\n * 16 MB document ceiling. Every other bound in the package is stated and\n * enforced (boundedMeta 4 KB, batch 512 KB / 100 records, index budget 24), so\n * this one is too. Override per instance with CreateTelemetryConfig.bodyMax.\n */\nexport const BODY_MAX_CHARS = 16_384;\n\n/** surfaced via t.counters so drops are never silent */\nexport interface TelemetryCounters {\n rejected: number;\n defaulted: number;\n sampled: number;\n capped: number;\n rollupSkipped: number;\n /** an insert-gated write whose dedupeKey (or usage.idempotencyKey) already existed */\n deduped: number;\n /** a `body` clipped to bodyMax — the row survives, marked */\n truncated: number;\n}\n\nexport const newCounters = (): TelemetryCounters => ({\n rejected: 0, defaulted: 0, sampled: 0, capped: 0, rollupSkipped: 0,\n deduped: 0, truncated: 0,\n});\n\n/**\n * Consistent probability sampling — deterministic per trace, no propagation\n * needed. A traceId with no parseable hex tail silently degrades to per-record\n * sampling, which invalidates every per-trace aggregate (schema §2.6). That is\n * a bug in the caller's id generation, so it fails loudly in dev rather than\n * skewing data in prod.\n */\nexport const traceKeep = (traceId: string | undefined, rate: number): boolean => {\n if (rate >= 1) return true;\n if (!traceId) return Math.random() < rate;\n const tail = parseInt(traceId.slice(-8), 16);\n if (!Number.isFinite(tail)) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `telemetry: traceId \"${traceId}\" has no parseable hex tail — trace-consistent ` +\n `sampling is impossible and would degrade to per-record. Use UUIDv7 or a ` +\n `32-hex OTel trace id.`,\n );\n }\n return Math.random() < rate;\n }\n return tail / 0xffffffff < rate;\n};\n\n/** Map has no toJSON — JSON.stringify(new Map()) is '{}'. Never stringify raw. */\nexport const plain = (v: unknown): unknown =>\n v instanceof Map ? Object.fromEntries([...v].map(([k, x]) => [k, plain(x)]))\n : Array.isArray(v) ? v.map(plain)\n : v instanceof Date ? v\n : v && typeof v === 'object' ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, plain(x)]))\n : v;\n\nexport interface Logger {\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport const noopLogger: Logger = { info() {}, warn() {}, error() {} };\n","import { Schema, type Connection, type Model } from 'mongoose';\nimport type { DimSource, Registry, RollupSpec } from './registry.js';\nimport type { TelemetryCounters } from './types.js';\n\n/**\n * One derived-aggregate primitive (schema §4.5). Group a stream of records by\n * a declared tuple and keep first / last / count / sums. The registry declares\n * the tuple; this file is agnostic — the same operation is a milestone, an\n * issue group, windowed spend, and an activity/retention stream.\n */\n\nexport function buildRollupModel(connection: Connection, modelName: string, collection: string): Model<any> {\n const existing = connection.models?.[modelName];\n if (existing) return existing;\n\n const schema = new Schema(\n {\n /** `${tenantId}|${as}|${dims.join('|')}|${bucketKey}` — deterministic, upsert-safe */\n _id: { type: String, required: true },\n tenantId: { type: String, required: true },\n /** rollup family. Several event names may feed one. */\n as: { type: String, required: true },\n /**\n * Dimension values in spec order. Subject dims keep their native\n * `type:id` form ('user:u_1') so erasure can match them directly;\n * everything else is `key=value`. Flattened strings for the same reason\n * subjectKeys is — never compound-index two fields of one subdoc array.\n */\n dims: { type: [String], required: true },\n /** denormalized prefix of the subject dim, when there is one — §5.4 hazard */\n subjectType: String,\n /** UTC-truncated bucket start; absent on lifetime rollups */\n bucketAt: Date,\n\n firstAt: { type: Date, required: true },\n lastAt: { type: Date, required: true },\n count: { type: Number, required: true, default: 0 },\n /** registry `sum` keys accumulated across every contributing record */\n sums: { type: Map, of: Number, default: {} },\n /** registry `capture` snapshot at FIRST occurrence — cohort dimensions */\n firstCapture: { type: Map, of: String, default: {} },\n firstTraceId: String,\n expiresAt: Date,\n },\n { collection, versionKey: false },\n );\n\n // cohort scan within one family, ordered by first occurrence\n schema.index({ tenantId: 1, as: 1, subjectType: 1, firstAt: 1 });\n // slice a family by dimension value, newest bucket first\n schema.index({ tenantId: 1, as: 1, dims: 1, bucketAt: -1 });\n // erasure + per-subject journey across ALL families — no `as` prefix on purpose\n schema.index({ tenantId: 1, dims: 1 });\n schema.index(\n { expiresAt: 1 },\n { expireAfterSeconds: 0, partialFilterExpression: { expiresAt: { $exists: true } } },\n );\n\n return connection.model(modelName, schema);\n}\n\n// ── dimension resolution ────────────────────────────────────────────────────\n\nconst path = (o: any, p: string): unknown =>\n typeof o?.get === 'function' ? o.get(p) : p.split('.').reduce((a: any, k) => a?.[k], o);\n\n/** `subject` is handled by fan-out, so it resolves to undefined here.\n * Exported: emit()'s burst cap keys on the same sources. */\nexport const resolveDim = (src: DimSource, doc: any): unknown =>\n src.startsWith('attr:') ? doc.attrs?.get(src.slice(5))\n : src.startsWith('field:') ? path(doc, src.slice(6))\n : undefined;\n\nconst label = (src: DimSource) => src.slice(src.indexOf(':') + 1);\n\n/** UTC. Weeks start Monday. */\nexport const truncate = (d: Date, b?: RollupSpec['bucket']): Date | undefined => {\n if (!b) return undefined;\n if (b === 'hour') return new Date(Math.floor(d.getTime() / 36e5) * 36e5);\n const y = d.getUTCFullYear();\n const m = d.getUTCMonth();\n if (b === 'month') return new Date(Date.UTC(y, m, 1));\n const day = new Date(Date.UTC(y, m, d.getUTCDate()));\n if (b === 'day') return day;\n return new Date(day.getTime() - ((day.getUTCDay() + 6) % 7) * 864e5);\n};\n\n/**\n * Update-pipeline upsert so late/backfilled events correct EVERYTHING, not\n * just the boundaries. `$setOnInsert` for firstCapture/firstTraceId would pin\n * the cohort dimensions to whichever record LANDED first rather than whichever\n * OCCURRED first — silently wrong for offline clients, backfills, and retries.\n */\nexport async function recordRollup(\n RollupModel: Model<any>,\n doc: any,\n name: string,\n spec: RollupSpec,\n counters: TelemetryCounters,\n): Promise<void> {\n // aggregate-plane actor gate: admin support browsing must never move a\n // customer aggregate; the raw row is untouched (RollupSpec.actors)\n if (spec.actors && doc.actor) {\n const actorType = String(doc.actor).split(':')[0];\n if (!spec.actors.includes(actorType)) return;\n }\n\n const as = spec.as ?? name;\n const at: Date = doc.occurredAt;\n const bucketAt = truncate(at, spec.bucket);\n const bucketKey = bucketAt ? bucketAt.toISOString() : '';\n\n // Non-subject dims resolved once. Absent a declared fallback, a missing dim\n // means the record cannot be keyed at all — an implicit 'null' bucket would\n // silently corrupt every group. `dimDefault` makes that bucket EXPLICIT: the\n // host names it, so \"no value\" is a group you can query rather than a drop\n // you have to notice in a counter.\n const fixed = new Map<DimSource, string>();\n for (const src of spec.by) {\n if (src === 'subject') continue;\n let v = resolveDim(src, doc);\n if (v == null || v === '') {\n if (spec.dimDefault === undefined) {\n counters.rollupSkipped++;\n return;\n }\n v = spec.dimDefault;\n }\n fixed.set(src, `${label(src)}=${String(v)}`);\n }\n\n // At most one `subject` dim per spec (asserted in validateRegistry).\n // dimDefault deliberately does NOT apply here: a non-subject dim that is\n // missing is a record we can still attribute to someone, while a record with\n // no matching subject cannot be attributed at all. Bucketing it under a\n // placeholder subject would invent a party that does not exist — and erasure\n // matches subject dims by their native `type:id` form, so the placeholder\n // would be unreachable by forget().\n const fansOut = spec.by.includes('subject');\n const refs: (string | null)[] = fansOut\n ? (doc.subjectKeys ?? []).filter(\n (r: string) => !spec.subjects || spec.subjects.includes(r.split(':')[0]!),\n )\n : [null];\n if (!refs.length) return;\n\n const firstCapture = Object.fromEntries(\n (spec.capture ?? [])\n .map((src) => [label(src), resolveDim(src, doc)] as const)\n .filter(([, v]) => v != null)\n .map(([k, v]) => [k, String(v)]),\n );\n\n const expiresAt =\n spec.retentionDays != null ? new Date(at.getTime() + spec.retentionDays * 864e5) : undefined;\n\n await RollupModel.bulkWrite(\n refs.map((ref) => {\n const dims = spec.by.map((src) => (src === 'subject' ? ref! : fixed.get(src)!));\n /** true when this record becomes the new earliest occurrence */\n const isNewFirst = {\n $or: [{ $eq: [{ $type: '$firstAt' }, 'missing'] }, { $lt: [at, '$firstAt'] }],\n };\n const sums = Object.fromEntries(\n (spec.sum ?? [])\n .map((k) => [k, doc.metrics?.get(k)] as const)\n .filter(([, v]) => typeof v === 'number')\n .map(([k, v]) => [`sums.${k}`, { $add: [{ $ifNull: [`$sums.${k}`, 0] }, v] }]),\n );\n return {\n updateOne: {\n filter: { _id: `${doc.tenantId}|${as}|${dims.join('|')}|${bucketKey}` },\n update: [\n {\n $set: {\n tenantId: doc.tenantId,\n as,\n dims,\n ...(ref ? { subjectType: ref.split(':')[0] } : {}),\n ...(bucketAt ? { bucketAt } : {}),\n ...(expiresAt ? { expiresAt } : {}),\n // aggregation $min/$max ignore missing, so correct on insert too\n firstAt: { $min: ['$firstAt', at] },\n lastAt: { $max: ['$lastAt', at] },\n count: { $add: [{ $ifNull: ['$count', 0] }, 1] },\n ...sums,\n firstTraceId: { $cond: [isNewFirst, doc.traceId ?? null, '$firstTraceId'] },\n firstCapture: { $cond: [isNewFirst, { $literal: firstCapture }, '$firstCapture'] },\n },\n },\n ],\n upsert: true,\n },\n };\n }),\n { ordered: false },\n );\n}\n","import type { Model } from 'mongoose';\nimport type { Registry, RollupSpec } from './registry.js';\nimport { truncate } from './rollups.js';\nimport type { TimeRange } from './query.js';\n\n/**\n * The sixth read primitive: cohort funnels over lifetime milestone rollups\n * (cohort-math §1). Every host that models milestones as lifetime rollups then\n * rewrites the same three things — stage ordering, the cohort window, and the\n * median time-to-step aggregation. This file is that code, once.\n *\n * The formulas are ported verbatim from maxed's `src/server/telemetry/funnel.ts`\n * (recon: plans/cohort-math.md). Names differ where maxed's were host-specific\n * or actively wrong; the mapping is exact and total:\n *\n * maxed here why\n * ─────────────────────────────────────────────────────────────────────────\n * accounts subjects the package counts subjects, not accounts\n * first first unchanged — |{s : stage 1 present}|\n * pctOfFirst pctOfFirst unchanged\n * pctOfPrevious pctOfPrevious unchanged\n * medianDaysFromSignup medianDaysFromAnchor the anchor milestone is configurable here\n * medianDaysFromPrevious medianDaysFromPrevious unchanged\n * StageSummary.stuckAccounts notReached reached k−1, not k (drop-off INTO k)\n * accountsStuckAt(k) stalledAt reached k, not k+1 (drop-off OUT OF k)\n *\n * The last two are the deliberate divergence. maxed ships both quantities under\n * the word \"stuck\", and its `accountsStuckAt` reports every CONVERTED subject as\n * stuck because the terminal stage has no k+1 to fail (cohort-math H3). Here the\n * two quantities have two names and `stalledAt` is **null** on the terminal\n * stage — \"how many stalled here\" has no answer when there is nowhere to go\n * next, and 0 would be as wrong as `subjects`.\n *\n * Interval convention: the package is half-open (`$gte`/`$lt`) everywhere and\n * stays that way. maxed's cohort window is closed on both ends (cohort-math R6),\n * so `cohort.endInclusive` exists to reproduce it — and is translated to a\n * half-open bound HERE (see `cohortWindow`) rather than leaking `$lte` into the\n * read layer.\n */\n\n// ── shapes ──────────────────────────────────────────────────────────────────\n\nexport interface FunnelStageSpec {\n /** the lifetime rollup family whose doc marks this stage — `firstAt` IS the timestamp */\n as: string;\n /** stable identifier in the response. Default: `as` */\n key?: string;\n label?: string;\n description?: string;\n}\n\nexport interface FunnelCohortWindow extends TimeRange {\n /**\n * Include `to` itself. Default FALSE — the package is half-open everywhere and\n * that is the correct convention. maxed's funnel is closed on both ends\n * (cohort-math R6), so a host migrating off it sets this to keep its numbers.\n */\n endInclusive?: boolean;\n}\n\nexport interface FunnelParams {\n /** ordered stages. A stage may be reached without its predecessor — see LITERAL below. */\n stages: readonly FunnelStageSpec[];\n /** the milestone that assigns cohort membership and anchors time-to-step. Default: `stages[0].as` */\n anchor?: string;\n /** the cohort window, applied to the ANCHOR family's `firstAt` */\n cohort: FunnelCohortWindow;\n /** exit families — counted, never staged (cohort-math §1.4) */\n exits?: readonly FunnelStageSpec[];\n /** restrict the cohort to one subject type ('account') */\n subjectType?: string;\n /** also slice the cohort by anchor date. UTC, Monday-start weeks — `truncate()` */\n interval?: 'day' | 'week' | 'month';\n /** cohort size cap. Clamped to `limits.funnel`; hitting it sets `truncated`. */\n limit?: number;\n}\n\nexport interface FunnelStageResult {\n /** 1-based position in `stages` */\n order: number;\n key: string;\n as: string;\n label: string;\n description?: string;\n /** subjects with this stage present. NOT monotonic — see LITERAL below. */\n subjects: number;\n /** 0–100, unrounded. null when stage 1's count is 0. */\n pctOfFirst: number | null;\n /** 0–100, unrounded. null on stage 1 and whenever the previous count is 0 — never 0, never Infinity. */\n pctOfPrevious: number | null;\n /** fractional days, unrounded. null on an empty sample. */\n medianDaysFromAnchor: number | null;\n /** fractional days, unrounded. null on stage 1 and on an empty sample. */\n medianDaysFromPrevious: number | null;\n /** reached the PREVIOUS stage and not this one — drop-off INTO this stage. 0 on stage 1. */\n notReached: number;\n /** reached this stage and not the NEXT — drop-off OUT OF it. null on the terminal stage. */\n stalledAt: number | null;\n}\n\nexport interface FunnelExitResult {\n key: string;\n as: string;\n label: string;\n subjects: number;\n}\n\nexport interface FunnelSlice {\n /** the truncated anchor date — UTC bucket start, not a \"2026-W31\" label */\n at: Date;\n subjects: number;\n stages: FunnelStageResult[];\n}\n\nexport interface FunnelResult {\n /** cohort size: subjects whose ANCHOR firstAt fell in the window */\n cohortSubjects: number;\n /** |{ s : stage 1 present }| — the `pctOfFirst` denominator. Equals cohortSubjects when the anchor IS stage 1. */\n first: number;\n stages: FunnelStageResult[];\n exits: FunnelExitResult[];\n /** present only when `interval` was asked for; ascending by `at` */\n slices: FunnelSlice[] | null;\n /** the cohort read hit its cap — every number below is an UNDERCOUNT (traps §18: no silent caps) */\n truncated: boolean;\n cohort: { from: Date; to: Date; endInclusive: boolean; anchor: string };\n dataSource: 'rollups';\n}\n\n/** one subject's assembled milestone index — the input `summarizeStages` reasons over */\nexport interface CohortSubject {\n ref: string;\n /** the anchor milestone's firstAt. Never null via `runFunnel`; the branch exists per cohort-math R9. */\n anchorAt: Date | null;\n /** stage key → first occurrence */\n stages: Record<string, Date>;\n /** exit key → first occurrence */\n exits: Record<string, Date>;\n}\n\ninterface ResolvedStage {\n order: number;\n key: string;\n as: string;\n label: string;\n description?: string;\n}\n\n// ── pure math ───────────────────────────────────────────────────────────────\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Mean of the two middles on even counts — NOT the lower middle (cohort-math R1).\n * Empty set is null, never 0: \"nobody got here\" and \"everybody got here\n * instantly\" are different facts and a chart must be able to tell them apart.\n * No rounding anywhere; the caller formats.\n */\nexport function median(values: readonly number[]): number | null {\n if (values.length === 0) return null;\n const s = [...values].sort((a, b) => a - b);\n const mid = s.length >> 1;\n return s.length % 2 === 1 ? s[mid]! : (s[mid - 1]! + s[mid]!) / 2;\n}\n\n/**\n * The stage table. Pure — same input, same output, no Mongo — so the equivalence\n * test can pin it cell by cell.\n *\n * LITERAL, never backfilled (cohort-math R4): a subject counts at stage N only\n * if a stage-N doc exists for it. Reaching N+1 does not imply N. So stage counts\n * are NOT required to be non-increasing, `pctOfPrevious` may exceed 100%, and a\n * skipping subject contributes to `subjects` for the later stage but to nothing\n * else on that row — not `prevReached`, not the fromPrevious sample, not\n * `notReached`. That is a data fact, not a bug, and smoothing it would invent\n * subjects that never existed.\n */\nexport function summarizeStages(\n subjects: readonly CohortSubject[],\n stages: readonly ResolvedStage[],\n): FunnelStageResult[] {\n const firstKey = stages[0]?.key;\n const first = firstKey ? subjects.filter((s) => s.stages[firstKey]).length : 0;\n\n return stages.map((st, i) => {\n const prev = i > 0 ? stages[i - 1]! : null;\n const next = i < stages.length - 1 ? stages[i + 1]! : null;\n\n const reached: CohortSubject[] = [];\n const fromAnchor: number[] = [];\n for (const s of subjects) {\n const at = s.stages[st.key];\n if (!at) continue;\n reached.push(s);\n // a subject with no anchor is excluded from the SAMPLE but still counted\n // in `subjects` (cohort-math R9) — unreachable through runFunnel, kept\n // because summarizeStages is exported and can be fed from elsewhere\n if (s.anchorAt) fromAnchor.push((at.getTime() - s.anchorAt.getTime()) / DAY_MS);\n }\n\n let prevReached = 0;\n let notReached = 0;\n const fromPrevious: number[] = [];\n if (prev) {\n for (const s of subjects) {\n const p = s.stages[prev.key];\n if (!p) continue;\n prevReached += 1;\n const cur = s.stages[st.key];\n if (cur) fromPrevious.push((cur.getTime() - p.getTime()) / DAY_MS);\n else notReached += 1;\n }\n }\n\n // `pctOfPrevious` divides ALL subjects that reached this stage by only those\n // that reached the previous one — two independent counts, not a conversion\n // rate (cohort-math H4). Ported deliberately: changing it would silently\n // disagree with the funnel a migrating host is reading today.\n return {\n order: st.order,\n key: st.key,\n as: st.as,\n label: st.label,\n ...(st.description ? { description: st.description } : {}),\n subjects: reached.length,\n pctOfFirst: first > 0 ? (reached.length / first) * 100 : null,\n pctOfPrevious: prev ? (prevReached > 0 ? (reached.length / prevReached) * 100 : null) : null,\n medianDaysFromAnchor: median(fromAnchor),\n medianDaysFromPrevious: prev ? median(fromPrevious) : null,\n notReached,\n // the divergence: maxed's `!next` clause makes every subject that reached\n // the terminal stage \"stuck\" there. null says \"undefined\", which is true.\n stalledAt: next ? subjects.filter((s) => s.stages[st.key] && !s.stages[next.key]).length : null,\n };\n });\n}\n\n// ── registry lookup ─────────────────────────────────────────────────────────\n\n/** the first declaration of a rollup family — validateRegistry pins the shape, so it speaks for all */\nexport function findFamily(registry: Registry, as: string): { name: string; spec: RollupSpec } | null {\n for (const [name, s] of Object.entries(registry)) {\n for (const r of s.rollups ?? []) {\n if ((r.as ?? name) === as) return { name, spec: r };\n }\n }\n return null;\n}\n\n/**\n * A milestone family is a LIFETIME rollup keyed on exactly one subject dim. Any\n * other shape cannot answer \"when did this subject first reach this stage\" —\n * a bucketed family has one doc per period, and an extra dim has one doc per\n * (subject, value). Both would produce a plausible wrong number, so this throws\n * instead. A registry mistake must fail loudly; that is what this package is for.\n */\nexport function requireMilestoneFamily(registry: Registry, as: string, primitive: string): RollupSpec {\n const found = findFamily(registry, as);\n if (!found) {\n throw new Error(\n `telemetry: ${primitive} — no rollup family \"${as}\" is declared. Add a \\`rollups: [{ as: '${as}', ` +\n `by: ['subject'], subjects: [...] }]\\` block to the event that marks it.`,\n );\n }\n const { name, spec } = found;\n const shape = `by: [${spec.by.map((d) => `'${d}'`).join(', ')}]${spec.bucket ? `, bucket: '${spec.bucket}'` : ''}`;\n if (spec.bucket) {\n throw new Error(\n `telemetry: ${primitive} — rollup family \"${as}\" (declared on \"${name}\") is BUCKETED (${shape}). ` +\n `A milestone needs a lifetime family so \\`firstAt\\` is the one moment the subject reached it; ` +\n `a bucketed family has one doc per period and would count the same subject repeatedly.`,\n );\n }\n if (spec.by.length !== 1 || spec.by[0] !== 'subject') {\n throw new Error(\n `telemetry: ${primitive} — rollup family \"${as}\" (declared on \"${name}\") is keyed ${shape}, ` +\n `but a milestone must be keyed by exactly one subject dim (\\`by: ['subject']\\`). ` +\n `Extra dims split one subject across several docs, which would over-count every stage.`,\n );\n }\n return spec;\n}\n\n// ── the read ────────────────────────────────────────────────────────────────\n\nexport interface FunnelCtx {\n RollupModel: Model<any>;\n registry: Registry;\n /** cohort size cap — config, per traps §18 */\n cohortCap: number;\n /**\n * The tenancy term for a scope, INJECTED. This file computes cohort math; it\n * does not get to decide who may read across tenants, and hard-wiring\n * `{ tenantId }` here would put that policy in two places. query.ts owns it.\n */\n scopeMatch: (scope: string) => Record<string, unknown>;\n}\n\n/**\n * Closed → half-open, exactly. BSON dates are integer milliseconds, so\n * `{$lte: e}` and `{$lt: e + 1ms}` select identically — which is why\n * `endInclusive` can be honoured without a `$lte` ever reaching the query layer.\n */\nfunction cohortWindow(c: FunnelCohortWindow): { from: Date; to: Date; endInclusive: boolean } {\n const endInclusive = c.endInclusive === true;\n return { from: c.from, to: endInclusive ? new Date(c.to.getTime() + 1) : c.to, endInclusive };\n}\n\nexport async function runFunnel(\n ctx: FunnelCtx,\n scope: string,\n params: FunnelParams,\n): Promise<FunnelResult> {\n if (!params.stages?.length) throw new Error('telemetry: funnel() needs at least one stage');\n\n const stages: ResolvedStage[] = params.stages.map((s, i) => ({\n order: i + 1,\n key: s.key ?? s.as,\n as: s.as,\n label: s.label ?? s.key ?? s.as,\n ...(s.description ? { description: s.description } : {}),\n }));\n const exits: ResolvedStage[] = (params.exits ?? []).map((s, i) => ({\n order: i + 1,\n key: s.key ?? s.as,\n as: s.as,\n label: s.label ?? s.key ?? s.as,\n }));\n\n // ANCHOR, not first-event (cohort-math R5): a subject whose earliest record is\n // a page view three weeks before signup is still assigned by the signup. A\n // subject with no anchor doc in the window is absent from the cohort entirely\n // — every other milestone it has is invisible, counted at no stage.\n const anchor = params.anchor ?? stages[0]!.as;\n\n requireMilestoneFamily(ctx.registry, anchor, 'funnel()');\n for (const s of [...stages, ...exits]) requireMilestoneFamily(ctx.registry, s.as, 'funnel()');\n\n const { from, to, endInclusive } = cohortWindow(params.cohort);\n const cap = Math.min(Math.max(1, params.limit ?? ctx.cohortCap), ctx.cohortCap);\n\n const cohortMatch: Record<string, unknown> = {\n ...ctx.scopeMatch(scope),\n as: anchor,\n // AMBIGUOUS-2 (cohort-math): maxed's cohort read overwrites per row with no\n // ordering, so its `signupAt` is whichever row Mongo returned last —\n // nondeterministic. Unreachable here by construction: the rollup's `firstAt`\n // is a `$min` maintained on write, so the anchor timestamp is the EARLIEST\n // occurrence, always, matching the tie-break rule maxed uses everywhere else\n // (R12). We take min(at) and the storage enforces it.\n firstAt: { $gte: from, $lt: to },\n };\n if (params.subjectType) cohortMatch.subjectType = params.subjectType;\n\n // limit+1 so \"hit the cap\" is a fact, not an inference off an exact match\n const cohortRows = await ctx.RollupModel.find(cohortMatch)\n .sort({ firstAt: 1 })\n .limit(cap + 1)\n .lean();\n const truncated = cohortRows.length > cap;\n if (truncated) cohortRows.pop();\n\n // A subject ref is unique only WITHIN a tenant, so the index is keyed by\n // (tenant, ref). Under a cross-tenant scope two tenants' 'account:a1' are two\n // subjects; keying on the ref alone would braid their milestones into one\n // funnel and report a conversion that never happened.\n const keyOf = (r: any) => `${r.tenantId ?? ''}|${r.dims?.[0]}`;\n const index = new Map<string, CohortSubject>();\n const refs = new Set<string>();\n const tenants = new Set<string>();\n for (const r of cohortRows as any[]) {\n const ref = r.dims?.[0];\n if (!ref) continue;\n refs.add(ref);\n tenants.add(String(r.tenantId ?? ''));\n index.set(keyOf(r), { ref, anchorAt: r.firstAt, stages: {}, exits: {} });\n }\n\n if (refs.size) {\n const byAs = new Map<string, ResolvedStage[]>();\n for (const s of [...stages, ...exits]) {\n const list = byAs.get(s.as) ?? [];\n list.push(s);\n byAs.set(s.as, list);\n }\n // One read for every stage of every cohort member — the multi-subject gap\n // (cohort-math G3). N single-subject reads or an uncapped family scan were\n // the alternatives; the second truncates a large cohort silently.\n const stageRows = await ctx.RollupModel.find({\n ...ctx.scopeMatch(scope),\n as: { $in: [...byAs.keys()] },\n dims: { $in: [...refs] },\n // AMBIGUOUS-1 (cohort-math): maxed collects stages with `at >= cohortStart`\n // and no upper bound. We keep the lower bound — reading (a), as-written.\n // Reasons: (1) it is what maxed does, and an equivalence test against a\n // table computed under the other reading would prove nothing; (2) over\n // rollup storage the predicate reads \"first reached no earlier than the\n // cohort opened\", which is the only reading under which a cohort's funnel\n // is a function of its own window — drop the bound and a backfill dated\n // before the window silently adds stages to a report already published.\n // No UPPER bound, deliberately: a conversion landing months after the\n // window still belongs to its cohort (R7).\n firstAt: { $gte: from },\n })\n // The exact worst case of the query as written: a lifetime by:['subject']\n // family holds at most one doc per (tenant, subject), and `dims` matches\n // refs across every tenant in the cohort. Under a single-tenant scope that\n // is index.size × families. The bound cannot bite; it is here because §18\n // says every read carries its limit, not because we expect to reach it.\n .limit(refs.size * tenants.size * byAs.size + 1)\n .lean();\n\n for (const r of stageRows as any[]) {\n const subject = index.get(keyOf(r));\n if (!subject) continue;\n for (const s of byAs.get(r.as) ?? []) {\n const bag = exits.includes(s) ? subject.exits : subject.stages;\n const prevAt = bag[s.key];\n // earliest wins (R12) — belt and braces, `firstAt` is already a $min\n if (!prevAt || r.firstAt < prevAt) bag[s.key] = r.firstAt;\n }\n }\n }\n\n const subjects = [...index.values()];\n const summary = summarizeStages(subjects, stages);\n\n let slices: FunnelSlice[] | null = null;\n if (params.interval) {\n const groups = new Map<number, CohortSubject[]>();\n for (const s of subjects) {\n // subjects with no anchor date are dropped from the slices, matching\n // maxed (adminTelemetry.ts:660-666); unreachable via this path\n if (!s.anchorAt) continue;\n const at = truncate(s.anchorAt, params.interval)!;\n const k = at.getTime();\n groups.set(k, [...(groups.get(k) ?? []), s]);\n }\n slices = [...groups.entries()]\n .sort((a, b) => a[0] - b[0])\n .map(([k, members]) => ({\n at: new Date(k),\n subjects: members.length,\n stages: summarizeStages(members, stages),\n }));\n }\n\n return {\n cohortSubjects: subjects.length,\n first: stages[0] ? subjects.filter((s) => s.stages[stages[0]!.key]).length : 0,\n stages: summary,\n exits: exits.map((e) => ({\n key: e.key,\n as: e.as,\n label: e.label,\n subjects: subjects.filter((s) => s.exits[e.key]).length,\n })),\n slices,\n truncated,\n cohort: { from: params.cohort.from, to: params.cohort.to, endInclusive, anchor },\n dataSource: 'rollups' as const,\n };\n}\n","import type { Model } from 'mongoose';\nimport { isPlatformScope } from './types.js';\nimport type { Registry, RollupSpec } from './registry.js';\nimport { findFamily, runFunnel, type FunnelParams, type FunnelResult } from './funnel.js';\n\n/**\n * The six read primitives (dashboards §2). Everything the UI renders comes\n * through these — kind pages never touch Mongo, which is the seam that lets\n * span/event route to a columnar store later without touching a component.\n *\n * Traps §18 is law here, but per primitive rather than blanket. A time range is\n * REQUIRED by records, series, distribution, journey and distinctCount, and a\n * cohort window by funnel; `rollups()`'s range is OPTIONAL because a lifetime\n * family has no time axis to filter on; `trace()` takes NONE — it is pinned by\n * an indexed traceId and bounded by the trace itself.\n *\n * What is universal is the cap: every primitive carries its `$limit` INSIDE the\n * pipeline rather than applying it to a materialised result, the caps are\n * config, and the four that can cut an answer short — rollups, distribution,\n * distinctCount, funnel — report `truncated` rather than undercounting in\n * silence. The slow-query counter is an adapter. Every response reports\n * `dataSource` (recon #2) so a spliced number can always say which store\n * answered.\n *\n * Tenancy: every primitive takes a SCOPE, not a tenant. A scope is a tenantId,\n * or PLATFORM_SCOPE ('*') for a cross-tenant read. `'*'` is only ever reachable\n * because a host's `viewerAdapter` put it on the Viewer — an authorization\n * decision the package does not make and cannot second-guess. The write side\n * treats '*' as reserved (types.ts), so no stored row can ever carry it and\n * \"omit the tenant term\" is unambiguous.\n *\n * Row-shaped primitives (records/rollups/trace/journey) already return the\n * stored `tenantId` on every row, which is what makes a cross-tenant number\n * attributable — do not project it away.\n */\n\nexport interface QueryLimits {\n records: number;\n series: number;\n rollups: number;\n trace: number;\n journey: number;\n /** raw docs distribution will scan before it reports an undercount */\n distribution: number;\n /** rollup docs distinctCount will scan before it reports an undercount */\n distinct: number;\n /** subjects in one funnel cohort */\n funnel: number;\n}\n\nexport const DEFAULT_LIMITS: QueryLimits = {\n records: 200,\n series: 744, // a month of hourly buckets\n rollups: 500,\n trace: 500,\n journey: 500,\n distribution: 100_000,\n distinct: 100_000,\n funnel: 5_000,\n};\n\nexport interface RecordFilter {\n kind?: string;\n name?: string;\n severity?: string;\n env?: string;\n service?: string;\n release?: string;\n /** pin to one subject: 'user:u_1' */\n subject?: string;\n traceId?: string;\n /** equality on declared attrs */\n attrs?: Record<string, string>;\n /** range on declared metrics */\n metrics?: Record<string, { gte?: number; lte?: number }>;\n /** the customer toggle: exclude these actor TYPES ('admin', 'system') */\n excludeActorTypes?: string[];\n}\n\nexport interface TimeRange {\n from: Date;\n to: Date;\n}\n\nconst esc = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nfunction buildMatch(scope: string, range: TimeRange, f: RecordFilter): Record<string, unknown> {\n const match: Record<string, any> = {\n // the ONLY place the tenant term is optional. Omitted under '*' — every\n // other term still applies, and the time range is still mandatory (§18).\n ...(isPlatformScope(scope) ? {} : { tenantId: scope }),\n occurredAt: { $gte: range.from, $lt: range.to },\n };\n for (const k of ['kind', 'name', 'severity', 'env', 'service', 'release', 'traceId'] as const) {\n if (f[k]) match[k] = f[k];\n }\n if (f.subject) match.subjectKeys = f.subject;\n for (const [k, v] of Object.entries(f.attrs ?? {})) match[`attrs.${k}`] = v;\n for (const [k, r] of Object.entries(f.metrics ?? {})) {\n const term: Record<string, number> = {};\n if (r.gte != null) term.$gte = r.gte;\n if (r.lte != null) term.$lte = r.lte;\n if (Object.keys(term).length) match[`metrics.${k}`] = term;\n }\n if (f.excludeActorTypes?.length) {\n // a record with NO actor is a customer fact; only typed actors are excludable\n match.$and = [\n ...(match.$and ?? []),\n {\n $or: [\n { actor: { $exists: false } },\n { actor: { $not: new RegExp(`^(${f.excludeActorTypes.map(esc).join('|')}):`) } },\n ],\n },\n ];\n }\n return match;\n}\n\n/** simple TTL cache with in-flight coalescing — proven shape in maxed */\nclass QueryCache {\n private store = new Map<string, { at: number; value: Promise<unknown> }>();\n constructor(private ttlMs: number, private cap: number) {}\n get<T>(key: string, produce: () => Promise<T>): Promise<T> {\n const hit = this.store.get(key);\n if (hit && Date.now() - hit.at < this.ttlMs) return hit.value as Promise<T>;\n const value = produce();\n // a failed query must not be cached as the answer for ten minutes\n value.catch(() => this.store.delete(key));\n if (this.store.size >= this.cap) {\n const oldest = [...this.store.entries()].sort((a, b) => a[1].at - b[1].at)[0];\n if (oldest) this.store.delete(oldest[0]);\n }\n this.store.set(key, { at: Date.now(), value });\n return value;\n }\n}\n\nexport interface QueryCtx {\n TelemetryModel: Model<any>;\n RollupModel: Model<any>;\n registry: Registry;\n limits?: Partial<QueryLimits>;\n /** called with { op, ms, params } when a read exceeds slowMs (default 500) */\n onSlowQuery?: (info: { op: string; ms: number; params: unknown }) => void;\n slowMs?: number;\n cacheTtlMs?: number;\n cacheSize?: number;\n}\n\nexport function createQueries(ctx: QueryCtx) {\n const limits = { ...DEFAULT_LIMITS, ...ctx.limits };\n const slowMs = ctx.slowMs ?? 500;\n const cache = new QueryCache(ctx.cacheTtlMs ?? 10 * 60_000, ctx.cacheSize ?? 60);\n\n const timed = async <T>(op: string, params: unknown, run: () => Promise<T>): Promise<T> => {\n const t0 = Date.now();\n try {\n return await run();\n } finally {\n const ms = Date.now() - t0;\n if (ms > slowMs) ctx.onSlowQuery?.({ op, ms, params });\n }\n };\n\n return {\n /** cursor-paged raw envelope reads — tables, lists, detail drawers */\n async records(\n scope: string,\n range: TimeRange,\n filter: RecordFilter = {},\n opts: { limit?: number; cursor?: string } = {},\n ) {\n const limit = Math.min(Math.max(1, opts.limit ?? limits.records), limits.records);\n const match = buildMatch(scope, range, filter);\n if (opts.cursor) {\n // keyset on (occurredAt desc, _id desc) — never $skip\n const [atIso, id] = JSON.parse(Buffer.from(opts.cursor, 'base64url').toString());\n const at = new Date(atIso);\n match.$and = [\n ...((match.$and as unknown[]) ?? []),\n { $or: [{ occurredAt: { $lt: at } }, { occurredAt: at, _id: { $lt: id } }] },\n ];\n }\n return timed('records', { scope, filter }, async () => {\n const items = await ctx.TelemetryModel.find(match)\n .sort({ occurredAt: -1, _id: -1 })\n .limit(limit + 1)\n .lean();\n const more = items.length > limit;\n if (more) items.pop();\n const last: any = items[items.length - 1];\n return {\n items,\n nextCursor: more\n ? Buffer.from(JSON.stringify([new Date(last.occurredAt).toISOString(), last._id])).toString('base64url')\n : null,\n dataSource: 'raw' as const,\n };\n });\n },\n\n /** time-series at query time. count extrapolates by 1/sampleRate (§5.3) —\n * exact while rates sit at 1, still honest the day one drops.\n *\n * Under PLATFORM_SCOPE this aggregates ACROSS tenants into one bucket per\n * interval. That is the platform-wide chart, not a bug — the sum of every\n * tenant is the number a platform operator came for. A per-tenant\n * breakdown is a different question; ask it with rollups() or by scoping\n * to a tenant. Same for distribution() below. */\n series(\n scope: string,\n range: TimeRange,\n filter: RecordFilter,\n opts: { measure?: string; interval?: 'hour' | 'day' | 'week' | 'month' } = {},\n ) {\n const { measure = 'count', interval = 'day' } = opts;\n const key = JSON.stringify(['series', scope, range.from, range.to, filter, measure, interval]);\n return cache.get(key, () =>\n timed('series', { scope, filter, measure, interval }, async () => {\n const m = /^(sum|avg):(.+)$/.exec(measure);\n const value = !m\n ? { $sum: { $divide: [1, { $ifNull: ['$sampleRate', 1] }] } }\n : m[1] === 'sum'\n ? { $sum: `$metrics.${m[2]}` }\n : { $avg: `$metrics.${m[2]}` };\n const buckets = await ctx.TelemetryModel.aggregate([\n { $match: buildMatch(scope, range, filter) },\n {\n $group: {\n _id: { $dateTrunc: { date: '$occurredAt', unit: interval, ...(interval === 'week' ? { startOfWeek: 'monday' } : {}) } },\n value,\n },\n },\n { $sort: { _id: 1 } },\n { $limit: limits.series },\n ] as any[]);\n return { buckets: buckets.map((b: any) => ({ at: b._id, value: b.value })), dataSource: 'raw' as const };\n }),\n );\n },\n\n /**\n * Percentiles + histogram off raw. Keep-all makes the SAMPLE complete —\n * no sampling stands between the match and the math (§5.3) — but the\n * computation is not exact and this comment used to claim it was:\n * `$percentile` runs `method: 'approximate'` (t-digest), and the scan stops\n * at `limits.distribution`.\n *\n * So the ceiling is read as cap+1 and `truncated` reports whether it was\n * actually reached, the same way rollups/distinctCount/funnel do. A match\n * wider than the ceiling is an undercount, and an undercount the response\n * does not mention is the silent cap this package refuses everywhere else.\n * Mongo 7+.\n */\n distribution(\n scope: string,\n range: TimeRange,\n filter: RecordFilter,\n opts: { measure?: string } = {},\n ) {\n const measure = opts.measure ?? 'durationMs';\n const path = measure === 'durationMs' ? '$durationMs' : `$metrics.${measure.replace(/^metric:/, '')}`;\n const key = JSON.stringify(['distribution', scope, range.from, range.to, filter, measure]);\n return cache.get(key, () =>\n timed('distribution', { scope, filter, measure }, async () => {\n const match = {\n ...buildMatch(scope, range, filter),\n [path.slice(1)]: { $exists: true },\n };\n // one scan ceiling, shared by both pipelines — and cap+1 so the\n // response can SAY it was truncated instead of quietly undercounting\n const cap = limits.distribution;\n const [summary] = await ctx.TelemetryModel.aggregate([\n { $match: match },\n { $limit: cap + 1 },\n {\n $group: {\n _id: null,\n p: { $percentile: { input: path, p: [0.5, 0.9, 0.95, 0.99], method: 'approximate' } },\n min: { $min: path },\n max: { $max: path },\n avg: { $avg: path },\n n: { $sum: 1 },\n },\n },\n ] as any[]);\n // `truncated` is always present, empty match included — a caller that\n // has to check whether the key exists before trusting it is back to\n // inferring the cap\n if (!summary) return { n: 0, truncated: false, dataSource: 'raw' as const };\n const [p50, p90, p95, p99] = summary.p;\n const histogram = await ctx.TelemetryModel.aggregate([\n { $match: match },\n { $limit: cap + 1 },\n { $bucketAuto: { groupBy: path, buckets: 20 } },\n ] as any[]);\n return {\n p50, p90, p95, p99,\n min: summary.min, max: summary.max, avg: summary.avg, n: summary.n,\n histogram: histogram.map((h: any) => ({ min: h._id.min, max: h._id.max, n: h.count })),\n truncated: summary.n > cap,\n dataSource: 'raw' as const,\n };\n }),\n );\n },\n\n /** rollup family reads — issues, spend, activity, milestones, funnels */\n rollups(\n scope: string,\n params: {\n as: string;\n /**\n * One dimension value, or several as an `$in`. The array form exists\n * because building an index for N subjects otherwise costs N queries or\n * an unfiltered family scan that the cap truncates — plausible, wrong,\n * and silent (cohort-math G3).\n */\n dims?: string | string[];\n subjectType?: string;\n /** the field `range` filters. Default: bucketAt when bucketed, lastAt otherwise. */\n on?: 'firstAt' | 'lastAt' | 'bucketAt';\n /**\n * Half-open (`$gte`/`$lt`), always. Applied to `on`.\n *\n * The default is today's behaviour and deliberately unchanged, but\n * `lastAt` is the MOST RECENT occurrence — cohort selection wants\n * `firstAt`, and on a once-per-subject milestone the two are equal only\n * until something re-emits it (cohort-math G1). `on: 'firstAt'` makes\n * that choice explicit rather than lucky; there is already an index for\n * it ({tenantId, as, subjectType, firstAt}).\n */\n range?: TimeRange;\n sort?: 'count' | 'lastAt' | 'firstAt' | 'bucketAt';\n limit?: number;\n },\n ) {\n const key = JSON.stringify(['rollups', scope, params]);\n return cache.get(key, () =>\n timed('rollups', { scope, params }, async () => {\n // family shape is pinned by validateRegistry, so the first declaration speaks for all\n let bucketed = false;\n outer: for (const [name, s] of Object.entries(ctx.registry)) {\n for (const r of s.rollups ?? []) {\n if ((r.as ?? name) === params.as) {\n bucketed = !!r.bucket;\n break outer;\n }\n }\n }\n // rollup rows carry their own tenantId, so a '*' read stays attributable\n const match: Record<string, any> = {\n ...(isPlatformScope(scope) ? {} : { tenantId: scope }),\n as: params.as,\n };\n if (params.dims) {\n match.dims = Array.isArray(params.dims) ? { $in: params.dims } : params.dims;\n }\n if (params.subjectType) match.subjectType = params.subjectType;\n if (params.range) {\n const on = params.on ?? (bucketed ? 'bucketAt' : 'lastAt');\n match[on] = { $gte: params.range.from, $lt: params.range.to };\n }\n const sortKey = params.sort ?? (bucketed ? 'bucketAt' : 'count');\n const limit = Math.min(Math.max(1, params.limit ?? limits.rollups), limits.rollups);\n // limit+1 so truncation is observed, not inferred from an exact match\n const rows = await ctx.RollupModel.find(match)\n .sort({ [sortKey]: sortKey === 'firstAt' || sortKey === 'bucketAt' ? 1 : -1 })\n .limit(limit + 1)\n .lean();\n const truncated = rows.length > limit;\n if (truncated) rows.pop();\n return { rows, bucketed, truncated, dataSource: 'rollups' as const };\n }),\n );\n },\n\n /** one trace, every kind, one time axis — the first join view */\n trace(scope: string, traceId: string) {\n return timed('trace', { scope, traceId }, async () => {\n const items = await ctx.TelemetryModel.find({\n ...(isPlatformScope(scope) ? {} : { tenantId: scope }),\n traceId,\n })\n .sort({ occurredAt: 1 })\n .limit(limits.trace)\n .lean();\n return { items, dataSource: 'raw' as const };\n });\n },\n\n /** one subject's whole story — records interleaved, milestones as markers */\n journey(scope: string, subjectRef: string, range: TimeRange, opts: { limit?: number } = {}) {\n return timed('journey', { scope, subjectRef }, async () => {\n const limit = Math.min(Math.max(1, opts.limit ?? limits.journey), limits.journey);\n // a subject ref is only unique WITHIN a tenant, so a '*' journey can\n // legitimately braid two tenants' 'user:u_1' together — every row and\n // milestone carries its tenantId, which is what keeps that readable\n const pin = isPlatformScope(scope) ? {} : { tenantId: scope };\n const [records, milestones] = await Promise.all([\n ctx.TelemetryModel.find({\n ...pin,\n subjectKeys: subjectRef,\n occurredAt: { $gte: range.from, $lt: range.to },\n })\n .sort({ occurredAt: -1 })\n .limit(limit)\n .lean(),\n // lifetime families only — bucketed activity rows would drown the markers\n ctx.RollupModel.find({ ...pin, dims: subjectRef, bucketAt: { $exists: false } })\n .sort({ firstAt: 1 })\n .limit(100)\n .lean(),\n ]);\n return { records, milestones, dataSource: 'raw+rollups' as const };\n });\n },\n\n /**\n * Distinct subjects per bucket, and over the whole range — DAU/MAU/WAU,\n * EXACTLY, with no sketch and no write-path change.\n *\n * The trick is that there is no trick. A family declared `by: ['subject']`\n * with a bucket already writes exactly ONE doc per (subject, bucket), which\n * is what the deterministic `_id` guarantees. So distinct-subjects-in-bucket\n * IS the doc count, and distinct-over-a-range is one `$group` on `dims`. An\n * HLL sketch would buy approximation we do not need and storage we would\n * have to maintain.\n *\n * `interval` may be COARSER than the family's own bucket (daily rows →\n * monthly MAU) — re-truncating bucket starts cannot split a bucket across\n * two periods, so the roll-up stays exact. Asking for finer than the family\n * writes cannot invent detail: it returns the family's own grain.\n */\n distinctCount(\n scope: string,\n params: {\n as: string;\n subjectType?: string;\n range: TimeRange;\n interval?: 'hour' | 'day' | 'week' | 'month';\n },\n ) {\n // A family with no subject dim or no bucket cannot answer this, and the\n // failure mode is a confident wrong number rather than an empty result —\n // so it throws, loudly, naming the family and the fix.\n const spec = requireDistinctFamily(ctx.registry, params.as);\n const interval = params.interval ?? spec.bucket!;\n const key = JSON.stringify(['distinctCount', scope, params]);\n return cache.get(key, () =>\n timed('distinctCount', { scope, params }, async () => {\n const match: Record<string, any> = {\n ...(isPlatformScope(scope) ? {} : { tenantId: scope }),\n as: params.as,\n bucketAt: { $gte: params.range.from, $lt: params.range.to },\n };\n if (params.subjectType) match.subjectType = params.subjectType;\n const cap = limits.distinct;\n const [out] = await ctx.RollupModel.aggregate([\n { $match: match },\n // one scan ceiling, shared by both branches — and cap+1 so the\n // response can SAY it was truncated instead of quietly undercounting\n { $limit: cap + 1 },\n {\n $facet: {\n buckets: [\n {\n $group: {\n _id: {\n at: { $dateTrunc: { date: '$bucketAt', unit: interval, ...(interval === 'week' ? { startOfWeek: 'monday' } : {}) } },\n dims: '$dims',\n },\n },\n },\n { $group: { _id: '$_id.at', value: { $sum: 1 } } },\n { $sort: { _id: 1 } },\n { $limit: limits.series },\n ],\n distinct: [{ $group: { _id: '$dims' } }, { $count: 'n' }],\n scanned: [{ $count: 'n' }],\n },\n },\n ] as any[]);\n const scanned = out?.scanned?.[0]?.n ?? 0;\n return {\n buckets: (out?.buckets ?? []).map((b: any) => ({ at: b._id, value: b.value })),\n /** distinct subjects across the WHOLE range — never the sum of the buckets */\n distinct: out?.distinct?.[0]?.n ?? 0,\n interval,\n truncated: scanned > cap,\n dataSource: 'rollups' as const,\n };\n }),\n );\n },\n\n /**\n * Cohort funnel over lifetime milestone families — stage counts, conversion,\n * and median time-to-step. See funnel.ts; the math lives there so it can be\n * unit-pinned without a database.\n */\n funnel(scope: string, params: FunnelParams): Promise<FunnelResult> {\n return timed('funnel', { scope, params }, () =>\n runFunnel(\n {\n RollupModel: ctx.RollupModel,\n registry: ctx.registry,\n cohortCap: limits.funnel,\n scopeMatch: (s) => (isPlatformScope(s) ? {} : { tenantId: s }),\n },\n scope,\n params,\n ),\n );\n },\n };\n}\n\n/**\n * distinctCount's precondition. The exactness argument rests entirely on the\n * family writing one doc per (subject, bucket), so each way of breaking that\n * gets its own message with its own fix — a registry mistake here would\n * otherwise surface as a number that looks like DAU and is not.\n */\nfunction requireDistinctFamily(registry: Registry, as: string): RollupSpec {\n const found = findFamily(registry, as);\n if (!found) {\n throw new Error(\n `telemetry: distinctCount() — no rollup family \"${as}\" is declared. Add ` +\n `\\`rollups: [{ as: '${as}', by: ['subject'], subjects: [...], bucket: 'day' }]\\` to the events that count as activity.`,\n );\n }\n const { name, spec } = found;\n const by = `by: [${spec.by.map((d) => `'${d}'`).join(', ')}]`;\n if (!spec.bucket) {\n throw new Error(\n `telemetry: distinctCount() — rollup family \"${as}\" (declared on \"${name}\") has no \\`bucket\\`. ` +\n `Distinct-per-period needs one doc per (subject, period); a lifetime family has one doc per subject ` +\n `forever, so every period would report the same number. Add \\`bucket: 'day'\\`, or ask this with rollups().`,\n );\n }\n if (!spec.by.includes('subject')) {\n throw new Error(\n `telemetry: distinctCount() — rollup family \"${as}\" (declared on \"${name}\") is keyed ${by} with no ` +\n `\\`subject\\` dim, so its docs count OCCURRENCES, not subjects. Add 'subject' to \\`by\\` (with \\`subjects: [...]\\`).`,\n );\n }\n if (spec.by.length !== 1) {\n throw new Error(\n `telemetry: distinctCount() — rollup family \"${as}\" (declared on \"${name}\") is keyed ${by}. Extra dims ` +\n `split one subject across several docs per period, so the count would exceed the true distinct total. ` +\n `Declare a second family with \\`by: ['subject']\\` for the distinct question.`,\n );\n }\n return spec;\n}\n\nexport type Queries = ReturnType<typeof createQueries>;\n","import { Schema, type Connection, type Model } from 'mongoose';\nimport type { Registry } from './registry.js';\nimport { newId } from './types.js';\n\n/**\n * Views — one shape, three producers (dashboards §3). A view is nothing but\n * named query state; derived views come from the registry for free, configured\n * ones ship in host code, saved ones live in `<collection>_views`. Name\n * collisions shadow saved → configured → derived, so a user can override a\n * default without editing anything.\n *\n * Tenancy: views scope on the LITERAL viewer scope, including PLATFORM_SCOPE.\n * '*' is an escape hatch for reading telemetry, not a master key to other\n * people's saved state, so it does NOT fan out here the way it does in the\n * query primitives. A platform viewer's views live in their own '*' namespace:\n * invisible to every tenant, and every tenant's views invisible to them. Both\n * directions fall out of the same literal match — in resolveViews below and in\n * the router's delete/ownership lookup, which must stay literal for the same\n * reason (a platform admin's `role: 'admin'` is admin OF the platform scope).\n *\n * Erasure across that boundary is opt-in. forget(tenantId, ref) is\n * tenant-scoped and '*' is not a tenant, so a person's PLATFORM-scoped views\n * are missed by default — set `globalSubjectRefs` on createTelemetry() and\n * forget() reaches them too. The flag is the host asserting that a ref names\n * one party globally; without that, `user:u_1` is a different person in every\n * tenant and one tenant's erasure would delete another's views. Bounded by the\n * named ref either way — never by the tenant, which is the escape hatch this\n * namespace exists to deny.\n */\n\nexport interface ViewSpec {\n name: string;\n icon?: string;\n page: 'errors' | 'traces' | 'events' | 'journeys' | 'usage' | 'overview' | 'system';\n query: {\n range?: string;\n filters?: Record<string, unknown>;\n groupBy?: string;\n sort?: string;\n display?: 'table' | 'series' | 'breakdown' | 'stream';\n };\n}\n\nexport function buildViewModel(connection: Connection, modelName: string, collection: string): Model<any> {\n const existing = connection.models?.[modelName];\n if (existing) return existing;\n const schema = new Schema(\n {\n _id: { type: String, required: true },\n tenantId: { type: String, required: true },\n /** a person — forget() deletes private views, redacts this on shared ones */\n ownerRef: String,\n shared: { type: Boolean, default: false },\n spec: { type: Schema.Types.Mixed, required: true },\n createdAt: { type: Date, required: true },\n },\n { collection, versionKey: false },\n );\n schema.index({ tenantId: 1, shared: 1 });\n schema.index({ tenantId: 1, ownerRef: 1 });\n return connection.model(modelName, schema);\n}\n\nconst KIND_PAGE: Record<string, ViewSpec['page']> = {\n error: 'errors',\n span: 'traces',\n event: 'events',\n state: 'journeys',\n usage: 'usage',\n};\n\n/**\n * Derived views — generated from the registry at load, zero config. One per\n * event name (a pre-filtered page), one per rollup family (a RollupExplorer\n * preset). They exist because the registry already knows enough to write them.\n */\nexport function deriveViews(registry: Registry): Array<ViewSpec & { origin: 'derived' }> {\n const views: Array<ViewSpec & { origin: 'derived' }> = [];\n const families = new Set<string>();\n for (const [name, spec] of Object.entries(registry)) {\n views.push({\n origin: 'derived',\n name,\n page: KIND_PAGE[spec.kind] ?? 'events',\n query: { range: '7d', filters: { name }, display: spec.kind === 'event' ? 'series' : 'table' },\n });\n for (const r of spec.rollups ?? []) families.add(r.as ?? name);\n }\n for (const as of families) {\n views.push({\n origin: 'derived',\n name: `rollup: ${as}`,\n page: 'journeys',\n query: { range: '30d', filters: { rollup: as }, display: 'breakdown' },\n });\n }\n return views;\n}\n\nexport interface ResolvedView extends ViewSpec {\n origin: 'derived' | 'configured' | 'saved';\n id?: string;\n ownerRef?: string;\n shared?: boolean;\n}\n\n/** merge the three producers; name collisions resolve saved → configured → derived */\nexport async function resolveViews(opts: {\n ViewModel: Model<any>;\n registry: Registry;\n configured: ViewSpec[];\n /** the viewer's scope — a tenantId, or PLATFORM_SCOPE. Matched literally. */\n tenantId: string;\n viewerRef?: string;\n}): Promise<ResolvedView[]> {\n const byName = new Map<string, ResolvedView>();\n for (const v of deriveViews(opts.registry)) byName.set(v.name, v);\n for (const v of opts.configured) byName.set(v.name, { ...v, origin: 'configured' });\n const saved = await opts.ViewModel.find({\n tenantId: opts.tenantId,\n $or: [{ shared: true }, ...(opts.viewerRef ? [{ ownerRef: opts.viewerRef }] : [])],\n })\n .sort({ createdAt: 1 })\n .limit(200)\n .lean();\n for (const doc of saved as any[]) {\n byName.set(doc.spec.name, {\n ...doc.spec,\n origin: 'saved',\n id: doc._id,\n ownerRef: doc.ownerRef,\n shared: doc.shared,\n });\n }\n return [...byName.values()];\n}\n\nexport async function saveView(opts: {\n ViewModel: Model<any>;\n tenantId: string;\n viewerRef?: string;\n spec: ViewSpec;\n shared: boolean;\n}): Promise<{ id: string }> {\n // a viewer without an identity cannot own a private view — share or nothing\n if (!opts.shared && !opts.viewerRef) {\n throw Object.assign(new Error('private views need a viewer identity'), { status: 400 });\n }\n const id = newId();\n await opts.ViewModel.create({\n _id: id,\n tenantId: opts.tenantId,\n ownerRef: opts.viewerRef,\n shared: opts.shared,\n spec: opts.spec,\n createdAt: new Date(),\n });\n return { id };\n}\n","import { z } from 'zod';\nimport { createQueries, type QueryLimits, type RecordFilter, type TimeRange } from './query.js';\nimport { buildViewModel, resolveViews, type ResolvedView, type ViewSpec } from './views.js';\nimport { isPlatformScope } from './types.js';\nimport type { Telemetry } from './index.js';\n// type-only — never pulls express/mongoose into the mcp bundle\nimport type { SubjectAdapter, Viewer } from './dashboard.js';\n\n/**\n * MCP tools — a suite of read-only tool descriptors over the same query\n * primitives the dashboard serves (dashboards §2), packaged so a host with an\n * existing MCP server can hand an agent telemetry access without writing query\n * code. See plans/mcp-tools.md.\n *\n * This is NOT a server: no transport, no MCP SDK runtime dependency. The\n * factory returns descriptors; the host maps them onto whatever server it runs.\n * The one seam to the official SDK lives in `./mcp-sdk.ts`, behind a structural\n * type, so the core here imports nothing framework-specific.\n *\n * The auth boundary is the dashboard's, reused: a `viewerAdapter.resolveViewer`\n * runs on EVERY tool call — the scope is never an argument, and an agent is the\n * exact confused-deputy this closes. The one scope-adjacent argument, `tenant`,\n * can only NARROW inside an already-granted platform scope (§ pickScope).\n */\n\n// ── the adapter (inbound; mirrors ViewerAdapter, ctx-generic) ────────────────\n\nexport interface McpViewerAdapter {\n /**\n * Resolve the agent's session/request context to a Viewer, exactly as the\n * dashboard resolves an express Request. `ctx` is whatever the host's MCP\n * server hands the tool — this package forwards it untouched and inspects\n * nothing but the returned Viewer. Return `'*'` (PLATFORM_SCOPE) only for a\n * viewer you have authorized to read across tenants.\n */\n resolveViewer(ctx: unknown): Viewer | null | Promise<Viewer | null>;\n}\n\n// ── the descriptor (outbound; what the host registers) ───────────────────────\n\nexport interface ToolDescriptor<A = any> {\n /** the MCP tool name — snake_case, stable */\n name: string;\n /** short human title */\n title: string;\n /** written for an LLM: what it answers, when to reach for it */\n description: string;\n /** zod object; args ONLY — never a scope/tenant-widening field */\n inputSchema: z.ZodObject<any>;\n /** returns JSON-serialisable data; the SDK seam wraps it as MCP content */\n handler: (args: A, ctx: unknown) => Promise<unknown>;\n}\n\nexport interface CreateTelemetryMcpOptions {\n /** the createTelemetry() handle — models + registry. Nothing new is built. */\n telemetry: Telemetry;\n /** REQUIRED. An unauthenticated telemetry tool is a data leak an agent will find. */\n viewerAdapter: McpViewerAdapter;\n /** optional; labels subject refs in journey/error results (user:u_1 → Ada). */\n subjectAdapter?: SubjectAdapter;\n /** host-configured reports, merged into list_reports (mirrors the dashboard). */\n configured?: ViewSpec[];\n /** forwarded to createQueries. */\n limits?: Partial<QueryLimits>;\n /**\n * Redact raw envelopes before they leave a tool. Default strips the `data`\n * payload — records carry declared bodies (PII). Pass a custom function to\n * opt back in, or `false` to disable (you accept the exposure).\n */\n redact?: ((record: any) => any) | false;\n}\n\n// ── shared arg fragments ─────────────────────────────────────────────────────\n\nconst tenantArg = {\n tenant: z\n .string()\n .optional()\n .describe(\n \"platform operators only: restrict the read to one tenant id. Permitted ONLY when your viewer is scoped to '*'; a hard error otherwise.\",\n ),\n};\n\nconst rangeArg = {\n from: z.string().optional().describe('ISO 8601 start, inclusive. Default: 7 days before `to`.'),\n to: z.string().optional().describe('ISO 8601 end, exclusive. Default: now.'),\n};\n\nconst filterArg = {\n kind: z.enum(['event', 'error', 'span', 'state', 'usage']).optional(),\n name: z.string().optional().describe('exact event name, e.g. \"user.signed_up\" — see describe_telemetry'),\n severity: z.string().optional(),\n env: z.string().optional().describe('prod | staging | dev'),\n service: z.string().optional(),\n release: z.string().optional(),\n subject: z.string().optional().describe('pin to one subject, e.g. \"user:u_1\"'),\n traceId: z.string().optional(),\n attrs: z.record(z.string(), z.string()).optional().describe('equality on declared indexed attrs'),\n excludeActorTypes: z\n .array(z.string())\n .optional()\n .describe('drop typed actors, e.g. [\"admin\",\"system\"] for a customer-only view'),\n};\n\ntype FilterArgs = {\n kind?: string;\n name?: string;\n severity?: string;\n env?: string;\n service?: string;\n release?: string;\n subject?: string;\n traceId?: string;\n attrs?: Record<string, string>;\n excludeActorTypes?: string[];\n};\n\nfunction toFilter(a: FilterArgs): RecordFilter {\n const f: RecordFilter = {};\n for (const k of ['kind', 'name', 'severity', 'env', 'service', 'release', 'subject', 'traceId'] as const) {\n if (a[k] != null) (f as any)[k] = a[k];\n }\n if (a.attrs) f.attrs = a.attrs;\n if (a.excludeActorTypes?.length) f.excludeActorTypes = a.excludeActorTypes;\n return f;\n}\n\nfunction parseRange(from?: string, to?: string): TimeRange {\n const toD = to ? new Date(to) : new Date();\n const fromD = from ? new Date(from) : new Date(toD.getTime() - 7 * 864e5);\n if (Number.isNaN(fromD.getTime()) || Number.isNaN(toD.getTime()) || fromD >= toD) {\n throw new Error('invalid time range: `from` must be a valid ISO time strictly before `to`');\n }\n return { from: fromD, to: toD };\n}\n\nconst INTERVAL = z.enum(['hour', 'day', 'week', 'month']);\n\n// ── the factory ───────────────────────────────────────────────────────────────\n\nexport function createTelemetryMcp(opts: CreateTelemetryMcpOptions): ToolDescriptor[] {\n const { telemetry: t, viewerAdapter, subjectAdapter, configured = [] } = opts;\n if (!viewerAdapter?.resolveViewer) {\n throw new Error(\n 'telemetry: createTelemetryMcp requires a viewerAdapter — an unauthenticated telemetry tool is a data leak an agent will find',\n );\n }\n\n const redact =\n opts.redact === false\n ? (r: any) => r\n : opts.redact ??\n ((r: any) => {\n // records carry a declared `data` payload — bodies, PII. Strip by default.\n if (!r || typeof r !== 'object' || r.data === undefined) return r;\n const { data, ...rest } = r;\n return { ...rest, data: '[redacted]' };\n });\n const redactAll = (items: any[]) => items.map(redact);\n\n // queries built HERE, behind the auth boundary — same as createDashboard\n const q = createQueries({\n TelemetryModel: t.models.telemetry,\n RollupModel: t.models.rollups,\n registry: t.registry,\n limits: opts.limits,\n });\n\n const ViewModel = buildViewModel(\n t.models.telemetry.db! as any,\n `${t.models.telemetry.modelName}View`,\n `${t.models.telemetry.collection.collectionName}_views`,\n );\n\n // resolve the viewer, then choose the scope. `tenant` may only NARROW inside\n // a platform grant — never widen. A mismatch is a hard error, not a silent\n // fallback: an agent that asked for tenant X must not be handed its own.\n async function resolve(ctx: unknown): Promise<Viewer> {\n const viewer = await viewerAdapter.resolveViewer(ctx);\n if (!viewer?.tenantId) throw new Error('unauthorized: viewerAdapter returned no viewer');\n return viewer;\n }\n function pickScope(viewer: Viewer, tenant?: string): string {\n if (tenant == null || tenant === '') return viewer.tenantId;\n if (!isPlatformScope(viewer.tenantId)) {\n throw new Error(\"the `tenant` argument is permitted only for platform-scope ('*') viewers\");\n }\n return tenant;\n }\n\n // pretty-label the subject refs found across a batch of records\n async function labelSubjects(items: any[]): Promise<Record<string, { label: string; href?: string }> | undefined> {\n if (!subjectAdapter) return undefined;\n const refs = new Set<string>();\n for (const it of items) for (const r of it?.subjectKeys ?? []) refs.add(r);\n if (!refs.size) return {};\n return subjectAdapter.describe([...refs].slice(0, 100));\n }\n\n const tool = <A>(d: ToolDescriptor<A>): ToolDescriptor<A> => d;\n\n const tools: ToolDescriptor[] = [\n // ── vocabulary ──────────────────────────────────────────────────────────\n tool({\n name: 'describe_telemetry',\n title: 'Describe telemetry schema',\n description:\n 'The vocabulary of this telemetry instance: every event name with its kind, declared attributes, metrics, indexed filters, and rollup families. CALL THIS FIRST — every other tool speaks the names it returns.',\n inputSchema: z.object({}),\n async handler(_args, ctx) {\n await resolve(ctx); // gate even the schema — an unauthorized agent learns nothing\n return { registry: registryProjection(t), kinds: ['event', 'error', 'span', 'state', 'usage'] };\n },\n }),\n\n // ── events ──────────────────────────────────────────────────────────────\n tool({\n name: 'search_events',\n title: 'Search events',\n description:\n 'Raw telemetry records matching a filter, newest first, cursor-paged. The general list/table/tail tool across every kind. `data` payloads are redacted by default.',\n inputSchema: z.object({\n ...filterArg,\n ...rangeArg,\n ...tenantArg,\n limit: z.number().int().positive().optional(),\n cursor: z.string().optional().describe('opaque nextCursor from a previous call'),\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n const res = await q.records(scope, parseRange(a.from, a.to), toFilter(a), {\n limit: a.limit,\n cursor: a.cursor,\n });\n return { ...res, items: redactAll(res.items as any[]), subjects: await labelSubjects(res.items as any[]) };\n },\n }),\n\n tool({\n name: 'list_errors',\n title: 'List errors',\n description:\n 'Recent error records, newest first — the focused feed for \"what is breaking?\". A convenience over search_events with kind pinned to \"error\"; still accepts name/service/env/severity filters.',\n inputSchema: z.object({\n name: z.string().optional(),\n severity: z.string().optional(),\n service: z.string().optional(),\n env: z.string().optional(),\n release: z.string().optional(),\n subject: z.string().optional(),\n excludeActorTypes: filterArg.excludeActorTypes,\n ...rangeArg,\n ...tenantArg,\n limit: z.number().int().positive().optional(),\n cursor: z.string().optional(),\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n const res = await q.records(scope, parseRange(a.from, a.to), { ...toFilter(a), kind: 'error' }, {\n limit: a.limit,\n cursor: a.cursor,\n });\n return { ...res, items: redactAll(res.items as any[]), subjects: await labelSubjects(res.items as any[]) };\n },\n }),\n\n tool({\n name: 'event_trends',\n title: 'Event trends over time',\n description:\n 'A time series of a measure, bucketed by interval — counts by default, or sum:/avg: of a declared metric (e.g. \"avg:durationMs\"). Use for \"is this rising?\" questions.',\n inputSchema: z.object({\n ...filterArg,\n ...rangeArg,\n ...tenantArg,\n measure: z\n .string()\n .optional()\n .describe('\"count\" (default), or \"sum:<metric>\" / \"avg:<metric>\", e.g. \"sum:tokens\"'),\n interval: INTERVAL.optional(),\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n return q.series(scope, parseRange(a.from, a.to), toFilter(a), {\n measure: a.measure,\n interval: a.interval,\n });\n },\n }),\n\n tool({\n name: 'metric_distribution',\n title: 'Metric distribution (percentiles)',\n description:\n 'Percentiles (p50/p90/p95/p99), min/max/avg, and a 20-bucket histogram of a numeric metric over the matched records — e.g. \"what is p95 latency for the checkout span?\". Defaults to durationMs.',\n inputSchema: z.object({\n ...filterArg,\n ...rangeArg,\n ...tenantArg,\n measure: z\n .string()\n .optional()\n .describe('\"durationMs\" (default) or a declared metric key, e.g. \"metric:tokens\"'),\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n return q.distribution(scope, parseRange(a.from, a.to), toFilter(a), { measure: a.measure });\n },\n }),\n\n // ── aggregates ────────────────────────────────────────────────────────────\n tool({\n name: 'rollup_breakdown',\n title: 'Rollup breakdown',\n description:\n 'Top rows of a pre-aggregated rollup family — top issues, top spenders, most-active accounts, whatever the registry declares. Name the family (`as`) from describe_telemetry; optionally slice by dimension values.',\n inputSchema: z.object({\n as: z.string().describe('the rollup family, e.g. \"spend_by_account\" — see describe_telemetry'),\n dims: z.array(z.string()).optional().describe('restrict to these dimension values'),\n subjectType: z.string().optional(),\n on: z.enum(['firstAt', 'lastAt', 'bucketAt']).optional(),\n sort: z.enum(['count', 'lastAt', 'firstAt', 'bucketAt']).optional(),\n ...rangeArg,\n ...tenantArg,\n limit: z.number().int().positive().optional(),\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n return q.rollups(scope, {\n as: a.as,\n dims: a.dims,\n subjectType: a.subjectType,\n on: a.on,\n sort: a.sort,\n range: a.from || a.to ? parseRange(a.from, a.to) : undefined,\n limit: a.limit,\n });\n },\n }),\n\n tool({\n name: 'active_users',\n title: 'Active users (DAU/WAU/MAU)',\n description:\n 'Exact distinct-subject counts per interval and across the whole range — daily/weekly/monthly actives — from a bucketed subject rollup family. Errors if the named family has no subject dimension or bucket.',\n inputSchema: z.object({\n as: z.string().describe('a bucketed subject family, e.g. \"active_accounts\"'),\n subjectType: z.string().optional(),\n interval: INTERVAL.optional(),\n ...rangeArg,\n ...tenantArg,\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n return q.distinctCount(scope, {\n as: a.as,\n subjectType: a.subjectType,\n interval: a.interval,\n range: parseRange(a.from, a.to),\n });\n },\n }),\n\n tool({\n name: 'funnel_analysis',\n title: 'Cohort funnel',\n description:\n 'A cohort funnel over lifetime milestone families: stage counts, conversion %, drop-off, and median time-to-step. Pass ordered stage families; the cohort window is the time range.',\n inputSchema: z.object({\n stages: z\n .array(z.string())\n .min(1)\n .describe('ordered rollup families, e.g. [\"signed_up\",\"activated\",\"converted\"]'),\n anchor: z.string().optional().describe('family assigning cohort membership. Default: stages[0].'),\n exits: z.array(z.string()).optional().describe('families counted but not staged'),\n subjectType: z.string().optional(),\n interval: z.enum(['day', 'week', 'month']).optional().describe('also slice the cohort by anchor date'),\n endInclusive: z.boolean().optional(),\n ...rangeArg,\n ...tenantArg,\n limit: z.number().int().positive().optional(),\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n const range = parseRange(a.from, a.to);\n return q.funnel(scope, {\n stages: (a.stages as string[]).map((as) => ({ as })),\n anchor: a.anchor,\n exits: (a.exits as string[] | undefined)?.map((as) => ({ as })),\n subjectType: a.subjectType,\n interval: a.interval,\n cohort: { ...range, endInclusive: a.endInclusive === true },\n limit: a.limit,\n });\n },\n }),\n\n // ── deep-dive ─────────────────────────────────────────────────────────────\n tool({\n name: 'inspect_trace',\n title: 'Inspect a trace',\n description:\n 'Every record sharing one traceId, on a single time axis — the correlated view of one request across services. Get a traceId from search_events or list_errors.',\n inputSchema: z.object({ traceId: z.string(), ...tenantArg }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n const res = await q.trace(scope, a.traceId);\n return { ...res, items: redactAll(res.items as any[]) };\n },\n }),\n\n tool({\n name: 'user_journey',\n title: 'User journey',\n description:\n \"One subject's whole story over a range — records interleaved with lifetime milestones. Pass a subject ref like \\\"user:u_1\\\".\",\n inputSchema: z.object({\n subject: z.string().describe('subject ref, e.g. \"user:u_1\"'),\n ...rangeArg,\n ...tenantArg,\n limit: z.number().int().positive().optional(),\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n const res = await q.journey(scope, a.subject, parseRange(a.from, a.to), { limit: a.limit });\n return {\n ...res,\n records: redactAll(res.records as any[]),\n subjects: await labelSubjects(res.records as any[]),\n };\n },\n }),\n\n // ── reports ───────────────────────────────────────────────────────────────\n tool({\n name: 'list_reports',\n title: 'List saved reports',\n description:\n 'The menu of reports available in this instance: saved (built by people), configured (wired by the host), and registry-derived. Each has a name and a stored query you can run with run_report.',\n inputSchema: z.object({ ...tenantArg }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n const views = await resolveViews({\n ViewModel,\n registry: t.registry,\n configured,\n tenantId: scope,\n viewerRef: viewer.viewerRef,\n });\n return { reports: views.map(reportSummary) };\n },\n }),\n\n tool({\n name: 'run_report',\n title: 'Run a saved report',\n description:\n 'Execute one named report from list_reports and return its result. Read-only: the report is a stored filter/range that dispatches to the same query the dashboard would run.',\n inputSchema: z.object({\n name: z.string().describe('the report name from list_reports'),\n ...rangeArg,\n ...tenantArg,\n }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n const views = await resolveViews({\n ViewModel,\n registry: t.registry,\n configured,\n tenantId: scope,\n viewerRef: viewer.viewerRef,\n });\n const view = views.find((v) => v.name === a.name);\n if (!view) throw new Error(`no report named \"${a.name}\" — call list_reports for the menu`);\n return runReport(q, scope, view, a.from, a.to, redactAll);\n },\n }),\n\n // ── platform ────────────────────────────────────────────────────────────\n tool({\n name: 'list_tenants',\n title: 'List tenants',\n description:\n 'The tenant roster with recent activity — tenants that emitted anything in the range, each with a rollup-doc count, family count, event total, and last-activity time. Cross-tenant under a platform (\"*\") viewer; a single row otherwise.',\n inputSchema: z.object({ ...rangeArg, limit: z.number().int().positive().optional() }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const { from, to } = parseRange(a.from, a.to);\n const limit = Math.min(Math.max(1, a.limit ?? 200), 1000);\n const match: Record<string, unknown> = { lastAt: { $gte: from, $lt: to } };\n if (!isPlatformScope(viewer.tenantId)) match.tenantId = viewer.tenantId;\n const rows = await t.models.rollups.aggregate([\n { $match: match },\n {\n $group: {\n _id: '$tenantId',\n rollupDocs: { $sum: 1 },\n families: { $addToSet: '$as' },\n events: { $sum: '$count' },\n lastActivity: { $max: '$lastAt' },\n },\n },\n { $sort: { lastActivity: -1 } },\n { $limit: limit + 1 },\n ] as any[]);\n const truncated = rows.length > limit;\n if (truncated) rows.pop();\n return {\n tenants: rows.map((r: any) => ({\n tenantId: r._id,\n rollupDocs: r.rollupDocs,\n families: r.families.length,\n events: r.events,\n lastActivity: r.lastActivity,\n })),\n truncated,\n dataSource: 'rollups' as const,\n };\n },\n }),\n\n // ── ops ─────────────────────────────────────────────────────────────────\n tool({\n name: 'telemetry_health',\n title: 'Telemetry health',\n description:\n 'The health of the telemetry pipeline itself: drop/default/cap counters, quarantined failed writes, and the index budget. Answers \"are we silently dropping events?\".',\n inputSchema: z.object({ ...tenantArg }),\n async handler(a: any, ctx) {\n const viewer = await resolve(ctx);\n const scope = pickScope(viewer, a.tenant);\n const quarantine = await t.collections\n .rejects()\n .find(isPlatformScope(scope) ? {} : { 'raw.tenantId': scope }, { sort: { at: -1 }, limit: 50 } as any)\n .toArray()\n .catch(() => []);\n const indexes = await t.models.telemetry.collection.indexes().catch(() => []);\n return { counters: t.counters, quarantine, indexCount: indexes.length };\n },\n }),\n ];\n\n return tools;\n}\n\n/** re-export so non-SDK hosts can derive JSON Schema (zod v4 native) */\nexport const toJsonSchema = (tool: ToolDescriptor) => z.toJSONSchema(tool.inputSchema);\n\n// ── helpers ───────────────────────────────────────────────────────────────────\n\n/** the registry as a plain, LLM-legible projection — keys, not zod objects */\nfunction registryProjection(t: Telemetry) {\n return Object.fromEntries(\n Object.entries(t.registry).map(([name, spec]) => [\n name,\n {\n kind: spec.kind,\n description: spec.description,\n attrKeys: spec.attrs ? Object.keys(spec.attrs.shape) : [],\n metricKeys: spec.metrics ? Object.keys(spec.metrics.shape) : [],\n indexedAttrs: spec.indexedAttrs ?? [],\n indexedMetrics: spec.indexedMetrics ?? [],\n rollups: (spec.rollups ?? []).map((r) => ({\n as: r.as ?? name,\n by: r.by,\n bucket: r.bucket ?? null,\n })),\n },\n ]),\n );\n}\n\nfunction reportSummary(v: ResolvedView) {\n return {\n name: v.name,\n origin: v.origin,\n page: v.page,\n shared: v.shared,\n display: (v.query as any)?.display,\n };\n}\n\n/**\n * Dispatch a saved view's stored query to the matching primitive. A view is a\n * `{ range, filters, display }` spec (dashboards §11.6); this reads it the way\n * the SPA would, so run_report invents no query of its own. Unknown shapes\n * return the resolved spec rather than guessing.\n */\nasync function runReport(\n q: ReturnType<typeof createQueries>,\n scope: string,\n view: ResolvedView,\n from: string | undefined,\n to: string | undefined,\n redactAll: (items: any[]) => any[],\n): Promise<unknown> {\n const query = (view.query ?? {}) as any;\n const filters = (query.filters ?? {}) as FilterArgs & { rollup?: string };\n // an explicit tool range wins; else fall back to the view's own textual range\n const range = from || to ? parseRange(from, to) : rangeFromView(query.range);\n\n if (filters.rollup) {\n return { report: view.name, result: await q.rollups(scope, { as: filters.rollup, range }) };\n }\n if (query.display === 'series') {\n return { report: view.name, result: await q.series(scope, range, toFilter(filters)) };\n }\n const res = await q.records(scope, range, toFilter(filters), { limit: 200 });\n return { report: view.name, result: { ...res, items: redactAll(res.items as any[]) } };\n}\n\n/** '7d' / '30d' / '24h' → a half-open range ending now. Default 7 days. */\nfunction rangeFromView(range: unknown): TimeRange {\n const to = new Date();\n const m = /^(\\d+)([dh])$/.exec(String(range ?? '7d'));\n const n = m ? Number(m[1]) : 7;\n const unit = m?.[2] === 'h' ? 36e5 : 864e5;\n return { from: new Date(to.getTime() - n * unit), to };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffjassky/telemetry",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Unified telemetry — product events, errors, traces, state transitions, and billable usage in one Mongo envelope, with typed SDKs and a mountable dashboard.",
5
5
  "license": "MIT",
6
6
  "author": "Jeff Jassky <jeff@jeffjassky.com>",
@@ -59,6 +59,16 @@
59
59
  "import": "./dist/cli.js",
60
60
  "require": "./dist/cli.cjs"
61
61
  },
62
+ "./mcp": {
63
+ "types": "./types/mcp.d.ts",
64
+ "import": "./dist/mcp.js",
65
+ "require": "./dist/mcp.cjs"
66
+ },
67
+ "./mcp/sdk": {
68
+ "types": "./types/mcp-sdk.d.ts",
69
+ "import": "./dist/mcp-sdk.js",
70
+ "require": "./dist/mcp-sdk.cjs"
71
+ },
62
72
  "./package.json": "./package.json"
63
73
  },
64
74
  "files": [
@@ -0,0 +1,17 @@
1
+ import type { ToolDescriptor } from './mcp.js';
2
+
3
+ /**
4
+ * The seam to the official MCP SDK. Structural — no dependency on
5
+ * `@modelcontextprotocol/sdk`; a real `McpServer` satisfies `McpServerLike`.
6
+ */
7
+
8
+ export interface McpServerLike {
9
+ registerTool(
10
+ name: string,
11
+ config: { title?: string; description?: string; inputSchema?: Record<string, unknown> },
12
+ handler: (args: any, extra: unknown) => unknown | Promise<unknown>,
13
+ ): unknown;
14
+ }
15
+
16
+ /** register every descriptor on an MCP (or MCP-shaped) server */
17
+ export declare function registerTelemetryTools(server: McpServerLike, tools: ToolDescriptor[]): void;
package/types/mcp.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ import type { z } from 'zod';
2
+ import type {
3
+ QueryLimits,
4
+ SubjectAdapter,
5
+ Telemetry,
6
+ Viewer,
7
+ ViewSpec,
8
+ } from './index.js';
9
+
10
+ /**
11
+ * MCP tools — read-only tool descriptors over the query primitives, for a host
12
+ * that wants to hand an agent telemetry access through its own MCP server. Not
13
+ * a server: the factory returns descriptors; the `./mcp/sdk` seam registers
14
+ * them. See plans/mcp-tools.md.
15
+ */
16
+
17
+ /** inbound adapter — resolves the agent's session context to a Viewer per call */
18
+ export interface McpViewerAdapter {
19
+ resolveViewer(ctx: unknown): Viewer | null | Promise<Viewer | null>;
20
+ }
21
+
22
+ /** one registrable tool: name, LLM-facing description, zod arg schema, handler */
23
+ export interface ToolDescriptor<A = any> {
24
+ name: string;
25
+ title: string;
26
+ description: string;
27
+ inputSchema: z.ZodObject<any>;
28
+ handler: (args: A, ctx: unknown) => Promise<unknown>;
29
+ }
30
+
31
+ export interface CreateTelemetryMcpOptions {
32
+ telemetry: Telemetry;
33
+ viewerAdapter: McpViewerAdapter;
34
+ subjectAdapter?: SubjectAdapter;
35
+ configured?: ViewSpec[];
36
+ limits?: Partial<QueryLimits>;
37
+ redact?: ((record: any) => any) | false;
38
+ }
39
+
40
+ /** the full read-only suite — all tools or none; there is no allowlist */
41
+ export declare function createTelemetryMcp(opts: CreateTelemetryMcpOptions): ToolDescriptor[];
42
+
43
+ /** derive a JSON Schema from a descriptor's zod input (zod v4 native) */
44
+ export declare const toJsonSchema: (tool: ToolDescriptor) => ReturnType<typeof z.toJSONSchema>;
package/types/test-d.ts CHANGED
@@ -476,6 +476,35 @@ const spa: string = defaultSpaDir();
476
476
  const derived: ResolvedView[] = deriveViews(registry);
477
477
  const caps: QueryLimits = DEFAULT_LIMITS;
478
478
 
479
+ // ── /mcp (types/mcp.d.ts) + /mcp/sdk (types/mcp-sdk.d.ts) ──
480
+ // The tool suite reuses the dashboard's Viewer/SubjectAdapter, ctx-generic.
481
+ import type { CreateTelemetryMcpOptions, McpViewerAdapter, ToolDescriptor } from './mcp.js';
482
+ import { createTelemetryMcp, toJsonSchema } from './mcp.js';
483
+ import type { McpServerLike } from './mcp-sdk.js';
484
+ import { registerTelemetryTools } from './mcp-sdk.js';
485
+
486
+ const mcpViewer: McpViewerAdapter = {
487
+ // ctx is opaque — the host's MCP request, forwarded, never inspected here
488
+ resolveViewer: (ctx) => (isPlatformScope(String(ctx)) ? platformViewer : viewer),
489
+ };
490
+ const mcpOpts: CreateTelemetryMcpOptions = {
491
+ telemetry: t,
492
+ viewerAdapter: mcpViewer,
493
+ subjectAdapter,
494
+ configured: [view],
495
+ limits: { records: 50 },
496
+ redact: (r) => r, // opt back into raw payloads; `false` disables entirely
497
+ };
498
+ const mcpTools: ToolDescriptor[] = createTelemetryMcp(mcpOpts);
499
+ const firstTool: ToolDescriptor = mcpTools[0]!;
500
+ void firstTool.name, firstTool.title, firstTool.description, firstTool.inputSchema;
501
+ void firstTool.handler({}, { session: 'x' });
502
+ const mcpSchema = toJsonSchema(firstTool);
503
+ void mcpSchema;
504
+ // the SDK seam is structural — a bare registerTool object satisfies it
505
+ const mcpServer: McpServerLike = { registerTool: () => undefined };
506
+ registerTelemetryTools(mcpServer, mcpTools);
507
+
479
508
  async function primitives() {
480
509
  const q: Queries = createQueries({
481
510
  TelemetryModel: t.models.telemetry,