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,134 @@
1
+ import { registerGovernanceHook, clearGovernanceHooks } from "./agent-pipeline.js";
2
+ import { getStores } from "../storage/store-registry.js";
3
+ import logger from "../sensor/logger.js";
4
+ const AGENT_HISTORY_LIMIT = 500;
5
+ const _baselines = /* @__PURE__ */ new Map();
6
+ let _gateEventCount = 0;
7
+ const BASELINE_RECOMPUTE_EVERY = 50;
8
+ function _buildBaseline(agentId, events) {
9
+ const agentEvents = events.filter((e) => e.agentId === agentId);
10
+ if (agentEvents.length < 5) {
11
+ return {
12
+ agentId,
13
+ avgCallsPerMin: 0,
14
+ blockRate: 0,
15
+ toolDistribution: {},
16
+ sampleCount: agentEvents.length,
17
+ computedAt: Date.now()
18
+ };
19
+ }
20
+ const blocks = agentEvents.filter((e) => e.verdict === "block").length;
21
+ const blockRate = blocks / agentEvents.length;
22
+ const span = agentEvents[agentEvents.length - 1].ts - agentEvents[0].ts;
23
+ const avgCallsPerMin = span > 0 ? agentEvents.length / (span / 6e4) : 0;
24
+ const toolCounts = {};
25
+ for (const e of agentEvents) {
26
+ toolCounts[e.toolName] = (toolCounts[e.toolName] ?? 0) + 1;
27
+ }
28
+ const toolDistribution = {};
29
+ for (const [tool, count] of Object.entries(toolCounts)) {
30
+ toolDistribution[tool] = count / agentEvents.length;
31
+ }
32
+ return { agentId, avgCallsPerMin, blockRate, toolDistribution, sampleCount: agentEvents.length, computedAt: Date.now() };
33
+ }
34
+ function detectAnomaly(agentId, recentEvents) {
35
+ const agentEvents = recentEvents.filter((e) => e.agentId === agentId);
36
+ if (agentEvents.length < 5) return { anomaly: false, severity: "low", reason: "insufficient data" };
37
+ const baseline = _baselines.get(agentId);
38
+ const last10 = agentEvents.slice(-10);
39
+ const recentBlocks = last10.filter((e) => e.verdict === "block").length;
40
+ if (recentBlocks > 5) {
41
+ return {
42
+ anomaly: true,
43
+ severity: "high",
44
+ reason: `Block rate spike: ${recentBlocks}/10 recent calls blocked (baseline: ${baseline ? (baseline.blockRate * 100).toFixed(0) : "?"}%)`
45
+ };
46
+ }
47
+ if (baseline && baseline.sampleCount >= 10) {
48
+ const last5min = agentEvents.filter((e) => e.ts > Date.now() - 5 * 6e4);
49
+ const currentRate = last5min.length / 5;
50
+ if (baseline.avgCallsPerMin > 0 && currentRate > baseline.avgCallsPerMin * 3) {
51
+ return {
52
+ anomaly: true,
53
+ severity: "medium",
54
+ reason: `Call rate spike: ${currentRate.toFixed(1)}/min vs baseline ${baseline.avgCallsPerMin.toFixed(1)}/min`
55
+ };
56
+ }
57
+ const recentTools = {};
58
+ for (const e of agentEvents.slice(-20)) {
59
+ recentTools[e.toolName] = (recentTools[e.toolName] ?? 0) + 1;
60
+ }
61
+ for (const [tool, count] of Object.entries(recentTools)) {
62
+ if (count > 3 && !(tool in baseline.toolDistribution)) {
63
+ return {
64
+ anomaly: true,
65
+ severity: "low",
66
+ reason: `Unseen tool \`${tool}\` called ${count} times \u2014 not in baseline distribution`
67
+ };
68
+ }
69
+ }
70
+ }
71
+ return { anomaly: false, severity: "low", reason: "" };
72
+ }
73
+ async function onGateEvent(agentId, events) {
74
+ if (!agentId || agentId === "agent") return;
75
+ _gateEventCount++;
76
+ if (_gateEventCount % BASELINE_RECOMPUTE_EVERY !== 0) return;
77
+ const durable = await _loadAgentHistory(agentId);
78
+ const source = durable.length >= 5 ? durable : events;
79
+ const baseline = _buildBaseline(agentId, source);
80
+ _baselines.set(agentId, baseline);
81
+ const report = detectAnomaly(agentId, source);
82
+ if (!report.anomaly) return;
83
+ logger.warn({ agentId, severity: report.severity, reason: report.reason }, "behavior-baseline: anomaly detected");
84
+ if (report.severity === "high") {
85
+ const capturedReason = report.reason;
86
+ registerGovernanceHook(async (_ctx) => {
87
+ clearGovernanceHooks();
88
+ return { verdict: "block", reason: `Anomaly block: ${capturedReason}` };
89
+ });
90
+ logger.warn({ agentId }, "behavior-baseline: high-severity anomaly \u2014 one-shot governance hook registered");
91
+ }
92
+ }
93
+ async function _loadAgentHistory(agentId) {
94
+ try {
95
+ const rows = await getStores().actionLedger.query({ agentId, limit: AGENT_HISTORY_LIMIT });
96
+ return rows.map((r) => ({ agentId: r.agentId, verdict: r.verdict, ts: r.ts, toolName: r.tool })).reverse();
97
+ } catch {
98
+ return [];
99
+ }
100
+ }
101
+ async function persistBaseline(agentId, baseline) {
102
+ try {
103
+ const { agentContextStore } = await import("../sensor/agent-context-store.js");
104
+ agentContextStore.store(agentId, "baseline_v1", JSON.stringify(baseline), 0);
105
+ } catch {
106
+ }
107
+ }
108
+ async function hydrateBaseline(agentId) {
109
+ if (!agentId || _baselines.has(agentId)) return;
110
+ try {
111
+ const { agentContextStore } = await import("../sensor/agent-context-store.js");
112
+ const entries = agentContextStore.recall(agentId, "baseline_v1", 1);
113
+ if (entries.length > 0) {
114
+ const b = JSON.parse(entries[0].value);
115
+ _baselines.set(agentId, b);
116
+ }
117
+ } catch {
118
+ }
119
+ }
120
+ function getBaseline(agentId) {
121
+ return _baselines.get(agentId) ?? null;
122
+ }
123
+ function _resetBaselinesForTesting() {
124
+ _baselines.clear();
125
+ _gateEventCount = 0;
126
+ }
127
+ export {
128
+ _resetBaselinesForTesting,
129
+ detectAnomaly,
130
+ getBaseline,
131
+ hydrateBaseline,
132
+ onGateEvent,
133
+ persistBaseline
134
+ };
@@ -0,0 +1 @@
1
+ export * from '../__stubs__/closed-source.js';
@@ -0,0 +1,224 @@
1
+ import { deriveRollback } from "./rollback.js";
2
+ const RULES = [
3
+ // kubectl rollout restart deploy/<name>
4
+ {
5
+ pattern: /kubectl\s+rollout\s+restart\s+(?:deploy(?:ment)?\/(\S+)|deploy(?:ment)?\s+(\S+))/i,
6
+ scope: "deployment",
7
+ estimatedDowntimeMs: 3e4,
8
+ reversible: true,
9
+ rollbackLatencyMs: 6e4,
10
+ dataAtRisk: false,
11
+ modelConfidence: "high",
12
+ extractResources: (m) => [`deploy/${m[1] ?? m[2] ?? "unknown"}`]
13
+ },
14
+ // kubectl set image deploy/<name>
15
+ {
16
+ pattern: /kubectl\s+set\s+image\s+(?:deploy(?:ment)?\/(\S+)|deploy(?:ment)?\s+(\S+))/i,
17
+ scope: "deployment",
18
+ estimatedDowntimeMs: 3e4,
19
+ reversible: true,
20
+ rollbackLatencyMs: 6e4,
21
+ dataAtRisk: false,
22
+ modelConfidence: "high",
23
+ extractResources: (m) => [`deploy/${m[1] ?? m[2] ?? "unknown"}`]
24
+ },
25
+ // kubectl set env deploy/<name>
26
+ {
27
+ pattern: /kubectl\s+set\s+env\s+(?:deploy(?:ment)?\/(\S+)|deploy(?:ment)?\s+(\S+))/i,
28
+ scope: "config-change",
29
+ estimatedDowntimeMs: 3e4,
30
+ reversible: true,
31
+ rollbackLatencyMs: 6e4,
32
+ dataAtRisk: false,
33
+ modelConfidence: "high",
34
+ extractResources: (m) => [`deploy/${m[1] ?? m[2] ?? "unknown"}`]
35
+ },
36
+ // kubectl delete deployment/<name>
37
+ {
38
+ pattern: /kubectl\s+delete\s+deploy(?:ment)?(?:\/(\S+)|\s+(\S+))/i,
39
+ scope: "deployment",
40
+ estimatedDowntimeMs: null,
41
+ reversible: false,
42
+ rollbackLatencyMs: null,
43
+ dataAtRisk: false,
44
+ modelConfidence: "high",
45
+ extractResources: (m) => [`deploy/${m[1] ?? m[2] ?? "unknown"}`]
46
+ },
47
+ // kubectl delete pod
48
+ {
49
+ pattern: /kubectl\s+delete\s+pod/i,
50
+ scope: "pod",
51
+ estimatedDowntimeMs: 5e3,
52
+ reversible: true,
53
+ rollbackLatencyMs: 1e4,
54
+ dataAtRisk: false,
55
+ modelConfidence: "high"
56
+ },
57
+ // helm upgrade <release>
58
+ {
59
+ pattern: /helm\s+upgrade\s+(\S+)/i,
60
+ scope: "deployment",
61
+ estimatedDowntimeMs: 6e4,
62
+ reversible: true,
63
+ rollbackLatencyMs: 12e4,
64
+ dataAtRisk: false,
65
+ modelConfidence: "high",
66
+ extractResources: (m) => [`helm-release/${m[1]}`]
67
+ },
68
+ // helm rollback <release>
69
+ {
70
+ pattern: /helm\s+rollback\s+(\S+)/i,
71
+ scope: "deployment",
72
+ estimatedDowntimeMs: 3e4,
73
+ reversible: true,
74
+ rollbackLatencyMs: 6e4,
75
+ dataAtRisk: false,
76
+ modelConfidence: "high",
77
+ extractResources: (m) => [`helm-release/${m[1]}`]
78
+ },
79
+ // npm install pkg@ver
80
+ {
81
+ pattern: /npm\s+install\s+(\S+@\S+)/i,
82
+ scope: "config-change",
83
+ estimatedDowntimeMs: 12e4,
84
+ reversible: true,
85
+ rollbackLatencyMs: 3e5,
86
+ dataAtRisk: false,
87
+ modelConfidence: "medium",
88
+ extractResources: (m) => [`npm:${m[1]}`]
89
+ },
90
+ // pip install pkg==ver
91
+ {
92
+ pattern: /pip\s+install\s+(\S+==[^\s]+)/i,
93
+ scope: "config-change",
94
+ estimatedDowntimeMs: 12e4,
95
+ reversible: true,
96
+ rollbackLatencyMs: 3e5,
97
+ dataAtRisk: false,
98
+ modelConfidence: "medium",
99
+ extractResources: (m) => [`pip:${m[1]}`]
100
+ },
101
+ // yarn add pkg@ver
102
+ {
103
+ pattern: /yarn\s+add\s+(\S+@\S+)/i,
104
+ scope: "config-change",
105
+ estimatedDowntimeMs: 12e4,
106
+ reversible: true,
107
+ rollbackLatencyMs: 3e5,
108
+ dataAtRisk: false,
109
+ modelConfidence: "medium",
110
+ extractResources: (m) => [`yarn:${m[1]}`]
111
+ },
112
+ // pm2 / systemctl / service restart
113
+ {
114
+ pattern: /(?:pm2\s+(?:restart|reload)\s+(\S+)|systemctl\s+(?:restart|reload)\s+(\S+)|service\s+(\S+)\s+(?:restart|reload))/i,
115
+ scope: "pod",
116
+ estimatedDowntimeMs: 5e3,
117
+ reversible: true,
118
+ rollbackLatencyMs: 1e4,
119
+ dataAtRisk: false,
120
+ modelConfidence: "high",
121
+ extractResources: (m) => [`service/${m[1] ?? m[2] ?? m[3] ?? "unknown"}`]
122
+ },
123
+ // docker restart <container>
124
+ {
125
+ pattern: /docker\s+restart\s+(\S+)/i,
126
+ scope: "pod",
127
+ estimatedDowntimeMs: 5e3,
128
+ reversible: true,
129
+ rollbackLatencyMs: 1e4,
130
+ dataAtRisk: false,
131
+ modelConfidence: "high",
132
+ extractResources: (m) => [`container/${m[1]}`]
133
+ },
134
+ // SQL DROP / TRUNCATE
135
+ {
136
+ pattern: /(?:DROP\s+(?:TABLE|DATABASE)|TRUNCATE(?:\s+TABLE)?)\s+(\S+)?/i,
137
+ scope: "data-destructive",
138
+ estimatedDowntimeMs: 0,
139
+ reversible: false,
140
+ rollbackLatencyMs: null,
141
+ dataAtRisk: true,
142
+ modelConfidence: "high",
143
+ extractResources: (m) => m[1] ? [`table/${m[1]}`] : []
144
+ },
145
+ // SQL UPDATE / INSERT
146
+ {
147
+ pattern: /(?:UPDATE|INSERT\s+INTO)\s+(\S+)/i,
148
+ scope: "data-write",
149
+ estimatedDowntimeMs: 0,
150
+ reversible: true,
151
+ rollbackLatencyMs: null,
152
+ dataAtRisk: true,
153
+ modelConfidence: "medium",
154
+ extractResources: (m) => [`table/${m[1]}`]
155
+ }
156
+ ];
157
+ function buildSummary(scope, estimatedDowntimeMs, reversible, dataAtRisk) {
158
+ const dt = estimatedDowntimeMs !== null ? `~${Math.round(estimatedDowntimeMs / 1e3)}s downtime` : "unpredictable downtime";
159
+ const rev = reversible ? "reversible" : "irreversible";
160
+ const data = dataAtRisk ? ", data at risk" : "";
161
+ return `${scope} scope, ${dt}, ${rev}${data}.`;
162
+ }
163
+ function computeBlastRadius(command, context) {
164
+ for (const rule of RULES) {
165
+ const match = command.match(rule.pattern);
166
+ if (!match) continue;
167
+ const resources = rule.extractResources ? rule.extractResources(match) : [];
168
+ if (resources.length === 0 && context?.service) resources.push(context.service);
169
+ const rollbackStrategy2 = deriveRollback(command, "");
170
+ const rollbackCommand2 = rollbackStrategy2.type === "command" ? rollbackStrategy2.command : null;
171
+ return {
172
+ command,
173
+ scope: rule.scope,
174
+ affectedResources: resources,
175
+ estimatedDowntimeMs: rule.estimatedDowntimeMs,
176
+ reversible: rule.reversible,
177
+ rollbackCommand: rollbackCommand2,
178
+ rollbackLatencyMs: rule.rollbackLatencyMs,
179
+ dataAtRisk: rule.dataAtRisk,
180
+ modelConfidence: rule.modelConfidence,
181
+ summary: buildSummary(rule.scope, rule.estimatedDowntimeMs, rule.reversible, rule.dataAtRisk)
182
+ };
183
+ }
184
+ const rollbackStrategy = deriveRollback(command, "");
185
+ const rollbackCommand = rollbackStrategy.type === "command" ? rollbackStrategy.command : null;
186
+ return {
187
+ command,
188
+ scope: "unknown",
189
+ affectedResources: context?.service ? [context.service] : [],
190
+ estimatedDowntimeMs: null,
191
+ reversible: rollbackCommand !== null,
192
+ rollbackCommand,
193
+ rollbackLatencyMs: null,
194
+ dataAtRisk: false,
195
+ modelConfidence: "low",
196
+ summary: buildSummary("unknown", null, rollbackCommand !== null, false)
197
+ };
198
+ }
199
+ const _SCOPE_SEVERITY = [
200
+ "unknown",
201
+ "pod",
202
+ "deployment",
203
+ "config-change",
204
+ "namespace",
205
+ "data-write",
206
+ "cluster",
207
+ "data-destructive"
208
+ ];
209
+ function mostSevereBlast(blasts) {
210
+ if (blasts.length === 0) throw new Error("mostSevereBlast: empty array");
211
+ return blasts.reduce((worst, current) => {
212
+ if (!current.reversible && worst.reversible) return current;
213
+ if (current.reversible && !worst.reversible) return worst;
214
+ if (current.dataAtRisk && !worst.dataAtRisk) return current;
215
+ if (!current.dataAtRisk && worst.dataAtRisk) return worst;
216
+ const cs = _SCOPE_SEVERITY.indexOf(current.scope);
217
+ const ws = _SCOPE_SEVERITY.indexOf(worst.scope);
218
+ return cs > ws ? current : worst;
219
+ });
220
+ }
221
+ export {
222
+ computeBlastRadius,
223
+ mostSevereBlast
224
+ };
@@ -0,0 +1,157 @@
1
+ import { randomBytes, createHmac, timingSafeEqual } from "crypto";
2
+ import fs from "fs";
3
+ import { recordHitlDecision } from "./gate-analytics.js";
4
+ import { BYPASS_PENDING_FILE, DATA_DIR, zeroRetentionMode } from "../sensor/paths.js";
5
+ import logger from "../sensor/logger.js";
6
+ let _bypassSigningSecret = "";
7
+ function setBypassSecret(secret) {
8
+ _bypassSigningSecret = secret;
9
+ }
10
+ function _signBypassPayload(payload) {
11
+ if (!_bypassSigningSecret) return "";
12
+ return createHmac("sha256", _bypassSigningSecret).update(payload).digest("hex");
13
+ }
14
+ const _pendingBypasses = /* @__PURE__ */ new Map();
15
+ function normalizeCommand(cmd) {
16
+ if (typeof cmd !== "string") return "";
17
+ return cmd.trim().replace(/\s+/g, " ");
18
+ }
19
+ function registerBypassBlock(toolName, commandArg, triggeredRules = []) {
20
+ const normalizedCmd = normalizeCommand(commandArg);
21
+ const now = Date.now();
22
+ for (const [token2, b] of _pendingBypasses.entries()) {
23
+ if (now > b.expiresAt) _pendingBypasses.delete(token2);
24
+ }
25
+ for (const [token2, b] of _pendingBypasses.entries()) {
26
+ if (b.toolName === toolName && b.commandArg === normalizedCmd && !b.approved) {
27
+ return token2;
28
+ }
29
+ }
30
+ let token;
31
+ do {
32
+ token = randomBytes(16).toString("hex");
33
+ } while (_pendingBypasses.has(token));
34
+ _pendingBypasses.set(token, {
35
+ token,
36
+ toolName,
37
+ commandArg: normalizedCmd,
38
+ triggeredRules,
39
+ registeredAt: now,
40
+ approved: false,
41
+ expiresAt: now + 10 * 60 * 1e3
42
+ // 10 minutes
43
+ });
44
+ return token;
45
+ }
46
+ function approveBypass(token) {
47
+ const b = _pendingBypasses.get(token);
48
+ if (!b || Date.now() > b.expiresAt) return { ok: false };
49
+ b.approved = true;
50
+ recordHitlDecision(b.triggeredRules, "approve", b.registeredAt);
51
+ return { ok: true, toolName: b.toolName, commandArg: b.commandArg };
52
+ }
53
+ function checkAndConsumeBypass(toolName, commandArg) {
54
+ const normalizedCmd = normalizeCommand(commandArg);
55
+ const now = Date.now();
56
+ for (const [token, b] of _pendingBypasses.entries()) {
57
+ if (now > b.expiresAt) {
58
+ _pendingBypasses.delete(token);
59
+ continue;
60
+ }
61
+ if (b.toolName === toolName && b.commandArg === normalizedCmd && b.approved) {
62
+ _pendingBypasses.delete(token);
63
+ return true;
64
+ }
65
+ }
66
+ return false;
67
+ }
68
+ function getPendingBypasses() {
69
+ const now = Date.now();
70
+ const list = [];
71
+ for (const [token, b] of _pendingBypasses.entries()) {
72
+ if (now > b.expiresAt) {
73
+ _pendingBypasses.delete(token);
74
+ continue;
75
+ }
76
+ if (!b.approved) {
77
+ list.push({
78
+ token: b.token,
79
+ toolName: b.toolName,
80
+ commandArg: b.commandArg,
81
+ expiresAt: b.expiresAt
82
+ });
83
+ }
84
+ }
85
+ return list;
86
+ }
87
+ function getPendingBypassDetail(token) {
88
+ const b = _pendingBypasses.get(token);
89
+ if (!b || Date.now() > b.expiresAt) return null;
90
+ return b;
91
+ }
92
+ function invalidateBypassToken(token) {
93
+ _pendingBypasses.delete(token);
94
+ }
95
+ function persistBypasses() {
96
+ if (zeroRetentionMode()) return;
97
+ try {
98
+ fs.mkdirSync(DATA_DIR, { recursive: true });
99
+ const now = Date.now();
100
+ const active = [..._pendingBypasses.values()].filter((b) => b.expiresAt > now);
101
+ const payload = JSON.stringify({ version: 1, bypasses: active });
102
+ const sig = _signBypassPayload(payload);
103
+ const final = sig ? JSON.stringify({ version: 1, bypasses: active, sig }) : payload;
104
+ const tmp = `${BYPASS_PENDING_FILE}.tmp.${process.pid}`;
105
+ fs.writeFileSync(tmp, final, { encoding: "utf8", mode: 384 });
106
+ fs.renameSync(tmp, BYPASS_PENDING_FILE);
107
+ } catch (err) {
108
+ logger.warn({ err }, "tool-guard: bypass persist failed");
109
+ }
110
+ }
111
+ function loadBypasses() {
112
+ if (zeroRetentionMode() || !fs.existsSync(BYPASS_PENDING_FILE)) return;
113
+ try {
114
+ const fileContent = fs.readFileSync(BYPASS_PENDING_FILE, "utf8");
115
+ const raw = JSON.parse(fileContent);
116
+ if (raw?.version !== 1 || !Array.isArray(raw.bypasses)) return;
117
+ if (_bypassSigningSecret && raw.sig !== void 0) {
118
+ const { sig, ...rest } = raw;
119
+ const expectedPayload = JSON.stringify(rest);
120
+ const expected = _signBypassPayload(expectedPayload);
121
+ const sigBuf = Buffer.from(sig, "hex");
122
+ const expectedBuf = Buffer.from(expected, "hex");
123
+ const valid = sigBuf.length === expectedBuf.length && timingSafeEqual(sigBuf, expectedBuf);
124
+ if (!valid) {
125
+ logger.error(
126
+ { path: BYPASS_PENDING_FILE },
127
+ "tool-guard: bypass file HMAC mismatch \u2014 file may have been tampered with. Discarding."
128
+ );
129
+ try {
130
+ fs.unlinkSync(BYPASS_PENDING_FILE);
131
+ } catch {
132
+ }
133
+ return;
134
+ }
135
+ } else if (_bypassSigningSecret && raw.sig === void 0) {
136
+ logger.warn({ path: BYPASS_PENDING_FILE }, "tool-guard: bypass file has no signature \u2014 accepting once and re-signing");
137
+ }
138
+ const now = Date.now();
139
+ for (const b of raw.bypasses) {
140
+ if (b.expiresAt > now) _pendingBypasses.set(b.token, b);
141
+ }
142
+ if (_pendingBypasses.size > 0) logger.info({ count: _pendingBypasses.size }, "tool-guard: restored pending bypass tokens");
143
+ } catch (err) {
144
+ logger.warn({ err }, "tool-guard: bypass load failed");
145
+ }
146
+ }
147
+ export {
148
+ approveBypass,
149
+ checkAndConsumeBypass,
150
+ getPendingBypassDetail,
151
+ getPendingBypasses,
152
+ invalidateBypassToken,
153
+ loadBypasses,
154
+ persistBypasses,
155
+ registerBypassBlock,
156
+ setBypassSecret
157
+ };
@@ -0,0 +1,77 @@
1
+ const LEARNING_RATE = 0.05;
2
+ const L2_LAMBDA = 1e-3;
3
+ const BIAS_IDX = 4;
4
+ function sigmoid(z) {
5
+ return 1 / (1 + Math.exp(-z));
6
+ }
7
+ class CalibrationClassifier {
8
+ /** [w0, w1, w2, w3, bias] */
9
+ w = [0, 0, 0, 0, 0];
10
+ _trainedOn = 0;
11
+ get trainedOn() {
12
+ return this._trainedOn;
13
+ }
14
+ /**
15
+ * Train (or incrementally update) the classifier on a single labelled sample.
16
+ * @param confidence Hypothesis confidence score (0–1)
17
+ * @param tagAccuracy Empirical accuracy of the detector tag (0–1)
18
+ * @param sampleCount Number of verdicts for this tag
19
+ * @param trusted Whether the tag has enough samples to be trusted
20
+ * @param isCorrect Ground truth: true = correct/partial, false = wrong
21
+ */
22
+ update(confidence, tagAccuracy, sampleCount, trusted, isCorrect) {
23
+ const x = this._features(confidence, tagAccuracy, sampleCount, trusted);
24
+ const y = isCorrect ? 1 : 0;
25
+ const p = this._predict(x);
26
+ const err = y - p;
27
+ for (let i = 0; i < 4; i++) {
28
+ this.w[i] = this.w[i] * (1 - LEARNING_RATE * L2_LAMBDA) + LEARNING_RATE * err * x[i];
29
+ }
30
+ this.w[BIAS_IDX] += LEARNING_RATE * err;
31
+ this._trainedOn++;
32
+ }
33
+ /**
34
+ * Predict P(correct | features).
35
+ * Returns 0.5 (neutral) if the model has fewer than 10 training samples.
36
+ */
37
+ predict(confidence, tagAccuracy, sampleCount, trusted) {
38
+ if (this._trainedOn < 10) return 0.5;
39
+ const x = this._features(confidence, tagAccuracy, sampleCount, trusted);
40
+ return this._predict(x);
41
+ }
42
+ /**
43
+ * Bulk-train from an existing record set.
44
+ * Call this on startup to warm-start from persisted verdicts.
45
+ */
46
+ trainBulk(samples) {
47
+ this.w = [0, 0, 0, 0, 0];
48
+ this._trainedOn = 0;
49
+ for (let pass = 0; pass < 2; pass++) {
50
+ for (const s of samples) {
51
+ this.update(s.confidence, s.tagAccuracy, s.sampleCount, s.trusted, s.isCorrect);
52
+ }
53
+ }
54
+ }
55
+ _features(confidence, tagAccuracy, sampleCount, trusted) {
56
+ return [
57
+ Math.max(0, Math.min(1, confidence)),
58
+ Math.max(0, Math.min(1, tagAccuracy)),
59
+ Math.log(sampleCount + 1) / Math.log(500),
60
+ trusted ? 1 : 0
61
+ ];
62
+ }
63
+ _predict(x) {
64
+ let z = this.w[BIAS_IDX];
65
+ for (let i = 0; i < 4; i++) z += this.w[i] * x[i];
66
+ return sigmoid(z);
67
+ }
68
+ /** Serialise weights for diagnostics / export. */
69
+ weights() {
70
+ return { w: [...this.w], trainedOn: this._trainedOn };
71
+ }
72
+ }
73
+ const calibrationClassifier = new CalibrationClassifier();
74
+ export {
75
+ CalibrationClassifier,
76
+ calibrationClassifier
77
+ };
@@ -0,0 +1,74 @@
1
+ import { execFile, spawn } from "child_process";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { DATA_DIR } from "../sensor/paths.js";
5
+ import logger from "../sensor/logger.js";
6
+ const GIT_DIR = DATA_DIR;
7
+ const GIT_LOCK = path.join(GIT_DIR, ".git");
8
+ const GITIGNORE = path.join(GIT_DIR, ".gitignore");
9
+ const GITIGNORE_CONTENT = [
10
+ "# Mergen data directory \u2014 only calibration and override corpus are tracked",
11
+ "secret",
12
+ "session.json",
13
+ "license.json",
14
+ "telemetry.json",
15
+ "*.db",
16
+ "*.db-wal",
17
+ "*.db-shm",
18
+ "*.tmp.*"
19
+ ].join("\n") + "\n";
20
+ function isEnabled() {
21
+ return process.env.MERGEN_GIT_SYNC === "true";
22
+ }
23
+ function ensureRepo() {
24
+ return new Promise((resolve) => {
25
+ if (fs.existsSync(GIT_LOCK)) {
26
+ resolve();
27
+ return;
28
+ }
29
+ fs.mkdirSync(GIT_DIR, { recursive: true });
30
+ if (!fs.existsSync(GITIGNORE)) {
31
+ fs.writeFileSync(GITIGNORE, GITIGNORE_CONTENT, "utf8");
32
+ }
33
+ execFile("git", ["-C", GIT_DIR, "init", "-b", "main"], (err) => {
34
+ if (err) {
35
+ execFile("git", ["-C", GIT_DIR, "init"], (err2) => {
36
+ if (err2) {
37
+ logger.warn({ err: err2 }, "calibration-git-sync: git init failed");
38
+ resolve();
39
+ return;
40
+ }
41
+ execFile("git", ["-C", GIT_DIR, "checkout", "-b", "main"], () => resolve());
42
+ });
43
+ return;
44
+ }
45
+ resolve();
46
+ });
47
+ });
48
+ }
49
+ function gitSyncCalibration(tag, verdict, pid) {
50
+ if (!isEnabled()) return;
51
+ const commit = async () => {
52
+ try {
53
+ await ensureRepo();
54
+ const msg = `calibration: ${tag} \u2192 ${verdict} (pid: ${pid.slice(0, 8)})`;
55
+ const files = ["calibration.json", "override-corpus.json", "shadow-log.json"].map((f) => path.join(GIT_DIR, f)).filter((f) => fs.existsSync(f)).map((f) => path.relative(GIT_DIR, f));
56
+ if (files.length === 0) return;
57
+ spawn("git", ["-C", GIT_DIR, "add", "--", ...files], { stdio: "ignore" }).on("close", (addCode) => {
58
+ if (addCode !== 0) return;
59
+ spawn("git", ["-C", GIT_DIR, "commit", "--allow-empty-message", "-m", msg, "--author", "Mergen <mergen@localhost>"], {
60
+ stdio: "ignore",
61
+ env: { ...process.env, GIT_AUTHOR_NAME: "Mergen", GIT_COMMITTER_NAME: "Mergen", GIT_AUTHOR_EMAIL: "mergen@localhost", GIT_COMMITTER_EMAIL: "mergen@localhost" }
62
+ });
63
+ });
64
+ } catch (err) {
65
+ logger.debug({ err }, "calibration-git-sync: commit skipped");
66
+ }
67
+ };
68
+ setTimeout(() => {
69
+ void commit();
70
+ }, 1500);
71
+ }
72
+ export {
73
+ gitSyncCalibration
74
+ };
@@ -0,0 +1 @@
1
+ export * from '../__stubs__/calibration.js';