mergen-server 1.4.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 (308) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +96 -0
  3. package/dist/__stubs__/calibration.js +471 -0
  4. package/dist/__stubs__/causal.js +601 -0
  5. package/dist/__stubs__/closed-source.js +150 -0
  6. package/dist/__stubs__/license.js +35 -0
  7. package/dist/__stubs__/plans.js +197 -0
  8. package/dist/app.js +467 -0
  9. package/dist/cli.js +250 -0
  10. package/dist/commands/agent-identity.js +79 -0
  11. package/dist/commands/gate.js +107 -0
  12. package/dist/commands/github.js +632 -0
  13. package/dist/commands/incident.js +644 -0
  14. package/dist/commands/policy.js +710 -0
  15. package/dist/commands/scan.js +243 -0
  16. package/dist/commands/setup.js +1218 -0
  17. package/dist/commands/shared.js +109 -0
  18. package/dist/commands/team.js +809 -0
  19. package/dist/datadog/blame-attribution.js +228 -0
  20. package/dist/datadog/client.js +159 -0
  21. package/dist/datadog/compactor.js +169 -0
  22. package/dist/datadog/fingerprinter.js +34 -0
  23. package/dist/datadog/incident-state.js +21 -0
  24. package/dist/datadog/line-matcher.js +56 -0
  25. package/dist/datadog/memory-store.js +399 -0
  26. package/dist/datadog/otel-trace.js +67 -0
  27. package/dist/index.js +505 -0
  28. package/dist/intelligence/action-risk.js +132 -0
  29. package/dist/intelligence/activity-feed.js +40 -0
  30. package/dist/intelligence/agent-identity.js +115 -0
  31. package/dist/intelligence/agent-pipeline.js +329 -0
  32. package/dist/intelligence/agent-profiles.js +81 -0
  33. package/dist/intelligence/ai-commit.js +16 -0
  34. package/dist/intelligence/approval-analytics.js +42 -0
  35. package/dist/intelligence/approval-events.js +5 -0
  36. package/dist/intelligence/arch-boundaries.js +94 -0
  37. package/dist/intelligence/arch-graph.js +117 -0
  38. package/dist/intelligence/autonomy.js +397 -0
  39. package/dist/intelligence/azure-broker.js +57 -0
  40. package/dist/intelligence/azure-proxy-sessions.js +49 -0
  41. package/dist/intelligence/baseline.js +1 -0
  42. package/dist/intelligence/behavior-baseline.js +134 -0
  43. package/dist/intelligence/billing.js +1 -0
  44. package/dist/intelligence/blast-radius.js +224 -0
  45. package/dist/intelligence/bypass.js +157 -0
  46. package/dist/intelligence/calibration-classifier.js +77 -0
  47. package/dist/intelligence/calibration-git-sync.js +74 -0
  48. package/dist/intelligence/calibration.js +1 -0
  49. package/dist/intelligence/cascade-detector.js +58 -0
  50. package/dist/intelligence/case-study-generator.js +126 -0
  51. package/dist/intelligence/causal-graph.js +126 -0
  52. package/dist/intelligence/causal.js +1 -0
  53. package/dist/intelligence/change-risk.js +102 -0
  54. package/dist/intelligence/compliance-report.js +134 -0
  55. package/dist/intelligence/confidence-report.js +85 -0
  56. package/dist/intelligence/corpus-to-policy.js +140 -0
  57. package/dist/intelligence/credential-broker.js +112 -0
  58. package/dist/intelligence/credential-misuse.js +39 -0
  59. package/dist/intelligence/debug-sessions.js +108 -0
  60. package/dist/intelligence/decision-escalation.js +36 -0
  61. package/dist/intelligence/decision-memory.js +47 -0
  62. package/dist/intelligence/decision-similarity.js +85 -0
  63. package/dist/intelligence/default-safety-tests.js +53 -0
  64. package/dist/intelligence/degradation-watcher.js +108 -0
  65. package/dist/intelligence/detector-plugins.js +70 -0
  66. package/dist/intelligence/detectors.js +1 -0
  67. package/dist/intelligence/device-auth.js +138 -0
  68. package/dist/intelligence/diff-size.js +42 -0
  69. package/dist/intelligence/ebpf-verify.js +45 -0
  70. package/dist/intelligence/enterprise-policy-engine.js +1122 -0
  71. package/dist/intelligence/error-fingerprint.js +1 -0
  72. package/dist/intelligence/execution-gate.js +100 -0
  73. package/dist/intelligence/execution-mode.js +22 -0
  74. package/dist/intelligence/gate-analytics.js +256 -0
  75. package/dist/intelligence/gate-decision.js +64 -0
  76. package/dist/intelligence/gcp-broker.js +50 -0
  77. package/dist/intelligence/git-adr-sync.js +207 -0
  78. package/dist/intelligence/hitl-hold.js +460 -0
  79. package/dist/intelligence/hypothesis-history.js +93 -0
  80. package/dist/intelligence/idp-identity.js +83 -0
  81. package/dist/intelligence/impl-critic.js +216 -0
  82. package/dist/intelligence/incident-autopilot.js +622 -0
  83. package/dist/intelligence/incident-replay.js +162 -0
  84. package/dist/intelligence/incident-result-cache.js +33 -0
  85. package/dist/intelligence/incident-similarity.js +58 -0
  86. package/dist/intelligence/infra-detectors.js +312 -0
  87. package/dist/intelligence/license.js +185 -0
  88. package/dist/intelligence/llm-spokesperson.js +62 -0
  89. package/dist/intelligence/mcp-prompts.js +320 -0
  90. package/dist/intelligence/mcp-resources.js +171 -0
  91. package/dist/intelligence/normalize.js +108 -0
  92. package/dist/intelligence/notifications.js +83 -0
  93. package/dist/intelligence/oidc-issuer.js +87 -0
  94. package/dist/intelligence/override-corpus.js +597 -0
  95. package/dist/intelligence/planning-gate.js +84 -0
  96. package/dist/intelligence/plans.js +223 -0
  97. package/dist/intelligence/platt-scaling.js +164 -0
  98. package/dist/intelligence/policy-proposals.js +84 -0
  99. package/dist/intelligence/policy-suggester.js +161 -0
  100. package/dist/intelligence/policy-sync.js +103 -0
  101. package/dist/intelligence/policy-test-runner.js +35 -0
  102. package/dist/intelligence/postmortem-parser.js +181 -0
  103. package/dist/intelligence/postmortem-retrieval.js +224 -0
  104. package/dist/intelligence/postmortem-store.js +596 -0
  105. package/dist/intelligence/pr-commenter.js +81 -0
  106. package/dist/intelligence/pr-policy-gate.js +38 -0
  107. package/dist/intelligence/pr-shadow-analyzer.js +229 -0
  108. package/dist/intelligence/process-tree.js +108 -0
  109. package/dist/intelligence/prompts.js +1 -0
  110. package/dist/intelligence/replay.js +38 -0
  111. package/dist/intelligence/repro-steps.js +1 -0
  112. package/dist/intelligence/resource-extractors.js +0 -0
  113. package/dist/intelligence/resource-file-context.js +138 -0
  114. package/dist/intelligence/risk-score.js +22 -0
  115. package/dist/intelligence/rollback.js +137 -0
  116. package/dist/intelligence/runbook-updater.js +143 -0
  117. package/dist/intelligence/sandbox.js +194 -0
  118. package/dist/intelligence/script-trust.js +104 -0
  119. package/dist/intelligence/session-metrics.js +67 -0
  120. package/dist/intelligence/session-threat-tracker.js +231 -0
  121. package/dist/intelligence/shadow-digest-cron.js +81 -0
  122. package/dist/intelligence/shadow-log.js +284 -0
  123. package/dist/intelligence/shell-ast.js +145 -0
  124. package/dist/intelligence/siem-forward.js +104 -0
  125. package/dist/intelligence/slack-digest.js +151 -0
  126. package/dist/intelligence/slack-override-loop.js +217 -0
  127. package/dist/intelligence/slack-routing.js +67 -0
  128. package/dist/intelligence/slack.js +900 -0
  129. package/dist/intelligence/sql-ast.js +31 -0
  130. package/dist/intelligence/team.js +1 -0
  131. package/dist/intelligence/telemetry.js +76 -0
  132. package/dist/intelligence/threshold-optimizer.js +75 -0
  133. package/dist/intelligence/token-budget.js +66 -0
  134. package/dist/intelligence/tool-guard.js +708 -0
  135. package/dist/intelligence/tool-manifest 2.js +74 -0
  136. package/dist/intelligence/tool-manifest.js +125 -0
  137. package/dist/intelligence/tools-analysis.js +857 -0
  138. package/dist/intelligence/tools-arch.js +152 -0
  139. package/dist/intelligence/tools-autonomy.js +495 -0
  140. package/dist/intelligence/tools-blast-radius.js +140 -0
  141. package/dist/intelligence/tools-browser.js +431 -0
  142. package/dist/intelligence/tools-change-timeline.js +154 -0
  143. package/dist/intelligence/tools-credentials.js +117 -0
  144. package/dist/intelligence/tools-datadog.js +233 -0
  145. package/dist/intelligence/tools-debug-sessions.js +209 -0
  146. package/dist/intelligence/tools-discovery.js +284 -0
  147. package/dist/intelligence/tools-infra.js +612 -0
  148. package/dist/intelligence/tools-intent.js +128 -0
  149. package/dist/intelligence/tools-memory.js +514 -0
  150. package/dist/intelligence/tools-runbook.js +951 -0
  151. package/dist/intelligence/tools-sessions.js +107 -0
  152. package/dist/intelligence/tools-state.js +232 -0
  153. package/dist/intelligence/tools-utility.js +430 -0
  154. package/dist/intelligence/tools-validate.js +215 -0
  155. package/dist/intelligence/tools.js +1 -0
  156. package/dist/intelligence/trace-context.js +11 -0
  157. package/dist/intelligence/unclassified-clusters.js +87 -0
  158. package/dist/intelligence/usage.js +195 -0
  159. package/dist/routes/active-authors.js +63 -0
  160. package/dist/routes/activity-feed.js +21 -0
  161. package/dist/routes/adr.js +117 -0
  162. package/dist/routes/agent-activity.js +184 -0
  163. package/dist/routes/agent-blunders.js +163 -0
  164. package/dist/routes/agents.js +175 -0
  165. package/dist/routes/api-keys.js +65 -0
  166. package/dist/routes/arch.js +75 -0
  167. package/dist/routes/audit-export.js +93 -0
  168. package/dist/routes/billing-dashboard.js +158 -0
  169. package/dist/routes/billing-outcome.js +77 -0
  170. package/dist/routes/calibration.js +305 -0
  171. package/dist/routes/ci-gate-history.js +20 -0
  172. package/dist/routes/ci-gate.js +232 -0
  173. package/dist/routes/ci.js +293 -0
  174. package/dist/routes/compliance-report.js +24 -0
  175. package/dist/routes/confidence.js +30 -0
  176. package/dist/routes/credentials.js +119 -0
  177. package/dist/routes/dashboard.js +1407 -0
  178. package/dist/routes/decision-memory.js +42 -0
  179. package/dist/routes/demo.js +844 -0
  180. package/dist/routes/device-auth-stub.js +173 -0
  181. package/dist/routes/explain-why.js +39 -0
  182. package/dist/routes/gate-analytics.js +213 -0
  183. package/dist/routes/gate.js +28 -0
  184. package/dist/routes/github-webhook.js +364 -0
  185. package/dist/routes/habituation.js +30 -0
  186. package/dist/routes/health-integrations.js +243 -0
  187. package/dist/routes/heartbeats.js +45 -0
  188. package/dist/routes/hitl.js +364 -0
  189. package/dist/routes/impact-report.js +645 -0
  190. package/dist/routes/incident-webhook.js +63 -0
  191. package/dist/routes/incidents.js +366 -0
  192. package/dist/routes/layers.js +62 -0
  193. package/dist/routes/license.js +187 -0
  194. package/dist/routes/oidc-issuer.js +30 -0
  195. package/dist/routes/onboarding.js +170 -0
  196. package/dist/routes/otel.js +67 -0
  197. package/dist/routes/otlp-receiver.js +179 -0
  198. package/dist/routes/overrides.js +241 -0
  199. package/dist/routes/pagerduty.js +258 -0
  200. package/dist/routes/policies.js +809 -0
  201. package/dist/routes/policy-nl.js +169 -0
  202. package/dist/routes/postmortem.js +102 -0
  203. package/dist/routes/pr-shadow.js +47 -0
  204. package/dist/routes/rbac.js +61 -0
  205. package/dist/routes/replay.js +17 -0
  206. package/dist/routes/risk-report.js +164 -0
  207. package/dist/routes/runbooks.js +125 -0
  208. package/dist/routes/safety-test.js +174 -0
  209. package/dist/routes/sdk.js +222 -0
  210. package/dist/routes/sensor.js +819 -0
  211. package/dist/routes/sentry.js +140 -0
  212. package/dist/routes/sessions.js +43 -0
  213. package/dist/routes/setup-ui.js +594 -0
  214. package/dist/routes/shadow-report.js +107 -0
  215. package/dist/routes/slack-routing.js +171 -0
  216. package/dist/routes/team-usage.js +71 -0
  217. package/dist/routes/telemetry.js +28 -0
  218. package/dist/routes/tenants.js +199 -0
  219. package/dist/routes/tickets.js +167 -0
  220. package/dist/routes/validate.js +13 -0
  221. package/dist/routes/war-room.js +113 -0
  222. package/dist/scripts/check-arch.js +22 -0
  223. package/dist/seeds/community-corpus.js +176 -0
  224. package/dist/seeds/corpus.js +1224 -0
  225. package/dist/sensor/action-ledger.js +327 -0
  226. package/dist/sensor/adr-store.js +140 -0
  227. package/dist/sensor/agent-blunder-store.js +370 -0
  228. package/dist/sensor/agent-context-store.js +225 -0
  229. package/dist/sensor/agent-memory-store.js +231 -0
  230. package/dist/sensor/audit-fetch.js +23 -0
  231. package/dist/sensor/audit-log.js +273 -0
  232. package/dist/sensor/buffer-schemas.js +225 -0
  233. package/dist/sensor/buffer.js +599 -0
  234. package/dist/sensor/bypass-tracker.js +93 -0
  235. package/dist/sensor/ci-gate-history.js +70 -0
  236. package/dist/sensor/cloud-auth.js +183 -0
  237. package/dist/sensor/commit-context-store.js +235 -0
  238. package/dist/sensor/docker-log-stream.js +164 -0
  239. package/dist/sensor/docker-monitor.js +122 -0
  240. package/dist/sensor/extended-buffer.js +46 -0
  241. package/dist/sensor/feedback-token.js +41 -0
  242. package/dist/sensor/file-lock.js +6 -0
  243. package/dist/sensor/fs-watcher.js +106 -0
  244. package/dist/sensor/gate-heartbeat.js +122 -0
  245. package/dist/sensor/git-suspect.js +136 -0
  246. package/dist/sensor/habituation-store.js +77 -0
  247. package/dist/sensor/heartbeat-monitor.js +133 -0
  248. package/dist/sensor/incident-store.js +340 -0
  249. package/dist/sensor/infra-normalizer.js +513 -0
  250. package/dist/sensor/ingest.js +339 -0
  251. package/dist/sensor/jest-reporter.js +52 -0
  252. package/dist/sensor/k8s-events.js +132 -0
  253. package/dist/sensor/layer2-store.js +111 -0
  254. package/dist/sensor/layer3-store.js +230 -0
  255. package/dist/sensor/layer4-store.js +147 -0
  256. package/dist/sensor/logger.js +13 -0
  257. package/dist/sensor/otel-exporter.js +161 -0
  258. package/dist/sensor/paths.js +71 -0
  259. package/dist/sensor/policy-history.js +147 -0
  260. package/dist/sensor/pr-shadow-store.js +83 -0
  261. package/dist/sensor/process-watcher.js +162 -0
  262. package/dist/sensor/rbac.js +92 -0
  263. package/dist/sensor/redact.js +114 -0
  264. package/dist/sensor/redis-store.js +191 -0
  265. package/dist/sensor/route-reachability.js +64 -0
  266. package/dist/sensor/security-utils.js +18 -0
  267. package/dist/sensor/service-graph.js +170 -0
  268. package/dist/sensor/service-topology.js +227 -0
  269. package/dist/sensor/session-history.js +74 -0
  270. package/dist/sensor/session-persist.js +38 -0
  271. package/dist/sensor/shadow-promote.js +122 -0
  272. package/dist/sensor/sourcemap.js +281 -0
  273. package/dist/sensor/sqlite-store.js +205 -0
  274. package/dist/sensor/sso.js +164 -0
  275. package/dist/sensor/vitest-reporter.js +65 -0
  276. package/dist/sensor/watcher.js +48 -0
  277. package/dist/storage/interfaces.js +0 -0
  278. package/dist/storage/pg/pg-action-ledger.js +138 -0
  279. package/dist/storage/pg/pg-approval-store.js +107 -0
  280. package/dist/storage/pg/pg-blunder-store.js +193 -0
  281. package/dist/storage/pg/pg-ci-gate-history.js +83 -0
  282. package/dist/storage/pg/pg-client.js +24 -0
  283. package/dist/storage/pg/pg-event-store.js +63 -0
  284. package/dist/storage/pg/pg-incident-store.js +190 -0
  285. package/dist/storage/pg/pg-migrations.js +26 -0
  286. package/dist/storage/pg/pg-override-corpus.js +447 -0
  287. package/dist/storage/pg/pg-shadow-log.js +129 -0
  288. package/dist/storage/sqlite/sqlite-action-ledger.js +18 -0
  289. package/dist/storage/sqlite/sqlite-approval-store.js +37 -0
  290. package/dist/storage/sqlite/sqlite-blunder-store.js +27 -0
  291. package/dist/storage/sqlite/sqlite-ci-gate-history.js +19 -0
  292. package/dist/storage/sqlite/sqlite-event-store.js +29 -0
  293. package/dist/storage/sqlite/sqlite-incident-store.js +34 -0
  294. package/dist/storage/sqlite/sqlite-override-corpus.js +75 -0
  295. package/dist/storage/sqlite/sqlite-shadow-log.js +36 -0
  296. package/dist/storage/store-factory.js +55 -0
  297. package/dist/storage/store-registry.js +31 -0
  298. package/dist/update-checker.js +107 -0
  299. package/dist/workers/autopilot-worker.js +28 -0
  300. package/dist/workers/notification-worker.js +28 -0
  301. package/dist/workers/queues.js +58 -0
  302. package/dist/workers/validation-worker.js +24 -0
  303. package/dist/workers/worker-registry.js +18 -0
  304. package/package.json +123 -0
  305. package/sdk/mergen-inject.js +260 -0
  306. package/sdk/node.js +313 -0
  307. package/sdk/vite-plugin.ts +57 -0
  308. package/sdk/webpack-plugin.js +106 -0
