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,117 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ const IMPORT_RE = /(?:import|export)(?:[\s\S]*?\bfrom\b)?\s+['"]([^'"]+)['"]/g;
4
+ function extractImports(source) {
5
+ const paths = [];
6
+ let m;
7
+ IMPORT_RE.lastIndex = 0;
8
+ while ((m = IMPORT_RE.exec(source)) !== null) {
9
+ paths.push(m[1]);
10
+ }
11
+ return paths;
12
+ }
13
+ function resolveImport(rawPath, importerDir) {
14
+ if (!rawPath.startsWith(".")) return null;
15
+ const withoutExt = rawPath.replace(/\.(js|ts|mjs)$/, "");
16
+ const candidates = [
17
+ path.resolve(importerDir, withoutExt + ".ts"),
18
+ path.resolve(importerDir, withoutExt, "index.ts")
19
+ ];
20
+ for (const c of candidates) {
21
+ if (fs.existsSync(c)) return c;
22
+ }
23
+ return null;
24
+ }
25
+ function collectTsFiles(dir, acc = []) {
26
+ if (!fs.existsSync(dir)) return acc;
27
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
28
+ const full = path.join(dir, entry.name);
29
+ if (entry.isDirectory()) {
30
+ if (["dist", "node_modules", "__stubs__", "evals"].includes(entry.name)) continue;
31
+ collectTsFiles(full, acc);
32
+ } else if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) {
33
+ acc.push(full);
34
+ }
35
+ }
36
+ return acc;
37
+ }
38
+ const _cache = /* @__PURE__ */ new Map();
39
+ function buildGraph(srcDir) {
40
+ const cached = _cache.get(srcDir);
41
+ if (cached && Date.now() - cached.builtAt < 6e4) return cached;
42
+ const files = collectTsFiles(srcDir).sort();
43
+ const forward = /* @__PURE__ */ new Map();
44
+ const reverse = /* @__PURE__ */ new Map();
45
+ for (const file of files) {
46
+ forward.set(file, /* @__PURE__ */ new Set());
47
+ reverse.set(file, /* @__PURE__ */ new Set());
48
+ }
49
+ for (const file of files) {
50
+ let source;
51
+ try {
52
+ source = fs.readFileSync(file, "utf8");
53
+ } catch {
54
+ continue;
55
+ }
56
+ const imports = extractImports(source);
57
+ for (const raw of imports) {
58
+ const resolved = resolveImport(raw, path.dirname(file));
59
+ if (!resolved || !forward.has(resolved)) continue;
60
+ forward.get(file).add(resolved);
61
+ reverse.get(resolved).add(file);
62
+ }
63
+ }
64
+ const graph = { root: srcDir, forward, reverse, files, builtAt: Date.now() };
65
+ _cache.set(srcDir, graph);
66
+ return graph;
67
+ }
68
+ function invalidateGraph(srcDir) {
69
+ _cache.delete(srcDir);
70
+ }
71
+ function queryGraph(graph, query) {
72
+ const { file, direction, maxDepth = 3 } = query;
73
+ const adjacency = direction === "depends-on" ? graph.forward : graph.reverse;
74
+ const direct = [...adjacency.get(file) ?? []];
75
+ const visited = /* @__PURE__ */ new Set([file]);
76
+ const frontier = [...direct];
77
+ const transitive = [];
78
+ let depth = 1;
79
+ while (frontier.length > 0 && depth < maxDepth) {
80
+ depth++;
81
+ const next = [];
82
+ for (const f of frontier) {
83
+ if (visited.has(f)) continue;
84
+ visited.add(f);
85
+ transitive.push(f);
86
+ const neighbors = adjacency.get(f) ?? /* @__PURE__ */ new Set();
87
+ for (const n of neighbors) {
88
+ if (!visited.has(n)) next.push(n);
89
+ }
90
+ }
91
+ frontier.length = 0;
92
+ frontier.push(...next);
93
+ }
94
+ return { file, direction, direct, transitive, depth };
95
+ }
96
+ function getZone(filePath) {
97
+ const p = filePath.replace(/\\/g, "/");
98
+ if (p.includes("/sensor/")) return "sensor";
99
+ if (p.includes("/intelligence/")) return "intelligence";
100
+ if (p.includes("/routes/")) return "routes";
101
+ if (p.includes("/datadog/")) return "datadog";
102
+ return "other";
103
+ }
104
+ const ZONE_DISPLAY = {
105
+ sensor: "Sensor (data ingestion / storage)",
106
+ intelligence: "Intelligence (MCP tools / analysis)",
107
+ routes: "Routes (Express handlers)",
108
+ datadog: "Datadog (external integration)",
109
+ other: "Other"
110
+ };
111
+ export {
112
+ ZONE_DISPLAY,
113
+ buildGraph,
114
+ getZone,
115
+ invalidateGraph,
116
+ queryGraph
117
+ };
@@ -0,0 +1,397 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+ import { recordExecutionAudit } from "../sensor/audit-log.js";
5
+ import { hasPermission } from "../sensor/rbac.js";
6
+ import { getStores } from "../storage/store-registry.js";
7
+ import logger from "../sensor/logger.js";
8
+ import { SAFETY_POLICY_FILE } from "../sensor/paths.js";
9
+ const DEFAULT_SAFETY_POLICY = {
10
+ // version 2: replaced overbroad bare-word 'delete' and 'truncate' with precise
11
+ // multi-word patterns ('delete from', 'truncate table') that don't false-positive
12
+ // on service names like 'delete-cache' or make targets like 'truncate-logs'.
13
+ // Database service names (postgres, mysql, redis, mongo) remain as they correctly
14
+ // block commands that target known production data stores by name.
15
+ version: 2,
16
+ blockedKeywords: [
17
+ "rm -rf",
18
+ "drop table",
19
+ "drop database",
20
+ "truncate table",
21
+ "delete from",
22
+ "kubectl delete",
23
+ "production-db",
24
+ "postgres",
25
+ "mysql",
26
+ "redis",
27
+ "mongo"
28
+ ],
29
+ blockedServices: [
30
+ "payments",
31
+ "auth-service",
32
+ "database"
33
+ ]
34
+ };
35
+ let _safetyPolicy = null;
36
+ let _safetyPolicyWatcherStarted = false;
37
+ function _watchSafetyPolicy() {
38
+ if (_safetyPolicyWatcherStarted) return;
39
+ _safetyPolicyWatcherStarted = true;
40
+ fs.watchFile(SAFETY_POLICY_FILE, { interval: 5e3, persistent: false }, () => {
41
+ _safetyPolicy = null;
42
+ logger.info({ path: SAFETY_POLICY_FILE }, "autonomy: safety-policy.json changed \u2014 reloading on next call");
43
+ });
44
+ }
45
+ function _resetSafetyPolicyForTesting(override) {
46
+ _safetyPolicy = override ?? null;
47
+ }
48
+ function loadSafetyPolicy(force = false) {
49
+ if (_safetyPolicy && !force) return _safetyPolicy;
50
+ _watchSafetyPolicy();
51
+ if (!fs.existsSync(SAFETY_POLICY_FILE)) {
52
+ try {
53
+ fs.mkdirSync(path.dirname(SAFETY_POLICY_FILE), { recursive: true });
54
+ fs.writeFileSync(SAFETY_POLICY_FILE, JSON.stringify(DEFAULT_SAFETY_POLICY, null, 2), "utf8");
55
+ logger.info({ path: SAFETY_POLICY_FILE }, "autonomy: created default safety-policy.json");
56
+ } catch (err) {
57
+ logger.warn({ err }, "autonomy: failed to write default safety policy");
58
+ }
59
+ _safetyPolicy = DEFAULT_SAFETY_POLICY;
60
+ return _safetyPolicy;
61
+ }
62
+ try {
63
+ const raw = fs.readFileSync(SAFETY_POLICY_FILE, "utf8");
64
+ const parsed = JSON.parse(raw);
65
+ if (parsed && Array.isArray(parsed.blockedKeywords) && Array.isArray(parsed.blockedServices)) {
66
+ if ((parsed.version ?? 0) < DEFAULT_SAFETY_POLICY.version) {
67
+ logger.info(
68
+ { oldVersion: parsed.version ?? 0, newVersion: DEFAULT_SAFETY_POLICY.version, path: SAFETY_POLICY_FILE },
69
+ "autonomy: safety-policy.json is outdated \u2014 upgrading to current defaults"
70
+ );
71
+ _safetyPolicy = DEFAULT_SAFETY_POLICY;
72
+ try {
73
+ fs.writeFileSync(SAFETY_POLICY_FILE, JSON.stringify(DEFAULT_SAFETY_POLICY, null, 2), "utf8");
74
+ } catch (err) {
75
+ logger.warn({ err }, "autonomy: failed to write upgraded safety policy");
76
+ }
77
+ } else {
78
+ _safetyPolicy = parsed;
79
+ }
80
+ } else {
81
+ _safetyPolicy = DEFAULT_SAFETY_POLICY;
82
+ }
83
+ } catch (err) {
84
+ logger.warn({ err }, "autonomy: failed to load safety policy \u2014 using defaults");
85
+ _safetyPolicy = DEFAULT_SAFETY_POLICY;
86
+ }
87
+ return _safetyPolicy;
88
+ }
89
+ function checkSafetyPolicy(command) {
90
+ const policy = loadSafetyPolicy();
91
+ const normalized = normalizeWhitespace(command).toLowerCase();
92
+ for (const keyword of policy.blockedKeywords) {
93
+ if (normalized.includes(keyword.toLowerCase())) {
94
+ return {
95
+ allowed: false,
96
+ blockReason: `Command matches safety-policy blocked keyword/pattern: '${keyword}'`,
97
+ matchedRule: `safety_keyword:${keyword}`
98
+ };
99
+ }
100
+ }
101
+ for (const service of policy.blockedServices) {
102
+ if (normalized.includes(service.toLowerCase())) {
103
+ return {
104
+ allowed: false,
105
+ blockReason: `Command targets safety-policy blocked service: '${service}'`,
106
+ matchedRule: `safety_service:${service}`
107
+ };
108
+ }
109
+ }
110
+ return { allowed: true };
111
+ }
112
+ const ALLOWED_COMMANDS = [
113
+ // Package managers
114
+ { pattern: /^npm (install|ci|rebuild|run \S+|update \S+)(\s|$)/i, description: "npm install / ci / rebuild / run / update" },
115
+ { pattern: /^yarn (install|run \S+|add \S+)(\s|$)/i, description: "yarn install / run / add" },
116
+ { pattern: /^pnpm (install|run \S+|update \S+|add \S+)(\s|$)/i, description: "pnpm install / run / update / add" },
117
+ // pip install: only allow package-name installs. Block URL schemes (https://, git+),
118
+ // and flags that write to arbitrary paths (--target, --prefix).
119
+ { pattern: /^pip3? install (?!https?:\/\/|git\+|--target[\s=]|--prefix[\s=])[\w.-]/i, description: "pip install <package-name> (no URL or path installs)" },
120
+ // Git — safe, read-side / restore operations only
121
+ { pattern: /^git checkout [a-f0-9]{4,40}(\s|$)/i, description: "git checkout <sha>" },
122
+ { pattern: /^git fetch(\s|$)/i, description: "git fetch" },
123
+ { pattern: /^git stash (push|pop)(\s|$)/i, description: "git stash push / pop" },
124
+ { pattern: /^git pull --ff-only(\s|$)/i, description: "git pull --ff-only" },
125
+ // Container orchestration
126
+ { pattern: /^docker (restart|stop|start|logs) \S+/i, description: "docker restart/stop/start/logs <container>" },
127
+ { pattern: /^kubectl rollout (restart|undo) /i, description: "kubectl rollout restart / undo" },
128
+ { pattern: /^kubectl scale deployment /i, description: "kubectl scale deployment" },
129
+ // Service control
130
+ { pattern: /^systemctl (restart|stop|start|reload) \S+/i, description: "systemctl restart/stop/start/reload" },
131
+ { pattern: /^service \S+ (restart|stop|start)/i, description: "service <name> restart/stop/start" },
132
+ // Build — restricted to known-safe targets only. Makefile targets can execute
133
+ // arbitrary shell code, so we permit only a fixed set rather than \S+ (any target).
134
+ { pattern: /^make (build|test|install|restart|reload|start|stop|clean|lint|check)(\s|$)/i, description: "make <build|test|install|restart|reload|start|stop|clean|lint|check>" }
135
+ ];
136
+ const ALLOWED_COMMAND_DESCRIPTIONS = ALLOWED_COMMANDS.map((r) => r.description);
137
+ function normalizeWhitespace(cmd) {
138
+ return cmd.trim().replace(/\s+/g, " ");
139
+ }
140
+ const SHELL_METACHAR_RE = /[;&|`$(){}[\]<>\\!\n\r]/;
141
+ function checkAllowlist(command) {
142
+ const normalized = normalizeWhitespace(command);
143
+ if (SHELL_METACHAR_RE.test(normalized)) {
144
+ return {
145
+ allowed: false,
146
+ blockReason: "Command contains shell metacharacter \u2014 possible injection attempt"
147
+ };
148
+ }
149
+ const match = ALLOWED_COMMANDS.find((r) => r.pattern.test(normalized));
150
+ if (match) return { allowed: true, matchedRule: match.description };
151
+ return {
152
+ allowed: false,
153
+ blockReason: `Command does not match any allowed pattern. Allowed forms: ${ALLOWED_COMMAND_DESCRIPTIONS.join(", ")}`
154
+ };
155
+ }
156
+ const MAX_OUTPUT_BYTES = 16 * 1024;
157
+ const EXEC_TIMEOUT_MS = 6e4;
158
+ function validateCwd(raw) {
159
+ if (!raw) return void 0;
160
+ const resolved = path.resolve(raw);
161
+ const safePrefixes = [process.cwd(), os.homedir()];
162
+ const isUnderSafeRoot = safePrefixes.some(
163
+ (p) => resolved === p || resolved.startsWith(p + path.sep)
164
+ );
165
+ if (!isUnderSafeRoot) {
166
+ logger.warn({ cwd: raw, resolved }, "autonomy: rejected cwd outside safe root");
167
+ return void 0;
168
+ }
169
+ try {
170
+ if (!fs.statSync(resolved).isDirectory()) return void 0;
171
+ } catch {
172
+ return void 0;
173
+ }
174
+ return resolved;
175
+ }
176
+ function _auditExecution(cmd, result, actor = "unknown") {
177
+ try {
178
+ recordExecutionAudit({
179
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
180
+ event: "autonomy.execute",
181
+ actor,
182
+ cmd: cmd.slice(0, 500),
183
+ ok: result.ok,
184
+ exitCode: result.exitCode ?? -1,
185
+ durationMs: result.durationMs,
186
+ blocked: result.blocked,
187
+ blockReason: result.blockReason ?? "",
188
+ timedOut: result.timedOut
189
+ });
190
+ } catch {
191
+ }
192
+ }
193
+ async function executeRemediation(command, opts = {}) {
194
+ const start = Date.now();
195
+ const actor = opts.actor ?? "unknown";
196
+ if (!hasPermission(actor, "responder")) {
197
+ const result = {
198
+ ok: false,
199
+ exitCode: null,
200
+ stdout: "",
201
+ stderr: "",
202
+ durationMs: 0,
203
+ timedOut: false,
204
+ blocked: true,
205
+ blockReason: `Actor '${actor}' does not have the 'responder' role required for fix execution`
206
+ };
207
+ _auditExecution(command, result, actor);
208
+ logger.warn({ actor, command }, "autonomy: command blocked by RBAC (insufficient role)");
209
+ await getStores().blunders.record({ blunderType: "rbac_block", command, blockReason: result.blockReason, service: null, tag: null, actor, pid: null, confidenceScore: null });
210
+ return result;
211
+ }
212
+ const { allowed, matchedRule, blockReason } = checkAllowlist(command);
213
+ if (!allowed) {
214
+ const result = {
215
+ ok: false,
216
+ exitCode: null,
217
+ stdout: "",
218
+ stderr: "",
219
+ durationMs: Date.now() - start,
220
+ timedOut: false,
221
+ blocked: true,
222
+ blockReason
223
+ };
224
+ _auditExecution(command, result, actor);
225
+ logger.warn({ command, blockReason }, "autonomy: command blocked by allowlist");
226
+ const blunderType = typeof blockReason === "string" && /inject/i.test(blockReason) ? "injection_attempt" : "allowlist_block";
227
+ await getStores().blunders.record({ blunderType, command, blockReason: blockReason ?? "", service: null, tag: null, actor, pid: null, confidenceScore: null });
228
+ return result;
229
+ }
230
+ const safetyCheck = checkSafetyPolicy(command);
231
+ if (!safetyCheck.allowed) {
232
+ const result = {
233
+ ok: false,
234
+ exitCode: null,
235
+ stdout: "",
236
+ stderr: "",
237
+ durationMs: Date.now() - start,
238
+ timedOut: false,
239
+ blocked: true,
240
+ blockReason: safetyCheck.blockReason
241
+ };
242
+ _auditExecution(command, result, actor);
243
+ logger.warn({ command, blockReason: safetyCheck.blockReason }, "autonomy: command blocked by safety policy");
244
+ await getStores().blunders.record({
245
+ blunderType: "pipeline_block",
246
+ command,
247
+ blockReason: safetyCheck.blockReason ?? "",
248
+ service: null,
249
+ tag: null,
250
+ actor,
251
+ pid: null,
252
+ confidenceScore: null,
253
+ triggeredRules: safetyCheck.matchedRule ? [safetyCheck.matchedRule] : null
254
+ });
255
+ return result;
256
+ }
257
+ logger.debug({ command, matchedRule }, "autonomy: command passed allowlist");
258
+ if (opts.dryRun) {
259
+ const result = {
260
+ ok: true,
261
+ exitCode: 0,
262
+ stdout: `[dry-run] would execute: ${command}`,
263
+ stderr: "",
264
+ durationMs: 0,
265
+ timedOut: false,
266
+ blocked: false
267
+ };
268
+ _auditExecution(command, result, actor);
269
+ return result;
270
+ }
271
+ const safeCwd = validateCwd(opts.cwd) ?? process.cwd();
272
+ logger.info({ command, cwd: safeCwd }, "autonomy: executing remediation command");
273
+ const [bin, ...cmdArgs] = normalizeWhitespace(command).split(" ").filter(Boolean);
274
+ const execEnv = {
275
+ PATH: process.env.PATH ?? "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
276
+ HOME: process.env.HOME ?? os.homedir(),
277
+ USER: process.env.USER ?? os.userInfo().username,
278
+ LOGNAME: process.env.LOGNAME ?? os.userInfo().username,
279
+ TMPDIR: process.env.TMPDIR ?? os.tmpdir(),
280
+ NO_COLOR: "1",
281
+ TERM: "dumb"
282
+ };
283
+ const { spawnSandboxed } = await import("./sandbox.js");
284
+ const { child: proc, sandboxed } = await spawnSandboxed(bin, cmdArgs, {
285
+ cwd: safeCwd,
286
+ env: execEnv,
287
+ stdio: ["ignore", "pipe", "pipe"]
288
+ });
289
+ return new Promise((resolve) => {
290
+ let stdout = "";
291
+ let stderr = "";
292
+ let timedOut = false;
293
+ let watcher = null;
294
+ if (!sandboxed && proc.pid) {
295
+ const rootPid = proc.pid;
296
+ void import("./process-tree.js").then(({ watchDescendants, killTree }) => {
297
+ watcher = watchDescendants(rootPid, {
298
+ onSpawn: (descendant) => {
299
+ const check = checkSafetyPolicy(descendant.command);
300
+ if (check.allowed) return;
301
+ logger.error(
302
+ { command, descendantPid: descendant.pid, descendantCommand: descendant.command },
303
+ "autonomy: descendant process matched a destructive pattern \u2014 terminating the process tree (post-spawn detect-and-kill, not pre-exec)"
304
+ );
305
+ void killTree(rootPid);
306
+ void getStores().blunders.record({
307
+ blunderType: "pipeline_block",
308
+ command: descendant.command,
309
+ blockReason: `Descendant of autonomous remediation command matched a destructive pattern (post-spawn detect-and-kill): ${check.blockReason ?? ""}`,
310
+ service: null,
311
+ tag: "process_tree_descendant",
312
+ actor,
313
+ pid: String(descendant.pid),
314
+ confidenceScore: null
315
+ });
316
+ }
317
+ });
318
+ }).catch(() => {
319
+ });
320
+ }
321
+ proc.stdout?.on("data", (chunk) => {
322
+ if (stdout.length < MAX_OUTPUT_BYTES) stdout += chunk.toString("utf8");
323
+ });
324
+ proc.stderr?.on("data", (chunk) => {
325
+ if (stderr.length < MAX_OUTPUT_BYTES) stderr += chunk.toString("utf8");
326
+ });
327
+ const timer = setTimeout(() => {
328
+ timedOut = true;
329
+ proc.kill("SIGTERM");
330
+ setTimeout(() => proc.kill("SIGKILL"), 2e3);
331
+ }, EXEC_TIMEOUT_MS);
332
+ proc.on("close", (code) => {
333
+ clearTimeout(timer);
334
+ watcher?.stop();
335
+ const result = {
336
+ ok: code === 0 && !timedOut,
337
+ exitCode: code,
338
+ stdout: stdout.slice(0, MAX_OUTPUT_BYTES),
339
+ stderr: stderr.slice(0, MAX_OUTPUT_BYTES),
340
+ durationMs: Date.now() - start,
341
+ timedOut,
342
+ blocked: false
343
+ };
344
+ _auditExecution(command, result, actor);
345
+ logger.info(
346
+ { exitCode: code, durationMs: result.durationMs, timedOut, actor, sandboxed },
347
+ "autonomy: command completed"
348
+ );
349
+ resolve(result);
350
+ });
351
+ proc.on("error", (err) => {
352
+ clearTimeout(timer);
353
+ watcher?.stop();
354
+ const result = {
355
+ ok: false,
356
+ exitCode: null,
357
+ stdout,
358
+ stderr: stderr + "\n" + String(err),
359
+ durationMs: Date.now() - start,
360
+ timedOut: false,
361
+ blocked: false
362
+ };
363
+ _auditExecution(command, result, actor);
364
+ resolve(result);
365
+ });
366
+ });
367
+ }
368
+ const CLI_PREFIXES = /^(npm|yarn|pnpm|pip3?|python3?|node|git|docker|kubectl|make|cargo|go|brew|apt|systemctl|service)\s/i;
369
+ function extractCommand(fixHint) {
370
+ if (!fixHint) return null;
371
+ const backtickMatches = [...fixHint.matchAll(/`([^`]{4,200})`/g)];
372
+ for (const m of backtickMatches) {
373
+ const candidate = m[1].trim();
374
+ if (CLI_PREFIXES.test(candidate)) return candidate;
375
+ }
376
+ const prompt = fixHint.match(/\$\s+([^\n]{4,200})/);
377
+ if (prompt) {
378
+ const candidate = prompt[1].trim();
379
+ if (CLI_PREFIXES.test(candidate)) return candidate;
380
+ }
381
+ const lines = fixHint.split("\n");
382
+ for (const line of lines) {
383
+ const trimmed = line.trim();
384
+ if (CLI_PREFIXES.test(trimmed)) return trimmed;
385
+ }
386
+ return null;
387
+ }
388
+ export {
389
+ ALLOWED_COMMAND_DESCRIPTIONS,
390
+ DEFAULT_SAFETY_POLICY,
391
+ _resetSafetyPolicyForTesting,
392
+ checkSafetyPolicy,
393
+ executeRemediation,
394
+ extractCommand,
395
+ loadSafetyPolicy,
396
+ validateCwd
397
+ };
@@ -0,0 +1,57 @@
1
+ import { issueFederationToken } from "./oidc-issuer.js";
2
+ import { createAzureProxySession } from "./azure-proxy-sessions.js";
3
+ const FEDERATED_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
4
+ const DEFAULT_FEDERATION_AUDIENCE = "api://AzureADTokenExchange";
5
+ async function requestAzureCredential(agentId, scopeName, scopeConfig, requestedDurationSeconds) {
6
+ const federationToken = await issueFederationToken({
7
+ agentId,
8
+ audience: scopeConfig.federationAudience ?? DEFAULT_FEDERATION_AUDIENCE,
9
+ // Azure access tokens are typically ~3600s regardless of what's asked
10
+ // for; the federation JWT itself only needs to live long enough to be
11
+ // exchanged, so it doesn't need to match the credential's own duration.
12
+ ttlSeconds: 300
13
+ });
14
+ const tokenUrl = `https://login.microsoftonline.com/${encodeURIComponent(scopeConfig.tenantId)}/oauth2/v2.0/token`;
15
+ const body = new URLSearchParams({
16
+ grant_type: "client_credentials",
17
+ client_id: scopeConfig.clientId,
18
+ client_assertion_type: FEDERATED_ASSERTION_TYPE,
19
+ client_assertion: federationToken,
20
+ scope: scopeConfig.azureScope
21
+ });
22
+ const res = await fetch(tokenUrl, {
23
+ method: "POST",
24
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
25
+ body: body.toString(),
26
+ signal: AbortSignal.timeout(1e4)
27
+ });
28
+ if (!res.ok) {
29
+ const text = await res.text().catch(() => "");
30
+ throw new Error(`azure-broker: ${tokenUrl} returned ${res.status}: ${text.slice(0, 500)}`);
31
+ }
32
+ const tokenResponse = await res.json();
33
+ const cappedSeconds = Math.min(
34
+ tokenResponse.expires_in,
35
+ requestedDurationSeconds ?? scopeConfig.maxDurationSeconds,
36
+ scopeConfig.maxDurationSeconds
37
+ );
38
+ const { proxyToken, proxyPath } = createAzureProxySession({
39
+ azureAccessToken: tokenResponse.access_token,
40
+ azureApiBase: scopeConfig.azureApiBase,
41
+ expiresInSeconds: cappedSeconds,
42
+ scopeName,
43
+ agentId
44
+ });
45
+ return {
46
+ provider: "azure",
47
+ proxyToken,
48
+ proxyPath,
49
+ clientId: scopeConfig.clientId,
50
+ expiration: new Date(Date.now() + cappedSeconds * 1e3),
51
+ scope: scopeName,
52
+ durationEnforced: true
53
+ };
54
+ }
55
+ export {
56
+ requestAzureCredential
57
+ };
@@ -0,0 +1,49 @@
1
+ import { randomBytes } from "crypto";
2
+ import logger from "../sensor/logger.js";
3
+ const _sessions = /* @__PURE__ */ new Map();
4
+ const SWEEP_INTERVAL_MS = 5 * 6e4;
5
+ setInterval(() => {
6
+ const now = Date.now();
7
+ for (const [token, session] of _sessions) {
8
+ if (now > session.expiresAt) _sessions.delete(token);
9
+ }
10
+ }, SWEEP_INTERVAL_MS).unref();
11
+ function createAzureProxySession(params) {
12
+ const proxyToken = randomBytes(32).toString("hex");
13
+ _sessions.set(proxyToken, {
14
+ azureAccessToken: params.azureAccessToken,
15
+ azureApiBase: params.azureApiBase,
16
+ expiresAt: Date.now() + params.expiresInSeconds * 1e3,
17
+ scopeName: params.scopeName,
18
+ agentId: params.agentId,
19
+ revoked: false
20
+ });
21
+ logger.info(
22
+ { scope: params.scopeName, agentId: params.agentId, expiresInSeconds: params.expiresInSeconds },
23
+ "azure-proxy-sessions: session created"
24
+ );
25
+ return { proxyToken, proxyPath: `/credentials/azure-proxy/${proxyToken}` };
26
+ }
27
+ function lookupAzureProxySession(proxyToken) {
28
+ const session = _sessions.get(proxyToken);
29
+ if (!session) return { status: "not_found" };
30
+ if (session.revoked) return { status: "revoked" };
31
+ if (Date.now() > session.expiresAt) return { status: "expired" };
32
+ return { status: "ok", session };
33
+ }
34
+ function revokeAzureProxySession(proxyToken) {
35
+ const session = _sessions.get(proxyToken);
36
+ if (!session) return false;
37
+ session.revoked = true;
38
+ logger.info({ scope: session.scopeName, agentId: session.agentId }, "azure-proxy-sessions: session revoked");
39
+ return true;
40
+ }
41
+ function _resetAzureProxySessionsForTesting() {
42
+ _sessions.clear();
43
+ }
44
+ export {
45
+ _resetAzureProxySessionsForTesting,
46
+ createAzureProxySession,
47
+ lookupAzureProxySession,
48
+ revokeAzureProxySession
49
+ };
@@ -0,0 +1 @@
1
+ export * from '../__stubs__/closed-source.js';