@secondlayer/shared 13.4.0 → 13.6.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.
Files changed (42) hide show
  1. package/dist/src/archive/range-digest.d.ts +45 -0
  2. package/dist/src/archive/semantic-digest-builder.d.ts +45 -0
  3. package/dist/src/coverage/index.d.ts +45 -0
  4. package/dist/src/db/index.d.ts +50 -1
  5. package/dist/src/db/index.js +4 -1
  6. package/dist/src/db/index.js.map +3 -3
  7. package/dist/src/db/queries/chain-reorgs.d.ts +45 -0
  8. package/dist/src/db/queries/chain-reorgs.js +4 -1
  9. package/dist/src/db/queries/chain-reorgs.js.map +3 -3
  10. package/dist/src/db/queries/contracts.d.ts +45 -0
  11. package/dist/src/db/queries/instance.d.ts +45 -0
  12. package/dist/src/db/queries/integrity.d.ts +45 -0
  13. package/dist/src/db/queries/subgraph-gaps.d.ts +45 -0
  14. package/dist/src/db/queries/subgraph-operations.d.ts +45 -0
  15. package/dist/src/db/queries/subgraphs.d.ts +45 -0
  16. package/dist/src/db/queries/sync-scope.d.ts +45 -0
  17. package/dist/src/db/queries/webhooks.d.ts +45 -0
  18. package/dist/src/db/schema.d.ts +47 -1
  19. package/dist/src/index.d.ts +144 -7
  20. package/dist/src/index.js +183 -87
  21. package/dist/src/index.js.map +5 -4
  22. package/dist/src/insights/index.d.ts +1554 -0
  23. package/dist/src/insights/index.js +2284 -0
  24. package/dist/src/insights/index.js.map +29 -0
  25. package/dist/src/insights/thresholds.d.ts +56 -0
  26. package/dist/src/insights/thresholds.js +86 -0
  27. package/dist/src/insights/thresholds.js.map +10 -0
  28. package/dist/src/leader.d.ts +3 -1
  29. package/dist/src/leader.js +3 -1
  30. package/dist/src/leader.js.map +3 -3
  31. package/dist/src/node/local-client.d.ts +45 -0
  32. package/dist/src/queue/listener.js +4 -1
  33. package/dist/src/queue/listener.js.map +3 -3
  34. package/dist/src/schemas/index.d.ts +95 -7
  35. package/dist/src/schemas/index.js +180 -87
  36. package/dist/src/schemas/index.js.map +4 -3
  37. package/dist/src/schemas/insights.d.ts +89 -0
  38. package/dist/src/schemas/insights.js +98 -0
  39. package/dist/src/schemas/insights.js.map +10 -0
  40. package/migrations/0170_insights.ts +63 -0
  41. package/migrations/0171_webhook_incident_alerts.ts +45 -0
  42. package/package.json +13 -1
