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,122 @@
1
+ import { spawn } from "child_process";
2
+ import fs from "fs";
3
+ import { store } from "./buffer.js";
4
+ import logger from "./logger.js";
5
+ const HEAP_CHECK_INTERVAL_MS = 3e4;
6
+ const HEAP_WARN_MARGIN_BYTES = 40 * 1024 * 1024;
7
+ function readCgroupMemoryLimit() {
8
+ try {
9
+ const v2 = fs.readFileSync("/sys/fs/cgroup/memory.max", "utf8").trim();
10
+ if (v2 !== "max") return parseInt(v2, 10);
11
+ } catch {
12
+ }
13
+ try {
14
+ const v1 = fs.readFileSync("/sys/fs/cgroup/memory/memory.limit_in_bytes", "utf8").trim();
15
+ const limit = parseInt(v1, 10);
16
+ if (limit > 0 && limit < 9e18) return limit;
17
+ } catch {
18
+ }
19
+ return null;
20
+ }
21
+ let _heapTimer = null;
22
+ let _lastHeapWarnAt = 0;
23
+ function startHeapMonitor() {
24
+ if (_heapTimer) return;
25
+ const limit = readCgroupMemoryLimit();
26
+ if (!limit) return;
27
+ const limitMb = Math.round(limit / 1024 / 1024);
28
+ logger.info({ limitMb }, "container memory limit detected \u2014 starting heap monitor");
29
+ _heapTimer = setInterval(() => {
30
+ const { rss } = process.memoryUsage();
31
+ const headroom = limit - rss;
32
+ if (headroom < HEAP_WARN_MARGIN_BYTES) {
33
+ const now = Date.now();
34
+ if (now - _lastHeapWarnAt < 5 * 6e4) return;
35
+ _lastHeapWarnAt = now;
36
+ const rssMb = Math.round(rss / 1024 / 1024);
37
+ const headroomMb = Math.round(headroom / 1024 / 1024);
38
+ logger.warn({ rssMb, limitMb, headroomMb }, "heap approaching container memory limit \u2014 OOM risk");
39
+ const event = {
40
+ type: "process_exit",
41
+ process: "current-process",
42
+ exitCode: -1,
43
+ reason: "oom",
44
+ memoryLimitBytes: limit,
45
+ timestamp: now
46
+ };
47
+ store.push(event);
48
+ }
49
+ }, HEAP_CHECK_INTERVAL_MS);
50
+ if (typeof _heapTimer.unref === "function") _heapTimer.unref();
51
+ }
52
+ function stopHeapMonitor() {
53
+ if (_heapTimer) {
54
+ clearInterval(_heapTimer);
55
+ _heapTimer = null;
56
+ }
57
+ }
58
+ let _dockerProc = null;
59
+ function startDockerMonitor() {
60
+ if (_dockerProc) return;
61
+ try {
62
+ _dockerProc = spawn(
63
+ "docker",
64
+ ["events", "--filter", "event=oom", "--filter", "event=die", "--format", "{{json .}}"],
65
+ { stdio: ["ignore", "pipe", "ignore"] }
66
+ );
67
+ } catch {
68
+ return;
69
+ }
70
+ if (!_dockerProc.stdout) return;
71
+ let buffer = "";
72
+ _dockerProc.stdout.on("data", (chunk) => {
73
+ buffer += chunk.toString();
74
+ const lines = buffer.split("\n");
75
+ buffer = lines.pop() ?? "";
76
+ for (const line of lines) {
77
+ const trimmed = line.trim();
78
+ if (!trimmed) continue;
79
+ try {
80
+ const ev = JSON.parse(trimmed);
81
+ if (ev.Action === "oom" || ev.Action === "die" && ev.Actor?.Attributes?.exitCode === "137") {
82
+ const containerName = ev.Actor?.Attributes?.name ?? ev.Actor?.ID?.slice(0, 12) ?? "unknown";
83
+ logger.warn({ containerName, action: ev.Action }, "Docker OOM kill detected");
84
+ const event = {
85
+ type: "process_exit",
86
+ process: containerName,
87
+ exitCode: 137,
88
+ reason: "oom",
89
+ signal: "SIGKILL",
90
+ timestamp: (ev.time ?? Date.now() / 1e3) * 1e3
91
+ };
92
+ store.push(event);
93
+ }
94
+ } catch {
95
+ }
96
+ }
97
+ });
98
+ _dockerProc.on("error", () => {
99
+ _dockerProc = null;
100
+ });
101
+ _dockerProc.on("exit", (code) => {
102
+ if (code !== 0 && code !== null) {
103
+ logger.debug({ code }, "docker events process exited");
104
+ }
105
+ _dockerProc = null;
106
+ });
107
+ logger.info("Docker OOM monitor started");
108
+ }
109
+ function stopDockerMonitor() {
110
+ if (_dockerProc) {
111
+ _dockerProc.stdout?.destroy();
112
+ _dockerProc.kill();
113
+ _dockerProc = null;
114
+ }
115
+ stopHeapMonitor();
116
+ }
117
+ export {
118
+ startDockerMonitor,
119
+ startHeapMonitor,
120
+ stopDockerMonitor,
121
+ stopHeapMonitor
122
+ };
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+ const ComponentTreeNodeSchema = z.object({
3
+ name: z.string(),
4
+ type: z.string(),
5
+ // 'React' | 'Vue' | 'Vue3'
6
+ props: z.record(z.unknown()).optional(),
7
+ state: z.record(z.unknown()).optional(),
8
+ children: z.array(z.lazy(() => ComponentTreeNodeSchema)).optional()
9
+ });
10
+ const StateDiffSchema = z.object({
11
+ framework: z.string(),
12
+ // 'Redux' | 'Zustand' | 'Jotai' | 'MobX'
13
+ before: z.unknown().optional(),
14
+ after: z.unknown().optional(),
15
+ field: z.string().optional(),
16
+ timestamp: z.number()
17
+ });
18
+ const PerformanceTraceSchema = z.object({
19
+ entryType: z.string(),
20
+ // 'long-task' | 'layout-shift' | 'paint' | 'navigation'
21
+ name: z.string(),
22
+ startTime: z.number(),
23
+ duration: z.number(),
24
+ metadata: z.record(z.unknown()).optional()
25
+ });
26
+ const ExtendedContextSnapshotSchema = z.object({
27
+ type: z.literal("context"),
28
+ trigger: z.enum(["error", "warn", "pageload", "hmr", "baseline", "manual"]),
29
+ timestamp: z.number(),
30
+ url: z.string(),
31
+ title: z.string(),
32
+ activeElement: z.string().optional(),
33
+ component: z.string().optional(),
34
+ localStorage: z.record(z.string()).default({}),
35
+ sessionStorage: z.record(z.string()).default({}),
36
+ // Layer 1 extensions
37
+ componentTree: ComponentTreeNodeSchema.optional(),
38
+ stateDiff: StateDiffSchema.optional(),
39
+ performanceTrace: z.array(PerformanceTraceSchema).optional()
40
+ });
41
+ export {
42
+ ComponentTreeNodeSchema,
43
+ ExtendedContextSnapshotSchema,
44
+ PerformanceTraceSchema,
45
+ StateDiffSchema
46
+ };
@@ -0,0 +1,41 @@
1
+ import crypto from "crypto";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { DATA_DIR } from "./paths.js";
5
+ const TOKEN_FILE = path.join(DATA_DIR, "feedback-secret");
6
+ const TOKEN_LEN = 32;
7
+ let _secret = "";
8
+ function getSecret() {
9
+ if (_secret) return _secret;
10
+ try {
11
+ const s = fs.readFileSync(TOKEN_FILE, "utf8").trim();
12
+ if (s.length >= 64) {
13
+ _secret = s;
14
+ return _secret;
15
+ }
16
+ } catch {
17
+ }
18
+ _secret = crypto.randomBytes(32).toString("hex");
19
+ try {
20
+ fs.mkdirSync(DATA_DIR, { recursive: true });
21
+ fs.writeFileSync(TOKEN_FILE, _secret, { mode: 384 });
22
+ } catch {
23
+ }
24
+ return _secret;
25
+ }
26
+ function generateFeedbackToken(id, correct) {
27
+ return crypto.createHmac("sha256", getSecret()).update(`${id}:${correct}`).digest("hex").slice(0, TOKEN_LEN);
28
+ }
29
+ function verifyFeedbackToken(id, correct, token) {
30
+ if (!token || token.length !== TOKEN_LEN) return false;
31
+ const expected = generateFeedbackToken(id, correct);
32
+ try {
33
+ return crypto.timingSafeEqual(Buffer.from(token, "hex"), Buffer.from(expected, "hex"));
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+ export {
39
+ generateFeedbackToken,
40
+ verifyFeedbackToken
41
+ };
@@ -0,0 +1,6 @@
1
+ function lockAndExecute(_lockFilePath, fn) {
2
+ return fn();
3
+ }
4
+ export {
5
+ lockAndExecute
6
+ };
@@ -0,0 +1,106 @@
1
+ import fs from "fs";
2
+ import { store } from "./buffer.js";
3
+ import { getRecords, recordVerdict } from "../intelligence/calibration.js";
4
+ import logger from "./logger.js";
5
+ const SETTLE_DELAY_MS = 2e3;
6
+ let _state = null;
7
+ function startFileWatch(opts) {
8
+ if (_state) stopFileWatch();
9
+ const watchers = [];
10
+ for (const p of opts.paths) {
11
+ try {
12
+ const recursive = p.endsWith("/") || !/\.[^/\\]+$/.test(p);
13
+ const watcher = fs.watch(p, { recursive }, () => {
14
+ if (!_state) return;
15
+ _state.lastChangedAt = Date.now();
16
+ scheduleValidation();
17
+ });
18
+ watcher.on("error", (err) => logger.warn({ path: p, err }, "fs-watcher: watch error"));
19
+ watchers.push(watcher);
20
+ } catch (err) {
21
+ logger.warn({ path: p, err }, "fs-watcher: could not watch path \u2014 skipping");
22
+ }
23
+ }
24
+ _state = {
25
+ pid: opts.pid,
26
+ since: opts.since,
27
+ paths: opts.paths,
28
+ watchers,
29
+ lastChangedAt: null,
30
+ validateTimer: null,
31
+ lastValidation: null
32
+ };
33
+ logger.info({ paths: opts.paths.length, pid: opts.pid }, "fs-watcher: started");
34
+ }
35
+ function scheduleValidation() {
36
+ if (!_state) return;
37
+ if (_state.validateTimer) clearTimeout(_state.validateTimer);
38
+ _state.validateTimer = setTimeout(runValidation, SETTLE_DELAY_MS);
39
+ if (typeof _state.validateTimer.unref === "function") _state.validateTimer.unref();
40
+ }
41
+ function runValidation() {
42
+ if (!_state) return;
43
+ const { pid, since } = _state;
44
+ const windowStart = since - 6e4;
45
+ const logsBefore = store.getLogs(200, "error", windowStart).filter((e) => e.timestamp < since);
46
+ const netBefore = store.getNetwork(200, void 0, windowStart).filter((e) => e.timestamp < since && (e.status >= 400 || !!e.error));
47
+ const logsAfter = store.getLogs(200, "error", since);
48
+ const netAfter = store.getNetwork(200, void 0, since).filter((e) => e.status >= 400 || !!e.error);
49
+ const errsBefore = logsBefore.length + netBefore.length;
50
+ const errsAfter = logsAfter.length + netAfter.length;
51
+ let verdict;
52
+ let status;
53
+ if (errsAfter === 0) {
54
+ verdict = "correct";
55
+ status = errsBefore > 0 ? "RESOLVED" : "CLEAN";
56
+ } else if (errsBefore > 0 && errsAfter < errsBefore * 0.5) {
57
+ verdict = "partial";
58
+ status = "PARTIAL";
59
+ } else {
60
+ verdict = "wrong";
61
+ status = errsAfter > errsBefore ? "REGRESSED" : "UNRESOLVED";
62
+ }
63
+ const records = getRecords();
64
+ const prediction = records.find((r) => r.pid === pid);
65
+ if (prediction && !prediction.verdict) recordVerdict(pid, verdict);
66
+ const result = { verdict, errsBefore, errsAfter, status, timestamp: Date.now() };
67
+ if (_state) _state.lastValidation = result;
68
+ store.push({
69
+ type: "terminal",
70
+ terminalName: "mergen:validate",
71
+ data: `[fix-validation] verdict=${verdict} before=${errsBefore} after=${errsAfter} pid=${pid}`,
72
+ timestamp: Date.now()
73
+ });
74
+ logger.info({ pid, verdict, errsBefore, errsAfter }, "fs-watcher: validation complete");
75
+ if (verdict === "correct") {
76
+ const t = setTimeout(stopFileWatch, 200);
77
+ if (typeof t.unref === "function") t.unref();
78
+ }
79
+ }
80
+ function stopFileWatch() {
81
+ if (!_state) return;
82
+ if (_state.validateTimer) clearTimeout(_state.validateTimer);
83
+ for (const w of _state.watchers) {
84
+ try {
85
+ w.close();
86
+ } catch {
87
+ }
88
+ }
89
+ logger.info({ pid: _state.pid }, "fs-watcher: stopped");
90
+ _state = null;
91
+ }
92
+ function getWatchState() {
93
+ if (!_state) return null;
94
+ return {
95
+ pid: _state.pid,
96
+ since: _state.since,
97
+ paths: _state.paths,
98
+ lastChangedAt: _state.lastChangedAt,
99
+ lastValidation: _state.lastValidation
100
+ };
101
+ }
102
+ export {
103
+ getWatchState,
104
+ startFileWatch,
105
+ stopFileWatch
106
+ };
@@ -0,0 +1,122 @@
1
+ import fs from "fs";
2
+ import { DATA_DIR, zeroRetentionMode } from "./paths.js";
3
+ import logger from "./logger.js";
4
+ const GATE_HEARTBEAT_FILE = `${DATA_DIR}/gate-heartbeat.json`;
5
+ const DEFAULT_INTERVAL_MS = 2e3;
6
+ const DEFAULT_MAX_AGE_MS = 1e4;
7
+ let _lastBeatAt = 0;
8
+ let _startedAt = 0;
9
+ let _handle = null;
10
+ function heartbeatIntervalMs() {
11
+ const raw = Number(process.env.MERGEN_GATE_HEARTBEAT_INTERVAL_MS);
12
+ return Number.isFinite(raw) && raw >= 250 ? raw : DEFAULT_INTERVAL_MS;
13
+ }
14
+ function gateHeartbeatMaxAgeMs() {
15
+ const raw = Number(process.env.MERGEN_GATE_HEARTBEAT_MAX_AGE_MS);
16
+ return Number.isFinite(raw) && raw >= 1e3 ? raw : DEFAULT_MAX_AGE_MS;
17
+ }
18
+ function requireGateHeartbeat() {
19
+ return process.env.MERGEN_REQUIRE_GATE_HEARTBEAT === "true";
20
+ }
21
+ function writeBeat(now = Date.now()) {
22
+ _lastBeatAt = now;
23
+ if (_startedAt === 0) _startedAt = now;
24
+ if (zeroRetentionMode()) return;
25
+ try {
26
+ fs.mkdirSync(DATA_DIR, { recursive: true });
27
+ const payload = {
28
+ version: 1,
29
+ pid: process.pid,
30
+ startedAt: _startedAt,
31
+ lastBeatAt: now
32
+ };
33
+ const tmp = `${GATE_HEARTBEAT_FILE}.tmp.${process.pid}`;
34
+ fs.writeFileSync(tmp, JSON.stringify(payload), { encoding: "utf8", mode: 384 });
35
+ fs.renameSync(tmp, GATE_HEARTBEAT_FILE);
36
+ } catch (err) {
37
+ logger.warn({ err }, "gate-heartbeat: failed to write heartbeat");
38
+ }
39
+ }
40
+ function startGateHeartbeat() {
41
+ if (_handle) return stopGateHeartbeat;
42
+ writeBeat();
43
+ _handle = setInterval(() => writeBeat(), heartbeatIntervalMs());
44
+ _handle.unref();
45
+ logger.info({ maxAgeMs: gateHeartbeatMaxAgeMs() }, "gate-heartbeat: started");
46
+ return stopGateHeartbeat;
47
+ }
48
+ function stopGateHeartbeat() {
49
+ if (_handle) {
50
+ clearInterval(_handle);
51
+ _handle = null;
52
+ }
53
+ }
54
+ function getGateHeartbeatStatus(now = Date.now()) {
55
+ const maxAgeMs = gateHeartbeatMaxAgeMs();
56
+ let lastBeatAt = _lastBeatAt;
57
+ if (!zeroRetentionMode() && fs.existsSync(GATE_HEARTBEAT_FILE)) {
58
+ try {
59
+ const raw = JSON.parse(fs.readFileSync(GATE_HEARTBEAT_FILE, "utf8"));
60
+ if (raw.version === 1 && typeof raw.lastBeatAt === "number") {
61
+ lastBeatAt = Math.max(lastBeatAt, raw.lastBeatAt);
62
+ }
63
+ } catch {
64
+ return {
65
+ ok: false,
66
+ required: requireGateHeartbeat(),
67
+ lastBeatAt: lastBeatAt || null,
68
+ ageMs: lastBeatAt ? now - lastBeatAt : null,
69
+ maxAgeMs,
70
+ reason: "gate heartbeat file is unreadable"
71
+ };
72
+ }
73
+ }
74
+ if (!lastBeatAt) {
75
+ return {
76
+ ok: false,
77
+ required: requireGateHeartbeat(),
78
+ lastBeatAt: null,
79
+ ageMs: null,
80
+ maxAgeMs,
81
+ reason: "gate heartbeat has not started"
82
+ };
83
+ }
84
+ const ageMs = now - lastBeatAt;
85
+ if (ageMs > maxAgeMs) {
86
+ return {
87
+ ok: false,
88
+ required: requireGateHeartbeat(),
89
+ lastBeatAt,
90
+ ageMs,
91
+ maxAgeMs,
92
+ reason: `gate heartbeat is stale (${ageMs}ms > ${maxAgeMs}ms)`
93
+ };
94
+ }
95
+ return {
96
+ ok: true,
97
+ required: requireGateHeartbeat(),
98
+ lastBeatAt,
99
+ ageMs,
100
+ maxAgeMs
101
+ };
102
+ }
103
+ function assertGateHeartbeatFresh() {
104
+ if (!requireGateHeartbeat()) return { ok: true };
105
+ const status = getGateHeartbeatStatus();
106
+ return status.ok ? { ok: true } : { ok: false, reason: status.reason ?? "gate heartbeat is not fresh" };
107
+ }
108
+ function _resetGateHeartbeatForTesting(lastBeatAt = 0) {
109
+ stopGateHeartbeat();
110
+ _startedAt = lastBeatAt;
111
+ _lastBeatAt = lastBeatAt;
112
+ }
113
+ export {
114
+ GATE_HEARTBEAT_FILE,
115
+ _resetGateHeartbeatForTesting,
116
+ assertGateHeartbeatFresh,
117
+ gateHeartbeatMaxAgeMs,
118
+ getGateHeartbeatStatus,
119
+ requireGateHeartbeat,
120
+ startGateHeartbeat,
121
+ stopGateHeartbeat
122
+ };
@@ -0,0 +1,136 @@
1
+ import { execFile } from "child_process";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { promisify } from "util";
5
+ const execFileAsync = promisify(execFile);
6
+ const CONVENTIONAL_HIGH = /^(feat|fix|refactor|perf)(\(.*?\))?!?:/;
7
+ const CONVENTIONAL_LOW = /^(chore|style|docs|ci|build|test)(\(.*?\))?!?:/;
8
+ async function findSuspectCommit(filePath, line, cwd) {
9
+ try {
10
+ const rel = path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath;
11
+ const { stdout: blameOut } = await execFileAsync(
12
+ "git",
13
+ ["blame", `-L${line},${line}`, "--porcelain", rel],
14
+ { cwd, timeout: 5e3 }
15
+ );
16
+ const sha = blameOut.slice(0, 40);
17
+ if (!sha || sha === "0".repeat(40)) return null;
18
+ const lines = blameOut.split("\n");
19
+ const get = (prefix) => lines.find((l) => l.startsWith(prefix))?.slice(prefix.length).trim() ?? "";
20
+ const author = get("author ");
21
+ const authorEmail = get("author-mail").replace(/[<>]/g, "");
22
+ const summary = get("summary");
23
+ const tsStr = get("author-time");
24
+ const timestamp = tsStr ? parseInt(tsStr, 10) * 1e3 : 0;
25
+ let causalWeight = "medium";
26
+ if (CONVENTIONAL_HIGH.test(summary)) causalWeight = "high";
27
+ else if (CONVENTIONAL_LOW.test(summary)) causalWeight = "low";
28
+ let hasLocalDiff = false;
29
+ let localDiffStat = "";
30
+ try {
31
+ const { stdout: diffOut } = await execFileAsync(
32
+ "git",
33
+ ["diff", "HEAD", "--stat", "--", rel],
34
+ { cwd, timeout: 3e3 }
35
+ );
36
+ hasLocalDiff = diffOut.trim().length > 0;
37
+ const statLine = diffOut.trim().split("\n").pop() ?? "";
38
+ localDiffStat = statLine.replace(/\s+\|\s+.*/, "").trim();
39
+ } catch {
40
+ }
41
+ return {
42
+ sha,
43
+ shortSha: sha.slice(0, 8),
44
+ author,
45
+ authorEmail,
46
+ summary,
47
+ timestamp,
48
+ causalWeight,
49
+ hasLocalDiff,
50
+ localDiffStat
51
+ };
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+ const _coCache = /* @__PURE__ */ new Map();
57
+ function _loadCodeowners(cwd) {
58
+ const candidates = [
59
+ path.join(cwd, ".github", "CODEOWNERS"),
60
+ path.join(cwd, "CODEOWNERS"),
61
+ path.join(cwd, "docs", "CODEOWNERS")
62
+ ];
63
+ for (const p of candidates) {
64
+ try {
65
+ const stat = fs.statSync(p);
66
+ const cached = _coCache.get(p);
67
+ if (cached && cached.mtime === stat.mtimeMs) return cached.parsed;
68
+ const lines = fs.readFileSync(p, "utf8").split("\n");
69
+ const parsed = [];
70
+ for (const line of lines) {
71
+ const trimmed = line.trim();
72
+ if (!trimmed || trimmed.startsWith("#")) continue;
73
+ const parts = trimmed.split(/\s+/);
74
+ if (parts.length < 2) continue;
75
+ parsed.push({ pattern: parts[0], owners: parts.slice(1) });
76
+ }
77
+ parsed.reverse();
78
+ _coCache.set(p, { parsed, mtime: stat.mtimeMs });
79
+ return parsed;
80
+ } catch {
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ function _matchesPattern(filePath, pattern) {
86
+ const fp = filePath.replace(/\\/g, "/");
87
+ const pat = pattern.replace(/\\/g, "/");
88
+ if (pat.endsWith("/")) return fp.startsWith(pat.slice(1)) || fp.startsWith(pat);
89
+ if (pat.includes("*")) {
90
+ const escaped = pat.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*");
91
+ return new RegExp(escaped + "$").test(fp) || new RegExp(escaped + "$").test(fp.split("/").pop() ?? "");
92
+ }
93
+ return fp === pat || fp === pat.replace(/^\//, "") || fp.endsWith("/" + pat) || fp.startsWith(pat + "/") || fp.startsWith(pat.replace(/^\//, "") + "/");
94
+ }
95
+ function findCodeOwners(filePath, cwd) {
96
+ try {
97
+ const rules = _loadCodeowners(cwd);
98
+ if (!rules) return null;
99
+ const rel = path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath;
100
+ const normalised = rel.replace(/\\/g, "/");
101
+ for (const rule of rules) {
102
+ if (_matchesPattern(normalised, rule.pattern)) {
103
+ const source = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"].find((f) => {
104
+ try {
105
+ fs.statSync(path.join(cwd, f));
106
+ return true;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }) ?? "CODEOWNERS";
111
+ return { owners: rule.owners, pattern: rule.pattern, source };
112
+ }
113
+ }
114
+ return null;
115
+ } catch {
116
+ return null;
117
+ }
118
+ }
119
+ async function getLocalDiff(filePath, cwd) {
120
+ try {
121
+ const rel = path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath;
122
+ const { stdout } = await execFileAsync(
123
+ "git",
124
+ ["diff", "HEAD", "--", rel],
125
+ { cwd, timeout: 5e3 }
126
+ );
127
+ return stdout.slice(0, 3e3);
128
+ } catch {
129
+ return "";
130
+ }
131
+ }
132
+ export {
133
+ findCodeOwners,
134
+ findSuspectCommit,
135
+ getLocalDiff
136
+ };
@@ -0,0 +1,77 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { DATA_DIR, zeroRetentionMode } from "./paths.js";
4
+ import logger from "./logger.js";
5
+ const HABITUATION_FILE = path.join(DATA_DIR, "habituation.json");
6
+ const MAX_EVENTS = 1e3;
7
+ let _events = [];
8
+ let _loaded = false;
9
+ function load() {
10
+ if (_loaded) return;
11
+ _loaded = true;
12
+ if (!fs.existsSync(HABITUATION_FILE)) return;
13
+ try {
14
+ const parsed = JSON.parse(fs.readFileSync(HABITUATION_FILE, "utf8"));
15
+ if (parsed?.version === 1 && Array.isArray(parsed.events)) _events = parsed.events;
16
+ } catch {
17
+ }
18
+ }
19
+ function persist() {
20
+ if (zeroRetentionMode()) return;
21
+ try {
22
+ fs.mkdirSync(DATA_DIR, { recursive: true, mode: 448 });
23
+ const tmp = `${HABITUATION_FILE}.tmp.${process.pid}`;
24
+ fs.writeFileSync(tmp, JSON.stringify({ version: 1, events: _events }), "utf8");
25
+ fs.renameSync(tmp, HABITUATION_FILE);
26
+ } catch (err) {
27
+ logger.warn({ err }, "habituation-store: persist failed");
28
+ }
29
+ }
30
+ function isoWeekKey(ts) {
31
+ const d = new Date(ts);
32
+ const thursday = new Date(d);
33
+ thursday.setDate(d.getDate() - (d.getDay() + 6) % 7 + 3);
34
+ const year = thursday.getFullYear();
35
+ const jan4 = new Date(year, 0, 4);
36
+ const week = 1 + Math.round((thursday.getTime() - jan4.getTime()) / (7 * 864e5));
37
+ return `${year}-W${String(week).padStart(2, "0")}`;
38
+ }
39
+ function recordHabituationEvent(event) {
40
+ load();
41
+ _events.push(event);
42
+ if (_events.length > MAX_EVENTS) _events = _events.slice(-MAX_EVENTS);
43
+ persist();
44
+ }
45
+ function getHabituationEvents() {
46
+ load();
47
+ return [..._events];
48
+ }
49
+ function getWeeklyHabituation(windowWeeks = 8) {
50
+ load();
51
+ const cutoff = Date.now() - windowWeeks * 7 * 24 * 60 * 60 * 1e3;
52
+ const recent = _events.filter((e) => e.recordedAt >= cutoff);
53
+ const byWeek = /* @__PURE__ */ new Map();
54
+ for (const e of recent) {
55
+ const wk = isoWeekKey(e.recordedAt);
56
+ if (!byWeek.has(wk)) byWeek.set(wk, { commented: /* @__PURE__ */ new Set(), engaged: /* @__PURE__ */ new Set(), count: 0 });
57
+ const w = byWeek.get(wk);
58
+ if (e.eventType === "comment_posted") {
59
+ w.commented.add(e.actor);
60
+ w.count++;
61
+ } else {
62
+ w.engaged.add(e.actor);
63
+ }
64
+ }
65
+ return [...byWeek.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([week, w]) => ({
66
+ week,
67
+ engineersWithComments: w.commented.size,
68
+ engineersEngaged: w.engaged.size,
69
+ engagementRate: w.commented.size > 0 ? w.engaged.size / w.commented.size : 0,
70
+ commentsPosted: w.count
71
+ }));
72
+ }
73
+ export {
74
+ getHabituationEvents,
75
+ getWeeklyHabituation,
76
+ recordHabituationEvent
77
+ };