package/LICENSE ADDED
@@ -0,0 +1,57 @@
1
+ Mergen — Open-Core License
2
+
3
+ This repository uses a dual-license model:
4
+
5
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
6
+ MIT License (open-source layer)
7
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8
+
9
+ The following components are MIT-licensed:
10
+
11
+ extension/ Chrome and Edge browser extension
12
+ extension-firefox/ Firefox browser extension
13
+ vscode-extension/ VS Code sidebar extension
14
+ packages/ mergen-node and mergen-python SDKs
15
+ sdk/ Integration helpers and plugins
16
+ server/src/sensor/ Ingest, ring buffer, event schemas
17
+ server/src/routes/ HTTP API routes
18
+ server/src/app.ts Express application factory
19
+ server/src/index.ts Boot entrypoint
20
+ scripts/ Setup and tooling scripts
21
+
22
+ Copyright (c) 2026 Mergen contributors
23
+
24
+ Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ of this software and associated documentation files (the "Software"), to deal
26
+ in the Software without restriction, including without limitation the rights
27
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ copies of the Software, and to permit persons to whom the Software is
29
+ furnished to do so, subject to the following conditions:
30
+
31
+ The above copyright notice and this permission notice shall be included in all
32
+ copies or substantial portions of the Software.
33
+
34
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ SOFTWARE.
41
+
42
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
43
+ Proprietary License (Hypothesis Engine)
44
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
45
+
46
+ The following components are proprietary and all rights are reserved:
47
+
48
+ server/src/intelligence/ Causal chain reconstruction, hypothesis
49
+ detectors, calibration feedback loop,
50
+ anomaly baseline, billing, and licensing
51
+
52
+ These components are distributed in compiled form only as part of the
53
+ mergen-server npm package. They may not be copied, modified, redistributed,
54
+ or used to build a competing service. See server/src/intelligence/LICENSE
55
+ for the full proprietary license terms.
56
+
57
+ Commercial licenses and pricing: https://mergen.dev/pricing
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # mergen-server
2
+
3
+ Claude Code doesn't know what happened in production. Mergen does.
4
+
5
+ MCP server for your AI IDE. Once connected, ask *"what caused the 3am incident"* and get a causal chain from live telemetry — not a log dump, a structured hypothesis with evidence and a fix command. At ≥85% confidence it executes the fix, validates the result, and posts the audit trail to your Slack thread.
6
+
7
+ [![npm](https://img.shields.io/npm/v/mergen-server)](https://www.npmjs.com/package/mergen-server)
8
+ [![License](https://img.shields.io/badge/license-MIT%20%2B%20Proprietary-blue)](https://github.com/omertt27/Mergen/blob/main/LICENSE)
9
+ [![MCP](https://img.shields.io/badge/MCP-stdio-black)](https://modelcontextprotocol.io)
10
+
11
+ ---
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ npm install -g mergen-server
17
+ mergen-server setup
18
+ ```
19
+
20
+ Then add to Claude Code:
21
+
22
+ ```bash
23
+ claude mcp add mergen --transport stdio -- mergen-server start
24
+ ```
25
+
26
+ Ask: *"Triage the api-service."*
27
+
28
+ ---
29
+
30
+ ## What it does
31
+
32
+ ```
33
+ PagerDuty fires
34
+ → Mergen pulls correlated telemetry
35
+ → Causal analysis (root cause + evidence)
36
+ → If confidence ≥ 85% → executes fix
37
+ → Validates result
38
+ → Posts audit trail to Slack thread
39
+ ```
40
+
41
+ All data stays on your infrastructure. No cloud. No copy-paste.
42
+
43
+ ---
44
+
45
+ ## MCP tools
46
+
47
+ | Tool | What it does |
48
+ |------|-------------|
49
+ | `triage_incident` | Full autonomous loop — diagnosis + optional fix |
50
+ | `execute_fix` | Execute a specific fix (requires `confirm: true`) |
51
+ | `analyze_runtime` | Causal analysis — root cause + fix hint, no execution |
52
+ | `get_recent_logs` | Console events from the ring buffer |
53
+ | `get_network_activity` | HTTP/fetch events with status, duration, body |
54
+ | `validate_fix` | Compare error counts before/after a fix |
55
+ | `generate_runbook` | Self-updating runbook from your incident corpus |
56
+ | `search_postmortems` | Semantic search over past incidents |
57
+ | `draft_postmortem` | Blameless post-mortem draft in seconds |
58
+
59
+ ---
60
+
61
+ ## Environment variables
62
+
63
+ ```bash
64
+ MERGEN_AUTOPILOT=true # enable autonomous fix execution
65
+ MERGEN_SLACK_BOT_TOKEN=xoxb-... # Slack Web API (threads + replies)
66
+ MERGEN_SLACK_CHANNEL=#incidents # default incident channel
67
+ ```
68
+
69
+ Point your OTLP exporter at `http://127.0.0.1:3000` and PagerDuty webhooks at `/webhooks/pagerduty`.
70
+
71
+ ---
72
+
73
+ ## Node.js SDK (one line)
74
+
75
+ ```js
76
+ import 'mergen-server/sdk/node.js';
77
+ // Captures uncaught exceptions, unhandledRejections, and process exits automatically
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Post-mortem from Slack thread
83
+
84
+ ```bash
85
+ curl -X POST http://127.0.0.1:3000/postmortem/from-slack \
86
+ -H 'Content-Type: application/json' \
87
+ -d '{"thread_url":"https://yourworkspace.slack.com/archives/C1234567/p1700000000000000","service":"api"}'
88
+ ```
89
+
90
+ Fetches the thread via `conversations.replies`, correlates with telemetry, and returns a blameless post-mortem draft in seconds.
91
+
92
+ ---
93
+
94
+ ## Full documentation
95
+
96
+ [github.com/omertt27/Mergen](https://github.com/omertt27/Mergen)
@@ -0,0 +1,471 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { randomUUID } from "crypto";
4
+ import { calibrationClassifier } from "../intelligence/calibration-classifier.js";
5
+ import { invalidateThresholdCache } from "../intelligence/threshold-optimizer.js";
6
+ import { CALIBRATION_FILE, zeroRetentionMode } from "../sensor/paths.js";
7
+ import { gitSyncCalibration } from "../intelligence/calibration-git-sync.js";
8
+ import logger from "../sensor/logger.js";
9
+ const MIN_SAMPLES = 5;
10
+ const DEMOTE_THRESHOLD = 0.5;
11
+ const SUPPRESS_THRESHOLD = 0.2;
12
+ const PENDING_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
13
+ const LABELED_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
14
+ const SEED_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
15
+ let _records = [];
16
+ let _loaded = false;
17
+ let _corpusIsSeeded = false;
18
+ let _persistTimer = null;
19
+ function schedulePersist() {
20
+ if (zeroRetentionMode()) return;
21
+ if (_persistTimer) clearTimeout(_persistTimer);
22
+ _persistTimer = setTimeout(() => {
23
+ _persistTimer = null;
24
+ _persist();
25
+ }, 1e3);
26
+ }
27
+ function _persist() {
28
+ try {
29
+ fs.mkdirSync(path.dirname(CALIBRATION_FILE), { recursive: true });
30
+ const payload = {
31
+ version: 1,
32
+ records: _records.map((r) => ({
33
+ ...r,
34
+ predictedAt: r.predictedAt.toISOString(),
35
+ verdictAt: r.verdictAt?.toISOString() ?? null,
36
+ isBuiltinSeed: r.isBuiltinSeed,
37
+ remediationVerdict: r.remediationVerdict,
38
+ remediationVerdictAt: r.remediationVerdictAt?.toISOString() ?? null
39
+ }))
40
+ };
41
+ const tmp = `${CALIBRATION_FILE}.tmp.${process.pid}`;
42
+ fs.writeFileSync(tmp, JSON.stringify(payload), "utf8");
43
+ fs.renameSync(tmp, CALIBRATION_FILE);
44
+ } catch (err) {
45
+ logger.warn({ err }, "calibration: persist failed");
46
+ }
47
+ }
48
+ function _trainClassifierFromCorpus() {
49
+ const tagMap = /* @__PURE__ */ new Map();
50
+ for (const r of _records) {
51
+ if (!r.verdict) continue;
52
+ if (r.isBuiltinSeed) continue;
53
+ const list = tagMap.get(r.tag) ?? [];
54
+ list.push(r);
55
+ tagMap.set(r.tag, list);
56
+ }
57
+ const samples = [];
58
+ for (const recs of tagMap.values()) {
59
+ const verdicts = recs.length;
60
+ let correct = 0;
61
+ for (const r of recs) {
62
+ if (r.verdict === "correct") correct += 1;
63
+ else if (r.verdict === "partial") correct += 0.5;
64
+ }
65
+ const accuracy = correct / verdicts;
66
+ const trusted = verdicts >= MIN_SAMPLES;
67
+ for (const r of recs) {
68
+ samples.push({
69
+ confidence: r.confidenceScore,
70
+ tagAccuracy: accuracy,
71
+ sampleCount: verdicts,
72
+ trusted,
73
+ isCorrect: r.verdict === "correct" || r.verdict === "partial"
74
+ });
75
+ }
76
+ }
77
+ if (samples.length >= 10) {
78
+ calibrationClassifier.trainBulk(samples);
79
+ }
80
+ logger.debug(
81
+ { samples: samples.length, totalRecords: _records.length },
82
+ "calibration: classifier warm-started from real corpus (seeds excluded)"
83
+ );
84
+ }
85
+ function load() {
86
+ if (_loaded) return;
87
+ _loaded = true;
88
+ if (zeroRetentionMode()) {
89
+ _seedBuiltInPriors();
90
+ return;
91
+ }
92
+ if (!fs.existsSync(CALIBRATION_FILE)) {
93
+ _seedBuiltInPriors();
94
+ schedulePersist();
95
+ _trainClassifierFromCorpus();
96
+ return;
97
+ }
98
+ try {
99
+ const raw = JSON.parse(fs.readFileSync(CALIBRATION_FILE, "utf8"));
100
+ if (raw?.version === 1 && Array.isArray(raw.records)) {
101
+ const now = Date.now();
102
+ _records = raw.records.filter((r) => r.expiresAt > now).map((r) => ({
103
+ pid: r.pid,
104
+ tag: r.tag,
105
+ confidence: r.confidence,
106
+ confidenceScore: r.confidenceScore,
107
+ predictedAt: new Date(r.predictedAt),
108
+ verdict: r.verdict,
109
+ verdictAt: r.verdictAt ? new Date(r.verdictAt) : null,
110
+ note: r.note,
111
+ verdictDimension: r.verdictDimension,
112
+ expiresAt: r.expiresAt,
113
+ isBuiltinSeed: r.isBuiltinSeed ?? false,
114
+ remediationVerdict: r.remediationVerdict ?? null,
115
+ remediationVerdictAt: r.remediationVerdictAt ? new Date(r.remediationVerdictAt) : null
116
+ }));
117
+ if (_records.length > 0) {
118
+ _corpusIsSeeded = _records.every((r) => r.isBuiltinSeed);
119
+ }
120
+ if (_records.length === 0) {
121
+ _seedBuiltInPriors();
122
+ schedulePersist();
123
+ }
124
+ } else {
125
+ _seedBuiltInPriors();
126
+ schedulePersist();
127
+ }
128
+ } catch (err) {
129
+ logger.warn({ err }, "calibration: failed to load \u2014 seeding built-in priors");
130
+ _seedBuiltInPriors();
131
+ schedulePersist();
132
+ }
133
+ _trainClassifierFromCorpus();
134
+ }
135
+ function _resetForTesting() {
136
+ _records = [];
137
+ _loaded = true;
138
+ }
139
+ function recordPrediction(hypotheses) {
140
+ load();
141
+ return hypotheses.map((h) => {
142
+ const existing = h.pid ? _records.find((r) => r.pid === h.pid) : null;
143
+ if (existing) return h;
144
+ const pid = randomUUID();
145
+ _records.push({
146
+ pid,
147
+ tag: h.tag,
148
+ confidence: h.confidence,
149
+ confidenceScore: h.confidenceScore,
150
+ predictedAt: /* @__PURE__ */ new Date(),
151
+ verdict: null,
152
+ verdictAt: null,
153
+ note: null,
154
+ verdictDimension: null,
155
+ // Pending predictions expire after PENDING_TTL_MS (30 days). If a verdict
156
+ // is recorded before expiry, recordVerdict() extends expiresAt to
157
+ // LABELED_TTL_MS (90 days) so the labeled record survives for classifier
158
+ // training. The two TTLs serve different purposes and must not be merged.
159
+ expiresAt: Date.now() + PENDING_TTL_MS,
160
+ isBuiltinSeed: false,
161
+ remediationVerdict: null,
162
+ remediationVerdictAt: null
163
+ });
164
+ schedulePersist();
165
+ return { ...h, pid };
166
+ });
167
+ }
168
+ function recordVerdict(pid, verdict, note, dimension) {
169
+ load();
170
+ const rec = _records.find((r) => r.pid === pid);
171
+ if (!rec) return { found: false, persisted: false };
172
+ rec.isBuiltinSeed = false;
173
+ _corpusIsSeeded = false;
174
+ if (dimension === "remediation") {
175
+ rec.remediationVerdict = verdict;
176
+ rec.remediationVerdictAt = /* @__PURE__ */ new Date();
177
+ } else {
178
+ rec.verdict = verdict;
179
+ rec.verdictAt = /* @__PURE__ */ new Date();
180
+ rec.note = note ?? null;
181
+ rec.verdictDimension = dimension ?? null;
182
+ }
183
+ rec.expiresAt = Date.now() + LABELED_TTL_MS;
184
+ if (dimension !== "remediation") {
185
+ const stats = getStatsForTag(rec.tag);
186
+ calibrationClassifier.update(
187
+ rec.confidenceScore,
188
+ stats?.accuracy ?? 0.5,
189
+ stats?.verdicts ?? 0,
190
+ stats?.trusted ?? false,
191
+ verdict === "correct" || verdict === "partial"
192
+ );
193
+ invalidateThresholdCache();
194
+ }
195
+ schedulePersist();
196
+ gitSyncCalibration(rec.tag, verdict, pid);
197
+ return { found: true, persisted: true };
198
+ }
199
+ function getRecords() {
200
+ load();
201
+ return [..._records];
202
+ }
203
+ function normalizeNote(s) {
204
+ return s.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+/g, " ");
205
+ }
206
+ function _computeTagStats(tag, recs) {
207
+ const predictions = recs.length;
208
+ const withVerdict = recs.filter((r) => r.verdict !== null);
209
+ const verdicts = withVerdict.length;
210
+ let correct = 0;
211
+ for (const r of withVerdict) {
212
+ if (r.verdict === "correct") correct += 1;
213
+ else if (r.verdict === "partial") correct += 0.5;
214
+ }
215
+ const accuracy = verdicts > 0 ? correct / verdicts : 0;
216
+ const trusted = verdicts >= MIN_SAMPLES;
217
+ const cutoff7d = Date.now() - 7 * 24 * 60 * 60 * 1e3;
218
+ const recent = withVerdict.filter((r) => r.verdictAt && r.verdictAt.getTime() >= cutoff7d);
219
+ let recentCorrect = 0;
220
+ for (const r of recent) {
221
+ if (r.verdict === "correct") recentCorrect += 1;
222
+ else if (r.verdict === "partial") recentCorrect += 0.5;
223
+ }
224
+ const accuracy7d = recent.length >= 3 ? recentCorrect / recent.length : null;
225
+ const trendDelta = accuracy7d !== null ? accuracy7d - accuracy : null;
226
+ const noteGroups = /* @__PURE__ */ new Map();
227
+ for (const r of withVerdict) {
228
+ if (r.note && r.verdict === "wrong") {
229
+ const key = normalizeNote(r.note);
230
+ const ex = noteGroups.get(key);
231
+ if (ex) ex.count++;
232
+ else noteGroups.set(key, { canonical: r.note, count: 1 });
233
+ }
234
+ }
235
+ const commonFailureModes = [...noteGroups.values()].sort((a, b) => b.count - a.count).map(({ canonical, count }) => ({ note: canonical, count }));
236
+ const remediationRecs = recs.filter((r) => r.remediationVerdict !== null);
237
+ let remediationCorrect = 0;
238
+ for (const r of remediationRecs) {
239
+ if (r.remediationVerdict === "correct") remediationCorrect += 1;
240
+ else if (r.remediationVerdict === "partial") remediationCorrect += 0.5;
241
+ }
242
+ const remediationAccuracy = remediationRecs.length > 0 ? remediationCorrect / remediationRecs.length : null;
243
+ return {
244
+ tag,
245
+ predictions,
246
+ verdicts,
247
+ accuracy,
248
+ trusted,
249
+ isEmpirical: trusted,
250
+ shouldInterrupt: trusted && accuracy < SUPPRESS_THRESHOLD,
251
+ diagnosisAccuracy: accuracy,
252
+ remediationAccuracy,
253
+ trendDelta,
254
+ accuracy7d,
255
+ commonFailureModes
256
+ };
257
+ }
258
+ function getStats() {
259
+ load();
260
+ const tagMap = /* @__PURE__ */ new Map();
261
+ for (const r of _records) {
262
+ const list = tagMap.get(r.tag) ?? [];
263
+ list.push(r);
264
+ tagMap.set(r.tag, list);
265
+ }
266
+ return [...tagMap.entries()].map(([tag, recs]) => _computeTagStats(tag, recs));
267
+ }
268
+ function getGlobalStats() {
269
+ return null;
270
+ }
271
+ function classifyVerdict(beforeCount, afterCount) {
272
+ if (afterCount === 0) return beforeCount > 0 ? "correct" : "partial";
273
+ if (afterCount < beforeCount) return "partial";
274
+ return "wrong";
275
+ }
276
+ function recordRemediationVerdict(pid, verdict) {
277
+ return recordVerdict(pid, verdict, void 0, "remediation");
278
+ }
279
+ function isCorpusSeeded() {
280
+ load();
281
+ return _corpusIsSeeded;
282
+ }
283
+ function getStatsForTag(tag) {
284
+ load();
285
+ const recs = _records.filter((r) => r.tag === tag);
286
+ if (recs.length === 0) return void 0;
287
+ return _computeTagStats(tag, recs);
288
+ }
289
+ function applyCalibration(hypotheses) {
290
+ load();
291
+ const active = [];
292
+ const suppressed = [];
293
+ for (const h of hypotheses) {
294
+ const stats = getStatsForTag(h.tag);
295
+ if (!stats || !stats.trusted) {
296
+ active.push({ ...h, calibrationAction: "passed" });
297
+ continue;
298
+ }
299
+ if (stats.accuracy >= DEMOTE_THRESHOLD) {
300
+ active.push({ ...h, calibrationAction: "passed" });
301
+ } else if (stats.accuracy >= SUPPRESS_THRESHOLD) {
302
+ const demoted = {
303
+ ...h,
304
+ confidence: h.confidence === "HIGH" ? "MEDIUM" : h.confidence === "MEDIUM" ? "LOW" : "LOW",
305
+ confidenceScore: h.confidenceScore * 0.5,
306
+ calibrationAction: "demoted"
307
+ };
308
+ active.push(demoted);
309
+ } else {
310
+ suppressed.push({ ...h, calibrationAction: "suppressed" });
311
+ }
312
+ }
313
+ return { active, suppressed };
314
+ }
315
+ function updateCalibrationNote(pid, note) {
316
+ load();
317
+ const rec = _records.find((r) => r.pid === pid);
318
+ if (!rec) return false;
319
+ rec.note = note.slice(0, 500);
320
+ schedulePersist();
321
+ return true;
322
+ }
323
+ function getRealVerdictCount() {
324
+ load();
325
+ return _records.filter((r) => !r.isBuiltinSeed && r.verdict !== null).length;
326
+ }
327
+ function getPendingFeedback() {
328
+ load();
329
+ const now = Date.now();
330
+ return _records.filter((r) => r.verdict === null && r.expiresAt > now).map((r) => ({ pid: r.pid, tag: r.tag, expiresAt: r.expiresAt }));
331
+ }
332
+ function seedCalibration(tag, counts) {
333
+ load();
334
+ const { correct = 0, wrong = 0, partial = 0 } = counts;
335
+ const addRecords = (count, verdict) => {
336
+ for (let i = 0; i < count; i++) {
337
+ _records.push({
338
+ pid: randomUUID(),
339
+ tag,
340
+ confidence: "HIGH",
341
+ confidenceScore: 0.9,
342
+ predictedAt: new Date(Date.now() - 6e4),
343
+ verdict,
344
+ verdictAt: /* @__PURE__ */ new Date(),
345
+ note: null,
346
+ verdictDimension: null,
347
+ expiresAt: Date.now() + LABELED_TTL_MS,
348
+ // already labeled — use retention TTL
349
+ isBuiltinSeed: false,
350
+ remediationVerdict: null,
351
+ remediationVerdictAt: null
352
+ });
353
+ }
354
+ };
355
+ addRecords(correct, "correct");
356
+ addRecords(wrong, "wrong");
357
+ addRecords(partial, "partial");
358
+ schedulePersist();
359
+ }
360
+ import { createHash } from "crypto";
361
+ function csvQuote(s) {
362
+ if (s === null || s === void 0) return "";
363
+ if (!/[",\n\r]/.test(s)) return s;
364
+ return '"' + s.replace(/"/g, '""') + '"';
365
+ }
366
+ function exportCsv() {
367
+ load();
368
+ const header = "pid,tag,confidence,predictedAt,verdict,verdictAt,note,verdictDimension";
369
+ const rows = _records.map((r) => [
370
+ r.pid,
371
+ r.tag,
372
+ r.confidence,
373
+ r.predictedAt.toISOString(),
374
+ r.verdict ?? "",
375
+ r.verdictAt ? r.verdictAt.toISOString() : "",
376
+ csvQuote(r.note),
377
+ r.verdictDimension ?? ""
378
+ ].join(","));
379
+ const dataBlock = rows.length > 0 ? rows.join("\n") : "";
380
+ const sha256 = createHash("sha256").update(dataBlock).digest("hex");
381
+ const comment = `# rows: ${rows.length}, sha256: ${sha256}`;
382
+ if (rows.length === 0) return `${comment}
383
+ ${header}`;
384
+ return `${comment}
385
+ ${header}
386
+ ${dataBlock}
387
+ `;
388
+ }
389
+ const CALIBRATION_CONFIG = {};
390
+ function _seedBuiltInPriors() {
391
+ if (_records.length > 0) return;
392
+ _corpusIsSeeded = true;
393
+ const DAY = 24 * 60 * 60 * 1e3;
394
+ const seeds = [
395
+ ["auth_token_not_persisted", 17, 3],
396
+ ["disk_full", 19, 1],
397
+ ["memory_leak_oom", 23, 2],
398
+ ["deployment_induced_regression", 22, 3],
399
+ ["missing_env_var", 20, 2],
400
+ ["unhandled_promise_rejection", 22, 2],
401
+ ["connection_refused", 19, 3],
402
+ ["rate_limit_silent", 18, 4],
403
+ ["cors_preflight_failure", 20, 4],
404
+ ["jwt_expiry", 21, 3],
405
+ ["n_plus_one_query", 15, 6],
406
+ ["health_check_degraded", 18, 5],
407
+ ["stale_cache", 14, 6],
408
+ ["cascading_timeout", 17, 6],
409
+ ["connection_pool_exhausted", 19, 5],
410
+ ["session_fixation", 15, 7],
411
+ ["db_migration_lock", 16, 6],
412
+ ["failed_migration", 20, 3],
413
+ ["slow_api_silent", 16, 7],
414
+ ["empty_response_silent", 14, 8]
415
+ ];
416
+ for (const [tag, correct, wrong] of seeds) {
417
+ for (let i = 0; i < correct; i++) {
418
+ _records.push({
419
+ pid: randomUUID(),
420
+ tag,
421
+ confidence: "HIGH",
422
+ confidenceScore: 0.88,
423
+ predictedAt: new Date(Date.now() - Math.floor(Math.random() * 60) * DAY),
424
+ verdict: "correct",
425
+ verdictAt: new Date(Date.now() - Math.floor(Math.random() * 59) * DAY),
426
+ note: null,
427
+ verdictDimension: null,
428
+ expiresAt: Date.now() + SEED_TTL_MS,
429
+ isBuiltinSeed: true,
430
+ remediationVerdict: null,
431
+ remediationVerdictAt: null
432
+ });
433
+ }
434
+ for (let i = 0; i < wrong; i++) {
435
+ _records.push({
436
+ pid: randomUUID(),
437
+ tag,
438
+ confidence: "HIGH",
439
+ confidenceScore: 0.88,
440
+ predictedAt: new Date(Date.now() - Math.floor(Math.random() * 60) * DAY),
441
+ verdict: "wrong",
442
+ verdictAt: new Date(Date.now() - Math.floor(Math.random() * 59) * DAY),
443
+ note: null,
444
+ verdictDimension: null,
445
+ expiresAt: Date.now() + SEED_TTL_MS,
446
+ isBuiltinSeed: true,
447
+ remediationVerdict: null,
448
+ remediationVerdictAt: null
449
+ });
450
+ }
451
+ }
452
+ }
453
+ export {
454
+ CALIBRATION_CONFIG,
455
+ _resetForTesting,
456
+ applyCalibration,
457
+ classifyVerdict,
458
+ exportCsv,
459
+ getGlobalStats,
460
+ getPendingFeedback,
461
+ getRealVerdictCount,
462
+ getRecords,
463
+ getStats,
464
+ getStatsForTag,
465
+ isCorpusSeeded,
466
+ recordPrediction,
467
+ recordRemediationVerdict,
468
+ recordVerdict,
469
+ seedCalibration,
470
+ updateCalibrationNote
471
+ };