@@ -0,0 +1,29 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/schemas/insights.ts", "../src/insights/rank.ts", "../src/insights/thresholds.ts", "../src/insights/prompt.ts", "../src/insights/store.ts", "../src/db/jsonb.ts", "../src/env.ts", "../src/logger.ts", "../src/classify/confidence.ts", "../src/classify/types.ts", "../src/classify/limits.ts", "../src/classify/system-one.ts", "../src/classify/providers.ts", "../src/classify/rules.ts", "../src/classify/classify.ts", "../src/insights/label.ts", "../src/insights/subgraph-rules.ts", "../src/subgraphs/health.ts", "../src/insights/webhook-rules.ts", "../src/insights/evaluate.ts"],
4
+ "sourcesContent": [
5
+ "import { z } from \"zod\";\n\n/**\n * The insight contract. The tenant (`/api/insights`) and platform\n * (`/api/accounts/insights`) routes serve `Insight` verbatim. The web app keeps\n * a field-for-field copy in `apps/web/src/lib/insights.ts`; change both\n * together.\n *\n * The types are written out and the schemas annotated with them, rather than\n * inferred: the bundled `.d.ts` loses inferred zod types (they come out as\n * `unknown`), the same reason `schemas/webhooks.ts` annotates its schemas.\n */\n\nexport const INSIGHT_SEVERITIES = [\n\t\"losing\",\n\t\"will_fail\",\n\t\"costs\",\n\t\"recovered\",\n] as const;\n\nexport type InsightSeverity = (typeof INSIGHT_SEVERITIES)[number];\n\nexport type InsightResourceKind = \"webhook\" | \"subgraph\" | \"account\";\n\nexport type InsightEvidence =\n\t| {\n\t\t\tkind: \"series\";\n\t\t\ttitle: string;\n\t\t\tseries: {\n\t\t\t\tname: string;\n\t\t\t\trole: \"a\" | \"context\" | \"mark\";\n\t\t\t\tpoints: number[];\n\t\t\t}[];\n\t\t\tlabels: [number, string][];\n\t\t\tunit?: string;\n\t }\n\t| {\n\t\t\tkind: \"table\";\n\t\t\ttitle: string;\n\t\t\tcolumns: string[];\n\t\t\trows: (string | number)[][];\n\t\t\tmarkRow?: number;\n\t }\n\t| {\n\t\t\tkind: \"shapes\";\n\t\t\ttitle: string;\n\t\t\tshapes: { code: string; body: string; count: number; isNew: boolean }[];\n\t }\n\t| {\n\t\t\tkind: \"timeline\";\n\t\t\ttitle: string;\n\t\t\tsteps: { what: string; at: string; note?: string; failed: boolean }[];\n\t };\n\nexport interface Insight {\n\tid: string;\n\trule: string;\n\tseverity: InsightSeverity;\n\timpact: number;\n\tresource: {\n\t\tkind: InsightResourceKind;\n\t\tid: string;\n\t\tname: string;\n\t};\n\tline: string;\n\tmeta?: string;\n\tlead: string;\n\tbody: string;\n\tevidence: InsightEvidence[];\n\thandoff: { prompt: string; commands: string[] };\n\thow: {\n\t\tdetector: string;\n\t\tfacts: [string, string][];\n\t\tlabel?: { cause: string; confidence: number };\n\t};\n\tcreatedAt: string;\n\tresolvedAt?: string | null;\n}\n\nexport interface InsightListResponse {\n\tdata: Insight[];\n}\n\nexport const InsightSeveritySchema: z.ZodType<InsightSeverity> =\n\tz.enum(INSIGHT_SEVERITIES);\n\nexport const InsightResourceKindSchema: z.ZodType<InsightResourceKind> = z.enum(\n\t[\"webhook\", \"subgraph\", \"account\"],\n);\n\nexport const InsightEvidenceSchema: z.ZodType<InsightEvidence> =\n\tz.discriminatedUnion(\"kind\", [\n\t\tz.object({\n\t\t\tkind: z.literal(\"series\"),\n\t\t\ttitle: z.string(),\n\t\t\tseries: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tname: z.string(),\n\t\t\t\t\trole: z.enum([\"a\", \"context\", \"mark\"]),\n\t\t\t\t\tpoints: z.array(z.number()),\n\t\t\t\t}),\n\t\t\t),\n\t\t\tlabels: z.array(z.tuple([z.number(), z.string()])),\n\t\t\tunit: z.string().optional(),\n\t\t}),\n\t\tz.object({\n\t\t\tkind: z.literal(\"table\"),\n\t\t\ttitle: z.string(),\n\t\t\tcolumns: z.array(z.string()),\n\t\t\trows: z.array(z.array(z.union([z.string(), z.number()]))),\n\t\t\tmarkRow: z.number().optional(),\n\t\t}),\n\t\tz.object({\n\t\t\tkind: z.literal(\"shapes\"),\n\t\t\ttitle: z.string(),\n\t\t\tshapes: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tcode: z.string(),\n\t\t\t\t\tbody: z.string(),\n\t\t\t\t\tcount: z.number(),\n\t\t\t\t\tisNew: z.boolean(),\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\tz.object({\n\t\t\tkind: z.literal(\"timeline\"),\n\t\t\ttitle: z.string(),\n\t\t\tsteps: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\twhat: z.string(),\n\t\t\t\t\tat: z.string(),\n\t\t\t\t\tnote: z.string().optional(),\n\t\t\t\t\tfailed: z.boolean(),\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t]);\n\nexport const InsightSchema: z.ZodType<Insight> = z.object({\n\tid: z.string(),\n\trule: z.string(),\n\tseverity: InsightSeveritySchema,\n\timpact: z.number(),\n\tresource: z.object({\n\t\tkind: InsightResourceKindSchema,\n\t\tid: z.string(),\n\t\tname: z.string(),\n\t}),\n\tline: z.string(),\n\tmeta: z.string().optional(),\n\tlead: z.string(),\n\tbody: z.string(),\n\tevidence: z.array(InsightEvidenceSchema),\n\thandoff: z.object({\n\t\tprompt: z.string(),\n\t\tcommands: z.array(z.string()),\n\t}),\n\thow: z.object({\n\t\tdetector: z.string(),\n\t\tfacts: z.array(z.tuple([z.string(), z.string()])),\n\t\tlabel: z.object({ cause: z.string(), confidence: z.number() }).optional(),\n\t}),\n\tcreatedAt: z.string(),\n\tresolvedAt: z.string().nullable().optional(),\n});\n\nexport const InsightListResponseSchema: z.ZodType<InsightListResponse> =\n\tz.object({\n\t\tdata: z.array(InsightSchema),\n\t});\n\nexport const SEVERITY_ORDER: Record<InsightSeverity, number> = {\n\tlosing: 0,\n\twill_fail: 1,\n\tcosts: 2,\n\trecovered: 3,\n};\n",
6
+ "import { type Insight, SEVERITY_ORDER } from \"../schemas/insights.ts\";\n\n/** Severity, then impact (desc), then createdAt (desc). Returns a new array. */\nexport function rankInsights(insights: readonly Insight[]): Insight[] {\n\treturn [...insights].sort(\n\t\t(a, b) =>\n\t\t\tSEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity] ||\n\t\t\tb.impact - a.impact ||\n\t\t\tDate.parse(b.createdAt) - Date.parse(a.createdAt),\n\t);\n}\n",
7
+ "// Detector thresholds. Named constants, not config. Tune from real data;\n// until then these stay conservative: a wrong insight costs more trust than\n// a missed one.\n//\n// This file must stay import-free. The SDK's webhook doctor imports it, and\n// the doctor reaches the browser through apps/web, so anything imported here\n// would ship to the client bundle.\n//\n// Constants computed from an expression carry an explicit `: number`: the\n// bundled `.d.ts` otherwise types them `unknown`.\n\n// ── Webhook doctor (shared with the SDK / `sl webhooks doctor`) ────────\nexport const RATE_LIMITED_MIN_ATTEMPTS = 10;\nexport const RATE_LIMITED_MIN_RATIO = 0.5;\nexport const DOWN_MIN_CONSECUTIVE = 5;\nexport const REJECTS_MIN_CONSECUTIVE = 5;\nexport const SLOW_WINDOW = 20;\nexport const SLOW_MIN_SAMPLES = 5;\nexport const SLOW_TIMEOUT_RATIO = 0.5;\nexport const LAG_WINDOW = 20;\nexport const LAG_MIN_SAMPLES = 5;\nexport const LAG_THRESHOLD_MS = 60_000;\n\n// ── Tenant rules ───────────────────────────────────────────────────────\n/** Consecutive newest 2xx HTML responses that mean a login wall. */\nexport const LOGIN_WALL_MIN_CONSECUTIVE = 20;\n/** Share of prior-week 2xx responses that must have been JSON. */\nexport const LOGIN_WALL_PRIOR_JSON_RATIO = 0.8;\n/** Prior-week successful responses needed before a baseline counts. */\nexport const LOGIN_WALL_MIN_PRIOR = 10;\nexport const SLOWING_DAYS = 14;\nexport const SLOWING_MIN_RISING_DAYS = 10;\nexport const SLOWING_MIN_R2 = 0.8;\nexport const TLS_WARN_DAYS = 14;\n/** Dead outbox rows are deleted at 90 days; warn at 83. */\nexport const DEAD_LETTER_WARN_AGE_DAYS = 83;\nexport const DEAD_LETTER_DELETE_AGE_DAYS = 90;\nexport const TEST_ENDPOINT_MIN_AGE_DAYS = 7;\nexport const HANDLER_ERRORS_MIN_24H = 10;\n/** Blocks behind tip, for 3 consecutive snapshots. */\nexport const SUBGRAPH_LAG_BLOCKS = 20;\nexport const SUBGRAPH_LAG_SNAPSHOTS = 3;\nexport const SUBGRAPH_LAG_LOSING_BLOCKS = 500;\n\n// ── Platform rules ─────────────────────────────────────────────────────\n/**\n * Index and Streams cap one page at 1000 rows\n * (`packages/api/src/index/_shared.ts` `Math.min(1000, parsed)`,\n * `packages/api/src/routes/streams.ts` `limit: Math.min(1000, limit)`).\n */\nexport const PAGE_CAP_ROWS = 1000;\nexport const DUPLICATE_READS_MIN_PAGES = 3;\nexport const DUPLICATE_READS_WINDOW_MS = 3_600_000;\n/** Only repeats that started within this long ago count as current. */\nexport const DUPLICATE_READS_LOOKBACK_MS: number = 24 * 3_600_000;\nexport const POLLING_MIN_DURATION_MS = 3_600_000;\n/** Requests needed in the window before an interval means anything. */\nexport const POLLING_MIN_REQUESTS = 5;\nexport const POLLING_LOOKBACK_MS: number = 6 * 3_600_000;\nexport const POLLING_MAX_STDDEV_RATIO = 0.2;\nexport const POLLING_MIN_EMPTY_RATIO = 0.8;\nexport const SERVER_ERRORS_MIN_5XX = 3;\nexport const BACKFILL_DAY_SHARE = 0.25;\nexport const BACKFILL_FULL_PAGE_RATIO = 0.9;\n/** A day under this many rows is never called a backfill, however full its pages. */\nexport const BACKFILL_MIN_DAY_ROWS = 100_000;\nexport const CREDITS_RUNWAY_WARN_DAYS = 7;\n\n// ── Lifecycle ──────────────────────────────────────────────────────────\n/** A resolved insight stays visible as \"recovered\" for this long. */\nexport const RECOVERED_VISIBLE_MS: number = 24 * 3600_000;\n/** Consecutive clean evaluations before an open insight resolves. */\nexport const CLEAR_STREAK_TO_RESOLVE = 2;\n",
8
+ "/**\n * Hand-off prompt helpers. A prompt tells the developer's coding agent what we\n * saw (names, times, counts, error text) and what to look at, in plain words.\n * It never contains code and never asks the agent to read the user's source.\n */\n\nexport function handoffPrompt(parts: {\n\t/** `My Secondlayer webhook \"mint-alerts\"` */\n\tsubject: string;\n\t/** Plain facts with numbers and UTC times. The first one continues the\n\t * subject (\"has been failing since 16:06 UTC\"); the rest are sentences. */\n\tobserved: string[];\n\t/** What to check, in words. */\n\tlookAt: string;\n\t/** Optional follow-up naming an `sl` command. */\n\tfollowUp?: string;\n}): string {\n\tconst sentence = (text: string) => {\n\t\tconst trimmed = text.trim().replace(/[.\\s]+$/, \"\");\n\t\treturn trimmed.length > 0 ? `${trimmed}.` : \"\";\n\t};\n\tconst [first = \"\", ...rest] = parts.observed;\n\tconst sentences = [\n\t\tsentence(`${parts.subject} ${first}`),\n\t\t...rest.map(sentence),\n\t\tsentence(parts.lookAt),\n\t\tparts.followUp ? sentence(parts.followUp) : \"\",\n\t];\n\treturn sentences.filter((s) => s.length > 0).join(\" \");\n}\n\n/**\n * Receiver and handler text is untrusted and often JSON or HTML. Collapse it\n * to one plain line with no braces, backticks or arrows so it can sit inside\n * a prompt or a body sentence.\n */\nexport function plainSnippet(\n\ttext: string | null | undefined,\n\tmax = 80,\n): string {\n\tif (!text) return \"\";\n\tconst flat = text\n\t\t.replace(/<[^>]*>/g, \" \")\n\t\t.replace(/=>/g, \"->\")\n\t\t.replace(/[`{}$\\\\]/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\treturn flat.length > max ? `${flat.slice(0, max - 1).trimEnd()}...` : flat;\n}\n\n/** \"16:06 UTC\" */\nexport function utcTime(date: Date): string {\n\treturn `${date.toISOString().slice(11, 16)} UTC`;\n}\n\n/** \"Oct 15 16:06 UTC\" */\nexport function utcDateTime(date: Date): string {\n\tconst month = date.toLocaleString(\"en-US\", {\n\t\tmonth: \"short\",\n\t\ttimeZone: \"UTC\",\n\t});\n\treturn `${month} ${date.getUTCDate()} ${utcTime(date)}`;\n}\n\n/** \"Oct 15\" */\nexport function utcDay(date: Date): string {\n\tconst month = date.toLocaleString(\"en-US\", {\n\t\tmonth: \"short\",\n\t\ttimeZone: \"UTC\",\n\t});\n\treturn `${month} ${date.getUTCDate()}`;\n}\n\nexport function plural(n: number, one: string, many = `${one}s`): string {\n\treturn `${n.toLocaleString(\"en-US\")} ${n === 1 ? one : many}`;\n}\n\n/** The host of a URL, or the input when it does not parse. */\nexport function hostOf(url: string): string {\n\ttry {\n\t\treturn new URL(url).host;\n\t} catch {\n\t\treturn url;\n\t}\n}\n",
9
+ "import { type Kysely, sql } from \"kysely\";\nimport { jsonb, parseJsonb } from \"../db/jsonb.ts\";\nimport type { Database } from \"../db/types.ts\";\nimport type { Insight, InsightSeverity } from \"../schemas/insights.ts\";\nimport { rankInsights } from \"./rank.ts\";\nimport { CLEAR_STREAK_TO_RESOLVE, RECOVERED_VISIBLE_MS } from \"./thresholds.ts\";\nimport type { DetectedInsight, EvaluatedKey, InsightScope } from \"./types.ts\";\n\ntype Payload = Pick<\n\tInsight,\n\t| \"resource\"\n\t| \"line\"\n\t| \"meta\"\n\t| \"lead\"\n\t| \"body\"\n\t| \"evidence\"\n\t| \"handoff\"\n\t| \"how\"\n>;\n\nconst NIL_UUID = \"00000000-0000-0000-0000-000000000000\";\nconst UUID_RE =\n\t/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** SQL predicate scoping a table to one account (or to the NULL tenant scope). */\nfunction scopeWhere(scope: InsightScope, column = sql.ref(\"account_id\")) {\n\treturn scope.accountId === null\n\t\t? sql`${column} IS NULL`\n\t\t: sql`${column} = ${scope.accountId}`;\n}\n\nfunction toPayload(insight: DetectedInsight): Payload {\n\treturn {\n\t\tresource: insight.resource,\n\t\tline: insight.line,\n\t\t...(insight.meta !== undefined ? { meta: insight.meta } : {}),\n\t\tlead: insight.lead,\n\t\tbody: insight.body,\n\t\tevidence: insight.evidence,\n\t\thandoff: insight.handoff,\n\t\thow: insight.how,\n\t};\n}\n\n/**\n * Persist one evaluation pass.\n *\n * Fired insights upsert on the open (resource, rule) row. Every key in\n * `evaluatedKeys` that has an open row and did not fire gains a clear streak,\n * and resolves once the streak reaches `CLEAR_STREAK_TO_RESOLVE`. A rule that\n * was skipped (missing data) must stay out of `evaluatedKeys` so it never\n * counts toward clearing.\n */\nexport async function recordEvaluation(\n\tdb: Kysely<Database>,\n\tscope: InsightScope,\n\tfired: DetectedInsight[],\n\tevaluatedKeys: EvaluatedKey[],\n\tnow: Date = new Date(),\n): Promise<void> {\n\tawait db.transaction().execute(async (trx) => {\n\t\tfor (const insight of fired) {\n\t\t\tawait sql`\n\t\t\t\tINSERT INTO insights\n\t\t\t\t\t(account_id, resource_kind, resource_id, rule, severity, impact,\n\t\t\t\t\t payload, first_seen_at, last_seen_at)\n\t\t\t\tVALUES\n\t\t\t\t\t(${scope.accountId}, ${insight.resource.kind}, ${insight.resource.id},\n\t\t\t\t\t ${insight.rule}, ${insight.severity}, ${insight.impact},\n\t\t\t\t\t ${jsonb(toPayload(insight))}, ${now}, ${now})\n\t\t\t\tON CONFLICT\n\t\t\t\t\t(coalesce(account_id, ${sql.lit(NIL_UUID)}::uuid), resource_kind, resource_id, rule)\n\t\t\t\t\tWHERE resolved_at IS NULL\n\t\t\t\tDO UPDATE SET\n\t\t\t\t\tpayload = EXCLUDED.payload,\n\t\t\t\t\tseverity = EXCLUDED.severity,\n\t\t\t\t\timpact = EXCLUDED.impact,\n\t\t\t\t\tlast_seen_at = EXCLUDED.last_seen_at,\n\t\t\t\t\tclear_streak = 0\n\t\t\t`.execute(trx);\n\t\t}\n\n\t\tconst firedKeys = new Set(\n\t\t\tfired.map(\n\t\t\t\t(i) => `${i.resource.kind}\\u0000${i.resource.id}\\u0000${i.rule}`,\n\t\t\t),\n\t\t);\n\t\tfor (const key of evaluatedKeys) {\n\t\t\tif (firedKeys.has(`${key.kind}\\u0000${key.id}\\u0000${key.rule}`))\n\t\t\t\tcontinue;\n\t\t\tawait sql`\n\t\t\t\tUPDATE insights\n\t\t\t\tSET clear_streak = clear_streak + 1,\n\t\t\t\t\tresolved_at = CASE\n\t\t\t\t\t\tWHEN clear_streak + 1 >= ${CLEAR_STREAK_TO_RESOLVE}\n\t\t\t\t\t\tTHEN ${now}::timestamptz\n\t\t\t\t\t\tELSE NULL\n\t\t\t\t\tEND\n\t\t\t\tWHERE ${scopeWhere(scope)}\n\t\t\t\t\tAND resource_kind = ${key.kind}\n\t\t\t\t\tAND resource_id = ${key.id}\n\t\t\t\t\tAND rule = ${key.rule}\n\t\t\t\t\tAND resolved_at IS NULL\n\t\t\t`.execute(trx);\n\t\t}\n\t});\n}\n\n/** (kind, id) pairs that have an open insight in this scope. */\nexport async function listOpenResources(\n\tdb: Kysely<Database>,\n\tscope: InsightScope,\n): Promise<{ kind: Insight[\"resource\"][\"kind\"]; id: string; rule: string }[]> {\n\tconst { rows } = await sql<{\n\t\tresource_kind: Insight[\"resource\"][\"kind\"];\n\t\tresource_id: string;\n\t\trule: string;\n\t}>`\n\t\tSELECT resource_kind, resource_id, rule FROM insights\n\t\tWHERE ${scopeWhere(scope)} AND resolved_at IS NULL\n\t`.execute(db);\n\treturn rows.map((r) => ({\n\t\tkind: r.resource_kind,\n\t\tid: r.resource_id,\n\t\trule: r.rule,\n\t}));\n}\n\n/**\n * Open rows plus rows resolved within `RECOVERED_VISIBLE_MS`, minus dismissed\n * and muted ones. Resolved rows come back as `recovered`. Ranked.\n */\nexport async function listInsights(\n\tdb: Kysely<Database>,\n\tscope: InsightScope,\n\tnow: Date = new Date(),\n\tfilter: { kind?: string; id?: string } = {},\n): Promise<Insight[]> {\n\tconst since = new Date(now.getTime() - RECOVERED_VISIBLE_MS);\n\tconst { rows } = await sql<{\n\t\tid: string;\n\t\trule: string;\n\t\tseverity: string;\n\t\timpact: number;\n\t\tpayload: unknown;\n\t\tfirst_seen_at: Date;\n\t\tresolved_at: Date | null;\n\t}>`\n\t\tSELECT i.id, i.rule, i.severity, i.impact, i.payload, i.first_seen_at, i.resolved_at\n\t\tFROM insights i\n\t\tWHERE ${scopeWhere(scope, sql.ref(\"i.account_id\"))}\n\t\t\tAND i.dismissed_at IS NULL\n\t\t\tAND (i.resolved_at IS NULL OR i.resolved_at > ${since})\n\t\t\t${filter.kind ? sql`AND i.resource_kind = ${filter.kind}` : sql``}\n\t\t\t${filter.id ? sql`AND i.resource_id = ${filter.id}` : sql``}\n\t\t\tAND NOT EXISTS (\n\t\t\t\tSELECT 1 FROM insight_mutes m\n\t\t\t\tWHERE ${scopeWhere(scope, sql.ref(\"m.account_id\"))}\n\t\t\t\t\tAND m.resource_kind = i.resource_kind\n\t\t\t\t\tAND m.resource_id = i.resource_id\n\t\t\t\t\tAND m.rule = i.rule\n\t\t\t)\n\t`.execute(db);\n\n\treturn rankInsights(\n\t\trows.map((row) => {\n\t\t\tconst payload = parseJsonb<Payload>(row.payload);\n\t\t\treturn {\n\t\t\t\tid: row.id,\n\t\t\t\trule: row.rule,\n\t\t\t\tseverity: row.resolved_at\n\t\t\t\t\t? \"recovered\"\n\t\t\t\t\t: (row.severity as InsightSeverity),\n\t\t\t\timpact: Number(row.impact),\n\t\t\t\t...payload,\n\t\t\t\tcreatedAt: row.first_seen_at.toISOString(),\n\t\t\t\tresolvedAt: row.resolved_at ? row.resolved_at.toISOString() : null,\n\t\t\t} satisfies Insight;\n\t\t}),\n\t);\n}\n\n/** Hide the current incident. Returns false when the id is not in scope. */\nexport async function dismissInsight(\n\tdb: Kysely<Database>,\n\tscope: InsightScope,\n\tid: string,\n\tnow: Date = new Date(),\n): Promise<boolean> {\n\tif (!UUID_RE.test(id)) return false;\n\tconst result = await sql`\n\t\tUPDATE insights SET dismissed_at = ${now}\n\t\tWHERE id = ${id}::uuid AND ${scopeWhere(scope)}\n\t`.execute(db);\n\treturn Number(result.numAffectedRows ?? 0) > 0;\n}\n\n/** Mute the insight's rule for its resource until un-muted. */\nexport async function muteRule(\n\tdb: Kysely<Database>,\n\tscope: InsightScope,\n\tid: string,\n): Promise<boolean> {\n\tif (!UUID_RE.test(id)) return false;\n\tconst result = await sql`\n\t\tINSERT INTO insight_mutes (account_id, resource_kind, resource_id, rule)\n\t\tSELECT account_id, resource_kind, resource_id, rule FROM insights\n\t\tWHERE id = ${id}::uuid AND ${scopeWhere(scope)}\n\t\tON CONFLICT (coalesce(account_id, ${sql.lit(NIL_UUID)}::uuid), resource_kind, resource_id, rule)\n\t\tDO NOTHING\n\t`.execute(db);\n\tif (Number(result.numAffectedRows ?? 0) > 0) return true;\n\t// Already muted counts as success; unknown id does not.\n\treturn existsInScope(db, scope, id);\n}\n\nexport async function unmuteRule(\n\tdb: Kysely<Database>,\n\tscope: InsightScope,\n\tid: string,\n): Promise<boolean> {\n\tif (!(await existsInScope(db, scope, id))) return false;\n\tawait sql`\n\t\tDELETE FROM insight_mutes m\n\t\tUSING insights i\n\t\tWHERE i.id = ${id}::uuid\n\t\t\tAND ${scopeWhere(scope, sql.ref(\"i.account_id\"))}\n\t\t\tAND ${scopeWhere(scope, sql.ref(\"m.account_id\"))}\n\t\t\tAND m.resource_kind = i.resource_kind\n\t\t\tAND m.resource_id = i.resource_id\n\t\t\tAND m.rule = i.rule\n\t`.execute(db);\n\treturn true;\n}\n\nasync function existsInScope(\n\tdb: Kysely<Database>,\n\tscope: InsightScope,\n\tid: string,\n): Promise<boolean> {\n\tif (!UUID_RE.test(id)) return false;\n\tconst { rows } = await sql<{ id: string }>`\n\t\tSELECT id FROM insights WHERE id = ${id}::uuid AND ${scopeWhere(scope)}\n\t`.execute(db);\n\treturn rows.length > 0;\n}\n\n/** Delete resolved or dismissed-and-resolved rows older than `olderThanDays`. */\nexport async function purgeResolved(\n\tdb: Kysely<Database>,\n\tscope: InsightScope,\n\tolderThanDays = 30,\n\tnow: Date = new Date(),\n): Promise<void> {\n\tconst cutoff = new Date(now.getTime() - olderThanDays * 86_400_000);\n\tawait sql`\n\t\tDELETE FROM insights\n\t\tWHERE ${scopeWhere(scope)} AND resolved_at IS NOT NULL AND resolved_at < ${cutoff}\n\t`.execute(db);\n}\n",
10
+ "import { type RawBuilder, sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n *\n * Generic parameter lets callers set the RawBuilder's output type so they\n * don't need to cast at the insert site. Default is `unknown` — widen at\n * the call site when the column type is narrower, e.g. `jsonb<MyShape>(...)`.\n */\nexport function jsonb<T = unknown>(value: T): RawBuilder<T> {\n\tconst escaped = JSON.stringify(value, (_k, v) =>\n\t\ttypeof v === \"bigint\" ? v.toString() : v,\n\t).replace(/'/g, \"''\");\n\treturn sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n",
11
+ "import { z } from \"zod\";\n\n// Parse comma-separated networks\nconst networksSchema = z.string().transform((val) => {\n\tconst networks = val\n\t\t.split(\",\")\n\t\t.map((n) => n.trim())\n\t\t.filter(Boolean);\n\tconst valid = [\"mainnet\", \"testnet\"];\n\tfor (const n of networks) {\n\t\tif (!valid.includes(n)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid network: ${n}. Must be one of: ${valid.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\treturn networks as (\"mainnet\" | \"testnet\")[];\n});\n\ninterface EnvSchemaOutput {\n\tDATABASE_URL?: string;\n\t/**\n\t * Shared indexer DB (blocks/txs/events). Falls back to DATABASE_URL.\n\t * Set this alongside TARGET_DATABASE_URL to enable dual-DB mode.\n\t */\n\tSOURCE_DATABASE_URL?: string;\n\t/**\n\t * Tenant DB (subgraph schemas + subgraphs table). Falls back to DATABASE_URL.\n\t * Set this alongside SOURCE_DATABASE_URL to enable dual-DB mode.\n\t */\n\tTARGET_DATABASE_URL?: string;\n\tNETWORK?: \"mainnet\" | \"testnet\";\n\tNETWORKS?: (\"mainnet\" | \"testnet\")[];\n\tLOG_LEVEL: \"debug\" | \"info\" | \"warn\" | \"error\";\n\tNODE_ENV: \"development\" | \"production\" | \"test\";\n}\n\n// Cast needed: z.preprocess / z.default create different _input vs _output types\n// that z.ZodType<T> can't represent without explicit input type param\nconst envSchema: z.ZodType<EnvSchemaOutput> = z.object({\n\tDATABASE_URL: z.preprocess(\n\t\t(val) => (typeof val === \"string\" && val.length === 0 ? undefined : val),\n\t\tz.string().url().optional(),\n\t),\n\tSOURCE_DATABASE_URL: z.preprocess(\n\t\t(val) => (typeof val === \"string\" && val.length === 0 ? undefined : val),\n\t\tz.string().url().optional(),\n\t),\n\tTARGET_DATABASE_URL: z.preprocess(\n\t\t(val) => (typeof val === \"string\" && val.length === 0 ? undefined : val),\n\t\tz.string().url().optional(),\n\t),\n\tNETWORK: z.enum([\"mainnet\", \"testnet\"]).optional(),\n\tNETWORKS: networksSchema.optional(),\n\tLOG_LEVEL: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n\tNODE_ENV: z\n\t\t.enum([\"development\", \"production\", \"test\"])\n\t\t.default(\"development\"),\n}) as unknown as z.ZodType<EnvSchemaOutput>;\n\nexport type Env = EnvSchemaOutput & {\n\tenabledNetworks: (\"mainnet\" | \"testnet\")[];\n};\n\nlet cachedEnv: Env | null = null;\n\nexport function getEnv(): Env {\n\tif (cachedEnv) {\n\t\treturn cachedEnv;\n\t}\n\n\tconst result = envSchema.safeParse(process.env);\n\n\tif (!result.success) {\n\t\tconsole.error(\"❌ Invalid environment configuration:\");\n\t\tconsole.error(z.treeifyError(result.error));\n\t\tthrow new Error(\"Invalid environment configuration\");\n\t}\n\n\t// Compute enabled networks from NETWORKS or NETWORK\n\tlet enabledNetworks: (\"mainnet\" | \"testnet\")[];\n\tif (result.data.NETWORKS && result.data.NETWORKS.length > 0) {\n\t\tenabledNetworks = result.data.NETWORKS;\n\t} else if (result.data.NETWORK) {\n\t\tenabledNetworks = [result.data.NETWORK];\n\t} else {\n\t\tenabledNetworks = [\"mainnet\"]; // Default\n\t}\n\n\tcachedEnv = { ...result.data, enabledNetworks };\n\treturn cachedEnv;\n}\n\n/**\n * True when `NODE_ENV=production`, read at RUNTIME — the single source of truth\n * for prod-vs-dev branching in this package.\n *\n * Why a helper instead of inlining `process.env.NODE_ENV === \"production\"`:\n * bunup/esbuild constant-folds the dot-access `process.env.NODE_ENV` to its\n * BUILD-time value. `@secondlayer/shared` is consumed by other services as its\n * built `dist`, so an inlined check freezes to a literal in the shipped bundle\n * (e.g. `const isProd = false`) and silently ignores the container's real\n * NODE_ENV. The bracket access below is NOT folded, so it stays a runtime read.\n * Route every prod check through here so the footgun lives in exactly one place.\n */\nexport function isProductionEnv(): boolean {\n\t// biome-ignore lint/complexity/useLiteralKeys: bracket access is deliberate — dot-access gets constant-folded by the bundler, freezing this to a build-time literal in the shipped dist.\n\treturn process.env[\"NODE_ENV\"] === \"production\";\n}\n\ntype EnvMap = NodeJS.ProcessEnv;\n\n/**\n * PoX-4 stacking decoder is ON by default — `/v1/index/stacking` is part of the\n * public surface, so the decoder that fills `pox4_calls` runs unless explicitly\n * opted out with `POX4_DECODER_ENABLED=false` (mirrors the sBTC decoder policy).\n */\nexport function isPox4DecoderEnabled(env: EnvMap = process.env): boolean {\n\treturn env.POX4_DECODER_ENABLED !== \"false\";\n}\n\n/**\n * PoX-5 print-event decoder is ON by default (mirrors pox-4/sBTC policy).\n * No activation gating needed: pox-5 events cannot exist before the Epoch 4.0\n * hard fork (Bitcoin block 960,230), so pre-activation the decoder idles at\n * tip. Opt out with `POX5_DECODER_ENABLED=false`.\n */\nexport function isPox5DecoderEnabled(env: EnvMap = process.env): boolean {\n\treturn env.POX5_DECODER_ENABLED !== \"false\";\n}\n\n/** Default ON. Opt out with `SBTC_DECODER_ENABLED=false`. */\nexport function isSbtcDecoderEnabled(env: EnvMap = process.env): boolean {\n\treturn env.SBTC_DECODER_ENABLED !== \"false\";\n}\n\n/** Default OFF. Opt in with `BNS_DECODER_ENABLED=true`. */\nexport function isBnsDecoderEnabled(env: EnvMap = process.env): boolean {\n\treturn env.BNS_DECODER_ENABLED === \"true\";\n}\n\n// Export for testing\nexport { envSchema };\n",
12
+ "import { getEnv } from \"./env.ts\";\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3,\n};\n\nclass Logger {\n\tprivate _level?: LogLevel;\n\tprivate _isProduction?: boolean;\n\tprivate _initialized = false;\n\n\tprivate init() {\n\t\tif (this._initialized) return;\n\t\tthis._initialized = true;\n\t\ttry {\n\t\t\tconst env = getEnv();\n\t\t\tthis._level = env.LOG_LEVEL;\n\t\t\tthis._isProduction = env.NODE_ENV === \"production\";\n\t\t} catch {\n\t\t\t// Fallback when env is unavailable (e.g. tests without DATABASE_URL)\n\t\t\tthis._level = \"info\";\n\t\t\tthis._isProduction = false;\n\t\t}\n\t}\n\n\tprivate get level(): LogLevel {\n\t\tthis.init();\n\t\t// biome-ignore lint/style/noNonNullAssertion: value is non-null after preceding check or by construction; TS narrowing limitation\n\t\treturn this._level!;\n\t}\n\n\tprivate get isProduction(): boolean {\n\t\tthis.init();\n\t\t// biome-ignore lint/style/noNonNullAssertion: value is non-null after preceding check or by construction; TS narrowing limitation\n\t\treturn this._isProduction!;\n\t}\n\n\tprivate shouldLog(level: LogLevel): boolean {\n\t\treturn LOG_LEVELS[level] >= LOG_LEVELS[this.level];\n\t}\n\n\tprivate formatMessage(\n\t\tlevel: LogLevel,\n\t\tmessage: string,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tmeta?: Record<string, any>,\n\t) {\n\t\tconst timestamp = new Date().toISOString();\n\n\t\tif (this.isProduction) {\n\t\t\t// JSON output for production\n\t\t\treturn JSON.stringify({\n\t\t\t\ttimestamp,\n\t\t\t\tlevel,\n\t\t\t\tmessage,\n\t\t\t\t...meta,\n\t\t\t});\n\t\t}\n\n\t\t// Human-readable output for development\n\t\tconst metaStr = meta ? ` ${JSON.stringify(meta)}` : \"\";\n\t\treturn `[${timestamp}] ${level.toUpperCase()}: ${message}${metaStr}`;\n\t}\n\n\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\tdebug(message: string, meta?: Record<string, any>): void {\n\t\tif (this.shouldLog(\"debug\")) {\n\t\t\tconsole.debug(this.formatMessage(\"debug\", message, meta));\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\tinfo(message: string, meta?: Record<string, any>): void {\n\t\tif (this.shouldLog(\"info\")) {\n\t\t\tconsole.info(this.formatMessage(\"info\", message, meta));\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\twarn(message: string, meta?: Record<string, any>): void {\n\t\tif (this.shouldLog(\"warn\")) {\n\t\t\tconsole.warn(this.formatMessage(\"warn\", message, meta));\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\terror(message: string, meta?: Record<string, any>): void {\n\t\tif (this.shouldLog(\"error\")) {\n\t\t\tconsole.error(this.formatMessage(\"error\", message, meta));\n\t\t}\n\t}\n}\n\n// Export singleton instance\nexport const logger: Logger = new Logger();\n",
13
+ "import type { Answer, Question } from \"./types.ts\";\n\nconst clamp01 = (n: number) => Math.min(1, Math.max(0, n));\n\n/** (p_max - 1/K) / (1 - 1/K). One option gives 1. */\nexport function choiceConfidence(\n\tprobabilities: Record<string, number>,\n): number {\n\tconst ps = Object.values(probabilities);\n\tconst k = ps.length;\n\tif (k <= 1) return 1;\n\tconst pmax = Math.max(...ps);\n\treturn clamp01((pmax - 1 / k) / (1 - 1 / k));\n}\n\n/** max(0, 1 - E|level - mode| / D); D = mean distance of a uniform\n * distribution over the levels from the middle level. */\nexport function scoreConfidence(\n\tprobabilities: Record<string, number>,\n\tlevels: number,\n): number {\n\tif (levels <= 1) return 1;\n\tlet mode = 0;\n\tlet best = -1;\n\tfor (let i = 0; i < levels; i++) {\n\t\tconst p = probabilities[String(i)] ?? 0;\n\t\tif (p > best) {\n\t\t\tbest = p;\n\t\t\tmode = i;\n\t\t}\n\t}\n\tlet e = 0;\n\tlet d = 0;\n\tfor (let i = 0; i < levels; i++) {\n\t\te += (probabilities[String(i)] ?? 0) * Math.abs(i - mode);\n\t\td += Math.abs(i - (levels - 1) / 2);\n\t}\n\td /= levels;\n\treturn clamp01(1 - e / d);\n}\n\n/** Host-defined: |2p - 1|. Not comparable with choice/score confidence. */\nexport function booleanConfidence(p: number): number {\n\treturn Math.abs(2 * p - 1);\n}\n\n/** Shape of an `ai` DecisionAnswer, or the rules output, before normalizing. */\nexport type RawAnswer =\n\t| { type: \"choice\"; choice: string; probabilities?: Record<string, number> }\n\t| { type: \"score\"; score: number; probabilities?: Record<string, number> }\n\t| { type: \"boolean\"; probability: number }\n\t| { type: \"refusal\" };\n\nexport function normalizeAnswer(question: Question, raw: RawAnswer): Answer {\n\tif (raw.type === \"refusal\") return { type: \"refusal\" };\n\tif (raw.type === \"boolean\" && question.type === \"boolean\") {\n\t\treturn {\n\t\t\ttype: \"boolean\",\n\t\t\tprobability: raw.probability,\n\t\t\tconfidence: booleanConfidence(raw.probability),\n\t\t};\n\t}\n\tif (raw.type === \"choice\" && question.type === \"choice\") {\n\t\tconst probabilities = raw.probabilities ?? {};\n\t\t// Never fabricate a distribution: no probabilities means confidence 0.\n\t\treturn {\n\t\t\ttype: \"choice\",\n\t\t\tchoice: raw.choice,\n\t\t\tprobabilities,\n\t\t\tconfidence: raw.probabilities ? choiceConfidence(probabilities) : 0,\n\t\t};\n\t}\n\tif (raw.type === \"score\" && question.type === \"score\") {\n\t\tconst probabilities = raw.probabilities ?? {};\n\t\treturn {\n\t\t\ttype: \"score\",\n\t\t\tscore: raw.score,\n\t\t\tprobabilities,\n\t\t\tconfidence: raw.probabilities\n\t\t\t\t? scoreConfidence(probabilities, question.criteria.length)\n\t\t\t\t: 0,\n\t\t};\n\t}\n\treturn { type: \"refusal\" };\n}\n",
14
+ "export type ClassifierName = \"jev\" | \"kev\" | \"clef\" | \"rules\";\n\nexport type Question =\n\t| { type: \"choice\"; instructions: string; criteria: Record<string, string> }\n\t| { type: \"score\"; instructions: string; criteria: readonly string[] }\n\t| {\n\t\t\ttype: \"boolean\";\n\t\t\tinstructions: string;\n\t\t\tcriteria: { true: string; false: string };\n\t };\n\nexport type Answer =\n\t| {\n\t\t\ttype: \"choice\";\n\t\t\tchoice: string;\n\t\t\tprobabilities: Record<string, number>;\n\t\t\tconfidence: number;\n\t }\n\t| {\n\t\t\ttype: \"score\";\n\t\t\tscore: number;\n\t\t\tprobabilities: Record<string, number>;\n\t\t\tconfidence: number;\n\t }\n\t| { type: \"boolean\"; probability: number; confidence: number }\n\t| { type: \"refusal\" };\n\nexport type Answers<Q extends Record<string, Question>> = {\n\t[K in keyof Q]: Answer;\n};\n\nexport type ClassifyResult<Q extends Record<string, Question>> = {\n\tprovider: ClassifierName;\n\t/** response.modelId when the backend reports one. */\n\tmodelId: string;\n\tanswers: Answers<Q>;\n};\n\n/** Deterministic answers for the offline provider. Return a value per question\n * it can decide; omitted questions come back as `{ type: \"refusal\" }`. */\nexport type RuleValue =\n\t| string /* choice option */\n\t| number /* score level index */\n\t| boolean;\nexport type RulesFn<Q extends Record<string, Question>> = (\n\tstate: string | Record<string, unknown>,\n) => { [K in keyof Q]?: RuleValue } | null;\n\nexport class ClassifierInputError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ClassifierInputError\";\n\t}\n}\n",
15
+ "import { ClassifierInputError, type Question } from \"./types.ts\";\n\n// Strictest common limits across jev, kev and clef.\nexport const MAX_QUESTIONS = 64;\nexport const MIN_CHOICE = 2;\nexport const MAX_CHOICE = 255;\nexport const MIN_SCORE = 2;\nexport const MAX_SCORE = 10;\nexport const QUESTION_ID = /^[A-Za-z0-9_.-]{1,100}$/;\nexport const MAX_STATE_CHARS = 24_000;\n\n/** A bad question definition is a programmer bug: throw, never fail open. */\nexport function validateQuestions(questions: Record<string, Question>): void {\n\tconst ids = Object.keys(questions);\n\tif (ids.length === 0) throw new ClassifierInputError(\"no questions\");\n\tif (ids.length > MAX_QUESTIONS) {\n\t\tthrow new ClassifierInputError(`more than ${MAX_QUESTIONS} questions`);\n\t}\n\tfor (const id of ids) {\n\t\tif (!QUESTION_ID.test(id)) {\n\t\t\tthrow new ClassifierInputError(`invalid question id: ${id}`);\n\t\t}\n\t\tconst q = questions[id] as Question;\n\t\tif (q.type === \"choice\") {\n\t\t\tconst n = Object.keys(q.criteria).length;\n\t\t\tif (n < MIN_CHOICE || n > MAX_CHOICE) {\n\t\t\t\tthrow new ClassifierInputError(\n\t\t\t\t\t`question ${id}: choice needs ${MIN_CHOICE}-${MAX_CHOICE} options`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (q.type === \"score\") {\n\t\t\tconst n = q.criteria.length;\n\t\t\tif (n < MIN_SCORE || n > MAX_SCORE) {\n\t\t\t\tthrow new ClassifierInputError(\n\t\t\t\t\t`question ${id}: score needs ${MIN_SCORE}-${MAX_SCORE} levels`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (q.type !== \"boolean\") {\n\t\t\tthrow new ClassifierInputError(`question ${id}: unknown type`);\n\t\t}\n\t}\n}\n\nexport function stateSize(state: string | Record<string, unknown>): number {\n\treturn typeof state === \"string\"\n\t\t? state.length\n\t\t: JSON.stringify(state).length;\n}\n",
16
+ "import type { Experimental_DecisionModel } from \"ai\"; // type-only: erased at runtime\n\ntype DecisionModelV4 = Extract<\n\tExperimental_DecisionModel,\n\t{ doDecide: unknown }\n>;\ntype DoDecideOptions = Parameters<DecisionModelV4[\"doDecide\"]>[0];\ntype DoDecideResult = Awaited<ReturnType<DecisionModelV4[\"doDecide\"]>>;\n\ntype WireAnswer = {\n\ttype: string;\n\tchoice?: string;\n\tscore?: number;\n\tnoul?: number;\n\tprobabilities?: Record<string, number>;\n};\n\ntype WireBody = {\n\tmodel?: string;\n\tanswers?: Record<string, WireAnswer>;\n\tusage?: { input_tokens?: number; output_tokens?: number };\n};\n\nfunction toWireState(state: DoDecideOptions[\"state\"]): unknown {\n\tconst parts = state.map((p) => {\n\t\tif (p.type === \"text\") return p.text;\n\t\tif (p.type === \"json\") return p.value;\n\t\tthrow new Error(\"file state unsupported\");\n\t});\n\treturn parts.length === 1 ? parts[0] : parts;\n}\n\n/** Divide by the sum so the distribution sums to exactly 1. */\nfunction renormalize(p: Record<string, number>): Record<string, number> {\n\tconst sum = Object.values(p).reduce((a, b) => a + b, 0);\n\tif (!(sum > 0)) return p;\n\treturn Object.fromEntries(Object.entries(p).map(([k, v]) => [k, v / sum]));\n}\n\nfunction mapAnswer(a: WireAnswer): DoDecideResult[\"answers\"][string] {\n\tif (a.type === \"noul\" && typeof a.noul === \"number\") {\n\t\treturn { type: \"boolean\", probability: a.noul };\n\t}\n\tif (a.type === \"choice\" && typeof a.choice === \"string\") {\n\t\treturn {\n\t\t\ttype: \"choice\",\n\t\t\tchoice: a.choice,\n\t\t\t...(a.probabilities && { probabilities: renormalize(a.probabilities) }),\n\t\t};\n\t}\n\tif (a.type === \"score\" && typeof a.score === \"number\") {\n\t\tif (!a.probabilities) return { type: \"score\", score: a.score };\n\t\tconst probabilities = renormalize(a.probabilities);\n\t\t// decide() checks score equals the weighted mean of the distribution.\n\t\tconst score = Object.entries(probabilities).reduce(\n\t\t\t(s, [level, p]) => s + Number(level) * p,\n\t\t\t0,\n\t\t);\n\t\treturn { type: \"score\", score, probabilities };\n\t}\n\treturn { type: \"refusal\" };\n}\n\n/** Adapter for the System One wire protocol spoken by kev and clef. */\nexport function systemOneModel(opts: {\n\tprovider: \"kev\" | \"clef\";\n\turl: string;\n\t/** Sent as body.model (\"kev-latest\", \"clef\", \"clef-flash\"). */\n\tmodelId: string;\n\tapiKey?: string;\n\tfetchImpl?: typeof fetch;\n}): DecisionModelV4 {\n\tconst doFetch = opts.fetchImpl ?? fetch;\n\treturn {\n\t\tspecificationVersion: \"v4\",\n\t\tprovider: `secondlayer.${opts.provider}`,\n\t\tmodelId: opts.modelId,\n\t\tsupportedQuestionTypes: [\"choice\", \"score\", \"boolean\"],\n\t\tasync doDecide({ state, questions, abortSignal }) {\n\t\t\tconst wireQuestions = Object.fromEntries(\n\t\t\t\tObject.entries(questions).map(([id, q]) => [\n\t\t\t\t\tid,\n\t\t\t\t\tq.type === \"boolean\" ? { ...q, type: \"noul\" } : q,\n\t\t\t\t]),\n\t\t\t);\n\t\t\tconst res = await doFetch(opts.url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t...(opts.apiKey && { authorization: `Bearer ${opts.apiKey}` }),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: opts.modelId,\n\t\t\t\t\tstate: toWireState(state),\n\t\t\t\t\tquestions: wireQuestions,\n\t\t\t\t}),\n\t\t\t\tsignal: abortSignal,\n\t\t\t});\n\t\t\t// Never include the body: it may echo state.\n\t\t\tif (!res.ok) throw new Error(`${opts.provider} ${res.status}`);\n\t\t\tconst json = (await res.json()) as { result?: WireBody } & WireBody;\n\t\t\tconst body = json.result ?? json;\n\t\t\tconst answers = Object.fromEntries(\n\t\t\t\tObject.entries(body.answers ?? {}).map(([id, a]) => [id, mapAnswer(a)]),\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tanswers,\n\t\t\t\twarnings: [],\n\t\t\t\tusage: {\n\t\t\t\t\tinputTokens: body.usage?.input_tokens,\n\t\t\t\t\toutputTokens: body.usage?.output_tokens,\n\t\t\t\t},\n\t\t\t\tresponse: { modelId: body.model ?? opts.modelId },\n\t\t\t} as DoDecideResult;\n\t\t},\n\t};\n}\n",
17
+ "import type { Experimental_DecisionModel } from \"ai\"; // type-only: erased at runtime\nimport { logger } from \"../logger.ts\";\nimport { systemOneModel } from \"./system-one.ts\";\nimport type { ClassifierName } from \"./types.ts\";\n\ntype DecisionModelV4 = Extract<\n\tExperimental_DecisionModel,\n\t{ doDecide: unknown }\n>;\ntype Env = Record<string, string | undefined>;\n\nexport const DEFAULT_MODEL: Record<Exclude<ClassifierName, \"rules\">, string> = {\n\tjev: \"typesafe-ai/jev\",\n\tkev: \"kev-latest\",\n\tclef: \"clef\",\n};\n\nconst REQUIRED_ENV: Record<Exclude<ClassifierName, \"rules\">, string[]> = {\n\tjev: [\"AI_GATEWAY_API_KEY\"],\n\tkev: [\"KEV_URL\"],\n\tclef: [\"CLOUDFLARE_ACCOUNT_ID\", \"CLOUDFLARE_API_TOKEN\"],\n};\n\n/** Compose passes CLASSIFIER_MODEL=\"\" when unset; blank means unset. */\nexport const envModel = (env: Env): string | undefined =>\n\tenv.CLASSIFIER_MODEL?.trim() || undefined;\n\n/** CLASSIFIER picks the backend; unset means jev when the gateway key is set,\n * else offline rules. A backend missing credentials falls back to rules. */\nexport function resolveProvider(env: Env = process.env): {\n\tname: ClassifierName;\n\tmodelId: string;\n} {\n\tconst rules = { name: \"rules\", modelId: \"rules\" } as const;\n\tconst requested = env.CLASSIFIER?.trim();\n\tlet name: ClassifierName;\n\tif (!requested) {\n\t\tname = env.AI_GATEWAY_API_KEY ? \"jev\" : \"rules\";\n\t} else if (\n\t\trequested === \"jev\" ||\n\t\trequested === \"kev\" ||\n\t\trequested === \"clef\" ||\n\t\trequested === \"rules\"\n\t) {\n\t\tname = requested;\n\t} else {\n\t\tlogger.warn(`classifier: unknown CLASSIFIER=${requested}; using rules`);\n\t\treturn rules;\n\t}\n\tif (name === \"rules\") return rules;\n\tconst missing = REQUIRED_ENV[name].find((v) => !env[v]);\n\tif (missing) {\n\t\tlogger.warn(\n\t\t\t`classifier: CLASSIFIER=${name} missing ${missing}; using rules`,\n\t\t);\n\t\treturn rules;\n\t}\n\treturn { name, modelId: envModel(env) ?? DEFAULT_MODEL[name] };\n}\n\nexport function buildModel(\n\tname: \"jev\" | \"kev\" | \"clef\",\n\tmodelId: string,\n\tenv: Env = process.env,\n\tfetchImpl?: typeof fetch,\n): string | DecisionModelV4 {\n\t// The AI Gateway resolves a plain model id string.\n\tif (name === \"jev\") return modelId;\n\tif (name === \"kev\") {\n\t\treturn systemOneModel({\n\t\t\tprovider: \"kev\",\n\t\t\turl: `${(env.KEV_URL ?? \"\").replace(/\\/$/, \"\")}/v1/systemone`,\n\t\t\tmodelId,\n\t\t\tapiKey: env.KEV_API_KEY,\n\t\t\tfetchImpl,\n\t\t});\n\t}\n\treturn systemOneModel({\n\t\tprovider: \"clef\",\n\t\turl: `https://api.cloudflare.com/client/v4/accounts/${env.CLOUDFLARE_ACCOUNT_ID}/ai/run/@cf/cloudflare/${modelId}`,\n\t\tmodelId,\n\t\tapiKey: env.CLOUDFLARE_API_TOKEN,\n\t\tfetchImpl,\n\t});\n}\n",
18
+ "import type { Answer, Answers, Question, RuleValue, RulesFn } from \"./types.ts\";\n\nfunction answerFor(question: Question, value: RuleValue | undefined): Answer {\n\tif (value === undefined) return { type: \"refusal\" };\n\tif (question.type === \"choice\") {\n\t\tif (typeof value !== \"string\" || !(value in question.criteria)) {\n\t\t\treturn { type: \"refusal\" };\n\t\t}\n\t\tconst probabilities = Object.fromEntries(\n\t\t\tObject.keys(question.criteria).map((k) => [k, k === value ? 1 : 0]),\n\t\t);\n\t\treturn { type: \"choice\", choice: value, probabilities, confidence: 1 };\n\t}\n\tif (question.type === \"score\") {\n\t\tconst n = question.criteria.length;\n\t\tif (typeof value !== \"number\" || !Number.isInteger(value)) {\n\t\t\treturn { type: \"refusal\" };\n\t\t}\n\t\tif (value < 0 || value >= n) return { type: \"refusal\" };\n\t\tconst probabilities = Object.fromEntries(\n\t\t\tArray.from({ length: n }, (_, i) => [String(i), i === value ? 1 : 0]),\n\t\t);\n\t\treturn { type: \"score\", score: value, probabilities, confidence: 1 };\n\t}\n\tif (typeof value !== \"boolean\") return { type: \"refusal\" };\n\treturn { type: \"boolean\", probability: value ? 1 : 0, confidence: 1 };\n}\n\n/** Offline provider: deterministic, never touches the network. */\nexport function runRules<Q extends Record<string, Question>>(\n\tquestions: Q,\n\tfn: RulesFn<Q>,\n\tstate: string | Record<string, unknown>,\n): Answers<Q> {\n\tconst values = fn(state) ?? {};\n\tconst out: Record<string, Answer> = {};\n\tfor (const id of Object.keys(questions)) {\n\t\tout[id] = answerFor(\n\t\t\tquestions[id] as Question,\n\t\t\t(values as Record<string, RuleValue | undefined>)[id],\n\t\t);\n\t}\n\treturn out as Answers<Q>;\n}\n",
19
+ "import { logger } from \"../logger.ts\";\nimport { type RawAnswer, normalizeAnswer } from \"./confidence.ts\";\nimport { MAX_STATE_CHARS, stateSize, validateQuestions } from \"./limits.ts\";\nimport {\n\tDEFAULT_MODEL,\n\tbuildModel,\n\tenvModel,\n\tresolveProvider,\n} from \"./providers.ts\";\nimport { runRules } from \"./rules.ts\";\nimport type {\n\tAnswers,\n\tClassifierName,\n\tClassifyResult,\n\tQuestion,\n\tRulesFn,\n} from \"./types.ts\";\n\n/**\n * One classifier call over jev, kev, clef or offline rules.\n * Returns null on any provider failure (fail-open: callers decide what null\n * means). Only ClassifierInputError, a bad question definition, escapes.\n * Never logs state or response bodies.\n */\nexport async function classify<\n\tconst Q extends Record<string, Question>,\n>(input: {\n\tstate: string | Record<string, unknown>;\n\tquestions: Q;\n\trules?: RulesFn<Q>;\n\t/** Overrides resolveProvider(); used by classifier-compare. */\n\tprovider?: ClassifierName;\n\t/** Overrides CLASSIFIER_MODEL / the provider default. */\n\tmodelId?: string;\n\t/** Default 5000. */\n\ttimeoutMs?: number;\n\tenv?: Record<string, string | undefined>;\n\t/** Tests only (kev/clef). */\n\tfetchImpl?: typeof fetch;\n}): Promise<ClassifyResult<Q> | null> {\n\tvalidateQuestions(input.questions);\n\tconst env = input.env ?? process.env;\n\tconst resolved = resolveProvider(env);\n\tconst name = input.provider ?? resolved.name;\n\tif (name === \"rules\") {\n\t\tif (!input.rules) return null;\n\t\treturn {\n\t\t\tprovider: \"rules\",\n\t\t\tmodelId: \"rules\",\n\t\t\tanswers: runRules(input.questions, input.rules, input.state),\n\t\t};\n\t}\n\t// An explicit provider override ignores CLASSIFIER_MODEL (it pins one backend).\n\tconst modelId =\n\t\tinput.modelId ??\n\t\t(input.provider ? undefined : envModel(env)) ??\n\t\tDEFAULT_MODEL[name];\n\n\tif (stateSize(input.state) > MAX_STATE_CHARS) {\n\t\tlogger.warn(`classifier: state over ${MAX_STATE_CHARS} chars; skipped`);\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst { experimental_decide: decide } = await import(\"ai\");\n\t\tconst r = await decide({\n\t\t\tmodel: buildModel(name, modelId, env, input.fetchImpl),\n\t\t\tstate: input.state as string,\n\t\t\t// Our Question is a structural subset of the SDK's question type.\n\t\t\tquestions: input.questions as Parameters<typeof decide>[0][\"questions\"],\n\t\t\tmaxRetries: 1,\n\t\t\tabortSignal: AbortSignal.timeout(input.timeoutMs ?? 5000),\n\t\t});\n\t\tconst answers: Record<string, ReturnType<typeof normalizeAnswer>> = {};\n\t\tfor (const id of Object.keys(input.questions)) {\n\t\t\tanswers[id] = normalizeAnswer(\n\t\t\t\tinput.questions[id] as Question,\n\t\t\t\t(r.answers as Record<string, RawAnswer>)[id] ?? { type: \"refusal\" },\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\tprovider: name,\n\t\t\tmodelId: r.response.modelId ?? modelId,\n\t\t\tanswers: answers as Answers<Q>,\n\t\t};\n\t} catch (err) {\n\t\tlogger.warn(\n\t\t\t`classifier: ${name} failed: ${err instanceof Error ? err.name : \"error\"}`,\n\t\t);\n\t\treturn null;\n\t}\n}\n",
20
+ "import { classify } from \"../classify/index.ts\";\nimport type { Question } from \"../classify/index.ts\";\nimport { logger } from \"../logger.ts\";\nimport type { DetectedInsight } from \"./types.ts\";\n\n/**\n * Optional cause label. Code finds every insight and writes every sentence;\n * the model may only pick one cause from a fixed list, and the label shows\n * only at confidence 0.85 or higher. Off unless INSIGHTS_MODEL_LABELS=on, and\n * turning it on is gated on the insights-layer spike (plan 054).\n */\n\nexport const CAUSE_CONFIDENCE = 0.85;\nconst LABEL_TIMEOUT_MS = 5000;\nconst CACHE_TTL_MS = 6 * 3600_000;\n\nexport const CAUSE_QUESTIONS = {\n\tcause: {\n\t\ttype: \"choice\",\n\t\tinstructions: \"Which cause best explains this webhook's change?\",\n\t\tcriteria: {\n\t\t\treceiver_down:\n\t\t\t\t\"receiver unreachable: 5xx from a proxy, connection refused, timeouts\",\n\t\t\treceiver_rate_limiting: \"receiver answers 429 or slows under load\",\n\t\t\treceiver_rejects_payload: \"4xx other than 429: auth, schema, signature\",\n\t\t\treceiver_slow: \"2xx but response time well above baseline\",\n\t\t\ttls_or_dns: \"certificate or name resolution errors\",\n\t\t\tchain_quiet: \"matching events dropped as much as deliveries\",\n\t\t\tconfig_change: \"change began right after the webhook's settings changed\",\n\t\t\tour_side: \"delivery service or chain source is behind (cursor lag)\",\n\t\t\tunknown: \"none of these fit the evidence\",\n\t\t},\n\t},\n} as const satisfies Record<string, Question>;\n\ntype LabelOutcome = { cause: string; confidence: number } | null;\ntype LabelCache = Map<string, { at: number; outcome: LabelOutcome }>;\n\nconst defaultCache: LabelCache = new Map();\n\nexport function resetLabelCache(): void {\n\tdefaultCache.clear();\n}\n\nexport interface LabelDeps {\n\tclassifyFn?: typeof classify;\n\tenv?: Record<string, string | undefined>;\n\tnow?: number;\n\tcache?: LabelCache;\n}\n\nfunction cacheKey(insight: DetectedInsight): string {\n\tconst codes = insight.evidence\n\t\t.flatMap((e) => (e.kind === \"shapes\" ? e.shapes.map((s) => s.code) : []))\n\t\t.sort()\n\t\t.join(\",\");\n\treturn `${insight.resource.kind}:${insight.resource.id}:${insight.rule}:${codes}`;\n}\n\nfunction renderState(insight: DetectedInsight): string {\n\treturn [\n\t\t`${insight.resource.kind} ${insight.resource.name}`,\n\t\t`rule: ${insight.rule}`,\n\t\t`detector: ${insight.how.detector}`,\n\t\t...insight.how.facts.map(([k, v]) => `${k}: ${v}`),\n\t].join(\"\\n\");\n}\n\nexport async function maybeLabel(\n\tinsight: DetectedInsight,\n\tdeps: LabelDeps = {},\n): Promise<DetectedInsight> {\n\tconst env = deps.env ?? process.env;\n\tif (env.INSIGHTS_MODEL_LABELS !== \"on\") return insight;\n\tif (!insight.rule.startsWith(\"receiver_\")) return insight;\n\n\tconst now = deps.now ?? Date.now();\n\tconst cache = deps.cache ?? defaultCache;\n\tconst key = cacheKey(insight);\n\tconst hit = cache.get(key);\n\tlet outcome: LabelOutcome;\n\n\tif (hit && now - hit.at < CACHE_TTL_MS) {\n\t\toutcome = hit.outcome;\n\t} else {\n\t\tconst result = await (deps.classifyFn ?? classify)({\n\t\t\tstate: renderState(insight),\n\t\t\tquestions: CAUSE_QUESTIONS,\n\t\t\ttimeoutMs: LABEL_TIMEOUT_MS,\n\t\t\tenv,\n\t\t}).catch((err: unknown) => {\n\t\t\tlogger.warn(\"insights.label_failed\", { error: String(err) });\n\t\t\treturn null;\n\t\t});\n\t\t// Provider down: leave the insight alone and try again next pass.\n\t\tif (!result) return insight;\n\t\tconst answer = result.answers.cause;\n\t\toutcome =\n\t\t\tanswer.type === \"choice\" &&\n\t\t\tanswer.confidence >= CAUSE_CONFIDENCE &&\n\t\t\tanswer.choice !== \"unknown\"\n\t\t\t\t? { cause: answer.choice, confidence: answer.confidence }\n\t\t\t\t: null;\n\t\tcache.set(key, { at: now, outcome });\n\t}\n\n\tif (!outcome) return insight;\n\treturn { ...insight, how: { ...insight.how, label: outcome } };\n}\n",
21
+ "import { type Kysely, sql } from \"kysely\";\nimport type { Database } from \"../db/types.ts\";\nimport { type HealthSnapshotPoint, bucketHourly } from \"../subgraphs/health.ts\";\nimport { handoffPrompt, plainSnippet, plural } from \"./prompt.ts\";\nimport {\n\tHANDLER_ERRORS_MIN_24H,\n\tSUBGRAPH_LAG_BLOCKS,\n\tSUBGRAPH_LAG_LOSING_BLOCKS,\n\tSUBGRAPH_LAG_SNAPSHOTS,\n} from \"./thresholds.ts\";\nimport type { InsightRule } from \"./types.ts\";\n\nconst HOUR_MS = 3_600_000;\nconst ERROR_GROUPS_SHOWN = 3;\n\nexport interface SubgraphErrorGroupInput {\n\tmessage: string;\n\teventType: string | null;\n\tsourceName: string;\n\t/** All-time count for the group (not limited to the last 24 hours). */\n\tcount: number;\n\tlastSeen: Date;\n}\n\nexport interface SubgraphRuleInput {\n\tnow: Date;\n\tsubgraph: { id: string; name: string; status: string };\n\t/** Health snapshots from roughly the last 25 hours. */\n\tsnapshots: HealthSnapshotPoint[];\n\t/** Groups seen in the last 24 hours, most frequent first. */\n\terrorGroups: SubgraphErrorGroupInput[];\n}\n\nfunction subject(name: string): string {\n\treturn `My Secondlayer subgraph \"${name}\"`;\n}\n\nfunction resourceOf(input: SubgraphRuleInput) {\n\treturn {\n\t\tkind: \"subgraph\" as const,\n\t\tid: input.subgraph.name,\n\t\tname: input.subgraph.name,\n\t};\n}\n\nconst handlerErrors: InsightRule<SubgraphRuleInput> = {\n\tid: \"subgraph_handler_errors\",\n\tskip: (input) => input.snapshots.length === 0,\n\tevaluate(input) {\n\t\tconst hours = bucketHourly(input.snapshots, 24, input.now.getTime());\n\t\tconst errors = hours.reduce((sum, h) => sum + h.errors, 0);\n\t\tif (errors < HANDLER_ERRORS_MIN_24H) return null;\n\n\t\tconst { subgraph } = input;\n\t\tconst top = input.errorGroups.slice(0, ERROR_GROUPS_SHOWN);\n\t\tconst leader = top[0];\n\t\tconst eventTypes = [\n\t\t\t...new Set(top.map((g) => g.eventType).filter((t): t is string => !!t)),\n\t\t];\n\t\tconst leaderText = leader ? plainSnippet(leader.message, 120) : \"\";\n\n\t\treturn {\n\t\t\trule: \"subgraph_handler_errors\",\n\t\t\tseverity: \"losing\",\n\t\t\timpact: errors,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${subgraph.name} threw ${plural(errors, \"handler error\")} today`,\n\t\t\tmeta: \"last 24 hours\",\n\t\t\tlead: \"Your handler is failing on some events.\",\n\t\t\tbody: [\n\t\t\t\t`${plural(errors, \"event\")} failed inside your handler in the last 24 hours, and those rows were not written.`,\n\t\t\t\tleader\n\t\t\t\t\t? `The most common error is: ${leaderText}${eventTypes.length > 0 ? ` (event type ${eventTypes[0]})` : \"\"}.`\n\t\t\t\t\t: \"\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\tevidence:\n\t\t\t\ttop.length > 0\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkind: \"table\",\n\t\t\t\t\t\t\t\ttitle: \"Most common errors\",\n\t\t\t\t\t\t\t\tcolumns: [\"Error\", \"Event type\", \"Seen\"],\n\t\t\t\t\t\t\t\trows: top.map((g) => [\n\t\t\t\t\t\t\t\t\tplainSnippet(g.message, 120),\n\t\t\t\t\t\t\t\t\tg.eventType ?? g.sourceName,\n\t\t\t\t\t\t\t\t\tg.count,\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: [],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(subgraph.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`threw ${plural(errors, \"handler error\")} in the last 24 hours`,\n\t\t\t\t\t\tleader\n\t\t\t\t\t\t\t? `The most common error is: ${leaderText}${eventTypes[0] ? ` on event type ${eventTypes[0]}` : \"\"}, seen ${plural(leader.count, \"time\")} in total`\n\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t],\n\t\t\t\t\tlookAt:\n\t\t\t\t\t\teventTypes.length > 0\n\t\t\t\t\t\t\t? `Check how the handler deals with ${eventTypes.join(\" and \")} events`\n\t\t\t\t\t\t\t: \"Check what the failing events have in common\",\n\t\t\t\t\tfollowUp: `Then run sl subgraphs gaps ${subgraph.name}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [`sl subgraphs gaps ${subgraph.name}`],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `${HANDLER_ERRORS_MIN_24H} or more handler errors in the last 24 hours of health history`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"errors in 24 hours\", String(errors)],\n\t\t\t\t\t[\n\t\t\t\t\t\t\"event types in top errors\",\n\t\t\t\t\t\teventTypes.join(\", \") || \"none recorded\",\n\t\t\t\t\t],\n\t\t\t\t\t[\"group counts\", \"all-time per group, not limited to 24 hours\"],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nconst lag: InsightRule<SubgraphRuleInput> = {\n\tid: \"subgraph_lag\",\n\tskip: (input) => lagSamples(input).length < SUBGRAPH_LAG_SNAPSHOTS,\n\tevaluate(input) {\n\t\tif (input.subgraph.status !== \"active\") return null;\n\t\tconst newest = lagSamples(input).slice(0, SUBGRAPH_LAG_SNAPSHOTS);\n\t\tif (newest.length < SUBGRAPH_LAG_SNAPSHOTS) return null;\n\t\tif (!newest.every((s) => s.blocks > SUBGRAPH_LAG_BLOCKS)) return null;\n\n\t\tconst current = newest[0]?.blocks ?? 0;\n\t\tconst since = newest[newest.length - 1]?.at ?? input.now;\n\t\tconst minutes = Math.max(\n\t\t\t1,\n\t\t\tMath.round((input.now.getTime() - since.getTime()) / 60_000),\n\t\t);\n\t\tconst { subgraph } = input;\n\t\tconst hours = bucketHourly(input.snapshots, 24, input.now.getTime());\n\n\t\treturn {\n\t\t\trule: \"subgraph_lag\",\n\t\t\tseverity: current > SUBGRAPH_LAG_LOSING_BLOCKS ? \"losing\" : \"will_fail\",\n\t\t\timpact: current,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${subgraph.name} is ${plural(current, \"block\")} behind`,\n\t\t\tmeta: `for about ${plural(minutes, \"minute\")}`,\n\t\t\tlead: \"Your subgraph is falling behind the chain.\",\n\t\t\tbody: `It has trailed the chain tip by more than ${SUBGRAPH_LAG_BLOCKS} blocks on its last ${SUBGRAPH_LAG_SNAPSHOTS} checks, and is ${plural(current, \"block\")} behind now. Rows are late until it catches up.`,\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"series\",\n\t\t\t\t\ttitle: \"Blocks behind the chain, per hour\",\n\t\t\t\t\tseries: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: \"Blocks behind\",\n\t\t\t\t\t\t\trole: \"a\",\n\t\t\t\t\t\t\tpoints: hours.map((h) => h.lagBlocks ?? 0),\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tlabels: [\n\t\t\t\t\t\t[0, \"24h ago\"],\n\t\t\t\t\t\t[hours.length - 1, \"now\"],\n\t\t\t\t\t],\n\t\t\t\t\tunit: \"blocks\",\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(subgraph.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`has been more than ${SUBGRAPH_LAG_BLOCKS} blocks behind the chain for about ${plural(minutes, \"minute\")}`,\n\t\t\t\t\t\t`It is ${plural(current, \"block\")} behind right now`,\n\t\t\t\t\t],\n\t\t\t\t\tlookAt:\n\t\t\t\t\t\t\"Check whether the handler got slower, a recent deploy changed how much work each block needs, or the subgraph is processing a very large range\",\n\t\t\t\t\tfollowUp: `Then run sl subgraphs status ${subgraph.name}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [`sl subgraphs status ${subgraph.name}`],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `more than ${SUBGRAPH_LAG_BLOCKS} blocks behind the tip on ${SUBGRAPH_LAG_SNAPSHOTS} consecutive health snapshots, and not reindexing`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"blocks behind now\", String(current)],\n\t\t\t\t\t[\"snapshots over the line\", String(newest.length)],\n\t\t\t\t\t[\"behind since about\", `${since.toISOString().slice(11, 16)} UTC`],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\n/** Per-snapshot lag, newest first, for snapshots that know both positions. */\nfunction lagSamples(input: SubgraphRuleInput): { at: Date; blocks: number }[] {\n\treturn input.snapshots\n\t\t.map((s) => ({\n\t\t\tat: new Date(s.captured_at),\n\t\t\ttip: s.chain_tip === null ? null : Number(s.chain_tip),\n\t\t\tcursor:\n\t\t\t\ts.last_processed_block === null ? null : Number(s.last_processed_block),\n\t\t}))\n\t\t.filter(\n\t\t\t(s): s is { at: Date; tip: number; cursor: number } =>\n\t\t\t\ts.tip !== null && s.cursor !== null,\n\t\t)\n\t\t.sort((a, b) => b.at.getTime() - a.at.getTime())\n\t\t.map((s) => ({ at: s.at, blocks: Math.max(0, s.tip - s.cursor) }));\n}\n\nexport const SUBGRAPH_RULES: InsightRule<SubgraphRuleInput>[] = [\n\thandlerErrors,\n\tlag,\n];\n\n/** Health history and error groups for every subgraph, in three queries. */\nexport async function loadSubgraphInputs(\n\tdb: Kysely<Database>,\n\tnow: Date,\n): Promise<SubgraphRuleInput[]> {\n\tconst subgraphs = await db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.select([\"id\", \"name\", \"status\"])\n\t\t.execute();\n\tif (subgraphs.length === 0) return [];\n\n\tconst since = new Date(now.getTime() - 25 * HOUR_MS);\n\tconst snapshots = await db\n\t\t.selectFrom(\"subgraph_health_snapshots\")\n\t\t.select([\n\t\t\t\"subgraph_id\",\n\t\t\t\"captured_at\",\n\t\t\t\"chain_tip\",\n\t\t\t\"last_processed_block\",\n\t\t\t\"rows_written_total\",\n\t\t\t\"total_errors\",\n\t\t\t\"total_processed\",\n\t\t])\n\t\t.where(\"captured_at\", \">=\", since)\n\t\t.orderBy(\"captured_at\", \"asc\")\n\t\t.execute();\n\n\tconst groups = await sql<{\n\t\tsubgraph_name: string;\n\t\tmessage: string;\n\t\tevent_type: string | null;\n\t\tsource_name: string;\n\t\tcount: string | number;\n\t\tlast_seen: Date;\n\t}>`\n\t\tSELECT subgraph_name, message, event_type, source_name, count, last_seen\n\t\tFROM subgraph_error_groups\n\t\tWHERE last_seen > ${new Date(now.getTime() - 24 * HOUR_MS)}\n\t\tORDER BY count DESC\n\t`.execute(db);\n\n\treturn subgraphs.map((subgraph) => ({\n\t\tnow,\n\t\tsubgraph,\n\t\tsnapshots: snapshots.filter((s) => s.subgraph_id === subgraph.id),\n\t\terrorGroups: groups.rows\n\t\t\t.filter((g) => g.subgraph_name === subgraph.name)\n\t\t\t.slice(0, ERROR_GROUPS_SHOWN)\n\t\t\t.map((g) => ({\n\t\t\t\tmessage: g.message,\n\t\t\t\teventType: g.event_type,\n\t\t\t\tsourceName: g.source_name,\n\t\t\t\tcount: Number(g.count),\n\t\t\t\tlastSeen: g.last_seen,\n\t\t\t})),\n\t}));\n}\n",
22
+ "/** Pure hourly bucketing of subgraph health snapshots, used by the API health route. */\n\nconst HOUR_MS = 3_600_000;\n\nexport interface HealthSnapshotPoint {\n\tcaptured_at: Date | string;\n\tchain_tip: string | number | null;\n\tlast_processed_block: string | number | null;\n\trows_written_total: string | number | null;\n\ttotal_errors: string | number;\n\ttotal_processed: string | number;\n}\n\nexport interface HealthHour {\n\thour: string;\n\tlagBlocks: number | null;\n\trowsWritten: number;\n\terrors: number;\n\teventsHandled: number;\n}\n\n/** Sum of positive consecutive differences; a counter reset counts as 0. */\nfunction positiveDeltas(values: (number | null)[]): number {\n\tlet total = 0;\n\tlet prev: number | null = null;\n\tfor (const value of values) {\n\t\tif (value === null) continue;\n\t\tif (prev !== null && value > prev) total += value - prev;\n\t\tprev = value;\n\t}\n\treturn total;\n}\n\nfunction toNumber(value: string | number | null): number | null {\n\tif (value === null) return null;\n\tconst n = Number(value);\n\treturn Number.isFinite(n) ? n : null;\n}\n\n/**\n * Bucket snapshots into the last `hours` UTC hours (oldest first, ending with\n * the hour containing `now`). The delta for an hour also spans the boundary\n * from the previous hour's last snapshot, so no growth falls between buckets.\n */\nexport function bucketHourly(\n\tsnapshots: HealthSnapshotPoint[],\n\thours: number,\n\tnow: number = Date.now(),\n): HealthHour[] {\n\tconst sorted = snapshots\n\t\t.map((s) => ({ ...s, at: new Date(s.captured_at).getTime() }))\n\t\t.sort((a, b) => a.at - b.at);\n\n\tconst currentHourStart = Math.floor(now / HOUR_MS) * HOUR_MS;\n\tconst out: HealthHour[] = [];\n\n\tfor (let i = hours - 1; i >= 0; i--) {\n\t\tconst start = currentHourStart - i * HOUR_MS;\n\t\tconst end = start + HOUR_MS;\n\t\tconst inHour = sorted.filter((s) => s.at >= start && s.at < end);\n\n\t\tif (inHour.length === 0) {\n\t\t\tout.push({\n\t\t\t\thour: new Date(start).toISOString(),\n\t\t\t\tlagBlocks: null,\n\t\t\t\trowsWritten: 0,\n\t\t\t\terrors: 0,\n\t\t\t\teventsHandled: 0,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Carry the last snapshot before this hour so boundary growth counts.\n\t\tconst before = sorted.filter((s) => s.at < start).at(-1);\n\t\tconst series = before ? [before, ...inHour] : inHour;\n\n\t\tconst lags = inHour\n\t\t\t.map((s) => {\n\t\t\t\tconst tip = toNumber(s.chain_tip);\n\t\t\t\tconst cursor = toNumber(s.last_processed_block);\n\t\t\t\treturn tip === null || cursor === null\n\t\t\t\t\t? null\n\t\t\t\t\t: Math.max(0, tip - cursor);\n\t\t\t})\n\t\t\t.filter((lag): lag is number => lag !== null);\n\n\t\tout.push({\n\t\t\thour: new Date(start).toISOString(),\n\t\t\tlagBlocks: lags.length > 0 ? Math.max(...lags) : null,\n\t\t\trowsWritten: positiveDeltas(\n\t\t\t\tseries.map((s) => toNumber(s.rows_written_total)),\n\t\t\t),\n\t\t\terrors: positiveDeltas(series.map((s) => toNumber(s.total_errors))),\n\t\t\teventsHandled: positiveDeltas(\n\t\t\t\tseries.map((s) => toNumber(s.total_processed)),\n\t\t\t),\n\t\t});\n\t}\n\n\treturn out;\n}\n",
23
+ "import { type Kysely, sql } from \"kysely\";\nimport type { Database } from \"../db/types.ts\";\nimport type { InsightEvidence } from \"../schemas/insights.ts\";\nimport {\n\thandoffPrompt,\n\thostOf,\n\tplainSnippet,\n\tplural,\n\tutcDateTime,\n\tutcTime,\n} from \"./prompt.ts\";\nimport {\n\tDEAD_LETTER_DELETE_AGE_DAYS,\n\tDEAD_LETTER_WARN_AGE_DAYS,\n\tDOWN_MIN_CONSECUTIVE,\n\tLOGIN_WALL_MIN_CONSECUTIVE,\n\tLOGIN_WALL_MIN_PRIOR,\n\tLOGIN_WALL_PRIOR_JSON_RATIO,\n\tRATE_LIMITED_MIN_ATTEMPTS,\n\tRATE_LIMITED_MIN_RATIO,\n\tREJECTS_MIN_CONSECUTIVE,\n\tSLOWING_DAYS,\n\tSLOWING_MIN_R2,\n\tSLOWING_MIN_RISING_DAYS,\n\tTEST_ENDPOINT_MIN_AGE_DAYS,\n\tTLS_WARN_DAYS,\n} from \"./thresholds.ts\";\nimport type { DetectedInsight, InsightRule } from \"./types.ts\";\n\nconst MINUTE_MS = 60_000;\nconst DAY_MS = 86_400_000;\nconst BUCKET_MS = 10 * MINUTE_MS;\nconst BUCKETS = 36;\nconst SHAPE_BODY_MAX = 80;\nconst SHAPES_MAX = 4;\nconst ATTEMPT_WINDOW = 300;\nconst ATTEMPT_LOOKBACK_DAYS = 8;\nconst PRIOR_WINDOW_MS = 7 * DAY_MS;\n\n// ── Inputs ─────────────────────────────────────────────────────────────\n\nexport interface DeliveryAttempt {\n\toutboxId: string | null;\n\tstatusCode: number | null;\n\tdurationMs: number | null;\n\tdispatchedAt: Date;\n\terrorMessage: string | null;\n\t/** First 200 characters of the receiver's response body. */\n\tbody: string | null;\n\tcontentType: string | null;\n\tserver: string | null;\n\tretryAfter: string | null;\n\tblockHeight: number | null;\n}\n\nexport interface WebhookRuleInput {\n\tnow: Date;\n\twebhook: {\n\t\tid: string;\n\t\tname: string;\n\t\tstatus: string;\n\t\turl: string;\n\t\ttriggers: unknown;\n\t\ttimeoutMs: number;\n\t\tcreatedAt: Date;\n\t\tlastSuccessAt: Date | null;\n\t\treceiverCertExpiresAt: Date | null;\n\t\treceiverCertCheckedAt: Date | null;\n\t};\n\t/** Newest first, last 8 days, at most 300. */\n\tattempts: DeliveryAttempt[];\n\t/** Outbox rows still waiting to be delivered. */\n\tpending: number;\n\t/** 36 ten-minute buckets ending at `startMs + 36 * 10min`. */\n\tthroughput: { startMs: number; matched: number[]; delivered: number[] };\n\t/** Last 14 UTC days, oldest first; null when no timed delivery that day. */\n\tdailyP95: { day: string; p95Ms: number | null }[];\n\tdead: {\n\t\tcount: number;\n\t\toldestAt: Date | null;\n\t\toldestOutboxId: string | null;\n\t};\n\tdeliveries30d: number;\n\t/** Other non-paused webhooks, for the duplicate check. */\n\tsiblings: {\n\t\tid: string;\n\t\tname: string;\n\t\turl: string;\n\t\ttriggers: unknown;\n\t\tcreatedAt: Date;\n\t}[];\n\t/**\n\t * Function names by contract id, only for contracts whose ABI was fetched.\n\t * `abiAvailable` is false when the lookup is off (no hosted read key) or\n\t * failed, which makes `filter_cant_match` skip.\n\t */\n\tabi: Record<string, string[]>;\n\tabiAvailable: boolean;\n}\n\n// ── Small helpers ──────────────────────────────────────────────────────\n\nfunction consecutiveFromStart(\n\trows: DeliveryAttempt[],\n\tpredicate: (row: DeliveryAttempt) => boolean,\n): DeliveryAttempt[] {\n\tconst out: DeliveryAttempt[] = [];\n\tfor (const row of rows) {\n\t\tif (!predicate(row)) break;\n\t\tout.push(row);\n\t}\n\treturn out;\n}\n\nconst is2xx = (d: DeliveryAttempt) =>\n\td.statusCode !== null && d.statusCode >= 200 && d.statusCode < 300;\nconst isDown = (d: DeliveryAttempt) =>\n\td.statusCode === null || d.statusCode >= 500;\nconst isReject = (d: DeliveryAttempt) =>\n\td.statusCode !== null &&\n\td.statusCode >= 400 &&\n\td.statusCode < 500 &&\n\td.statusCode !== 429;\nconst isHtml = (d: DeliveryAttempt) =>\n\t(d.contentType ?? \"\").toLowerCase().startsWith(\"text/html\");\nconst isJson = (d: DeliveryAttempt) =>\n\t(d.contentType ?? \"\").toLowerCase().startsWith(\"application/json\");\n\nfunction codeLabel(statusCode: number | null): string {\n\treturn statusCode === null ? \"no response\" : String(statusCode);\n}\n\nfunction shapeText(d: DeliveryAttempt): string {\n\treturn (d.body ?? d.errorMessage ?? \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim()\n\t\t.slice(0, SHAPE_BODY_MAX);\n}\n\nfunction shapeKey(d: DeliveryAttempt): string {\n\treturn `${codeLabel(d.statusCode)}\\u0000${shapeText(d)}`;\n}\n\n/** Distinct (status, body) shapes, most common first. `isNew` = not seen in\n * the 7 days before these attempts. */\nfunction buildShapes(\n\taffected: DeliveryAttempt[],\n\tearlier: DeliveryAttempt[],\n\tnow: Date,\n): Extract<InsightEvidence, { kind: \"shapes\" }>[\"shapes\"] {\n\tconst seenBefore = new Set(\n\t\tearlier\n\t\t\t.filter(\n\t\t\t\t(d) => now.getTime() - d.dispatchedAt.getTime() <= PRIOR_WINDOW_MS,\n\t\t\t)\n\t\t\t.map(shapeKey),\n\t);\n\tconst counts = new Map<string, { sample: DeliveryAttempt; count: number }>();\n\tfor (const d of affected) {\n\t\tconst key = shapeKey(d);\n\t\tconst entry = counts.get(key);\n\t\tif (entry) entry.count++;\n\t\telse counts.set(key, { sample: d, count: 1 });\n\t}\n\treturn [...counts.entries()]\n\t\t.sort((a, b) => b[1].count - a[1].count)\n\t\t.slice(0, SHAPES_MAX)\n\t\t.map(([key, { sample, count }]) => ({\n\t\t\tcode: codeLabel(sample.statusCode),\n\t\t\tbody: shapeText(sample),\n\t\t\tcount,\n\t\t\tisNew: !seenBefore.has(key),\n\t\t}));\n}\n\nfunction subject(name: string): string {\n\treturn `My Secondlayer webhook \"${name}\"`;\n}\n\nfunction mostCommonCode(rows: DeliveryAttempt[]): number | null {\n\tconst counts = new Map<number | null, number>();\n\tfor (const r of rows)\n\t\tcounts.set(r.statusCode, (counts.get(r.statusCode) ?? 0) + 1);\n\treturn [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;\n}\n\nfunction breakdown(rows: DeliveryAttempt[]): string {\n\tconst counts = new Map<string, number>();\n\tfor (const r of rows) {\n\t\tconst key = codeLabel(r.statusCode);\n\t\tcounts.set(key, (counts.get(key) ?? 0) + 1);\n\t}\n\treturn [...counts.entries()].map(([k, n]) => `${n}x ${k}`).join(\", \");\n}\n\nfunction attemptsLast24h(input: WebhookRuleInput): number {\n\tconst since = input.now.getTime() - DAY_MS;\n\treturn input.attempts.filter((a) => a.dispatchedAt.getTime() >= since).length;\n}\n\nfunction resourceOf(input: WebhookRuleInput) {\n\treturn {\n\t\tkind: \"webhook\" as const,\n\t\tid: input.webhook.id,\n\t\tname: input.webhook.name,\n\t};\n}\n\nfunction throughputEvidence(\n\tinput: WebhookRuleInput,\n): Extract<InsightEvidence, { kind: \"series\" }> {\n\tconst { startMs, matched, delivered } = input.throughput;\n\tconst at = (i: number) =>\n\t\tutcTime(new Date(startMs + i * BUCKET_MS)).slice(0, 5);\n\treturn {\n\t\tkind: \"series\",\n\t\ttitle: \"Matched and delivered, every 10 minutes\",\n\t\tseries: [\n\t\t\t{ name: \"Matched\", role: \"context\", points: matched },\n\t\t\t{ name: \"Delivered\", role: \"a\", points: delivered },\n\t\t],\n\t\tlabels: [\n\t\t\t[0, at(0)],\n\t\t\t[Math.floor(BUCKETS / 2), at(Math.floor(BUCKETS / 2))],\n\t\t\t[BUCKETS - 1, \"now\"],\n\t\t],\n\t\tunit: \"events\",\n\t};\n}\n\nfunction matchedPerHour(input: WebhookRuleInput): number {\n\tconst total = input.throughput.matched.reduce((a, b) => a + b, 0);\n\treturn Math.round(total / (BUCKETS / 6));\n}\n\nfunction levenshtein(a: string, b: string): number {\n\tconst prev = Array.from({ length: b.length + 1 }, (_, j) => j);\n\tfor (let i = 1; i <= a.length; i++) {\n\t\tlet diagonal = prev[0] ?? 0;\n\t\tprev[0] = i;\n\t\tfor (let j = 1; j <= b.length; j++) {\n\t\t\tconst above = prev[j] ?? 0;\n\t\t\tprev[j] = Math.min(\n\t\t\t\tabove + 1,\n\t\t\t\t(prev[j - 1] ?? 0) + 1,\n\t\t\t\tdiagonal + (a[i - 1] === b[j - 1] ? 0 : 1),\n\t\t\t);\n\t\t\tdiagonal = above;\n\t\t}\n\t}\n\treturn prev[b.length] ?? 0;\n}\n\n/** JSON with object keys sorted at every level, for equality checks. */\nexport function canonicalJson(value: unknown): string {\n\tif (Array.isArray(value)) return `[${value.map(canonicalJson).join(\",\")}]`;\n\tif (value && typeof value === \"object\") {\n\t\tconst entries = Object.entries(value as Record<string, unknown>)\n\t\t\t.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n\t\t\t.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`);\n\t\treturn `{${entries.join(\",\")}}`;\n\t}\n\treturn JSON.stringify(value) ?? \"null\";\n}\n\n// ── Rules ──────────────────────────────────────────────────────────────\n\nconst receiverDown: InsightRule<WebhookRuleInput> = {\n\tid: \"receiver_down\",\n\tevaluate(input) {\n\t\tconst streak = consecutiveFromStart(input.attempts, isDown);\n\t\tif (streak.length < DOWN_MIN_CONSECUTIVE) return null;\n\n\t\tconst { webhook } = input;\n\t\tconst since = streak[streak.length - 1]?.dispatchedAt ?? input.now;\n\t\tconst host = hostOf(webhook.url);\n\t\tconst code = mostCommonCode(streak);\n\t\tconst server = streak.find((d) => d.server)?.server ?? null;\n\t\tconst answer =\n\t\t\tcode === null\n\t\t\t\t? \"did not answer\"\n\t\t\t\t: `returns HTTP ${code}${server ? ` from ${plainSnippet(server, 30)}` : \"\"}`;\n\t\tconst hourly = matchedPerHour(input);\n\t\tconst queued = input.pending;\n\t\tconst flowing =\n\t\t\thourly > 0\n\t\t\t\t? `Chain matches are normal (about ${hourly} an hour), so the problem is the receiver.`\n\t\t\t\t: \"\";\n\n\t\tconst shapes = buildShapes(\n\t\t\tstreak,\n\t\t\tinput.attempts.slice(streak.length),\n\t\t\tinput.now,\n\t\t);\n\n\t\treturn {\n\t\t\trule: \"receiver_down\",\n\t\t\tseverity: \"losing\",\n\t\t\timpact: queued > 0 ? queued : streak.length,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${webhook.name} has failed ${streak.length} deliveries in a row`,\n\t\t\tmeta: `since ${utcTime(since)}`,\n\t\t\tlead: \"Your receiver is down, not us.\",\n\t\t\tbody: [\n\t\t\t\t`${host} ${answer} on every delivery since ${utcTime(since)}.`,\n\t\t\t\tqueued > 0 ? `${plural(queued, \"delivery\")} queued to retry.` : \"\",\n\t\t\t\tflowing,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\tevidence: [\n\t\t\t\tthroughputEvidence(input),\n\t\t\t\t{ kind: \"shapes\", title: \"What the receiver sent back\", shapes },\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`has been failing since ${utcTime(since)}`,\n\t\t\t\t\t\t`The receiver ${host} ${answer} on every delivery`,\n\t\t\t\t\t\tqueued > 0 ? `${plural(queued, \"delivery\")} queued to retry` : \"\",\n\t\t\t\t\t\tflowing.replace(/\\.$/, \"\"),\n\t\t\t\t\t],\n\t\t\t\t\tlookAt: `Check the service behind ${host}`,\n\t\t\t\t\tfollowUp: `Then run sl webhooks doctor ${webhook.name}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [\n\t\t\t\t\t`sl webhooks doctor ${webhook.name}`,\n\t\t\t\t\t`sl webhooks dead ${webhook.name}`,\n\t\t\t\t],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `the newest ${DOWN_MIN_CONSECUTIVE} or more delivery attempts are 5xx or got no response`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"consecutive failures\", String(streak.length)],\n\t\t\t\t\t[\"breakdown\", breakdown(streak)],\n\t\t\t\t\t[\"first failure\", utcDateTime(since)],\n\t\t\t\t\t[\n\t\t\t\t\t\t\"last success\",\n\t\t\t\t\t\twebhook.lastSuccessAt\n\t\t\t\t\t\t\t? utcDateTime(webhook.lastSuccessAt)\n\t\t\t\t\t\t\t: \"never\",\n\t\t\t\t\t],\n\t\t\t\t\t[\"queued retries\", String(queued)],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nconst receiverRejects: InsightRule<WebhookRuleInput> = {\n\tid: \"receiver_rejects\",\n\tevaluate(input) {\n\t\tconst streak = consecutiveFromStart(input.attempts, isReject);\n\t\tif (streak.length < REJECTS_MIN_CONSECUTIVE) return null;\n\n\t\tconst { webhook } = input;\n\t\tconst host = hostOf(webhook.url);\n\t\tconst code = mostCommonCode(streak);\n\t\tconst since = streak[streak.length - 1]?.dispatchedAt ?? input.now;\n\t\tconst text = plainSnippet(\n\t\t\tstreak.find((d) => d.body || d.errorMessage)?.body ??\n\t\t\t\tstreak.find((d) => d.errorMessage)?.errorMessage,\n\t\t\t100,\n\t\t);\n\n\t\treturn {\n\t\t\trule: \"receiver_rejects\",\n\t\t\tseverity: \"losing\",\n\t\t\timpact: input.pending > 0 ? input.pending : streak.length,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${webhook.name} is being rejected by ${host}`,\n\t\t\tmeta: `${streak.length} in a row, HTTP ${code}`,\n\t\t\tlead: \"Your receiver is refusing our requests.\",\n\t\t\tbody: [\n\t\t\t\t`${host} answered HTTP ${code} to the last ${streak.length} deliveries, since ${utcTime(since)}.`,\n\t\t\t\ttext ? `It said: ${text}.` : \"\",\n\t\t\t\t\"It is reachable, so this is usually its auth or signature check.\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"shapes\",\n\t\t\t\t\ttitle: \"What the receiver sent back\",\n\t\t\t\t\tshapes: buildShapes(\n\t\t\t\t\t\tstreak,\n\t\t\t\t\t\tinput.attempts.slice(streak.length),\n\t\t\t\t\t\tinput.now,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`has been rejected since ${utcTime(since)}`,\n\t\t\t\t\t\t`The receiver ${host} answered HTTP ${code} to ${plural(streak.length, \"delivery\")} in a row`,\n\t\t\t\t\t\ttext ? `The response said: ${text}` : \"\",\n\t\t\t\t\t],\n\t\t\t\t\tlookAt: `Check how ${host} authenticates or verifies signatures on incoming webhook requests`,\n\t\t\t\t\tfollowUp: `Then run sl webhooks test ${webhook.name}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [`sl webhooks test ${webhook.name}`],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `the newest ${REJECTS_MIN_CONSECUTIVE} or more delivery attempts are 4xx other than 429`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"consecutive rejects\", String(streak.length)],\n\t\t\t\t\t[\"breakdown\", breakdown(streak)],\n\t\t\t\t\t[\"first reject\", utcDateTime(since)],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nconst receiverRateLimited: InsightRule<WebhookRuleInput> = {\n\tid: \"receiver_rate_limited\",\n\tevaluate(input) {\n\t\tconst window = input.attempts.slice(0, 100);\n\t\tif (window.length < RATE_LIMITED_MIN_ATTEMPTS) return null;\n\t\tconst limited = window.filter((d) => d.statusCode === 429);\n\t\tif (limited.length / window.length < RATE_LIMITED_MIN_RATIO) return null;\n\n\t\tconst { webhook } = input;\n\t\tconst host = hostOf(webhook.url);\n\t\tconst retryAfter =\n\t\t\tlimited.find((d) => d.retryAfter && /^\\d+$/.test(d.retryAfter))\n\t\t\t\t?.retryAfter ?? null;\n\t\tconst percent = Math.round((limited.length / window.length) * 100);\n\n\t\treturn {\n\t\t\trule: \"receiver_rate_limited\",\n\t\t\tseverity: \"will_fail\",\n\t\t\timpact: limited.length,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${host} is rate limiting ${webhook.name}`,\n\t\t\tmeta: `${limited.length} of the last ${window.length} attempts`,\n\t\t\tlead: \"Your receiver is telling us to slow down.\",\n\t\t\tbody: [\n\t\t\t\t`${percent}% of recent deliveries got HTTP 429.`,\n\t\t\t\tretryAfter ? `It asked us to wait ${retryAfter} seconds.` : \"\",\n\t\t\t\t\"We retry, so nothing is lost yet, but a long streak ends in dead letters.\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"shapes\",\n\t\t\t\t\ttitle: \"What the receiver sent back\",\n\t\t\t\t\tshapes: buildShapes(\n\t\t\t\t\t\twindow,\n\t\t\t\t\t\tinput.attempts.slice(window.length),\n\t\t\t\t\t\tinput.now,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`is being rate limited by ${host}`,\n\t\t\t\t\t\t`${limited.length} of the last ${window.length} delivery attempts got HTTP 429`,\n\t\t\t\t\t\tretryAfter\n\t\t\t\t\t\t\t? `The receiver asked for ${retryAfter} seconds between retries`\n\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t],\n\t\t\t\t\tlookAt: `Check the rate limit on ${host} and whether this webhook should have its own allowance`,\n\t\t\t\t\tfollowUp: `Then run sl webhooks deliveries ${webhook.name}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [`sl webhooks deliveries ${webhook.name}`],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `at least ${RATE_LIMITED_MIN_ATTEMPTS} recent attempts and ${Math.round(RATE_LIMITED_MIN_RATIO * 100)}% or more are HTTP 429`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"429 responses\", `${limited.length} of ${window.length}`],\n\t\t\t\t\t[\"latest Retry-After\", retryAfter ? `${retryAfter}s` : \"not sent\"],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nconst receiverLoginWall: InsightRule<WebhookRuleInput> = {\n\tid: \"receiver_login_wall\",\n\tevaluate(input) {\n\t\tconst streak = consecutiveFromStart(\n\t\t\tinput.attempts,\n\t\t\t(d) => is2xx(d) && isHtml(d),\n\t\t);\n\t\tif (streak.length < LOGIN_WALL_MIN_CONSECUTIVE) return null;\n\n\t\tconst start = streak[streak.length - 1]?.dispatchedAt ?? input.now;\n\t\tconst prior = input.attempts\n\t\t\t.slice(streak.length)\n\t\t\t.filter(\n\t\t\t\t(d) =>\n\t\t\t\t\tis2xx(d) &&\n\t\t\t\t\tstart.getTime() - d.dispatchedAt.getTime() <= PRIOR_WINDOW_MS,\n\t\t\t);\n\t\tif (prior.length < LOGIN_WALL_MIN_PRIOR) return null;\n\t\tconst json = prior.filter(isJson);\n\t\tif (json.length / prior.length < LOGIN_WALL_PRIOR_JSON_RATIO) return null;\n\n\t\tconst { webhook } = input;\n\t\tconst host = hostOf(webhook.url);\n\t\tconst heights = streak\n\t\t\t.map((d) => d.blockHeight)\n\t\t\t.filter((h): h is number => h !== null);\n\t\tconst from = heights.length > 0 ? Math.min(...heights) : null;\n\t\tconst to = heights.length > 0 ? Math.max(...heights) : null;\n\t\tconst htmlSample = streak.find((d) => d.body);\n\t\tconst jsonSample = json.find((d) => d.body);\n\n\t\treturn {\n\t\t\trule: \"receiver_login_wall\",\n\t\t\tseverity: \"losing\",\n\t\t\timpact: streak.length,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${host} now answers ${webhook.name} with a web page`,\n\t\t\tmeta: `${streak.length} deliveries since ${utcTime(start)}`,\n\t\t\tlead: \"Your receiver says OK, but sends back a web page.\",\n\t\t\tbody: [\n\t\t\t\t`Since ${utcTime(start)} every delivery got HTTP 200 with an HTML page instead of JSON.`,\n\t\t\t\t\"That is usually a login page or proxy screen in front of the endpoint, so the events are not reaching your handler.\",\n\t\t\t].join(\" \"),\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"shapes\",\n\t\t\t\t\ttitle: \"Response before and after\",\n\t\t\t\t\tshapes: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcode: \"200\",\n\t\t\t\t\t\t\tbody: jsonSample ? shapeText(jsonSample) : \"application/json\",\n\t\t\t\t\t\t\tcount: json.length,\n\t\t\t\t\t\t\tisNew: false,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcode: \"200\",\n\t\t\t\t\t\t\tbody: htmlSample ? shapeText(htmlSample) : \"text/html\",\n\t\t\t\t\t\t\tcount: streak.length,\n\t\t\t\t\t\t\tisNew: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`has been answered with a web page since ${utcTime(start)}`,\n\t\t\t\t\t\t`${host} returns HTTP 200 with text/html on ${streak.length} deliveries in a row, where it used to return JSON`,\n\t\t\t\t\t\t`${json.length} of ${prior.length} successful deliveries in the week before were JSON`,\n\t\t\t\t\t],\n\t\t\t\t\tlookAt: `Check whether a login page, access policy or proxy rule was put in front of ${host}`,\n\t\t\t\t\tfollowUp:\n\t\t\t\t\t\tfrom !== null && to !== null\n\t\t\t\t\t\t\t? `Once it is fixed, replay the missed blocks with sl webhooks replay ${webhook.name} --from-block ${from} --to-block ${to} -y`\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}),\n\t\t\t\tcommands:\n\t\t\t\t\tfrom !== null && to !== null\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t`sl webhooks replay ${webhook.name} --from-block ${from} --to-block ${to} -y`,\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: [],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `the newest ${LOGIN_WALL_MIN_CONSECUTIVE} or more successful responses are text/html while ${Math.round(LOGIN_WALL_PRIOR_JSON_RATIO * 100)}% or more of the week before were JSON`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"html responses in a row\", String(streak.length)],\n\t\t\t\t\t[\"first html response\", utcDateTime(start)],\n\t\t\t\t\t[\"json share before\", `${json.length} of ${prior.length}`],\n\t\t\t\t\t...(from !== null && to !== null\n\t\t\t\t\t\t? ([[\"affected blocks\", `${from} to ${to}`]] as [string, string][])\n\t\t\t\t\t\t: []),\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\n/** Least-squares line through (x, y). */\nfunction fitLine(points: { x: number; y: number }[]) {\n\tconst n = points.length;\n\tconst meanX = points.reduce((s, p) => s + p.x, 0) / n;\n\tconst meanY = points.reduce((s, p) => s + p.y, 0) / n;\n\tlet sxx = 0;\n\tlet sxy = 0;\n\tlet syy = 0;\n\tfor (const p of points) {\n\t\tsxx += (p.x - meanX) ** 2;\n\t\tsxy += (p.x - meanX) * (p.y - meanY);\n\t\tsyy += (p.y - meanY) ** 2;\n\t}\n\tconst slope = sxx === 0 ? 0 : sxy / sxx;\n\tconst intercept = meanY - slope * meanX;\n\tconst r2 = sxx === 0 || syy === 0 ? 0 : (sxy * sxy) / (sxx * syy);\n\treturn { slope, intercept, r2 };\n}\n\nconst receiverSlowing: InsightRule<WebhookRuleInput> = {\n\tid: \"receiver_slowing\",\n\tevaluate(input) {\n\t\tconst days = input.dailyP95;\n\t\tif (days.length < SLOWING_DAYS) return null;\n\n\t\tlet rising = 0;\n\t\tfor (let i = 1; i < days.length; i++) {\n\t\t\tconst prev = days[i - 1]?.p95Ms;\n\t\t\tconst cur = days[i]?.p95Ms;\n\t\t\tif (prev != null && cur != null && cur > prev) rising++;\n\t\t}\n\t\tif (rising < SLOWING_MIN_RISING_DAYS) return null;\n\n\t\tconst points = days.flatMap((d, x) =>\n\t\t\td.p95Ms === null ? [] : [{ x, y: d.p95Ms }],\n\t\t);\n\t\tif (points.length < SLOWING_MIN_RISING_DAYS) return null;\n\t\tconst fit = fitLine(points);\n\t\tif (fit.slope <= 0 || fit.r2 < SLOWING_MIN_R2) return null;\n\n\t\tconst timeout = input.webhook.timeoutMs;\n\t\tconst lastX = days.length - 1;\n\t\tconst nowFit = fit.intercept + fit.slope * lastX;\n\t\tconst laterFit = fit.intercept + fit.slope * (lastX + SLOWING_DAYS);\n\t\tif (!(nowFit < timeout && laterFit >= timeout)) return null;\n\n\t\tconst daysLeft = Math.max(1, Math.round((timeout - nowFit) / fit.slope));\n\t\tconst { webhook } = input;\n\t\tconst host = hostOf(webhook.url);\n\t\tconst first = days.find((d) => d.p95Ms !== null)?.p95Ms ?? 0;\n\t\tconst last = [...days].reverse().find((d) => d.p95Ms !== null)?.p95Ms ?? 0;\n\n\t\treturn {\n\t\t\trule: \"receiver_slowing\",\n\t\t\tseverity: \"will_fail\",\n\t\t\timpact: attemptsLast24h(input),\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${host} is getting slower for ${webhook.name}`,\n\t\t\tmeta: `may hit the ${timeout / 1000}s timeout in about ${plural(daysLeft, \"day\")}`,\n\t\t\tlead: \"Your receiver is slowing down, and not by chance.\",\n\t\t\tbody: `The slowest 5% of deliveries went from ${Math.round(first)} ms to ${Math.round(last)} ms over ${days.length} days, rising on ${rising} of them. At this pace they reach the ${timeout} ms timeout in about ${plural(daysLeft, \"day\")}, and then they start failing.`,\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"series\",\n\t\t\t\t\ttitle: \"Slowest 5% of response times, per day\",\n\t\t\t\t\tseries: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: \"p95\",\n\t\t\t\t\t\t\trole: \"a\",\n\t\t\t\t\t\t\tpoints: days.map((d) => Math.round(d.p95Ms ?? 0)),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: \"Timeout\",\n\t\t\t\t\t\t\trole: \"context\",\n\t\t\t\t\t\t\tpoints: days.map(() => timeout),\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tlabels: [\n\t\t\t\t\t\t[0, days[0]?.day.slice(5) ?? \"\"],\n\t\t\t\t\t\t[lastX, days[lastX]?.day.slice(5) ?? \"\"],\n\t\t\t\t\t],\n\t\t\t\t\tunit: \"ms\",\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`is getting slower: its receiver ${host} took ${Math.round(first)} ms at the slow end ${days.length} days ago and ${Math.round(last)} ms now`,\n\t\t\t\t\t\t`The delivery timeout is ${timeout} ms, which the trend reaches in about ${plural(daysLeft, \"day\")}`,\n\t\t\t\t\t],\n\t\t\t\t\tlookAt: `Check what changed on ${host} over the last two weeks, such as data growth, a slower dependency or a heavier handler`,\n\t\t\t\t\tfollowUp: `Then run sl webhooks deliveries ${webhook.name}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [`sl webhooks deliveries ${webhook.name}`],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `daily p95 response time rose on ${SLOWING_MIN_RISING_DAYS} or more of ${SLOWING_DAYS} days, a straight-line fit has r2 of ${SLOWING_MIN_R2} or more, and it reaches the timeout within ${SLOWING_DAYS} days`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"rising days\", `${rising} of ${days.length}`],\n\t\t\t\t\t[\"trend\", `+${Math.round(fit.slope)} ms a day`],\n\t\t\t\t\t[\"fit r2\", fit.r2.toFixed(2)],\n\t\t\t\t\t[\"timeout\", `${timeout} ms`],\n\t\t\t\t\t[\"days until timeout\", String(daysLeft)],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nconst tlsExpiring: InsightRule<WebhookRuleInput> = {\n\tid: \"tls_expiring\",\n\tevaluate(input) {\n\t\tconst expires = input.webhook.receiverCertExpiresAt;\n\t\tif (!expires) return null;\n\t\tconst msLeft = expires.getTime() - input.now.getTime();\n\t\tif (msLeft >= TLS_WARN_DAYS * DAY_MS) return null;\n\n\t\tconst { webhook } = input;\n\t\tconst host = hostOf(webhook.url);\n\t\tconst daysLeft = Math.floor(msLeft / DAY_MS);\n\t\tconst expired = msLeft <= 0;\n\n\t\treturn {\n\t\t\trule: \"tls_expiring\",\n\t\t\tseverity: \"will_fail\",\n\t\t\timpact: attemptsLast24h(input),\n\t\t\tresource: resourceOf(input),\n\t\t\tline: expired\n\t\t\t\t? `The certificate for ${host} has expired`\n\t\t\t\t: `The certificate for ${host} expires soon`,\n\t\t\tmeta: expired\n\t\t\t\t? `expired ${utcDateTime(expires)}`\n\t\t\t\t: `in ${plural(daysLeft, \"day\")}`,\n\t\t\tlead: expired\n\t\t\t\t? \"Your receiver's certificate has expired.\"\n\t\t\t\t: \"Your receiver's certificate is about to expire.\",\n\t\t\tbody: expired\n\t\t\t\t? `Deliveries to ${host} fail the TLS check until the certificate is renewed.`\n\t\t\t\t: `After ${utcDateTime(expires)} deliveries to ${host} fail the TLS check until the certificate is renewed.`,\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"table\",\n\t\t\t\t\ttitle: \"Receiver certificate\",\n\t\t\t\t\tcolumns: [\"Host\", \"Expires\", \"Days left\", \"Last checked\"],\n\t\t\t\t\trows: [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\thost,\n\t\t\t\t\t\t\tutcDateTime(expires),\n\t\t\t\t\t\t\tdaysLeft,\n\t\t\t\t\t\t\tinput.webhook.receiverCertCheckedAt\n\t\t\t\t\t\t\t\t? utcDateTime(input.webhook.receiverCertCheckedAt)\n\t\t\t\t\t\t\t\t: \"unknown\",\n\t\t\t\t\t\t],\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\texpired\n\t\t\t\t\t\t\t? `points at ${host}, whose TLS certificate expired on ${utcDateTime(expires)}`\n\t\t\t\t\t\t\t: `points at ${host}, whose TLS certificate expires on ${utcDateTime(expires)}`,\n\t\t\t\t\t],\n\t\t\t\t\tlookAt: `Check why the certificate on ${host} is not renewing automatically`,\n\t\t\t\t}),\n\t\t\t\tcommands: [],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `the receiver certificate expires in less than ${TLS_WARN_DAYS} days`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"expires\", utcDateTime(expires)],\n\t\t\t\t\t[\"days left\", String(daysLeft)],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\ninterface ContractCallTrigger {\n\tcontractId: string;\n\tfunctionName: string;\n}\n\nfunction literalContractCalls(triggers: unknown): ContractCallTrigger[] {\n\tif (!Array.isArray(triggers)) return [];\n\tconst out: ContractCallTrigger[] = [];\n\tfor (const t of triggers) {\n\t\tif (!t || typeof t !== \"object\") continue;\n\t\tconst { type, contractId, functionName } = t as Record<string, unknown>;\n\t\tif (type !== \"contract_call\") continue;\n\t\tif (typeof contractId !== \"string\" || typeof functionName !== \"string\")\n\t\t\tcontinue;\n\t\tif (contractId.includes(\"*\") || functionName.includes(\"*\")) continue;\n\t\tout.push({ contractId, functionName });\n\t}\n\treturn out;\n}\n\n/** Contract ids whose ABI the loader must fetch for this webhook. */\nexport function contractIdsToCheck(triggers: unknown): string[] {\n\treturn [...new Set(literalContractCalls(triggers).map((t) => t.contractId))];\n}\n\nconst filterCantMatch: InsightRule<WebhookRuleInput> = {\n\tid: \"filter_cant_match\",\n\tskip: (input) =>\n\t\tcontractIdsToCheck(input.webhook.triggers).length > 0 &&\n\t\t!input.abiAvailable,\n\tevaluate(input) {\n\t\tconst offending = literalContractCalls(input.webhook.triggers).filter(\n\t\t\t(t) => {\n\t\t\t\tconst names = input.abi[t.contractId];\n\t\t\t\treturn names !== undefined && !names.includes(t.functionName);\n\t\t\t},\n\t\t);\n\t\tconst first = offending[0];\n\t\tif (!first) return null;\n\n\t\tconst names = input.abi[first.contractId] ?? [];\n\t\tconst ranked = [...names].sort(\n\t\t\t(a, b) =>\n\t\t\t\tlevenshtein(a, first.functionName) - levenshtein(b, first.functionName),\n\t\t);\n\t\tconst closest = ranked[0];\n\t\tconst { webhook } = input;\n\n\t\treturn {\n\t\t\trule: \"filter_cant_match\",\n\t\t\tseverity: \"will_fail\",\n\t\t\timpact: offending.length,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${webhook.name} can never match a call`,\n\t\t\tmeta: `${first.contractId} has no ${first.functionName}`,\n\t\t\tlead: \"This filter waits for something that does not exist.\",\n\t\t\tbody: [\n\t\t\t\t`${first.contractId} has no public function named ${first.functionName}, so no event will ever match.`,\n\t\t\t\tclosest ? `The closest is ${closest}.` : \"\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"table\",\n\t\t\t\t\ttitle: \"Public functions on the contract\",\n\t\t\t\t\tcolumns: [\"Function\", \"In contract\"],\n\t\t\t\t\trows: [\n\t\t\t\t\t\t[first.functionName, \"not found\"],\n\t\t\t\t\t\t...ranked.slice(0, 6).map((n): [string, string] => [n, \"yes\"]),\n\t\t\t\t\t],\n\t\t\t\t\tmarkRow: 0,\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`waits for calls to ${first.functionName} on ${first.contractId}`,\n\t\t\t\t\t\t`That contract has no public method with that name${closest ? `, and the closest name is ${closest}` : \"\"}`,\n\t\t\t\t\t\t\"The webhook has never matched and cannot\",\n\t\t\t\t\t],\n\t\t\t\t\tlookAt:\n\t\t\t\t\t\t\"Check the spelling in the trigger and whether the contract was redeployed under another name\",\n\t\t\t\t\tfollowUp: `Create the corrected webhook first, then run sl webhooks delete ${webhook.name}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [`sl webhooks delete ${webhook.name}`],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector:\n\t\t\t\t\t\"a contract_call trigger names a literal contract and method, the contract exists, and its ABI has no method with that name\",\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"contract\", first.contractId],\n\t\t\t\t\t[\"method in trigger\", first.functionName],\n\t\t\t\t\t[\"methods on contract\", String(names.length)],\n\t\t\t\t\t[\"triggers that cannot match\", String(offending.length)],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nconst deadLettersExpiring: InsightRule<WebhookRuleInput> = {\n\tid: \"dead_letters_expiring\",\n\tevaluate(input) {\n\t\tconst { dead, webhook } = input;\n\t\tif (dead.count === 0 || !dead.oldestAt) return null;\n\n\t\tconst deleteAt = new Date(\n\t\t\tdead.oldestAt.getTime() + DEAD_LETTER_DELETE_AGE_DAYS * DAY_MS,\n\t\t);\n\t\treturn {\n\t\t\trule: \"dead_letters_expiring\",\n\t\t\tseverity: \"will_fail\",\n\t\t\timpact: dead.count,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${plural(dead.count, \"failed event\")} on ${webhook.name} will be deleted`,\n\t\t\tmeta: `from ${utcDateTime(deleteAt)}`,\n\t\t\tlead: \"Failed events are kept for 90 days, and these are almost there.\",\n\t\t\tbody: `${plural(dead.count, \"event\")} that never reached your receiver are older than ${DEAD_LETTER_WARN_AGE_DAYS} days. The oldest is deleted on ${utcDateTime(deleteAt)}. Send them again before then if you still want them.`,\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"table\",\n\t\t\t\t\ttitle: \"Failed events near deletion\",\n\t\t\t\t\tcolumns: [\"Failed events\", \"Oldest failure\", \"Deleted from\"],\n\t\t\t\t\trows: [\n\t\t\t\t\t\t[dead.count, utcDateTime(dead.oldestAt), utcDateTime(deleteAt)],\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`has ${plural(dead.count, \"failed event\")} older than ${DEAD_LETTER_WARN_AGE_DAYS} days`,\n\t\t\t\t\t\t`The oldest failed on ${utcDateTime(dead.oldestAt)} and is deleted from ${utcDateTime(deleteAt)}`,\n\t\t\t\t\t],\n\t\t\t\t\tlookAt:\n\t\t\t\t\t\t\"Decide whether those events still matter, and confirm the receiver can handle them now\",\n\t\t\t\t\tfollowUp: `Then run sl webhooks dead ${webhook.name}${dead.oldestOutboxId ? ` and sl webhooks requeue ${webhook.name} ${dead.oldestOutboxId}` : \"\"}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [\n\t\t\t\t\t`sl webhooks dead ${webhook.name}`,\n\t\t\t\t\t...(dead.oldestOutboxId\n\t\t\t\t\t\t? [`sl webhooks requeue ${webhook.name} ${dead.oldestOutboxId}`]\n\t\t\t\t\t\t: []),\n\t\t\t\t],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `failed outbox rows older than ${DEAD_LETTER_WARN_AGE_DAYS} days exist (deleted at ${DEAD_LETTER_DELETE_AGE_DAYS})`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"failed events past the line\", String(dead.count)],\n\t\t\t\t\t[\"oldest failure\", utcDateTime(dead.oldestAt)],\n\t\t\t\t\t[\"oldest is deleted\", utcDateTime(deleteAt)],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nconst duplicateWebhook: InsightRule<WebhookRuleInput> = {\n\tid: \"duplicate_webhook\",\n\tevaluate(input) {\n\t\tconst { webhook, siblings } = input;\n\t\tconst mine = canonicalJson(webhook.triggers);\n\t\tconst twin = siblings.find(\n\t\t\t(s) => s.url === webhook.url && canonicalJson(s.triggers) === mine,\n\t\t);\n\t\tif (!twin) return null;\n\t\t// One card, on the newer webhook (ties broken by id).\n\t\tconst newer =\n\t\t\twebhook.createdAt.getTime() !== twin.createdAt.getTime()\n\t\t\t\t? webhook.createdAt.getTime() > twin.createdAt.getTime()\n\t\t\t\t: webhook.id > twin.id;\n\t\tif (!newer) return null;\n\n\t\treturn {\n\t\t\trule: \"duplicate_webhook\",\n\t\t\tseverity: \"costs\",\n\t\t\timpact: input.deliveries30d,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${webhook.name} duplicates ${twin.name}`,\n\t\t\tmeta: \"same URL and same filters\",\n\t\t\tlead: \"Two webhooks do the same job.\",\n\t\t\tbody: `${webhook.name} and ${twin.name} send the same events to ${hostOf(webhook.url)}. Your receiver gets each event twice, and each delivery counts.`,\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"table\",\n\t\t\t\t\ttitle: \"Matching webhooks\",\n\t\t\t\t\tcolumns: [\"Webhook\", \"Created\"],\n\t\t\t\t\trows: [\n\t\t\t\t\t\t[twin.name, utcDateTime(twin.createdAt)],\n\t\t\t\t\t\t[webhook.name, utcDateTime(webhook.createdAt)],\n\t\t\t\t\t],\n\t\t\t\t\tmarkRow: 1,\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`duplicates the webhook \"${twin.name}\"`,\n\t\t\t\t\t\t`Both send the same filters to ${webhook.url}, so each event is delivered twice`,\n\t\t\t\t\t],\n\t\t\t\t\tlookAt: `Check that nothing relies on both, then remove the newer one, ${webhook.name}`,\n\t\t\t\t}),\n\t\t\t\tcommands: [`sl webhooks delete ${webhook.name}`],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: \"two non-paused webhooks share a URL and identical triggers\",\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"older webhook\", twin.name],\n\t\t\t\t\t[\"newer webhook\", webhook.name],\n\t\t\t\t\t[\"deliveries in the last 30 days\", String(input.deliveries30d)],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nconst TEST_HOST_SUFFIXES = [\".ngrok.io\", \".ngrok-free.app\", \".pipedream.net\"];\nconst TEST_HOSTS = new Set([\"postman-echo.com\", \"webhook.site\"]);\n\n/** True for well-known throwaway receiver hosts. */\nexport function isTestHost(host: string): boolean {\n\tconst name = host.toLowerCase().split(\":\")[0] ?? \"\";\n\treturn (\n\t\tTEST_HOSTS.has(name) ||\n\t\tname.startsWith(\"requestbin.\") ||\n\t\tTEST_HOST_SUFFIXES.some((suffix) => name.endsWith(suffix))\n\t);\n}\n\nconst testEndpoint: InsightRule<WebhookRuleInput> = {\n\tid: \"test_endpoint\",\n\tevaluate(input) {\n\t\tconst { webhook } = input;\n\t\tconst host = hostOf(webhook.url);\n\t\tif (!isTestHost(host)) return null;\n\t\tconst ageMs = input.now.getTime() - webhook.createdAt.getTime();\n\t\tif (ageMs <= TEST_ENDPOINT_MIN_AGE_DAYS * DAY_MS) return null;\n\n\t\tconst ageDays = Math.floor(ageMs / DAY_MS);\n\t\treturn {\n\t\t\trule: \"test_endpoint\",\n\t\t\tseverity: \"costs\",\n\t\t\timpact: input.deliveries30d,\n\t\t\tresource: resourceOf(input),\n\t\t\tline: `${webhook.name} still sends to a test endpoint`,\n\t\t\tmeta: host,\n\t\t\tlead: \"This webhook points at a throwaway test URL.\",\n\t\t\tbody: `${host} is a public testing service, and ${webhook.name} has been sending to it for ${plural(ageDays, \"day\")}. Real events are leaving your account, and each delivery counts.`,\n\t\t\tevidence: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"table\",\n\t\t\t\t\ttitle: \"Test endpoint\",\n\t\t\t\t\tcolumns: [\"Host\", \"Age\", \"Deliveries, last 30 days\"],\n\t\t\t\t\trows: [[host, `${ageDays} days`, input.deliveries30d]],\n\t\t\t\t},\n\t\t\t],\n\t\t\thandoff: {\n\t\t\t\tprompt: handoffPrompt({\n\t\t\t\t\tsubject: subject(webhook.name),\n\t\t\t\t\tobserved: [\n\t\t\t\t\t\t`has sent to the test service ${host} for ${plural(ageDays, \"day\")}`,\n\t\t\t\t\t\t`${plural(input.deliveries30d, \"delivery\")} in the last 30 days`,\n\t\t\t\t\t],\n\t\t\t\t\tlookAt:\n\t\t\t\t\t\t\"Decide whether this should point at a real receiver, or be paused or removed\",\n\t\t\t\t}),\n\t\t\t\tcommands: [\n\t\t\t\t\t`sl webhooks pause ${webhook.name}`,\n\t\t\t\t\t`sl webhooks delete ${webhook.name}`,\n\t\t\t\t],\n\t\t\t},\n\t\t\thow: {\n\t\t\t\tdetector: `the receiver host is a known test service and the webhook is older than ${TEST_ENDPOINT_MIN_AGE_DAYS} days`,\n\t\t\t\tfacts: [\n\t\t\t\t\t[\"host\", host],\n\t\t\t\t\t[\"age\", `${ageDays} days`],\n\t\t\t\t\t[\"deliveries in the last 30 days\", String(input.deliveries30d)],\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport const WEBHOOK_RULES: InsightRule<WebhookRuleInput>[] = [\n\treceiverDown,\n\treceiverRejects,\n\treceiverRateLimited,\n\treceiverLoginWall,\n\treceiverSlowing,\n\ttlsExpiring,\n\tfilterCantMatch,\n\tdeadLettersExpiring,\n\tduplicateWebhook,\n\ttestEndpoint,\n];\n\nexport type { DetectedInsight };\n\n// ── ABI lookup ─────────────────────────────────────────────────────────\n\nconst ABI_TTL_MS = 24 * 3600_000;\n\nexport interface AbiCache {\n\tget(contractId: string, now: number): string[] | undefined;\n\tset(contractId: string, names: string[], now: number): void;\n}\n\nexport function createAbiCache(): AbiCache {\n\tconst entries = new Map<string, { names: string[]; at: number }>();\n\treturn {\n\t\tget(contractId, now) {\n\t\t\tconst hit = entries.get(contractId);\n\t\t\treturn hit && now - hit.at < ABI_TTL_MS ? hit.names : undefined;\n\t\t},\n\t\tset(contractId, names, now) {\n\t\t\tentries.set(contractId, { names, at: now });\n\t\t},\n\t};\n}\n\nexport interface AbiDeps {\n\tenv: Record<string, string | undefined>;\n\tfetchImpl: typeof fetch;\n\tcache: AbiCache;\n}\n\n/**\n * Public function names for one contract from the hosted read API. Returns\n * `\"unavailable\"` on any error (the rule then skips: never fire on missing\n * data), and `null` when the contract is not in the registry.\n */\nasync function fetchFunctionNames(\n\tcontractId: string,\n\tbaseUrl: string,\n\tapiKey: string,\n\tdeps: AbiDeps,\n): Promise<string[] | null | \"unavailable\"> {\n\ttry {\n\t\tconst res = await deps.fetchImpl(\n\t\t\t`${baseUrl.replace(/\\/$/, \"\")}/v1/contracts/${encodeURIComponent(contractId)}?include=abi`,\n\t\t\t{\n\t\t\t\theaders: { Authorization: `Bearer ${apiKey}` },\n\t\t\t\tsignal: AbortSignal.timeout(5000),\n\t\t\t},\n\t\t);\n\t\tif (res.status === 404) return null;\n\t\tif (!res.ok) return \"unavailable\";\n\t\tconst body = (await res.json()) as {\n\t\t\tcontract?: { abi?: { functions?: { name?: unknown }[] } | null };\n\t\t};\n\t\tconst functions = body.contract?.abi?.functions;\n\t\tif (!Array.isArray(functions)) return \"unavailable\";\n\t\treturn functions\n\t\t\t.map((f) => f.name)\n\t\t\t.filter((n): n is string => typeof n === \"string\");\n\t} catch {\n\t\treturn \"unavailable\";\n\t}\n}\n\n// ── Loader ─────────────────────────────────────────────────────────────\n\ntype Num = string | number | null;\nconst num = (v: Num): number | null => (v === null ? null : Number(v));\n\n/** Everything the webhook rules read, in a handful of queries for all webhooks. */\nexport async function loadWebhookInputs(\n\tdb: Kysely<Database>,\n\tnow: Date,\n\tabiDeps: AbiDeps,\n): Promise<WebhookRuleInput[]> {\n\tconst nowMs = now.getTime();\n\tconst webhooks = await db\n\t\t.selectFrom(\"webhooks\")\n\t\t.select([\n\t\t\t\"id\",\n\t\t\t\"name\",\n\t\t\t\"status\",\n\t\t\t\"url\",\n\t\t\t\"triggers\",\n\t\t\t\"timeout_ms\",\n\t\t\t\"created_at\",\n\t\t\t\"last_success_at\",\n\t\t\t\"receiver_cert_expires_at\",\n\t\t\t\"receiver_cert_checked_at\",\n\t\t])\n\t\t.execute();\n\tif (webhooks.length === 0) return [];\n\n\tconst lookback = new Date(nowMs - ATTEMPT_LOOKBACK_DAYS * DAY_MS);\n\tconst attemptRows = await sql<{\n\t\twebhook_id: string;\n\t\toutbox_id: string | null;\n\t\tstatus_code: number | null;\n\t\tduration_ms: number | null;\n\t\tdispatched_at: Date;\n\t\terror_message: string | null;\n\t\tbody: string | null;\n\t\tcontent_type: string | null;\n\t\tserver: string | null;\n\t\tretry_after: string | null;\n\t\tblock_height: Num;\n\t}>`\n\t\tSELECT webhook_id, outbox_id, status_code, duration_ms, dispatched_at,\n\t\t\terror_message, body, content_type, server, retry_after, block_height\n\t\tFROM (\n\t\t\tSELECT d.webhook_id, d.outbox_id, d.status_code, d.duration_ms,\n\t\t\t\td.dispatched_at, d.error_message,\n\t\t\t\tleft(d.response_body, 200) AS body,\n\t\t\t\td.response_headers ->> 'content-type' AS content_type,\n\t\t\t\td.response_headers ->> 'server' AS server,\n\t\t\t\td.response_headers ->> 'retry-after' AS retry_after,\n\t\t\t\to.block_height,\n\t\t\t\trow_number() OVER (PARTITION BY d.webhook_id ORDER BY d.dispatched_at DESC) AS rn\n\t\t\tFROM webhook_deliveries d\n\t\t\tLEFT JOIN webhook_outbox o ON o.id = d.outbox_id\n\t\t\tWHERE d.dispatched_at > ${lookback}\n\t\t) t\n\t\tWHERE rn <= ${ATTEMPT_WINDOW}\n\t\tORDER BY webhook_id, dispatched_at DESC\n\t`.execute(db);\n\n\tconst startMs =\n\t\tMath.floor(nowMs / BUCKET_MS) * BUCKET_MS - (BUCKETS - 1) * BUCKET_MS;\n\tconst start = new Date(startMs);\n\tconst matchedRows = await sql<{ webhook_id: string; b: number; n: Num }>`\n\t\tSELECT webhook_id,\n\t\t\tfloor(extract(epoch FROM (created_at - ${start}::timestamptz)) / 600)::int AS b,\n\t\t\tcount(*) AS n\n\t\tFROM webhook_outbox\n\t\tWHERE created_at >= ${start} AND is_replay = false\n\t\tGROUP BY 1, 2\n\t`.execute(db);\n\tconst deliveredRows = await sql<{ webhook_id: string; b: number; n: Num }>`\n\t\tSELECT webhook_id,\n\t\t\tfloor(extract(epoch FROM (delivered_at - ${start}::timestamptz)) / 600)::int AS b,\n\t\t\tcount(*) AS n\n\t\tFROM webhook_outbox\n\t\tWHERE delivered_at >= ${start} AND status = 'delivered'\n\t\tGROUP BY 1, 2\n\t`.execute(db);\n\tconst pendingRows = await sql<{ webhook_id: string; n: Num }>`\n\t\tSELECT webhook_id, count(*) AS n FROM webhook_outbox\n\t\tWHERE status = 'pending' GROUP BY 1\n\t`.execute(db);\n\n\tconst dayStart =\n\t\tMath.floor(nowMs / DAY_MS) * DAY_MS - (SLOWING_DAYS - 1) * DAY_MS;\n\tconst p95Rows = await sql<{ webhook_id: string; day: string; p95: Num }>`\n\t\tSELECT webhook_id,\n\t\t\tto_char(dispatched_at AT TIME ZONE 'UTC', 'YYYY-MM-DD') AS day,\n\t\t\tpercentile_cont(0.95) WITHIN GROUP (ORDER BY duration_ms) AS p95\n\t\tFROM webhook_deliveries\n\t\tWHERE dispatched_at >= ${new Date(dayStart)} AND duration_ms IS NOT NULL\n\t\tGROUP BY 1, 2\n\t`.execute(db);\n\n\tconst warnBefore = new Date(nowMs - DEAD_LETTER_WARN_AGE_DAYS * DAY_MS);\n\tconst deadRows = await sql<{\n\t\twebhook_id: string;\n\t\tn: Num;\n\t\toldest: Date;\n\t\toldest_id: string;\n\t}>`\n\t\tSELECT webhook_id, count(*) AS n,\n\t\t\tmin(coalesce(failed_at, created_at)) AS oldest,\n\t\t\t(array_agg(id ORDER BY coalesce(failed_at, created_at)))[1] AS oldest_id\n\t\tFROM webhook_outbox\n\t\tWHERE status = 'dead' AND coalesce(failed_at, created_at) < ${warnBefore}\n\t\tGROUP BY 1\n\t`.execute(db);\n\tconst deliveryCountRows = await sql<{ webhook_id: string; n: Num }>`\n\t\tSELECT webhook_id, count(*) AS n FROM webhook_deliveries\n\t\tWHERE dispatched_at > ${new Date(nowMs - 30 * DAY_MS)} GROUP BY 1\n\t`.execute(db);\n\n\tconst attemptsBy = new Map<string, DeliveryAttempt[]>();\n\tfor (const r of attemptRows.rows) {\n\t\tconst list = attemptsBy.get(r.webhook_id) ?? [];\n\t\tlist.push({\n\t\t\toutboxId: r.outbox_id,\n\t\t\tstatusCode: r.status_code,\n\t\t\tdurationMs: r.duration_ms,\n\t\t\tdispatchedAt: r.dispatched_at,\n\t\t\terrorMessage: r.error_message,\n\t\t\tbody: r.body,\n\t\t\tcontentType: r.content_type,\n\t\t\tserver: r.server,\n\t\t\tretryAfter: r.retry_after,\n\t\t\tblockHeight: num(r.block_height),\n\t\t});\n\t\tattemptsBy.set(r.webhook_id, list);\n\t}\n\tconst series = (rows: { webhook_id: string; b: number; n: Num }[]) => {\n\t\tconst by = new Map<string, number[]>();\n\t\tfor (const r of rows) {\n\t\t\tif (r.b < 0 || r.b >= BUCKETS) continue;\n\t\t\tconst arr = by.get(r.webhook_id) ?? new Array<number>(BUCKETS).fill(0);\n\t\t\tarr[r.b] = Number(r.n);\n\t\t\tby.set(r.webhook_id, arr);\n\t\t}\n\t\treturn by;\n\t};\n\tconst matchedBy = series(matchedRows.rows);\n\tconst deliveredBy = series(deliveredRows.rows);\n\tconst pendingBy = new Map(\n\t\tpendingRows.rows.map((r) => [r.webhook_id, Number(r.n)]),\n\t);\n\tconst countBy = new Map(\n\t\tdeliveryCountRows.rows.map((r) => [r.webhook_id, Number(r.n)]),\n\t);\n\tconst deadBy = new Map(deadRows.rows.map((r) => [r.webhook_id, r]));\n\tconst p95By = new Map<string, Map<string, number>>();\n\tfor (const r of p95Rows.rows) {\n\t\tconst m = p95By.get(r.webhook_id) ?? new Map<string, number>();\n\t\tm.set(r.day, Number(r.p95));\n\t\tp95By.set(r.webhook_id, m);\n\t}\n\tconst days = Array.from({ length: SLOWING_DAYS }, (_, i) =>\n\t\tnew Date(dayStart + i * DAY_MS).toISOString().slice(0, 10),\n\t);\n\n\t// ABI lookups: only literal contract_call triggers, only when a hosted\n\t// read key and URL exist. Cached 24h per contract.\n\tconst baseUrl = abiDeps.env.STREAMS_API_URL?.trim();\n\tconst apiKey = abiDeps.env.INDEX_INTERNAL_API_KEY?.trim();\n\tconst abiEnabled = Boolean(baseUrl && apiKey);\n\tconst abiBy = new Map<string, string[] | null | \"unavailable\">();\n\tif (abiEnabled && baseUrl && apiKey) {\n\t\tconst wanted = new Set(\n\t\t\twebhooks\n\t\t\t\t.filter((w) => w.status !== \"paused\")\n\t\t\t\t.flatMap((w) => contractIdsToCheck(w.triggers)),\n\t\t);\n\t\tfor (const id of wanted) {\n\t\t\tconst cached = abiDeps.cache.get(id, nowMs);\n\t\t\tif (cached) {\n\t\t\t\tabiBy.set(id, cached);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst names = await fetchFunctionNames(id, baseUrl, apiKey, abiDeps);\n\t\t\tif (Array.isArray(names)) abiDeps.cache.set(id, names, nowMs);\n\t\t\tabiBy.set(id, names);\n\t\t}\n\t}\n\n\tconst live = webhooks.filter((w) => w.status !== \"paused\");\n\treturn webhooks.map((w) => {\n\t\tconst dead = deadBy.get(w.id);\n\t\tconst ids = contractIdsToCheck(w.triggers);\n\t\tconst abi: Record<string, string[]> = {};\n\t\tlet abiAvailable = abiEnabled;\n\t\tfor (const id of ids) {\n\t\t\tconst found = abiBy.get(id);\n\t\t\tif (Array.isArray(found)) abi[id] = found;\n\t\t\telse if (found === \"unavailable\") abiAvailable = false;\n\t\t}\n\t\tconst p95 = p95By.get(w.id);\n\t\treturn {\n\t\t\tnow,\n\t\t\twebhook: {\n\t\t\t\tid: w.id,\n\t\t\t\tname: w.name,\n\t\t\t\tstatus: w.status,\n\t\t\t\turl: w.url,\n\t\t\t\ttriggers: w.triggers,\n\t\t\t\ttimeoutMs: w.timeout_ms,\n\t\t\t\tcreatedAt: w.created_at,\n\t\t\t\tlastSuccessAt: w.last_success_at,\n\t\t\t\treceiverCertExpiresAt: w.receiver_cert_expires_at,\n\t\t\t\treceiverCertCheckedAt: w.receiver_cert_checked_at,\n\t\t\t},\n\t\t\tattempts: attemptsBy.get(w.id) ?? [],\n\t\t\tpending: pendingBy.get(w.id) ?? 0,\n\t\t\tthroughput: {\n\t\t\t\tstartMs,\n\t\t\t\tmatched: matchedBy.get(w.id) ?? new Array<number>(BUCKETS).fill(0),\n\t\t\t\tdelivered: deliveredBy.get(w.id) ?? new Array<number>(BUCKETS).fill(0),\n\t\t\t},\n\t\t\tdailyP95: days.map((day) => ({ day, p95Ms: p95?.get(day) ?? null })),\n\t\t\tdead: {\n\t\t\t\tcount: dead ? Number(dead.n) : 0,\n\t\t\t\toldestAt: dead?.oldest ?? null,\n\t\t\t\toldestOutboxId: dead?.oldest_id ?? null,\n\t\t\t},\n\t\t\tdeliveries30d: countBy.get(w.id) ?? 0,\n\t\t\tsiblings: live\n\t\t\t\t.filter((o) => o.id !== w.id)\n\t\t\t\t.map((o) => ({\n\t\t\t\t\tid: o.id,\n\t\t\t\t\tname: o.name,\n\t\t\t\t\turl: o.url,\n\t\t\t\t\ttriggers: o.triggers,\n\t\t\t\t\tcreatedAt: o.created_at,\n\t\t\t\t})),\n\t\t\tabi,\n\t\t\tabiAvailable,\n\t\t};\n\t});\n}\n",
24
+ "import type { Kysely } from \"kysely\";\nimport type { Database } from \"../db/types.ts\";\nimport { logger } from \"../logger.ts\";\nimport { type LabelDeps, maybeLabel } from \"./label.ts\";\nimport { listOpenResources, purgeResolved, recordEvaluation } from \"./store.ts\";\nimport { SUBGRAPH_RULES, loadSubgraphInputs } from \"./subgraph-rules.ts\";\nimport type {\n\tDetectedInsight,\n\tEvaluatedKey,\n\tInsightRule,\n\tInsightScope,\n} from \"./types.ts\";\nimport {\n\ttype AbiCache,\n\tWEBHOOK_RULES,\n\tcreateAbiCache,\n\tloadWebhookInputs,\n} from \"./webhook-rules.ts\";\n\n/**\n * Run one resource's rules. A rule that throws or reports `skip` is left out\n * of `evaluated`, so missing data can never clear an open insight.\n */\nexport function runRules<Input>(\n\trules: InsightRule<Input>[],\n\tinput: Input,\n\tresource: { kind: EvaluatedKey[\"kind\"]; id: string },\n\topts: { active?: boolean } = {},\n): { fired: DetectedInsight[]; evaluated: EvaluatedKey[] } {\n\tconst fired: DetectedInsight[] = [];\n\tconst evaluated: EvaluatedKey[] = [];\n\tfor (const rule of rules) {\n\t\ttry {\n\t\t\tif (rule.skip?.(input)) continue;\n\t\t\tconst key = { kind: resource.kind, id: resource.id, rule: rule.id };\n\t\t\t// An inactive resource (a paused webhook) counts as clear.\n\t\t\tconst result = opts.active === false ? null : rule.evaluate(input);\n\t\t\tevaluated.push(key);\n\t\t\tif (result) fired.push(result);\n\t\t} catch (err) {\n\t\t\tlogger.warn(\"insights.rule_failed\", {\n\t\t\t\trule: rule.id,\n\t\t\t\terror: String(err),\n\t\t\t});\n\t\t}\n\t}\n\treturn { fired, evaluated };\n}\n\nexport interface TenantEvaluateDeps extends Omit<LabelDeps, \"now\"> {\n\tnow?: Date;\n\tfetchImpl?: typeof fetch;\n\tabiCache?: AbiCache;\n}\n\nconst defaultAbiCache = createAbiCache();\n\n/**\n * One tenant (or self-host) pass: webhook rules, subgraph rules, optional\n * cause labels, then persist. Open insights for resources that no longer\n * exist resolve through the normal two-pass clear.\n */\nexport async function evaluateTenantInsights(\n\tdb: Kysely<Database>,\n\tdeps: TenantEvaluateDeps = {},\n): Promise<void> {\n\tconst now = deps.now ?? new Date();\n\tconst env = deps.env ?? process.env;\n\tconst scope: InsightScope = { accountId: null };\n\n\tconst webhookInputs = await loadWebhookInputs(db, now, {\n\t\tenv,\n\t\tfetchImpl: deps.fetchImpl ?? fetch,\n\t\tcache: deps.abiCache ?? defaultAbiCache,\n\t});\n\tconst subgraphInputs = await loadSubgraphInputs(db, now);\n\n\tconst fired: DetectedInsight[] = [];\n\tconst evaluated: EvaluatedKey[] = [];\n\n\tfor (const input of webhookInputs) {\n\t\tconst result = runRules(\n\t\t\tWEBHOOK_RULES,\n\t\t\tinput,\n\t\t\t{ kind: \"webhook\", id: input.webhook.id },\n\t\t\t{ active: input.webhook.status !== \"paused\" },\n\t\t);\n\t\tfired.push(...result.fired);\n\t\tevaluated.push(...result.evaluated);\n\t}\n\tfor (const input of subgraphInputs) {\n\t\tconst result = runRules(SUBGRAPH_RULES, input, {\n\t\t\tkind: \"subgraph\",\n\t\t\tid: input.subgraph.name,\n\t\t});\n\t\tfired.push(...result.fired);\n\t\tevaluated.push(...result.evaluated);\n\t}\n\n\t// Deleted resources: their open rows are evaluated with nothing to fire.\n\tconst live = new Set([\n\t\t...webhookInputs.map((i) => `webhook\\u0000${i.webhook.id}`),\n\t\t...subgraphInputs.map((i) => `subgraph\\u0000${i.subgraph.name}`),\n\t]);\n\tfor (const open of await listOpenResources(db, scope)) {\n\t\tif (!live.has(`${open.kind}\\u0000${open.id}`)) evaluated.push(open);\n\t}\n\n\tconst labelled = await Promise.all(\n\t\tfired.map((insight) =>\n\t\t\tmaybeLabel(insight, {\n\t\t\t\tclassifyFn: deps.classifyFn,\n\t\t\t\tcache: deps.cache,\n\t\t\t\tenv,\n\t\t\t\tnow: now.getTime(),\n\t\t\t}),\n\t\t),\n\t);\n\tawait recordEvaluation(db, scope, labelled, evaluated, now);\n\tawait purgeResolved(db, scope, 30, now);\n}\n"
25
+ ],
26
+ "mappings": "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",
27
+ "debugId": "85A02E469680D94664756E2164756E21",
28
+ "names": []
29
+ }
@@ -0,0 +1,56 @@
1
+ declare const RATE_LIMITED_MIN_ATTEMPTS = 10;
2
+ declare const RATE_LIMITED_MIN_RATIO = .5;
3
+ declare const DOWN_MIN_CONSECUTIVE = 5;
4
+ declare const REJECTS_MIN_CONSECUTIVE = 5;
5
+ declare const SLOW_WINDOW = 20;
6
+ declare const SLOW_MIN_SAMPLES = 5;
7
+ declare const SLOW_TIMEOUT_RATIO = .5;
8
+ declare const LAG_WINDOW = 20;
9
+ declare const LAG_MIN_SAMPLES = 5;
10
+ declare const LAG_THRESHOLD_MS = 6e4;
11
+ /** Consecutive newest 2xx HTML responses that mean a login wall. */
12
+ declare const LOGIN_WALL_MIN_CONSECUTIVE = 20;
13
+ /** Share of prior-week 2xx responses that must have been JSON. */
14
+ declare const LOGIN_WALL_PRIOR_JSON_RATIO = .8;
15
+ /** Prior-week successful responses needed before a baseline counts. */
16
+ declare const LOGIN_WALL_MIN_PRIOR = 10;
17
+ declare const SLOWING_DAYS = 14;
18
+ declare const SLOWING_MIN_RISING_DAYS = 10;
19
+ declare const SLOWING_MIN_R2 = .8;
20
+ declare const TLS_WARN_DAYS = 14;
21
+ /** Dead outbox rows are deleted at 90 days; warn at 83. */
22
+ declare const DEAD_LETTER_WARN_AGE_DAYS = 83;
23
+ declare const DEAD_LETTER_DELETE_AGE_DAYS = 90;
24
+ declare const TEST_ENDPOINT_MIN_AGE_DAYS = 7;
25
+ declare const HANDLER_ERRORS_MIN_24H = 10;
26
+ /** Blocks behind tip, for 3 consecutive snapshots. */
27
+ declare const SUBGRAPH_LAG_BLOCKS = 20;
28
+ declare const SUBGRAPH_LAG_SNAPSHOTS = 3;
29
+ declare const SUBGRAPH_LAG_LOSING_BLOCKS = 500;
30
+ /**
31
+ * Index and Streams cap one page at 1000 rows
32
+ * (`packages/api/src/index/_shared.ts` `Math.min(1000, parsed)`,
33
+ * `packages/api/src/routes/streams.ts` `limit: Math.min(1000, limit)`).
34
+ */
35
+ declare const PAGE_CAP_ROWS = 1e3;
36
+ declare const DUPLICATE_READS_MIN_PAGES = 3;
37
+ declare const DUPLICATE_READS_WINDOW_MS = 36e5;
38
+ /** Only repeats that started within this long ago count as current. */
39
+ declare const DUPLICATE_READS_LOOKBACK_MS: number;
40
+ declare const POLLING_MIN_DURATION_MS = 36e5;
41
+ /** Requests needed in the window before an interval means anything. */
42
+ declare const POLLING_MIN_REQUESTS = 5;
43
+ declare const POLLING_LOOKBACK_MS: number;
44
+ declare const POLLING_MAX_STDDEV_RATIO = .2;
45
+ declare const POLLING_MIN_EMPTY_RATIO = .8;
46
+ declare const SERVER_ERRORS_MIN_5XX = 3;
47
+ declare const BACKFILL_DAY_SHARE = .25;
48
+ declare const BACKFILL_FULL_PAGE_RATIO = .9;
49
+ /** A day under this many rows is never called a backfill, however full its pages. */
50
+ declare const BACKFILL_MIN_DAY_ROWS = 1e5;
51
+ declare const CREDITS_RUNWAY_WARN_DAYS = 7;
52
+ /** A resolved insight stays visible as "recovered" for this long. */
53
+ declare const RECOVERED_VISIBLE_MS: number;
54
+ /** Consecutive clean evaluations before an open insight resolves. */
55
+ declare const CLEAR_STREAK_TO_RESOLVE = 2;
56
+ export { BACKFILL_DAY_SHARE, BACKFILL_FULL_PAGE_RATIO, BACKFILL_MIN_DAY_ROWS, CLEAR_STREAK_TO_RESOLVE, CREDITS_RUNWAY_WARN_DAYS, DEAD_LETTER_DELETE_AGE_DAYS, DEAD_LETTER_WARN_AGE_DAYS, DOWN_MIN_CONSECUTIVE, DUPLICATE_READS_LOOKBACK_MS, DUPLICATE_READS_MIN_PAGES, DUPLICATE_READS_WINDOW_MS, HANDLER_ERRORS_MIN_24H, LAG_MIN_SAMPLES, LAG_THRESHOLD_MS, LAG_WINDOW, LOGIN_WALL_MIN_CONSECUTIVE, LOGIN_WALL_MIN_PRIOR, LOGIN_WALL_PRIOR_JSON_RATIO, PAGE_CAP_ROWS, POLLING_LOOKBACK_MS, POLLING_MAX_STDDEV_RATIO, POLLING_MIN_DURATION_MS, POLLING_MIN_EMPTY_RATIO, POLLING_MIN_REQUESTS, RATE_LIMITED_MIN_ATTEMPTS, RATE_LIMITED_MIN_RATIO, RECOVERED_VISIBLE_MS, REJECTS_MIN_CONSECUTIVE, SERVER_ERRORS_MIN_5XX, SLOWING_DAYS, SLOWING_MIN_R2, SLOWING_MIN_RISING_DAYS, SLOW_MIN_SAMPLES, SLOW_TIMEOUT_RATIO, SLOW_WINDOW, SUBGRAPH_LAG_BLOCKS, SUBGRAPH_LAG_LOSING_BLOCKS, SUBGRAPH_LAG_SNAPSHOTS, TEST_ENDPOINT_MIN_AGE_DAYS, TLS_WARN_DAYS };
@@ -0,0 +1,86 @@
1
+ // src/insights/thresholds.ts
2
+ var RATE_LIMITED_MIN_ATTEMPTS = 10;
3
+ var RATE_LIMITED_MIN_RATIO = 0.5;
4
+ var DOWN_MIN_CONSECUTIVE = 5;
5
+ var REJECTS_MIN_CONSECUTIVE = 5;
6
+ var SLOW_WINDOW = 20;
7
+ var SLOW_MIN_SAMPLES = 5;
8
+ var SLOW_TIMEOUT_RATIO = 0.5;
9
+ var LAG_WINDOW = 20;
10
+ var LAG_MIN_SAMPLES = 5;
11
+ var LAG_THRESHOLD_MS = 60000;
12
+ var LOGIN_WALL_MIN_CONSECUTIVE = 20;
13
+ var LOGIN_WALL_PRIOR_JSON_RATIO = 0.8;
14
+ var LOGIN_WALL_MIN_PRIOR = 10;
15
+ var SLOWING_DAYS = 14;
16
+ var SLOWING_MIN_RISING_DAYS = 10;
17
+ var SLOWING_MIN_R2 = 0.8;
18
+ var TLS_WARN_DAYS = 14;
19
+ var DEAD_LETTER_WARN_AGE_DAYS = 83;
20
+ var DEAD_LETTER_DELETE_AGE_DAYS = 90;
21
+ var TEST_ENDPOINT_MIN_AGE_DAYS = 7;
22
+ var HANDLER_ERRORS_MIN_24H = 10;
23
+ var SUBGRAPH_LAG_BLOCKS = 20;
24
+ var SUBGRAPH_LAG_SNAPSHOTS = 3;
25
+ var SUBGRAPH_LAG_LOSING_BLOCKS = 500;
26
+ var PAGE_CAP_ROWS = 1000;
27
+ var DUPLICATE_READS_MIN_PAGES = 3;
28
+ var DUPLICATE_READS_WINDOW_MS = 3600000;
29
+ var DUPLICATE_READS_LOOKBACK_MS = 24 * 3600000;
30
+ var POLLING_MIN_DURATION_MS = 3600000;
31
+ var POLLING_MIN_REQUESTS = 5;
32
+ var POLLING_LOOKBACK_MS = 6 * 3600000;
33
+ var POLLING_MAX_STDDEV_RATIO = 0.2;
34
+ var POLLING_MIN_EMPTY_RATIO = 0.8;
35
+ var SERVER_ERRORS_MIN_5XX = 3;
36
+ var BACKFILL_DAY_SHARE = 0.25;
37
+ var BACKFILL_FULL_PAGE_RATIO = 0.9;
38
+ var BACKFILL_MIN_DAY_ROWS = 1e5;
39
+ var CREDITS_RUNWAY_WARN_DAYS = 7;
40
+ var RECOVERED_VISIBLE_MS = 24 * 3600000;
41
+ var CLEAR_STREAK_TO_RESOLVE = 2;
42
+ export {
43
+ BACKFILL_DAY_SHARE,
44
+ BACKFILL_FULL_PAGE_RATIO,
45
+ BACKFILL_MIN_DAY_ROWS,
46
+ CLEAR_STREAK_TO_RESOLVE,
47
+ CREDITS_RUNWAY_WARN_DAYS,
48
+ DEAD_LETTER_DELETE_AGE_DAYS,
49
+ DEAD_LETTER_WARN_AGE_DAYS,
50
+ DOWN_MIN_CONSECUTIVE,
51
+ DUPLICATE_READS_LOOKBACK_MS,
52
+ DUPLICATE_READS_MIN_PAGES,
53
+ DUPLICATE_READS_WINDOW_MS,
54
+ HANDLER_ERRORS_MIN_24H,
55
+ LAG_MIN_SAMPLES,
56
+ LAG_THRESHOLD_MS,
57
+ LAG_WINDOW,
58
+ LOGIN_WALL_MIN_CONSECUTIVE,
59
+ LOGIN_WALL_MIN_PRIOR,
60
+ LOGIN_WALL_PRIOR_JSON_RATIO,
61
+ PAGE_CAP_ROWS,
62
+ POLLING_LOOKBACK_MS,
63
+ POLLING_MAX_STDDEV_RATIO,
64
+ POLLING_MIN_DURATION_MS,
65
+ POLLING_MIN_EMPTY_RATIO,
66
+ POLLING_MIN_REQUESTS,
67
+ RATE_LIMITED_MIN_ATTEMPTS,
68
+ RATE_LIMITED_MIN_RATIO,
69
+ RECOVERED_VISIBLE_MS,
70
+ REJECTS_MIN_CONSECUTIVE,
71
+ SERVER_ERRORS_MIN_5XX,
72
+ SLOWING_DAYS,
73
+ SLOWING_MIN_R2,
74
+ SLOWING_MIN_RISING_DAYS,
75
+ SLOW_MIN_SAMPLES,
76
+ SLOW_TIMEOUT_RATIO,
77
+ SLOW_WINDOW,
78
+ SUBGRAPH_LAG_BLOCKS,
79
+ SUBGRAPH_LAG_LOSING_BLOCKS,
80
+ SUBGRAPH_LAG_SNAPSHOTS,
81
+ TEST_ENDPOINT_MIN_AGE_DAYS,
82
+ TLS_WARN_DAYS
83
+ };
84
+
85
+ //# debugId=502E5DF0B63B38FF64756E2164756E21
86
+ //# sourceMappingURL=thresholds.js.map