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
@@ -0,0 +1,185 @@
1
+ import fs from "fs/promises";
2
+ import { DATA_DIR, LICENSE_FILE } from "../sensor/paths.js";
3
+ import { PLANS } from "./plans.js";
4
+ import logger from "../sensor/logger.js";
5
+ let _state = null;
6
+ function getLicenseState() {
7
+ return _state;
8
+ }
9
+ function getActivePlanId() {
10
+ return _state?.planId ?? "free";
11
+ }
12
+ async function initLicense() {
13
+ try {
14
+ const raw = await fs.readFile(LICENSE_FILE, "utf8");
15
+ const parsed = JSON.parse(raw);
16
+ if (parsed.key && parsed.planId && PLANS[parsed.planId]) {
17
+ _state = parsed;
18
+ logger.info({ planId: _state.planId }, "license: loaded");
19
+ }
20
+ } catch (err) {
21
+ if (err.code !== "ENOENT") {
22
+ logger.warn({ err }, "license: failed to read license file");
23
+ }
24
+ }
25
+ }
26
+ async function activateKey(key) {
27
+ const apiUrl = process.env.MERGEN_API_URL ?? "https://api.mergen.app";
28
+ if (key.startsWith("mrgn_")) {
29
+ return _activateViaCloud(key, apiUrl);
30
+ }
31
+ return _activateViaLemonSqueezy(key);
32
+ }
33
+ async function _activateViaCloud(key, apiUrl) {
34
+ const res = await fetch(`${apiUrl}/v1/validate`, {
35
+ method: "POST",
36
+ headers: { "content-type": "application/json" },
37
+ body: JSON.stringify({ key, instance: "mergen-server" })
38
+ });
39
+ const data = await res.json();
40
+ if (!res.ok || !data.valid) {
41
+ throw new Error(data.error ?? "Key validation failed");
42
+ }
43
+ _state = {
44
+ key,
45
+ planId: data.planId ?? "free",
46
+ customerEmail: data.email ?? void 0,
47
+ validatedAt: (/* @__PURE__ */ new Date()).toISOString(),
48
+ lsSubscriptionItemId: data.subscriptionItemId ?? void 0,
49
+ resetsAt: data.resetsAt ?? void 0,
50
+ status: "active",
51
+ customerName: data.name ?? void 0
52
+ };
53
+ await _persist();
54
+ return { activated: true, planId: _state.planId, email: _state.customerEmail };
55
+ }
56
+ async function _activateViaLemonSqueezy(key) {
57
+ const res = await fetch("https://api.lemonsqueezy.com/v1/licenses/activate", {
58
+ method: "POST",
59
+ headers: { "content-type": "application/json" },
60
+ body: JSON.stringify({ license_key: key, instance_name: "mergen-server" })
61
+ });
62
+ const data = await res.json();
63
+ if (!res.ok || !data.activated) {
64
+ throw new Error(data.error ?? "Activation failed");
65
+ }
66
+ const meta = data.meta ?? {};
67
+ const variantId = meta.variant_id;
68
+ const instance = data.instance ?? {};
69
+ const resolvedPlan = planFromVariantId(variantId);
70
+ const hasVariant = variantId != null && variantId !== "" && variantId !== 0;
71
+ if (hasVariant && resolvedPlan === "free") {
72
+ throw new Error(
73
+ `License activated but its LemonSqueezy variant (${String(variantId)}) is not mapped to a plan. Set MERGEN_LS_VARIANT_MAP={"${String(variantId)}":"<planId>"} and retry. Refusing to activate as free.`
74
+ );
75
+ }
76
+ _state = {
77
+ key,
78
+ planId: resolvedPlan,
79
+ customerEmail: meta.customer_email ?? void 0,
80
+ validatedAt: (/* @__PURE__ */ new Date()).toISOString(),
81
+ lsSubscriptionItemId: void 0,
82
+ lsInstanceId: instance.id ?? void 0,
83
+ status: "active"
84
+ };
85
+ await _persist();
86
+ return { activated: true, planId: _state.planId, email: _state.customerEmail };
87
+ }
88
+ async function _persist() {
89
+ try {
90
+ await fs.mkdir(DATA_DIR, { recursive: true });
91
+ await fs.writeFile(LICENSE_FILE, JSON.stringify(_state, null, 2));
92
+ } catch (err) {
93
+ logger.warn({ err }, "license: failed to persist license file");
94
+ }
95
+ }
96
+ async function deactivateKey() {
97
+ if (!_state) return;
98
+ try {
99
+ if (_state.key.startsWith("mrgn_")) {
100
+ const apiUrl = process.env.MERGEN_API_URL ?? "https://api.mergen.app";
101
+ await fetch(`${apiUrl}/v1/deactivate`, {
102
+ method: "POST",
103
+ headers: { "content-type": "application/json" },
104
+ body: JSON.stringify({ key: _state.key })
105
+ });
106
+ } else if (_state.lsInstanceId) {
107
+ await fetch("https://api.lemonsqueezy.com/v1/licenses/deactivate", {
108
+ method: "POST",
109
+ headers: { "content-type": "application/json" },
110
+ body: JSON.stringify({ license_key: _state.key, instance_id: _state.lsInstanceId })
111
+ });
112
+ }
113
+ } catch (err) {
114
+ logger.warn({ err }, "license: deactivate request failed");
115
+ }
116
+ _state = null;
117
+ try {
118
+ await fs.rm(LICENSE_FILE);
119
+ } catch {
120
+ }
121
+ }
122
+ const VARIANT_PLAN_MAP = {
123
+ // Map LemonSqueezy variant IDs to plan IDs.
124
+ // Override with MERGEN_LS_VARIANT_MAP={"123":"solo_pro",...} if needed.
125
+ ...process.env.MERGEN_LS_VARIANT_MAP ? JSON.parse(process.env.MERGEN_LS_VARIANT_MAP) : {}
126
+ };
127
+ function planFromVariantId(variantId) {
128
+ if (variantId == null || variantId === "" || variantId === 0) return "free";
129
+ const key = String(variantId);
130
+ const mapped = VARIANT_PLAN_MAP[key];
131
+ if (mapped && PLANS[mapped]) return mapped;
132
+ logger.warn(
133
+ { variantId },
134
+ 'license: unknown LemonSqueezy variant ID \u2014 defaulting to free plan. Set MERGEN_LS_VARIANT_MAP={"' + key + '":"<planId>"} to fix.'
135
+ );
136
+ return "free";
137
+ }
138
+ async function revalidateLicense() {
139
+ if (!_state) return;
140
+ try {
141
+ if (_state.key.startsWith("mrgn_")) {
142
+ const apiUrl = process.env.MERGEN_API_URL ?? "https://api.mergen.app";
143
+ const res = await fetch(`${apiUrl}/v1/validate`, {
144
+ method: "POST",
145
+ headers: { "content-type": "application/json" },
146
+ body: JSON.stringify({ key: _state.key, instance: "mergen-server" })
147
+ });
148
+ const data = await res.json();
149
+ if (!res.ok || !data.valid) {
150
+ logger.warn({ status: res.status }, "license: revalidation failed \u2014 reverting to free");
151
+ _state = null;
152
+ try {
153
+ await fs.rm(LICENSE_FILE);
154
+ } catch {
155
+ }
156
+ }
157
+ } else {
158
+ const res = await fetch("https://api.lemonsqueezy.com/v1/licenses/validate", {
159
+ method: "POST",
160
+ headers: { "content-type": "application/json" },
161
+ body: JSON.stringify({ license_key: _state.key, instance_name: "mergen-server" })
162
+ });
163
+ const data = await res.json();
164
+ if (!res.ok || !data.valid) {
165
+ logger.warn({ status: res.status }, "license: revalidation failed \u2014 reverting to free");
166
+ _state = null;
167
+ try {
168
+ await fs.rm(LICENSE_FILE);
169
+ } catch {
170
+ }
171
+ }
172
+ }
173
+ } catch (err) {
174
+ logger.warn({ err }, "license: revalidation request failed \u2014 keeping current state");
175
+ }
176
+ }
177
+ export {
178
+ activateKey,
179
+ deactivateKey,
180
+ getActivePlanId,
181
+ getLicenseState,
182
+ initLicense,
183
+ planFromVariantId,
184
+ revalidateLicense
185
+ };
@@ -0,0 +1,62 @@
1
+ import { plattScale } from "./platt-scaling.js";
2
+ import { serviceGraph } from "../sensor/service-graph.js";
3
+ function formatValidatedFactsForLLM(hyp, service, gate, errorCount, runtimeFact, calibratedScore) {
4
+ const rawScore = hyp.confidenceScore;
5
+ const { calibrated: autoCalibrated, source, n } = plattScale(rawScore, hyp.tag);
6
+ const calibrated = calibratedScore ?? autoCalibrated;
7
+ const calibratedPct = Math.round(calibrated * 100);
8
+ const basisNote = source === "raw" ? "(prior \u2014 no calibration data yet)" : source === "empirical" ? `(empirical \u2014 ${n} verdicts)` : `(Platt-calibrated \u2014 ${n} verdicts)`;
9
+ const callers = serviceGraph.getCallers(service);
10
+ const callees = serviceGraph.getCallees(service);
11
+ const topoLines = [];
12
+ if (callers.length > 0) topoLines.push(`- Called by: ${callers.slice(0, 5).join(", ")}`);
13
+ if (callees.length > 0) topoLines.push(`- Calls: ${callees.slice(0, 5).join(", ")}`);
14
+ const topoSection = topoLines.length > 0 ? `
15
+ Service topology (from live OTLP traces):
16
+ ${topoLines.join("\n")}` : "";
17
+ const signalLines = [
18
+ `- Classifier P(correct): ${(gate.signals.classifierScore * 100).toFixed(0)}%`,
19
+ `- Blast risk: ${gate.signals.blastRisk} (${gate.signals.upstreamImpact} upstream services)`,
20
+ gate.signals.histSuccessRate !== null ? `- Historical success rate for this detector: ${(gate.signals.histSuccessRate * 100).toFixed(0)}%` : "- Historical success rate: insufficient data"
21
+ ];
22
+ const systemPrompt = "You are the communications interface for an autonomous incident-response system. You will be given a structured facts package produced by deterministic analysis (topology graphs, calibrated classifiers, verdict corpus). Your ONLY job is to translate these validated facts into clear, human-readable engineering language. Do NOT add new hypotheses, speculate beyond the provided facts, or introduce uncertainty the data does not support. Do NOT pad with generic advice. Every sentence must trace back to a provided fact.";
23
+ const userPrompt = [
24
+ `## Incident Facts Package \u2014 ${service}`,
25
+ "",
26
+ `**Service under analysis:** ${service}`,
27
+ `**Error count in window:** ${errorCount}`,
28
+ topoSection,
29
+ "",
30
+ `## Validated Root Cause`,
31
+ `**Detector:** ${hyp.tag}`,
32
+ `**Summary:** ${hyp.summary}`,
33
+ `**Calibrated confidence:** ${calibratedPct}% ${basisNote}`,
34
+ hyp.causalPath.length > 0 ? `**Causal path:**
35
+ ${hyp.causalPath.map((s, i) => `${i + 1}. ${s}`).join("\n")}` : "",
36
+ hyp.evidence.length > 0 ? `**Evidence:**
37
+ ${hyp.evidence.map((e) => `- ${e}`).join("\n")}` : "",
38
+ "",
39
+ `## Validated Planning Decision`,
40
+ `**Decision:** ${gate.execute ? "EXECUTE FIX" : "HOLD \u2014 MANUAL ACTION REQUIRED"}`,
41
+ `**Reason:** ${gate.reason}`,
42
+ `**Signals:**
43
+ ${signalLines.join("\n")}`,
44
+ hyp.fixHint ? `
45
+ ## Recommended Action
46
+ ${hyp.fixHint}` : "",
47
+ runtimeFact ? `
48
+ ## Runtime Fact (Datadog trace summary)
49
+ ${runtimeFact.slice(0, 800)}` : "",
50
+ "",
51
+ "## Instructions",
52
+ "Write a concise Slack thread reply (max 300 words) that explains the root cause and recommended action to the on-call engineer. Use the facts above. Do not add speculation."
53
+ ].filter((l) => l !== null).join("\n");
54
+ return {
55
+ systemPrompt,
56
+ userPrompt,
57
+ estimatedTokens: Math.round((systemPrompt.length + userPrompt.length) / 4)
58
+ };
59
+ }
60
+ export {
61
+ formatValidatedFactsForLLM
62
+ };
@@ -0,0 +1,320 @@
1
+ import { z } from "zod";
2
+ import { store } from "../sensor/buffer.js";
3
+ import { hypothesisHistory } from "./hypothesis-history.js";
4
+ function registerPrompts(server) {
5
+ server.registerPrompt(
6
+ "debug",
7
+ {
8
+ description: "Universal debugging prompt \u2014 use this when something is broken. Pre-fills the AI with your live browser telemetry so you skip the copy-paste step. Works for any error type: JavaScript crashes, auth failures, network errors, performance issues.",
9
+ argsSchema: {
10
+ focus: z.enum(["errors", "network", "auth", "all"]).optional().describe('Narrow the focus (default: all). Use "auth" for login issues, "network" for API failures.')
11
+ }
12
+ },
13
+ async ({ focus = "all" }) => {
14
+ const errors = store.getLogs(10, "error");
15
+ const warns = store.getLogs(5, "warn");
16
+ const network = store.getNetwork(10);
17
+ const netFails = network.filter((n) => n.status >= 400 || n.status === 0 || !!n.error);
18
+ const contexts = store.getContext(3);
19
+ const signals = store.getSignals();
20
+ const latest = hypothesisHistory.latest();
21
+ const topHyp = latest?.topHypothesis ?? null;
22
+ const total = errors.length + netFails.length;
23
+ if (total === 0 && warns.length === 0) {
24
+ return {
25
+ messages: [{
26
+ role: "user",
27
+ content: {
28
+ type: "text",
29
+ text: [
30
+ "I want to debug my app with Mergen, but the buffer is currently empty.",
31
+ "",
32
+ "Steps to capture events:",
33
+ "1. Make sure the Mergen Chrome extension is active on this tab (check the toolbar \u2014 icon should be lit, not greyed out)",
34
+ ' Alternative: add `<script src="http://localhost:3000/sdk.js"></script>` to your page HTML',
35
+ "2. Reproduce the issue in your browser",
36
+ "3. Come back and ask me again",
37
+ "",
38
+ "Or run: `npx mergen-server demo` for an instant demo.",
39
+ "",
40
+ "Once events are captured, call `quick_check` to see what's in the buffer."
41
+ ].join("\n")
42
+ }
43
+ }]
44
+ };
45
+ }
46
+ const lines = [];
47
+ if (topHyp && topHyp.confidenceScore >= 0.45) {
48
+ const pct = Math.round(topHyp.confidenceScore * 100);
49
+ lines.push(`## Prior hypothesis (${pct}% confidence)`);
50
+ lines.push(`**${topHyp.summary}**`);
51
+ if (topHyp.fixHint) lines.push(`Fix hint: ${topHyp.fixHint}`);
52
+ lines.push("");
53
+ }
54
+ lines.push(`## Live buffer state`);
55
+ lines.push(`- Console errors: ${errors.length}`);
56
+ lines.push(`- Warnings: ${warns.length}`);
57
+ lines.push(`- Network failures: ${netFails.length}`);
58
+ if (signals.length > 0) {
59
+ lines.push(`- Detected patterns: ${signals.map((s) => s.message).join("; ")}`);
60
+ }
61
+ lines.push("");
62
+ if ((focus === "all" || focus === "errors") && errors.length > 0) {
63
+ lines.push("## Recent console errors");
64
+ for (const e of errors.slice(0, 5)) {
65
+ const msg = e.args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ").slice(0, 200);
66
+ lines.push(`- [${new Date(e.timestamp).toISOString()}] ${msg}`);
67
+ if (e.stack) lines.push(` Stack: ${e.stack.split("\n")[0]}`);
68
+ }
69
+ lines.push("");
70
+ }
71
+ if ((focus === "all" || focus === "network" || focus === "auth") && netFails.length > 0) {
72
+ lines.push("## Failing network requests");
73
+ for (const n of netFails.slice(0, 5)) {
74
+ const traceNote = n.traceId ? ` [traceId: ${n.traceId.slice(0, 8)}\u2026]` : "";
75
+ lines.push(`- ${n.method} ${n.url} \u2192 ${n.status || "ERR"} (${n.duration}ms)${traceNote}`);
76
+ if (n.error) lines.push(` Error: ${n.error}`);
77
+ }
78
+ lines.push("");
79
+ }
80
+ if ((focus === "auth" || focus === "all") && contexts.length > 0) {
81
+ const ctx = contexts[contexts.length - 1];
82
+ const authKeys = Object.entries(ctx.localStorage).filter(([k]) => /token|auth|session|jwt|user/i.test(k));
83
+ if (authKeys.length > 0) {
84
+ lines.push("## Auth-related localStorage");
85
+ for (const [k, v] of authKeys) lines.push(`- ${k}: ${v.slice(0, 100)}`);
86
+ lines.push("");
87
+ }
88
+ }
89
+ lines.push("## Task");
90
+ lines.push(
91
+ "Call `reconstruct_context` to get the root cause with source-mapped stack frames and a fix. If there are network failures with traceIds, call `get_unified_timeline` first \u2014 `EXACT` joins mean the browser request and backend log share the same trace ID (deterministic, not a guess). Lead your response with: what broke, why, and the exact code change needed."
92
+ );
93
+ return {
94
+ messages: [{
95
+ role: "user",
96
+ content: { type: "text", text: lines.join("\n") }
97
+ }]
98
+ };
99
+ }
100
+ );
101
+ server.registerPrompt(
102
+ "debug_auth_failure",
103
+ {
104
+ description: "Diagnose why login or authentication is broken. Use when users cannot sign in, tokens are missing after login, or auth endpoints are returning 4xx/5xx. Injects live error logs, auth network calls, and localStorage state."
105
+ },
106
+ async () => {
107
+ const errors = store.getLogs(10, "error");
108
+ const authNetwork = store.getNetwork(10).filter(
109
+ (e) => /login|auth|signin|token|session/i.test(e.url)
110
+ );
111
+ const contexts = store.getContext(3);
112
+ const storage = contexts.length > 0 ? contexts[contexts.length - 1].localStorage : {};
113
+ const telemetry = { errors, authNetwork, localStorage: storage };
114
+ return {
115
+ messages: [{
116
+ role: "user",
117
+ content: {
118
+ type: "text",
119
+ text: "Authentication is broken in my app. Here is the live browser telemetry:\n\n" + JSON.stringify(telemetry, null, 2) + "\n\nDiagnose:\n1. Which auth endpoint is failing and the HTTP status/error\n2. Whether the token is being stored in localStorage after a successful response\n3. The exact code fix (before/after diff)"
120
+ }
121
+ }]
122
+ };
123
+ }
124
+ );
125
+ server.registerPrompt(
126
+ "debug_network_error",
127
+ {
128
+ description: "Diagnose a failing network request. Use when a specific API call returns 4xx/5xx or a network error. Optionally filter to a specific URL pattern.",
129
+ argsSchema: {
130
+ url_pattern: z.string().optional().describe("URL substring to focus on, e.g. /api/users or checkout")
131
+ }
132
+ },
133
+ async ({ url_pattern }) => {
134
+ let failed = store.getNetwork(20).filter(
135
+ (e) => e.status >= 400 || e.status === 0 || !!e.error
136
+ );
137
+ if (url_pattern) failed = failed.filter((e) => e.url.includes(url_pattern));
138
+ return {
139
+ messages: [{
140
+ role: "user",
141
+ content: {
142
+ type: "text",
143
+ text: "A network request is failing. Failed requests from the live buffer:\n\n" + JSON.stringify(failed, null, 2) + "\n\nDiagnose:\n1. Root cause of the failure\n2. Whether this is a client error (bad request) or server error\n3. The exact code fix"
144
+ }
145
+ }]
146
+ };
147
+ }
148
+ );
149
+ server.registerPrompt(
150
+ "debug_page_error",
151
+ {
152
+ description: "Diagnose the most recent JavaScript error or page crash. Use immediately after reproducing a bug to get a structured diagnosis with the stack trace, storage state, and network context."
153
+ },
154
+ async () => {
155
+ const errors = store.getLogs(5, "error");
156
+ const network = store.getNetwork(5);
157
+ const contexts = store.getContext(2);
158
+ const telemetry = { errors, lastNetworkCalls: network, dom: contexts };
159
+ return {
160
+ messages: [{
161
+ role: "user",
162
+ content: {
163
+ type: "text",
164
+ text: "The page just showed an error. Live telemetry at the moment of crash:\n\n" + JSON.stringify(telemetry, null, 2) + "\n\nDiagnose:\n1. Root cause with the exact source file and line\n2. What application state triggered it\n3. The fix as a before/after code diff"
165
+ }
166
+ }]
167
+ };
168
+ }
169
+ );
170
+ server.registerPrompt(
171
+ "summarize_session",
172
+ {
173
+ description: "Summarize the entire current debugging session. Use at end of session or when picking up after a break. Returns top issues found, what is healthy, and next actions."
174
+ },
175
+ async () => {
176
+ const counters = store.getCounters();
177
+ const signals = store.getSignals();
178
+ const errors = store.getLogs(10, "error");
179
+ const network = store.getNetwork(10);
180
+ const telemetry = { counters, signals, recentErrors: errors, recentNetwork: network };
181
+ return {
182
+ messages: [{
183
+ role: "user",
184
+ content: {
185
+ type: "text",
186
+ text: "Please summarize this debugging session:\n\n" + JSON.stringify(telemetry, null, 2) + "\n\nProvide:\n1. Top 3 issues found (with root causes)\n2. What is healthy and working correctly\n3. Recommended next actions in priority order"
187
+ }
188
+ }]
189
+ };
190
+ }
191
+ );
192
+ server.registerPrompt(
193
+ "fix",
194
+ {
195
+ description: "Check if your last code change fixed the problem. Call this after applying a fix and reloading the app. Returns: what was resolved, what still persists, and any new regressions.",
196
+ argsSchema: {
197
+ session_id: z.string().optional().describe("Debug session ID from start_debug_session (if you started one). Skip if you just want to check current error state.")
198
+ }
199
+ },
200
+ async ({ session_id }) => {
201
+ const errors = store.getLogs(200, "error");
202
+ const warns = store.getLogs(50, "warn");
203
+ const netFails = store.getNetwork(200).filter((n) => n.status >= 400 || n.status === 0 || !!n.error);
204
+ const signals = store.getSignals();
205
+ const lines = [];
206
+ if (session_id) {
207
+ lines.push(
208
+ `I applied a fix. Check the current state against the debug session baseline.
209
+ Call \`checkpoint_debug_session\` with session_id: "${session_id}" and note: "applied fix".
210
+ Then tell me: what resolved, what persists, any new regressions.`
211
+ );
212
+ } else {
213
+ const total = errors.length + netFails.length;
214
+ lines.push(`I just applied a fix and reloaded the app. Current Mergen state:
215
+ `);
216
+ lines.push(`- Console errors: ${errors.length}`);
217
+ lines.push(`- Network failures: ${netFails.length}`);
218
+ lines.push(`- Warnings: ${warns.length}`);
219
+ lines.push(`- Active signals: ${signals.length}`);
220
+ lines.push("");
221
+ if (errors.length > 0) {
222
+ lines.push("**Current errors (may or may not be the same as before the fix):**");
223
+ for (const e of errors.slice(0, 3)) {
224
+ const msg = e.args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ").slice(0, 150);
225
+ lines.push(`- ${msg}`);
226
+ }
227
+ lines.push("");
228
+ }
229
+ if (netFails.length > 0) {
230
+ lines.push("**Current network failures:**");
231
+ for (const n of netFails.slice(0, 3)) {
232
+ lines.push(`- ${n.method} ${n.url} \u2192 ${n.status || "ERR"}`);
233
+ }
234
+ lines.push("");
235
+ }
236
+ lines.push(
237
+ total === 0 ? `**Give me a verdict: \u2705 Buffer is clean \u2014 the fix appears to have worked. Tell me clearly: "Fixed" or "Not fixed" and what to do next.**` : `**Give me a verdict: is this the same error as before, or is it different? Clearly state: "Fixed", "Partially fixed" (describe what changed), or "Not fixed" (describe what's the same). Then tell me the next step.**`
238
+ );
239
+ lines.push("");
240
+ lines.push("Call `get_recent_logs` and `get_network_activity` to get the fresh events if needed.");
241
+ }
242
+ return {
243
+ messages: [{
244
+ role: "user",
245
+ content: { type: "text", text: lines.join("\n") }
246
+ }]
247
+ };
248
+ }
249
+ );
250
+ server.registerPrompt(
251
+ "why",
252
+ {
253
+ description: "Get the root cause of the current error \u2014 fastest path from symptom to fix. Use when you see an error but don't know why it's happening. Pre-fills the AI with the full error context so you skip description entirely.",
254
+ argsSchema: {
255
+ symptom: z.string().optional().describe('Optional: one sentence describing what you see (e.g. "login button does nothing", "page goes blank", "500 from /api/users"). Skip to use whatever is in the buffer.')
256
+ }
257
+ },
258
+ async ({ symptom }) => {
259
+ const errors = store.getLogs(5, "error");
260
+ const netFails = store.getNetwork(5).filter((n) => n.status >= 400 || n.status === 0 || !!n.error);
261
+ const contexts = store.getContext(2);
262
+ const latest = hypothesisHistory.latest();
263
+ const topHyp = latest?.topHypothesis;
264
+ if (errors.length === 0 && netFails.length === 0 && !symptom) {
265
+ return {
266
+ messages: [{
267
+ role: "user",
268
+ content: {
269
+ type: "text",
270
+ text: 'I want to diagnose a bug but the Mergen buffer is empty. Reproduce the issue in your browser first, then run /why again. Or describe the symptom: /why "login page goes blank after submit"'
271
+ }
272
+ }]
273
+ };
274
+ }
275
+ const lines = [];
276
+ if (symptom) lines.push(`**Symptom:** ${symptom}
277
+ `);
278
+ if (topHyp && topHyp.confidenceScore >= 0.5) {
279
+ const pct = Math.round(topHyp.confidenceScore * 100);
280
+ lines.push(`**Prior hypothesis (${pct}% confidence):** ${topHyp.summary}`);
281
+ if (topHyp.fixHint) lines.push(`**Prior fix hint:** ${topHyp.fixHint}`);
282
+ lines.push("");
283
+ }
284
+ lines.push("**Live telemetry:**");
285
+ for (const e of errors.slice(0, 3)) {
286
+ const msg = e.args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ").slice(0, 300);
287
+ lines.push(`
288
+ [ERROR] ${msg}`);
289
+ if (e.stack) lines.push(`Stack: ${e.stack.split("\n").slice(0, 4).join("\n ")}`);
290
+ if (e.gitSuspect) lines.push(`Git: ${e.gitSuspect.sha.slice(0, 7)} by ${e.gitSuspect.author} \u2014 "${e.gitSuspect.summary}"`);
291
+ }
292
+ for (const n of netFails.slice(0, 3)) {
293
+ lines.push(`
294
+ [NETWORK] ${n.method} ${n.url} \u2192 ${n.status || "ERR"} (${n.duration}ms)`);
295
+ if (n.error) lines.push(` Error: ${n.error}`);
296
+ if (n.traceId) lines.push(` TraceId: ${n.traceId} \u2014 call get_correlated_trace if you have backend spans`);
297
+ }
298
+ if (contexts.length > 0) {
299
+ const ctx = contexts[contexts.length - 1];
300
+ lines.push(`
301
+ [STATE] URL: ${ctx.url} | Component: ${ctx.component ?? "none"} | Focused: ${ctx.activeElement ?? "none"}`);
302
+ const authKeys = Object.entries(ctx.localStorage).filter(([k]) => /token|auth|user|session/i.test(k));
303
+ if (authKeys.length > 0) lines.push(`Auth state: ${authKeys.map(([k, v]) => `${k}=${v.slice(0, 30)}`).join(", ")}`);
304
+ }
305
+ lines.push("");
306
+ lines.push(
307
+ '**Task:** Explain WHY this is broken. One clear sentence: "This is broken because [specific cause]." Then give the exact file + line + code change that fixes it. Call `reconstruct_context` for source-mapped stack frames if you need more context. Do NOT ask clarifying questions \u2014 diagnose from what you have.'
308
+ );
309
+ return {
310
+ messages: [{
311
+ role: "user",
312
+ content: { type: "text", text: lines.join("\n") }
313
+ }]
314
+ };
315
+ }
316
+ );
317
+ }
318
+ export {
319
+ registerPrompts
320
+ };